diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index fd67a6b9102a97fda77e9ed716b8e546b8f75fae..ffe11f93f25c157de46f4d033b37faccc4bec62b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,7 +4,7 @@ variables: REPO_DIR: gitlab.com/elixxir REPO_NAME: crypto DOCKER_IMAGE: elixxirlabs/cuda-go:latest - MIN_CODE_COVERAGE: "95.5" + MIN_CODE_COVERAGE: "95.4" before_script: - go version || echo "Go executable not found." diff --git a/e2e/singleUse/mac.go b/e2e/singleUse/mac.go index 649339f16d639629402e69535abf27324ad02b5d..810076b7daee8ce5c59241a408172cb5d1ace963 100644 --- a/e2e/singleUse/mac.go +++ b/e2e/singleUse/mac.go @@ -10,31 +10,38 @@ package singleUse import ( "bytes" jww "github.com/spf13/jwalterweatherman" - "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/hash" ) -const macConstant = "macConstant" +const macSalt = "singleUseMacSalt" -// MakeMAC generates the MAC for the given base key and encrypted payload. -func MakeMAC(baseKey *cyclic.Int, encryptedPayload []byte) []byte { +// MakeMAC generates the MAC used in both the transmission and response CMIX +// messages. +func MakeMAC(key []byte, encryptedPayload []byte) []byte { + // Create new hash h, err := hash.NewCMixHash() if err != nil { - jww.ERROR.Panicf("Failed to create hash: %v", err) + jww.ERROR.Panicf("Failed to create new hash for single-use MAC: %v", err) } - // Hash the key, number, and constant - h.Write(baseKey.Bytes()) + // Hash the key, encrypted payload, and salt + h.Write(key) h.Write(encryptedPayload) - h.Write([]byte(macConstant)) + h.Write([]byte(macSalt)) - return h.Sum(nil) + // Get hash bytes + mac := h.Sum(nil) + + // Set the first bit as zero to ensure everything stays in the group + mac[0] &= 0b01111111 + + return mac } -// VerifyMAC determines if the provided MAC is valid for the given base key and +// VerifyMAC determines if the provided MAC is valid for the given key and // encrypted payload. -func VerifyMAC(baseKey *cyclic.Int, encryptedPayload, receivedMAC []byte) bool { - newMAC := MakeMAC(baseKey, encryptedPayload) +func VerifyMAC(key []byte, encryptedPayload, receivedMAC []byte) bool { + newMAC := MakeMAC(key, encryptedPayload) return bytes.Equal(newMAC, receivedMAC) } diff --git a/e2e/singleUse/mac_test.go b/e2e/singleUse/mac_test.go index 1817a33fd9d2403acd64d47fd9a6f6a51e765352..e1c6a0a006cad59d5d2943fca7bcf20cc59136db 100644 --- a/e2e/singleUse/mac_test.go +++ b/e2e/singleUse/mac_test.go @@ -10,114 +10,175 @@ package singleUse import ( "encoding/base64" "gitlab.com/elixxir/crypto/cyclic" - "gitlab.com/elixxir/crypto/diffieHellman" + "gitlab.com/xx_network/crypto/large" "math/rand" "testing" ) // Tests that the generated MACs do not change. func TestMAC_Consistency(t *testing.T) { - expectedMACs := []string{ - "D7vXMT3iX/1tvFPjowoz3w5b3PbECB4EFgkR3hNzyCM=", - "ffW26eqwyTHERUPHbmFkUitRj7c+c/PQayfwQPLihNs=", - "/lyTEnFbzEEmmMjOQhq6qr0jUYE2j22ERcR4CLH9368=", - "vPB8eXP8BXWdxSU4a+Lp1pYVWb98COtWtR0sBngGyTs=", - "BuvhuSO01uM+nVQPwXlOwRzr7xchVM1dRoF9h5TC2HU=", - "vF5iYaFdhLtgOO5hWNpAF6jJaW4utF1MLGaz/gYb0TY=", - "opI2htMJEqZvvEKHAwWnTSqgvaAempY9//rRxNNFHQU=", - "ppGzFglPaLzytF5gJRtSFbYoidsShEBoX1cxkAJY3F4=", - "pdzZmlz6qdChwo7B5sChTRUpQ5zLvlH5LgndOhDHAt8=", - "BHP2yvbUcynr6vM61qt+18YshTsyArJqv0VCq/EI3+o=", + expectedMacs := []string{ + "P5Arek8yIlKVmM4IHJTOycUph0RLeiP0emA0DCx4z90=", + "P9LcMFLn+q8/6SOSb9rrYpLyn3X5MMuephBSOtIF4M0=", + "UOPiUuATfn9C/KjmFZdA4zTfIykMb0HIgELKLbixrtk=", + "UYUHQLjbyDBdZn/MTNQBgrvUQPN0MnVpdYEI0iB/Q68=", + "fArOwzcunIaDYoIgtViaoBOkL2/v95Hm+6KRTEE6F9A=", + "XMhNPEyxy8q0p+bsjGHPQL5vzA+HyqUoooHXrTBEYF4=", + "IDJ4iTo9IcNB/oR5RP45de53SxuWsSSKlYcmGInQtoU=", + "Z4QqQfBp0ezUzAU2MHBUR9K0Bdl9Z3WF5Rcy553fb+8=", + "HthTNmjJ78iCdjMX+zSyhabsiCEE8QmH/AsDFfH8yUA=", + "GnE0KJAwk594cKLt6i2kHAqMJZ8DnXv7XyrByU27F/g=", } - grp := getGrp() prng := rand.New(rand.NewSource(42)) - for i, expected := range expectedMACs { - privKey := diffieHellman.GeneratePrivateKey(diffieHellman.DefaultPrivateKeyLength, grp, prng) - pubkey := diffieHellman.GeneratePublicKey(privKey, grp) - baseKey := diffieHellman.GenerateSessionKey(privKey, pubkey, grp) - encryptedPayload := make([]byte, 128) + for i, expectedMac := range expectedMacs { + key := make([]byte, prng.Intn(255)) + prng.Read(key) + encryptedPayload := make([]byte, prng.Intn(500)) prng.Read(encryptedPayload) - testMAC := MakeMAC(baseKey, encryptedPayload) - testMACBase64 := base64.StdEncoding.EncodeToString(testMAC) + testMAC := MakeMAC(key, encryptedPayload) + testMacBase64 := base64.StdEncoding.EncodeToString(testMAC) - if expected != testMACBase64 { - t.Errorf("MakeMAC() did not return the expected MAC for the given "+ - "base key and encrypted payload at index %d."+ - "\nbase key: %s\nexpected: %s\nreceived: %s", - i, baseKey.Text(10), expected, testMACBase64) + if expectedMac != testMacBase64 { + t.Errorf("MakeMAC() did not return the expected MAC (%d)."+ + "\nexpected: %s\nreceived: %s", i, expectedMac, testMacBase64) } } } // Tests that all generated MACs are unique. func TestMAC_Unique(t *testing.T) { - grp := getGrp() + testRuns := 20 prng := rand.New(rand.NewSource(42)) MACs := make(map[string]struct { - key *cyclic.Int + key []byte encryptedPayload []byte - }, 100) + }) - for i := 0; i < 100; i++ { - privKey := diffieHellman.GeneratePrivateKey(diffieHellman.DefaultPrivateKeyLength, grp, prng) - pubkey := diffieHellman.GeneratePublicKey(privKey, grp) - baseKey := diffieHellman.GenerateSessionKey(privKey, pubkey, grp) - encryptedPayload := make([]byte, 128) + // Test with same key but differing payloads + for i := 0; i < testRuns; i++ { + key := make([]byte, prng.Intn(32)+i) + prng.Read(key) + for j := 0; j < testRuns; j++ { + encryptedPayload := make([]byte, prng.Intn(500)+j) + prng.Read(encryptedPayload) + + testMAC := MakeMAC(key, encryptedPayload) + testMACBase64 := base64.StdEncoding.EncodeToString(testMAC) + + if _, exists := MACs[testMACBase64]; exists { + t.Errorf("Generated MAC collides with previously generated MAC (%d, %d)."+ + "\ncurrent MAC: key: %+v encryptedPayload: %+v"+ + "\npreviouse MAC: key: %+v encryptedPayload: %+v"+ + "\nMAC: %s", i, j, + key, encryptedPayload, MACs[testMACBase64].key, + MACs[testMACBase64].encryptedPayload, testMAC) + } else { + MACs[testMACBase64] = struct { + key []byte + encryptedPayload []byte + }{key, encryptedPayload} + } + } + } + + // Test with same payload but differing keys + for i := 0; i < testRuns; i++ { + encryptedPayload := make([]byte, prng.Intn(500)+i) prng.Read(encryptedPayload) - testMAC := MakeMAC(baseKey, encryptedPayload) - testMACBase64 := base64.StdEncoding.EncodeToString(testMAC) + for j := 0; j < testRuns; j++ { + key := make([]byte, prng.Intn(32)+j) + prng.Read(key) - if _, exists := MACs[testMACBase64]; exists { - t.Errorf("Generated MAC collides with previously generated MAC."+ - "\ncurrent MAC: baseKey: %s encryptedPayload: %s"+ - "\npreviouse MAC: baseKey: %s encryptedPayload: %s"+ - "\nMAC: %s", - baseKey.Text(10), - base64.StdEncoding.EncodeToString(encryptedPayload), - MACs[testMACBase64].key.Text(10), - base64.StdEncoding.EncodeToString(MACs[testMACBase64].encryptedPayload), - base64.StdEncoding.EncodeToString(testMAC)) - } else { - MACs[testMACBase64] = struct { - key *cyclic.Int - encryptedPayload []byte - }{baseKey, encryptedPayload} + testMAC := MakeMAC(key, encryptedPayload) + testMACBase64 := base64.StdEncoding.EncodeToString(testMAC) + + if _, exists := MACs[testMACBase64]; exists { + t.Errorf("Generated MAC collides with previously generated MAC (%d, %d)."+ + "\ncurrent MAC: key: %+v encryptedPayload: %+v"+ + "\npreviouse MAC: key: %+v encryptedPayload: %+v"+ + "\nMAC: %s", i, j, + key, encryptedPayload, MACs[testMACBase64].key, + MACs[testMACBase64].encryptedPayload, testMAC) + } else { + MACs[testMACBase64] = struct { + key []byte + encryptedPayload []byte + }{key, encryptedPayload} + } } } } +// Happy path. func TestVerifyMAC(t *testing.T) { expectedMACs := []string{ - "D7vXMT3iX/1tvFPjowoz3w5b3PbECB4EFgkR3hNzyCM=", - "ffW26eqwyTHERUPHbmFkUitRj7c+c/PQayfwQPLihNs=", - "/lyTEnFbzEEmmMjOQhq6qr0jUYE2j22ERcR4CLH9368=", - "vPB8eXP8BXWdxSU4a+Lp1pYVWb98COtWtR0sBngGyTs=", - "BuvhuSO01uM+nVQPwXlOwRzr7xchVM1dRoF9h5TC2HU=", - "vF5iYaFdhLtgOO5hWNpAF6jJaW4utF1MLGaz/gYb0TY=", - "opI2htMJEqZvvEKHAwWnTSqgvaAempY9//rRxNNFHQU=", - "ppGzFglPaLzytF5gJRtSFbYoidsShEBoX1cxkAJY3F4=", - "pdzZmlz6qdChwo7B5sChTRUpQ5zLvlH5LgndOhDHAt8=", - "BHP2yvbUcynr6vM61qt+18YshTsyArJqv0VCq/EI3+o=", + "P5Arek8yIlKVmM4IHJTOycUph0RLeiP0emA0DCx4z90=", + "P9LcMFLn+q8/6SOSb9rrYpLyn3X5MMuephBSOtIF4M0=", + "UOPiUuATfn9C/KjmFZdA4zTfIykMb0HIgELKLbixrtk=", + "UYUHQLjbyDBdZn/MTNQBgrvUQPN0MnVpdYEI0iB/Q68=", + "fArOwzcunIaDYoIgtViaoBOkL2/v95Hm+6KRTEE6F9A=", + "XMhNPEyxy8q0p+bsjGHPQL5vzA+HyqUoooHXrTBEYF4=", + "IDJ4iTo9IcNB/oR5RP45de53SxuWsSSKlYcmGInQtoU=", + "Z4QqQfBp0ezUzAU2MHBUR9K0Bdl9Z3WF5Rcy553fb+8=", + "HthTNmjJ78iCdjMX+zSyhabsiCEE8QmH/AsDFfH8yUA=", + "GnE0KJAwk594cKLt6i2kHAqMJZ8DnXv7XyrByU27F/g=", } - grp := getGrp() prng := rand.New(rand.NewSource(42)) for i, expected := range expectedMACs { - privKey := diffieHellman.GeneratePrivateKey(diffieHellman.DefaultPrivateKeyLength, grp, prng) - pubkey := diffieHellman.GeneratePublicKey(privKey, grp) - baseKey := diffieHellman.GenerateSessionKey(privKey, pubkey, grp) - encryptedPayload := make([]byte, 128) + key := make([]byte, prng.Intn(255)) + prng.Read(key) + encryptedPayload := make([]byte, prng.Intn(500)) prng.Read(encryptedPayload) - testMAC := MakeMAC(baseKey, encryptedPayload) - testMACBase64 := base64.StdEncoding.EncodeToString(testMAC) + testMAC := MakeMAC(key, encryptedPayload) + testMACBase64 := base64.StdEncoding.EncodeToString(testMAC) receivedMac, _ := base64.StdEncoding.DecodeString(expected) - if !VerifyMAC(baseKey, encryptedPayload, receivedMac) { + if !VerifyMAC(key, encryptedPayload, receivedMac) { t.Errorf("VerifyMAC() failed for a correct MAC (%d)."+ - "\nbase key: %s\nexpected: %s\nreceived: %s", - i, baseKey.Text(10), expected, testMACBase64) + "\nkey: %+v\nexpected: %s\nreceived: %s", + i, key, expected, testMACBase64) } } } + +// Error path: tests that bad MACs are not verified. +func TestVerifyMAC_InvalidMacError(t *testing.T) { + prng := rand.New(rand.NewSource(42)) + + for i := 0; i < 100; i++ { + key := make([]byte, prng.Intn(255)) + prng.Read(key) + encryptedPayload := make([]byte, prng.Intn(500)) + prng.Read(encryptedPayload) + expectedMac := make([]byte, prng.Intn(255)) + prng.Read(expectedMac) + + testMAC := MakeMAC(key, encryptedPayload) + testMACBase64 := base64.StdEncoding.EncodeToString(testMAC) + expectedMACBase64 := base64.StdEncoding.EncodeToString(expectedMac) + + if VerifyMAC(key, encryptedPayload, expectedMac) { + t.Errorf("VerifyMAC() verified invalid MAC (%d)."+ + "\nkey: %+v\nexpected: %s\nreceived: %s", + i, key, expectedMACBase64, testMACBase64) + } + } +} + +// getGrp returns a cyclic Group for testing. +func getGrp() *cyclic.Group { + primeString := "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088" + + "A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1" + + "4374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDE" + + "E386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA4" + + "8361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077" + + "096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E8" + + "6039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE51" + + "5D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF" + p := large.NewIntFromString(primeString, 16) + g := large.NewInt(2) + return cyclic.NewGroup(p, g) +} diff --git a/e2e/singleUse/recipientID.go b/e2e/singleUse/recipientID.go new file mode 100644 index 0000000000000000000000000000000000000000..b2ec0af3e8ba4767364a193e120f6d82232e9fe3 --- /dev/null +++ b/e2e/singleUse/recipientID.go @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +/////////////////////////////////////////////////////////////////////////////// + +package singleUse + +import ( + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/crypto/hash" + "gitlab.com/xx_network/primitives/id" +) + +// NewRecipientID generates the recipient ID for a single-use sender. The ID is +// generated from the hash of the unencrypted transmission payload. The +// unencryptedPayload must contain a nonce to prevent collision on the same +// message being sent multiple times. +func NewRecipientID(pubKey *cyclic.Int, unencryptedPayload []byte) *id.ID { + // Create new hash + h, err := hash.NewCMixHash() + if err != nil { + jww.ERROR.Panicf("Failed to create new hash for single-use "+ + "communication recipient ID: %v", err) + } + + // Hash the public key and unencrypted payload + h.Write(pubKey.Bytes()) + h.Write(unencryptedPayload) + + // Get hash bytes + rid := &id.ID{} + copy(rid[:], h.Sum(nil)) + + // Set the ID type to user + rid.SetType(id.User) + + return rid +} diff --git a/e2e/singleUse/recipientID_test.go b/e2e/singleUse/recipientID_test.go new file mode 100644 index 0000000000000000000000000000000000000000..1e03a1dfc9608779027b265b1141b9ecc8768c9e --- /dev/null +++ b/e2e/singleUse/recipientID_test.go @@ -0,0 +1,107 @@ +package singleUse + +import ( + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/crypto/diffieHellman" + "gitlab.com/xx_network/primitives/id" + "math/rand" + "testing" +) + +// Tests that the generated IDs do not change. +func TestNewRecipientID_Consistency(t *testing.T) { + expectedRIDs := []string{ + "8weTLQUZT0NXuJL6bt9BbksEt0LLywnvvHbMIO7Is0YD", + "olmvbVz3Jgu/lh60yicuTWg15neBB2i/zcd9JZVz9/8D", + "JWJAWiCARp+uLYg70YYCYB/1bsTH7Gr3M4mi2ACYhxMD", + "onYyGcAjVcwr5rwqQweVxYYnYpgWWjGhiNA/CxnxmsYD", + "nIQ/WrgPyl07IPStrcCHTe1+rWrEAxQiFtwzA+K1IMED", + "tF+cgN69Xk7fAhHSzuD2diYmMpaaCsWCr1A7+Th1WUkD", + "7UW7O94ZB94RJM5Ku4n6/BVREOO4+m7dQgqzzmpo0xgD", + "ePDyChmF5Gxr/Ny/9AWgGlPMdqymJ3ivv7bg0dMDH64D", + "f7RPMI/NHERKj6MW+bbl0bupBgnIvnOj5Xhum/ItTn8D", + "MqXY49FVJoxeuRdQAwYfswf9Li3ZnCZ9fig+EbTl7BkD", + } + prng := rand.New(rand.NewSource(42)) + + for i, expectedRID := range expectedRIDs { + privKey := diffieHellman.GeneratePrivateKey(diffieHellman.DefaultPrivateKeyLength, getGrp(), prng) + pubKey := diffieHellman.GeneratePublicKey(privKey, getGrp()) + unencryptedPayload := make([]byte, prng.Intn(500)) + prng.Read(unencryptedPayload) + + testRID := NewRecipientID(pubKey, unencryptedPayload) + + if expectedRID != testRID.String() { + t.Errorf("NewRecipientID() did not return the expected ID (%d)."+ + "\nexpected: %s\nreceived: %s", i, expectedRID, testRID) + } + + if testRID.GetType() != id.User { + t.Errorf("NewRecipientID() did not return expected ID type (%d)."+ + "\nexpected: %s\nreceived: %s", i, id.User, testRID.GetType()) + } + } +} + +// Tests that all generated IDs are unique. +func TestNewRecipientID_Unique(t *testing.T) { + testRuns := 20 + prng := rand.New(rand.NewSource(42)) + IDs := make(map[*id.ID]struct { + pubKey *cyclic.Int + encryptedPayload []byte + }) + + // Test with same public key but differing payloads + for i := 0; i < testRuns; i++ { + privKey := diffieHellman.GeneratePrivateKey(diffieHellman.DefaultPrivateKeyLength+i, getGrp(), prng) + pubKey := diffieHellman.GeneratePublicKey(privKey, getGrp()) + for j := 0; j < testRuns; j++ { + unencryptedPayload := make([]byte, prng.Intn(500)+j) + prng.Read(unencryptedPayload) + + testID := NewRecipientID(pubKey, unencryptedPayload) + + if _, exists := IDs[testID]; exists { + t.Errorf("Generated ID collides with previously generated ID (%d, %d)."+ + "\ncurrent ID: key: %s unencryptedPayload: %+v"+ + "\npreviouse ID: key: %s unencryptedPayload: %+v"+ + "\nID: %s", i, j, + pubKey.Text(10), unencryptedPayload, IDs[testID].pubKey.Text(10), + IDs[testID].encryptedPayload, testID) + } else { + IDs[testID] = struct { + pubKey *cyclic.Int + encryptedPayload []byte + }{pubKey, unencryptedPayload} + } + } + } + + // Test with same payload but differing public key + for i := 0; i < testRuns; i++ { + unencryptedPayload := make([]byte, prng.Intn(500)+i) + prng.Read(unencryptedPayload) + for j := 0; j < testRuns; j++ { + privKey := diffieHellman.GeneratePrivateKey(diffieHellman.DefaultPrivateKeyLength+j, getGrp(), prng) + pubKey := diffieHellman.GeneratePublicKey(privKey, getGrp()) + + testID := NewRecipientID(pubKey, unencryptedPayload) + + if _, exists := IDs[testID]; exists { + t.Errorf("Generated ID collides with previously generated ID (%d, %d)."+ + "\ncurrent ID: key: %s unencryptedPayload: %+v"+ + "\npreviouse ID: key: %s unencryptedPayload: %+v"+ + "\nID: %s", i, j, + pubKey.Text(10), unencryptedPayload, IDs[testID].pubKey.Text(10), + IDs[testID].encryptedPayload, testID) + } else { + IDs[testID] = struct { + pubKey *cyclic.Int + encryptedPayload []byte + }{pubKey, unencryptedPayload} + } + } + } +} diff --git a/e2e/singleUse/responseFingerprint.go b/e2e/singleUse/responseFingerprint.go index 5669053ca0a8310803ae40916541c79473efae97..37f77266f9773d8c29cd3f76d73c1b47b6ecb53d 100644 --- a/e2e/singleUse/responseFingerprint.go +++ b/e2e/singleUse/responseFingerprint.go @@ -15,30 +15,32 @@ import ( "gitlab.com/elixxir/primitives/format" ) -const responseFPConstant = "responseFPConstant" +const responseFpSalt = "singleUseResponseFingerprintSalt" -func ResponseFingerprint(pubKey *cyclic.Int, keyNum uint64) format.Fingerprint { - // Create fingerprint - fp := format.Fingerprint{} - copy(fp[:], makeHash(pubKey, keyNum, responseFPConstant)) - - return fp -} - -func makeHash(pubKey *cyclic.Int, keyNum uint64, constant string) []byte { +// NewResponseFingerprint generates the fingerprint for the response message for +// the given key number. +func NewResponseFingerprint(dhKey *cyclic.Int, keyNum uint64) format.Fingerprint { + // Create new hash h, err := hash.NewCMixHash() if err != nil { - jww.ERROR.Panicf("Failed to create hash: %v", err) + jww.ERROR.Panicf("Failed to create new hash for single-use response "+ + "fingerprint: %v", err) } - // Convert the key number to bytes - buff := make([]byte, binary.MaxVarintLen64) - binary.BigEndian.PutUint64(buff, keyNum) + keyNumBytes := make([]byte, binary.MaxVarintLen64) + binary.BigEndian.PutUint64(keyNumBytes, keyNum) - // Hash the key, number, and constant - h.Write(pubKey.Bytes()) - h.Write(buff) - h.Write([]byte(constant)) + // Hash the DH key, key number, and salt + h.Write(dhKey.Bytes()) + h.Write(keyNumBytes) + h.Write([]byte(responseFpSalt)) - return h.Sum(nil) + // Get hash bytes + fp := format.Fingerprint{} + copy(fp[:], h.Sum(nil)) + + // Set the first bit as zero to ensure everything stays in the group + fp[0] &= 0b01111111 + + return fp } diff --git a/e2e/singleUse/responseFingerprint_test.go b/e2e/singleUse/responseFingerprint_test.go index bfdb6f2b4cb6c6d02b1c669f6ba4ec5d122256f1..7db2f95d0705d8142c012d6e1bb9b2933168ab25 100644 --- a/e2e/singleUse/responseFingerprint_test.go +++ b/e2e/singleUse/responseFingerprint_test.go @@ -11,79 +11,99 @@ import ( "encoding/base64" "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/diffieHellman" + "gitlab.com/elixxir/primitives/format" "math/rand" "testing" ) // Tests that the generated fingerprints do not change. -func TestResponseFingerprint(t *testing.T) { - expectedFP := "H+GySzAGq3y6o1JA0Mqxn68GO1uSzCEmpVVbHR2sJc8=" - grp := getGrp() - prng := rand.New(rand.NewSource(42)) - - pubKey := diffieHellman.GeneratePublicKey(diffieHellman.GeneratePrivateKey( - diffieHellman.DefaultPrivateKeyLength, grp, prng), grp) - testFP := ResponseFingerprint(pubKey, 0) - testFPBase64 := base64.StdEncoding.EncodeToString(testFP[:]) - - if expectedFP != testFPBase64 { - t.Errorf("ResponseFingerprint() did not return the expected "+ - "fingerprint for public key %s."+ - "\nexpected: %s\nreceived: %s", - pubKey.Text(10), expectedFP, testFPBase64) +func TestNewResponseFingerprint_Consistency(t *testing.T) { + expectedFPs := []string{ + "OrMBolBbKdrbOYgzErZekpk4EZkoJ+rnM1NDq86aPVs=", + "KO7Ri01TBy45IQY2P21R4BAe6GAYQu86tP5JYIlkkeU=", + "KgSF7wk/VBx506dgR2DygKoCXLCErjxWHc6WoGqe5eQ=", + "OXP6eKBx/pvU6WEZW9vQPq8IcaYHBzLow8zUDtXaJGQ=", + "c/E2DCT3iCnOVDV2kBTe2BwbxsiR9dH6ImB8oPS3nFo=", + "NS/WmbI3gdwBY0qfoNt+OKavi+TFYy0GKcAXtV0gIuo=", + "fuJQu0DiFIImUYYILEtw3Jw5iFs9z/L+32RujfTVoRo=", + "LvMcRLC2XTPaV5PuRJ2RTJrQTy6L0I+FwboSLiRYnfM=", + "ZUpBiFwcfM/Geae5nyoDVvb3fgkIbZD1HlfTfN19PYI=", + "SW7r5m5FuOY/hkEr/d4ze+0RJa9iXf3xRzvyUtyL6yY=", } -} - -// Tests that the generated fingerprints do not change. -func Test_makeHash_Consistency(t *testing.T) { - expectedHashes := []string{ - "R7UL7by0fP2bXRBlzUOrUKo+7pRATF4r6bUD8/H+PBc=", - "CQsWbxgdjYOIJsdfTk/w3549Ltp7TJlSUquK94O8rAM=", - "5+pY4khsqHh0bEXZvYC2DLEJbxq4g5bL19JsllOcfso=", - "FW9gcwJAl90cLcsnxdnHLcIuJO6NpXqkVWFfbXqWXmA=", - "peyTwZZUKPwHB5J1UptEbL+TUEjvg4ZBVceE/J1Q3pQ=", - "f8YCUyFXznRHQ1gzuBJrSQegE7wC1JYb8MPTbtJ+qj0=", - "g6pHsrATAkSl7QMn9Re01nuc5VF88yvyWvPCHKm3upk=", - "S0WrlEhrMIDvJ68hhY6c0Mk5FabZqoAoUdxaBrDXdhg=", - "2mHHoyjQExs314tLMg+6pt1cGE5j4vckWZa18pPjZmQ=", - "2YMO0HeFF6dCd3cKhNQAXw76zaQ573fw0TM/3PLmUGI=", - } - grp := getGrp() prng := rand.New(rand.NewSource(42)) - for i, expectedHash := range expectedHashes { - pubKey := diffieHellman.GeneratePublicKey(diffieHellman.GeneratePrivateKey( - diffieHellman.DefaultPrivateKeyLength, grp, prng), grp) - testHash := makeHash(pubKey, uint64(i), "constant") - testHashBase64 := base64.StdEncoding.EncodeToString(testHash) + for i, expectedFP := range expectedFPs { + privKey := diffieHellman.GeneratePrivateKey(diffieHellman.DefaultPrivateKeyLength, getGrp(), prng) + pubKey := diffieHellman.GeneratePublicKey(privKey, getGrp()) + dhKey := diffieHellman.GenerateSessionKey(privKey, pubKey, getGrp()) + + testFP := NewResponseFingerprint(dhKey, uint64(i)) + testFpBase64 := base64.StdEncoding.EncodeToString(testFP[:]) - if expectedHash != testHashBase64 { - t.Errorf("makeHash() did not return the expected hash for public "+ - "key %s at index %d.\nexpected: %s\nreceived: %s", - pubKey.Text(10), i, expectedHash, testHashBase64) + if expectedFP != testFpBase64 { + t.Errorf("NewResponseFingerprint() did not return the expected "+ + "fingerprint (%d).\nexpected: %s\nreceived: %s", + i, expectedFP, testFpBase64) } } } // Tests that all generated fingerprints are unique. -func Test_makeHash_Unique(t *testing.T) { - grp := getGrp() +func TestNewResponseFingerprint_Unique(t *testing.T) { + testRuns := 20 prng := rand.New(rand.NewSource(42)) - hashes := make(map[string]*cyclic.Int, 100) + FPs := make(map[format.Fingerprint]struct { + dhKey *cyclic.Int + keyNum uint64 + }) + + // Test with same DH key but differing key numbers + for i := 0; i < testRuns; i++ { + privKey := diffieHellman.GeneratePrivateKey(diffieHellman.DefaultPrivateKeyLength+i, getGrp(), prng) + pubKey := diffieHellman.GeneratePublicKey(privKey, getGrp()) + dhKey := diffieHellman.GenerateSessionKey(privKey, pubKey, getGrp()) + for j := 0; j < testRuns; j++ { + testFP := NewResponseFingerprint(dhKey, uint64(j)) - for i := 0; i < 100; i++ { - pubKey := diffieHellman.GeneratePublicKey(diffieHellman.GeneratePrivateKey( - diffieHellman.DefaultPrivateKeyLength, grp, prng), grp) - testHash := makeHash(pubKey, uint64(i), "constant") + if _, exists := FPs[testFP]; exists { + t.Errorf("Generated fingerprint collides with previously "+ + "generated fingerprint (%d, %d)."+ + "\ncurrent FP: dhKey: %s keyNum: %d"+ + "\npreviouse FP: dhKey: %s keyNum: %d"+ + "\nFP: %s", i, j, + dhKey.Text(10), j, FPs[testFP].dhKey.Text(10), + FPs[testFP].keyNum, testFP) + } else { + FPs[testFP] = struct { + dhKey *cyclic.Int + keyNum uint64 + }{dhKey, uint64(j)} + } + } + } - hashBase64 := base64.StdEncoding.EncodeToString(testHash) + // Test with same key numbers but differing DH keys + for i := 0; i < testRuns; i++ { + for j := 0; j < testRuns; j++ { + privKey := diffieHellman.GeneratePrivateKey(diffieHellman.DefaultPrivateKeyLength+j, getGrp(), prng) + pubKey := diffieHellman.GeneratePublicKey(privKey, getGrp()) + dhKey := diffieHellman.GenerateSessionKey(privKey, pubKey, getGrp()) + testFP := NewResponseFingerprint(dhKey, uint64(i)) - if hashes[hashBase64] != nil { - t.Errorf("Generated hash from key %s collides with "+ - "previously generated hash from key %s.\nfingerprint: %s", - pubKey.Text(10), hashes[hashBase64].Text(10), hashBase64) - } else { - hashes[hashBase64] = pubKey + if _, exists := FPs[testFP]; exists { + t.Errorf("Generated fingerprint collides with previously "+ + "generated fingerprint (%d, %d)."+ + "\ncurrent FP: dhKey: %s keyNum: %d"+ + "\npreviouse FP: dhKey: %s keyNum: %d"+ + "\nFP: %s", i, j, + dhKey.Text(10), i, FPs[testFP].dhKey.Text(10), + FPs[testFP].keyNum, testFP) + } else { + FPs[testFP] = struct { + dhKey *cyclic.Int + keyNum uint64 + }{dhKey, uint64(i)} + } } } } diff --git a/e2e/singleUse/responseKey.go b/e2e/singleUse/responseKey.go index a506757b7f45788e282a3053bba49bffd91e3479..b5ada4327670845424156f6659599b92da4c1e35 100644 --- a/e2e/singleUse/responseKey.go +++ b/e2e/singleUse/responseKey.go @@ -8,11 +8,32 @@ package singleUse import ( + "encoding/binary" + jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/crypto/hash" ) -const responseKeyConstant = "responseKeyConstant" +const responseKeySalt = "singleUseResponseKeySalt" -func ResponseKey(pubKey *cyclic.Int, keyNum uint64) []byte { - return makeHash(pubKey, keyNum, responseKeyConstant) +// NewResponseKey generates the key for the response message that corresponds +// with the given key number. +func NewResponseKey(dhKey *cyclic.Int, keyNum uint64) []byte { + // Create new hash + h, err := hash.NewCMixHash() + if err != nil { + jww.ERROR.Panicf("Failed to create new hash for single-use response "+ + "key: %v", err) + } + + keyNumBytes := make([]byte, binary.MaxVarintLen64) + binary.BigEndian.PutUint64(keyNumBytes, keyNum) + + // Hash the DH key, key number, and salt + h.Write(dhKey.Bytes()) + h.Write(keyNumBytes) + h.Write([]byte(responseKeySalt)) + + // Get hash bytes + return h.Sum(nil) } diff --git a/e2e/singleUse/responseKey_test.go b/e2e/singleUse/responseKey_test.go index 424eee618d697621a03a18ac07b099ee58e61d77..5a981f979f5bd5f28bf84bea91333f62d45feca8 100644 --- a/e2e/singleUse/responseKey_test.go +++ b/e2e/singleUse/responseKey_test.go @@ -9,25 +9,101 @@ package singleUse import ( "encoding/base64" + "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/diffieHellman" "math/rand" "testing" ) // Tests that the generated key does not change. -func TestResponseKey(t *testing.T) { - expectedKey := "6H61xTtJZYhmT8Q2cm3ZXj7J95yHO5lQN/s1cRu/j40=" - grp := getGrp() +func TestNewResponseKey_Consistency(t *testing.T) { + expectedKeys := []string{ + "gMv3mjIgCpTgqx/1d0+k3jYlfkPTM2DtOGYzUKGHwK4=", + "k5E9tfudE5W8APDNvZ9XfHuxuivsefcP3IhOBACh7Mg=", + "iqQrsHI7bmJGxqHC6bUqXgt03VeGSHHSRBGZ0RrEvP8=", + "+taXBcQ+4IB00gvkjQXia6Y6BJJ05vYaO2Ou+8yTd+M=", + "PiaH5qrVUdRCKwyf2Kic/M8rqm+q4UNvoVmFFI51k8Y=", + "jpKUMoteRvQVJnDsTi4pjw0XL5P4U5rhWaK+0RaKVVw=", + "2zx20KGB9xBObQ8cwF3FzpCwApiV8Du/FL2+Ykmh/ck=", + "hVYAGYQtd3x0FgXuavULI8saisTjs4rJamebd5aNzQ8=", + "zN+Cl7h5KvFGcGPLUVZ0Qjiws5LNUXXhuDUUw6osSfU=", + "MVzZoMffLm7Bi7XAuOZxYFxAzeNqxUAG6kZCSCKJuDE=", + } prng := rand.New(rand.NewSource(42)) - pubKey := diffieHellman.GeneratePublicKey(diffieHellman.GeneratePrivateKey( - diffieHellman.DefaultPrivateKeyLength, grp, prng), grp) - testKey := ResponseKey(pubKey, 0) - testKeyBase64 := base64.StdEncoding.EncodeToString(testKey) + for i, expectedKey := range expectedKeys { + privKey := diffieHellman.GeneratePrivateKey(diffieHellman.DefaultPrivateKeyLength, getGrp(), prng) + pubKey := diffieHellman.GeneratePublicKey(privKey, getGrp()) + dhKey := diffieHellman.GenerateSessionKey(privKey, pubKey, getGrp()) + + testKey := NewResponseKey(dhKey, uint64(i)) + testKeyBase64 := base64.StdEncoding.EncodeToString(testKey) + + if expectedKey != testKeyBase64 { + t.Errorf("NewResponseKey() did not return the expected key (%d)."+ + "\nexpected: %s\nreceived: %s", i, expectedKey, testKeyBase64) + } + } +} + +// Tests that all generated keys are unique. +func TestNewResponseKey_Unique(t *testing.T) { + testRuns := 20 + prng := rand.New(rand.NewSource(42)) + keys := make(map[string]struct { + dhKey *cyclic.Int + keyNum uint64 + }) + + // Test with same DH key but differing key numbers + for i := 0; i < testRuns; i++ { + privKey := diffieHellman.GeneratePrivateKey(diffieHellman.DefaultPrivateKeyLength+i, getGrp(), prng) + pubKey := diffieHellman.GeneratePublicKey(privKey, getGrp()) + dhKey := diffieHellman.GenerateSessionKey(privKey, pubKey, getGrp()) + for j := 0; j < testRuns; j++ { + testKey := NewResponseKey(dhKey, uint64(j)) + testKeyBase64 := base64.StdEncoding.EncodeToString(testKey) + + if _, exists := keys[testKeyBase64]; exists { + t.Errorf("Generated key collides with previously generated "+ + "key (%d, %d)."+ + "\ncurrent key: dhKey: %s keyNum: %d"+ + "\npreviouse key: dhKey: %s keyNum: %d"+ + "\nkey: %s", i, j, + dhKey.Text(10), j, keys[testKeyBase64].dhKey.Text(10), + keys[testKeyBase64].keyNum, testKeyBase64) + } else { + keys[testKeyBase64] = struct { + dhKey *cyclic.Int + keyNum uint64 + }{dhKey, uint64(j)} + } + } + } + + // Test with same key number but differing DH keys + for i := 0; i < testRuns; i++ { + for j := 0; j < testRuns; j++ { + privKey := diffieHellman.GeneratePrivateKey(diffieHellman.DefaultPrivateKeyLength+j, getGrp(), prng) + pubKey := diffieHellman.GeneratePublicKey(privKey, getGrp()) + dhKey := diffieHellman.GenerateSessionKey(privKey, pubKey, getGrp()) + testKey := NewResponseKey(dhKey, uint64(i)) + testKeyBase64 := base64.StdEncoding.EncodeToString(testKey) - if expectedKey != testKeyBase64 { - t.Errorf("ResponseKey() did not return the expected key for public "+ - "key %s.\nexpected: %s\nreceived: %s", - pubKey.Text(10), expectedKey, testKeyBase64) + if _, exists := keys[testKeyBase64]; exists { + t.Errorf("Generated key collides with previously generated "+ + "key (%d, %d)."+ + "\ncurrent key: dhKey: %s keyNum: %d"+ + "\npreviouse key: dhKey: %s keyNum: %d"+ + "\nkey: %s", i, j, + dhKey.Text(10), i, keys[testKeyBase64].dhKey.Text(10), + keys[testKeyBase64].keyNum, testKeyBase64) + } else { + keys[testKeyBase64] = struct { + dhKey *cyclic.Int + keyNum uint64 + }{dhKey, uint64(i)} + } + } } } diff --git a/e2e/singleUse/tagFingerprint.go b/e2e/singleUse/tagFingerprint.go new file mode 100644 index 0000000000000000000000000000000000000000..ac45059738f8baaf6348a4ea6bf973dcee3144dc --- /dev/null +++ b/e2e/singleUse/tagFingerprint.go @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +/////////////////////////////////////////////////////////////////////////////// + +package singleUse + +import ( + "encoding/base64" + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/crypto/hash" +) + +const tagFpSalt = "singleUseTagFingerprintSalt" +const TagFpSize = 16 + +type TagFP [TagFpSize]byte + +// NewTagFP generates the tag fingerprint used to identify the module that the +// transmission message belongs to. The tag can be anything, but should be long +// enough so that it is unique. +func NewTagFP(tag string) TagFP { + // Create new hash + h, err := hash.NewCMixHash() + if err != nil { + jww.ERROR.Panicf("Failed to create new hash for single-use "+ + "communication tag fingerprint: %v", err) + } + + // Hash tag and salt + h.Write([]byte(tag)) + h.Write([]byte(tagFpSalt)) + + // Get hash bytes + return UnmarshalTagFP(h.Sum(nil)) +} + +// UnmarshalTagFP generates a new TagFP from the specified bytes. +func UnmarshalTagFP(b []byte) TagFP { + var tagFp TagFP + copy(tagFp[:], b[:]) + return tagFp +} + +// Bytes returns the tag fingerprint as a byte slice. +func (fp TagFP) Bytes() []byte { + return fp[:] +} + +// String returns the base64 string encoding of the tag fingerprint. +func (fp TagFP) String() string { + return base64.StdEncoding.EncodeToString(fp[:]) +} diff --git a/e2e/singleUse/tagFingerprint_test.go b/e2e/singleUse/tagFingerprint_test.go new file mode 100644 index 0000000000000000000000000000000000000000..7e6289b239d91b89707d18cb5024ddd661c3f87c --- /dev/null +++ b/e2e/singleUse/tagFingerprint_test.go @@ -0,0 +1,120 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +/////////////////////////////////////////////////////////////////////////////// + +package singleUse + +import ( + "bytes" + "encoding/base64" + "math/rand" + "testing" +) + +// Tests that the generated tag fingerprint does not change. +func TestNewTagFP_Consistency(t *testing.T) { + expectedFPs := []string{ + "m0cTfRUOrBSMCcsuDFubEA==", + "n2wr2FhimigxeaKMQwoOww==", + "gQFbF80cFWmRFJCKTcWAtg==", + "6B330SPGBjmFIoBwkdkqhg==", + "a+AuHpTbVLxbO/wNS6QPAg==", + "Npgjn6oPpzAgDe2gb3VPHQ==", + "vg0K64iRRVFdNs181ToEtg==", + "w9LCSCCo76301pzLKGpvVQ==", + "/7ZRLcScR6xlAkjPLHy3rg==", + "yV37d2Zs0X+OJp9AlRWhjQ==", + } + prng := rand.New(rand.NewSource(42)) + + for i, expectedFP := range expectedFPs { + tag := make([]byte, prng.Intn(255)) + prng.Read(tag) + + testFP := NewTagFP(string(tag)) + + if expectedFP != testFP.String() { + t.Errorf("NewTagFP() did not return the expected fingerprint (%d)."+ + "\nexpected: %s\nreceived: %s", i, expectedFP, testFP) + } + } +} + +// Tests that all generated tag fingerprints are unique. +func TestNewTagFP_Unique(t *testing.T) { + testRuns := 20 + prng := rand.New(rand.NewSource(42)) + FPs := make(map[TagFP]string) + + // Test with differing tag strings + for i := 0; i < testRuns; i++ { + tag := make([]byte, prng.Intn(255)+i) + prng.Read(tag) + testTagFP := NewTagFP(string(tag)) + + if _, exists := FPs[testTagFP]; exists { + t.Errorf("Generated tag fingerprint collides with previously "+ + "generated tag fingerprint (%d)."+ + "\ncurrent tag: %s\npreviouse tag: %s\nFP: %s", + i, tag, FPs[testTagFP], testTagFP) + } else { + FPs[testTagFP] = string(tag) + } + } +} + +// Happy path. +func TestUnmarshalTagFP(t *testing.T) { + prng := rand.New(rand.NewSource(42)) + fpBytes := make([]byte, TagFpSize) + prng.Read(fpBytes) + + fp := UnmarshalTagFP(fpBytes) + if !bytes.Equal(fpBytes, fp[:]) { + t.Errorf("UnmarshalTagFP() failed to copy the correct bytes into the tag "+ + "fingerprint.\nexpected: %+v\nreceived: %+v", fpBytes, fp) + } + + // Ensure that the data is copied + fpBytes[2]++ + if fp[2] == fpBytes[2] { + t.Errorf("UnmarshalTagFP() failed to create a copy of the data.") + } +} + +// Happy path. +func TestTagFP_Bytes(t *testing.T) { + prng := rand.New(rand.NewSource(42)) + fpBytes := make([]byte, TagFpSize) + prng.Read(fpBytes) + + fp := UnmarshalTagFP(fpBytes) + testFpBytes := fp.Bytes() + if !bytes.Equal(fpBytes, testFpBytes) { + t.Errorf("Bytes() failed to return the expected bytes."+ + "\nexpected: %+v\nreceived: %+v", fpBytes, testFpBytes) + } + + // Ensure that the data is copied + testFpBytes[2]++ + if fp[2] == testFpBytes[2] { + t.Errorf("Bytes() failed to create a copy of the data.") + } +} + +// Happy path. +func TestTagFP_String(t *testing.T) { + prng := rand.New(rand.NewSource(42)) + fpBytes := make([]byte, TagFpSize) + prng.Read(fpBytes) + fp := UnmarshalTagFP(fpBytes) + + expectedString := base64.StdEncoding.EncodeToString(fpBytes) + if expectedString != fp.String() { + t.Errorf("String() failed to return the expected string."+ + "\nexpected: %s\nreceived: %s", expectedString, fp.String()) + } +} diff --git a/e2e/singleUse/transmitFingerprint.go b/e2e/singleUse/transmitFingerprint.go index 1c4aaa8ece5db4b08811cd9b4b1f866d4230b8b5..3969e42d469844ef118dde46acd008991336cab8 100644 --- a/e2e/singleUse/transmitFingerprint.go +++ b/e2e/singleUse/transmitFingerprint.go @@ -14,23 +14,28 @@ import ( "gitlab.com/elixxir/primitives/format" ) -const transmitFPConstant = "transmitFPConstant" +const transmitFpSalt = "singleUseTransmitFingerprintSalt" -func TransmitFingerprint(pubKey *cyclic.Int) format.Fingerprint { +// NewTransmitFingerprint generates the fingerprint used for the transmission +// message. +func NewTransmitFingerprint(pubKey *cyclic.Int) format.Fingerprint { // Create new hash h, err := hash.NewCMixHash() if err != nil { - jww.ERROR.Panicf("Failed to create hash: %v", err) + jww.ERROR.Panicf("Failed to create new hash for single-use "+ + "transmission fingerprint: %v", err) } - // Hash the key and constant + // Hash the public key and salt h.Write(pubKey.Bytes()) - h.Write([]byte(transmitFPConstant)) - keyHash := h.Sum(nil) + h.Write([]byte(transmitFpSalt)) - // Create fingerprint + // Get hash bytes fp := format.Fingerprint{} - copy(fp[:], keyHash) + copy(fp[:], h.Sum(nil)) + + // Set the first bit as zero to ensure everything stays in the group + fp[0] &= 0b01111111 return fp } diff --git a/e2e/singleUse/transmitFingerprint_test.go b/e2e/singleUse/transmitFingerprint_test.go index a0d8787bbc1de700ac865de5a64fd4aa903b8cba..3d4676ea339cd556a3f5a2780f4e3c6682375db2 100644 --- a/e2e/singleUse/transmitFingerprint_test.go +++ b/e2e/singleUse/transmitFingerprint_test.go @@ -12,75 +12,59 @@ import ( "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/diffieHellman" "gitlab.com/elixxir/primitives/format" - "gitlab.com/xx_network/crypto/large" "math/rand" "testing" ) // Tests that the generated fingerprints do not change. -func TestTransmitFingerprint_Consistency(t *testing.T) { +func TestNewTransmitFingerprint_Consistency(t *testing.T) { expectedFPs := []string{ - "8xef7y86YmR6+qVBaVghPYbEd0j7seLUxR1v1dxqiNo=", - "LJ1HQ8zZPY3z+6UkKZMSDN2WymMmwuWW3GCjtAGfUlc=", - "Fc8CN69uMxT1zBAr3Ed/AOy6Py3XDb3i7LngRsax0K8=", - "4mvA+dxgIy/UxRVIvzFgGtEuMoWI8RodF7JMFIO5xcI=", - "3YJPhLeI2+RawlSMUTEwTP1iJrFAa5zboCf5fjc/k3o=", - "mEOS0pDuNYS5hpNHeJ2IhzlZFf9J5oHRvFpovNwWS7c=", - "i3k7dv1Nch1oTT4aOxbBDifmf7+mT34ErFFaWW5LDb8=", - "ck/wkZ3lv0bcQuW+Z2dw37m+Re0URPw4UsWBPAWBEtw=", - "k/YRXaE2dplVPZSiEY+7C0e6GlDeR3jLrIPRWgeL3cI=", - "qkT2Hfz8A7jNxOhDYG266b/hvWcQtAx0ay67Az80bAY=", + "Esl9VY4LNOmXqmmEpZfu0Koo/++Zqx/vDSqBFFpdvjI=", + "ASz49SEnYLLW7KkVLbHJiYOAlkkY1r/AJBHaR2s1UDk=", + "ZxN1S6SBESGAB+2LpKQv18rXqVtwANe19AK2dXckpiM=", + "CNZUU0N7gmkjg+/rCZZaZz3NNfLA8zQychvFuhdBa6s=", + "blMhqREthbMo9HInS4gq85k4oLOq4L5WlT+U1yFGfYQ=", + "b//RiQzsN1BG6pYufzZFmRyrkHP5f4+GaYIutfUZlNw=", + "VRbcFvAMFf8SvYLGf2aL9jlenwNG45BzZbuhFbrOZ9Y=", + "TiQPO2mHY4Qv/Kr+jqMDiokIWoE6HSn5exlwQw7iK+Y=", + "NG21WIBMqCTB4zZAb6CWvQ+B0080zh5e4BQ9Xzh52cI=", + "TrA7y8S2NayhqYWPyZDs5q+rC+mlo/CXBt3P6bsmGwQ=", } - grp := getGrp() prng := rand.New(rand.NewSource(42)) - for i, expected := range expectedFPs { - pubKey := diffieHellman.GeneratePublicKey(diffieHellman.GeneratePrivateKey( - diffieHellman.DefaultPrivateKeyLength, grp, prng), grp) - testFP := TransmitFingerprint(pubKey) - testFPBase64 := base64.StdEncoding.EncodeToString(testFP[:]) + for i, expectedFP := range expectedFPs { + privKey := diffieHellman.GeneratePrivateKey(diffieHellman.DefaultPrivateKeyLength, getGrp(), prng) + pubKey := diffieHellman.GeneratePublicKey(privKey, getGrp()) - if expected != testFPBase64 { - t.Errorf("TransmitFingerprint() did not return the expected "+ - "fingerprint for public key %s at index %d."+ - "\nexpected: %s\nreceived: %s", - pubKey.Text(10), i, expected, testFPBase64) + testFP := NewTransmitFingerprint(pubKey) + testFpBase64 := base64.StdEncoding.EncodeToString(testFP[:]) + + if expectedFP != testFpBase64 { + t.Errorf("NewTransmitFingerprint() did not return the expected "+ + "fingerprint (%d).\nexpected: %s\nreceived: %s", + i, expectedFP, testFpBase64) } } } // Tests that all generated fingerprints are unique. -func TestTransmitFingerprint_Unique(t *testing.T) { - grp := getGrp() +func TestNewTransmitFingerprint_Unique(t *testing.T) { + testRuns := 20 prng := rand.New(rand.NewSource(42)) - FPs := make(map[format.Fingerprint]*cyclic.Int, 100) + FPs := make(map[format.Fingerprint]*cyclic.Int) - for i := 0; i < 100; i++ { - pubKey := diffieHellman.GeneratePublicKey(diffieHellman.GeneratePrivateKey( - diffieHellman.DefaultPrivateKeyLength, grp, prng), grp) - testFP := TransmitFingerprint(pubKey) + for i := 0; i < testRuns; i++ { + privKey := diffieHellman.GeneratePrivateKey(diffieHellman.DefaultPrivateKeyLength, getGrp(), prng) + pubKey := diffieHellman.GeneratePublicKey(privKey, getGrp()) + testFP := NewTransmitFingerprint(pubKey) if FPs[testFP] != nil { t.Errorf("Generated fingerprint from key %s collides with "+ "previously generated fingerprint from key %s."+ "\nfingerprint: %s", pubKey.Text(10), FPs[testFP].Text(10), - base64.StdEncoding.EncodeToString(testFP[:])) + testFP) } else { FPs[testFP] = pubKey } } } - -func getGrp() *cyclic.Group { - primeString := "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088" + - "A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1" + - "4374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDE" + - "E386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA4" + - "8361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077" + - "096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E8" + - "6039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE51" + - "5D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF" - p := large.NewIntFromString(primeString, 16) - g := large.NewInt(2) - return cyclic.NewGroup(p, g) -} diff --git a/e2e/singleUse/transmitKey.go b/e2e/singleUse/transmitKey.go new file mode 100644 index 0000000000000000000000000000000000000000..038b75efe6a8dcfca8473ee4691c24072fe6c03f --- /dev/null +++ b/e2e/singleUse/transmitKey.go @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +/////////////////////////////////////////////////////////////////////////////// + +package singleUse + +import ( + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/crypto/hash" +) + +const transmitKeySalt = "singleUseTransmitKeySalt" + +// NewTransmitKey generates the key used for the transmission message. +func NewTransmitKey(dhKey *cyclic.Int) []byte { + // Create new hash + h, err := hash.NewCMixHash() + if err != nil { + jww.ERROR.Panicf("Failed to create new hash for single-use "+ + "communication transmission key: %v", err) + } + + // Hash the DH key and salt + h.Write(dhKey.Bytes()) + h.Write([]byte(transmitKeySalt)) + + // Get hash bytes + return h.Sum(nil) +} diff --git a/e2e/singleUse/transmitKey_test.go b/e2e/singleUse/transmitKey_test.go new file mode 100644 index 0000000000000000000000000000000000000000..52f182a8ae8f1923447d5d7b2557649518396a38 --- /dev/null +++ b/e2e/singleUse/transmitKey_test.go @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +/////////////////////////////////////////////////////////////////////////////// + +package singleUse + +import ( + "encoding/base64" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/crypto/diffieHellman" + "math/rand" + "testing" +) + +// Tests that the generated keys do not change. +func TestNewTransmitKey_Consistency(t *testing.T) { + expectedKeys := []string{ + "4/q8XevjJmz2ectxCr/NseWS9cSzVD/SUW36m/gk5iA=", + "zdF5z3ouPBE1LX8HGuKmlif9QXoURoB8SYvJ/hmrD78=", + "kWfO8yo6AXWsfWlo5hZGFei/MdU6kjQO/Q5+tW8CH6A=", + "fFnJJTooPyT489Z6DDmXviKvO3ANO/go+SK9bsw47J8=", + "ea0K4bAH6j6svv62OF0/xck/V6fsHAzGqK3+gYshfsY=", + "L8YsjbsdUcOjYTthWDXDDD0GwizPxCMtkuYOSdNEvxw=", + "zcJiH/vhbj9fGwp1FIIqdTwU9zwgLC1vv13NKjpznUc=", + "9Tg08QAHOcPR9HmPmKOTkMetLdGYF16VJ0NT62o93aQ=", + "8ky3KxQGlf6+6W/XZd8hV++sfMFdiTDI+XGMJXGck/A=", + "Jzz96sW79uil/WCnY+8wgH5fDstCbKGs+H7Fd5+olW8=", + } + prng := rand.New(rand.NewSource(42)) + + for i, expectedKey := range expectedKeys { + privKey := diffieHellman.GeneratePrivateKey(diffieHellman.DefaultPrivateKeyLength, getGrp(), prng) + pubKey := diffieHellman.GeneratePublicKey(privKey, getGrp()) + dhKey := diffieHellman.GenerateSessionKey(privKey, pubKey, getGrp()) + + testKey := NewTransmitKey(dhKey) + testKeyBase64 := base64.StdEncoding.EncodeToString(testKey) + + if expectedKey != testKeyBase64 { + t.Errorf("NewTransmitKey() did not return the expected key (%d)."+ + "\nexpected: %s\nreceived: %s", i, expectedKey, testKeyBase64) + } + } +} + +// Tests that all generated keys are unique. +func TestNewTransmitKey_Unique(t *testing.T) { + testRuns := 20 + prng := rand.New(rand.NewSource(42)) + keys := make(map[string]*cyclic.Int, 100) + + for i := 0; i < testRuns; i++ { + privKey := diffieHellman.GeneratePrivateKey(diffieHellman.DefaultPrivateKeyLength, getGrp(), prng) + pubKey := diffieHellman.GeneratePublicKey(privKey, getGrp()) + dhKey := diffieHellman.GenerateSessionKey(privKey, pubKey, getGrp()) + + testKey := base64.StdEncoding.EncodeToString(NewTransmitKey(dhKey)) + + if keys[testKey] != nil { + t.Errorf("Generated fingerprint from key %s collides with "+ + "previously generated fingerprint from key %s.\nfingerprint: %s", + dhKey.Text(10), keys[testKey].Text(10), testKey) + } else { + keys[testKey] = dhKey + } + } +} diff --git a/go.mod b/go.mod index bb774233bce42170f1692be61beb864d1fdf4448..ff4e99973af78257ea3681fa0337865e760874fd 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,4 @@ require ( gitlab.com/xx_network/crypto v0.0.5-0.20210107183440-804e0f8b7d22 gitlab.com/xx_network/primitives v0.0.4-0.20210106014326-691ebfca3b07 golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de - golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 // indirect - golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 // indirect - honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc // indirect ) diff --git a/go.sum b/go.sum index 0099d5257aa53f19746f2a260b51d546ab7f4908..9a576c91f188c7f0e3ef3111066bb7521e38553d 100644 --- a/go.sum +++ b/go.sum @@ -79,6 +79,8 @@ gitlab.com/elixxir/primitives v0.0.3-0.20210106001742-69578b457afa h1:qjuFd1G1v8 gitlab.com/elixxir/primitives v0.0.3-0.20210106001742-69578b457afa/go.mod h1:RigMLE2qLcqgRcPFUHRmA1ChSRVf5AuvKLFYxrJr6is= gitlab.com/elixxir/primitives v0.0.3-0.20210107183456-9cf6fe2de1e5 h1:50HbCJWirpX2Q+NNhIHcs0M9f45H1UJ/7LNMu81Bnn0= gitlab.com/elixxir/primitives v0.0.3-0.20210107183456-9cf6fe2de1e5/go.mod h1:Ph6isHUDVjmRUd9DioyKpd8W9J52gKBiDeue4DCygXA= +gitlab.com/elixxir/primitives v0.0.3-0.20210115234332-ea62d7ec1b4b h1:YzrIpXBU/DvBZEvzGQc0h7CFZ3XLbmNl6TdpJeqTvJM= +gitlab.com/elixxir/primitives v0.0.3-0.20210115234332-ea62d7ec1b4b/go.mod h1:Ph6isHUDVjmRUd9DioyKpd8W9J52gKBiDeue4DCygXA= gitlab.com/xx_network/comms v0.0.0-20200805174823-841427dd5023/go.mod h1:owEcxTRl7gsoM8c3RQ5KAm5GstxrJp5tn+6JfQ4z5Hw= gitlab.com/xx_network/comms v0.0.4-0.20201130190834-365ddae56e7b/go.mod h1:YViGbRj7FjJYoaO4NpALGEd9dK/l8uUT000FEBbUTL8= gitlab.com/xx_network/crypto v0.0.3 h1:MDQX71yKLLGC9cgvp9gzJ5b4bShPeYuoqk9tFsCjqIE=