From 1d434a4ca852e83ba27dc0a7a72f19f444d2ee67 Mon Sep 17 00:00:00 2001 From: joshemb <josh@elixxir.io> Date: Tue, 24 Jan 2023 13:11:08 -0800 Subject: [PATCH] Revert "Propagate moving of rsa/cyclic/hash packages" This reverts commit 9f11137488138301add52508ee7819a90d677868. --- authorize/authorize.go | 2 +- authorize/authorize_test.go | 8 +- authorize/certRequest.go | 4 +- authorize/certRequest_test.go | 2 +- backup/backup.go | 2 +- broadcast/asymmetric.go | 2 +- broadcast/asymmetric_test.go | 2 +- broadcast/channel.go | 2 +- broadcast/channel_test.go | 2 +- broadcast/exportPrivateKey.go | 2 +- broadcast/intermediary.go | 2 +- broadcast/intermediary_test.go | 2 +- broadcast/keysize.go | 2 +- broadcast/keysize_test.go | 2 +- broadcast/mac.go | 2 +- broadcast/symmetric_test.go | 2 +- channel/database.go | 2 +- channel/request.go | 2 +- channel/request_test.go | 2 +- cmix/clientGateway.go | 4 +- cmix/encryptDecrypt.go | 2 +- cmix/key.go | 4 +- cmix/key_test.go | 2 +- cmix/kmac.go | 2 +- cmix/kmac_test.go | 14 +- cmix/setGroupBits.go | 2 +- codename/identity.go | 2 +- contact/contact.go | 2 +- contact/contact_test.go | 2 +- contact/unmarshalVersion.go | 2 +- cyclic/buffer.go | 66 + cyclic/buffer_test.go | 206 ++ cyclic/group.go | 638 ++++++ cyclic/group_test.go | 2069 ++++++++++++++++++ cyclic/int.go | 262 +++ cyclic/int_test.go | 363 +++ diffieHellman/dhkx.go | 2 +- diffieHellman/dhkx_test.go | 8 +- dm/selfCipher.go | 2 +- e2e/auth/encryptDecrypt.go | 4 +- e2e/auth/keygen.go | 4 +- e2e/auth/mac.go | 2 +- e2e/auth/negotiationFingerprint.go | 4 +- e2e/auth/negotiationFingerprint_test.go | 2 +- e2e/auth/ownership.go | 4 +- e2e/auth/ownership_test.go | 2 +- e2e/auth/requestFP.go | 8 +- e2e/dummykeygen.go | 4 +- e2e/dummykeygen_test.go | 2 +- e2e/encryptionChecker.go | 2 +- e2e/encryptionChecker_test.go | 2 +- e2e/keys.go | 2 +- e2e/keys_test.go | 8 +- e2e/messageID.go | 2 +- e2e/relationshipFingerprint.go | 4 +- e2e/relationshipFingerprint_test.go | 4 +- e2e/residue.go | 2 +- e2e/singleUse/mac.go | 2 +- e2e/singleUse/mac_test.go | 2 +- e2e/singleUse/recipientID.go | 4 +- e2e/singleUse/recipientID_test.go | 2 +- e2e/singleUse/requestFingerprint.go | 4 +- e2e/singleUse/requestFingerprint_test.go | 2 +- e2e/singleUse/requestKey.go | 4 +- e2e/singleUse/requestKey_test.go | 2 +- e2e/singleUse/requestPartFingerprint.go | 4 +- e2e/singleUse/requestPartFingerprint_test.go | 2 +- e2e/singleUse/requestPartKey.go | 4 +- e2e/singleUse/requestPartKey_test.go | 2 +- e2e/singleUse/responseFingerprint.go | 4 +- e2e/singleUse/responseFingerprint_test.go | 2 +- e2e/singleUse/responseKey.go | 4 +- e2e/singleUse/responseKey_test.go | 2 +- e2e/singleUse/tagFingerprint.go | 2 +- e2e/ttl.go | 2 +- fileTransfer/fingerprint.go | 2 +- fileTransfer/keyGen.go | 2 +- fileTransfer/mac.go | 2 +- go.mod | 2 +- go.sum | 2 - group/mac.go | 4 +- group/membership.go | 2 +- group/membership_test.go | 2 +- hash/hash.go | 65 + hash/hash_test.go | 64 + hash/keys.go | 43 + hash/keys_test.go | 81 + message/id.go | 2 +- nike/dh/dh.go | 2 +- registration/hmac_test.go | 2 +- registration/keygen.go | 2 +- registration/keygen_test.go | 4 +- 92 files changed, 3975 insertions(+), 120 deletions(-) create mode 100644 cyclic/buffer.go create mode 100644 cyclic/buffer_test.go create mode 100644 cyclic/group.go create mode 100644 cyclic/group_test.go create mode 100644 cyclic/int.go create mode 100644 cyclic/int_test.go create mode 100644 hash/hash.go create mode 100644 hash/hash_test.go create mode 100644 hash/keys.go create mode 100644 hash/keys_test.go diff --git a/authorize/authorize.go b/authorize/authorize.go index a196344a..e8901755 100644 --- a/authorize/authorize.go +++ b/authorize/authorize.go @@ -13,7 +13,7 @@ package authorize import ( "encoding/binary" "github.com/pkg/errors" - "gitlab.com/xx_network/crypto/rsa" + "gitlab.com/elixxir/crypto/rsa" oldRsa "gitlab.com/xx_network/crypto/signature/rsa" "gitlab.com/xx_network/crypto/xx" "gitlab.com/xx_network/primitives/id" diff --git a/authorize/authorize_test.go b/authorize/authorize_test.go index eeb99204..3d89f2dd 100644 --- a/authorize/authorize_test.go +++ b/authorize/authorize_test.go @@ -11,7 +11,7 @@ import ( "bytes" "crypto/rand" "fmt" - "gitlab.com/xx_network/crypto/rsa" + "gitlab.com/elixxir/crypto/rsa" "gitlab.com/xx_network/crypto/xx" "gitlab.com/xx_network/primitives/id" "strconv" @@ -70,7 +70,7 @@ func TestSignVerify_Consistency(t *testing.T) { } // Sign data - sig, err := Sign(notRand, testTime, serverPrivKey.GetOldRSA()) + sig, err := Sign(notRand, testTime, serverPrivKey) if err != nil { t.Fatalf("SignVerify error: "+ "Could not sign data: %v", err.Error()) @@ -151,7 +151,7 @@ func TestSignVerify(t *testing.T) { "Could not generate key: %v", err.Error()) } - sig, err := Sign(rand.Reader, testTime, serverPrivKey.GetOldRSA()) + sig, err := Sign(rand.Reader, testTime, serverPrivKey) if err != nil { t.Fatalf("SignVerify error: "+ "Could not sign data: %v", err.Error()) @@ -193,7 +193,7 @@ func TestVerify_Error(t *testing.T) { // use insecure seeded rng to reproduce key notRand := &CountingReader{count: uint8(0)} - sig, err := Sign(notRand, signedTime, serverPrivKey.GetOldRSA()) + sig, err := Sign(notRand, signedTime, serverPrivKey) if err != nil { t.Fatalf("SignVerify error: "+ "Could not sign data: %v", err.Error()) diff --git a/authorize/certRequest.go b/authorize/certRequest.go index 84cb9586..630ffa52 100644 --- a/authorize/certRequest.go +++ b/authorize/certRequest.go @@ -9,8 +9,8 @@ package authorize import ( "encoding/binary" - "gitlab.com/xx_network/crypto/hash" - "gitlab.com/xx_network/crypto/rsa" + "gitlab.com/elixxir/crypto/hash" + "gitlab.com/elixxir/crypto/rsa" "io" "time" ) diff --git a/authorize/certRequest_test.go b/authorize/certRequest_test.go index b4ddba25..ea40daa3 100644 --- a/authorize/certRequest_test.go +++ b/authorize/certRequest_test.go @@ -2,8 +2,8 @@ package authorize import ( "bytes" + "gitlab.com/elixxir/crypto/rsa" "gitlab.com/xx_network/crypto/csprng" - "gitlab.com/xx_network/crypto/rsa" "testing" "time" ) diff --git a/backup/backup.go b/backup/backup.go index 95f8cf88..d4d98642 100644 --- a/backup/backup.go +++ b/backup/backup.go @@ -13,9 +13,9 @@ import ( "encoding/json" "github.com/pkg/errors" + "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/primitives/fact" "gitlab.com/xx_network/crypto/csprng" - "gitlab.com/xx_network/crypto/cyclic" "gitlab.com/xx_network/crypto/signature/rsa" "gitlab.com/xx_network/primitives/id" ) diff --git a/broadcast/asymmetric.go b/broadcast/asymmetric.go index b4297af6..3a3ad300 100644 --- a/broadcast/asymmetric.go +++ b/broadcast/asymmetric.go @@ -11,9 +11,9 @@ import ( "bytes" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/crypto/rsa" "gitlab.com/elixxir/primitives/format" "gitlab.com/xx_network/crypto/csprng" - "gitlab.com/xx_network/crypto/rsa" ) // IsPublicKey returns true if the passed public key is the public key for the diff --git a/broadcast/asymmetric_test.go b/broadcast/asymmetric_test.go index d3ba6a91..07c18c03 100644 --- a/broadcast/asymmetric_test.go +++ b/broadcast/asymmetric_test.go @@ -10,8 +10,8 @@ package broadcast import ( "bytes" "gitlab.com/elixxir/crypto/cmix" + "gitlab.com/elixxir/crypto/rsa" "gitlab.com/xx_network/crypto/csprng" - "gitlab.com/xx_network/crypto/rsa" "gitlab.com/xx_network/primitives/netTime" "testing" ) diff --git a/broadcast/channel.go b/broadcast/channel.go index cb6fd948..2dd2bc8e 100644 --- a/broadcast/channel.go +++ b/broadcast/channel.go @@ -28,7 +28,7 @@ import ( "gitlab.com/elixxir/crypto/broadcast/escape" "gitlab.com/elixxir/crypto/cmix" - "gitlab.com/xx_network/crypto/rsa" + "gitlab.com/elixxir/crypto/rsa" ) const ( diff --git a/broadcast/channel_test.go b/broadcast/channel_test.go index 29cfda4a..bdadc78e 100644 --- a/broadcast/channel_test.go +++ b/broadcast/channel_test.go @@ -11,8 +11,8 @@ import ( "bytes" "github.com/pkg/errors" "gitlab.com/elixxir/crypto/cmix" + "gitlab.com/elixxir/crypto/rsa" "gitlab.com/xx_network/crypto/csprng" - "gitlab.com/xx_network/crypto/rsa" "gitlab.com/xx_network/primitives/id" "gitlab.com/xx_network/primitives/netTime" "reflect" diff --git a/broadcast/exportPrivateKey.go b/broadcast/exportPrivateKey.go index 29dbbf5a..531f52f2 100644 --- a/broadcast/exportPrivateKey.go +++ b/broadcast/exportPrivateKey.go @@ -14,7 +14,7 @@ import ( "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/crypto/backup" - "gitlab.com/xx_network/crypto/rsa" + "gitlab.com/elixxir/crypto/rsa" "gitlab.com/xx_network/primitives/id" "golang.org/x/crypto/argon2" "golang.org/x/crypto/blake2b" diff --git a/broadcast/intermediary.go b/broadcast/intermediary.go index 17539ec6..9f8583f6 100644 --- a/broadcast/intermediary.go +++ b/broadcast/intermediary.go @@ -3,7 +3,7 @@ package broadcast import ( "encoding/binary" jww "github.com/spf13/jwalterweatherman" - "gitlab.com/xx_network/crypto/rsa" + "gitlab.com/elixxir/crypto/rsa" "hash" "time" ) diff --git a/broadcast/intermediary_test.go b/broadcast/intermediary_test.go index 09107bd5..2b058d82 100644 --- a/broadcast/intermediary_test.go +++ b/broadcast/intermediary_test.go @@ -3,7 +3,7 @@ package broadcast import ( "bytes" "encoding/hex" - "gitlab.com/xx_network/crypto/rsa" + "gitlab.com/elixxir/crypto/rsa" "gitlab.com/xx_network/primitives/netTime" "hash" "io" diff --git a/broadcast/keysize.go b/broadcast/keysize.go index 0251fe70..4bdf50da 100644 --- a/broadcast/keysize.go +++ b/broadcast/keysize.go @@ -2,7 +2,7 @@ package broadcast import ( jww "github.com/spf13/jwalterweatherman" - "gitlab.com/xx_network/crypto/rsa" + "gitlab.com/elixxir/crypto/rsa" ) // calculateKeySize finds the optimal key size and number of sub-packets smaller diff --git a/broadcast/keysize_test.go b/broadcast/keysize_test.go index 88304c26..d20b9cec 100644 --- a/broadcast/keysize_test.go +++ b/broadcast/keysize_test.go @@ -1,7 +1,7 @@ package broadcast import ( - "gitlab.com/xx_network/crypto/rsa" + "gitlab.com/elixxir/crypto/rsa" "testing" ) diff --git a/broadcast/mac.go b/broadcast/mac.go index d0c2dafe..a071a90e 100644 --- a/broadcast/mac.go +++ b/broadcast/mac.go @@ -10,7 +10,7 @@ package broadcast import ( "crypto/hmac" - "gitlab.com/xx_network/crypto/hash" + "gitlab.com/elixxir/crypto/hash" ) // makeMAC returns the MAC for the given payload. It is an HMAC with proper H diff --git a/broadcast/symmetric_test.go b/broadcast/symmetric_test.go index 93c040b5..0022f175 100644 --- a/broadcast/symmetric_test.go +++ b/broadcast/symmetric_test.go @@ -11,7 +11,7 @@ import ( "bytes" "encoding/base64" "gitlab.com/elixxir/crypto/cmix" - "gitlab.com/xx_network/crypto/rsa" + "gitlab.com/elixxir/crypto/rsa" oldRsa "gitlab.com/xx_network/crypto/signature/rsa" "gitlab.com/xx_network/primitives/id" "gitlab.com/xx_network/primitives/netTime" diff --git a/channel/database.go b/channel/database.go index f47b5dbf..697029be 100644 --- a/channel/database.go +++ b/channel/database.go @@ -12,7 +12,7 @@ import ( "encoding/json" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" - "gitlab.com/xx_network/crypto/hash" + "gitlab.com/elixxir/crypto/hash" "io" ) diff --git a/channel/request.go b/channel/request.go index b3b9319f..13b0474c 100644 --- a/channel/request.go +++ b/channel/request.go @@ -8,7 +8,7 @@ import ( "io" "time" - "gitlab.com/xx_network/crypto/rsa" + "gitlab.com/elixxir/crypto/rsa" ) var ( diff --git a/channel/request_test.go b/channel/request_test.go index b3fe8047..e03c684b 100644 --- a/channel/request_test.go +++ b/channel/request_test.go @@ -2,8 +2,8 @@ package channel import ( "crypto/ed25519" + "gitlab.com/elixxir/crypto/rsa" "gitlab.com/xx_network/crypto/csprng" - "gitlab.com/xx_network/crypto/rsa" "testing" "time" ) diff --git a/cmix/clientGateway.go b/cmix/clientGateway.go index a923b8ba..22514b70 100644 --- a/cmix/clientGateway.go +++ b/cmix/clientGateway.go @@ -17,8 +17,8 @@ package cmix import ( jww "github.com/spf13/jwalterweatherman" - "gitlab.com/xx_network/crypto/cyclic" - "gitlab.com/xx_network/crypto/hash" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/crypto/hash" ) // GenerateClientGatewayKey hashes the symmetric key between client and the node diff --git a/cmix/encryptDecrypt.go b/cmix/encryptDecrypt.go index 466cb358..fbf4e2f9 100644 --- a/cmix/encryptDecrypt.go +++ b/cmix/encryptDecrypt.go @@ -17,8 +17,8 @@ package cmix import ( jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/primitives/format" - "gitlab.com/xx_network/crypto/cyclic" "gitlab.com/xx_network/primitives/id" "golang.org/x/crypto/blake2b" ) diff --git a/cmix/key.go b/cmix/key.go index c0375680..fac18a25 100644 --- a/cmix/key.go +++ b/cmix/key.go @@ -13,8 +13,8 @@ package cmix import ( "crypto/sha256" "encoding/binary" - "gitlab.com/xx_network/crypto/cyclic" - "gitlab.com/xx_network/crypto/hash" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/crypto/hash" "gitlab.com/xx_network/primitives/id" goHash "hash" ) diff --git a/cmix/key_test.go b/cmix/key_test.go index fb67b65d..e1dda9de 100644 --- a/cmix/key_test.go +++ b/cmix/key_test.go @@ -8,7 +8,7 @@ package cmix import ( - "gitlab.com/xx_network/crypto/cyclic" + "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/xx_network/crypto/large" "gitlab.com/xx_network/primitives/id" "testing" diff --git a/cmix/kmac.go b/cmix/kmac.go index bd8c33fb..3cbb9347 100644 --- a/cmix/kmac.go +++ b/cmix/kmac.go @@ -14,7 +14,7 @@ import ( "encoding/binary" "hash" - "gitlab.com/xx_network/crypto/cyclic" + "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/xx_network/primitives/id" ) diff --git a/cmix/kmac_test.go b/cmix/kmac_test.go index 2b18412d..d142fb5a 100644 --- a/cmix/kmac_test.go +++ b/cmix/kmac_test.go @@ -8,9 +8,9 @@ package cmix import ( + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/crypto/hash" "gitlab.com/xx_network/crypto/csprng" - "gitlab.com/xx_network/crypto/cyclic" - "gitlab.com/xx_network/crypto/hash" "gitlab.com/xx_network/crypto/large" "gitlab.com/xx_network/primitives/id" "math/rand" @@ -18,7 +18,7 @@ import ( "testing" ) -// Tests that the kmac function outputs wha +//Tests that the kmac function outputs wha func TestGenerateKMAC_Consistency(t *testing.T) { grp := grpTest() @@ -56,7 +56,7 @@ func TestGenerateKMAC_Consistency(t *testing.T) { } } -// Tests that the kmac function outputs wha +//Tests that the kmac function outputs wha func TestGenerateKMACs_Consistency(t *testing.T) { grp := grpTest() @@ -101,7 +101,7 @@ func TestGenerateKMACs_Consistency(t *testing.T) { } } -// Happy path +//Happy path func TestVerifyKMAC(t *testing.T) { grp := grpTest() @@ -137,7 +137,7 @@ func TestVerifyKMAC(t *testing.T) { } } -// Error path +//Error path func TestVerifyKMAC_WrongExpectedKmac(t *testing.T) { grp := grpTest() @@ -172,7 +172,7 @@ func TestVerifyKMAC_WrongExpectedKmac(t *testing.T) { } -// Error path +//Error path func TestVerifyKACY_Mismatch(t *testing.T) { grp := grpTest() diff --git a/cmix/setGroupBits.go b/cmix/setGroupBits.go index 23b77d2d..c15a1777 100644 --- a/cmix/setGroupBits.go +++ b/cmix/setGroupBits.go @@ -9,9 +9,9 @@ package cmix import ( jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/primitives/format" "gitlab.com/xx_network/crypto/csprng" - "gitlab.com/xx_network/crypto/cyclic" ) // SetGroupBits takes a message and a cyclic group and randomly sets diff --git a/codename/identity.go b/codename/identity.go index 37fd7d6d..b41d5b90 100644 --- a/codename/identity.go +++ b/codename/identity.go @@ -15,7 +15,7 @@ import ( "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" - "gitlab.com/xx_network/crypto/hash" + "gitlab.com/elixxir/crypto/hash" "golang.org/x/crypto/blake2b" ) diff --git a/contact/contact.go b/contact/contact.go index f526da5f..4f08eee3 100644 --- a/contact/contact.go +++ b/contact/contact.go @@ -17,8 +17,8 @@ import ( "github.com/pkg/errors" "github.com/skip2/go-qrcode" + "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/primitives/fact" - "gitlab.com/xx_network/crypto/cyclic" "gitlab.com/xx_network/primitives/id" "golang.org/x/crypto/blake2b" ) diff --git a/contact/contact_test.go b/contact/contact_test.go index 253e51bc..a8db2b9b 100644 --- a/contact/contact_test.go +++ b/contact/contact_test.go @@ -20,9 +20,9 @@ import ( "github.com/liyue201/goqr" "github.com/skip2/go-qrcode" + "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/primitives/fact" "gitlab.com/xx_network/crypto/csprng" - "gitlab.com/xx_network/crypto/cyclic" "gitlab.com/xx_network/crypto/large" "gitlab.com/xx_network/primitives/id" ) diff --git a/contact/unmarshalVersion.go b/contact/unmarshalVersion.go index 003c9672..756d1d9b 100644 --- a/contact/unmarshalVersion.go +++ b/contact/unmarshalVersion.go @@ -18,8 +18,8 @@ import ( "encoding/binary" "github.com/pkg/errors" + "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/primitives/fact" - "gitlab.com/xx_network/crypto/cyclic" "gitlab.com/xx_network/primitives/id" ) diff --git a/cyclic/buffer.go b/cyclic/buffer.go new file mode 100644 index 00000000..bf742089 --- /dev/null +++ b/cyclic/buffer.go @@ -0,0 +1,66 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2022 xx foundation // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file. // +//////////////////////////////////////////////////////////////////////////////// + +// Package cyclic wraps our large.Int structure. It is designed to be used in +// conjunction with the cyclic.Group object. The cyclic.Group object +// will provide implementations of various modular operations within the group. +// A cyclic.IntBuffer type will be created to store large batches of groups. +package cyclic + +import ( + "gitlab.com/xx_network/crypto/large" +) + +// Store the same group fingerprint for multiple values +type IntBuffer struct { + values []large.Int + fingerprint uint64 +} + +// Get gets the cyclic int at a specific index in the int buffer +func (ib *IntBuffer) Get(index uint32) *Int { + return &Int{&ib.values[index], ib.fingerprint} +} + +// GetSubBuffer get an intBuffer representing a specific region in the int buffer +func (ib *IntBuffer) GetSubBuffer(begin, end uint32) *IntBuffer { + return &IntBuffer{ + values: ib.values[begin:end], + fingerprint: ib.fingerprint} +} + +// DeepCopy gets a deep copy of an intBuffer +func (ib *IntBuffer) DeepCopy() *IntBuffer { + newBuffer := IntBuffer{make([]large.Int, len(ib.values)), ib.fingerprint} + for i := range newBuffer.values { + (&newBuffer.values[i]).Set(&ib.values[i]) + } + return &newBuffer +} + +// Len gets the length of the int buffer +func (ib *IntBuffer) Len() int { + return len(ib.values) +} + +// GetFingerprint gets the int buffer's group fingerprint +func (ib *IntBuffer) GetFingerprint() uint64 { + return ib.fingerprint +} + +// Contains checks that the index is within the amount of the values slice +func (ib *IntBuffer) Contains(index uint32) bool { + return index < uint32(len(ib.values)) +} + +// Erase overwrites all underlying data from an IntBuffer by setting its values +// slice to nil and its fingerprint to zero. All underlying released data will +// be removed by the garbage collector. +func (ib *IntBuffer) Erase() { + ib.values = nil + ib.fingerprint = 0 +} diff --git a/cyclic/buffer_test.go b/cyclic/buffer_test.go new file mode 100644 index 00000000..5b966dfd --- /dev/null +++ b/cyclic/buffer_test.go @@ -0,0 +1,206 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2022 xx foundation // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file. // +//////////////////////////////////////////////////////////////////////////////// + +package cyclic + +import ( + "gitlab.com/xx_network/crypto/large" + "testing" +) + +//Tests that getting and interacting with the intbuffer is correct +func TestIntBuffer_Get(t *testing.T) { + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + + buf := grp.NewIntBuffer(5, nil) + + // Set an int + grp.SetUint64(buf.Get(2), 322) + + // Ensure that overwriting a large int in the buffer doesn't overwrite any of + // the things it shouldn't overwrite + shouldStillBePSub1 := buf.Get(0) + if shouldStillBePSub1.Cmp(grp.NewInt(1000000010101111110)) != 0 { + t.Error("Setting the buffer element also set another element of the" + + " buffer (probably aliased)") + } + shouldAlsoStillBePSub1 := grp.GetPSub1() + if shouldAlsoStillBePSub1.value.Cmp(large.NewInt(1000000010101111110)) != 0 { + t.Error("Setting the buffer element also set PSub1 (probably aliased)") + } + + // Ensure that when you get an int that you've set, + // it has the value that you set it to + shouldBe322 := buf.Get(2) + if shouldBe322.Cmp(grp.NewInt(322)) != 0 { + t.Error("The buffer item that should have been set to 322 wasn't") + } +} + +//Tests that len works correctly +func TestIntBuffer_Len(t *testing.T) { + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + + for i := 0; i < 1000; i++ { + ib := grp.NewIntBuffer(uint32(i), nil) + if ib.Len() != i { + t.Errorf("IntBuffer.Len: returned incorrect len, Expected: %v, Received: %v", i, ib.Len()) + } + } +} + +//Tests that GetFingerprint returns the correct fingerprint +func TestIntBuffer_GetFingerprint(t *testing.T) { + p1 := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp1 := NewGroup(p1, g) + + ib1 := grp1.NewIntBuffer(5, nil) + + if ib1.GetFingerprint() != grp1.GetFingerprint() { + t.Errorf("IntBuffer.GetFingerprint: returned incorrect fingerprint,"+ + "Expected: %v, Received: %v", grp1.GetFingerprint(), ib1.fingerprint) + } + + p2 := large.NewInt(1000000010101111011) + grp2 := NewGroup(p2, g) + + ib2 := grp2.NewIntBuffer(5, nil) + + if ib2.GetFingerprint() != grp2.GetFingerprint() { + t.Errorf("IntBuffer.GetFingerprint: returned incorrect fingerprint,"+ + "Expected: %v, Received: %v", grp2.GetFingerprint(), ib2.fingerprint) + } + +} + +//Tests that getting a region in the intbuffer works correctly +func TestIntBuffer_GetRegion(t *testing.T) { + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + + intBuffLen := uint32(20) + + buf := grp.NewIntBuffer(intBuffLen, nil) + + // Set all ints + for i := uint32(0); i < intBuffLen; i++ { + grp.SetUint64(buf.Get(i), uint64(i)) + } + + begin := uint32(3) + end := uint32(17) + + //Get a region of the int buffer + bufSub := buf.GetSubBuffer(begin, end) + + //check that the length is correct + if bufSub.Len() != int(end-begin) { + t.Errorf("IntBuffer.GetSubBuffer: Size of region of incorrect,"+ + "Expected: %v, Received: %v", end-begin, bufSub.Len()) + } + + for i := begin; i < end; i++ { + //check that the copy is exact + bufint := bufSub.Get(i - begin) + if bufint.GetLargeInt().Int64() != int64(i) { + t.Errorf("IntBuffer.GetSubBuffer: Region mapped incorrectly,"+ + "Expected: %v, Received: %v", i, bufint.GetLargeInt().Int64()) + } + //check that when editing one, the other is edited + grp.SetUint64(bufint, uint64(100-i)) + if buf.Get(i).GetLargeInt().Int64() != int64(100-i) { + t.Errorf("IntBuffer.GetSubBuffer: Region not connected to originator,"+ + "Expected: %v, Received: %v", 100-i, buf.Get(i).GetLargeInt().Int64()) + } + } +} + +//Tests that deep copy of an int buffer copies correctly +func TestIntBuffer_DeepCopy(t *testing.T) { + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + + intBuffLen := uint32(20) + + buf := grp.NewIntBuffer(intBuffLen, nil) + + // Set all ints + for i := uint32(0); i < intBuffLen; i++ { + grp.SetUint64(buf.Get(i), uint64(i)) + } + + //Get a deep copy of the int buffer + bufCpy := buf.DeepCopy() + + if bufCpy.Len() != buf.Len() { + t.Errorf("IntBuffer.DeepCopy: Size of region of incorrect,"+ + "Expected: %v, Received: %v", buf.Len(), bufCpy.Len()) + } + + for i := uint32(0); i < intBuffLen; i++ { + //check that the copy is the same as the original + bufint := bufCpy.Get(i) + if bufint.GetLargeInt().Int64() != int64(i) { + t.Errorf("IntBuffer.DeepCopy: Copy not equal to original at %v,"+ + "Expected: %v, Received: %v", i, i, bufint.GetLargeInt().Int64()) + } + //check that editing one does not edit the other + grp.SetUint64(bufint, uint64(100-i)) + if buf.Get(i).GetLargeInt().Int64() == int64(100-i) { + t.Errorf("IntBuffer.DeepCopy: Region connected to originator,"+ + "Expected: %v, Received: %v", 100-i, buf.Get(i).GetLargeInt().Int64()) + } + } +} + +func TestIntBuffer_Contains(t *testing.T) { + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + + b := grp.NewIntBuffer(15, nil) + + for i := 0; i < b.Len(); i++ { + if !b.Contains(uint32(i)) { + t.Errorf("IntBuffer.Contains: Does not contain index %v when it does", i) + } + } + + if b.Contains(uint32(b.Len())) { + t.Errorf("IntBuffer.Contains: Contains index %v when it doesnt", b.Len()) + } + + if b.Contains(uint32(b.Len() + 1)) { + t.Errorf("IntBuffer.Contains: Contains index %v when it doesnt", b.Len()+1) + } +} + +// Tests that Erase() removes all underlying data from the IntBuffer. +func TestIntBuffer_Erase(t *testing.T) { + ib := grp.NewIntBuffer(uint32(20), nil) + + ib.Erase() + + if ib.values != nil { + t.Errorf("Erase() did not properly delete the IntBuffer's "+ + "underlying value\n\treceived: %v\n\texpected: %v", + ib.values, nil) + } + + if ib.fingerprint != 0 { + t.Errorf("Erase() did not properly delete the IntBuffer's "+ + "underlying fingerprint\n\treceived: %v\n\texpected: %v", + ib.fingerprint, 0) + } +} diff --git a/cyclic/group.go b/cyclic/group.go new file mode 100644 index 00000000..4568b59c --- /dev/null +++ b/cyclic/group.go @@ -0,0 +1,638 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2022 xx foundation // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file. // +//////////////////////////////////////////////////////////////////////////////// + +// Package cyclic wraps our large.Int structure. It is designed to be used in +// conjunction with the cyclic.Group object. The cyclic.Group object +// will provide implementations of various modular operations within the group. +// A cyclic.IntBuffer type will be created to store large batches of groups. +package cyclic + +import ( + "bytes" + "crypto/sha256" + "encoding/base64" + "encoding/binary" + "encoding/gob" + "encoding/json" + "github.com/pkg/errors" + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/xx_network/crypto/csprng" + "gitlab.com/xx_network/crypto/large" +) + +// Groups provide cyclic int operations that keep the return values confined to +// a finite field under modulo p +type Group struct { + psub1 *large.Int + psub2 *large.Int + psub3 *large.Int + prime *large.Int + psub1factor *large.Int + zero *large.Int + one *large.Int + two *large.Int + gen *large.Int + rng csprng.Source + random []byte + fingerprint uint64 + primeBytes []byte +} + +const GroupFingerprintSize = 8 + +// NewGroup returns a group with the given prime and generator +func NewGroup(p, g *large.Int) *Group { + h := sha256.New() + h.Write(p.Bytes()) + h.Write(g.Bytes()) + hashVal := h.Sum(nil)[:GroupFingerprintSize] + value := large.NewIntFromBytes(hashVal) + return &Group{ + prime: p, + psub1: large.NewInt(1).Sub(p, large.NewInt(1)), + psub2: large.NewInt(1).Sub(p, large.NewInt(2)), + psub3: large.NewInt(1).Sub(p, large.NewInt(3)), + psub1factor: large.NewInt(1).RightShift(large.NewInt(1).Sub(p, large.NewInt(1)), 1), + + zero: large.NewInt(0), + one: large.NewInt(1), + two: large.NewInt(2), + gen: g, + rng: csprng.NewSystemRNG(), + random: make([]byte, (p.BitLen()+7)/8), + fingerprint: value.Uint64(), + primeBytes: p.Bytes(), + } +} + +// -------------- Constructors -------------- // + +// NewIntBuffer is a constructor for IntBuffer +// if defaultValue is nil, it is set to the max value possible in the group, p-1 +func (g *Group) NewIntBuffer(length uint32, defaultValue *Int) *IntBuffer { + var defaultValueLarge *large.Int + + if defaultValue == nil { + defaultValueLarge = g.psub1.DeepCopy() + } else { + g.checkInts(defaultValue) + defaultValueLarge = defaultValue.value.DeepCopy() + } + + newBuffer := IntBuffer{make([]large.Int, length), g.fingerprint} + for i := range newBuffer.values { + (&newBuffer.values[i]).Set(defaultValueLarge) + } + return &newBuffer +} + +// NewInt creates a new cyclicInt in the group from an int64 value +func (g *Group) NewInt(x int64) *Int { + val := large.NewInt(x) + n := &Int{value: val, fingerprint: g.fingerprint} + if !g.Inside(n.value) { + jww.FATAL.Panic("NewInt: Attempted creation of cyclic outside of group") + } + return n +} + +// NewIntFromLargeInt creates a new cyclicInt in the group from a large.Int value +func (g *Group) NewIntFromLargeInt(x *large.Int) *Int { + n := &Int{value: x, fingerprint: g.fingerprint} + if !g.Inside(n.value) { + jww.FATAL.Panic("NewIntFromLargeInt: Attempted creation of cyclic outside of group") + } + return n +} + +// NewIntFromBytes creates a new cyclicInt in the group from a byte buffer +func (g *Group) NewIntFromBytes(buf []byte) *Int { + val := large.NewIntFromBytes(buf) + n := &Int{value: val, fingerprint: g.fingerprint} + if !g.Inside(n.value) { + jww.FATAL.Panic("NewIntFromBytes: Attempted creation of cyclic outside of group") + } + return n +} + +// NewIntFromString creates a new cyclicInt in the group from a string using the passed base +// returns nil if string cannot be parsed +func (g *Group) NewIntFromString(str string, base int) *Int { + val := large.NewIntFromString(str, base) + if val == nil { + return nil + } + n := &Int{value: val, fingerprint: g.fingerprint} + if !g.Inside(n.value) { + jww.FATAL.Panic("NewIntFromString: Attempted creation of cyclic outside of group") + } + return n +} + +// NewMaxInt creates a new cyclicInt in the group at the max group value +func (g *Group) NewMaxInt() *Int { + n := &Int{value: g.psub1, fingerprint: g.fingerprint} + return n.DeepCopy() +} + +// NewIntFromUInt creates a new cyclicInt in the group from an uint64 value +func (g *Group) NewIntFromUInt(i uint64) *Int { + val := large.NewIntFromUInt(i) + n := &Int{value: val, fingerprint: g.fingerprint} + if !g.Inside(n.value) { + jww.FATAL.Panic("NewIntFromUInt: Attempted creation of cyclic outside of group") + } + return n +} + +// NewIntFromBits creates a new cyclic int from a words array +// This method doesn't copy the bits array, so if you need a copy, copy the array before passing it in +func (g *Group) NewIntFromBits(b large.Bits) *Int { + val := large.NewIntFromBits(b) + n := &Int{ + value: val, + fingerprint: g.fingerprint, + } + if !g.Inside(n.value) { + jww.FATAL.Panic("NewIntFromBits: Attempted creation of cyclic outside of group") + } + return n +} + +// Check if all cyclic Ints belong to the group and panic otherwise +func (g *Group) checkInts(ints ...*Int) { + for _, i := range ints { + if i.GetGroupFingerprint() != g.fingerprint { + // The error is created separately to ensure that the stack trace is + // printed on panic + err := errors.Errorf("cyclicInt being used in wrong group! "+ + "Group fingerprint is %d and cyclicInt has %d", + g.fingerprint, i.GetGroupFingerprint()) + jww.FATAL.Panicf("%+v", err) + } + } +} + +// GetFingerprint gets the group's fingerprint +func (g *Group) GetFingerprint() uint64 { + return g.fingerprint +} + +func (g *Group) GetFingerprintText() string { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, g.fingerprint) + fullText := base64.StdEncoding.EncodeToString(buf) + return fullText[:8] + "..." +} + +// -------------- Setters -------------- // + +// Set sets x to y in the group and returns x +func (g *Group) Set(x, y *Int) *Int { + g.checkInts(x, y) + x.value.Set(y.value) + return x +} + +// SetLargeInt sets x's value to y s.t. y is inside the group +func (g *Group) SetLargeInt(x *Int, y *large.Int) *Int { + success := g.Inside(y) + + if !success { + return nil + } + + x.value.Set(y) + + return x +} + +// SetBits sets x in the group to bits and returns x +// This method does not copy. If you need to set the number to a copy, please copy the bits outside of this. +func (g *Group) SetBits(x *Int, b large.Bits) *Int { + x.value.SetBits(b) + g.checkInts(x) + return x +} + +// OverwriteBits copies b over x. If there isn't enough memory +// available in x already, it allocates a new slice with enough memory +// Under no circumstance will b be the backing memory of the returned Int +// This is important for our usage of CGBN, which constantly overwrites the output memory +func (g *Group) OverwriteBits(x *Int, b large.Bits) *Int { + bits := x.Bits() + if cap(bits) >= len(b) { + // Existing int is big enough; copying over existing bits is OK + bits = bits[:cap(bits)] + copy(bits, b) + x.value.SetBits(bits[:len(b)]) + } else { + // A new slice is needed, since the existing int isn't big enough + newBits := make(large.Bits, len(b)) + copy(newBits, b) + x.value.SetBits(newBits) + } + g.checkInts(x) + return x +} + +// SetBytes sets x in the group to bytes and returns x +func (g *Group) SetBytes(x *Int, buf []byte) *Int { + x.value.SetBytes(buf) + g.checkInts(x) + return x +} + +// SetString sets x in the group to string and returns x +// or nil if error parsing the string +func (g *Group) SetString(x *Int, s string, base int) *Int { + g.checkInts(x) + _, ret := x.value.SetString(s, base) + if ret == false { + return nil + } + return x +} + +// SetMaxInt sets x in the group to Max4KInt value and returns x +func (g *Group) SetMaxInt(x *Int) *Int { + g.checkInts(x) + x.value.Set(g.psub1) + return x +} + +// SetUint64 sets x in the group to uint64 value and returns x +func (g *Group) SetUint64(x *Int, u uint64) *Int { + g.checkInts(x) + x.value.SetUint64(u) + return x +} + +// Mul multiplies a and b within the group, putting the result in c +// and returning c +func (g *Group) Mul(a, b, c *Int) *Int { + g.checkInts(a, b, c) + c.value.Mod(c.value.Mul(a.value, b.value), g.prime) + return c +} + +// Inside returns true of the Int is within the group, false if it isn't +func (g *Group) Inside(a *large.Int) bool { + return a.Cmp(g.zero) == 1 && a.Cmp(g.prime) == -1 +} + +// bytesInside returns true of the all the Ints represented by the byte slices +// are within the group, false if it isn't +func (g *Group) BytesInside(buffers ...[]byte) bool { + inside := true + + for _, buf := range buffers { + inside = inside && g.singleBytesInside(buf) + } + + return inside +} + +// BytesInside returns true of the Int represented by the byte slice is within the group, false if it isn't +func (g *Group) singleBytesInside(buf []byte) bool { + return csprng.InGroup(buf, g.primeBytes) +} + +// ModP sets z ≡ x mod prime within the group and returns z. +func (g Group) ModP(x *large.Int, z *Int) *Int { + g.checkInts(z) + z.value.Mod(x, g.prime) + return z +} + +// Inverse sets b equal to the inverse of a within the group and returns b +func (g *Group) Inverse(a, b *Int) *Int { + g.checkInts(a, b) + b.value.ModInverse(a.value, g.prime) + return b +} + +// Random securely generates a random number in the group: 2 <= rand <= p-1 +// Sets r to the number and returns it +func (g *Group) Random(r *Int) *Int { + g.checkInts(r) + rng := g.rng + r.value.Set(g.one) + for g.one.Cmp(r.value) == 0 { + b, err := csprng.GenerateInGroup(g.primeBytes, + len(g.primeBytes), rng) + if err != nil { + jww.FATAL.Panicf("Could not generate random number in group: %s", err) + } + r.value.SetBytes(b) + } + return r +} + +// GetP returns a copy of the group's prime +func (g *Group) GetP() *large.Int { + n := large.NewInt(1) + n.Set(g.prime) + return n +} + +// GetG returns a copy of the group's generator +func (g *Group) GetG() *large.Int { + n := large.NewInt(1) + n.Set(g.gen) + return n +} + +// GetGCyclic returns a new cyclicInt with the group's generator +func (g *Group) GetGCyclic() *Int { + return g.NewIntFromLargeInt(g.gen) +} + +// GetPBytes returns a copy of the group's prime bytes +func (g *Group) GetPBytes() []byte { + pCopy := make([]byte, len(g.primeBytes)) + copy(pCopy, g.primeBytes) + return pCopy +} + +// GetPSub1 returns a copy of the group's p-1 +func (g *Group) GetPSub1() *Int { + n := large.NewInt(1) + n.Set(g.psub1) + return &Int{n, g.fingerprint} +} + +// GetPSub1Cyclic returns a new cyclicInt with the group's p-1 +func (g *Group) GetPSub1Cyclic() *Int { + return g.NewIntFromLargeInt(g.psub1) +} + +// GetPSub1Factor returns a copy of the group's (p-1)/2 +func (g *Group) GetPSub1Factor() *large.Int { + n := large.NewInt(1) + n.Set(g.psub1factor) + return n +} + +// GetPSub1FactorCyclic returns a new cyclicInt with the group's (p-1)/2 +func (g *Group) GetPSub1FactorCyclic() *Int { + return g.NewIntFromLargeInt(g.psub1factor) +} + +// FullBytes gets cyclicInt left-padded to the size of the prime +func (g *Group) FullBytes(x *Int) []byte { + return x.value.LeftpadBytes(uint64(g.prime.BitLen() >> 3)) +} + +// GroupMul Multiplies all ints in the passed slice slc together and +// places the result in c +func (g Group) MulMulti(c *Int, ints ...*Int) *Int { + + g.checkInts(append(ints, c)...) + c.value.SetInt64(1) + + for _, islc := range ints { + g.Mul(c, islc, c) + } + + return c +} + +// Exp sets z = x**y mod p, and returns z. +func (g Group) Exp(x, y, z *Int) *Int { + g.checkInts(x, y, z) + z.value.Exp(x.value, y.value, g.prime) + return z +} + +// ExpG sets z = g**y mod p, and returns z. +func (g Group) ExpG(y, z *Int) *Int { + g.checkInts(y, z) + z.value.Exp(g.gen, y.value, g.prime) + return z +} + +// RandomCoprime randomly generates coprimes in the group (coprime +// against g.prime-1) +func (g *Group) RandomCoprime(r *Int) *Int { + g.checkInts(r) + var err error + for r.value.Set(g.psub1); !r.value.IsCoprime(g.psub1); { + g.random, err = csprng.GenerateInGroup(g.prime.Bytes(), len(g.psub1.Bytes()), g.rng) + if err != nil { + jww.FATAL.Panicf("Could not generate random "+ + "Coprime number in group: %v", err.Error()) + } + r.value.SetBytes(g.random) + r.value.Mod(r.value, g.psub3) + r.value.Add(r.value, g.two) + } + return r +} + +// RootCoprime sets tmp = y√x mod p, and returns tmp. Only works with y's +// coprime with g.prime-1 (g.psub1) +func (g Group) RootCoprime(x, y, z *Int) *Int { + g.checkInts(x, y, z) + tmp := g.NewInt(1) + tmp.value.ModInverse(y.value, g.psub1) + g.Exp(x, tmp, z) + return z +} + +// Finds a number coprime with p-1 and who's modular exponential inverse is +// the number of prescribed bits value. Bits must be greater than 1. +// Only works when the prime is safe or strong +// Using a smaller bytes length is acceptable because modular logarithm algorithm's +// complexities derive primarily from the size of the prime defining the group +// not the size of the exponent. More information can be found here: +// TODO: add link to doc +// The function will panic if bits >= log2(g.prime), so the caller MUST use +// a correct value of bits + +func (g Group) FindSmallCoprimeInverse(z *Int, bitLen uint32) *Int { + if bitLen >= uint32(g.prime.BitLen()) { + jww.FATAL.Panicf("Requested bits: %d is greater than"+ + " or equal to group's prime: %d", bitLen, g.prime.BitLen()) + } + + g.checkInts(z) + // RNG that ensures the output is an odd number between 2 and 2^bitLen + // that is not equal to p-1/2. This must occur because for a proper + // modular inverse to exist within a group a number must have no common + // factors with the number that defines the group. Normally that would not + // be a problem because the number that defines the group normally is a prime, + // but we are inverting within a group defined by the even number p-1 to find the + // modular exponential inverse, so the number must be chozen from a different set + + // In order to generate the number in the range + // the following steps are taken: + // 1. max = 2^(bitLen)-2 + // 2. gen rand num by reading from rng + // 3. rand mod max : giving a range of 0 - 2^(bitLen)-3 + // 4. rand + 2: range: 2 - 2^(bitLen)-1 + // 5. rand ^ 1: range: 3 - 2^(bitLen)-1, odd number + max := large.NewInt(1).Sub( + large.NewInt(1).LeftShift( + g.one, + uint(bitLen)), + g.two) + + zinv := large.NewInt(1) + + for true { + n, err := g.rng.Read(g.random) + if err != nil || n != len(g.random) { + jww.FATAL.Panicf("Could not generate random "+ + "number in group: %v", err.Error()) + } + zinv.SetBytes(g.random) + zinv.Mod(zinv, max) + zinv.Add(zinv, g.two) + zinv.Xor(zinv, g.one) + + // p-1 has one odd factor, (p-1)/2, we must check that the generated number is not that + if zinv.Cmp(g.psub1factor) == 0 { + continue + } + + // Modulo inverse zinv and check that the inverse exists + if z.value.ModInverse(zinv, g.psub1) == nil { + continue + } + + zbytes := z.Bytes() + + // Checks if the lowest bit is 1, implying the value is odd. + // Due to the fact that p is a safe prime, this means the value is + // coprime with p minus 1 because its only has one odd factor, which is + // also checked + + if zbytes[len(zbytes)-1]&0x01 == 1 { + if zinv.Cmp(g.psub1factor) != 0 { + break + } + } + + } + + return z +} + +// GobEncode returns a byte slice representing the encoding of Group for the +// transmission to a GobDecode(). +func (g *Group) GobEncode() ([]byte, error) { + // Anonymous structure that flattens nested structures + s := struct { + P []byte + G []byte + }{ + g.prime.Bytes(), + g.gen.Bytes(), + } + + var buf bytes.Buffer + + // Create new encoder that will transmit the buffer + enc := gob.NewEncoder(&buf) + + // Transmit the data + err := enc.Encode(s) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +// GobDecode overwrites the receiver, which must be a pointer, with Group +// represented by the byte slice, which was written by GobEncode(). +func (g *Group) GobDecode(b []byte) error { + // Anonymous, empty, flat structure + s := struct { + P []byte + G []byte + }{ + []byte{}, + []byte{}, + } + + var buf bytes.Buffer + + // Write bytes to the buffer + buf.Write(b) + + // Create new decoder that reads from the buffer + dec := gob.NewDecoder(&buf) + + // Receive and decode data + err := dec.Decode(&s) + + if err != nil { + return err + } + + // Convert decoded bytes and put into empty structure + prime := large.NewIntFromBytes(s.P) + gen := large.NewIntFromBytes(s.G) + + *g = *NewGroup(prime, gen) + + return nil +} + +// MarshalJSON extracts prime, gen and primeQ to a json object. +// Returns the json object as a byte slice. +func (g *Group) MarshalJSON() ([]byte, error) { + + // Get group parameters + prime := g.GetP() + gen := g.GetG() + + // Create json object + base := 16 + jsonObj := map[string]string{ + "prime": prime.TextVerbose(base, 0), + "gen": gen.TextVerbose(base, 0), + } + + // Marshal json object into byte slice + b, err := json.Marshal(&jsonObj) + if err != nil { + return nil, err + } + + return b, nil +} + +// Overwrites the receiver, which must be a pointer, with Group +// represented by the byte slice which contains encoded JSON data +func (g *Group) UnmarshalJSON(b []byte) error { + + // Initialize json object to contain max sized group params + base := 16 + max := large.NewMaxInt().TextVerbose(base, 0) + jsonObj := map[string]string{ + "prime": max, + "gen": max, + } + + // Unmarshal byte slice into json object + err := json.Unmarshal(b, &jsonObj) + + if err != nil { + return err + } + + // Get group params from json object and put into receiver + prime := large.NewIntFromString(jsonObj["prime"], base) + gen := large.NewIntFromString(jsonObj["gen"], base) + *g = *NewGroup(prime, gen) + + return nil +} diff --git a/cyclic/group_test.go b/cyclic/group_test.go new file mode 100644 index 00000000..7b25e8ee --- /dev/null +++ b/cyclic/group_test.go @@ -0,0 +1,2069 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2022 xx foundation // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file. // +//////////////////////////////////////////////////////////////////////////////// + +package cyclic + +import ( + "bytes" + "crypto/sha256" + "errors" + "gitlab.com/xx_network/crypto/large" + "math/rand" + "reflect" + "testing" +) + +// Tests NewGroup functionality +func TestNewGroup(t *testing.T) { + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + actual := NewGroup(p, g) + + type testStruct struct { + prime *large.Int + g *large.Int + } + expected := testStruct{p, g} + if actual.prime.Cmp(expected.prime) != 0 { + t.Errorf("TestNewGroup failed to initialize prime, expected: '%v',"+ + " got: '%v'", expected.prime.Text(10), actual.prime.Text(10)) + } else if actual.gen.Cmp(expected.g) != 0 { + t.Errorf("TestNewGroup failed to initialize generator, expected: '%v',"+ + " got: '%v'", expected.g.Text(10), actual.gen.Text(10)) + } +} + +// Test creation of cyclicInt in the group from int64 +func TestNewInt(t *testing.T) { + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + + expected := large.NewInt(42) + actual := grp.NewInt(42) + + if actual.value.Cmp(expected) != 0 { + t.Errorf("NewInt creation failed, expected: %v,"+ + "got: %v", expected, actual.value) + } else if actual.GetGroupFingerprint() != grp.GetFingerprint() { + t.Errorf("NewInt is not in the group, expected group fingerprint: %v,"+ + "got: %v", grp.GetFingerprint(), actual.GetGroupFingerprint()) + } +} + +//Tests creation and properties of an IntBuffer +func TestGroup_NewIntBuffer(t *testing.T) { + + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + + //test that the size is correct and the default value is set correctly + rng := rand.New(rand.NewSource(42)) + + tests := 100 + + for i := 0; i < tests; i++ { + defaultInt := grp.Random(grp.NewInt(1)) + size := rng.Uint32() % 10000 + buf := grp.NewIntBuffer(size, defaultInt) + + //test that the length is correct + if len(buf.values) != int(size) { + t.Errorf("NewIntBuffer did not generate buffer of the correct size: "+ + "Expected %v, Recieved: %v", size, len(buf.values)) + } + + pass := true + + defaultIntLarge := defaultInt.GetLargeInt() + + //test that the default value is set correctly + for _, i := range buf.values { + if i.Cmp(defaultIntLarge) != 0 { + pass = false + } + } + + if !pass { + t.Errorf("NewIntBuffer internal values not equal to default value") + } + } + + //test that when passed default int is nil values are set to prime-1 + buf := grp.NewIntBuffer(10, nil) + + for _, i := range buf.values { + if i.Cmp(grp.psub1) != 0 { + t.Errorf("NewIntBuffer internal values not equal to psub1 when nill passed") + } + } + +} + +// Test creation of cyclicInt in the group from int64 fails when outside the group +func TestNewInt_Panic(t *testing.T) { + defer func() { + if r := recover(); r != nil { + return + } + }() + + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + + grp.NewInt(0) + + t.Errorf("NewInt created even when outside of the group") +} + +// Test creation of cyclicInt in the group from large.Int +func TestNewIntFromLargeInt(t *testing.T) { + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + + expected := large.NewInt(42) + actual := grp.NewIntFromLargeInt(expected) + + if actual.value.Cmp(expected) != 0 { + t.Errorf("NewIntFromLargeInt creation failed, expected: %v,"+ + "got: %v", expected, actual.value) + } else if actual.GetGroupFingerprint() != grp.GetFingerprint() { + t.Errorf("NewIntFromLargeInt is not in the group, expected group fingerprint: %v,"+ + "got: %v", grp.GetFingerprint(), actual.GetGroupFingerprint()) + } +} + +// Test creation of cyclicInt in the group from large.Int fails when outside the group +func TestNewIntFromLargeInt_Panic(t *testing.T) { + defer func() { + if r := recover(); r != nil { + return + } + }() + + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + + grp.NewIntFromLargeInt(large.NewInt(0)) + + t.Errorf("NewIntFromLargeInt created even when outside of the group") +} + +// Test creation of cyclicInt in the group from byte array +func TestNewIntFromBytes(t *testing.T) { + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + + expected := large.NewInt(42) + value := []byte{0x2A} + actual := grp.NewIntFromBytes(value) + + if actual.value.Cmp(expected) != 0 { + t.Errorf("NewIntFromBytes creation failed, expected: %v,"+ + "got: %v", expected, actual.value) + } else if actual.GetGroupFingerprint() != grp.GetFingerprint() { + t.Errorf("NewIntFromBytes is not in the group, expected group fingerprint: %v,"+ + "got: %v", grp.GetFingerprint(), actual.GetGroupFingerprint()) + } +} + +// Test creation of cyclicInt in the group from bytes fails when outside the group +func TestNewIntFromBytes_Panic(t *testing.T) { + defer func() { + if r := recover(); r != nil { + return + } + }() + + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + + grp.NewIntFromBytes([]byte{0}) + + t.Errorf("NewIntFromBytes created even when outside of the group") +} + +// Test creation of cyclicInt in the group from string +// Also confirm that if the string can't be converted, nil is returned +func TestNewIntFromString(t *testing.T) { + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + + expected := large.NewInt(42) + value := "42" + actual := grp.NewIntFromString(value, 10) + + if actual.value.Cmp(expected) != 0 { + t.Errorf("NewIntFromString creation failed, expected: %v,"+ + "got: %v", expected, actual.value) + } else if actual.GetGroupFingerprint() != grp.GetFingerprint() { + t.Errorf("NewIntFromString is not in the group, expected group fingerprint: %v,"+ + "got: %v", grp.GetFingerprint(), actual.GetGroupFingerprint()) + } + + errVal := grp.NewIntFromString("185", 5) + + if errVal != nil { + t.Errorf("NewIntFromString should return nil when error occurs decoding string") + } +} + +// Test creation of cyclicInt in the group from string fails when outside the group +func TestNewIntFromString_Panic(t *testing.T) { + defer func() { + if r := recover(); r != nil { + return + } + }() + + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + + grp.NewIntFromString("0", 16) + + t.Errorf("NewIntFromString created even when outside of the group") +} + +// Show that NewIntFromBits fails when outside of group +func TestGroup_NewIntFromBits_Panic(t *testing.T) { + defer func() { + if r := recover(); r != nil { + return + } + }() + + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + + grp.NewIntFromBits(large.Bits{0}) + + t.Errorf("NewIntFromBits created even when outside of the group") +} + +// Show that NewIntFromBits makes a big int from a word string +func TestGroup_NewIntFromBits(t *testing.T) { + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + + expected := grp.NewIntFromString("123456", 16) + i := grp.NewIntFromBits(large.Bits{0x123456}) + t.Log(i.TextVerbose(16, 0)) + + if expected.Cmp(i) != 0 { + t.Errorf("Expected int to be %v, got %v", expected.Text(16), i.Text(16)) + } +} + +// Test creation of cyclicInt in the group from Max4KInt value +func TestNewMaxInt(t *testing.T) { + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + + expected := grp.psub1 + actual := grp.NewMaxInt() + + if actual.value.Cmp(expected) != 0 { + t.Errorf("NewMaxInt creation failed, expected: %v,"+ + "got: %v", expected, actual.value) + } else if actual.GetGroupFingerprint() != grp.GetFingerprint() { + t.Errorf("NewMaxInt is not in the group, expected group fingerprint: %v,"+ + "got: %v", grp.GetFingerprint(), actual.GetGroupFingerprint()) + } +} + +// Test creation of cyclicInt in the group from uint64 +func TestNewIntFromUInt(t *testing.T) { + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + + expected := large.NewInt(42) + actual := grp.NewIntFromUInt(uint64(42)) + + if actual.value.Cmp(expected) != 0 { + t.Errorf("NewIntFromUInt creation failed, expected: %v,"+ + "got: %v", expected, actual.value) + } else if actual.GetGroupFingerprint() != grp.GetFingerprint() { + t.Errorf("NewIntFromUInt is not in the group, expected group fingerprint: %v,"+ + "got: %v", grp.GetFingerprint(), actual.GetGroupFingerprint()) + } +} + +// Test creation of cyclicInt in the group from uint64 fails when outside the group +func TestNewIntFromUInt_Panic(t *testing.T) { + defer func() { + if r := recover(); r != nil { + return + } + }() + + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + + grp.NewIntFromUInt(0) + + t.Errorf("NewIntFromUInt created even when outside of the group") +} + +// Test group fingerprint getter +func TestGetFingerprint(t *testing.T) { + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + + h := sha256.New() + h.Write(p.Bytes()) + h.Write(g.Bytes()) + expected := large.NewIntFromBytes(h.Sum(nil)[:GroupFingerprintSize]).Uint64() + + if grp.GetFingerprint() != expected { + t.Errorf("GetFingerprint returned wrong value, expected: %v,"+ + " got: %v", expected, grp.GetFingerprint()) + } +} + +// Test group fingerprint getter +func TestGetFingerprintText(t *testing.T) { + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + + expected := "ln9lzlk2..." + + if grp.GetFingerprintText() != expected { + t.Errorf("GetFingerprintText returned wrong value, expected: %v,"+ + " got: %v", expected, grp.GetFingerprintText()) + } +} + +// Test setting cyclicInt to another from the same group +func TestSet(t *testing.T) { + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + + expected := grp.NewInt(int64(42)) + actual := grp.NewInt(int64(69)) + + if actual.Cmp(expected) == 0 { + t.Errorf("Original Ints should be different to test Set") + } + + grp.Set(actual, expected) + + result := actual.Cmp(expected) + + if result != 0 { + t.Errorf("Test of Set failed, expected: '0', got: '%v'", + result) + } +} + +// Test that Set panics if cyclicInt doesn't belong to the group +func TestSet_Panic(t *testing.T) { + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + g2 := large.NewInt(2) + grp2 := NewGroup(p, g2) + + expected := grp.NewInt(int64(42)) + actual := grp2.NewInt(int64(69)) + + defer func() { + if r := recover(); r == nil { + t.Errorf("Set should panic when one of involved " + + "cyclicInts doesn't belong to the group") + } + }() + + grp.Set(actual, expected) +} + +// TestSetLargeInt tests if the value is properly copied +func TestSetLargeInt(t *testing.T) { + p := large.NewInt(17) + g := large.NewInt(7) + group := NewGroup(p, g) + + inputs := []int64{2, 1, 12, 17, 18} + + for i := 0; i < len(inputs); i++ { + tmp := group.NewInt(5) + v := tmp.value + exp := group.NewInt(inputs[i%3]) + li := large.NewInt(inputs[i]) + if group.SetLargeInt(tmp, li) != nil { + if i >= 3 { + t.Errorf("TestLargeInt set value outside "+ + "of group: %d", inputs[i]) + } + } + if i < 3 && (tmp.Cmp(exp) != 0 || v.Cmp(li) != 0) { + t.Errorf("TestSetFromLargeInt failed at index %v", i) + } + } +} + +// Test setting cyclicInt in the same group from bytes +func TestSetBytes(t *testing.T) { + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + + expected := []*Int{ + grp.NewInt(42), + grp.NewInt(6553522), + grp.NewInt(2)} + testBytes := [][]byte{ + {0x2A}, // 42 + {0x63, 0xFF, 0xB2}, // 6553522 + {0x02}} + + actual := grp.NewInt(55) + + for i, testi := range testBytes { + actual = grp.SetBytes(actual, testi) + if actual.Cmp(expected[i]) != 0 { + t.Errorf("Test of SetBytes failed at index %v, expected: '%v', "+ + "actual: %v", i, expected[i].Text(10), actual.Text(10)) + } + } + +} + +// Test that SetBytes panics if cyclicInt doesn't belong to the group +func TestSetBytes_Panic(t *testing.T) { + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + g2 := large.NewInt(2) + grp2 := NewGroup(p, g2) + + actual := grp2.NewInt(int64(42)) + + defer func() { + if r := recover(); r == nil { + t.Errorf("SetBytes should panic when " + + "cyclicInt doesn't belong to the group") + } + }() + + grp.SetBytes(actual, []byte("TEST")) +} + +// Shows that setting bits results in a different integer +func TestGroup_SetBits(t *testing.T) { + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + + expected := grp.NewInt(2) + newInt := grp.NewInt(1) + grp.SetBits(newInt, large.Bits{2}) + if expected.Cmp(newInt) != 0 { + t.Errorf("Setbits didn't set to the expected int result. Got %v, expected %v", newInt.Text(16), expected.Text(16)) + } +} + +// Shows that setting bits with a different group from the original integer panics +func TestGroup_SetBits_Panic(t *testing.T) { + defer func() { + if r := recover(); r != nil { + return + } + }() + + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + g2 := large.NewInt(2) + grp2 := NewGroup(p, g2) + + i := grp.NewInt(1) + grp2.SetBits(i, large.Bits{123456}) + + t.Errorf("SetBits worked even when another group was used") +} + +// OverwriteBits is a higher-level method that copies a bits slice into an integer +// or allocates a new slice if necessary +// Shows that OverwriteBits never uses the passed bits slice to back the new integer +// Shows that the results are as expected, even in cases where the lengths aren't equal +func TestGroup_OverwriteBits(t *testing.T) { + // We need a bigger group than the other tests, because for this test to be meaningful + // the prime needs to take up more than one word + // Using modp1536 + p := large.NewIntFromString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF", 16) + g := large.NewInt(5) + grp := NewGroup(p, g) + + // Case 1: enough space in the existing int for the overwriting int to exist + existing := grp.NewMaxInt() + existingStart := &existing.Bits()[0] + t.Log("existing cap:", cap(existing.Bits())) + expected := grp.NewIntFromString("1234567890abcdef1234567890abcdef1234567890abcdef123", 16) + t.Log("expected len:", len(expected.Bits())) + grp.OverwriteBits(existing, expected.Bits()) + + // Start of backing bits slice should be the same + if existingStart != &existing.Bits()[0] { + t.Errorf("start of existing changed. had %v, got %v", existingStart, &existing.Bits()[0]) + } + // Should get expected number in existing + if existing.Cmp(expected) != 0 { + t.Errorf("actual differed from expected. actual: %v, expected %v", existing, expected) + } + + // Case 2: not enough space in the existing int for the overwriting int to exist + newAlloc := grp.NewInt(1) + newAllocStart := &newAlloc.Bits()[0] + t.Log("newAlloc cap:", cap(newAlloc.Bits())) + grp.OverwriteBits(newAlloc, expected.Bits()) + + // Start of backing bits slice should be different + if newAllocStart == &newAlloc.Bits()[0] { + t.Errorf("start of newAlloc didn't change. had %v, got %v", newAllocStart, &newAlloc.Bits()[0]) + } + // It should also be different from the backing bits slice of expected + if &newAlloc.Bits()[0] == &existing.Bits()[0] { + t.Errorf("newAlloc uses same backing memory as existing! bad!!") + } + // Of course, the int should be equal to existing + if expected.Cmp(newAlloc) != 0 { + t.Errorf("actual differed from expected. actual: %v, expected %v", newAlloc, expected) + } +} + +// Is it possible to trigger the panic? +func TestGroup_OverwriteBits_Panic(t *testing.T) { + defer func() { + if r := recover(); r != nil { + return + } + }() + + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + g2 := large.NewInt(2) + grp2 := NewGroup(p, g2) + + i := grp.NewInt(1) + grp2.OverwriteBits(i, large.Bits{123456}) + + t.Errorf("OverwriteBits worked with a different group") +} + +// Test setting cyclicInt in the same group from string +func TestSetString(t *testing.T) { + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + + type testStructure struct { + str string + base int + } + + testStructs := []testStructure{ + {"42", 0}, + {"100000000", 0}, + {"5", 0}, + {"1", 0}, + {"f", 0}, + {"182", 5}, + {"10", 2}, + } + + expected := []*Int{ + grp.NewInt(42), + grp.NewInt(100000000), + grp.NewInt(5), + grp.NewInt(1), + nil, + nil, + grp.NewInt(2), + } + + actual := grp.NewInt(1) + + for i, testi := range testStructs { + ret := grp.SetString(actual, testi.str, testi.base) + + // Test invalid input making sure it occurs when expected + if ret == nil { + if expected[i] != nil { + t.Error("Test of SetString() failed at index:", i, + "Function didn't handle invalid input correctly") + } + } else { + if actual.Cmp(expected[i]) != 0 { + t.Errorf("Test of SetString() failed at index: %v Expected: %v;"+ + " Actual: %v", i, expected[i].Text(10), actual.Text(10)) + } + } + } + +} + +// Test that SetString panics if cyclicInt doesn't belong to the group +func TestSetString_Panic(t *testing.T) { + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + g2 := large.NewInt(2) + grp2 := NewGroup(p, g2) + + actual := grp2.NewInt(int64(42)) + + defer func() { + if r := recover(); r == nil { + t.Errorf("SetString should panic when " + + "cyclicInt doesn't belong to the group") + } + }() + + grp.SetString(actual, "1234", 10) +} + +// Test setting cyclicInt in the same group to Max4KInt value +func TestSetMaxInt(t *testing.T) { + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + + expected := grp.GetPSub1() + actual := grp.NewInt(int64(69)) + + if actual.Cmp(expected) == 0 { + t.Errorf("Original Ints should be different to test SetMaxInt") + } + + grp.SetMaxInt(actual) + + result := actual.Cmp(expected) + + if result != 0 { + t.Errorf("Test of SetMaxInt failed, expected: '0', got: '%v'", + result) + } +} + +// Test that Set panics if cyclicInt doesn't belong to the group +func TestSetMaxInt_Panic(t *testing.T) { + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + g2 := large.NewInt(2) + grp2 := NewGroup(p, g2) + + actual := grp2.NewInt(int64(69)) + + defer func() { + if r := recover(); r == nil { + t.Errorf("SetMaxInt should panic when " + + "cyclicInt doesn't belong to the group") + } + }() + + grp.SetMaxInt(actual) +} + +// Test setting cyclicInt in the same group to uint64 value +func TestSetUint64(t *testing.T) { + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + + expected := grp.NewInt(int64(42)) + actual := grp.NewInt(int64(69)) + + if actual.Cmp(expected) == 0 { + t.Errorf("Original Ints should be different to test SetUint64") + } + + grp.SetUint64(actual, uint64(42)) + + result := actual.Cmp(expected) + + if result != 0 { + t.Errorf("Test of SetUint64 failed, expected: '0', got: '%v'", + result) + } +} + +// Test that Set panics if cyclicInt doesn't belong to the group +func TestSetUint64_Panic(t *testing.T) { + p := large.NewInt(1000000010101111111) + g := large.NewInt(5) + grp := NewGroup(p, g) + g2 := large.NewInt(2) + grp2 := NewGroup(p, g2) + + actual := grp2.NewInt(int64(69)) + + defer func() { + if r := recover(); r == nil { + t.Errorf("SetUint64 should panic when " + + "cyclicInt doesn't belong to the group") + } + }() + + grp.SetUint64(actual, uint64(0)) +} + +// Test multiplication under the group +func TestMul(t *testing.T) { + prime := int64(107) + p := large.NewInt(prime) + g := large.NewInt(5) + group := NewGroup(p, g) + + actual := []*Int{ + group.Mul(group.NewInt(20), group.NewInt(11), group.NewInt(1)), + group.Mul(group.NewInt(1), group.NewInt(10), group.NewInt(1)), + } + expected := []*Int{ + group.NewInt((20 * 11) % prime), + group.NewInt(10), + } + + for i := 0; i < len(actual); i++ { + if actual[i].value.Cmp(expected[i].value) != 0 { + t.Errorf("TestMulForGroup failed at index:%v, expected:%v, got:%v", + i, expected[i].value.Text(10), actual[i].value.Text(10)) + } + } + +} + +// Test left padded bytes getter +func TestFullBytes(t *testing.T) { + + expected := []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A} + + actual := grp.NewInt(int64(42)) + + actualbytes := grp.FullBytes(actual) + + if !bytes.Equal(actualbytes, expected) { + t.Errorf("Test of FullBytes failed, expected: '%v', got: '%v'", + actualbytes, expected) + } +} + +// Test that mul panics if cyclicInt doesn't belong to the group +func TestMul_Panic(t *testing.T) { + prime := int64(107) + p := large.NewInt(prime) + g := large.NewInt(5) + group := NewGroup(p, g) + g2 := large.NewInt(2) + group2 := NewGroup(p, g2) + + a := group.NewInt(20) + b := group2.NewInt(11) + c := group.NewInt(1) + + defer func() { + if r := recover(); r == nil { + t.Errorf("Mul should panic when one of involved " + + "cyclicInts doesn't belong to the group") + } + }() + + group.Mul(a, b, c) +} + +// Test Inside that checks if a number is inside the group +func TestInside(t *testing.T) { + p := large.NewInt(17) + g := large.NewInt(7) + group := NewGroup(p, g) + expected := []bool{ + false, + true, + false, + false, + true, + } + actual := []bool{ + group.Inside(large.NewInt(0)), + group.Inside(large.NewInt(1)), + group.Inside(large.NewInt(17)), + group.Inside(large.NewInt(18)), + group.Inside(large.NewInt(12)), + } + + for i := 0; i < len(expected); i++ { + if actual[i] != expected[i] { + t.Errorf("TestInside failed at index:%v, expected:%v, got:%v", + i, expected[i], actual[i]) + } + } +} + +// Test Inside that checks if a number is inside the group +func TestSingleBytesInside(t *testing.T) { + p := large.NewInt(1023) + g := large.NewInt(7) + group := NewGroup(p, g) + expected := []bool{ + false, + false, + true, + true, + true, + true, + false, + false, + } + actual := []bool{ + group.singleBytesInside([]byte{0}), + group.singleBytesInside(large.NewInt(0).Bytes()), + group.singleBytesInside(large.NewInt(1).Bytes()), + group.singleBytesInside(large.NewInt(17).Bytes()), + group.singleBytesInside(large.NewInt(70).Bytes()), + group.singleBytesInside(large.NewInt(1022).Bytes()), + group.singleBytesInside(large.NewInt(1111).Bytes()), + group.singleBytesInside(large.NewInt(100000).Bytes()), + } + + for i := 0; i < len(expected); i++ { + if actual[i] != expected[i] { + t.Errorf("TestBytesInside failed at index:%v, expected:%v, got:%v", + i, expected[i], actual[i]) + } + } +} + +// Test Inside that checks if a number is inside the group +func TestBytesInside(t *testing.T) { + p := large.NewInt(1023) + g := large.NewInt(7) + group := NewGroup(p, g) + expected := []bool{ + true, + true, + false, + false, + } + actual := []bool{ + group.BytesInside(large.NewInt(1).Bytes()), + group.BytesInside(large.NewInt(1).Bytes(), large.NewInt(1000).Bytes(), large.NewInt(300).Bytes()), + group.BytesInside(large.NewInt(1).Bytes(), large.NewInt(1000).Bytes(), large.NewInt(300).Bytes(), large.NewInt(2000).Bytes()), + group.BytesInside(large.NewInt(0).Bytes(), large.NewInt(1100).Bytes(), large.NewInt(30000000).Bytes(), large.NewInt(400900).Bytes()), + } + + for i := 0; i < len(expected); i++ { + if actual[i] != expected[i] { + t.Errorf("TestBytesInside failed at index:%v, expected:%v, got:%v", + i, expected[i], actual[i]) + } + } +} + +// Test modulus under the group +func TestModP(t *testing.T) { + p := []*large.Int{large.NewInt(17), large.NewIntFromString("717190887961", 10), + large.NewIntFromString("717190905917", 10)} + g := large.NewInt(13) + + group := make([]*Group, 0, len(p)) + for i := 0; i < len(p); i++ { + group = append(group, NewGroup(p[i], g)) + } + + expected := []*large.Int{large.NewInt(2), large.NewIntFromString("269673339004", 10), + large.NewIntFromString("623940771224", 10)} + a := []*large.Int{large.NewInt(5000), large.NewIntFromString("beefbeecafe80386", 16), + large.NewIntFromString("77777777777777777777", 16)} + actual := make([]*Int, len(expected)) + for i := 0; i < len(expected); i++ { + actual[i] = group[i].NewInt(1) + group[i].ModP(a[i], actual[i]) + } + + for i := 0; i < len(expected); i++ { + if actual[i].value.Cmp(expected[i]) != 0 { + t.Errorf("TestModP failed, expected: '%v', got: '%v'", + expected[i].Text(10), actual[i].value.Text(10)) + } + } + +} + +// Test that inside panics if cyclicInt doesn't belong to the group +func TestModP_Panic(t *testing.T) { + prime := int64(107) + p := large.NewInt(prime) + g := large.NewInt(5) + group := NewGroup(p, g) + g2 := large.NewInt(2) + group2 := NewGroup(p, g2) + + a := large.NewInt(20) + b := group2.NewInt(1) + + defer func() { + if r := recover(); r == nil { + t.Errorf("ModP should panic when one of involved " + + "cyclicInts doesn't belong to the group") + } + }() + + group.ModP(a, b) +} + +// Test Inverse under the group +func TestInverse(t *testing.T) { + p := large.NewInt(17) + g := large.NewInt(13) + group := NewGroup(p, g) + x := group.NewInt(13) //message + a := group.NewInt(10) //encryption key + inv := group.NewInt(1) + inv = group.Inverse(a, inv) //decryption key + a = group.Mul(x, a, a) // encrypted message + c := group.Mul(inv, a, group.NewInt(1)) //decrypted message (x) + + if c.value.Cmp(x.value) != 0 { + t.Errorf("TestInverse failed, expected: '%v', got: '%v'", + x.value.Text(10), c.value.Text(10)) + } +} + +// Test that inverse panics if cyclicInt doesn't belong to the group +func TestInverse_Panic(t *testing.T) { + prime := int64(107) + p := large.NewInt(prime) + g := large.NewInt(5) + group := NewGroup(p, g) + g2 := large.NewInt(2) + group2 := NewGroup(p, g2) + + a := group.NewInt(20) + b := group2.NewInt(1) + + defer func() { + if r := recover(); r == nil { + t.Errorf("Inverse should panic when one of involved " + + "cyclicInts doesn't belong to the group") + } + }() + + group.Inverse(a, b) +} + +// Test Random multiple times to check if +// the number generated is ever outside the group +func TestRandom(t *testing.T) { + p := large.NewInt(107) + g := large.NewInt(4) + group := NewGroup(p, g) + for i := 0; i < 100000; i++ { + if !group.Inside(group.Random(group.NewInt(1)).GetLargeInt()) { + t.Errorf("Generated number is not inside the group!") + } + } +} + +// Test that Random panics if cyclicInt doesn't belong to the group +func TestRandom_Panic(t *testing.T) { + p := large.NewInt(107) + g := large.NewInt(4) + group := NewGroup(p, g) + g2 := large.NewInt(2) + group2 := NewGroup(p, g2) + + a := group2.NewInt(20) + + defer func() { + if r := recover(); r == nil { + t.Errorf("Random should panic when " + + "cyclicInt doesn't belong to the group") + } + }() + + group.Random(a) +} + +type AlwaysErrorReader struct{} + +func (r AlwaysErrorReader) Read(b []byte) (int, error) { + return 1, errors.New("testing reader error") +} + +func (r AlwaysErrorReader) SetSeed(seed []byte) error { + return nil +} + +// This test forces random to panic by overwriting the CSPRNG inside the group +func TestRandom_PanicReadErr(t *testing.T) { + p := large.NewInt(107) + g := large.NewInt(4) + group := NewGroup(p, g) + + // Overwrite CSPRNG + group.rng = AlwaysErrorReader{} + + defer func() { + if r := recover(); r == nil { + t.Errorf("Random should panic on read error") + } + }() + + group.Random(group.NewInt(1)) +} + +func TestGen(t *testing.T) { + // setup test group and generator + p := large.NewInt(17) + g := large.NewInt(29) + group := NewGroup(p, g) + + // setup array to keep track of frequency of random values + r := group.NewInt(1) + rng := make([]int, int(p.Int64())) + + tests := 500 + thresh := 0.3 + + // generate randoms + for i := 0; i < tests; i++ { + rng[int(group.Random(r).value.Int64())]++ + } + + // make sure 0 and 1 were not generated + if rng[0] > 0 { + t.Errorf("TestGen() failed, 0 is outside of the required range") + } + if rng[1] > 0 { + t.Errorf("TestGen() failed, 1 is outside of the required range") + } + + // check that frequency doesn't exceed threshold + for i := 0; i < len(rng); i++ { + if float64(rng[i])/float64(tests) > thresh { + t.Errorf("TestGen() failed, insufficiently random, value: %v"+ + " occured: %v out of %v tests", i, rng[i], tests) + } + } + +} + +// Test prime getter from the group +func TestGetP(t *testing.T) { + // setup test group and generator + p := large.NewInt(17) + g := large.NewInt(29) + group := NewGroup(p, g) + actual := group.GetP() + + if actual.Cmp(p) != 0 { + t.Errorf("TestGetP failed, expected: '%v', got: '%v'", + p.Text(10), actual.Text(10)) + } +} + +//Tests the prime byte getter from the group +//Tests the old manual call against the implementation (p.Bytes() vs grp.GetPBytes()) +//P is never nil, so no edge case there +func TestGetPBytes(t *testing.T) { + // setup test group and generator + p := large.NewInt(17) + g := large.NewInt(29) + group := NewGroup(p, g) + actual := group.GetPBytes() + + if bytes.Compare(p.Bytes(), actual) != 0 { + t.Errorf("TestGetPBytes failed, expected: '%v', got: '%v'", + p.Text(10), actual) + } + +} + +// Test generator getter from the group +func TestGetG(t *testing.T) { + // setup test group and generator + p := large.NewInt(17) + g := large.NewInt(29) + group := NewGroup(p, g) + actual := group.GetG() + + if actual.Cmp(g) != 0 { + t.Errorf("TestGetP failed, expected: '%v', got: '%v'", + g.Text(10), actual.Text(10)) + } +} + +// Test generator getter from the group cyclic version +func TestGetGCyclic(t *testing.T) { + // setup test group and generator + p := large.NewInt(33) + g := large.NewInt(29) + group := NewGroup(p, g) + actual := group.GetGCyclic() + + if actual.value.Cmp(g) != 0 { + t.Errorf("TestGetGCyclic failed, expected: '%v', got: '%v'", + g.Text(10), actual.value.Text(10)) + } +} + +// Test prime-1 getter from the group +func TestGetPSub1(t *testing.T) { + // setup test group and generator + p := large.NewInt(17) + g := large.NewInt(29) + group := NewGroup(p, g) + actual := group.GetPSub1() + ps1 := large.NewInt(16) + + if actual.value.Cmp(ps1) != 0 { + t.Errorf("TestGetPSub1 failed, expected: '%v', got: '%v'", + ps1.Text(10), actual.Text(10)) + } +} + +// Test prime-1 getter from the group cyclic version +func TestGetPSub1Cyclic(t *testing.T) { + // setup test group and generator + p := large.NewInt(17) + g := large.NewInt(29) + group := NewGroup(p, g) + actual := group.GetPSub1Cyclic() + ps1 := large.NewInt(16) + + if actual.value.Cmp(ps1) != 0 { + t.Errorf("TestGetPSub1Cyclic failed, expected: '%v', got: '%v'", + ps1.Text(10), actual.value.Text(10)) + } + +} + +// Test (prime-1)/2 getter from the group +func TestGetPSub1Factor(t *testing.T) { + // setup test group and generator + p := large.NewInt(17) + g := large.NewInt(29) + group := NewGroup(p, g) + actual := group.GetPSub1Factor() + pfactor := large.NewInt(8) + + if actual.Cmp(pfactor) != 0 { + t.Errorf("TestGetPSub1Factor failed, expected: '%v', got: '%v'", + pfactor.Text(10), actual.Text(10)) + } +} + +// Test (prime-1)/2 getter from the group cyclic version +func TestGetPSub1FactorCyclic(t *testing.T) { + // setup test group and generator + p := large.NewInt(17) + g := large.NewInt(29) + group := NewGroup(p, g) + actual := group.GetPSub1FactorCyclic() + pfactor := large.NewInt(8) + + if actual.value.Cmp(pfactor) != 0 { + t.Errorf("TestGetPSub1FactorCyclic failed, expected: '%v', got: '%v'", + pfactor.Text(10), actual.value.Text(10)) + } +} + +// Test array multiplication under the group +func TestArrayMul(t *testing.T) { + p := large.NewInt(11) + g := large.NewInt(7) + grp := NewGroup(p, g) + + expected := large.NewInt(10) + + slc := []*Int{ + grp.NewInt(2), + grp.NewInt(3), + grp.NewInt(4), + grp.NewInt(5), + } + + c := grp.NewInt(1) + actual := grp.MulMulti(c, slc...) + + if actual.value.Cmp(expected) != 0 { + t.Errorf("TestArrayMul failed, expected: '%v', got: '%v'", + expected, actual) + } + +} + +// Test that ArrayMult panics if cyclicInt doesn't belong to the group +func TestArrayMult_Panic(t *testing.T) { + prime := int64(107) + p := large.NewInt(prime) + g := large.NewInt(5) + group := NewGroup(p, g) + g2 := large.NewInt(2) + group2 := NewGroup(p, g2) + + slc := []*Int{ + group.NewInt(2), + group2.NewInt(3), + group.NewInt(4), + group.NewInt(5), + } + a := group.NewInt(20) + + defer func() { + if r := recover(); r == nil { + t.Errorf("ArrayMult should panic when one of involved " + + "cyclicInts doesn't belong to the group") + } + }() + + group.MulMulti(a, slc...) +} + +// Test exponentiation under the group +func TestExp(t *testing.T) { + p := large.NewInt(117) + g := large.NewInt(5) + grp := NewGroup(p, g) + + type testStructure struct { + x *Int + y *Int + z *Int + } + + testStrings := [][]string{ + {"42", "41", "9"}, + {"42", "63", "27"}, + {"69", "42", "27"}, + {"99", "81", "99"}, + } + + var testStructs []testStructure + + for i, strs := range testStrings { + var ts testStructure + + ts.x = grp.NewIntFromString(strs[0], 10) + + if ts.x == nil { + t.Errorf("Setup for Test of Exp() for Group failed at 'x' phase of index: %v", i) + } + + ts.y = grp.NewIntFromString(strs[1], 10) + + if ts.y == nil { + t.Errorf("Setup for Test of Exp() for Group failed at 'y' phase of index: %v", i) + } + + ts.z = grp.NewIntFromString(strs[2], 10) + + if ts.z == nil { + t.Errorf("Setup for Test of Exp() for Group failed at 'z' phase of index: %v", i) + } + + testStructs = append(testStructs, ts) + } + + tests := len(testStructs) + pass := 0 + + expected := 0 + + for i, testi := range testStructs { + actual := grp.NewInt(1) + actual = grp.Exp(testi.x, testi.y, actual) + + result := actual.value.Cmp(testi.z.value) + + if result != expected { + t.Errorf("Test of Exp() for Group failed at index: %v Expected: %v, %v; Actual: %v, %v", + i, expected, testi.z.value.Text(10), result, actual.value.Text(10)) + } else { + pass += 1 + } + } + println("Exp() for Group", pass, "out of", tests, "tests passed.") + +} + +// Test exponentiation of the generator in the group +func TestExpG(t *testing.T) { + p := large.NewInt(117) + g := large.NewInt(5) + grp := NewGroup(p, g) + + type testStructure struct { + y *Int + z *Int + } + + testStrings := [][]string{ + {"42", "64"}, + {"69", "44"}, + {"43", "86"}, + {"2", "25"}, + } + + var testStructs []testStructure + + for i, strs := range testStrings { + var ts testStructure + + ts.y = grp.NewIntFromString(strs[0], 10) + + if ts.y == nil { + t.Errorf("Setup for Test of Exp() for Group failed at 'y' phase of index: %v", i) + } + + ts.z = grp.NewIntFromString(strs[1], 10) + + if ts.z == nil { + t.Errorf("Setup for Test of Exp() for Group failed at 'z' phase of index: %v", i) + } + + testStructs = append(testStructs, ts) + } + + expected := 0 + + for i, testi := range testStructs { + actual := grp.NewInt(1) + actual = grp.ExpG(testi.y, actual) + + result := actual.value.Cmp(testi.z.value) + + if result != expected { + t.Errorf("Test of Exp() for Group failed at index: %v Expected: %v; Actual: %v", + i, testi.z.value.Text(10), actual.value.Text(10)) + } + } + +} + +// Test that Exp panics if cyclicInt doesn't belong to the group +func TestExp_Panic(t *testing.T) { + prime := int64(107) + p := large.NewInt(prime) + g := large.NewInt(5) + group := NewGroup(p, g) + g2 := large.NewInt(2) + group2 := NewGroup(p, g2) + + a := group2.NewInt(20) + b := group.NewInt(11) + c := group.NewInt(1) + + defer func() { + if r := recover(); r == nil { + t.Errorf("Exp should panic when one of involved " + + "cyclicInts doesn't belong to the group") + } + }() + + group.Exp(a, b, c) +} + +// Test random Coprime number generation under the group +func TestRandomCoprime(t *testing.T) { + // setup test group and generator + p := large.NewInt(17) + g := large.NewInt(29) + group := NewGroup(p, g) + + // setup array to keep track of frequency of random values + r := group.NewInt(1) + rng := make([]int, int(p.Int64())) + + tests := 500 + + thresh := 0.3 + + // generate randoms + for i := 0; i < tests; i++ { + rng[int(group.RandomCoprime(r).value.Int64())]++ + } + + // make sure 0 and 1 were not generated + if rng[0] > 0 { + t.Errorf("TestRandomeCoprime() failed, 0 is outside of the required range") + } + if rng[1] > 0 { + t.Errorf("TestRandomeCoprime() failed, 1 is outside of the required range") + } + + // check that frequency doesn't exceed threshold + for i := 0; i < len(rng); i++ { + if float64(rng[i])/float64(tests) > thresh { + t.Errorf("TestRandomCoprime() failed, insufficiently random, value: %v"+ + " occured: %v out of %v tests", i, rng[i], tests) + } + } +} + +// Test that RandomCoprime panics if cyclicInt doesn't belong to the group +func TestRandomCoprime_Panic(t *testing.T) { + prime := int64(107) + p := large.NewInt(prime) + g := large.NewInt(5) + group := NewGroup(p, g) + g2 := large.NewInt(2) + group2 := NewGroup(p, g2) + + a := group2.NewInt(20) + + defer func() { + if r := recover(); r == nil { + t.Errorf("RandomCoprime should panic when " + + "cyclicInt doesn't belong to the group") + } + }() + + group.RandomCoprime(a) +} + +// This test forces RandomCoprime to panic by overwriting the CSPRNG inside the group +func TestRandomCoprime_PanicReadErr(t *testing.T) { + p := large.NewInt(5) + g := large.NewInt(4) + group := NewGroup(p, g) + + // Overwrite CSPRNG + group.rng = AlwaysErrorReader{} + + defer func() { + if r := recover(); r == nil { + t.Errorf("RandomCoprime should panic on read error") + } + }() + + group.RandomCoprime(group.NewInt(1)) +} + +//Tests whether the z value is prematurely overwritten in the group after RootCoprime is run +//Tests z value after rootcoprime against a value it's known to be given this input +func TestRootCoprime_ZVal(t *testing.T) { + p := large.NewInt(17) + g := large.NewInt(29) + + group := NewGroup(p, g) + + x := group.NewInt(12) + z := group.NewInt(12) + + //tmp in rootCoPrime is 1 in this case, so z=x**1 + group.RootCoprime(x, z, z) + if z.value.Cmp(x.value) != 0 { + t.Errorf("RootCoprime overwrote z value") + } + +} + +// You pass a value x = a^y to the RootCoprime function, where y is (supposed to be) coprime with (p-1). +// If y is coprime, then the function returns the value of a +func TestRootCoprime(t *testing.T) { + tests := 2 + pass := 0 + + p := large.NewInt(17) + g := large.NewInt(29) + + group := NewGroup(p, g) + + a := []*Int{group.NewInt(5), group.NewInt(4), group.NewInt(15)} + x := group.NewInt(1) + y := []*Int{group.NewInt(5), group.NewInt(11), group.NewInt(2)} + z := []*Int{group.NewInt(1), group.NewInt(1), group.NewInt(1)} + + passing := []bool{true, true, false} + + for i := 0; i < 2; i++ { + group.Exp(a[i], y[i], x) + + tmp := group.RootCoprime(x, y[i], z[i]) + if tmp.value.Cmp(a[i].value) != 0 && passing[i] { //this is wrong, why would z change? + t.Errorf("RootCoprime Error: Function did not output expected value! a: %v z: %v", a[i].value.Text(16), z[i].value.Text(16)) + } else { + pass++ + } + + } + + println("RootCoprime", pass, "out of", tests, "tests passed.") +} + +// Test that RootCoprime panics if cyclicInt doesn't belong to the group +func TestRootCoprime_Panic(t *testing.T) { + prime := int64(107) + p := large.NewInt(prime) + g := large.NewInt(5) + group := NewGroup(p, g) + g2 := large.NewInt(2) + group2 := NewGroup(p, g2) + + a := group.NewInt(20) + b := group.NewInt(11) + c := group2.NewInt(1) + + defer func() { + if r := recover(); r == nil { + t.Errorf("RootCoprime should panic when one of involved " + + "cyclicInts doesn't belong to the group") + } + }() + + group.RootCoprime(a, b, c) +} + +// Test finding a small coprime inverse number in the group +func TestFindSmallCoprimeInverse(t *testing.T) { + primeString := "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + + "15728E5A8AACAA68FFFFFFFFFFFFFFFF" + + p := large.NewIntFromString(primeString, 16) + g := large.NewInt(2) + group := NewGroup(p, g) + + num := 1000 + + totalBitLen := 0 + + bits := uint32(256) + + for i := 0; i < num; i++ { + z := group.NewInt(1) + + base := group.Random(group.NewInt(1)) + + group.FindSmallCoprimeInverse(z, bits) + + zinv := large.NewInt(1).ModInverse(z.value, group.psub1) + + totalBitLen += len(zinv.Bytes()) * 8 + + if len(zinv.Bytes())*8 > int(bits) { + t.Errorf("FindSmallExponent Error: Inverse too large on "+ + "attempt %v; Expected: <%v, Recieved: %v", i, bits, + uint32(len(zinv.Bytes())*8)) + } + + baseZ := group.NewInt(1) + + group.Exp(base, z, baseZ) + + basecalc := group.NewInt(1) + + basecalc = group.RootCoprime(baseZ, z, basecalc) + + if base.value.Cmp(basecalc.value) != 0 { + t.Errorf("FindSmallExponent Error: Result incorrect"+ + " on attempt %v; Expected: %s, Recieved: %s", i, base.value.Text(10), + basecalc.value.Text(10)) + } + } + + avgBitLen := float32(totalBitLen) / float32(num) + + if float32(avgBitLen) < 0.98*float32(bits) { + t.Errorf("FindSmallExponent Error: Inverses are not the correct length on average "+ + "; Expected: ~%v, Recieved: %v", 0.95*float32(bits), avgBitLen) + } + +} + +// Test finding a small coprime inverse in a group with small p +// This will hit the case where the generated number equals (p-1)/2 +func TestFindSmallCoprimeInverse_SmallGroup(t *testing.T) { + p := large.NewInt(107) + g := large.NewInt(2) + + group := NewGroup(p, g) + one := large.NewInt(1) + num := 1000 + + bits := uint32(p.BitLen() - 1) + + for i := 0; i < num; i++ { + z := group.NewInt(1) + + base := group.Random(group.NewInt(1)) + + // z will be unchanged if a number with no inverse is returned + for z.value.Cmp(one) == 0 { + group.FindSmallCoprimeInverse(z, bits) + } + + zinv := large.NewInt(1).ModInverse(z.value, group.psub1) + + if zinv.BitLen() > int(bits) { + t.Errorf("FindSmallExponent Error: Inverse too large on "+ + "attempt %v; Expected: <%v, Recieved: %v", i, bits, + zinv.BitLen()) + } + + baseZ := group.NewInt(1) + + group.Exp(base, z, baseZ) + + basecalc := group.NewInt(1) + + basecalc = group.RootCoprime(baseZ, z, basecalc) + + if base.value.Cmp(basecalc.value) != 0 { + t.Errorf("FindSmallExponent Error: Result incorrect"+ + " on attempt %v; Expected: %s, Recieved: %s", i, base.value.Text(10), + basecalc.value.Text(10)) + } + } +} + +// Test finding a small coprime inverse in an unsafe group, meaning +// that some numbers don't have an inverse +func TestFindSmallCoprimeInverse_UnsafeGroup(t *testing.T) { + p := large.NewInt(101) + g := large.NewInt(2) + + group := NewGroup(p, g) + one := large.NewInt(1) + num := 1000 + + bits := uint32(6) + + for i := 0; i < num; i++ { + z := group.NewInt(1) + + base := group.Random(group.NewInt(1)) + + // z will be unchanged if a number with no inverse is returned + for z.value.Cmp(one) == 0 { + group.FindSmallCoprimeInverse(z, bits) + } + + zinv := large.NewInt(1).ModInverse(z.value, group.psub1) + + if zinv.BitLen() > int(bits) { + t.Errorf("FindSmallExponent Error: Inverse too large on "+ + "attempt %v; Expected: <%v, Recieved: %v", i, bits, + zinv.BitLen()) + } + + baseZ := group.NewInt(1) + + group.Exp(base, z, baseZ) + + basecalc := group.NewInt(1) + + basecalc = group.RootCoprime(baseZ, z, basecalc) + + if base.value.Cmp(basecalc.value) != 0 { + t.Errorf("FindSmallExponent Error: Result incorrect"+ + " on attempt %v; Expected: %s, Recieved: %s", i, base.value.Text(10), + basecalc.value.Text(10)) + } + } +} + +// Test that FindSmallCoprimeInverse panics when number of bits is >= log2(p) +func TestFindSmallCoprimeInverse_Panic(t *testing.T) { + p := large.NewInt(107) + g := large.NewInt(2) + + group := NewGroup(p, g) + z := group.NewInt(1) + + bits := uint32(7) + + defer func() { + if r := recover(); r == nil { + t.Errorf("FindSmallCoprimeInverse should panic on bits >= log2(g.prime)") + } + }() + + group.FindSmallCoprimeInverse(z, bits) +} + +// Test that FindSmallCoprimeInverse panics if cyclicInt doesn't belong to the group +func TestFindSmallCoprimeInverse_PanicArgs(t *testing.T) { + prime := int64(107) + p := large.NewInt(prime) + g := large.NewInt(5) + group := NewGroup(p, g) + g2 := large.NewInt(2) + group2 := NewGroup(p, g2) + + a := group2.NewInt(20) + + defer func() { + if r := recover(); r == nil { + t.Errorf("RootCoprime should panic when " + + "cyclicInt doesn't belong to the group") + } + }() + + group.FindSmallCoprimeInverse(a, uint32(p.BitLen()-1)) +} + +// This test forces FindSmallCoprimeInverse to panic by overwriting the CSPRNG inside the group +func TestFindSmallCoprimeInverse_PanicReadErr(t *testing.T) { + p := large.NewInt(107) + g := large.NewInt(2) + + group := NewGroup(p, g) + + bits := uint32(p.BitLen() - 1) + + // Overwrite CSPRNG + group.rng = AlwaysErrorReader{} + + defer func() { + if r := recover(); r == nil { + t.Errorf("FindSmallCoprimeInverse should panic on read error") + } + }() + + group.FindSmallCoprimeInverse(group.NewInt(1), bits) +} + +// Tests that a Group structure that is encoded and then decoded, as a +// gob has the same values. +func TestGroup_GobEncode_GobDecode(t *testing.T) { + + prime := large.NewInt(1000000010101111111) + gen := large.NewInt(5) + grp1 := NewGroup(prime, gen) + + b, _ := grp1.GobEncode() + + grp2 := Group{} + _ = grp2.GobDecode(b) + + if !reflect.DeepEqual(*grp1, grp2) { + t.Errorf("GobDecode() did not produce the the same original undecoded data\n\treceived: %v\n\texpected: %v", grp1, grp2) + } +} + +// Tests that a Group structure can be marshaled to JSON and unmarshaled to recreate equivalent group +func TestGroup_MarshalJSON_IsValid(t *testing.T) { + + prime := large.NewInt(1000000010101111111) + gen := large.NewInt(5) + grp1 := NewGroup(prime, gen) + + // Marshall to bytes + b, err := grp1.MarshalJSON() + + if err != nil { + t.Errorf("MarshalJSON() failed to serialize the group: %v", grp1) + } + + // Unmarshal from bytes + grp2 := Group{} + err = grp2.UnmarshalJSON(b) + + if err != nil { + t.Errorf("UnmarshalJSON() failed to serialize the group: %v", grp1) + } + + if !reflect.DeepEqual(*grp1, grp2) { + t.Errorf("UnmarshalJSON() did not produce the the same original undecoded data\n\treceived: %v\n\texpected: %v", grp1, grp2) + } +} + +// BENCHMARKS + +func BenchmarkExpForGroup2k(b *testing.B) { + primeString := "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + + "15728E5A8AACAA68FFFFFFFFFFFFFFFF" + + p := large.NewIntFromString(primeString, 16) + g := large.NewInt(2) + grp := NewGroup(p, g) + + //prebake inputs + z := grp.NewInt(1) + G := grp.GetGCyclic() + + var inputs []*Int + var outputs []*Int + + for i := 0; i < b.N; i++ { + nint := grp.Random(grp.NewInt(1)) + inputs = append(inputs, nint) + outputs = append(outputs, grp.NewInt(1)) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + grp.Exp(G, inputs[i], z) + } +} + +func BenchmarkExpForGroup4k(b *testing.B) { + primeString := "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" + + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" + + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" + + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" + + "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" + + "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" + + "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" + + "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" + + "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" + + "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" + + "FFFFFFFFFFFFFFFF" + + p := large.NewIntFromString(primeString, 16) + g := large.NewInt(2) + grp := NewGroup(p, g) + + //prebake inputs + z := grp.NewInt(1) + G := grp.GetGCyclic() + + var inputs []*Int + var outputs []*Int + + for i := 0; i < b.N; i++ { + nint := grp.Random(grp.NewInt(1)) + inputs = append(inputs, nint) + outputs = append(outputs, grp.NewInt(1)) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + grp.Exp(G, inputs[i], z) + } +} + +func BenchmarkMulForGroup2k(b *testing.B) { + primeString := "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + + "15728E5A8AACAA68FFFFFFFFFFFFFFFF" + + p := large.NewIntFromString(primeString, 16) + g := large.NewInt(2) + grp := NewGroup(p, g) + + //prebake inputs + z := grp.NewInt(1) + + var inputA []*Int + var inputB []*Int + var outputs []*Int + + for i := 0; i < b.N; i++ { + nint := grp.Random(grp.NewInt(1)) + inputA = append(inputA, nint) + mint := grp.Random(grp.NewInt(1)) + inputB = append(inputB, mint) + outputs = append(outputs, grp.NewInt(1)) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + grp.Mul(inputA[i], inputB[i], z) + } +} + +func BenchmarkMulForGroup4k(b *testing.B) { + primeString := "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" + + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" + + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" + + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" + + "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" + + "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" + + "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" + + "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" + + "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" + + "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" + + "FFFFFFFFFFFFFFFF" + + p := large.NewIntFromString(primeString, 16) + g := large.NewInt(2) + grp := NewGroup(p, g) + + //prebake inputs + z := grp.NewInt(1) + + var inputA []*Int + var inputB []*Int + var outputs []*Int + + for i := 0; i < b.N; i++ { + nint := grp.Random(grp.NewInt(1)) + inputA = append(inputA, nint) + mint := grp.Random(grp.NewInt(1)) + inputB = append(inputB, mint) + outputs = append(outputs, grp.NewInt(1)) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + grp.Mul(inputA[i], inputB[i], z) + } +} + +func BenchmarkInverse2k(b *testing.B) { + primeString := "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + + "15728E5A8AACAA68FFFFFFFFFFFFFFFF" + + p := large.NewIntFromString(primeString, 16) + g := large.NewInt(2) + grp := NewGroup(p, g) + + //prebake inputs + z := grp.NewInt(1) + G := grp.GetGCyclic() + + var inputs []*Int + var outputs []*Int + + for i := 0; i < b.N; i++ { + nint := grp.Random(grp.NewInt(1)) + nint = grp.Exp(G, nint, z) + inputs = append(inputs, nint) + outputs = append(outputs, grp.NewInt(1)) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + grp.Inverse(inputs[i], outputs[i]) + } +} + +func BenchmarkInverse4k(b *testing.B) { + primeString := "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" + + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" + + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" + + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" + + "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" + + "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" + + "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" + + "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" + + "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" + + "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" + + "FFFFFFFFFFFFFFFF" + + p := large.NewIntFromString(primeString, 16) + g := large.NewInt(2) + grp := NewGroup(p, g) + + //prebake inputs + z := grp.NewInt(1) + G := grp.GetGCyclic() + + var inputs []*Int + var outputs []*Int + + for i := 0; i < b.N; i++ { + nint := grp.Random(grp.NewInt(1)) + nint = grp.Exp(G, nint, z) + inputs = append(inputs, nint) + outputs = append(outputs, grp.NewInt(1)) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + grp.Inverse(inputs[i], outputs[i]) + } +} + +func TestGroup_BytesInside(t *testing.T) { + test1 := []byte{5} + test2 := []byte{79} + test3 := []byte{17} + test4 := []byte{36} + grp := NewGroup(large.NewInt(107), large.NewInt(4)) + + if !grp.BytesInside(test1, test2, test3, test4) { + t.Errorf("BytesInside Failed!") + } +} diff --git a/cyclic/int.go b/cyclic/int.go new file mode 100644 index 00000000..f0fc4663 --- /dev/null +++ b/cyclic/int.go @@ -0,0 +1,262 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2022 xx foundation // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file. // +//////////////////////////////////////////////////////////////////////////////// + +// Package cyclic wraps our large.Int structure. It is designed to be used in +// conjunction with the cyclic.Group object. The cyclic.Group object +// will provide implementations of various modular operations within the group. +// A cyclic.IntBuffer type will be created to store large batches of groups. +package cyclic + +import ( + "bytes" + "encoding/base64" + "encoding/binary" + "encoding/gob" + "encoding/json" + "github.com/pkg/errors" + "gitlab.com/xx_network/crypto/large" +) + +// Create the cyclic.Int type as a wrapper of a large.Int +// and group fingerprint +type Int struct { + value *large.Int + fingerprint uint64 +} + +// ByteLen gets the byte length of cyclic int +func (z *Int) ByteLen() int { + byteLen := z.value.ByteLen() + return byteLen +} + +// GetLargeInt gets a deepcopy of the largeInt from cyclicInt +// This is necessary because otherwise the internal +// value of the into could be edited and made to be +// outside the group. +func (z *Int) GetLargeInt() *large.Int { + r := large.NewInt(0) + r.Set(z.value) + return r +} + +// GetGroupFingerprint gets the group fingerprint from cyclicInt +func (z *Int) GetGroupFingerprint() uint64 { + return z.fingerprint +} + +// Bits gets the underlying word slice of cyclic int +// Use this for low-level functions where speed is critical +// For speed reasons, I don't copy here. This could allow the int to be set outside of the group +func (z *Int) Bits() large.Bits { + return z.value.Bits() +} + +// Bytes gets the bytes of cyclicInt value +func (z *Int) Bytes() []byte { + return z.value.Bytes() +} + +// LeftpadBytes gets left padded bytes of cyclicInt value +func (z *Int) LeftpadBytes(length uint64) []byte { + return z.value.LeftpadBytes(length) +} + +// BitLen gets the length of the cyclic int +func (z *Int) BitLen() int { + return z.value.BitLen() +} + +// DeepCopy returns a complete copy of the cyclic int such that no +// underlying data is linked +func (z *Int) DeepCopy() *Int { + return &Int{ + z.value.DeepCopy(), + z.fingerprint, + } +} + +// Compare two cyclicInts +// returns -2 if fingerprint differs +// returns value.Cmp otherwise +func (z *Int) Cmp(x *Int) int { + if z.fingerprint != x.fingerprint { + return -2 + } + return z.value.Cmp(x.value) +} + +// Reset cyclicInt to 1 +func (z *Int) Reset() { + z.value.SetInt64(1) +} + +// Return truncated base64 encoded string of group fingerprint +func (z *Int) textFingerprint(length int) string { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, z.fingerprint) + fullText := base64.StdEncoding.EncodeToString(buf) + if length == 0 || len(fullText) <= length { + return fullText + } else { + return fullText[:length] + "..." + } +} + +// Text returns the string representation of z in the given base. Base +// must be between 2 and 36, inclusive. The result uses the lower-case +// letters 'a' to 'z' for digit values >= 10. No base prefix (such as +// "0x") is added to the string. +// Text truncates ints to a length of 10, appending an ellipsis +// if the int is too long. +// The group fingerprint is base64 encoded and also truncated +// z is then represented as: value... in GRP: fingerprint... +func (z *Int) Text(base int) string { + const intTextLen = 10 + return z.TextVerbose(base, intTextLen) +} + +// TextVerbose returns the string representation of z in the given base. Base +// must be between 2 and 36, inclusive. The result uses the lower-case +// letters 'a' to 'z' for digit values >= 10. No base prefix (such as +// "0x") is added to the string. +// TextVerbose truncates ints to a length of length in characters (not runes) +// and append an ellipsis to indicate that the whole int wasn't returned, +// unless len is 0, in which case it will return the whole int as a string. +// The group fingerprint is base64 encoded and also truncated +// z is then represented as: value... in GRP: fingerprint... +func (z *Int) TextVerbose(base int, length int) string { + valueText := z.value.TextVerbose(base, length) + fingerprintText := z.textFingerprint(length) + return valueText + " in GRP: " + fingerprintText +} + +// GOB decode bytes to cyclicInt +func (z *Int) GobDecode(in []byte) error { + // anonymous structure + s := struct { + F []byte + V []byte + }{ + make([]byte, 8), + []byte{}, + } + + var buf bytes.Buffer + + // Write bytes to the buffer + buf.Write(in) + + // Create new decoder that reads from the buffer + dec := gob.NewDecoder(&buf) + + // Receive and decode data + err := dec.Decode(&s) + + if err != nil { + return err + } + + // Convert decoded bytes and put into empty structure + z.value = large.NewIntFromBytes(s.V) + z.fingerprint = binary.BigEndian.Uint64(s.F) + + return nil +} + +// GOB encode cyclicInt to bytes +func (z *Int) GobEncode() ([]byte, error) { + // Anonymous structure + s := struct { + F []byte + V []byte + }{ + make([]byte, 8), + z.Bytes(), + } + + binary.BigEndian.PutUint64(s.F, z.fingerprint) + var buf bytes.Buffer + + // Create new encoder that will transmit the buffer + enc := gob.NewEncoder(&buf) + + // Transmit the data + err := enc.Encode(s) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +// BinaryEncode encodes the Int into a compressed byte format. +func (z *Int) BinaryEncode() []byte { + var buff bytes.Buffer + b := make([]byte, 8) + + binary.LittleEndian.PutUint64(b, z.fingerprint) + buff.Write(b) + buff.Write(z.Bytes()) + + return buff.Bytes() +} + +// BinaryDecode decompresses the encoded byte slice to an Int. +func (z *Int) BinaryDecode(b []byte) error { + if len(b) < 8 { + return errors.Errorf("length of buffer %d != %d expected", len(b), 8) + } + + buff := bytes.NewBuffer(b) + z.fingerprint = binary.LittleEndian.Uint64(buff.Next(8)) + z.value = large.NewIntFromBytes(buff.Bytes()) + return nil +} + +// Erase overwrite all underlying data from a cyclic Int by setting its value +// and fingerprint to zero. All underlying released data will be removed by the +// garbage collector. +func (z *Int) Erase() { + z.value.SetInt64(0) + z.fingerprint = 0 +} + +// -------------- Marshal Operators -------------- // +// intData holds the value of a cyclic int in public fields to allow for +// marshalling and unmarshalling. +type intData struct { + Value *large.Int + Fingerprint uint64 +} + +// MarshalJSON is a custom marshaling function for cyclic int. It is used when +// json.Marshal is called on a large int. +func (z *Int) MarshalJSON() ([]byte, error) { + data := intData{ + Value: z.value, + Fingerprint: z.fingerprint, + } + + return json.Marshal(data) +} + +// UnmarshalJSON is a custom unmarshalling function for cyclic int. It is used +// when json.Unmarshal is called on a large int. +func (z *Int) UnmarshalJSON(b []byte) error { + data := &intData{} + err := json.Unmarshal(b, data) + if err != nil { + return err + } + + z.value = data.Value + z.fingerprint = data.Fingerprint + + return nil +} diff --git a/cyclic/int_test.go b/cyclic/int_test.go new file mode 100644 index 00000000..73d625c6 --- /dev/null +++ b/cyclic/int_test.go @@ -0,0 +1,363 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2022 xx foundation // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file. // +//////////////////////////////////////////////////////////////////////////////// + +package cyclic + +import ( + "bytes" + "encoding/gob" + "errors" + "gitlab.com/xx_network/crypto/large" + "reflect" + "testing" +) + +var p = large.NewInt(1000000010101111111) +var g = large.NewInt(5) +var grp = NewGroup(p, g) + +// Test largeInt getter and show it returns a copy +func TestGetLargeInt(t *testing.T) { + expected := large.NewInt(42) + + actual := grp.NewInt(42) + + if actual.GetLargeInt().Cmp(expected) != 0 { + t.Errorf("Test of GetLargeInt failed, expected: '%v', got: '%v'", + actual.GetLargeInt(), expected) + } + + li := actual.GetLargeInt() + + li.SetInt64(33) + + if actual.GetLargeInt().Cmp(expected) != 0 { + t.Errorf("Test of GetLargeInt failed, did not create deep copy") + } + +} + +// Test group fingeprint getter +func TestGetGroupFingerprint(t *testing.T) { + + expected := grp.GetFingerprint() + + actual := grp.NewInt(int64(42)) + + if actual.GetGroupFingerprint() != expected { + t.Errorf("Test of GetGroupFingerprint failed, expected: '%v', got: '%v'", + actual.GetGroupFingerprint(), expected) + } +} + +// Test bytes getter +func TestBytes(t *testing.T) { + expected := []byte{0x2A} + + actual := grp.NewInt(int64(42)) + + if !bytes.Equal(actual.Bytes(), expected) { + t.Errorf("Test of Bytes failed, expected: '%v', got: '%v'", + actual.Bytes(), expected) + } +} + +// Test left padded bytes getter +func TestLeftpadBytes(t *testing.T) { + expected := []byte{0x00, 0x00, 0x00, 0x2A} + + actual := grp.NewInt(int64(42)) + + if !bytes.Equal(actual.LeftpadBytes(4), expected) { + t.Errorf("Test of LeftPadBytes failed, expected: '%v', got: '%v'", + actual.LeftpadBytes(4), expected) + } +} + +//TestBitLen checks if BitLen works +func TestBitLen(t *testing.T) { + testints := []*Int{ + grp.NewInt(42), + grp.NewInt(6553522), + grp.NewInt(7777), + grp.NewInt(21234)} + + expectedlens := []int{ + 6, + 23, + 13, + 15} + + for i, tsti := range testints { + actual := tsti.BitLen() + if actual != expectedlens[i] { + t.Errorf("Case %v of BitLen failed, got: '%v', expected: '%v'", i, actual, + expectedlens[i]) + } + } +} + +// Tests that the copy returned by deep copy is identical and that editing +// one does not edit the other +func TestInt_DeepCopy(t *testing.T) { + i := grp.NewInt(55) + + cpy := i.DeepCopy() + + if !reflect.DeepEqual(i, cpy) { + t.Errorf("Test of DeepCopy failed, fingerprints did not match "+ + "expected: '%#v', got: '%#v'", i, cpy) + } + + cpy.fingerprint = ^cpy.fingerprint + + cpy.value.SetInt64(42) + + if i.fingerprint == cpy.fingerprint { + t.Errorf("Test of DeepCopy failed, fingerprints matched after edit "+ + "expected: '%#v', got: '%#v'", i.fingerprint, cpy.fingerprint) + } + + if reflect.DeepEqual(i.value, cpy.value) { + t.Errorf("Test of DeepCopy failed, values matched after edit"+ + "expected: '%#v', got: '%#v'", i.value.Text(16), cpy.value.Text(16)) + } +} + +// Test that Cmp works, and that it returns -2 when fingerprints differ +func TestCmp(t *testing.T) { + val1 := grp.NewInt(int64(42)) + val2 := grp.NewInt(int64(42)) + + ret := val1.Cmp(val2) + + if ret != 0 { + t.Errorf("Test of Cmp failed, expected: 0, "+ + "got: '%v'", ret) + } + // Overwrite group fingerprint and confirm Cmp returns -1 + val2.fingerprint = uint64(1234) + + ret = val1.Cmp(val2) + + if ret != -2 { + t.Errorf("Test of Cmp failed, expected: -2, "+ + "got: '%v'", ret) + } +} + +// Test that Clear works by setting value to 1 +func TestReset(t *testing.T) { + actual := grp.NewInt(42) + expected := large.NewInt(42) + + // Verify proper initialization to expected + if actual.value.Cmp(expected) != 0 { + t.Errorf("Value not initialized correctly") + } + + // Call reset on cyclic Int + actual.Reset() + expected = large.NewInt(1) + + // Ensure it is equal to 1 + if actual.value.Cmp(expected) != 0 { + t.Errorf("Test of GetLargeInt failed, expected: '%v', got: '%v'", + actual.GetLargeInt(), expected) + } +} + +// Test text representation (limited to length of 10) +func TestText(t *testing.T) { + testints := []*Int{ + grp.NewInt(42), + grp.NewInt(6553522), + grp.NewIntFromString("8675309182", 10), + grp.NewInt(43)} + expectedstrs := []string{ + "42 in GRP: ln9lzlk21/...", + "6553522 in GRP: ln9lzlk21/...", + "8675309182 in GRP: ln9lzlk21/...", + "43 in GRP: ln9lzlk21/..."} // TODO: Should be <nil>, not -42 + + for i, tsti := range testints { + actual := tsti.Text(10) + expected := expectedstrs[i] + if actual != expected { + t.Errorf("Test of Text failed, got: '%v', expected: '%v'", actual, + expected) + } + } +} + +// Test text verbose representation with different lengths +func TestTextVerbose(t *testing.T) { + p_t := large.NewIntFromString("867530918239450598372829049587118723612836", 10) + g_t := large.NewInt(5) + group := NewGroup(p_t, g_t) + + testInt := group.NewIntFromString("867530918239450598372829049587", 10) + lens := []int{3, 12, 16, 18, 0} + expected := []string{ + "867... in GRP: XND...", + "867530918239... in GRP: XNDDRA8PF/4=", + "8675309182394505... in GRP: XNDDRA8PF/4=", + "867530918239450598... in GRP: XNDDRA8PF/4=", + "867530918239450598372829049587 in GRP: XNDDRA8PF/4="} + + for i, testLen := range lens { + actual := testInt.TextVerbose(10, testLen) + if actual != expected[i] { + t.Errorf("Test of TextVerbose failed, got: %v,"+ + "expected: %v", actual, expected[i]) + } + } +} + +//TestByteLen checks if the ByteLen placeholder exists +func TestByteLen(t *testing.T) { + testints := []*Int{ + grp.NewInt(1), //1 bits --> 1 byte (where +7 works) + grp.NewInt(8388608), //24 bits --> 3 bytes (exactly) + grp.NewInt(7777), //13 bits --> 2 bytes (where +3 works) + grp.NewInt(1002), //10 bits --> 2 bytes (where +6 works) + } + + expectedlens := []int{ + 1, + 3, + 2, + 2, + } + + for i, tsti := range testints { + actual := tsti.ByteLen() + if actual != expectedlens[i] { + t.Errorf("Case %v of ByteLen failed, got: '%v', expected: '%v'", i, actual, + expectedlens[i]) + } + } +} + +// Test GOB encoding/decoding +func TestGob(t *testing.T) { + var byteBuf bytes.Buffer + + enc := gob.NewEncoder(&byteBuf) + dec := gob.NewDecoder(&byteBuf) + + inInt := grp.NewInt(42) + + err := enc.Encode(inInt) + + if err != nil { + t.Errorf("Error GOB Encoding Int: %s", err) + } + + outInt := grp.NewInt(1) + + err = dec.Decode(&outInt) + + if err != nil { + t.Errorf("Error GOB Decoding Int: %s", err) + } + + if inInt.Cmp(outInt) != 0 { + t.Errorf("GobEncoder/GobDecoder failed, "+ + "Expected: %v; Received: %v ", + inInt.TextVerbose(10, 12), + outInt.TextVerbose(10, 12)) + } +} + +// Tests that GobDecode() for cyclicInt throws an error for a +// malformed byte array +func TestGobDecode_Error(t *testing.T) { + inInt := Int{} + err := inInt.GobDecode([]byte{}) + + if !reflect.DeepEqual(err, errors.New("EOF")) { + t.Errorf("GobDecode() did not produce the expected error\n\treceived: %v"+ + "\n\texpected: %v", err, errors.New("EOF")) + } +} + +// Tests that Erase() removes all underlying data from the Int. +func TestInt_Erase(t *testing.T) { + cycInt := grp.NewInt(42) + zeroInt := large.NewInt(5).SetInt64(0) + cycInt.Erase() + + if !reflect.DeepEqual(cycInt.value, zeroInt) { + t.Errorf("Erase() did not properly delete Int's underlying value"+ + "\n\treceived: %#v\n\texpected: %#v", + cycInt.value, zeroInt) + } + + if cycInt.fingerprint != 0 { + t.Errorf("Erase() did not properly delete Int's underlying fingerprint"+ + "\n\treceived: %#v\n\texpected: %#v", + cycInt.fingerprint, 0) + } +} + +// Happy path. +func TestInt_MarshalJSON_UnmarshalJSON(t *testing.T) { + cycInt := grp.NewInt(42) + data, err := cycInt.MarshalJSON() + if err != nil { + t.Errorf("MarshalJSON() returned an error: %+v", err) + } + outInt := &Int{} + err = outInt.UnmarshalJSON(data) + if err != nil { + t.Errorf("UnmarshalJSON() returned an error: %+v", err) + } + + if cycInt.Cmp(outInt) != 0 { + t.Errorf("Failed to correctly marshal and unmarshal cyclic int."+ + "\n\texpected: %s\n\trecieved: %s", cycInt.Text(10), outInt.Text(10)) + } +} + +// Error path: invalid JSON data. +func TestInt_UnmarshalJSON(t *testing.T) { + data := []byte("invalid JSON") + outInt := &Int{} + err := outInt.UnmarshalJSON(data) + if err == nil { + t.Errorf("UnmarshalJSON() did not return an error for invalid JSON.") + } +} + +// Happy path. +func TestInt_BinaryEncode_BinaryDecode(t *testing.T) { + cycInt := grp.NewInt(42) + buff := cycInt.BinaryEncode() + testInt := &Int{} + err := testInt.BinaryDecode(buff) + if err != nil { + t.Errorf("BinaryDecode() returned an error: %+v", err) + } + + if cycInt.Cmp(testInt) != 0 { + t.Errorf("Failed to encode and decode Int."+ + "\n\texpected: %s\n\treceived: %s", + cycInt.TextVerbose(10, 32), testInt.TextVerbose(10, 32)) + } +} + +// Error path: buffer not long enough +func TestInt_BinaryDecode_BuffTooShortErr(t *testing.T) { + cycInt := grp.NewInt(42) + buff := cycInt.BinaryEncode() + testInt := &Int{} + err := testInt.BinaryDecode(buff[:2]) + if err == nil { + t.Errorf("BinaryDecode() did not return an error on a buffer that is too short.") + } +} diff --git a/diffieHellman/dhkx.go b/diffieHellman/dhkx.go index 89b4e5fa..d7cdf7eb 100644 --- a/diffieHellman/dhkx.go +++ b/diffieHellman/dhkx.go @@ -13,8 +13,8 @@ import ( jww "github.com/spf13/jwalterweatherman" "io" + "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/xx_network/crypto/csprng" - "gitlab.com/xx_network/crypto/cyclic" ) const DefaultPrivateKeyLengthBits = 256 diff --git a/diffieHellman/dhkx_test.go b/diffieHellman/dhkx_test.go index 62f757b0..05e532ef 100644 --- a/diffieHellman/dhkx_test.go +++ b/diffieHellman/dhkx_test.go @@ -9,8 +9,8 @@ package diffieHellman import ( "encoding/hex" + "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/xx_network/crypto/csprng" - "gitlab.com/xx_network/crypto/cyclic" "gitlab.com/xx_network/crypto/large" "testing" ) @@ -56,7 +56,7 @@ func TestGeneratePrivateKey(t *testing.T) { } } -// tests public keys are generated correctly +//tests public keys are generated correctly func TestGeneratePublicKey(t *testing.T) { const numTests = 50 @@ -97,7 +97,7 @@ func TestGeneratePublicKey(t *testing.T) { } } -// tests Session keys are generated correctly +//tests Session keys are generated correctly func TestGenerateSessionKey(t *testing.T) { const numTests = 50 @@ -196,7 +196,7 @@ func TestCheckPublicKey(t *testing.T) { } -// benchmarks session key creation +//benchmarks session key creation func BenchmarkCreateDHSessionKey(b *testing.B) { primeString := "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + diff --git a/dm/selfCipher.go b/dm/selfCipher.go index 66b08a1e..c774d333 100644 --- a/dm/selfCipher.go +++ b/dm/selfCipher.go @@ -13,10 +13,10 @@ import ( "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/crypto/hash" "gitlab.com/elixxir/crypto/nike" "gitlab.com/elixxir/crypto/nike/ecdh" "gitlab.com/xx_network/crypto/csprng" - "gitlab.com/xx_network/crypto/hash" "golang.org/x/crypto/chacha20poly1305" ) diff --git a/e2e/auth/encryptDecrypt.go b/e2e/auth/encryptDecrypt.go index b63d31a5..a2a13865 100644 --- a/e2e/auth/encryptDecrypt.go +++ b/e2e/auth/encryptDecrypt.go @@ -8,7 +8,7 @@ package auth import ( - "gitlab.com/xx_network/crypto/cyclic" + "gitlab.com/elixxir/crypto/cyclic" ) // Encrypts the payload for use in authenticated channels and provides a MAC @@ -17,7 +17,7 @@ func Encrypt(myPrivKey, partnerPubKey *cyclic.Int, payload []byte, grp *cyclic.Group) (ecrPayload, mac []byte) { // Generate the base key - authKey, vec := MakeAuthKey(myPrivKey, partnerPubKey, grp) + authKey, vec := MakeAuthKey(myPrivKey, partnerPubKey,grp) // Encrypt the payload ecrPayload = Crypt(authKey, vec, payload) diff --git a/e2e/auth/keygen.go b/e2e/auth/keygen.go index f99b6eb1..a81ff3fd 100644 --- a/e2e/auth/keygen.go +++ b/e2e/auth/keygen.go @@ -9,9 +9,9 @@ package auth import ( jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/crypto/cyclic" dh "gitlab.com/elixxir/crypto/diffieHellman" - "gitlab.com/xx_network/crypto/cyclic" - "gitlab.com/xx_network/crypto/hash" + "gitlab.com/elixxir/crypto/hash" ) // Const string which gets hashed into the auth key diff --git a/e2e/auth/mac.go b/e2e/auth/mac.go index 0d311f4f..deff88eb 100644 --- a/e2e/auth/mac.go +++ b/e2e/auth/mac.go @@ -14,7 +14,7 @@ package auth import ( "crypto/hmac" - "gitlab.com/xx_network/crypto/hash" + "gitlab.com/elixxir/crypto/hash" ) // MakeMac returns the MAC for the given payload. diff --git a/e2e/auth/negotiationFingerprint.go b/e2e/auth/negotiationFingerprint.go index 7ba99486..0faf390b 100644 --- a/e2e/auth/negotiationFingerprint.go +++ b/e2e/auth/negotiationFingerprint.go @@ -10,8 +10,8 @@ package auth import ( "github.com/cloudflare/circl/dh/sidh" jww "github.com/spf13/jwalterweatherman" - "gitlab.com/xx_network/crypto/cyclic" - "gitlab.com/xx_network/crypto/hash" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/crypto/hash" ) const NegotiationFingerprintLen = 32 diff --git a/e2e/auth/negotiationFingerprint_test.go b/e2e/auth/negotiationFingerprint_test.go index e4a40794..e136f9e2 100644 --- a/e2e/auth/negotiationFingerprint_test.go +++ b/e2e/auth/negotiationFingerprint_test.go @@ -10,9 +10,9 @@ package auth import ( "encoding/base64" "github.com/cloudflare/circl/dh/sidh" + "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/diffieHellman" "gitlab.com/xx_network/crypto/csprng" - "gitlab.com/xx_network/crypto/cyclic" "io" "math/rand" "testing" diff --git a/e2e/auth/ownership.go b/e2e/auth/ownership.go index fbe50b54..dd78f531 100644 --- a/e2e/auth/ownership.go +++ b/e2e/auth/ownership.go @@ -10,10 +10,10 @@ package auth import ( "crypto/hmac" + "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/diffieHellman" + "gitlab.com/elixxir/crypto/hash" "gitlab.com/elixxir/primitives/format" - "gitlab.com/xx_network/crypto/cyclic" - "gitlab.com/xx_network/crypto/hash" ) const ownershipVector = "ownershipVector" diff --git a/e2e/auth/ownership_test.go b/e2e/auth/ownership_test.go index 0f392bec..8d83d528 100644 --- a/e2e/auth/ownership_test.go +++ b/e2e/auth/ownership_test.go @@ -14,9 +14,9 @@ import ( "testing" "github.com/stretchr/testify/require" + "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/diffieHellman" "gitlab.com/elixxir/crypto/nike/dh" - "gitlab.com/xx_network/crypto/cyclic" "gitlab.com/xx_network/crypto/large" ) diff --git a/e2e/auth/requestFP.go b/e2e/auth/requestFP.go index 7bf676aa..a34afe4f 100644 --- a/e2e/auth/requestFP.go +++ b/e2e/auth/requestFP.go @@ -8,9 +8,9 @@ package auth import ( + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/crypto/hash" "gitlab.com/elixxir/primitives/format" - "gitlab.com/xx_network/crypto/cyclic" - "gitlab.com/xx_network/crypto/hash" ) // the auth request fingerprint designates that a message is an auth request @@ -18,7 +18,7 @@ import ( const authRequestFingerprintVector = "authRequestFingerprintVector" -// Sets the message as an authenticated channel creation message +//Sets the message as an authenticated channel creation message func SetRequestFingerprint(m format.Message, partnerPublicKey *cyclic.Int) { //get the key hash @@ -28,7 +28,7 @@ func SetRequestFingerprint(m format.Message, partnerPublicKey *cyclic.Int) { m.SetKeyFP(keyHash) } -// creates a valid auth request fingerprint from a public key +//creates a valid auth request fingerprint from a public key func MakeRequestFingerprint(publicKey *cyclic.Int) format.Fingerprint { // Create new hash //suppress because we just panic and a nil hash will panic anyhow diff --git a/e2e/dummykeygen.go b/e2e/dummykeygen.go index 2cd81d5c..8a497e4a 100644 --- a/e2e/dummykeygen.go +++ b/e2e/dummykeygen.go @@ -11,8 +11,8 @@ package e2e import ( "bytes" - "gitlab.com/xx_network/crypto/cyclic" - "gitlab.com/xx_network/crypto/hash" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/crypto/hash" "gitlab.com/xx_network/primitives/id" goHash "hash" ) diff --git a/e2e/dummykeygen_test.go b/e2e/dummykeygen_test.go index 478736a3..9fceec39 100644 --- a/e2e/dummykeygen_test.go +++ b/e2e/dummykeygen_test.go @@ -8,7 +8,7 @@ package e2e import ( - "gitlab.com/xx_network/crypto/cyclic" + "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/xx_network/crypto/large" "gitlab.com/xx_network/primitives/id" "os" diff --git a/e2e/encryptionChecker.go b/e2e/encryptionChecker.go index 167f779b..e09bf5bf 100644 --- a/e2e/encryptionChecker.go +++ b/e2e/encryptionChecker.go @@ -13,8 +13,8 @@ import ( "crypto/hmac" jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/crypto/hash" "gitlab.com/elixxir/primitives/format" - "gitlab.com/xx_network/crypto/hash" "gitlab.com/xx_network/primitives/id" ) diff --git a/e2e/encryptionChecker_test.go b/e2e/encryptionChecker_test.go index 1c5ae752..04a6f611 100644 --- a/e2e/encryptionChecker_test.go +++ b/e2e/encryptionChecker_test.go @@ -12,8 +12,8 @@ import ( "math/rand" "testing" + "gitlab.com/elixxir/crypto/hash" "gitlab.com/elixxir/primitives/format" - "gitlab.com/xx_network/crypto/hash" "gitlab.com/xx_network/primitives/id" ) diff --git a/e2e/keys.go b/e2e/keys.go index 2afcf5dd..f6c4c5d4 100644 --- a/e2e/keys.go +++ b/e2e/keys.go @@ -11,8 +11,8 @@ package e2e import ( jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/primitives/format" - "gitlab.com/xx_network/crypto/cyclic" "golang.org/x/crypto/blake2b" ) diff --git a/e2e/keys_test.go b/e2e/keys_test.go index a01bd330..32a00338 100644 --- a/e2e/keys_test.go +++ b/e2e/keys_test.go @@ -11,14 +11,14 @@ import ( "bytes" "encoding/base64" "fmt" - "gitlab.com/xx_network/crypto/cyclic" + "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/xx_network/primitives/id" "math/rand" "reflect" "testing" ) -// test consistency for DeriveKey +//test consistency for DeriveKey func TestDeriveKey_Consistency(t *testing.T) { expectedKeys := []string{ "2gQs27wW7ckb6zGUBibdyYs6/aBqJxK2YFW6hToO2EI=", @@ -76,7 +76,7 @@ func TestDeriveKey_Consistency_doubleSalt(t *testing.T) { deriveConsistencyTester(t, expectedKeys, d, "DeriveKey()") } -// test consistency for DeriveKeyFingerprint +//test consistency for DeriveKeyFingerprint func TestDeriveKeyFingerprint_Consistency(t *testing.T) { expectedKeys := []string{ "biUwFTuy+udrvH9iMCjBfen4seZAC9Q/5yZMwtVVTyk=", @@ -126,7 +126,7 @@ func deriveConsistencyTester(t *testing.T, expectedKeys []string, d func(dhkey * } } -// verifies that all derived fingerprints and keys are different +//verifies that all derived fingerprints and keys are different func TestAllDifferent(t *testing.T) { const numtests = 25 diff --git a/e2e/messageID.go b/e2e/messageID.go index eddf72a5..31cf853f 100644 --- a/e2e/messageID.go +++ b/e2e/messageID.go @@ -12,7 +12,7 @@ import ( "encoding/binary" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" - "gitlab.com/xx_network/crypto/hash" + "gitlab.com/elixxir/crypto/hash" ) const MessageIDLen = 32 diff --git a/e2e/relationshipFingerprint.go b/e2e/relationshipFingerprint.go index 2bfc2414..f2d8aa0e 100644 --- a/e2e/relationshipFingerprint.go +++ b/e2e/relationshipFingerprint.go @@ -9,8 +9,8 @@ package e2e import ( jww "github.com/spf13/jwalterweatherman" - "gitlab.com/xx_network/crypto/cyclic" - "gitlab.com/xx_network/crypto/hash" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/crypto/hash" "gitlab.com/xx_network/primitives/id" ) diff --git a/e2e/relationshipFingerprint_test.go b/e2e/relationshipFingerprint_test.go index 37dbd615..1ca37c58 100644 --- a/e2e/relationshipFingerprint_test.go +++ b/e2e/relationshipFingerprint_test.go @@ -9,13 +9,13 @@ package e2e import ( "bytes" - "gitlab.com/xx_network/crypto/cyclic" + "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/xx_network/crypto/large" "gitlab.com/xx_network/primitives/id" "testing" ) -// show that every input changes the output hash +//show that every input changes the output hash func TestMakeRelationshipFingerprint(t *testing.T) { grp := getGroup() diff --git a/e2e/residue.go b/e2e/residue.go index bddd20ff..29b1e3ad 100644 --- a/e2e/residue.go +++ b/e2e/residue.go @@ -10,7 +10,7 @@ package e2e import ( "encoding/base64" "github.com/pkg/errors" - "gitlab.com/xx_network/crypto/hash" + "gitlab.com/elixxir/crypto/hash" ) // KeyResidue generation constants. diff --git a/e2e/singleUse/mac.go b/e2e/singleUse/mac.go index ab19c062..601b085e 100644 --- a/e2e/singleUse/mac.go +++ b/e2e/singleUse/mac.go @@ -10,7 +10,7 @@ package singleUse import ( "crypto/hmac" - "gitlab.com/xx_network/crypto/hash" + "gitlab.com/elixxir/crypto/hash" ) const macSalt = "singleUseMacSalt" diff --git a/e2e/singleUse/mac_test.go b/e2e/singleUse/mac_test.go index b6cc5b97..c884a83f 100644 --- a/e2e/singleUse/mac_test.go +++ b/e2e/singleUse/mac_test.go @@ -9,7 +9,7 @@ package singleUse import ( "encoding/base64" - "gitlab.com/xx_network/crypto/cyclic" + "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/xx_network/crypto/large" "math/rand" "testing" diff --git a/e2e/singleUse/recipientID.go b/e2e/singleUse/recipientID.go index e5e52dc5..84c048b8 100644 --- a/e2e/singleUse/recipientID.go +++ b/e2e/singleUse/recipientID.go @@ -9,8 +9,8 @@ package singleUse import ( jww "github.com/spf13/jwalterweatherman" - "gitlab.com/xx_network/crypto/cyclic" - "gitlab.com/xx_network/crypto/hash" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/crypto/hash" "gitlab.com/xx_network/primitives/id" ) diff --git a/e2e/singleUse/recipientID_test.go b/e2e/singleUse/recipientID_test.go index e62c4b36..5a25c553 100644 --- a/e2e/singleUse/recipientID_test.go +++ b/e2e/singleUse/recipientID_test.go @@ -8,8 +8,8 @@ package singleUse import ( + "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/diffieHellman" - "gitlab.com/xx_network/crypto/cyclic" "gitlab.com/xx_network/primitives/id" "math/rand" "testing" diff --git a/e2e/singleUse/requestFingerprint.go b/e2e/singleUse/requestFingerprint.go index c9802757..73a02ce5 100644 --- a/e2e/singleUse/requestFingerprint.go +++ b/e2e/singleUse/requestFingerprint.go @@ -9,9 +9,9 @@ package singleUse import ( jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/crypto/hash" "gitlab.com/elixxir/primitives/format" - "gitlab.com/xx_network/crypto/cyclic" - "gitlab.com/xx_network/crypto/hash" ) const requestFpSalt = "singleUseTransmitFingerprintSalt" diff --git a/e2e/singleUse/requestFingerprint_test.go b/e2e/singleUse/requestFingerprint_test.go index 4925809a..e90fa2e0 100644 --- a/e2e/singleUse/requestFingerprint_test.go +++ b/e2e/singleUse/requestFingerprint_test.go @@ -9,9 +9,9 @@ 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/cyclic" "math/rand" "testing" ) diff --git a/e2e/singleUse/requestKey.go b/e2e/singleUse/requestKey.go index 00915546..d2ce4be1 100644 --- a/e2e/singleUse/requestKey.go +++ b/e2e/singleUse/requestKey.go @@ -9,8 +9,8 @@ package singleUse import ( jww "github.com/spf13/jwalterweatherman" - "gitlab.com/xx_network/crypto/cyclic" - "gitlab.com/xx_network/crypto/hash" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/crypto/hash" ) const requestKeySalt = "singleUseTransmitKeySalt" diff --git a/e2e/singleUse/requestKey_test.go b/e2e/singleUse/requestKey_test.go index 4333288a..e0162c5e 100644 --- a/e2e/singleUse/requestKey_test.go +++ b/e2e/singleUse/requestKey_test.go @@ -9,8 +9,8 @@ package singleUse import ( "encoding/base64" + "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/diffieHellman" - "gitlab.com/xx_network/crypto/cyclic" "math/rand" "testing" ) diff --git a/e2e/singleUse/requestPartFingerprint.go b/e2e/singleUse/requestPartFingerprint.go index 1b9ae275..0990325f 100644 --- a/e2e/singleUse/requestPartFingerprint.go +++ b/e2e/singleUse/requestPartFingerprint.go @@ -10,9 +10,9 @@ 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" - "gitlab.com/xx_network/crypto/cyclic" - "gitlab.com/xx_network/crypto/hash" ) const requestPartFpSalt = "singleUseRequestFingerprintSalt" diff --git a/e2e/singleUse/requestPartFingerprint_test.go b/e2e/singleUse/requestPartFingerprint_test.go index 6a7e4361..4d91249f 100644 --- a/e2e/singleUse/requestPartFingerprint_test.go +++ b/e2e/singleUse/requestPartFingerprint_test.go @@ -9,9 +9,9 @@ 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/cyclic" "math/rand" "testing" ) diff --git a/e2e/singleUse/requestPartKey.go b/e2e/singleUse/requestPartKey.go index 3334ade7..b984efe4 100644 --- a/e2e/singleUse/requestPartKey.go +++ b/e2e/singleUse/requestPartKey.go @@ -10,8 +10,8 @@ package singleUse import ( "encoding/binary" jww "github.com/spf13/jwalterweatherman" - "gitlab.com/xx_network/crypto/cyclic" - "gitlab.com/xx_network/crypto/hash" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/crypto/hash" ) const requestPartKeySalt = "singleUseRequestKeySalt" diff --git a/e2e/singleUse/requestPartKey_test.go b/e2e/singleUse/requestPartKey_test.go index 5bf5fc25..492751f9 100644 --- a/e2e/singleUse/requestPartKey_test.go +++ b/e2e/singleUse/requestPartKey_test.go @@ -9,8 +9,8 @@ package singleUse import ( "encoding/base64" + "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/diffieHellman" - "gitlab.com/xx_network/crypto/cyclic" "math/rand" "testing" ) diff --git a/e2e/singleUse/responseFingerprint.go b/e2e/singleUse/responseFingerprint.go index 517c7d56..4f8273b5 100644 --- a/e2e/singleUse/responseFingerprint.go +++ b/e2e/singleUse/responseFingerprint.go @@ -10,9 +10,9 @@ 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" - "gitlab.com/xx_network/crypto/cyclic" - "gitlab.com/xx_network/crypto/hash" ) const responseFpSalt = "singleUseResponseFingerprintSalt" diff --git a/e2e/singleUse/responseFingerprint_test.go b/e2e/singleUse/responseFingerprint_test.go index 61f487c9..75bb22fa 100644 --- a/e2e/singleUse/responseFingerprint_test.go +++ b/e2e/singleUse/responseFingerprint_test.go @@ -9,9 +9,9 @@ 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/cyclic" "math/rand" "testing" ) diff --git a/e2e/singleUse/responseKey.go b/e2e/singleUse/responseKey.go index 14dc3a2d..e89c3f6d 100644 --- a/e2e/singleUse/responseKey.go +++ b/e2e/singleUse/responseKey.go @@ -10,8 +10,8 @@ package singleUse import ( "encoding/binary" jww "github.com/spf13/jwalterweatherman" - "gitlab.com/xx_network/crypto/cyclic" - "gitlab.com/xx_network/crypto/hash" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/crypto/hash" ) const responseKeySalt = "singleUseResponseKeySalt" diff --git a/e2e/singleUse/responseKey_test.go b/e2e/singleUse/responseKey_test.go index 3fb8238d..3dcae7bb 100644 --- a/e2e/singleUse/responseKey_test.go +++ b/e2e/singleUse/responseKey_test.go @@ -9,8 +9,8 @@ package singleUse import ( "encoding/base64" + "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/diffieHellman" - "gitlab.com/xx_network/crypto/cyclic" "math/rand" "testing" ) diff --git a/e2e/singleUse/tagFingerprint.go b/e2e/singleUse/tagFingerprint.go index dc80066f..0bc037f7 100644 --- a/e2e/singleUse/tagFingerprint.go +++ b/e2e/singleUse/tagFingerprint.go @@ -10,7 +10,7 @@ package singleUse import ( "encoding/base64" jww "github.com/spf13/jwalterweatherman" - "gitlab.com/xx_network/crypto/hash" + "gitlab.com/elixxir/crypto/hash" ) const tagFpSalt = "singleUseTagFingerprintSalt" diff --git a/e2e/ttl.go b/e2e/ttl.go index 845cf9bc..d671bbc8 100644 --- a/e2e/ttl.go +++ b/e2e/ttl.go @@ -12,7 +12,7 @@ package e2e import ( "encoding/binary" jww "github.com/spf13/jwalterweatherman" - "gitlab.com/xx_network/crypto/hash" + "gitlab.com/elixxir/crypto/hash" "gitlab.com/xx_network/crypto/large" "math" ) diff --git a/fileTransfer/fingerprint.go b/fileTransfer/fingerprint.go index 764c12cd..c629a237 100644 --- a/fileTransfer/fingerprint.go +++ b/fileTransfer/fingerprint.go @@ -15,8 +15,8 @@ package fileTransfer import ( "encoding/binary" + "gitlab.com/elixxir/crypto/hash" "gitlab.com/elixxir/primitives/format" - "gitlab.com/xx_network/crypto/hash" ) const fingerprintVector = "FileTransferKeyFingerprint" diff --git a/fileTransfer/keyGen.go b/fileTransfer/keyGen.go index 64c9d931..ac5a8633 100644 --- a/fileTransfer/keyGen.go +++ b/fileTransfer/keyGen.go @@ -20,8 +20,8 @@ import ( "encoding/binary" "encoding/json" "github.com/pkg/errors" + "gitlab.com/elixxir/crypto/hash" "gitlab.com/xx_network/crypto/csprng" - "gitlab.com/xx_network/crypto/hash" ) // Key length constants, in bytes. diff --git a/fileTransfer/mac.go b/fileTransfer/mac.go index 2663854b..75688d0a 100644 --- a/fileTransfer/mac.go +++ b/fileTransfer/mac.go @@ -18,7 +18,7 @@ package fileTransfer import ( "crypto/hmac" - "gitlab.com/xx_network/crypto/hash" + "gitlab.com/elixxir/crypto/hash" ) // CreateTransferMAC creates a MAC for the entire file. This is for consistency diff --git a/go.mod b/go.mod index 0d70baa4..7fc72568 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/spf13/jwalterweatherman v1.1.0 github.com/stretchr/testify v1.8.1 gitlab.com/elixxir/primitives v0.0.3-0.20230109222259-f62b2a90b62c - gitlab.com/xx_network/crypto v0.0.5-0.20230120192824-c0516b176d84 + gitlab.com/xx_network/crypto v0.0.5-0.20230120185816-1788861281c9 gitlab.com/xx_network/primitives v0.0.4-0.20221219230308-4b5550a9247d gitlab.com/yawning/nyquist.git v0.0.0-20221003103146-de5645224a22 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa diff --git a/go.sum b/go.sum index 75fbc959..958f24d4 100644 --- a/go.sum +++ b/go.sum @@ -61,8 +61,6 @@ gitlab.com/xx_network/crypto v0.0.5-0.20230113190331-06f2eb12b97f h1:fY97KmNOyH1 gitlab.com/xx_network/crypto v0.0.5-0.20230113190331-06f2eb12b97f/go.mod h1:1zOTNhUZmMrus0eI227vWggdKJLeMvMPXcxm29dgt1Q= gitlab.com/xx_network/crypto v0.0.5-0.20230120185816-1788861281c9 h1:zYAU/+zKmBTpyOJpupKVAzT+qr/DCLeLVjXV9UmEDZs= gitlab.com/xx_network/crypto v0.0.5-0.20230120185816-1788861281c9/go.mod h1:YXQqutM8DxFihrirM5fgippte9dsFq3TZlSlLt0hXy0= -gitlab.com/xx_network/crypto v0.0.5-0.20230120192824-c0516b176d84 h1:oYwG7zCbR6otwgxQg0x+rbQtOfAQ577aobu/OaJzWkY= -gitlab.com/xx_network/crypto v0.0.5-0.20230120192824-c0516b176d84/go.mod h1:YXQqutM8DxFihrirM5fgippte9dsFq3TZlSlLt0hXy0= gitlab.com/xx_network/primitives v0.0.4-0.20221219230308-4b5550a9247d h1:D9hEtiQ7xj0yFBkDkb4X4S95RfNoeXxtB1eE4UuFHtk= gitlab.com/xx_network/primitives v0.0.4-0.20221219230308-4b5550a9247d/go.mod h1:wUxbEBGOBJZ/RkAiVAltlC1uIlIrU0dE113Nq7HiOhw= gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo= diff --git a/group/mac.go b/group/mac.go index 064f8f77..44712bbe 100644 --- a/group/mac.go +++ b/group/mac.go @@ -13,8 +13,8 @@ package group import ( "crypto/hmac" - "gitlab.com/xx_network/crypto/cyclic" - "gitlab.com/xx_network/crypto/hash" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/crypto/hash" ) // NewMAC generates a MAC for the encrypted internal message and the recipient's diff --git a/group/membership.go b/group/membership.go index 3cb183d1..293c376c 100644 --- a/group/membership.go +++ b/group/membership.go @@ -16,7 +16,7 @@ import ( "encoding/binary" "github.com/pkg/errors" "gitlab.com/elixxir/crypto/contact" - "gitlab.com/xx_network/crypto/cyclic" + "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/xx_network/primitives/id" "golang.org/x/crypto/blake2b" "sort" diff --git a/group/membership_test.go b/group/membership_test.go index 58d79510..039aaf25 100644 --- a/group/membership_test.go +++ b/group/membership_test.go @@ -13,8 +13,8 @@ import ( "encoding/binary" "fmt" "gitlab.com/elixxir/crypto/contact" + "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/shuffle" - "gitlab.com/xx_network/crypto/cyclic" "gitlab.com/xx_network/crypto/large" "gitlab.com/xx_network/primitives/id" "math/rand" diff --git a/hash/hash.go b/hash/hash.go new file mode 100644 index 00000000..cdb0fafd --- /dev/null +++ b/hash/hash.go @@ -0,0 +1,65 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2022 xx foundation // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file. // +//////////////////////////////////////////////////////////////////////////////// + +// Package hash includes a general-purpose hashing algorithm, blake2b, +// that should be suitable for most of our needs. +// It also includes functions to calculate an HMAC. +package hash + +import ( + "crypto" + "crypto/hmac" + "crypto/sha256" + jww "github.com/spf13/jwalterweatherman" + "golang.org/x/crypto/blake2b" + "hash" +) + +// NewCMixHash returns the current cMix hash implementation +// which is currently the 256 bit version of blake2b +func NewCMixHash() (hash.Hash, error) { + + return blake2b.New256(nil) +} + +// DefaultHash returns a CMIX hash or panics +func DefaultHash() hash.Hash { + h, err := blake2b.New256(nil) + if err != nil { + jww.FATAL.Panicf("Could not initialize blake2b: %+v", err) + } + return h +} + +// CMixHash type is currently BLAKE2b_256 +var CMixHash = crypto.BLAKE2b_256 + +// NewHMAC creates a new Message Authentication Code from a message payload and a key. +// This function does not accept keys that are less than 256 bits (or 32 bytes) +// *Function was copied from (https://golang.org/pkg/crypto/hmac/), we need to analyze this again in the future * +func CreateHMAC(message, key []byte) []byte { + + h := hmac.New(sha256.New, key) + h.Write(message) + + hMAC := h.Sum(nil) + + // blank out the first first bit in order to ensure the group is satisfied + // in the message payload. See primitives/format/message.go for more details + hMAC[0] &= 0x7F + + return hMAC +} + +// CheckHMAC receives a MAC value along with the respective message and key associated with the Msg Authentication Code +// Returns true if calculated MAC matches the received one. False if not. +// *Function was copied from (https://golang.org/pkg/crypto/hmac/), we need to analyze this again in the future * +func VerifyHMAC(message, MAC, key []byte) bool { + expectedMAC := CreateHMAC(message, key) + + return hmac.Equal(MAC, expectedMAC) +} diff --git a/hash/hash_test.go b/hash/hash_test.go new file mode 100644 index 00000000..b0ed7948 --- /dev/null +++ b/hash/hash_test.go @@ -0,0 +1,64 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2022 xx foundation // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file. // +//////////////////////////////////////////////////////////////////////////////// + +package hash + +import ( + "encoding/hex" + "testing" +) + +// TestNewCMixHash tests that we get the expected value for the cmix hash +func TestNewCMixHash(t *testing.T) { + expected := []byte{ + 72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33, 14, 87, 81, + 192, 38, 229, 67, 178, 232, 171, 46, 176, 96, 153, 218, 161, 209, 229, + 223, 71, 119, 143, 119, 135, 250, 171, 69, 205, 241, 47, 227, 168} + h, err := NewCMixHash() + if err != nil { + t.Errorf("NewCMixHash failed: %v", err) + } + + actual := h.Sum([]byte("Hello, World!")) + + for i, b := range actual { + if b != expected[i] { + t.Errorf("NewCMixHash byte %v failed, expected: '%v', got: '%v'", + i, expected, actual) + } + } +} + +// TestHMAC tests that we get the expected value for the payload "Mario" and a key "key" +func TestHMAC(t *testing.T) { + payload := []byte("Mario") + key := []byte("a906df88f30d6afbfa6165a50cc9e208d16b34e70b367068dc5d6bd6e155b2c3") + + hmac1 := CreateHMAC(payload, key) + expectedHMAC := "0b716229f4920f70265ee25045d3dc01f40ec423c4da97d249ca9c0dd146693e" + + if hex.EncodeToString(hmac1) != expectedHMAC { + t.Errorf("TestHMAC(): Error 1: MACs should have matched!") + } + + if !VerifyHMAC(payload, hmac1, key) { + t.Errorf("TestHMAC(): Error 2: MACs should have matched!") + } +} + +// tests that the first bit is blanked when we have a leading 1 on +// the output +func TestHMAC_LeadingOne(t *testing.T) { + payload := []byte("ARO00OOO") + key := []byte("a906df88f30d6afbfa6165a50cc9e208d16b34e70b367068dc5d6bd6e155b2c3") + + hmac1 := CreateHMAC(payload, key) + + if hmac1[0]>>7 != 0 { + t.Errorf("First bit not blanked!") + } +} diff --git a/hash/keys.go b/hash/keys.go new file mode 100644 index 00000000..5addc49e --- /dev/null +++ b/hash/keys.go @@ -0,0 +1,43 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2022 xx foundation // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file. // +//////////////////////////////////////////////////////////////////////////////// + +// Package hash includes a general-purpose hashing algorithm, blake2b, +// that should be suitable for most of our needs. +// It also includes functions to calculate an HMAC. +package hash + +import ( + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/xx_network/crypto/csprng" + "gitlab.com/xx_network/crypto/large" + "golang.org/x/crypto/hkdf" + "hash" +) + +type NewHash interface { + New() hash.Hash +} + +// ExpandKey is a function that receives a key and expands such key to the size +// of the prime group +func ExpandKey(h func() hash.Hash, g *cyclic.Group, key []byte, + output *cyclic.Int) *cyclic.Int { + keyGen := hkdf.Expand(h, key, nil) + + pBytes := g.GetPBytes() + expandedKey, err := csprng.GenerateInGroup(pBytes, len(pBytes), keyGen) + if err != nil { + jww.FATAL.Panicf("Key expansion failure: %v", err) + } + + keyInt := large.NewInt(0) + keyInt.SetBytes(expandedKey) + g.SetLargeInt(output, keyInt) + + return output +} diff --git a/hash/keys_test.go b/hash/keys_test.go new file mode 100644 index 00000000..85144fea --- /dev/null +++ b/hash/keys_test.go @@ -0,0 +1,81 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2022 xx foundation // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file. // +//////////////////////////////////////////////////////////////////////////////// + +package hash + +import ( + "crypto/sha512" + "encoding/hex" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/xx_network/crypto/large" + "hash" + "testing" +) + +var primeString = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + + "15728E5A8AACAA68FFFFFFFFFFFFFFFF" + +var p = large.NewIntFromString(primeString, 16) +var g = large.NewInt(2) +var grp = cyclic.NewGroup(p, g) + +//TestExpandKey verifies ExpandKey with two different hashes +func TestExpandKey(t *testing.T) { + test := 4 + pass := 0 + + key := []byte("a906df88f30d6afbfa6165a50cc9e208d16b34e70b367068dc5d6bd6e155b2c3") + + hashFunc := func() hash.Hash { + h, _ := NewCMixHash() + return h + } + x1 := ExpandKey(hashFunc, grp, []byte("key"), grp.NewInt(1)) + x2 := ExpandKey(hashFunc, grp, key, grp.NewInt(1)) + + if x1.BitLen()/8 != 256 { + t.Errorf("TestExpandKey(): Error with the resulting key size") + } else { + pass++ + } + + if hex.EncodeToString(x1.Bytes()) != hex.EncodeToString(x2.Bytes()) { + pass++ + } else { + t.Errorf("TestExpandKey():Error in the Key Expansion. Keys should not be the same!") + } + + hashFunc = func() hash.Hash { + return sha512.New() + } + x1 = ExpandKey(hashFunc, grp, []byte("key"), grp.NewInt(1)) + x2 = ExpandKey(hashFunc, grp, key, grp.NewInt(1)) + + if x1.BitLen()/8 != 255 { + t.Errorf("TestExpandKey(): Error with the resulting key size."+ + "\nexpected: %d\nreceived: %d", 256, x1.BitLen()/8) + } else { + pass++ + } + + if hex.EncodeToString(x1.Bytes()) != hex.EncodeToString(x2.Bytes()) { + pass++ + } else { + t.Errorf("TestExpandKey():Error in the Key Expansion. Keys should not be the same!") + } + + println("TestExpandKey():", pass, "out of", test, "tests passed") +} diff --git a/message/id.go b/message/id.go index 9a1c6fb4..34f03123 100644 --- a/message/id.go +++ b/message/id.go @@ -14,7 +14,7 @@ import ( "encoding/json" "github.com/pkg/errors" - "gitlab.com/xx_network/crypto/hash" + "gitlab.com/elixxir/crypto/hash" "gitlab.com/xx_network/primitives/id" ) diff --git a/nike/dh/dh.go b/nike/dh/dh.go index 8f5abdfe..75391177 100644 --- a/nike/dh/dh.go +++ b/nike/dh/dh.go @@ -14,8 +14,8 @@ import ( "gitlab.com/xx_network/crypto/large" + "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/diffieHellman" - "gitlab.com/xx_network/crypto/cyclic" ) const ( diff --git a/registration/hmac_test.go b/registration/hmac_test.go index 35acc223..540aa6c8 100644 --- a/registration/hmac_test.go +++ b/registration/hmac_test.go @@ -9,7 +9,7 @@ package registration import ( "bytes" - "gitlab.com/xx_network/crypto/hash" + "gitlab.com/elixxir/crypto/hash" "testing" ) diff --git a/registration/keygen.go b/registration/keygen.go index d9178cc4..0adc8d37 100644 --- a/registration/keygen.go +++ b/registration/keygen.go @@ -10,8 +10,8 @@ package registration import ( + "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/diffieHellman" - "gitlab.com/xx_network/crypto/cyclic" "hash" ) diff --git a/registration/keygen_test.go b/registration/keygen_test.go index 1a4a323d..4d030a4e 100644 --- a/registration/keygen_test.go +++ b/registration/keygen_test.go @@ -9,9 +9,9 @@ package registration import ( "crypto/sha256" + "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/diffieHellman" - "gitlab.com/xx_network/crypto/cyclic" - "gitlab.com/xx_network/crypto/hash" + "gitlab.com/elixxir/crypto/hash" "gitlab.com/xx_network/crypto/large" "math/rand" "testing" -- GitLab