From c6823ac6e40ecefd59a87fc93f694f4533d498c1 Mon Sep 17 00:00:00 2001 From: Brendan Mc Date: Thu, 4 Sep 2014 13:15:22 -0400 Subject: [PATCH] Cleaned up code some. --- crypto/key.go | 155 ++++++++++++++++++++- crypto/rsa.go | 8 -- identify/identify.go | 317 ++++++++++--------------------------------- 3 files changed, 219 insertions(+), 261 deletions(-) diff --git a/crypto/key.go b/crypto/key.go index f28ecf1c093..7e502775018 100644 --- a/crypto/key.go +++ b/crypto/key.go @@ -1,10 +1,18 @@ package crypto import ( + "bytes" "errors" + "crypto/elliptic" + "crypto/hmac" "crypto/rand" "crypto/rsa" + "crypto/sha1" + "crypto/sha256" + "crypto/sha512" + "hash" + "math/big" "code.google.com/p/goprotobuf/proto" ) @@ -19,9 +27,6 @@ type PrivKey interface { // Cryptographically sign the given bytes Sign([]byte) ([]byte, error) - // Decrypt a message encrypted with this keys public key - Decrypt([]byte) ([]byte, error) - // Return a public key paired with this private key GetPublic() PubKey @@ -36,13 +41,13 @@ type PubKey interface { // Verify that 'sig' is the signed hash of 'data' Verify(data []byte, sig []byte) (bool, error) - // Encrypt the given data with the public key - Encrypt([]byte) ([]byte, error) - // Bytes returns a serialized, storeable representation of this key Bytes() ([]byte, error) } +// Given a public key, generates the shared key. +type GenSharedKey func([]byte) ([]byte, error) + func GenerateKeyPair(typ, bits int) (PrivKey, PubKey, error) { switch typ { case RSA: @@ -57,6 +62,144 @@ func GenerateKeyPair(typ, bits int) (PrivKey, PubKey, error) { } } +// Generates an ephemeral public key and returns a function that will compute +// the shared secret key. Used in the identify module. +// +// Focuses only on ECDH now, but can be made more general in the future. +func GenerateEKeyPair(curveName string) ([]byte, GenSharedKey, error) { + var curve elliptic.Curve + + switch curveName { + case "P-224": + curve = elliptic.P224() + case "P-256": + curve = elliptic.P256() + case "P-384": + curve = elliptic.P384() + case "P-521": + curve = elliptic.P521() + } + + priv, x, y, err := elliptic.GenerateKey(curve, rand.Reader) + if err != nil { + return nil, nil, err + } + + var pubKey bytes.Buffer + pubKey.Write(x.Bytes()) + pubKey.Write(y.Bytes()) + + done := func(theirPub []byte) ([]byte, error) { + // Verify and unpack node's public key. + curveSize := curve.Params().BitSize + + if len(theirPub) != (curveSize / 4) { + return nil, errors.New("Malformed public key.") + } + + bound := (curveSize / 8) + x := big.NewInt(0) + y := big.NewInt(0) + + x.SetBytes(theirPub[0:bound]) + y.SetBytes(theirPub[bound : bound*2]) + + if !curve.IsOnCurve(x, y) { + return nil, errors.New("Invalid public key.") + } + + // Generate shared secret. + secret, _ := curve.ScalarMult(x, y, priv) + + return secret.Bytes(), nil + } + + return pubKey.Bytes(), done, nil +} + +// Generates a set of keys for each party by stretching the shared key. +// (myIV, theirIV, myCipherKey, theirCipherKey, myMACKey, theirMACKey) +func KeyStretcher(cmp int, cipherType string, hashType string, secret []byte) ([]byte, []byte, []byte, []byte, []byte, []byte) { + var cipherKeySize int + switch cipherType { + case "AES-128": + cipherKeySize = 16 + case "AES-256": + cipherKeySize = 32 + } + + ivSize := 16 + hmacKeySize := 20 + + seed := []byte("key expansion") + + result := make([]byte, 2*(ivSize+cipherKeySize+hmacKeySize)) + + var h func() hash.Hash + + switch hashType { + case "SHA1": + h = sha1.New + case "SHA256": + h = sha256.New + case "SHA512": + h = sha512.New + } + + m := hmac.New(h, secret) + m.Write(seed) + + a := m.Sum(nil) + + j := 0 + for j < len(result) { + m.Reset() + m.Write(a) + m.Write(seed) + b := m.Sum(nil) + + todo := len(b) + + if j+todo > len(result) { + todo = len(result) - j + } + + copy(result[j:j+todo], b) + + j += todo + + m.Reset() + m.Write(a) + a = m.Sum(nil) + } + + myResult := make([]byte, ivSize+cipherKeySize+hmacKeySize) + theirResult := make([]byte, ivSize+cipherKeySize+hmacKeySize) + + half := len(result) / 2 + + if cmp == 1 { + copy(myResult, result[:half]) + copy(theirResult, result[half:]) + } else if cmp == -1 { + copy(myResult, result[half:]) + copy(theirResult, result[:half]) + } else { // Shouldn't happen, but oh well. + copy(myResult, result[half:]) + copy(theirResult, result[half:]) + } + + myIV := myResult[0:ivSize] + myCKey := myResult[ivSize : ivSize+cipherKeySize] + myMKey := myResult[ivSize+cipherKeySize:] + + theirIV := theirResult[0:ivSize] + theirCKey := theirResult[ivSize : ivSize+cipherKeySize] + theirMKey := theirResult[ivSize+cipherKeySize:] + + return myIV, theirIV, myCKey, theirCKey, myMKey, theirMKey +} + func UnmarshalPublicKey(data []byte) (PubKey, error) { pmes := new(PBPublicKey) err := proto.Unmarshal(data, pmes) diff --git a/crypto/rsa.go b/crypto/rsa.go index fda3bcf5812..31aa695960b 100644 --- a/crypto/rsa.go +++ b/crypto/rsa.go @@ -28,10 +28,6 @@ func (pk *RsaPublicKey) Verify(data, sig []byte) (bool, error) { return true, nil } -func (pk *RsaPublicKey) Encrypt(message []byte) ([]byte, error) { - return rsa.EncryptPKCS1v15(rand.Reader, pk.k, message) -} - func (pk *RsaPublicKey) Bytes() ([]byte, error) { b, err := x509.MarshalPKIXPublicKey(pk.k) if err != nil { @@ -56,10 +52,6 @@ func (sk *RsaPrivateKey) Sign(message []byte) ([]byte, error) { return rsa.SignPKCS1v15(rand.Reader, sk.k, crypto.SHA256, hashed[:]) } -func (sk *RsaPrivateKey) Decrypt(ciphertext []byte) ([]byte, error) { - return rsa.DecryptPKCS1v15(rand.Reader, sk.k, ciphertext) -} - func (sk *RsaPrivateKey) GetPublic() PubKey { return &RsaPublicKey{&sk.k.PublicKey} } diff --git a/identify/identify.go b/identify/identify.go index 0bc99f31198..017b03c0fd3 100644 --- a/identify/identify.go +++ b/identify/identify.go @@ -4,18 +4,17 @@ package identify import ( "bytes" + "errors" + "strings" + "crypto/aes" "crypto/cipher" - "crypto/elliptic" "crypto/hmac" "crypto/rand" "crypto/sha1" "crypto/sha256" "crypto/sha512" - "errors" "hash" - "math/big" - "strings" proto "code.google.com/p/goprotobuf/proto" ci "github.com/jbenet/go-ipfs/crypto" @@ -95,7 +94,7 @@ func Handshake(self, remote *peer.Peer, in, out chan []byte) (chan []byte, chan return nil, nil, err } - epubkey, done, err := generateEPubKey(exchange) // Generate EphemeralPubKey + epubkey, done, err := ci.GenerateEKeyPair(exchange) // Generate EphemeralPubKey var handshake bytes.Buffer // Gather corpus to sign. handshake.Write(encoded) @@ -144,87 +143,13 @@ func Handshake(self, remote *peer.Peer, in, out chan []byte) (chan []byte, chan } cmp := bytes.Compare(myPubKey, helloResp.GetPubkey()) - mIV, tIV, mCKey, tCKey, mMKey, tMKey := keyGenerator(cmp, cipherType, hashType, secret) + mIV, tIV, mCKey, tCKey, mMKey, tMKey := ci.KeyStretcher(cmp, cipherType, hashType, secret) secureIn := make(chan []byte) secureOut := make(chan []byte) - go func() { - myBlock, _ := aes.NewCipher(mCKey) - myCipher := cipher.NewCTR(myBlock, mIV) - - theirBlock, _ := aes.NewCipher(tCKey) - theirCipher := cipher.NewCTR(theirBlock, tIV) - - var myMac, theirMac hash.Hash - var macSize int - - switch hashType { - case "SHA1": - myMac = hmac.New(sha1.New, mMKey) - theirMac = hmac.New(sha1.New, tMKey) - macSize = 20 - - case "SHA256": - myMac = hmac.New(sha256.New, mMKey) - theirMac = hmac.New(sha256.New, tMKey) - macSize = 32 - - case "SHA512": - myMac = hmac.New(sha512.New, mMKey) - theirMac = hmac.New(sha512.New, tMKey) - macSize = 64 - } - - for { - select { - case data, ok := <-secureOut: - if !ok { - return - } - - if len(data) == 0 { - continue - } - - buff := make([]byte, len(data)+macSize) - - myCipher.XORKeyStream(buff, data) - - myMac.Write(buff[0:len(data)]) - copy(buff[len(data):], myMac.Sum(nil)) - myMac.Reset() - - out <- buff - - case data, ok := <-in: - if !ok { - return - } - - if len(data) <= macSize { - continue - } - - mark := len(data) - macSize - buff := make([]byte, mark) - - theirCipher.XORKeyStream(buff, data[0:mark]) - - theirMac.Write(data[0:mark]) - expected := theirMac.Sum(nil) - theirMac.Reset() - - hmacOk := hmac.Equal(data[mark:], expected) - - if hmacOk { - secureIn <- buff - } else { - secureIn <- nil - } - } - } - }() + go secureInProxy(in, secureIn, hashType, tIV, tCKey, tMKey) + go secureOutProxy(out, secureOut, hashType, mIV, mCKey, mMKey) finished := []byte("Finished") @@ -240,99 +165,90 @@ func Handshake(self, remote *peer.Peer, in, out chan []byte) (chan []byte, chan return secureIn, secureOut, nil } -func IdFromPubKey(pk ci.PubKey) (peer.ID, error) { - b, err := pk.Bytes() - if err != nil { - return nil, err - } - hash, err := u.Hash(b) - if err != nil { - return nil, err +func makeMac(hashType string, key []byte) (hash.Hash, int) { + switch hashType { + case "SHA1": + return hmac.New(sha1.New, key), sha1.Size + case "SHA512": + return hmac.New(sha512.New, key), sha512.Size + default: + return hmac.New(sha256.New, key), sha256.Size } - return peer.ID(hash), nil } -// Generates a set of keys for each party by stretching the shared key. -// (myIV, theirIV, myCipherKey, theirCipherKey, myMACKey, theirMACKey) -func keyGenerator(cmp int, cipherType string, hashType string, secret []byte) ([]byte, []byte, []byte, []byte, []byte, []byte) { - var cipherKeySize int - switch cipherType { - case "AES-128": - cipherKeySize = 16 - case "AES-256": - cipherKeySize = 32 - } - - ivSize := 16 - hmacKeySize := 20 - - seed := []byte("key expansion") +func secureInProxy(in, secureIn chan []byte, hashType string, tIV, tCKey, tMKey []byte) { + theirBlock, _ := aes.NewCipher(tCKey) + theirCipher := cipher.NewCTR(theirBlock, tIV) - result := make([]byte, 2*(ivSize+cipherKeySize+hmacKeySize)) + theirMac, macSize := makeMac(hashType, tMKey) - var h func() hash.Hash + for { + data, ok := <-in + if !ok { + return + } - switch hashType { - case "SHA1": - h = sha1.New - case "SHA256": - h = sha256.New - case "SHA512": - h = sha512.New - } + if len(data) <= macSize { + continue + } - m := hmac.New(h, secret) - m.Write(seed) + mark := len(data) - macSize + buff := make([]byte, mark) - a := m.Sum(nil) + theirCipher.XORKeyStream(buff, data[0:mark]) - j := 0 - for j < len(result) { - m.Reset() - m.Write(a) - m.Write(seed) - b := m.Sum(nil) + theirMac.Write(data[0:mark]) + expected := theirMac.Sum(nil) + theirMac.Reset() - todo := len(b) + hmacOk := hmac.Equal(data[mark:], expected) - if j+todo > len(result) { - todo = len(result) - j + if hmacOk { + secureIn <- buff + } else { + secureIn <- nil } + } +} - copy(result[j:j+todo], b) +func secureOutProxy(out, secureOut chan []byte, hashType string, mIV, mCKey, mMKey []byte) { + myBlock, _ := aes.NewCipher(mCKey) + myCipher := cipher.NewCTR(myBlock, mIV) - j += todo + myMac, macSize := makeMac(hashType, mMKey) - m.Reset() - m.Write(a) - a = m.Sum(nil) - } + for { + data, ok := <-secureOut + if !ok { + return + } - myResult := make([]byte, ivSize+cipherKeySize+hmacKeySize) - theirResult := make([]byte, ivSize+cipherKeySize+hmacKeySize) + if len(data) == 0 { + continue + } - half := len(result) / 2 + buff := make([]byte, len(data)+macSize) - if cmp == 1 { - copy(myResult, result[:half]) - copy(theirResult, result[half:]) - } else if cmp == -1 { - copy(myResult, result[half:]) - copy(theirResult, result[:half]) - } else { // Shouldn't happen, but oh well. - copy(myResult, result[half:]) - copy(theirResult, result[half:]) - } + myCipher.XORKeyStream(buff, data) - myIV := myResult[0:ivSize] - myCKey := myResult[ivSize : ivSize+cipherKeySize] - myMKey := myResult[ivSize+cipherKeySize:] + myMac.Write(buff[0:len(data)]) + copy(buff[len(data):], myMac.Sum(nil)) + myMac.Reset() - theirIV := theirResult[0:ivSize] - theirCKey := theirResult[ivSize : ivSize+cipherKeySize] - theirMKey := theirResult[ivSize+cipherKeySize:] + out <- buff + } +} - return myIV, theirIV, myCKey, theirCKey, myMKey, theirMKey +func IdFromPubKey(pk ci.PubKey) (peer.ID, error) { + b, err := pk.Bytes() + if err != nil { + return nil, err + } + hash, err := u.Hash(b) + if err != nil { + return nil, err + } + return peer.ID(hash), nil } // Determines which algorithm to use. Note: f(a, b) = f(b, a) @@ -372,96 +288,3 @@ func selectBest(myPrefs, theirPrefs string) (string, error) { return "", errors.New("No algorithms in common!") } - -// Generates an ephemeral public key and returns a function that will compute -// the shared secret key. -// -// Focuses only on ECDH now, but can be made more general in the future. -func generateEPubKey(exchange string) ([]byte, func([]byte) ([]byte, error), error) { - genKeyPair := func(curve elliptic.Curve) ([]byte, []byte, error) { - priv, x, y, err := elliptic.GenerateKey(curve, rand.Reader) - if err != nil { - return nil, nil, err - } - - var pubKey bytes.Buffer - pubKey.Write(x.Bytes()) - pubKey.Write(y.Bytes()) - - return pubKey.Bytes(), priv, nil - } - - genSec := func(curve elliptic.Curve, theirPub []byte, myPriv []byte) ([]byte, error) { - // Verify and unpack node's public key. - curveSize := curve.Params().BitSize - - if len(theirPub) != (curveSize / 4) { - return nil, errors.New("Malformed public key.") - } - - bound := (curveSize / 8) - x := big.NewInt(0) - y := big.NewInt(0) - - x.SetBytes(theirPub[0:bound]) - y.SetBytes(theirPub[bound : bound*2]) - - if !curve.IsOnCurve(x, y) { - return nil, errors.New("Invalid public key.") - } - - // Generate shared secret. - secret, _ := curve.ScalarMult(x, y, myPriv) - - return secret.Bytes(), nil - } - - switch exchange { - case "P-224": - curve := elliptic.P224() - pub, priv, err := genKeyPair(curve) - if err != nil { - return nil, nil, err - } - - done := func(theirs []byte) ([]byte, error) { return genSec(curve, theirs, priv) } - - return pub, done, nil - - case "P-256": - curve := elliptic.P256() - pub, priv, err := genKeyPair(curve) - if err != nil { - return nil, nil, err - } - - done := func(theirs []byte) ([]byte, error) { return genSec(curve, theirs, priv) } - - return pub, done, nil - - case "P-384": - curve := elliptic.P384() - pub, priv, err := genKeyPair(curve) - if err != nil { - return nil, nil, err - } - - done := func(theirs []byte) ([]byte, error) { return genSec(curve, theirs, priv) } - - return pub, done, nil - - case "P-521": - curve := elliptic.P521() - pub, priv, err := genKeyPair(curve) - if err != nil { - return nil, nil, err - } - - done := func(theirs []byte) ([]byte, error) { return genSec(curve, theirs, priv) } - - return pub, done, nil - - } - - return nil, nil, errors.New("Something silly happened.") -}