From d46655c56eeeb14d466e185de804bdf2c7e76a0d Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 21 Oct 2015 14:33:43 +0200 Subject: [PATCH] crypto/ecies: export and improve ConcatKDF --- crypto/ecies/ecies.go | 59 +++++++++++--------------------- crypto/ecies/ecies_test.go | 69 +++++++++++++++++++++++++++++++------- 2 files changed, 75 insertions(+), 53 deletions(-) diff --git a/crypto/ecies/ecies.go b/crypto/ecies/ecies.go index 65dc5b38ba..2c4dc70a4c 100644 --- a/crypto/ecies/ecies.go +++ b/crypto/ecies/ecies.go @@ -35,9 +35,11 @@ import ( "crypto/elliptic" "crypto/hmac" "crypto/subtle" + "encoding/binary" "fmt" "hash" "io" + "math" "math/big" ) @@ -143,50 +145,27 @@ var ( ErrInvalidMessage = fmt.Errorf("ecies: invalid message") ) -var ( - big2To32 = new(big.Int).Exp(big.NewInt(2), big.NewInt(32), nil) - big2To32M1 = new(big.Int).Sub(big2To32, big.NewInt(1)) -) - -func incCounter(ctr []byte) { - if ctr[3]++; ctr[3] != 0 { - return - } else if ctr[2]++; ctr[2] != 0 { - return - } else if ctr[1]++; ctr[1] != 0 { - return - } else if ctr[0]++; ctr[0] != 0 { - return +// ConcatKDF implements the Concatenation Key Derivation Function +// specified in NIST SP 800-56 (section 5.8.1). +// It returns kdlen bytes of key material derived from z and s1 using hash. +// kdlen is expected to be reasonably small. +func ConcatKDF(hash hash.Hash, z, s1 []byte, kdlen int) ([]byte, error) { + hashlen := hash.Size() + reps := (kdlen + hashlen - 1) / hashlen + if reps > math.MaxUint32 { + return nil, ErrKeyDataTooLong // prevent counter overflow } - return -} - -// NIST SP 800-56 Concatenation Key Derivation Function (see section 5.8.1). -func concatKDF(hash hash.Hash, z, s1 []byte, kdLen int) (k []byte, err error) { - if s1 == nil { - s1 = make([]byte, 0) - } - - reps := ((kdLen + 7) * 8) / (hash.BlockSize() * 8) - if big.NewInt(int64(reps)).Cmp(big2To32M1) > 0 { - fmt.Println(big2To32M1) - return nil, ErrKeyDataTooLong - } - - counter := []byte{0, 0, 0, 1} - k = make([]byte, 0) - - for i := 0; i <= reps; i++ { + counter := []byte{0, 0, 0, 0} + k := make([]byte, 0, reps*hashlen) + for i := uint32(1); i <= uint32(reps); i++ { + binary.BigEndian.PutUint32(counter, i) hash.Write(counter) hash.Write(z) hash.Write(s1) - k = append(k, hash.Sum(nil)...) + k = hash.Sum(k) hash.Reset() - incCounter(counter) } - - k = k[:kdLen] - return + return k[:kdlen], nil } // messageTag computes the MAC of a message (called the tag) as per @@ -264,7 +243,7 @@ func Encrypt(rand io.Reader, pub *PublicKey, m, s1, s2 []byte) (ct []byte, err e if err != nil { return } - K, err := concatKDF(hash, z, s1, params.KeyLen+params.KeyLen) + K, err := ConcatKDF(hash, z, s1, params.KeyLen+params.KeyLen) if err != nil { return } @@ -343,7 +322,7 @@ func (prv *PrivateKey) Decrypt(rand io.Reader, c, s1, s2 []byte) (m []byte, err return } - K, err := concatKDF(hash, z, s1, params.KeyLen+params.KeyLen) + K, err := ConcatKDF(hash, z, s1, params.KeyLen+params.KeyLen) if err != nil { return } diff --git a/crypto/ecies/ecies_test.go b/crypto/ecies/ecies_test.go index 6a0ea3f022..c671e061e5 100644 --- a/crypto/ecies/ecies_test.go +++ b/crypto/ecies/ecies_test.go @@ -53,20 +53,63 @@ func init() { dumpEnc = *flDump } -// Ensure the KDF generates appropriately sized keys. -func TestKDF(t *testing.T) { - msg := []byte("Hello, world") - h := sha256.New() +type kdftest struct { + key, s1 string + kdlen int + output string +} - k, err := concatKDF(h, msg, nil, 64) - if err != nil { - fmt.Println(err.Error()) - t.FailNow() - } - if len(k) != 64 { - fmt.Printf("KDF: generated key is the wrong size (%d instead of 64\n", - len(k)) - t.FailNow() +var kdftests = []kdftest{ + { + key: "38f9a331c022f51d66658f301837108c9710d5ee0697bfac97bb6b0ea8d4f273", + s1: "", + kdlen: 0, + output: "", + }, + { + key: "38f9a331c022f51d66658f301837108c9710d5ee0697bfac97bb6b0ea8d4f273", + s1: "", + kdlen: 32, + output: "bbfb3912ffc0d1789be7c3c2773fb6abd8df69578df2ca16beee3d0f7a9692d1", + }, + { + key: "38f9a331c022f51d66658f301837108c9710d5ee0697bfac97bb6b0ea8d4f273", + s1: "", + kdlen: 64, + output: "bbfb3912ffc0d1789be7c3c2773fb6abd8df69578df2ca16beee3d0f7a9692d1eae543288220e41452942fe268297fff38423b65b19cf7c6263aa611a4190741", + }, + { + key: "38f9a331c022f51d66658f301837108c9710d5ee0697bfac97bb6b0ea8d4f273", + s1: "", + kdlen: 242, + output: "bbfb3912ffc0d1789be7c3c2773fb6abd8df69578df2ca16beee3d0f7a9692d1eae543288220e41452942fe268297fff38423b65b19cf7c6263aa611a41907410a49acd5adbfbe93e902349105d7bd7ef5b106d9357b20bb4a7977d548bc2bf1a0b275a9f1de19ff8f963ec58171aa31da964edb0131436d88a7714e2429d85693409f718c8fea7ecaa076dce68f282aba4010a42feedcb1affc350497fa1078ad89e23e8a04ba2ef179c3c625b054817d792076b5882e80925d45a2874285d45a7767fa6a3853bd8417930923a554743f6eb4691e4790a97c467337a307c64a8bed5b5c1829d0a690a554d3e5334ee4980a", + }, + { + key: "38f9a331c022f51d66658f301837108c9710d5ee0697bfac97bb6b0ea8d4f273", + s1: "343434", + kdlen: 242, + output: "71e6fe05a6a57e2312a996eb3b91fc613fb57196ea09880b7b0ed880afa399f942ad56c1a484f4ccd329f9c21911ae09b497e991d8f47060114c8f137ab65df10c3f1d2478a7af913aa406817f34ef55d7852f3390f8201056451b0c29fb73a5c9e5a5093e6d93fefa15846b81bc02e1a7033948d67fc70c1dbbbf11d97f34b15d9967709c666eff05d895cc2f415064b382a98e68c178f9e7e2d9b6169ef4cdd5bb855b21d0ff71339e7e5b3afcb2393a8c6f4c7a4e618094222be87cbfdd2417ebdced5870bfbe8761d2e646c5a62dd31852dba399507adb84334a81b4ce54714f1828a0cdf3067908815d516368c2dd58", + }, +} + +func TestKDF(t *testing.T) { + for _, test := range kdftests { + z, _ := hex.DecodeString(test.key) + s1, _ := hex.DecodeString(test.s1) + h := sha256.New() + k, err := ConcatKDF(h, z, s1, test.kdlen) + if err != nil { + t.Error(err) + } + if len(k) != test.kdlen { + t.Errorf("output length mismatch: got %d, want %d", len(k), test.kdlen) + } + if hexk := hex.EncodeToString(k); hexk != test.output { + t.Errorf("output mismatch:\ngot %s\nwant %s", hexk, test.output) + } + if t.Failed() { + t.Fatalf("failed test: %#v", test) + } } }