diff --git a/e2e/singleUse/mac.go b/e2e/singleUse/mac.go new file mode 100644 index 0000000000000000000000000000000000000000..649339f16d639629402e69535abf27324ad02b5d --- /dev/null +++ b/e2e/singleUse/mac.go @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////// +// 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" + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/crypto/hash" +) + +const macConstant = "macConstant" + +// MakeMAC generates the MAC for the given base key and encrypted payload. +func MakeMAC(baseKey *cyclic.Int, encryptedPayload []byte) []byte { + h, err := hash.NewCMixHash() + if err != nil { + jww.ERROR.Panicf("Failed to create hash: %v", err) + } + + // Hash the key, number, and constant + h.Write(baseKey.Bytes()) + h.Write(encryptedPayload) + h.Write([]byte(macConstant)) + + return h.Sum(nil) +} + +// VerifyMAC determines if the provided MAC is valid for the given base key and +// encrypted payload. +func VerifyMAC(baseKey *cyclic.Int, encryptedPayload, receivedMAC []byte) bool { + newMAC := MakeMAC(baseKey, encryptedPayload) + + return bytes.Equal(newMAC, receivedMAC) +} diff --git a/e2e/singleUse/mac_test.go b/e2e/singleUse/mac_test.go new file mode 100644 index 0000000000000000000000000000000000000000..1817a33fd9d2403acd64d47fd9a6f6a51e765352 --- /dev/null +++ b/e2e/singleUse/mac_test.go @@ -0,0 +1,123 @@ +/////////////////////////////////////////////////////////////////////////////// +// 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 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=", + } + 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) + prng.Read(encryptedPayload) + testMAC := MakeMAC(baseKey, 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) + } + } +} + +// Tests that all generated MACs are unique. +func TestMAC_Unique(t *testing.T) { + grp := getGrp() + prng := rand.New(rand.NewSource(42)) + MACs := make(map[string]struct { + key *cyclic.Int + 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) + prng.Read(encryptedPayload) + testMAC := MakeMAC(baseKey, encryptedPayload) + testMACBase64 := base64.StdEncoding.EncodeToString(testMAC) + + 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} + } + } +} + +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=", + } + 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) + prng.Read(encryptedPayload) + testMAC := MakeMAC(baseKey, encryptedPayload) + testMACBase64 := base64.StdEncoding.EncodeToString(testMAC) + + receivedMac, _ := base64.StdEncoding.DecodeString(expected) + + if !VerifyMAC(baseKey, encryptedPayload, receivedMac) { + t.Errorf("VerifyMAC() failed for a correct MAC (%d)."+ + "\nbase key: %s\nexpected: %s\nreceived: %s", + i, baseKey.Text(10), expected, testMACBase64) + } + } +} diff --git a/e2e/singleUse/responseFingerprint.go b/e2e/singleUse/responseFingerprint.go new file mode 100644 index 0000000000000000000000000000000000000000..5669053ca0a8310803ae40916541c79473efae97 --- /dev/null +++ b/e2e/singleUse/responseFingerprint.go @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////// +// 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/binary" + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/crypto/hash" + "gitlab.com/elixxir/primitives/format" +) + +const responseFPConstant = "responseFPConstant" + +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 { + h, err := hash.NewCMixHash() + if err != nil { + jww.ERROR.Panicf("Failed to create hash: %v", err) + } + + // Convert the key number to bytes + buff := make([]byte, binary.MaxVarintLen64) + binary.BigEndian.PutUint64(buff, keyNum) + + // Hash the key, number, and constant + h.Write(pubKey.Bytes()) + h.Write(buff) + h.Write([]byte(constant)) + + return h.Sum(nil) +} diff --git a/e2e/singleUse/responseFingerprint_test.go b/e2e/singleUse/responseFingerprint_test.go new file mode 100644 index 0000000000000000000000000000000000000000..bfdb6f2b4cb6c6d02b1c669f6ba4ec5d122256f1 --- /dev/null +++ b/e2e/singleUse/responseFingerprint_test.go @@ -0,0 +1,89 @@ +/////////////////////////////////////////////////////////////////////////////// +// 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 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) + } +} + +// 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) + + 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) + } + } +} + +// Tests that all generated fingerprints are unique. +func Test_makeHash_Unique(t *testing.T) { + grp := getGrp() + prng := rand.New(rand.NewSource(42)) + hashes := make(map[string]*cyclic.Int, 100) + + for i := 0; i < 100; i++ { + pubKey := diffieHellman.GeneratePublicKey(diffieHellman.GeneratePrivateKey( + diffieHellman.DefaultPrivateKeyLength, grp, prng), grp) + testHash := makeHash(pubKey, uint64(i), "constant") + + hashBase64 := base64.StdEncoding.EncodeToString(testHash) + + 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 + } + } +} diff --git a/e2e/singleUse/responseKey.go b/e2e/singleUse/responseKey.go new file mode 100644 index 0000000000000000000000000000000000000000..a506757b7f45788e282a3053bba49bffd91e3479 --- /dev/null +++ b/e2e/singleUse/responseKey.go @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////// +// 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 ( + "gitlab.com/elixxir/crypto/cyclic" +) + +const responseKeyConstant = "responseKeyConstant" + +func ResponseKey(pubKey *cyclic.Int, keyNum uint64) []byte { + return makeHash(pubKey, keyNum, responseKeyConstant) +} diff --git a/e2e/singleUse/responseKey_test.go b/e2e/singleUse/responseKey_test.go new file mode 100644 index 0000000000000000000000000000000000000000..424eee618d697621a03a18ac07b099ee58e61d77 --- /dev/null +++ b/e2e/singleUse/responseKey_test.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 ( + "encoding/base64" + "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() + 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) + + 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) + } +} diff --git a/e2e/singleUse/transmitFingerprint.go b/e2e/singleUse/transmitFingerprint.go new file mode 100644 index 0000000000000000000000000000000000000000..1c4aaa8ece5db4b08811cd9b4b1f866d4230b8b5 --- /dev/null +++ b/e2e/singleUse/transmitFingerprint.go @@ -0,0 +1,36 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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/elixxir/primitives/format" +) + +const transmitFPConstant = "transmitFPConstant" + +func TransmitFingerprint(pubKey *cyclic.Int) format.Fingerprint { + // Create new hash + h, err := hash.NewCMixHash() + if err != nil { + jww.ERROR.Panicf("Failed to create hash: %v", err) + } + + // Hash the key and constant + h.Write(pubKey.Bytes()) + h.Write([]byte(transmitFPConstant)) + keyHash := h.Sum(nil) + + // Create fingerprint + fp := format.Fingerprint{} + copy(fp[:], keyHash) + + return fp +} diff --git a/e2e/singleUse/transmitFingerprint_test.go b/e2e/singleUse/transmitFingerprint_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a0d8787bbc1de700ac865de5a64fd4aa903b8cba --- /dev/null +++ b/e2e/singleUse/transmitFingerprint_test.go @@ -0,0 +1,86 @@ +/////////////////////////////////////////////////////////////////////////////// +// 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" + "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) { + 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=", + } + 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[:]) + + 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) + } + } +} + +// Tests that all generated fingerprints are unique. +func TestTransmitFingerprint_Unique(t *testing.T) { + grp := getGrp() + prng := rand.New(rand.NewSource(42)) + FPs := make(map[format.Fingerprint]*cyclic.Int, 100) + + for i := 0; i < 100; i++ { + pubKey := diffieHellman.GeneratePublicKey(diffieHellman.GeneratePrivateKey( + diffieHellman.DefaultPrivateKeyLength, grp, prng), grp) + testFP := TransmitFingerprint(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[:])) + } 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) +}