diff --git a/.gitignore b/.gitignore index daf913b..a264a73 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# Larger testcases can be generated locally +testcases/* + # Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a diff --git a/README.md b/README.md index b9b75bc..27843fa 100644 --- a/README.md +++ b/README.md @@ -2,17 +2,21 @@ Master: [![Build Status](https://travis-ci.org/Project-Arda/bgls.svg?branch=master)](https://travis-ci.org/Project-Arda/bgls) Develop: [![Build Status](https://travis-ci.org/Project-Arda/bgls.svg?branch=develop)](https://travis-ci.org/Project-Arda/bgls) -Aggregate and Multi Signatures based on BGLS over Alt bn128 +Aggregate and Multi Signatures based on BGLS over Alt bn128 and BLS12-381 This library provides no security against side channel attacks. We provide no security guarantees of this implementation. ## Design -The goal of this library is to create an efficient and secure ad hoc aggregate and multi signature scheme. It relies on [alt bn128](https://github.com/ethereum/go-ethereum/tree/master/crypto/bn256) for curve and pairing operations. It implements hashing of arbitrary byte data to curve points, the standard BGLS scheme for aggregate signatures, and a custom multi signature scheme. +The goal of this library is to create an efficient and secure ad hoc aggregate and multi signature scheme. It supports the curves [bls12-381](https://github.com/dis2/bls12) and [alt bn128](https://github.com/ethereum/go-ethereum/tree/master/crypto/bn256). It implements hashing of arbitrary byte data to curve points, the standard BGLS scheme for aggregate signatures, and a custom multi signature scheme. ### Multi Signature -The multi signature scheme is a modification of the BGLS scheme, where all signatures are on the same message. This allows verification with a constant number of pairing operations, at the cost of being insecure to chosen key attacks. To fix the chosen key attack, users are required to prove knowledge of their secret key, through the use of the Schnorr scheme applied to their public key. +The multi signature scheme is a modification of the BGLS scheme, where all signatures are on the same message. This allows verification with a constant number of pairing operations, at the cost of being insecure to rogue public key attacks. We have three separate solutions to the rogue public key attack implemented. (Proving knowlege of the secret key, Enforcing that messages are distinct, and performing aggregation with hashed exponents. These are described in Dan Boneh's [recent paper]((https://crypto.stanford.edu/~dabo/pubs/papers/BLSmultisig.html))) ## Curves +### Bls12-381 + +This is the set of curves which zcash is switching too. Its official documentation is located [here](https://github.com/ebfull/pairing/tree/master/src/bls12_381). + ### Alt bn128 The group `G_1` is a cyclic group of prime order on the curve `Y^2 = X^3 + 3` defined over the field `F_p` with `p = 21888242871839275222246405745257275088696311157297823662689037894645226208583`. @@ -28,15 +32,15 @@ The generator `g_2` is defined as: `(1155973203298638710799100402139228578392581 The identity element for both groups (The point at infinity in affine space) is internally represented as `(0,0)` ## Benchmarks -The following benchmarks are from a 3.80GHz i7-7700HQ CPU with 16GB ram. The aggregate verification is utilizing parallelization for the pairing operations. The multisignature has parellilization for the two involved pairing operations, and parallelization for the pairing checks at the end. +The following benchmarks are from a 3.80GHz i7-7700HQ CPU with 16GB ram. The aggregate verification is utilizing parallelization for the pairing operations. The multisignature has parellilization for the two involved pairing operations, and parallelization for the pairing checks at the end. Note, all of the benchmarks need to be updated. -For reference, the pairing operation (the slowest operation involved) takes ~1.6 milliseconds. +For reference, the pairing operation on Altbn128 (the slowest operation involved) takes ~1.9 milliseconds. ``` -BenchmarkG1-8 10000 141018 ns/op -BenchmarkG2-8 3000 471002 ns/op -BenchmarkPairing-8 1000 1609893 ns/op -PASS -ok github.com/ethereum/go-ethereum/crypto/bn256/cloudflare 4.725s +BenchmarkPairing-8 1000 1958898 ns/op +``` +and for Bls12 its: +``` +BenchmarkPairGT-8 1000 1539918 ns/op ``` - `Signing` ~.22 milliseconds @@ -44,8 +48,10 @@ ok github.com/ethereum/go-ethereum/crypto/bn256/cloudflare 4.725s - `Multi Signature verification` ~2 milliseconds + ~1.1 microseconds per signer, two pairings + n point additions - `Aggregate Signature verification` ~.36 milliseconds per signer/message pair, with n+1 pairings run in parallel. (4.45x speedup with 8 cores) +The following benchmarks are done with altbn128, before the product of pairings +abstraction was included. These need to be updated. ``` -$ go test github.com/Project-Arda/bgls/ -v -bench . +$ go test github.com/Project-Arda/bgls/bgls/ -v -bench . BenchmarkKeygen-8 3000 434484 ns/op BenchmarkAltBnHashToCurve-8 20000 91947 ns/op BenchmarkSigning-8 10000 218670 ns/op @@ -71,21 +77,22 @@ ok golang.org/x/crypto/ed25519 5.750s ``` ### Hashing -The hashing algorithm is currently try-and-increment, and we support SHA3, Kangaroo twelve, Keccak256, and Blake2b. +Currently only hashing to G1 is supported. Hashing to G2 is planned. +For altbn128, the hashing algorithm is currently try-and-increment, and we support SHA3, Kangaroo twelve, Keccak256, and Blake2b. -We previously used a direct implementation of [Indifferentiable Hashing to Barreto–Naehrig Curves](http://www.di.ens.fr/~fouque/pub/latincrypt12.pdf) using blake2b. This was removed because it can't be implemented in the EVM due to gas costs, and because it will not work for BLS12-381. +For bls12-381, we are using [Fouque-Tibouchi hashing](http://www.di.ens.fr/~fouque/pub/latincrypt12.pdf) using blake2b. This is interoperable with ebfull's repository. ## Future work - Optimize bigint allocations. -- Add utility operations for serialization of keys/signatures. -- Implement a better Hashing algorithm, such as Elligator Squared. -- Integrate [BLS12-381](https://github.com/ebfull/pairing/tree/master/src/bls12_381) with go bindings. +- Add hashing to G2 - Integrations with [bgls-on-evm](https://github.com/jlandrews/bgls-on-evm). - Add tests to show that none of the functions mutate data. - More complete usage documentation. - Add buffering for the channels used in parallelization. +- Make upstream libraries implement [product of pairings algorithms](https://eprint.iacr.org/2006/172.pdf) ## References +- Dan Boneh [Methods to prevent the rogue public key attack](https://crypto.stanford.edu/~dabo/pubs/papers/BLSmultisig.html) - Dan Boneh, Craig Gentry, Ben Lynn, and Hovav Shacham. [Aggregate and verifiably encrypted signatures from bilinear maps](https://www.iacr.org/archive/eurocrypt2003/26560416/26560416.pdf) - Pierre-Alain Fouque and Mehdi Tibouchi. [Indifferentiable Hashing to Barreto–Naehrig Curves](http://www.di.ens.fr/~fouque/pub/latincrypt12.pdf) diff --git a/bgls.go b/bgls.go deleted file mode 100644 index bd77413..0000000 --- a/bgls.go +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright (C) 2018 Authors -// distributed under Apache 2.0 license - -package bgls - -import ( - "crypto/rand" - - "math/big" -) - -//MultiSig holds set of keys and one message plus signature -type MultiSig struct { - keys []Point2 - sig Point1 - msg []byte -} - -//AggSig holds paired sequences of keys and messages, and one signature -type AggSig struct { - keys []Point2 - msgs [][]byte - sig Point1 -} - -//KeyGen generates a *big.Int and Point2 -func KeyGen(curve CurveSystem) (*big.Int, Point2, error) { - x, err := rand.Int(rand.Reader, curve.getG1Order()) - if err != nil { - return nil, nil, err - } - pubKey := LoadPublicKey(curve, x) - return x, pubKey, nil -} - -//LoadPublicKey turns secret key into a public key of type Point2 -func LoadPublicKey(curve CurveSystem, sk *big.Int) Point2 { - pubKey := curve.GetG2().Mul(sk) - return pubKey -} - -// Authenticate generates an Authentication for a valid *big.Int/Point2 combo -// It signs a verification key with x. -func Authenticate(curve CurveSystem, sk *big.Int) Point1 { - return AuthenticateCustHash(curve, sk, curve.HashToG1) -} - -// AuthenticateCustHash generates an Authentication for a valid *big.Int/Point2 combo -// It signs a verification key with x. This runs with the specified hash function. -func AuthenticateCustHash(curve CurveSystem, sk *big.Int, hash func([]byte) Point1) Point1 { - m := LoadPublicKey(curve, sk).Marshal() - return SignCustHash(sk, m, hash) -} - -//CheckAuthentication verifies that this Point2 is valid -func CheckAuthentication(curve CurveSystem, v Point2, authentication Point1) bool { - return CheckAuthenticationCustHash(curve, v, authentication, curve.HashToG1) -} - -//CheckAuthenticationCustHash verifies that this Point2 is valid, with the specified hash function -func CheckAuthenticationCustHash(curve CurveSystem, v Point2, authentication Point1, hash func([]byte) Point1) bool { - m := v.Marshal() - return VerifyCustHash(curve, v, m, authentication, hash) -} - -//Sign creates a signature on a message with a private key -func Sign(curve CurveSystem, sk *big.Int, m []byte) Point1 { - return SignCustHash(sk, m, curve.HashToG1) -} - -// SignCustHash creates a signature on a message with a private key, using -// a supplied function to hash to g1. -func SignCustHash(sk *big.Int, m []byte, hash func([]byte) Point1) Point1 { - h := hash(m) - i := h.Mul(sk) - return i -} - -// Verify checks that a signature is valid -func Verify(curve CurveSystem, pubKey Point2, m []byte, sig Point1) bool { - return VerifyCustHash(curve, pubKey, m, sig, curve.HashToG1) -} - -// VerifyCustHash checks that a signature is valid with the supplied hash function -func VerifyCustHash(curve CurveSystem, pubKey Point2, m []byte, sig Point1, hash func([]byte) Point1) bool { - h := hash(m) - p1, ok1 := h.Pair(pubKey) - p2, ok2 := sig.Pair(curve.GetG2()) - if !ok1 || !ok2 { - return false - } - return p1.Equals(p2) -} - -// AggregateG1 takes the sum of points on G1. This is used to convert a set of signatures into a single signature -func AggregateG1(sigs []Point1) Point1 { - c := make(chan Point1) - if len(sigs) == 2 { - aggG1, _ := sigs[0].Add(sigs[1]) - return aggG1 - } - aggSigs := make([]Point1, (len(sigs)/2)+(len(sigs)%2)) - counter := 0 - for i := 0; i < len(sigs); i += 2 { // No parallelization needed - go concurrentAggregateG1(i, i+2, sigs, c) - counter++ - } - for i := 0; i < counter; i++ { - aggSigs[i] = <-c - } - - for { - nxtAggSigs := make([]Point1, (len(aggSigs)/2)+(len(aggSigs)%2)) - counter = 0 - if len(aggSigs) == 1 { - break - } - for i := 0; i < len(aggSigs); i += 2 { - go concurrentAggregateG1(i, i+2, aggSigs, c) - counter++ - } - for i := 0; i < counter; i++ { - nxtAggSigs[i] = <-c - } - aggSigs = nxtAggSigs - } - return aggSigs[0] -} - -func concurrentAggregateG1(start int, end int, sigs []Point1, c chan Point1) { - if end > len(sigs) { - c <- sigs[start] - return - } - summed, _ := sigs[start].Add(sigs[end-1]) - c <- summed -} - -// AggregateG2 takes the sum of points on G2. This is used to sum a set of public keys for the multisignature -func AggregateG2(keys []Point2) Point2 { - c := make(chan Point2) - if len(keys) == 2 { // No parallelization needed - aggG2, _ := keys[0].Add(keys[1]) - return aggG2 - } - aggKeys := make([]Point2, (len(keys)/2)+(len(keys)%2)) - counter := 0 - for i := 0; i < len(keys); i += 2 { - go concurrentAggregateG2(i, i+2, keys, c) - counter++ - } - for i := 0; i < counter; i++ { - aggKeys[i] = <-c - } - - for { - nxtAggKeys := make([]Point2, (len(aggKeys)/2)+(len(aggKeys)%2)) - counter = 0 - if len(aggKeys) == 1 { - break - } - for i := 0; i < len(aggKeys); i += 2 { - go concurrentAggregateG2(i, i+2, aggKeys, c) - counter++ - } - for i := 0; i < counter; i++ { - nxtAggKeys[i] = <-c - } - aggKeys = nxtAggKeys - } - return aggKeys[0] -} - -func concurrentAggregateG2(start int, end int, keys []Point2, c chan Point2) { - if end > len(keys) { - c <- keys[start] - return - } - summed, _ := keys[start].Add(keys[end-1]) - c <- summed -} - -// Verify checks that all messages were signed by associated keys -// Will fail under duplicate messages -func (a AggSig) Verify(curve CurveSystem) bool { - return VerifyAggregateSignature(curve, a.sig, a.keys, a.msgs, false) -} - -// VerifyAggregateSignature verifies that the aggregated signature proves that all messages were signed by associated keys -// Will fail under duplicate messages, unless allow duplicates is True. -func VerifyAggregateSignature(curve CurveSystem, aggsig Point1, keys []Point2, msgs [][]byte, allowDuplicates bool) bool { - if len(keys) != len(msgs) { - return false - } - if !allowDuplicates { - if containsDuplicateMessage(msgs) { - return false - } - } - c := make(chan PointT) - c2 := make(chan PointT) - go concurrentPair(curve, aggsig, curve.GetG2(), c2) - for i := 0; i < len(msgs); i++ { - go concurrentMsgPair(curve, msgs[i], keys[i], c) - } - e1 := <-c2 - e2 := <-c - for i := 1; i < len(msgs); i++ { - e3 := <-c - e2, _ = e2.Add(e3) - } - return e1.Equals(e2) -} - -func concurrentPair(curve CurveSystem, pt Point1, key Point2, c chan PointT) { - targetPoint, _ := pt.Pair(key) - c <- targetPoint -} - -func concurrentMsgPair(curve CurveSystem, msg []byte, key Point2, c chan PointT) { - h := curve.HashToG1(msg) - targetPoint, _ := h.Pair(key) - c <- targetPoint -} - -//Verify checks that a single message has been signed by a set of keys -//vulnerable against chosen key attack, if keys have not been authenticated -func (m MultiSig) Verify(curve CurveSystem) bool { - return VerifyMultiSignature(curve, m.sig, m.keys, m.msg) -} - -// VerifyMultiSignature checks that the aggregate signature correctly proves that a single message has been signed by a set of keys, -// vulnerable against chosen key attack, if keys have not been authenticated -func VerifyMultiSignature(curve CurveSystem, aggsig Point1, keys []Point2, msg []byte) bool { - vs := AggregateG2(keys) - c := make(chan PointT) - go concurrentPair(curve, aggsig, curve.GetG2(), c) - go concurrentMsgPair(curve, msg, vs, c) - e1 := <-c - e2 := <-c - return e1.Equals(e2) -} - -func containsDuplicateMessage(msgs [][]byte) bool { - hashmap := make(map[string]bool) - for i := 0; i < len(msgs); i++ { - msg := string(msgs[i]) - if _, ok := hashmap[msg]; !ok { - hashmap[msg] = true - } else { - return true - } - } - return false -} diff --git a/bgls/bgls.go b/bgls/bgls.go new file mode 100644 index 0000000..f2712c5 --- /dev/null +++ b/bgls/bgls.go @@ -0,0 +1,149 @@ +// Copyright (C) 2018 Authors +// distributed under Apache 2.0 license + +package bgls + +import ( + "crypto/rand" + "math/big" + "sync" + + . "github.com/Project-Arda/bgls/curves" // nolint: golint +) + +//MultiSig holds set of keys and one message plus signature +type MultiSig struct { + keys []Point + sig Point + msg []byte +} + +//AggSig holds paired sequences of keys and messages, and one signature +type AggSig struct { + keys []Point + msgs [][]byte + sig Point +} + +//KeyGen generates a *big.Int and Point2 +func KeyGen(curve CurveSystem) (*big.Int, Point, error) { + x, err := rand.Int(rand.Reader, curve.GetG1Order()) + if err != nil { + return nil, nil, err + } + pubKey := LoadPublicKey(curve, x) + return x, pubKey, nil +} + +//LoadPublicKey turns secret key into a public key of type Point2 +func LoadPublicKey(curve CurveSystem, sk *big.Int) Point { + pubKey := curve.GetG2().Mul(sk) + return pubKey +} + +// Sign creates a standard BLS signature on a message with a private key +func Sign(curve CurveSystem, sk *big.Int, msg []byte) Point { + return SignCustHash(sk, msg, curve.HashToG1) +} + +// SignCustHash creates a standard BLS signature on a message with a private key, +// using a supplied function to hash onto the curve where signatures lie. +func SignCustHash(sk *big.Int, msg []byte, hash func([]byte) Point) Point { + h := hash(msg) + i := h.Mul(sk) + return i +} + +// VerifySingleSignature checks that a single standard BLS signature is valid +func VerifySingleSignature(curve CurveSystem, sig Point, pubKey Point, msg []byte) bool { + return VerifySingleSignatureCustHash(curve, sig, pubKey, msg, curve.HashToG1) +} + +// VerifySingleSignatureCustHash checks that a single standard BLS signature is +// valid, using the supplied hash function to hash onto the curve where signatures lie. +func VerifySingleSignatureCustHash(curve CurveSystem, sig Point, pubkey Point, + msg []byte, hash func([]byte) Point) bool { + h := hash(msg).Mul(new(big.Int).SetInt64(-1)) + paired, _ := curve.PairingProduct([]Point{h, sig}, []Point{pubkey, curve.GetG2()}) + return curve.GetGTIdentity().Equals(paired) +} + +// Verify verifies an aggregate signature type. +func (a *AggSig) Verify(curve CurveSystem) bool { + return VerifyAggregateSignature(curve, a.sig, a.keys, a.msgs) +} + +// VerifyAggregateSignature verifies that the aggregated signature proves that +// all messages were signed by the associated keys. This will fail if there are +// duplicate messages, due to the possibility of the rogue public-key attack. +// If duplicate messages should be allowed, one of the protections against the +// rogue public-key attack should be used. See doc.go for more details. +func VerifyAggregateSignature(curve CurveSystem, aggsig Point, keys []Point, msgs [][]byte) bool { + return verifyAggSig(curve, aggsig, keys, msgs, false) +} + +// verifyMultiSignature checks that the aggregate signature correctly proves +// that a single message has been signed by a set of keys. This is +// vulnerable to the rogue public attack, so one of the defense mechanisms should be used. +func verifyMultiSignature(curve CurveSystem, aggsig Point, keys []Point, msg []byte) bool { + vs := AggregatePoints(keys) + return VerifySingleSignature(curve, aggsig, vs, msg) +} + +func verifyAggSig(curve CurveSystem, aggsig Point, keys []Point, msgs [][]byte, allowDuplicates bool) bool { + if len(keys) != len(msgs) { + return false + } + if !allowDuplicates { + if containsDuplicateMessage(msgs) { + return false + } + } + pts1 := make([]Point, len(keys)+1) + pts2 := make([]Point, len(keys)+1) + var wg sync.WaitGroup + wg.Add(len(msgs)) + for i := 0; i < len(msgs); i++ { + go concurrentHash(curve, i, pts1, msgs[i], &wg) + pts2[i] = keys[i] + } + wg.Wait() + pts1[len(keys)] = aggsig.Mul(new(big.Int).SetInt64(-1)) + pts2[len(keys)] = curve.GetG2() + aggPt, ok := curve.PairingProduct(pts1, pts2) + if ok { + return aggPt.Equals(curve.GetGTIdentity()) + } + return ok +} + +// AggregateSignatures aggregates an array of signatures into one aggsig. +// This wrapper only exists so end-users don't have to use the method from curves +func AggregateSignatures(sigs []Point) Point { + return AggregatePoints(sigs) +} + +// AggregateKeys sums an array of public keys into one key. +// This wrapper only exists so end-users don't have to use the method from curve +func AggregateKeys(keys []Point) Point { + return AggregatePoints(keys) +} + +// concurrentHash hashes the message and sends the result down the channel. +func concurrentHash(curve CurveSystem, i int, pts []Point, msg []byte, wg *sync.WaitGroup) { + pts[i] = curve.HashToG1(msg) + wg.Done() +} + +func containsDuplicateMessage(msgs [][]byte) bool { + hashmap := make(map[string]bool) + for i := 0; i < len(msgs); i++ { + msg := string(msgs[i]) + if _, ok := hashmap[msg]; !ok { + hashmap[msg] = true + } else { + return true + } + } + return false +} diff --git a/bgls/bgls_test.go b/bgls/bgls_test.go new file mode 100644 index 0000000..c9e9923 --- /dev/null +++ b/bgls/bgls_test.go @@ -0,0 +1,204 @@ +// Copyright (C) 2018 Authors +// distributed under Apache 2.0 license + +package bgls + +import ( + "crypto/rand" + "math/big" + "os" + "testing" + + . "github.com/Project-Arda/bgls/curves" + "github.com/stretchr/testify/assert" +) + +var curves = []CurveSystem{Altbn128, Bls12} +var benchmarkCurve = Bls12 + +func TestSingleSigner(t *testing.T) { + for _, curve := range curves { + sk, vk, err := KeyGen(curve) + assert.Nil(t, err, "Key generation failed") + d := make([]byte, 64) + _, err = rand.Read(d) + assert.Nil(t, err, "test data generation failed") + sig := Sign(curve, sk, d) + assert.True(t, VerifySingleSignature(curve, sig, vk, d), "Standard BLS "+ + "signature verification failed") + + sigTmp := sig.Copy() + sigTmp, _ = sigTmp.Add(curve.GetG1()) + sig2 := sigTmp + assert.False(t, VerifySingleSignature(curve, sig2, vk, d), "Standard BLS "+ + "signature verification succeeding when it shouldn't") + + // TODO Add tests to show that this doesn't succeed if d or vk is altered + } +} + +func TestAggregation(t *testing.T) { + for _, curve := range curves { + N, Size := 6, 32 + msgs := make([][]byte, N+1) + sigs := make([]Point, N+1) + pubkeys := make([]Point, N+1) + for i := 0; i < N; i++ { + msgs[i] = make([]byte, Size) + rand.Read(msgs[i]) + + sk, vk, _ := KeyGen(curve) + sig := Sign(curve, sk, msgs[i]) + pubkeys[i] = vk + sigs[i] = sig + } + aggSig := AggregateSignatures(sigs[:N]) + assert.True(t, VerifyAggregateSignature(curve, aggSig, pubkeys[:N], msgs[:N]), + "Aggregate Point1 verification failed") + assert.False(t, VerifyAggregateSignature(curve, aggSig, pubkeys[:N-1], msgs[:N]), + "Aggregate Point1 verification succeeding without enough pubkeys") + skf, vkf, _ := KeyGen(curve) + pubkeys[N] = vkf + sigs[N] = Sign(curve, skf, msgs[0]) + msgs[N] = msgs[0] + aggSig = AggregateSignatures(sigs) + assert.False(t, VerifyAggregateSignature(curve, aggSig, pubkeys, msgs), + "Aggregate Signature succeeding with duplicate messages") + assert.True(t, KoskVerifyAggregateSignature(curve, aggSig, pubkeys, msgs), + "Aggregate Kosk signature failing with duplicate messages") + assert.False(t, VerifyAggregateSignature(curve, aggSig, pubkeys[:N], msgs[:N]), + "Aggregate Point1 succeeding with invalid signature") + msgs[0] = msgs[1] + msgs[1] = msgs[N] + aggSig = AggregateSignatures(sigs[:N]) + assert.False(t, VerifyAggregateSignature(curve, aggSig, pubkeys[:N], msgs[:N]), + "Aggregate Point1 succeeded with messages 0 and 1 switched") + + // TODO Add tests to make sure there is no mutation + } +} + +func BenchmarkKeygen(b *testing.B) { + b.ResetTimer() + curve := Altbn128 + for i := 0; i < b.N; i++ { + _, _, res := KeyGen(curve) + if res != nil { + b.Error("key gen failure") + } + } +} + +func BenchmarkAltBnHashToCurve(b *testing.B) { + curve := Altbn128 + ms := make([][]byte, b.N) + for i := 0; i < b.N; i++ { + ms[i] = make([]byte, 64) + rand.Read(ms[i]) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + curve.HashToG1(ms[i]) + } +} + +func BenchmarkSigning(b *testing.B) { + curve := Altbn128 + sks := make([]*big.Int, b.N) + ms := make([][]byte, b.N) + for i := 0; i < b.N; i++ { + ms[i] = make([]byte, 64) + rand.Read(ms[i]) + sk, _, _ := KeyGen(curve) + sks[i] = sk + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = Sign(curve, sks[i], ms[i]) + } +} + +func BenchmarkVerification(b *testing.B) { + curve := Altbn128 + message := make([]byte, 64) + rand.Read(message) + sk, vk, _ := KeyGen(curve) + sig := Sign(curve, sk, message) + b.ResetTimer() + for i := 0; i < b.N; i++ { + if !VerifySingleSignature(curve, sig, vk, message) { + b.Error("verification failed") + } + } +} + +var vks []Point +var sgs []Point +var msg []byte + +func TestMain(m *testing.M) { + vks = make([]Point, 2048) + sgs = make([]Point, 2048) + msg = make([]byte, 64) + rand.Read(msg) + for i := 0; i < 2048; i++ { + sk, vk, _ := KeyGen(benchmarkCurve) + vks[i] = vk + sgs[i] = KoskSign(benchmarkCurve, sk, msg) + } + os.Exit(m.Run()) +} + +func benchmulti(b *testing.B, k int) { + //multisig := MultiSig{vks[:k], AggregateSignatures(sgs[:k]), msg} + aggsig := AggregateSignatures(sgs[:k]) + keys := vks[:k] + b.ResetTimer() + for i := 0; i < b.N; i++ { + if !KoskVerifyMultiSignature(benchmarkCurve, aggsig, keys, msg) { + b.Error("MultiSig verification failed") + } + } +} + +func BenchmarkMultiVerification64(b *testing.B) { + benchmulti(b, 64) +} + +func BenchmarkMultiVerification128(b *testing.B) { + benchmulti(b, 128) +} + +func BenchmarkMultiVerification256(b *testing.B) { + benchmulti(b, 256) +} + +func BenchmarkMultiVerification512(b *testing.B) { + benchmulti(b, 512) +} + +func BenchmarkMultiVerification1024(b *testing.B) { + benchmulti(b, 1024) +} + +func BenchmarkMultiVerification2048(b *testing.B) { + benchmulti(b, 2048) +} + +func BenchmarkAggregateVerification(b *testing.B) { + verifkeys := make([]Point, b.N) + sigs := make([]Point, b.N) + messages := make([][]byte, b.N) + for i := 0; i < b.N; i++ { + messages[i] = make([]byte, 64) + rand.Read(messages[i]) + sk, vk, _ := KeyGen(benchmarkCurve) + verifkeys[i] = vk + sigs[i] = Sign(benchmarkCurve, sk, messages[i]) + } + aggsig := AggregateSignatures(sigs) + b.ResetTimer() + if !VerifyAggregateSignature(benchmarkCurve, aggsig, verifkeys, messages) { + b.Error("Aggregate verificaton failed") + } +} diff --git a/bgls/blsDistinctMessage.go b/bgls/blsDistinctMessage.go new file mode 100644 index 0000000..4b54756 --- /dev/null +++ b/bgls/blsDistinctMessage.go @@ -0,0 +1,57 @@ +// Copyright (C) 2018 Authors +// distributed under Apache 2.0 license + +package bgls + +// This file implements the method of using Distinct Messages for aggregate signatures. +// This ensures that no two messages are used from separate pubkeys by prepending +// the public key before the message, thereby preventing the rogue public key +// attack. +// +// If you are using DistinctMsg to secure against the rogue public key attack, you are +// intended to use: AggregateSignatures, KeyGen, DistinctMsgSign, +// DistinctMsgVerifySingleSignature, DistinctMsgVerifyAggregateSignature + +import ( + "math/big" + + . "github.com/Project-Arda/bgls/curves" // nolint: golint +) + +// DistinctMsgSign creates a signature on a message with a private key, with +// prepending the public key to the message. +func DistinctMsgSign(curve CurveSystem, sk *big.Int, m []byte) Point { + return DistinctMsgSignCustHash(curve, sk, m, curve.HashToG1) +} + +// DistinctMsgSignCustHash creates a signature on a message with a private key, using +// a supplied function to hash to g1. +func DistinctMsgSignCustHash(curve CurveSystem, sk *big.Int, msg []byte, hash func([]byte) Point) Point { + m := append(LoadPublicKey(curve, sk).MarshalUncompressed(), msg...) + h := hash(m) + i := h.Mul(sk) + return i +} + +// DistinctMsgVerifySingleSignature checks that a single 'Distinct Message' signature is valid +func DistinctMsgVerifySingleSignature(curve CurveSystem, sig Point, pubkey Point, m []byte) bool { + msg := append(pubkey.MarshalUncompressed(), m...) + return VerifySingleSignature(curve, sig, pubkey, msg) +} + +// DistinctMsgVerifyAggregateSignature checks that an aggsig was generated from the +// the provided set of public key / msg pairs, when the messages are signed using +// the 'Distinct Message' method. +func DistinctMsgVerifyAggregateSignature(curve CurveSystem, aggsig Point, keys []Point, msgs [][]byte) bool { + if len(keys) != len(msgs) { + return false + } + prependedMsgs := make([][]byte, len(msgs)) + for i := 0; i < len(msgs); i++ { + prependedMsgs[i] = append(keys[i].MarshalUncompressed(), msgs[i]...) + } + // Use true for allow duplicates even though duplicates aren't allowed + // This is because the prepending ensures that there are no duplicates, + // So setting this to true skips that check. + return verifyAggSig(curve, aggsig, keys, prependedMsgs, true) +} diff --git a/bgls/blsDistinctMessage_test.go b/bgls/blsDistinctMessage_test.go new file mode 100644 index 0000000..b5f5da6 --- /dev/null +++ b/bgls/blsDistinctMessage_test.go @@ -0,0 +1,57 @@ +// Copyright (C) 2018 Authors +// distributed under Apache 2.0 license + +package bgls + +import ( + "crypto/rand" + "testing" + + . "github.com/Project-Arda/bgls/curves" // nolint: golint + "github.com/stretchr/testify/assert" +) + +func TestDistinctMsgSingleSigner(t *testing.T) { + for _, curve := range curves { + sk, vk, err := KeyGen(curve) + assert.Nil(t, err, "Key generation failed") + msg := make([]byte, 64) + _, err = rand.Read(msg) + assert.Nil(t, err, "test data generation failed") + sig := DistinctMsgSign(curve, sk, msg) + assert.True(t, DistinctMsgVerifySingleSignature(curve, sig, vk, msg), "Point1 verification failed") + + sig2 := sig.Copy() + sig2, _ = sig2.Add(curve.GetG1()) + assert.False(t, DistinctMsgVerifySingleSignature(curve, sig2, vk, msg), "Point1 verification succeeding when it shouldn't") + } +} + +func TestDistinctMsgAggregation(t *testing.T) { + for _, curve := range curves { + N, Size := 6, 32 + msgs := make([][]byte, N) + sigs := make([]Point, N) + pubkeys := make([]Point, N) + for i := 0; i < N; i++ { + msgs[i] = make([]byte, Size) + rand.Read(msgs[i]) + + sk, vk, _ := KeyGen(curve) + sig := DistinctMsgSign(curve, sk, msgs[i]) + pubkeys[i] = vk + sigs[i] = sig + } + aggSig := AggregatePoints(sigs) + assert.True(t, DistinctMsgVerifyAggregateSignature(curve, aggSig, pubkeys, msgs), + "Aggregate Point1 verification failed") + assert.False(t, DistinctMsgVerifyAggregateSignature(curve, aggSig, pubkeys[:N-1], msgs), + "Aggregate Point1 verification succeeding without enough pubkeys") + msgs[0] = msgs[1] + aggSig = AggregatePoints(sigs) + assert.False(t, VerifyAggregateSignature(curve, aggSig, pubkeys, msgs), + "Aggregate Point1 succeeded with messages 0 and 1 switched") + + // TODO Add tests to make sure there is no mutation + } +} diff --git a/bgls/blsHAE.go b/bgls/blsHAE.go new file mode 100644 index 0000000..3d9dae9 --- /dev/null +++ b/bgls/blsHAE.go @@ -0,0 +1,75 @@ +// Copyright (C) 2018 Authors +// distributed under Apache 2.0 license + +package bgls + +// BLS with hashed aggregation exponents(HAE). This is normal bls, but when aggregating +// you hash the `n` public keys to get `n` numbers in the range [0,2^(128)). +// Call these numbers t_0, t_1, ... t_{n-1}. Then you scale the ith signature to the +// by t_i, before multiplying them together. +// +// For Verification, you hash to obtain the same t_0, t_1, ... t_{n-1}, and scale +// the public keys accordingly. Then BLS proceeds as normal with these scaled public keys. +// +// The hash function from G^n \to \R^n is blake2x. The uncompressed marshal of every +// key is written to then blake2x instance. Then n 16 byte numbers are read from the XOF, +// each corresponding to a value of t. +// +// Note. I am calling this Hashed Aggregation Exponents in lieu of a better name +// for this defense against the rogue public key attack. This method is discussed +// with a corresponding security proof here: +// https://crypto.stanford.edu/~dabo/pubs/papers/BLSmultisig.html#mjx-eqn-eqforge1 +// +// If you are using HAE to secure against the rogue public key attack, you are +// intended to use: KeyGen, Sign, VerifySingleSignature, AggregateSignaturesWithHAE, +// VerifyMultiSignatureWithHAE, VerifyAggregateSignatureWithHAE + +import ( + "math/big" + + "golang.org/x/crypto/blake2b" + + . "github.com/Project-Arda/bgls/curves" // nolint: golint +) + +// AggregateSignaturesWithHAE aggregates the signatures, using the +// hashed exponents derived from the pubkeys to protect against the rogue +// public key attack. +func AggregateSignaturesWithHAE(sigs []Point, pubkeys []Point) Point { + if len(pubkeys) != len(sigs) { + return nil + } + t := hashPubKeysToExponents(pubkeys) + newsigs := ScalePoints(sigs, t) + return AggregatePoints(newsigs) +} + +// VerifyAggregateSignatureWithHAE verifies signatures of different messages aggregated with HAE. +func VerifyAggregateSignatureWithHAE(curve CurveSystem, aggsig Point, pubkeys []Point, msgs [][]byte) bool { + t := hashPubKeysToExponents(pubkeys) + newkeys := ScalePoints(pubkeys, t) + return verifyAggSig(curve, aggsig, newkeys, msgs, true) +} + +// VerifyMultiSignatureWithHAE verifies signatures of the same message aggregated with HAE. +func VerifyMultiSignatureWithHAE(curve CurveSystem, aggsig Point, pubkeys []Point, msg []byte) bool { + t := hashPubKeysToExponents(pubkeys) + newkeys := ScalePoints(pubkeys, t) + return verifyMultiSignature(curve, aggsig, newkeys, msg) +} + +// My hash from G^n \to \R^n is using blake2x. The inputs to the hash are the +// uncompressed marshal's of each of the pubkeys. +func hashPubKeysToExponents(pubkeys []Point) []*big.Int { + hashFunc, _ := blake2b.NewXOF(uint32(16*len(pubkeys)), []byte{}) + for i := 0; i < len(pubkeys); i++ { + hashFunc.Write(pubkeys[i].MarshalUncompressed()) + } + t := make([]*big.Int, len(pubkeys)) + for i := 0; i < len(pubkeys); i++ { + sum := make([]byte, 16) + hashFunc.Read(sum) + t[i] = new(big.Int).SetBytes(sum) + } + return t +} diff --git a/bgls/blsHAE_test.go b/bgls/blsHAE_test.go new file mode 100644 index 0000000..597ed1c --- /dev/null +++ b/bgls/blsHAE_test.go @@ -0,0 +1,82 @@ +// Copyright (C) 2018 Authors +// distributed under Apache 2.0 license + +package bgls + +import ( + "crypto/rand" + "testing" + + . "github.com/Project-Arda/bgls/curves" + "github.com/stretchr/testify/assert" +) + +func TestAggregationWithHAE(t *testing.T) { + for _, curve := range curves { + N, Size := 6, 32 + msgs := make([][]byte, N+1) + sigs := make([]Point, N+1) + pubkeys := make([]Point, N+1) + for i := 0; i < N; i++ { + msgs[i] = make([]byte, Size) + rand.Read(msgs[i]) + + sk, vk, _ := KeyGen(curve) + sig := Sign(curve, sk, msgs[i]) + pubkeys[i] = vk + sigs[i] = sig + } + aggSig := AggregateSignaturesWithHAE(sigs[:N], pubkeys[:N]) + assert.True(t, VerifyAggregateSignatureWithHAE(curve, aggSig, pubkeys[:N], msgs[:N]), + "Aggregate Point1 verification failed") + assert.False(t, VerifyAggregateSignatureWithHAE(curve, aggSig, pubkeys[:N-1], msgs[:N]), + "Aggregate Point1 verification succeeding without enough pubkeys") + assert.Nil(t, AggregateSignaturesWithHAE(sigs[:N], pubkeys[:N-1]), + "Aggregation of signatures succeeding with differing numbers of signatures"+ + " and pubkeys") + skf, vkf, _ := KeyGen(curve) + pubkeys[N] = vkf + msgs[N] = msgs[0] + sigs[N] = Sign(curve, skf, msgs[N]) + aggSig = AggregateSignaturesWithHAE(sigs, pubkeys) + assert.True(t, VerifyAggregateSignatureWithHAE(curve, aggSig, pubkeys, msgs), + "Aggregate HAE signature failing with duplicate messages") + assert.False(t, VerifyAggregateSignatureWithHAE(curve, aggSig, pubkeys[:N], msgs[:N]), + "Aggregate Point1 succeeding with invalid signature") + msgs[0] = msgs[1] + msgs[1] = msgs[N] + aggSig = AggregatePoints(sigs[:N]) + assert.False(t, VerifyAggregateSignatureWithHAE(curve, aggSig, pubkeys[:N], msgs[:N]), + "Aggregate Point1 succeeded with messages 0 and 1 switched") + + // TODO Add tests to make sure there is no mutation + } +} + +func TestMultiSigWithHAE(t *testing.T) { + for _, curve := range curves { + Tests, Size, Signers := 5, 32, 10 + for i := 0; i < Tests; i++ { + msg := make([]byte, Size) + rand.Read(msg) + signers := make([]Point, Signers) + sigs := make([]Point, Signers) + for j := 0; j < Signers; j++ { + sk, vk, _ := KeyGen(curve) + sigs[j] = Sign(curve, sk, msg) + signers[j] = vk + } + aggSig := AggregateSignaturesWithHAE(sigs, signers) + assert.True(t, VerifyMultiSignatureWithHAE(curve, aggSig, signers, msg), + "Aggregate MultiSig verification failed") + msg2 := make([]byte, Size) + rand.Read(msg2) + assert.False(t, VerifyMultiSignatureWithHAE(curve, aggSig, signers, msg2), + "Aggregate MultiSig verification succeeded on incorrect msg") + _, vkf, _ := KeyGen(curve) + signers[0] = vkf + assert.False(t, VerifyMultiSignatureWithHAE(curve, aggSig, signers, msg), + "Aggregate MultiSig verification succeeded on incorrect signers") + } + } +} diff --git a/bgls/blsKosk.go b/bgls/blsKosk.go new file mode 100644 index 0000000..66ab9f0 --- /dev/null +++ b/bgls/blsKosk.go @@ -0,0 +1,133 @@ +// Copyright (C) 2018 Authors +// distributed under Apache 2.0 license + +package bgls + +// This file is for Knowledge of secret key (Kosk) BLS. You do a proof to +// show that you know the secret key, to avoid the rogue public key attack, +// which is done in the authentication methods here. +// +// Proof of knowledge of the secret key is done in this library through +// doing a BLS signature on the public key itself as a message. There is a +// situation where this doesn't prove knowledge of the secret key. Suppose there +// is a BLS signing oracle for (pk1, sk1). Let pkA = -pk1 + x*g_2. +// Note that sig_pkA(pkA) = -sig_pk1(pkA) + xH(-pk1 + x*g_2) +// Consequently, if pk1 signs on pkA, this doesn't prove that person A knows skA +// and the rogue public key attack is possible as pkA is an authenticated key. +// One solution to fix this is to ensure that it +// is impossible for pk1 to sign the same pkA that is used in authentication. +// The way this is implemented here is to make the sign/verify methods prepend a +// null byte to any message that is being signed, and to make authentication +// prepend 0x01 to public key before its signed. Since one would only ever +// authenticate their own public key, noone could get a signature from you that +// would work for their own authentication. (As all signatures you give out, other +// than your own authentication, have a null byte prepended instead of a 0x01 byte) +// +// This method sacrifices interoperability between KoskBls and normal BLS, +// however the advantage is that the authentications are aggregatable. They're +// aggregatable, since they are in effect, BLS signatures but all on distinct +// messages since they are distinct public keys. +// +// If you are using Kosk to secure against the rogue public key attack, you are +// intended to use: AggregateSignatures, KeyGen, KoskSign, +// KoskVerifySingleSignature, KoskVerifyMultiSignature +// KoskVerifyMultiSignatureWithMultiplicity, KoskVerifyAggregateSignature + +import ( + "math/big" + + . "github.com/Project-Arda/bgls/curves" // nolint: golint +) + +// Authenticate generates an Aggregatable Authentication for a given secret key. +// It signs the public key generated from sk, with a 0x01 byte prepended to it. +func Authenticate(curve CurveSystem, sk *big.Int) Point { + return AuthenticateCustHash(curve, sk, curve.HashToG1) +} + +// AuthenticateCustHash generates an Aggregatable Authentication for a given secret key. +// It signs the public key generated from sk, with a 0x01 byte prepended to it. +// This runs with the specified hash function. +func AuthenticateCustHash(curve CurveSystem, sk *big.Int, hash func([]byte) Point) Point { + msg := LoadPublicKey(curve, sk).Marshal() + msg = append(make([]byte, 1), msg...) + return SignCustHash(sk, msg, hash) +} + +// CheckAuthentication verifies that the provided signature is in fact authentication +// for this public key. +func CheckAuthentication(curve CurveSystem, pubkey Point, authentication Point) bool { + return CheckAuthenticationCustHash(curve, pubkey, authentication, curve.HashToG1) +} + +// CheckAuthenticationCustHash verifies that the provided signature is in fact authentication +// for this public key. +func CheckAuthenticationCustHash(curve CurveSystem, pubkey Point, authentication Point, hash func([]byte) Point) bool { + msg := pubkey.Marshal() + msg = append(make([]byte, 1), msg...) + return VerifySingleSignatureCustHash(curve, authentication, pubkey, msg, hash) +} + +// KoskSign creates a kosk signature on a message with a private key. +// A kosk signature prepends a 0x01 byte to the message before signing. +func KoskSign(curve CurveSystem, sk *big.Int, msg []byte) Point { + return KoskSignCustHash(curve, sk, msg, curve.HashToG1) +} + +// KoskSignCustHash creates a kosk signature on a message with a private key, using +// a supplied function to hash to point. A kosk signature prepends a 0x01 byte +// to the message before signing. +func KoskSignCustHash(curve CurveSystem, sk *big.Int, msg []byte, hash func([]byte) Point) Point { + m := append([]byte{1}, msg...) + return SignCustHash(sk, m, hash) +} + +// KoskVerifySingleSignature checks that a single kosk signature is valid. +func KoskVerifySingleSignature(curve CurveSystem, sig Point, pubKey Point, msg []byte) bool { + return KoskVerifySingleSignatureCustHash(curve, pubKey, msg, sig, curve.HashToG1) +} + +// KoskVerifySingleSignatureCustHash checks that a single kosk signature is valid, +// with the supplied hash function. +func KoskVerifySingleSignatureCustHash(curve CurveSystem, pubKey Point, msg []byte, + sig Point, hash func([]byte) Point) bool { + m := append([]byte{1}, msg...) + return VerifySingleSignature(curve, sig, pubKey, m) +} + +// KoskVerifyAggregateSignature verifies that the aggregated signature proves +// that all messages were signed by the associated keys. +func KoskVerifyAggregateSignature(curve CurveSystem, aggsig Point, keys []Point, msgs [][]byte) bool { + return verifyAggSig(curve, aggsig, keys, msgs, true) +} + +// Verify checks that a single message has been signed by a set of keys +// vulnerable against rogue public-key attack, if keys have not been authenticated +func (m MultiSig) Verify(curve CurveSystem) bool { + return KoskVerifyMultiSignature(curve, m.sig, m.keys, m.msg) +} + +// KoskVerifyMultiSignature checks that the aggregate signature correctly proves +// that a single message has been signed by a set of keys, +// vulnerable against chosen key attack, if keys have not been authenticated +func KoskVerifyMultiSignature(curve CurveSystem, aggsig Point, keys []Point, msg []byte) bool { + msg2 := append([]byte{1}, msg...) + return verifyMultiSignature(curve, aggsig, keys, msg2) +} + +// KoskVerifyMultiSignatureWithMultiplicity verifies a BLS multi signature where +// multiple copies of each signature may have been included in the aggregation +func KoskVerifyMultiSignatureWithMultiplicity(curve CurveSystem, aggsig Point, keys []Point, + multiplicity []int64, msg []byte) bool { + if multiplicity == nil { + return KoskVerifyMultiSignature(curve, aggsig, keys, msg) + } else if len(keys) != len(multiplicity) { + return false + } + factors := make([]*big.Int, len(multiplicity)) + for i := 0; i < len(keys); i++ { + factors[i] = big.NewInt(multiplicity[i]) + } + scaledKeys := ScalePoints(keys, factors) + return KoskVerifyMultiSignature(curve, aggsig, scaledKeys, msg) +} diff --git a/bgls/blsKosk_test.go b/bgls/blsKosk_test.go new file mode 100644 index 0000000..fe5e8ef --- /dev/null +++ b/bgls/blsKosk_test.go @@ -0,0 +1,94 @@ +// Copyright (C) 2018 Authors +// distributed under Apache 2.0 license + +package bgls + +import ( + "crypto/rand" + "math/big" + mathrand "math/rand" + "testing" + + . "github.com/Project-Arda/bgls/curves" + "github.com/stretchr/testify/assert" +) + +func TestKoskSingleSigner(t *testing.T) { + for _, curve := range curves { + sk, vk, err := KeyGen(curve) + assert.Nil(t, err, "Key generation failed") + assert.True(t, CheckAuthentication(curve, vk, Authenticate(curve, sk)), "Key Authentication failed") + msg := make([]byte, 64) + _, err = rand.Read(msg) + assert.Nil(t, err, "test data generation failed") + sig := KoskSign(curve, sk, msg) + assert.True(t, KoskVerifySingleSignature(curve, sig, vk, msg), "Kosk signature verification failed") + + sig2 := sig.Copy() + sig2, _ = sig2.Add(curve.GetG1()) + assert.False(t, KoskVerifySingleSignature(curve, sig2, vk, msg), "Kosk signature succeeding when it shouldn't") + + // TODO Add tests to show that this doesn't succeed if d or vk is altered + } +} + +func TestKoskMultiSig(t *testing.T) { + for _, curve := range curves { + Tests, Size, Signers := 5, 32, 10 + for i := 0; i < Tests; i++ { + msg := make([]byte, Size) + rand.Read(msg) + signers := make([]Point, Signers) + sigs := make([]Point, Signers) + for j := 0; j < Signers; j++ { + sk, vk, _ := KeyGen(curve) + sigs[j] = KoskSign(curve, sk, msg) + signers[j] = vk + } + aggsig := AggregateSignatures(sigs) + assert.True(t, KoskVerifyMultiSignature(curve, aggsig, signers, msg), + "Aggregate MultiSig verification failed") + msg2 := make([]byte, Size) + rand.Read(msg2) + assert.False(t, KoskVerifyMultiSignature(curve, aggsig, signers, msg2), + "Aggregate MultiSig verification succeeded on incorrect msg") + _, vkf, _ := KeyGen(curve) + aggkey := AggregateKeys(signers) + assert.True(t, KoskVerifySingleSignature(curve, aggsig, aggkey, msg), + "Aggregate MultiSig verification failed with the aggkey pre-aggregated") + signers[0] = vkf + assert.False(t, KoskVerifyMultiSignature(curve, aggsig, signers, msg), + "Aggregate MultiSig verification succeeded on incorrect signers") + } + } +} + +func TestKoskMultiSigWithMultiplicity(t *testing.T) { + for _, curve := range curves { + Tests, Size, Signers := 5, 32, 10 + for i := 0; i < Tests; i++ { + msg := make([]byte, Size) + rand.Read(msg) + signers := make([]Point, Signers) + sigs := make([]Point, Signers) + multi := make([]int64, Signers) + for j := 0; j < Signers; j++ { + sk, vk, _ := KeyGen(curve) + multi[j] = mathrand.Int63() + sigs[j] = KoskSign(curve, sk, msg).Mul(big.NewInt(multi[j])) + signers[j] = vk + } + aggSig := AggregatePoints(sigs) + assert.True(t, KoskVerifyMultiSignatureWithMultiplicity(curve, aggSig, signers, multi, msg), + "Aggregate MultiSig verification failed") + msg2 := make([]byte, Size) + rand.Read(msg2) + assert.False(t, KoskVerifyMultiSignatureWithMultiplicity(curve, aggSig, signers, multi, msg2), + "Aggregate MultiSig verification succeeded on incorrect msg") + _, vkf, _ := KeyGen(curve) + signers[0] = vkf + assert.False(t, KoskVerifyMultiSignatureWithMultiplicity(curve, aggSig, signers, multi, msg), + "Aggregate MultiSig verification succeeded on incorrect signers") + } + } +} diff --git a/bgls/doc.go b/bgls/doc.go new file mode 100644 index 0000000..7637f6a --- /dev/null +++ b/bgls/doc.go @@ -0,0 +1,36 @@ +// Copyright (C) 2018 Authors +// distributed under Apache 2.0 license + +// Package bgls implements bls signatures, as described by +// Short Signatures from the Weil Pairing. In this library, an aggregate signature +// refers to an aggregation of signatures on different messages into a single signature. +// A multi signature refers to an aggregation of signatures on the same message +// into the same signature. The difference is that a multi signature can be +// verified quite quickly, using 2 pairing operations regardless of the number +// of signers, whereas an aggregate signature requires n+1 pairing operations. +// +// There are three different +// methods to protect against the rogue public key attack. The three methods are +// proving knowledge of secret key (kosk), enforcing that all messages are distinct +// (Distinct Message), and using a hash of the public keys to create exponents +// that are used in aggregation (Hashed Aggregation Exponents - HAE). These are +// all described in https://crypto.stanford.edu/~dabo/pubs/papers/BLSmultisig.html, +// where HAE is Dan Boneh's new method introduced in that article. +// +// Proof of knowledge of the secret key is done in this library through +// doing a BLS signature on the public key itself as a message. See blsKosk.go +// for more details. Note that this Kosk method is not interoperable with 'plain' +// bls due to design choices explained in blsKosk.go +// +// BLS with distinct messages is done in this library by prepending the public +// key to each message, before signing, to ensure that it each message is unique. +// (thereby circumventing rogue public key attacks). See blsDistinctMessage.go +// for more details. Note that BLS with distinct messages does not offer multi +// sigs in their efficiently computable form where only 2 pairings are required. +// +// The third method for preventing the rogue public key attack is explained in +// in blsHAE.go, and in Boneh's paper. The method for hashing public keys to the +// exponents is to write them to blake2x, and then to squeeze the corresponding +// amount of output from the XOF. +// +package bgls diff --git a/bgls_test.go b/bgls_test.go deleted file mode 100644 index 69ed3a3..0000000 --- a/bgls_test.go +++ /dev/null @@ -1,396 +0,0 @@ -// Copyright (C) 2018 Authors -// distributed under Apache 2.0 license - -package bgls - -import ( - "crypto/rand" - "math/big" - "os" - "testing" - - "github.com/stretchr/testify/assert" -) - -var curves = []CurveSystem{Altbn128} - -func TestAltbnHashToCurve(t *testing.T) { - curve := Altbn128 - N := 10 - msgs := make([][]byte, N) - for i := 0; i < N; i++ { - msgs[i] = make([]byte, N) - _, _ = rand.Read(msgs[i]) - - testHashConsistency(AltbnSha3, "altbn sha3 hash", msgs[i], t) - testHashConsistency(AltbnKang12, "altbn kang12 hash", msgs[i], t) - testHashConsistency(AltbnBlake2b, "altbn blake2b hash", msgs[i], t) - - p1 := curve.HashToG1(msgs[i]) - p2 := curve.HashToG1(msgs[i]) - assert.True(t, p1.Equals(p2), "inconsistent results in Altbn HashToCurve") - } -} - -// func TestBls12Hashing(t *testing.T) { -// // Tests consistency -// N := 10 -// msgs := make([][]byte, N) -// for i := 0; i < N; i++ { -// msgs[i] = make([]byte, N) -// _, _ = rand.Read(msgs[i]) -// testHashConsistency(Bls12Sha3, "bls12 sha3 hash", msgs[i], t) -// testHashConsistency(Bls12Kang12, "bls12 kang12 hash", msgs[i], t) -// testHashConsistency(Bls12Blake2b, "bls12 blake2b hash", msgs[i], t) -// } -// } - -func testHashConsistency(hashFunc func(message []byte) (p1, p2 *big.Int), hashname string, msg []byte, t *testing.T) { - x1, y1 := hashFunc(msg) - x2, y2 := hashFunc(msg) - assert.True(t, x1.Cmp(x2) == 0 && y1.Cmp(y2) == 0, "inconsistent results in "+hashname) -} - -func TestEthereumHash(t *testing.T) { - curve := Altbn128 - // Tests Altbn hash to curve against known solidity test case. - a := []byte{116, 101, 115, 116} - x, y := AltbnKeccak3(a) - expX, _ := new(big.Int).SetString("634489172570043803084693618096875920319784881922983678883461805150451460743", 10) - expY, _ := new(big.Int).SetString("15164142362807052582232776116457640322025300091343369508144366426999358332749", 10) - assert.True(t, x.Cmp(expX) == 0 && y.Cmp(expY) == 0, "Hash does not match known Ethereum Output") - pt := curve.HashToG1(a) - x2, y2 := pt.ToAffineCoords() - assert.True(t, x.Cmp(x2) == 0 && y.Cmp(y2) == 0, "Conversion of point to coordinates is not working") - - xi, xr, yi, yr := altbnG2.ToAffineCoords() - knownxi, _ := new(big.Int).SetString("11559732032986387107991004021392285783925812861821192530917403151452391805634", 10) - knownxr, _ := new(big.Int).SetString("10857046999023057135944570762232829481370756359578518086990519993285655852781", 10) - knownyi, _ := new(big.Int).SetString("4082367875863433681332203403145435568316851327593401208105741076214120093531", 10) - knownyr, _ := new(big.Int).SetString("8495653923123431417604973247489272438418190587263600148770280649306958101930", 10) - - assert.Zero(t, xi.Cmp(knownxi), "xi doesn't match") - assert.Zero(t, xr.Cmp(knownxr), "xr doesn't match") - assert.Zero(t, yi.Cmp(knownyi), "yi doesn't match") - assert.Zero(t, yr.Cmp(knownyr), "yr doesn't match") - - altG2, _ := curve.MakeG2Point(xi, xr, yi, yr) - assert.True(t, altG2.Equals(curve.GetG2()), "MakeG2Point Failed") -} - -func TestSingleSigner(t *testing.T) { - for _, curve := range curves { - sk, vk, err := KeyGen(curve) - assert.Nil(t, err, "Key generation failed") - assert.True(t, CheckAuthentication(curve, vk, Authenticate(curve, sk)), "Key Authentication failed") - d := make([]byte, 64) - _, err = rand.Read(d) - assert.Nil(t, err, "test data generation failed") - sig := Sign(curve, sk, d) - assert.True(t, Verify(curve, vk, d, sig), "Point1 verification failed") - - sigTmp := sig.Copy() - sigTmp, _ = sigTmp.Add(curve.GetG1()) - sig2 := sigTmp - assert.False(t, Verify(curve, vk, d, sig2), "Point1 verification succeeding when it shouldn't") - - // TODO Add tests to show that this doesn't succeed if d or vk is altered - } -} - -func TestAggregation(t *testing.T) { - for _, curve := range curves { - N, Size := 6, 32 - msgs := make([][]byte, N+1) - sigs := make([]Point1, N+1) - pubkeys := make([]Point2, N+1) - for i := 0; i < N; i++ { - msgs[i] = make([]byte, Size) - rand.Read(msgs[i]) - - sk, vk, _ := KeyGen(curve) - sig := Sign(curve, sk, msgs[i]) - pubkeys[i] = vk - sigs[i] = sig - } - aggSig := AggregateG1(sigs[:N]) - assert.True(t, VerifyAggregateSignature(curve, aggSig, pubkeys[:N], msgs[:N], false), - "Aggregate Point1 verification failed") - assert.False(t, VerifyAggregateSignature(curve, aggSig, pubkeys[:N-1], msgs[:N], false), - "Aggregate Point1 verification succeeding without enough pubkeys") - skf, vkf, _ := KeyGen(curve) - pubkeys[N] = vkf - sigs[N] = Sign(curve, skf, msgs[0]) - msgs[N] = msgs[0] - aggSig = AggregateG1(sigs) - assert.False(t, VerifyAggregateSignature(curve, aggSig, pubkeys, msgs, false), - "Aggregate Point1 succeeding with duplicate messages with allow duplicates being false") - assert.True(t, VerifyAggregateSignature(curve, aggSig, pubkeys, msgs, true), - "Aggregate Point1 failing with duplicate messages with allow duplicates") - assert.False(t, VerifyAggregateSignature(curve, aggSig, pubkeys[:N], msgs[:N], false), - "Aggregate Point1 succeeding with invalid signature") - msgs[0] = msgs[1] - msgs[1] = msgs[N] - aggSig = AggregateG1(sigs[:N]) - assert.False(t, VerifyAggregateSignature(curve, aggSig, pubkeys[:N], msgs[:N], false), - "Aggregate Point1 succeeded with messages 0 and 1 switched") - - // TODO Add tests to make sure there is no mutation - } -} - -func TestMultiSig(t *testing.T) { - for _, curve := range curves { - Tests, Size, Signers := 5, 32, 10 - for i := 0; i < Tests; i++ { - msg := make([]byte, Size) - rand.Read(msg) - signers := make([]Point2, Signers) - sigs := make([]Point1, Signers) - for j := 0; j < Signers; j++ { - sk, vk, _ := KeyGen(curve) - sigs[j] = Sign(curve, sk, msg) - signers[j] = vk - } - aggSig := AggregateG1(sigs) - assert.True(t, VerifyMultiSignature(curve, aggSig, signers, msg), - "Aggregate MultiSig verification failed") - msg2 := make([]byte, Size) - rand.Read(msg2) - assert.False(t, VerifyMultiSignature(curve, aggSig, signers, msg2), - "Aggregate MultiSig verification succeeded on incorrect msg") - _, vkf, _ := KeyGen(curve) - signers[0] = vkf - assert.False(t, VerifyMultiSignature(curve, aggSig, signers, msg), - "Aggregate MultiSig verification succeeded on incorrect signers") - } - } -} - -func TestMarshal(t *testing.T) { - for _, curve := range curves { - numTests := 32 - requiredScalars := []*big.Int{one, altbnG1Order} - for i := 0; i < numTests; i++ { - scalar, _ := rand.Int(rand.Reader, curve.getG1Order()) - if i < len(requiredScalars) { - scalar = requiredScalars[i] - } - - mulg1 := curve.GetG1().Mul(scalar) - marshalled := mulg1.Marshal() - if recoveredG1, ok := curve.UnmarshalG1(marshalled); ok { - assert.True(t, recoveredG1.Equals(mulg1), - "Unmarshalling G1 is not consistent with Marshal G1") - } else { - t.Error("Unmarshalling G1 failed") - } - marshalled = marshalled[1:] - if _, ok := curve.UnmarshalG1(marshalled); ok { - t.Error("Unmarshalling G1 is succeeding when the byte array is of the wrong length") - } - - mulg2 := curve.GetG2().Mul(scalar) - marshalled = mulg2.Marshal() - if recoveredG2, ok := curve.UnmarshalG2(marshalled); ok { - assert.True(t, recoveredG2.Equals(mulg2), - "Unmarshalling G2 is not consistent with Marshal G2") - } else { - t.Error("Unmarshalling G2 failed on scalar " + scalar.String()) - } - marshalled = marshalled[1:] - if _, ok := curve.UnmarshalG2(marshalled); ok { - t.Error("Unmarshalling G2 is succeeding when the byte array is of the wrong length") - } - - mulgT, _ := mulg1.Pair(curve.GetG2()) - marshalled = mulgT.Marshal() - // fmt.Println("Coordinate wise representation of g1 * " + scalar.String() + " paired with g2") - // for i := 0; i < 12; i++ { // Code to print coordinates - // fmt.Println(marshalled[i*32 : (i+1)*32]) - // } - if recoveredGT, ok := curve.UnmarshalGT(marshalled); ok { - assert.True(t, recoveredGT.Equals(mulgT), - "Unmarshalling GT is not consistent with Marshal GT") - } else { - t.Error("Unmarshalling GT failed") - } - marshalled = marshalled[1:] - if _, ok := curve.UnmarshalGT(marshalled); ok { - t.Error("Unmarshalling GT is succeeding when the byte array is of the wrong length") - } - } - } -} - -func TestKnownCases(t *testing.T) { - curve := Altbn128 - N := 3 - msgs := make([][]byte, N) - msg1 := []byte{65, 20, 86, 143, 250} - msg2 := []byte{157, 76, 30, 64, 128} - msg3 := []byte{202, 255, 227, 59, 238} - sk1, _ := new(big.Int).SetString("7830752896741750908830464020410322281763657818307273013205711220156049734883", 10) - sk2, _ := new(big.Int).SetString("10065703961787583059826108098259128135713944641698809475150397710106034167549", 10) - sk3, _ := new(big.Int).SetString("17145080297596291172729378766677038070724014074212589728874454474449054012678", 10) - - pubkeys := make([]Point2, N) - vk1, vk2, vk3 := LoadPublicKey(curve, sk1), LoadPublicKey(curve, sk2), LoadPublicKey(curve, sk3) - msgs[0], msgs[1], msgs[2] = msg1, msg2, msg3 - pubkeys[0], pubkeys[1], pubkeys[2] = vk1, vk2, vk3 - - sigGen1 := Sign(curve, sk1, msgs[0]) - sigGen2 := Sign(curve, sk2, msgs[1]) - sigGen3 := Sign(curve, sk3, msgs[2]) - sigVal1_1, _ := new(big.Int).SetString("21637350149051642305293442272499488026428127697128429631193536777535027009518", 10) - sigVal1_2, _ := new(big.Int).SetString("149479762519169687769683150632580363857094522511606512652585818657412262489", 10) - sigVal2_1, _ := new(big.Int).SetString("14834848655731874780751719195269704123719987185153910215596714529658047741046", 10) - sigVal2_2, _ := new(big.Int).SetString("5847895190688397897156144807293187828750812390735163763226617490736304595451", 10) - sigVal3_1, _ := new(big.Int).SetString("21239057713889019692075876723610689006006025737755828182426488764514117409847", 10) - sigVal3_2, _ := new(big.Int).SetString("11967902298809667109716532536825395835657143208987520118971083760489593281874", 10) - sigChk1, _ := curve.MakeG1Point(sigVal1_1, sigVal1_2) - sigChk2, _ := curve.MakeG1Point(sigVal2_1, sigVal2_2) - sigChk3, _ := curve.MakeG1Point(sigVal3_1, sigVal3_2) - - assert.False(t, (!sigChk1.Equals(sigGen1) || !sigChk2.Equals(sigGen2) || !sigChk3.Equals(sigGen3)), - "Recreating message signatures from known test cases failed") - - sigs := make([]Point1, N) - sigs[0], sigs[1], sigs[2] = sigGen1, sigGen2, sigGen3 - - aggSig1, _ := new(big.Int).SetString("12682380538491839124790562586247816360937861029087546329767912056050859037239", 10) - aggSig2, _ := new(big.Int).SetString("5755139208159515629159661524903000057840676877654799839167369795924360592246", 10) - aggSigChk, _ := curve.MakeG1Point(aggSig1, aggSig2) - - aggSig := AggregateG1(sigs) - assert.True(t, aggSigChk.Equals(aggSig), - "Aggregate Point1 does not match the known test case.") - assert.True(t, VerifyAggregateSignature(curve, aggSig, pubkeys, msgs, false), - "Aggregate Point1 verification failed") -} - -func BenchmarkKeygen(b *testing.B) { - b.ResetTimer() - curve := Altbn128 - for i := 0; i < b.N; i++ { - _, _, res := KeyGen(curve) - if res != nil { - b.Error("key gen failure") - } - } -} - -func BenchmarkAltBnHashToCurve(b *testing.B) { - curve := Altbn128 - ms := make([][]byte, b.N) - for i := 0; i < b.N; i++ { - ms[i] = make([]byte, 64) - rand.Read(ms[i]) - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - curve.HashToG1(ms[i]) - } -} - -func BenchmarkSigning(b *testing.B) { - curve := Altbn128 - sks := make([]*big.Int, b.N) - ms := make([][]byte, b.N) - for i := 0; i < b.N; i++ { - ms[i] = make([]byte, 64) - rand.Read(ms[i]) - sk, _, _ := KeyGen(curve) - sks[i] = sk - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - _ = Sign(curve, sks[i], ms[i]) - } -} - -func BenchmarkVerification(b *testing.B) { - curve := Altbn128 - message := make([]byte, 64) - rand.Read(message) - sk, vk, _ := KeyGen(curve) - sig := Sign(curve, sk, message) - b.ResetTimer() - for i := 0; i < b.N; i++ { - if !Verify(curve, vk, message, sig) { - b.Error("verification failed") - } - } -} - -var vks []Point2 -var sgs []Point1 -var msg []byte - -func TestMain(m *testing.M) { - curve := Altbn128 - vks = make([]Point2, 2048) - sgs = make([]Point1, 2048) - msg = make([]byte, 64) - rand.Read(msg) - for i := 0; i < 2048; i++ { - sk, vk, _ := KeyGen(curve) - vks[i] = vk - sgs[i] = Sign(curve, sk, msg) - } - os.Exit(m.Run()) -} - -func benchmulti(b *testing.B, k int) { - curve := Altbn128 - multisig := MultiSig{vks[:k], AggregateG1(sgs[:k]), msg} - b.ResetTimer() - for i := 0; i < b.N; i++ { - if !multisig.Verify(curve) { - b.Error("MultiSig verification failed") - } - } -} - -func BenchmarkMultiVerification64(b *testing.B) { - benchmulti(b, 64) -} - -func BenchmarkMultiVerification128(b *testing.B) { - benchmulti(b, 128) -} - -func BenchmarkMultiVerification256(b *testing.B) { - benchmulti(b, 256) -} - -func BenchmarkMultiVerification512(b *testing.B) { - benchmulti(b, 512) -} - -func BenchmarkMultiVerification1024(b *testing.B) { - benchmulti(b, 1024) -} - -func BenchmarkMultiVerification2048(b *testing.B) { - benchmulti(b, 2048) -} - -func BenchmarkAggregateVerification(b *testing.B) { - curve := Altbn128 - verifkeys := make([]Point2, b.N) - sigs := make([]Point1, b.N) - messages := make([][]byte, b.N) - for i := 0; i < b.N; i++ { - messages[i] = make([]byte, 64) - rand.Read(messages[i]) - sk, vk, _ := KeyGen(curve) - verifkeys[i] = vk - sigs[i] = Sign(curve, sk, messages[i]) - } - aggsig := AggSig{verifkeys, messages, AggregateG1(sigs)} - b.ResetTimer() - if !aggsig.Verify(curve) { - b.Error("Aggregate verificaton failed") - } -} diff --git a/bls12_381.go b/bls12_381.go deleted file mode 100644 index a5ec229..0000000 --- a/bls12_381.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (C) 2018 Authors -// distributed under Apache 2.0 license - -package bgls - -import ( - "math/big" -) - -var bls12Q, _ = new(big.Int).SetString("0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab", 0) -var bls12X, _ = new(big.Int).SetString("-0xd201000000010000", 0) -var bls12A, _ = new(big.Int).SetString("0", 10) -var bls12B, _ = new(big.Int).SetString("4", 10) -var bls12Cofactor = makeBls12Cofactor(bls12X) - -func makeBls12Cofactor(x *big.Int) *big.Int { - x.Mod(x, bls12Q) - x.Sub(x, one) - x.Exp(x, x, two) - x.Div(x, three) - return x -} - -// // Bls12Sha3 Hashes a message to a point on BLS12-381 using SHA3 and try and increment -// // The return value is the x,y affine coordinate pair. -// func Bls12Sha3(message []byte) (p1, p2 *big.Int) { -// // TODO ADD COFACTOR MULTIPLICATION -// p1, p2 = hash64(message, sha3.Sum512, bls12Q, bls12XToYSquared) -// return -// } -// -// // Bls12Blake2b Hashes a message to a point on BLS12-381 using Blake2b and try and increment -// // The return value is the x,y affine coordinate pair. -// func Bls12Blake2b(message []byte) (p1, p2 *big.Int) { -// // TODO ADD COFACTOR MULTIPLICATION -// p1, p2 = hash64(message, blake2b.Sum512, bls12Q, bls12XToYSquared) -// return -// } -// -// // Bls12Kang12 Hashes a message to a point on BLS12-381 using Kangaroo Twelve and try and increment -// // The return value is the x,y affine coordinate pair. -// func Bls12Kang12(message []byte) (p1, p2 *big.Int) { -// // TODO ADD COFACTOR MULTIPLICATION -// p1, p2 = hash64(message, kang12_64, bls12Q, bls12XToYSquared) -// return -// } - -// func bls12XToYSquared(x *big.Int) *big.Int { -// result := new(big.Int) -// result.Exp(x, three, bls12Q) -// result.Add(result, bls12B) -// return result -// } diff --git a/curve.go b/curve.go deleted file mode 100644 index b58e228..0000000 --- a/curve.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (C) 2018 Authors -// distributed under Apache 2.0 license - -package bgls - -import ( - "math/big" -) - -// CurveSystem is a set of parameters and functions for a pairing based cryptosystem -// It has everything necessary to support all bgls functionality which we use. -type CurveSystem interface { - MakeG1Point(*big.Int, *big.Int) (Point1, bool) - // MakeG2Point(*big.Int, *big.Int, *big.Int, *big.Int) (Point2, bool) - // MakeGTPoint(*big.Int, *big.Int) (PointT, bool) - - // - // GTToAffineCoords(PointT) (*big.Int, *big.Int) - - UnmarshalG1([]byte) (Point1, bool) - UnmarshalG2([]byte) (Point2, bool) - UnmarshalGT([]byte) (PointT, bool) - - GetG1() Point1 - GetG2() Point2 - GetGT() PointT - - HashToG1(message []byte) Point1 - - getG1Q() *big.Int - // getGTQ() *big.Int - - getG1A() *big.Int - getG1B() *big.Int - getG1Order() *big.Int - g1XToYSquared(*big.Int) *big.Int -} - -// Point1 is a way to represent a point on G1, in the first elliptic curve. -type Point1 interface { - Add(Point1) (Point1, bool) - Copy() Point1 - Equals(Point1) bool - Marshal() []byte - Mul(*big.Int) Point1 - Pair(Point2) (PointT, bool) - ToAffineCoords() (*big.Int, *big.Int) -} - -// Point2 is a way to represent a point on G2, in the first elliptic curve. -type Point2 interface { - Add(Point2) (Point2, bool) - Copy() Point2 - Equals(Point2) bool - Marshal() []byte - Mul(*big.Int) Point2 - ToAffineCoords() (*big.Int, *big.Int, *big.Int, *big.Int) -} - -// PointT is a way to represent a point on GT, in the first elliptic curve. -type PointT interface { - Add(PointT) (PointT, bool) - Copy() PointT - Equals(PointT) bool - Marshal() []byte - Mul(*big.Int) PointT - // ToAffineCoords() (*big.Int, *big.Int) -} diff --git a/alt_bn128.go b/curves/altbn128.go similarity index 58% rename from alt_bn128.go rename to curves/altbn128.go index 8fa964b..8d9fa94 100644 --- a/alt_bn128.go +++ b/curves/altbn128.go @@ -1,11 +1,10 @@ // Copyright (C) 2018 Authors // distributed under Apache 2.0 license -package bgls +package curves import ( "bytes" - "fmt" "math/big" "github.com/dchest/blake2b" @@ -32,9 +31,19 @@ type altbn128PointT struct { // Altbn128Inst is the instance for the altbn128 curve, with all of its functions. var Altbn128 = &altbn128{} +// Returns the name of the curve +func (curve *altbn128) Name() string { + return "altbn128" +} + // MakeG1Point copies points into []byte and unmarshals to get around curvePoint not being exported -func (curve *altbn128) MakeG1Point(x, y *big.Int) (Point1, bool) { - xBytes, yBytes := x.Bytes(), y.Bytes() +// Check does nothing here, because the upstream library always +// ensures that the point is on the curve. +func (curve *altbn128) MakeG1Point(coords []*big.Int, check bool) (Point, bool) { + if len(coords) != 2 { + return nil, false + } + xBytes, yBytes := coords[0].Bytes(), coords[1].Bytes() ret := make([]byte, 64) copy(ret[32-len(xBytes):], xBytes) copy(ret[64-len(yBytes):], yBytes) @@ -47,7 +56,7 @@ func (curve *altbn128) MakeG1Point(x, y *big.Int) (Point1, bool) { return &altbn128Point1{result}, true } -func (g1Point *altbn128Point1) Add(otherPoint1 Point1) (Point1, bool) { +func (g1Point *altbn128Point1) Add(otherPoint1 Point) (Point, bool) { if other, ok := (otherPoint1).(*altbn128Point1); ok { sum := new(bn256.G1).Add(g1Point.point, other.point) ret := &altbn128Point1{sum} @@ -56,13 +65,13 @@ func (g1Point *altbn128Point1) Add(otherPoint1 Point1) (Point1, bool) { return nil, false } -func (g1Point *altbn128Point1) Copy() Point1 { +func (g1Point *altbn128Point1) Copy() Point { result := new(bn256.G1) result.Unmarshal(g1Point.point.Marshal()) return &altbn128Point1{result} } -func (g1Point *altbn128Point1) Equals(otherPoint1 Point1) bool { +func (g1Point *altbn128Point1) Equals(otherPoint1 Point) bool { if other, ok := (otherPoint1).(*altbn128Point1); ok { return bytes.Equal(g1Point.point.Marshal(), other.point.Marshal()) } @@ -70,15 +79,19 @@ func (g1Point *altbn128Point1) Equals(otherPoint1 Point1) bool { } func (g1Point *altbn128Point1) Marshal() []byte { - x, y := g1Point.ToAffineCoords() - xBytes := pad32Bytes(x.Bytes()) - y.Mul(y, two) - if y.Cmp(altbnG1Q) == 1 { + coords := g1Point.ToAffineCoords() + xBytes := pad32Bytes(coords[0].Bytes()) + coords[1].Mul(coords[1], two) + if coords[1].Cmp(altbnG1Q) == 1 { xBytes[0] += 128 } return xBytes } +func (g1Point *altbn128Point1) MarshalUncompressed() []byte { + return g1Point.point.Marshal() +} + func pad32Bytes(xBytes []byte) []byte { if len(xBytes) < 32 { offset := 32 - len(xBytes) @@ -91,50 +104,81 @@ func pad32Bytes(xBytes []byte) []byte { return xBytes } -func (g1Point *altbn128Point1) Mul(scalar *big.Int) Point1 { - prod := new(bn256.G1).ScalarMult(g1Point.point, scalar) +func (g1Point *altbn128Point1) Mul(scalar *big.Int) Point { + scalar2 := new(big.Int) + cmp := scalar.Cmp(zero) + if cmp < 0 { + g1Point = g1Point.Negate() + scalar2.Mul(scalar, big.NewInt(-1)) + } else if cmp == 0 { + return Altbn128.GetG1Infinity() + } else { + scalar2 = scalar + } + prod := new(bn256.G1).ScalarMult(g1Point.point, scalar2) ret := &altbn128Point1{prod} return ret } -func (g1Point *altbn128Point1) Pair(g2Point Point2) (PointT, bool) { - if other, ok := (g2Point).(*altbn128Point2); ok { - p3 := bn256.Pair(g1Point.point, other.point) +func (g1Point *altbn128Point1) Negate() *altbn128Point1 { + coords := g1Point.ToAffineCoords() + coords[1].Sub(altbnG1Q, coords[1]) + newPt, _ := Altbn128.MakeG1Point(coords, false) + return newPt.(*altbn128Point1) +} + +func (curve *altbn128) Pair(g1Point Point, g2Point Point) (PointT, bool) { + pt1, ok := g1Point.(*altbn128Point1) + if !ok { + return nil, false + } + if pt2, ok := (g2Point).(*altbn128Point2); ok { + p3 := bn256.Pair(pt1.point, pt2.point) ret := altbn128PointT{p3} return ret, true } return nil, false } -func (g1Point *altbn128Point1) ToAffineCoords() (x, y *big.Int) { +func (curve *altbn128) PairingProduct(g1Points []Point, g2Points []Point) (PointT, bool) { + return concurrentPairingProduct(curve, g1Points, g2Points) +} + +// ToAffineCoords returns the affine coordinate representation of the point +// in the form: [X, Y] +func (g1Point *altbn128Point1) ToAffineCoords() []*big.Int { Bytestream := g1Point.point.Marshal() xBytes, yBytes := Bytestream[:32], Bytestream[32:64] - x = new(big.Int).SetBytes(xBytes) - y = new(big.Int).SetBytes(yBytes) - return + x := new(big.Int).SetBytes(xBytes) + y := new(big.Int).SetBytes(yBytes) + return []*big.Int{x, y} } -// MakeG2Point copies points into []byte and unmarshals to get around twistPoint not being exported -func (curve *altbn128) MakeG2Point(xx, xy, yx, yy *big.Int) (Point2, bool) { - xxBytes, xyBytes := pad32Bytes(xx.Bytes()), pad32Bytes(xy.Bytes()) - yxBytes, yyBytes := pad32Bytes(yx.Bytes()), pad32Bytes(yy.Bytes()) +// MakeG2Point expects coords to be of the form: [x0, x1, y0, y1], +// where X = x0 * i + x1, and Y = y0 * i + y1 +// check does nothing, since the upstream repository always checks if the point +// is on the curve +func (curve *altbn128) MakeG2Point(coords []*big.Int, check bool) (Point, bool) { + if len(coords) != 4 { + return nil, false + } + x0Bytes, x1Bytes := pad32Bytes(coords[0].Bytes()), pad32Bytes(coords[1].Bytes()) + y0Bytes, y1Bytes := pad32Bytes(coords[2].Bytes()), pad32Bytes(coords[3].Bytes()) ret := make([]byte, 128) - copy(ret[:32], xxBytes) - copy(ret[32:], xyBytes) - copy(ret[64:], yxBytes) - copy(ret[96:], yyBytes) + copy(ret[:32], x0Bytes) + copy(ret[32:], x1Bytes) + copy(ret[64:], y0Bytes) + copy(ret[96:], y1Bytes) result := new(bn256.G2) var ok error _, ok = result.Unmarshal(ret) if ok != nil { - fmt.Println(ok) - fmt.Println(len(xxBytes), len(xyBytes), len(yxBytes), len(yyBytes)) return nil, false } return &altbn128Point2{result}, true } -func (g2Point *altbn128Point2) Add(otherPoint2 Point2) (Point2, bool) { +func (g2Point *altbn128Point2) Add(otherPoint2 Point) (Point, bool) { if other, ok := (otherPoint2).(*altbn128Point2); ok { sum := new(bn256.G2).Add(g2Point.point, other.point) ret := &altbn128Point2{sum} @@ -143,13 +187,13 @@ func (g2Point *altbn128Point2) Add(otherPoint2 Point2) (Point2, bool) { return nil, false } -func (g2Point *altbn128Point2) Copy() Point2 { +func (g2Point *altbn128Point2) Copy() Point { result := new(bn256.G2) result.Unmarshal(g2Point.point.Marshal()) return &altbn128Point2{result} } -func (g2Point *altbn128Point2) Equals(otherPoint2 Point2) bool { +func (g2Point *altbn128Point2) Equals(otherPoint2 Point) bool { if other, ok := (otherPoint2).(*altbn128Point2); ok { return bytes.Equal(g2Point.point.Marshal(), other.point.Marshal()) } @@ -157,17 +201,17 @@ func (g2Point *altbn128Point2) Equals(otherPoint2 Point2) bool { } func (g2Point *altbn128Point2) Marshal() []byte { - xi, xr, yi, yr := g2Point.ToAffineCoords() - xiBytes := pad32Bytes(xi.Bytes()) - xrBytes := pad32Bytes(xr.Bytes()) - y2 := &complexNum{yi, yr} + coords := g2Point.ToAffineCoords() + xiBytes := pad32Bytes(coords[0].Bytes()) + xrBytes := pad32Bytes(coords[1].Bytes()) + y2 := &complexNum{coords[2], coords[3]} y2.Exp(y2, two, altbnG1Q) - yi.Mul(yi, two) - yr.Mul(yr, two) - if yi.Cmp(altbnG1Q) == 1 { + coords[2].Mul(coords[2], two) + coords[3].Mul(coords[3], two) + if coords[2].Cmp(altbnG1Q) == 1 { xiBytes[0] += 128 } - if yr.Cmp(altbnG1Q) == 1 { + if coords[3].Cmp(altbnG1Q) == 1 { xrBytes[0] += 128 } xBytes := make([]byte, 64, 64) @@ -176,21 +220,45 @@ func (g2Point *altbn128Point2) Marshal() []byte { return xBytes } -func (g2Point *altbn128Point2) Mul(scalar *big.Int) Point2 { - prod := new(bn256.G2).ScalarMult(g2Point.point, scalar) +func (g2Point *altbn128Point2) MarshalUncompressed() []byte { + return g2Point.point.Marshal() +} + +func (g2Point *altbn128Point2) Negate() *altbn128Point2 { + coords := g2Point.ToAffineCoords() + coords[2].Sub(altbnG1Q, coords[2]) + coords[3].Sub(altbnG1Q, coords[3]) + newPt, _ := Altbn128.MakeG2Point(coords, false) + return newPt.(*altbn128Point2) +} + +func (g2Point *altbn128Point2) Mul(scalar *big.Int) Point { + scalar2 := new(big.Int) + cmp := scalar.Cmp(zero) + if cmp < 0 { + g2Point = g2Point.Negate() + scalar2.Mul(scalar, big.NewInt(-1)) + } else if cmp == 0 { + return Altbn128.GetG2Infinity() + } else { + scalar2 = scalar + } + prod := new(bn256.G2).ScalarMult(g2Point.point, scalar2) ret := &altbn128Point2{prod} return ret } -func (g2Point *altbn128Point2) ToAffineCoords() (xx, xy, yx, yy *big.Int) { +// ToAffineCoords returns the affine coordinate representation of the point +// in the form: [x0, x1, y0, y1], where X = x0 * u + x1, and Y = y0 * u + y1 +func (g2Point *altbn128Point2) ToAffineCoords() []*big.Int { Bytestream := g2Point.point.Marshal() - xxBytes, xyBytes := Bytestream[:32], Bytestream[32:64] - yxBytes, yyBytes := Bytestream[64:96], Bytestream[96:128] - xx = new(big.Int).SetBytes(xxBytes) - xy = new(big.Int).SetBytes(xyBytes) - yx = new(big.Int).SetBytes(yxBytes) - yy = new(big.Int).SetBytes(yyBytes) - return + x0Bytes, x1Bytes := Bytestream[:32], Bytestream[32:64] + y0Bytes, y1Bytes := Bytestream[64:96], Bytestream[96:128] + x0 := new(big.Int).SetBytes(x0Bytes) + x1 := new(big.Int).SetBytes(x1Bytes) + y0 := new(big.Int).SetBytes(y0Bytes) + y1 := new(big.Int).SetBytes(y1Bytes) + return []*big.Int{x0, x1, y0, y1} } func (gTPoint altbn128PointT) Add(otherPointT PointT) (PointT, bool) { @@ -225,7 +293,7 @@ func (gTPoint altbn128PointT) Mul(scalar *big.Int) PointT { return ret } -func (curve *altbn128) UnmarshalG1(data []byte) (Point1, bool) { +func (curve *altbn128) UnmarshalG1(data []byte) (Point, bool) { if data == nil || (len(data) != 64 && len(data) != 32) { return nil, false } @@ -241,24 +309,25 @@ func (curve *altbn128) UnmarshalG1(data []byte) (Point1, bool) { } x := new(big.Int).SetBytes(data) if x.Cmp(zero) == 0 { - return Altbn128.MakeG1Point(zero, zero) + return Altbn128.GetG1Infinity(), true } y := Altbn128.g1XToYSquared(x) // Underlying library already checks that y is on the curve, thus isQuadRes isn't checked here y = calcQuadRes(y, altbnG1Q) doubleY := new(big.Int).Mul(y, two) + // TODO switch this to use the parity method cmpRes := doubleY.Cmp(altbnG1Q) if ySgn && cmpRes == -1 { y.Sub(altbnG1Q, y) } else if !ySgn && cmpRes == 1 { y.Sub(altbnG1Q, y) } - return Altbn128.MakeG1Point(x, y) + return Altbn128.MakeG1Point([]*big.Int{x, y}, true) } return nil, false } -func (curve *altbn128) UnmarshalG2(data []byte) (Point2, bool) { +func (curve *altbn128) UnmarshalG2(data []byte) (Point, bool) { if data == nil || (len(data) != 64 && len(data) != 128) { return nil, false } @@ -281,7 +350,7 @@ func (curve *altbn128) UnmarshalG2(data []byte) (Point2, bool) { xi := new(big.Int).SetBytes(xiBytes) xr := new(big.Int).SetBytes(xrBytes) if xi.Cmp(zero) == 0 && xr.Cmp(zero) == 0 { - return Altbn128.MakeG2Point(zero, zero, zero, zero) + return Altbn128.MakeG2Point([]*big.Int{zero, zero, zero, zero}, false) } x := &complexNum{xi, xr} y := Altbn128.g2XToYSquared(x) @@ -301,7 +370,7 @@ func (curve *altbn128) UnmarshalG2(data []byte) (Point2, bool) { } else if !yrSgn && cmpResRe == 1 { y.re.Sub(altbnG1Q, y.re) } - return Altbn128.MakeG2Point(x.im, x.re, y.im, y.re) + return Altbn128.MakeG2Point([]*big.Int{x.im, x.re, y.im, y.re}, false) } return nil, false } @@ -325,11 +394,15 @@ func (curve *altbn128) getG1B() *big.Int { return altbnG1B } -func (curve *altbn128) getG1Q() *big.Int { +func (curve *altbn128) GetG1Q() *big.Int { return altbnG1Q } -func (curve *altbn128) getG1Order() *big.Int { +func (curve *altbn128) getG1QDivTwo() *big.Int { + return altbnG1QDiv2 +} + +func (curve *altbn128) GetG1Order() *big.Int { return altbnG1Order } @@ -347,21 +420,44 @@ func (curve *altbn128) g2XToYSquared(x *complexNum) *complexNum { return result } -func (curve *altbn128) GetG1() Point1 { +func (curve *altbn128) GetG1() Point { return altbnG1 } -func (curve *altbn128) GetG2() Point2 { +func (curve *altbn128) GetG2() Point { return altbnG2 } +func (curve *altbn128) GetG1Infinity() (pt Point) { + pt, _ = curve.MakeG1Point([]*big.Int{zero, zero}, false) + return +} + +func (curve *altbn128) GetG2Infinity() Point { + pt, _ := curve.MakeG2Point([]*big.Int{zero, zero, zero, zero}, false) + return pt +} + +func (curve *altbn128) GetGTIdentity() PointT { + return altbnGTIdentity +} + func (curve *altbn128) GetGT() PointT { return altbnGT } +func (curve *altbn128) getG1Cofactor() *big.Int { + return one +} + +func (curve *altbn128) getFTHashParams() (*big.Int, *big.Int) { + return altbnSqrtn3, altbnZ +} + //curve specific constants var altbnG1B = big.NewInt(3) var altbnG1Q, _ = new(big.Int).SetString("21888242871839275222246405745257275088696311157297823662689037894645226208583", 10) +var altbnG1QDiv2 = new(big.Int).Div(altbnG1Q, two) var altbnG2BRe, _ = new(big.Int).SetString("19485874751759354771024239261021720505790618469301721065564631296452457478373", 10) var altbnG2BIm, _ = new(big.Int).SetString("266929791119991161246907387137283842545076965332900288569378510910307636690", 10) @@ -375,7 +471,11 @@ var altbnSqrtn3, _ = new(big.Int).SetString("44079209702962438428372074856515240 var altbnG1 = &altbn128Point1{new(bn256.G1).ScalarBaseMult(one)} var altbnG2 = &altbn128Point2{new(bn256.G2).ScalarBaseMult(one)} -var altbnGT, _ = altbnG1.Pair(altbnG2) +var altbnGT, _ = Altbn128.Pair(altbnG1, altbnG2) + +// Ensure zero has been created +var z = zero +var altbnGTIdentity, _ = Altbn128.Pair(Altbn128.GetG1(), Altbn128.GetG2Infinity()) var altbnG1Order, _ = new(big.Int).SetString("21888242871839275222246405745257275088548364400416034343698204186575808495617", 10) @@ -383,39 +483,32 @@ var altbnG1Order, _ = new(big.Int).SetString("2188824287183927522224640574525727 // AltbnSha3 Hashes a message to a point on Altbn128 using SHA3 and try and increment // The return value is the x,y affine coordinate pair. -func AltbnSha3(message []byte) (p1, p2 *big.Int) { - p1, p2 = hash64(message, sha3.Sum512, Altbn128) - return +func AltbnSha3(message []byte) []*big.Int { + p1, p2 := tryAndIncrement64(message, sha3.Sum512, Altbn128) + return []*big.Int{p1, p2} } // AltbnKeccak3 Hashes a message to a point on Altbn128 using Keccak3 and try and increment // Keccak3 is only for compatability with Ethereum hashing. // The return value is the x,y affine coordinate pair. -func AltbnKeccak3(message []byte) (p1, p2 *big.Int) { - p1, p2 = hash32(message, EthereumSum256, Altbn128) - return +func AltbnKeccak3(message []byte) []*big.Int { + p1, p2 := tryAndIncrementEvm(message, EthereumSum256, Altbn128) + return []*big.Int{p1, p2} } // AltbnBlake2b Hashes a message to a point on Altbn128 using Blake2b and try and increment // The return value is the x,y affine coordinate pair. -func AltbnBlake2b(message []byte) (p1, p2 *big.Int) { - p1, p2 = hash64(message, blake2b.Sum512, Altbn128) - return -} - -// AltbnKang12 Hashes a message to a point on Altbn128 using Blake2b and try and increment -// The return value is the x,y affine coordinate pair. -func AltbnKang12(message []byte) (p1, p2 *big.Int) { - p1, p2 = hash64(message, kang12_64, Altbn128) - return +func AltbnBlake2b(message []byte) []*big.Int { + p1, p2 := tryAndIncrement64(message, blake2b.Sum512, Altbn128) + return []*big.Int{p1, p2} } // HashToG1 Hashes a message to a point on Altbn128 using Keccak3 and try and increment // This is for compatability with Ethereum hashing. // The return value is the altbn_128 library's internel representation for points. -func (curve *altbn128) HashToG1(message []byte) Point1 { - x, y := AltbnKeccak3(message) - p, _ := curve.MakeG1Point(x, y) +func (curve *altbn128) HashToG1(message []byte) Point { + coords := AltbnKeccak3(message) + p, _ := curve.MakeG1Point(coords, false) return p } diff --git a/curves/altbn128_test.go b/curves/altbn128_test.go new file mode 100644 index 0000000..326387f --- /dev/null +++ b/curves/altbn128_test.go @@ -0,0 +1,39 @@ +// Copyright (C) 2018 Authors +// distributed under Apache 2.0 license + +package curves + +import ( + "math/big" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestEthereumHash(t *testing.T) { + curve := Altbn128 + // Tests Altbn hash to curve against known solidity test case. + a, _ := new(big.Int).SetString("9121282642809701931333593728297233225556711250127745709186816755779879923737", 10) + aBytes := a.Bytes() + coords := AltbnKeccak3(aBytes) + expX, _ := new(big.Int).SetString("11423386531623885114587219621463106117140760157404497425836076043015227528156", 10) + expY, _ := new(big.Int).SetString("20262289731964024720969923714809935701428881933342918937283877214228227624643", 10) + assert.True(t, coords[0].Cmp(expX) == 0 && coords[1].Cmp(expY) == 0, "Hash does not match known Ethereum Output") + pt := curve.HashToG1(aBytes) + coords2 := pt.ToAffineCoords() + assert.True(t, coords[0].Cmp(coords2[0]) == 0 && coords[1].Cmp(coords2[1]) == 0, "Conversion of point to coordinates is not working") + + coords = altbnG2.ToAffineCoords() + knownxi, _ := new(big.Int).SetString("11559732032986387107991004021392285783925812861821192530917403151452391805634", 10) + knownxr, _ := new(big.Int).SetString("10857046999023057135944570762232829481370756359578518086990519993285655852781", 10) + knownyi, _ := new(big.Int).SetString("4082367875863433681332203403145435568316851327593401208105741076214120093531", 10) + knownyr, _ := new(big.Int).SetString("8495653923123431417604973247489272438418190587263600148770280649306958101930", 10) + + assert.Zero(t, coords[0].Cmp(knownxi), "xi doesn't match") + assert.Zero(t, coords[1].Cmp(knownxr), "xr doesn't match") + assert.Zero(t, coords[2].Cmp(knownyi), "yi doesn't match") + assert.Zero(t, coords[3].Cmp(knownyr), "yr doesn't match") + + altG2, _ := curve.MakeG2Point(coords, false) + assert.True(t, altG2.Equals(curve.GetG2()), "MakeG2Point Failed") +} diff --git a/curves/bls12_381.go b/curves/bls12_381.go new file mode 100644 index 0000000..4571ac6 --- /dev/null +++ b/curves/bls12_381.go @@ -0,0 +1,400 @@ +// Copyright (C) 2018 Authors +// distributed under Apache 2.0 license + +package curves + +import ( + "encoding" + "hash" + "math/big" + + "github.com/dis2/bls12" + "golang.org/x/crypto/blake2b" +) + +type bls12Curve struct { +} + +type bls12Point1 struct { + point *bls12.G1 +} + +type bls12Point2 struct { + point *bls12.G2 +} + +type bls12PointT struct { + point *bls12.GT +} + +// Bls12 is the instance for the bls12 curve, with all of its functions. +var Bls12 = &bls12Curve{} + +func (pt *bls12Point1) Add(otherPt Point) (Point, bool) { + g1Copy, _ := pt.Copy().(*bls12Point1) + if other, ok := (otherPt).(*bls12Point1); ok { + sum := g1Copy.point.Add(other.point).(*bls12.G1) + ret := &bls12Point1{sum} + return ret, true + } + return nil, false +} + +func (pt *bls12Point1) Copy() Point { + result := bls12Point1{pt.point.Copy().(*bls12.G1)} + return &result +} + +func (pt *bls12Point1) Equals(otherPt Point) bool { + if other, ok := (otherPt).(*bls12Point1); ok { + return pt.point.Equal(other.point) + } + return false +} + +// TODO Make this match ebfull/pairing marshalling. +func (pt *bls12Point1) Marshal() []byte { + return pt.point.Marshal() +} + +// TODO Make this match ebfull/pairing marshalling. +func (pt *bls12Point1) MarshalUncompressed() []byte { + return pt.point.MarshalUncompressed() +} + +func (pt *bls12Point1) Mul(scalar *big.Int) Point { + prod, _ := pt.Copy().(*bls12Point1) + cmp := scalar.Cmp(zero) + if cmp < 0 { + prod = prod.Negate() + scalar.Mul(scalar, big.NewInt(-1)) + } else if cmp == 0 { + return Bls12.GetG1Infinity() + } + prod.point.ScalarMult(new(bls12.Scalar).FromInt(scalar)) + return prod +} + +func (pt *bls12Point1) Negate() *bls12Point1 { + coords := pt.ToAffineCoords() + coords[1].Sub(bls12Q, coords[1]) + newPt, _ := Bls12.MakeG1Point(coords, false) + return newPt.(*bls12Point1) +} + +// ToAffineCoords returns the affine coordinate representation of the point +// in the form: [X, Y] +func (pt *bls12Point1) ToAffineCoords() []*big.Int { + // Upstream library uses projective space + pt.point.Normalize() + blsx, blsy, _ := pt.point.GetXYZ() + return []*big.Int{blsx.ToInt()[0], blsy.ToInt()[0]} +} + +func (pt *bls12Point2) Add(otherPt Point) (Point, bool) { + copy, _ := pt.Copy().(*bls12Point2) + if other, ok := (otherPt).(*bls12Point2); ok { + sum := copy.point.Add(other.point).(*bls12.G2) + ret := &bls12Point2{sum} + return ret, true + } + return nil, false +} + +func (pt *bls12Point2) Copy() Point { + result := bls12Point2{pt.point.Copy().(*bls12.G2)} + return &result +} + +func (pt *bls12Point2) Equals(otherPt Point) bool { + if other, ok := (otherPt).(*bls12Point2); ok { + return pt.point.Equal(other.point) + } + return false +} + +// TODO Make this match ebfull/pairing marshalling. +func (pt *bls12Point2) Marshal() []byte { + return pt.point.Marshal() +} + +// TODO Make this match ebfull/pairing marshalling. +func (pt *bls12Point2) MarshalUncompressed() []byte { + return pt.point.MarshalUncompressed() +} + +func (pt *bls12Point2) Mul(scalar *big.Int) Point { + prod, _ := pt.Copy().(*bls12Point2) + cmp := scalar.Cmp(zero) + if cmp < 0 { + prod = prod.Negate() + scalar.Mul(scalar, big.NewInt(-1)) + } else if cmp == 0 { + return Bls12.GetG2Infinity() + } + prod.point.ScalarMult(new(bls12.Scalar).FromInt(scalar)) + return prod +} + +func (pt *bls12Point2) Negate() *bls12Point2 { + coords := pt.ToAffineCoords() + coords[2].Sub(bls12Q, coords[2]) + coords[3].Sub(bls12Q, coords[3]) + newPt, _ := Bls12.MakeG2Point(coords, false) + return newPt.(*bls12Point2) +} + +// ToAffineCoords returns the affine coordinate representation of the point +// in the form: [x0, x1, y0, y1], where X = x0 * u + x1, and Y = y0 * u + y1 +func (pt *bls12Point2) ToAffineCoords() []*big.Int { + Bytestream := pt.point.MarshalUncompressed() + x0Bytes, x1Bytes := Bytestream[:48], Bytestream[48:96] + y0Bytes, y1Bytes := Bytestream[96:144], Bytestream[144:192] + x0 := new(big.Int).SetBytes(x0Bytes) + x1 := new(big.Int).SetBytes(x1Bytes) + y0 := new(big.Int).SetBytes(y0Bytes) + y1 := new(big.Int).SetBytes(y1Bytes) + return []*big.Int{x0, x1, y0, y1} +} + +func (pt bls12PointT) Add(otherPt PointT) (PointT, bool) { + copy, _ := pt.Copy().(bls12PointT) + if other, ok := (otherPt).(bls12PointT); ok { + sum := copy.point.Add(other.point) + ret := bls12PointT{sum} + return ret, true + } + return nil, false +} + +func (pt bls12PointT) Copy() PointT { + result := bls12PointT{pt.point.Copy()} + return result +} + +func (pt bls12PointT) Equals(otherPt PointT) bool { + if other, ok := (otherPt).(bls12PointT); ok { + return pt.point.Equal(other.point) + } + return false +} + +func (pt bls12PointT) Marshal() []byte { + return pt.point.Marshal() +} + +func (pt bls12PointT) Mul(scalar *big.Int) PointT { + prod, _ := pt.Copy().(bls12PointT) + prod.point.ScalarMult(new(bls12.Scalar).FromInt(scalar)) + return prod +} + +func (curve *bls12Curve) Name() string { + return "bls12" +} + +// MakeG2Point expects coords to be of the form: [X, Y] +func (curve *bls12Curve) MakeG1Point(coords []*big.Int, check bool) (Point, bool) { + if len(coords) != 2 { + return nil, false + } + pt := new(bls12.G1) + pt.SetXY(bls12.FqFromInt(coords[0]), bls12.FqFromInt(coords[1])) + if check && !pt.Check() { + return nil, false + } + return &bls12Point1{pt}, true +} + +// MakeG2Point expects coords to be of the form: [x0, x1, y0, y1], +// where X = x0 * u + x1, and Y = y0 * u + y1 +func (curve *bls12Curve) MakeG2Point(coords []*big.Int, check bool) (Point, bool) { + if len(coords) != 4 { + return nil, false + } + pt := new(bls12.G2) + x := new(bls12.Fq2) + // Underlying library expects coordinates in the form [x1, x0] amd [y1, y0] + x.FromInt([]*big.Int{coords[1], coords[0]}) + y := new(bls12.Fq2) + y.FromInt([]*big.Int{coords[3], coords[2]}) + pt.SetXY(x, y) + if check && !pt.Check() { + return nil, false + } + return &bls12Point2{pt}, true +} + +func (curve *bls12Curve) Pair(pt1 Point, pt2 Point) (PointT, bool) { + p1, ok1 := pt1.(*bls12Point1) + if p2, ok2 := pt2.(*bls12Point2); ok1 && ok2 { + p3 := new(bls12.GT).Pair(p1.point, p2.point) + ret := bls12PointT{p3} + return ret, true + } + return nil, false +} + +func (curve *bls12Curve) PairingProduct(pts1 []Point, pts2 []Point) (PointT, bool) { + return concurrentPairingProduct(curve, pts1, pts2) +} + +func (curve *bls12Curve) UnmarshalG1(data []byte) (Point, bool) { + if len(data) != 48 && len(data) != 96 { + return nil, false + } + result := new(bls12.G1) + success := result.Unmarshal(data) + if success == nil || !result.Check() { + return nil, false + } + return &bls12Point1{result}, true +} + +func (curve *bls12Curve) UnmarshalG2(data []byte) (Point, bool) { + if len(data) != 96 && len(data) != 192 { + return nil, false + } + result := new(bls12.G2) + success := result.Unmarshal(data) + if success == nil || !result.Check() { + return nil, false + } + return &bls12Point2{result}, true +} + +func (curve *bls12Curve) UnmarshalGT(data []byte) (PointT, bool) { + result := new(bls12.GT) + success := result.Unmarshal(data) + if success == nil { + return nil, false + } + return &bls12PointT{result}, true +} + +func (curve *bls12Curve) GetG1() Point { + return &bls12Point1{bls12.G1One()} +} + +func (curve *bls12Curve) GetG2() Point { + return &bls12Point2{bls12.G2One()} +} + +func (curve *bls12Curve) GetGT() PointT { + return bls12GT +} + +func (curve *bls12Curve) GetG1Infinity() Point { + return &bls12Point1{bls12.G1Zero()} +} + +func (curve *bls12Curve) GetG2Infinity() Point { + return &bls12Point2{bls12.G2Zero()} +} + +func (curve *bls12Curve) GetGTIdentity() PointT { + return bls12GTIdentity +} + +func (curve *bls12Curve) getG1A() *big.Int { + return zero +} + +func (curve *bls12Curve) getG1B() *big.Int { + return bls12B +} + +func (curve *bls12Curve) GetG1Q() *big.Int { + return bls12Q +} + +func (curve *bls12Curve) getG1Cofactor() *big.Int { + return bls12Cofactor +} + +func (curve *bls12Curve) g1XToYSquared(x *big.Int) *big.Int { + y := bls12.FqFromInt(x).Y2FromX(nil) + return y.ToInt()[0] +} + +func (curve *bls12Curve) GetG1Order() *big.Int { + return bls12Order +} + +func (curve *bls12Curve) getFTHashParams() (*big.Int, *big.Int) { + return bls12SwencSqrtNegThree, bls12SwencSqrtNegThreeMinusOneOverTwo +} + +var bls12Q, _ = new(big.Int).SetString("0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab", 0) +var bls12X, _ = new(big.Int).SetString("-0xd201000000010000", 0) +var bls12A, _ = new(big.Int).SetString("0", 10) +var bls12B, _ = new(big.Int).SetString("4", 10) + +//precomputed bls12SwencSqrtNegThreeMinusOneOverTwo = (-1 + sqrt(-3))/2 in Fq +var bls12SwencSqrtNegThreeMinusOneOverTwo, _ = new(big.Int).SetString("793479390729215512621379701633421447060886740281060493010456487427281649075476305620758731620350", 10) + +//precomputed bls12SwencSqrtNegThree in Fq +var bls12SwencSqrtNegThree, _ = new(big.Int).SetString("1586958781458431025242759403266842894121773480562120986020912974854563298150952611241517463240701", 10) +var bls12Cofactor, _ = new(big.Int).SetString("76329603384216526031706109802092473003", 10) +var bls12Order, _ = new(big.Int).SetString("52435875175126190479447740508185965837690552500527637822603658699938581184513", 10) +var bls12GT, _ = Bls12.Pair(Bls12.GetG1(), Bls12.GetG2()) +var bls12GTIdentity, _ = Bls12.Pair(Bls12.GetG1Infinity(), Bls12.GetG2()) +var bls12G1Tag1 = []byte("G1_0") +var bls12G1Tag2 = []byte("G1_1") + +var bls12FTRoot1, _ = new(big.Int).SetString("248294325734266649657405162895821171812231848760181225578082735178502750823719347628762635478508544819911854747095", 10) +var bls12FTRoot2, _ = new(big.Int).SetString("3754115229487400743760384662840082984744650971178826659753975400945528899667118516813924993650507119217982417812692", 10) + +// Fouque Tibouchi hashing as specified in https://github.com/ebfull/pairing/pull/30 +func (curve *bls12Curve) HashToG1(message []byte) Point { + return hashToG1BlindingAbstracted(message, false) +} + +// Fouque Tibouchi hashing as specified in https://github.com/ebfull/pairing/pull/30 +// This also adds time blinding +func (curve *bls12Curve) HashToG1Blind(message []byte) Point { + return hashToG1BlindingAbstracted(message, true) +} + +// This hashes a given message to G1, and the second parameter specifies whether +// or not to blind the computation, to prevent timing information from being leaked. +func hashToG1BlindingAbstracted(message []byte, blind bool) Point { + b2, _ := blake2b.New512(nil) + b2Copy, _ := blake2b.New512(nil) + b2.Write(message) + b2State, _ := b2.(encoding.BinaryMarshaler).MarshalBinary() + b2Copy.(encoding.BinaryUnmarshaler).UnmarshalBinary(b2State) + t1Bytes := bls12Blake2b(b2, bls12G1Tag1) + pt1 := bls12FouqueTibouchi(t1Bytes, blind) + t2Bytes := bls12Blake2b(b2Copy, bls12G1Tag2) + pt2 := bls12FouqueTibouchi(t2Bytes, blind) + + pt1, _ = pt1.Add(pt2) + // Underlying library does cofactor multiplication implicitly + // pt1 = pt1.Mul(bls12Cofactor) + return pt1 +} + +func bls12FouqueTibouchi(tBytes []byte, blind bool) Point { + t := new(big.Int).SetBytes(tBytes) + t.Mod(t, bls12Q) + // Explicitly handle degenerate cases for t + if t.Cmp(zero) == 0 { // Hash(0) = infty + pt := Bls12.GetG1Infinity() + return pt + } else if t.Cmp(bls12FTRoot1) == 0 { // encode(sqrt(-5)) = -g1 + return Bls12.GetG1() + } else if t.Cmp(bls12FTRoot2) == 0 { // encode(-sqrt(-5)) = g1 + return Bls12.GetG1().(*bls12Point1).Negate() + } + + pt, _ := fouqueTibouchiG1(Bls12, t, blind) + return pt +} + +// bls12Blake2b returns Blake2b(message || Tag) +// The tags with what is being used in https://github.com/ebfull/pairing/pull/30 +func bls12Blake2b(blake2b hash.Hash, tag []byte) []byte { + blake2b.Write(tag) + return blake2b.Sum([]byte{}) +} diff --git a/curves/bls12_test.go b/curves/bls12_test.go new file mode 100644 index 0000000..65e2e3d --- /dev/null +++ b/curves/bls12_test.go @@ -0,0 +1,67 @@ +// Copyright (C) 2018 Authors +// distributed under Apache 2.0 license + +package curves + +import ( + "crypto/rand" + "math/big" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestG1BlindingMatches(t *testing.T) { + N := 100 + msgSize := 64 + for i := 0; i < N; i++ { + msg := make([]byte, msgSize) + _, _ = rand.Read(msg) + + p1 := Bls12.HashToG1(msg) + p2 := Bls12.HashToG1Blind(msg) + assert.True(t, p1.Equals(p2), "inconsistent results with BLS normal and blind hashing to G1") + } +} + +func TestG1SwEncodeDegenerate(t *testing.T) { + // Check that bls12FouqueTibouchi([0]) = point at infinity + infty := Bls12.GetG1Infinity() + var zeroArr []byte + chkInfty := bls12FouqueTibouchi(zeroArr, false) + assert.True(t, chkInfty.Equals(infty), "Degenerate case for t=0 did not return the point at infinity.") + + // Check that bls12FouqueTibouchi([-sqrt(5)]) = +-g1 + // The plus or minus for g1 must be set such that it matches the input. + g1 := Bls12.GetG1() + negG1 := g1.(*bls12Point1).Negate() + sqrtNeg5 := new(big.Int).Sub(bls12Q, big.NewInt(5)) + sqrtNeg5 = calcQuadRes(sqrtNeg5, bls12Q) + tBytes := sqrtNeg5.Bytes() + chkNegG1 := bls12FouqueTibouchi(tBytes, false) + coords := chkNegG1.ToAffineCoords() + assert.True(t, parity(coords[1], bls12Q) == parity(sqrtNeg5, bls12Q), "Parity for t=sqrt(-5) doesn't match return value") + assert.True(t, chkNegG1.Equals(negG1), "Degenerate case for t=sqrt(-5) did not return g1.") + // Invert the parity of sqrtNeg5, and check the other side + sqrtNeg5.Sub(bls12Q, sqrtNeg5) + tBytes = sqrtNeg5.Bytes() + chkG1 := bls12FouqueTibouchi(tBytes, false) + coords = chkG1.ToAffineCoords() + assert.True(t, parity(coords[1], bls12Q) == parity(sqrtNeg5, bls12Q), "Parity for t=-sqrt(-5) doesn't match return value") + assert.True(t, chkG1.Equals(g1), "Degenerate case for t=-sqrt(-5) did not return g1.") + chkInfty, _ = g1.Add(negG1) + assert.True(t, chkInfty.Equals(infty), "Point at infinity isn't being returned under addition") +} + +// taken from https://github.com/ebfull/pairing/pull/30/commits/092a0f2846ca9e1a18eef849355e847f61eaf2bc +func TestKnownBls12G1Hashes(t *testing.T) { + msg := []byte{} + p := Bls12.HashToG1(msg) + x, _ := new(big.Int).SetString("315124130825307604287835216317628428134609737854237653839182597515996444073032649481416725367158979153513345579672", 10) + y, _ := new(big.Int).SetString("3093537746211397858160667262592024570071165158580434464756577567510401504168962073691924150397172185836012224315174", 10) + q, ok := Bls12.MakeG1Point([]*big.Int{x, y}, true) + if !ok { + t.Error("known point not registering as on the curve") + } + assert.True(t, p.Equals(q)) +} diff --git a/complexNum.go b/curves/complexNum.go similarity index 99% rename from complexNum.go rename to curves/complexNum.go index 84fa9cb..37b19b8 100644 --- a/complexNum.go +++ b/curves/complexNum.go @@ -1,7 +1,7 @@ // Copyright (C) 2018 Authors // distributed under Apache 2.0 license -package bgls +package curves import ( "math/big" diff --git a/curves/curve.go b/curves/curve.go new file mode 100644 index 0000000..14d6c41 --- /dev/null +++ b/curves/curve.go @@ -0,0 +1,223 @@ +// Copyright (C) 2018 Authors +// distributed under Apache 2.0 license + +package curves + +import ( + "math/big" +) + +// CurveSystem is a set of parameters and functions for a pairing based cryptosystem +// It has everything necessary to support all bgls functionality which we use. +type CurveSystem interface { + Name() string + + MakeG1Point([]*big.Int, bool) (Point, bool) + MakeG2Point([]*big.Int, bool) (Point, bool) + + // GTToAffineCoords(PointT) (*big.Int, *big.Int) + + UnmarshalG1([]byte) (Point, bool) + UnmarshalG2([]byte) (Point, bool) + UnmarshalGT([]byte) (PointT, bool) + + GetG1() Point + GetG2() Point + GetGT() PointT + + GetG1Infinity() Point + GetG2Infinity() Point + GetGTIdentity() PointT + + HashToG1(message []byte) Point + + GetG1Q() *big.Int + GetG1Order() *big.Int + // getGTQ() *big.Int + + getG1Cofactor() *big.Int + + getG1A() *big.Int + getG1B() *big.Int + // Fouque-Tibouchi hash parameters, sqrt(-3), (-1 + sqrt(-3))/2 computed in F_q + getFTHashParams() (*big.Int, *big.Int) + g1XToYSquared(*big.Int) *big.Int + + Pair(Point, Point) (PointT, bool) + // Product of Pairings + PairingProduct([]Point, []Point) (PointT, bool) +} + +// Point is a way to represent a point on G1 or G2, in the first two elliptic curves. +type Point interface { + Add(Point) (Point, bool) + Copy() Point + Equals(Point) bool + Marshal() []byte + MarshalUncompressed() []byte + Mul(*big.Int) Point + ToAffineCoords() []*big.Int +} + +// PointT is a way to represent a point on GT, in the target group +type PointT interface { + Add(PointT) (PointT, bool) + Copy() PointT + Equals(PointT) bool + Marshal() []byte + Mul(*big.Int) PointT + // ToAffineCoords() (*big.Int, *big.Int) +} + +// AggregatePoints takes the sum of points. +func AggregatePoints(points []Point) Point { + if len(points) == 2 { // No parallelization needed + aggPoint, _ := points[0].Add(points[1]) + return aggPoint + } + // Aggregate all the points together using concurrency + c := make(chan Point) + + // Initialize aggPoint to an array with elements being the sum of two + // adjacent Points. + counter := 0 + for i := 0; i < len(points); i += 2 { + go concurrentAggregatePoints(i, points, c) + counter++ + } + aggPoint := make([]Point, counter) + for i := 0; i < counter; i++ { + aggPoint[i] = <-c + } + + // Keep on aggregating every pair of points until only one aggregate point remains + for { + counter = 0 + if len(aggPoint) == 1 { + break + } + for i := 0; i < len(aggPoint); i += 2 { + go concurrentAggregatePoints(i, aggPoint, c) + counter++ + } + nxtAggPoint := make([]Point, counter) + for i := 0; i < counter; i++ { + nxtAggPoint[i] = <-c + } + aggPoint = nxtAggPoint + } + return aggPoint[0] +} + +// concurrentAggregatePoints handles the channel for concurrent Aggregation of points. +// It only adds the element at points[start] and points[start + 1], and sends it through the channel +func concurrentAggregatePoints(start int, points []Point, c chan Point) { + if start+1 >= len(points) { + c <- points[start] + return + } + summed, _ := points[start].Add(points[start+1]) + c <- summed +} + +// concurrentPairingProduct computes a set of pairings in parallel, +// and then takes their product again using concurrency. +func concurrentPairingProduct(curve CurveSystem, points1 []Point, points2 []Point) (PointT, bool) { + if len(points1) != len(points2) { + return nil, false + } + // Compute all the pairings in parallel + c := make(chan PointT) + pairedPoints := make([]PointT, len(points1)) + for i := 0; i < len(pairedPoints); i++ { + go concurrentPair(curve, points1[i], points2[i], c) + } + for i := 0; i < len(pairedPoints); i++ { + pairedPoints[i] = <-c + if pairedPoints[i] == nil { + return nil, false + } + } + counter := 0 + // Set aggPairedPoints to an array with elements being the sum of two + // adjacent Points. + for i := 0; i < len(pairedPoints); i += 2 { + go concurrentAggregatePointTs(i, pairedPoints, c) + counter++ + } + aggPairedPoints := make([]PointT, counter) + for i := 0; i < counter; i++ { + aggPairedPoints[i] = <-c + } + + // Keep on aggregating every pair of points until only one aggregate point remains + for { + counter = 0 + if len(aggPairedPoints) == 1 { + break + } + for i := 0; i < len(aggPairedPoints); i += 2 { + go concurrentAggregatePointTs(i, aggPairedPoints, c) + counter++ + } + nxtPairedPoints := make([]PointT, counter) + for i := 0; i < counter; i++ { + nxtPairedPoints[i] = <-c + } + aggPairedPoints = nxtPairedPoints + } + return aggPairedPoints[0], true +} + +// concurrentAggregatePoints handles the channel for concurrent Aggregation of points. +// It only adds the element at points[start] and points[start + 1], and sends it through the channel +func concurrentAggregatePointTs(start int, points []PointT, c chan PointT) { + if start+1 >= len(points) { + c <- points[start] + return + } + summed, _ := points[start].Add(points[start+1]) + c <- summed +} + +type indexedPoint struct { + index int + pt Point +} + +// ScalePoints takes a set of points, and a set of multiples, and returns a +// new set of points multiplied by the corresponding factor. +func ScalePoints(pts []Point, factors []*big.Int) (newKeys []Point) { + if factors == nil { + return pts + } else if len(pts) != len(factors) { + return nil + } + newKeys = make([]Point, len(pts)) + c := make(chan *indexedPoint) + for i := 0; i < len(pts); i++ { + go concurrentScale(pts[i], factors[i], i, c) + } + for i := 0; i < len(pts); i++ { + pt := <-c + newKeys[pt.index] = pt.pt + } + return newKeys +} + +func concurrentScale(key Point, factor *big.Int, index int, c chan *indexedPoint) { + if factor == nil { + c <- &indexedPoint{index, key.Copy()} + } else { + c <- &indexedPoint{index, key.Mul(factor)} + } +} + +// concurrentPair pairs pt with key, and sends the result down the channel. +func concurrentPair(curve CurveSystem, pt1 Point, pt2 Point, c chan PointT) { + if targetPoint, ok := curve.Pair(pt1, pt2); ok { + c <- targetPoint + return + } + c <- nil +} diff --git a/curves/curve_test.go b/curves/curve_test.go new file mode 100644 index 0000000..1bc3044 --- /dev/null +++ b/curves/curve_test.go @@ -0,0 +1,270 @@ +// Copyright (C) 2018 Authors +// distributed under Apache 2.0 license + +package curves + +import ( + "bufio" + "crypto/rand" + "io/ioutil" + "math/big" + "os" + "strconv" + "strings" + "testing" + + b64 "encoding/base64" + + "github.com/stretchr/testify/assert" +) + +var curves = []CurveSystem{Altbn128, Bls12} + +func TestMarshal(t *testing.T) { + for _, curve := range curves { + numTests := 16 + requiredScalars := []*big.Int{big.NewInt(1), Altbn128.GetG1Order()} + for i := 0; i < numTests; i++ { + scalar, _ := rand.Int(rand.Reader, curve.GetG1Order()) + if i < len(requiredScalars) { + scalar = requiredScalars[i] + } + + mulg1 := curve.GetG1().Mul(scalar) + marshalled := mulg1.Marshal() + if recoveredG1, ok := curve.UnmarshalG1(marshalled); ok { + assert.True(t, recoveredG1.Equals(mulg1), + "Unmarshalling G1 is not consistent with Marshal G1") + } else { + t.Error("Unmarshalling G1 failed") + } + marshalled = mulg1.MarshalUncompressed() + if recoveredG1, ok := curve.UnmarshalG1(marshalled); ok { + assert.True(t, recoveredG1.Equals(mulg1), + "Unmarshalling G1 is not consistent with MarshalUncompressed G1") + } else { + t.Error("Unmarshalling G1 failed") + } + marshalled = marshalled[1:] + if _, ok := curve.UnmarshalG1(marshalled); ok { + t.Error("Unmarshalling G1 is succeeding when the byte array is of the wrong length") + } + + mulg2 := curve.GetG2().Mul(scalar) + marshalled = mulg2.Marshal() + if recoveredG2, ok := curve.UnmarshalG2(marshalled); ok { + assert.True(t, recoveredG2.Equals(mulg2), + "Unmarshalling G2 is not consistent with Marshal G2") + } else { + t.Error("Unmarshalling G2 failed on scalar " + scalar.String()) + } + marshalled = mulg2.Marshal() + if recoveredG2, ok := curve.UnmarshalG2(marshalled); ok { + assert.True(t, recoveredG2.Equals(mulg2), + "Unmarshalling G2 is not consistent with MarshalUncompressed G2") + } else { + t.Error("Unmarshalling G2 failed on scalar " + scalar.String()) + } + marshalled = marshalled[1:] + if _, ok := curve.UnmarshalG2(marshalled); ok { + t.Error("Unmarshalling G2 is succeeding when the byte array is of the wrong length") + } + + mulgT, _ := curve.Pair(mulg1, curve.GetG2()) + marshalled = mulgT.Marshal() + if recoveredGT, ok := curve.UnmarshalGT(marshalled); ok { + assert.True(t, recoveredGT.Equals(mulgT), + "Unmarshalling GT is not consistent with Marshal GT") + } else { + t.Error("Unmarshalling GT failed") + } + marshalled = marshalled[1:] + if _, ok := curve.UnmarshalGT(marshalled); ok { + t.Error("Unmarshalling GT is succeeding when the byte array is of the wrong length") + } + } + } +} + +func TestMakePoint(t *testing.T) { + for _, curve := range curves { + numTests := 10 + requiredScalars := []*big.Int{big.NewInt(1), Altbn128.GetG1Order()} + for i := 0; i < numTests; i++ { + scalar, _ := rand.Int(rand.Reader, curve.GetG1Order()) + if i < len(requiredScalars) { + scalar = requiredScalars[i] + } + + mulg1 := curve.GetG1().Mul(scalar) + coords := mulg1.ToAffineCoords() + if recoveredG1, ok := curve.MakeG1Point(coords, true); ok { + assert.True(t, recoveredG1.Equals(mulg1), + "Making G1 points is not consistent with G1.ToAffineCoords()") + } else { + t.Error("Making G1 point failed on scalar " + scalar.String()) + } + + mulg2 := curve.GetG2().Mul(scalar) + coords = mulg2.ToAffineCoords() + if recoveredG2, ok := curve.MakeG2Point(coords, true); ok { + assert.True(t, recoveredG2.Equals(mulg2), + "Making G2 points is not consistent with G2.ToAffineCoords()") + } else { + t.Error("Making G2 point failed on scalar " + scalar.String()) + } + } + } +} + +func TestMul(t *testing.T) { + // TODO: Create known test cases specific to each curve from another library. + for _, curve := range curves { + numTests := 32 + requiredScalars := []*big.Int{zero, one} + for i := 0; i < numTests; i++ { + scalar, _ := rand.Int(rand.Reader, curve.GetG1Order()) + if i < len(requiredScalars) { + scalar = requiredScalars[i] + } + scalarNeg := new(big.Int).Sub(zero, scalar) + pt1 := curve.GetG1().Mul(scalar) + pt2 := curve.GetG1().Mul(scalarNeg) + inf, _ := pt1.Add(pt2) + assert.True(t, inf.Equals(curve.GetG1Infinity())) + pt1 = curve.GetG2().Mul(scalar) + pt2 = curve.GetG2().Mul(scalarNeg) + inf, _ = pt1.Add(pt2) + assert.True(t, inf.Equals(curve.GetG2Infinity())) + } + } +} + +func TestPairingProd(t *testing.T) { + // TODO: Make upstream libraries include proper product of pairing functionality + for _, curve := range curves { + numTests := 5 + for i := 0; i < numTests; i++ { + numPoints := 5 + points1 := make([]Point, numPoints) + points2 := make([]Point, numPoints) + prod := curve.GetGTIdentity() + for j := 0; j < numPoints; j++ { + g1Scalar, _ := rand.Int(rand.Reader, curve.GetG1Order()) + g2Scalar, _ := rand.Int(rand.Reader, curve.GetG1Order()) + + points1[j] = curve.GetG1().Mul(g1Scalar) + points2[j] = curve.GetG2().Mul(g2Scalar) + pair, _ := curve.Pair(points1[j], points2[j]) + prod, _ = prod.Add(pair) + } + pairCheck, _ := curve.PairingProduct(points1, points2) + assert.True(t, pairCheck.Equals(prod)) + } + } +} + +func TestAggregation(t *testing.T) { + for _, curve := range curves { + for _, N := range []int{2, 4, 6, 8} { + g1 := make([]Point, N) + g2 := make([]Point, N) + sum := new(big.Int).SetInt64(0) + for i := 0; i < N; i++ { + x, _ := rand.Int(rand.Reader, curve.GetG1Order()) + sum.Add(sum, x) + sum.Mod(sum, curve.GetG1Order()) + g1[i] = curve.GetG1().Mul(x) + g2[i] = curve.GetG2().Mul(x) + } + aggG1 := AggregatePoints(g1) + aggG2 := AggregatePoints(g2) + assert.True(t, aggG1.Equals(curve.GetG1().Mul(sum)), curve.Name()+" "+strconv.Itoa(N)) + assert.True(t, aggG2.Equals(curve.GetG2().Mul(sum)), curve.Name()+" "+strconv.Itoa(N)) + } + } +} + +func TestScaling(t *testing.T) { + N := 5 + for _, curve := range curves { + for _, g := range []Point{curve.GetG1(), curve.GetG2()} { + pts1 := make([]Point, N) + pts2 := make([]Point, N) + factors := make([]*big.Int, N) + for i := 0; i < N; i++ { + x, _ := rand.Int(rand.Reader, curve.GetG1Order()) + pts1[i] = g.Mul(x) + f, _ := rand.Int(rand.Reader, curve.GetG1Order()) + pts2[i] = pts1[i].Copy().Mul(f) + factors[i] = f + } + pts1 = ScalePoints(pts1, factors) + for i := 0; i < N; i++ { + assert.True(t, pts1[i].Equals(pts2[i])) + } + } + } +} + +func TestG1HashVectors(t *testing.T) { + for _, curve := range curves { + // Says whether or not to generate test vectors + generate := false + if generate { + generateG1HashVectors(curve) + } + file, err := os.Open("testcases/" + curve.Name() + "G1Hash.dat") + if err != nil { + t.Error(err) + } + defer file.Close() + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + s := strings.Split(line, ",") + msg, err1 := b64.StdEncoding.DecodeString(s[0]) + marshalledPt, err2 := b64.StdEncoding.DecodeString(s[1]) + if err1 != nil || err2 != nil { + t.Error("Incorrectly formatted test vector file " + err.Error()) + } + chkPt := curve.HashToG1(msg) + pt, ok := curve.UnmarshalG1(marshalledPt) + if !ok { + t.Error("Error in unmarshalling point") + } + assert.True(t, pt.Equals(chkPt)) + } + + if err := scanner.Err(); err != nil { + t.Error(err) + } + } +} + +func generateG1HashVectors(curve CurveSystem) { + NumberOfTests := 10 + msgSize := 64 + output := make([]byte, 0, NumberOfTests*(msgSize+96)) + for i := 0; i < NumberOfTests; i++ { + msg := make([]byte, msgSize) + _, _ = rand.Read(msg) + pt := curve.HashToG1(msg) + // Make the created format for these: + // base64(msg),base64(Uncompressed Marshal of HashToG1(msg)) + // Note that there is no space between the two base64'd messages. + mutativeAppend(&output, []byte(b64.StdEncoding.EncodeToString(msg))) + mutativeAppend(&output, []byte(",")) + mutativeAppend(&output, []byte(b64.StdEncoding.EncodeToString(pt.MarshalUncompressed()))) + mutativeAppend(&output, []byte("\n")) + } + // Delete old file it exists + os.Remove("testcases/" + curve.Name() + "G1Hash.dat") + ioutil.WriteFile("testcases/"+curve.Name()+"G1Hash.dat", output, 0644) +} + +// Mutatively appends msg to s. This is used to avoid having to reallocate more memory for s. +func mutativeAppend(s *[]byte, msg []byte) { + *s = append(*s, msg...) +} diff --git a/curves/hash.go b/curves/hash.go new file mode 100644 index 0000000..fbd0e68 --- /dev/null +++ b/curves/hash.go @@ -0,0 +1,265 @@ +// Copyright (C) 2018 Authors +// distributed under Apache 2.0 license + +package curves + +import ( + "crypto/rand" + "math/big" +) + +var zero = big.NewInt(0) +var one = big.NewInt(1) +var two = big.NewInt(2) +var three = big.NewInt(3) +var four = big.NewInt(4) + +// 64 byte hash +func tryAndIncrement64(message []byte, hashfunc func(message []byte) [64]byte, curve CurveSystem) (px, py *big.Int) { + counter := []byte{byte(0)} + px = new(big.Int) + py = new(big.Int) + q := curve.GetG1Q() + for { + h := hashfunc(append(counter, message...)) + counter[0]++ + px.SetBytes(h[:48]) + px.Mod(px, q) + ySqr := curve.g1XToYSquared(px) + root := calcQuadRes(ySqr, q) + rootSqr := new(big.Int).Exp(root, two, q) + if rootSqr.Cmp(ySqr) == 0 { + otherRoot := py.Sub(q, py) + // Set root to the canonical square root. + root, otherRoot = sortBigInts(root, otherRoot) + // Use the canonical root for py, unless the cofactor is one, in which case + // use an extra bit to determine parity. + py = root + if curve.getG1Cofactor().Cmp(one) == 0 { + signY := int(h[48]) % 2 + if signY == 1 { + py = otherRoot + break + } + } + break + } + } + return +} + +// Try and Increment hashing that is meant to comply with the standards we are using in the solidity contract. +// This is not recommended for use anywhere else. +func tryAndIncrementEvm(message []byte, hashfunc func(message []byte) [32]byte, curve CurveSystem) (px, py *big.Int) { + counter := []byte{byte(0)} + px = new(big.Int) + py = new(big.Int) + q := curve.GetG1Q() + for { + h := hashfunc(append(counter, message...)) + counter[0]++ + px.SetBytes(h[:32]) + px.Mod(px, q) + ySqr := curve.g1XToYSquared(px) + root := calcQuadRes(ySqr, q) + rootSqr := new(big.Int).Exp(root, two, q) + if rootSqr.Cmp(ySqr) == 0 { + py = root + counter[0] = byte(255) + signY := hashfunc(append(counter, message...))[31] % 2 + if signY == 1 { + py.Sub(q, py) + } + break + } + } + return +} + +func sortBigInts(b1 *big.Int, b2 *big.Int) (*big.Int, *big.Int) { + if b1.Cmp(b2) > 0 { + return b2, b1 + } + return b1, b2 +} + +func fouqueTibouchiG1(curve CurveSystem, t *big.Int, blind bool) (Point, bool) { + pt, ok := sw(curve, t, blind) + if !ok { + return nil, false + } + pt = pt.Mul(curve.getG1Cofactor()) + return pt, true +} + +// Shallue - van de Woestijne encoding +// from "Indifferentiable Hashing to Barreto–Naehrig Curves" +func sw(curve CurveSystem, t *big.Int, blind bool) (Point, bool) { + var x [3]*big.Int + b := curve.getG1B() + q := curve.GetG1Q() + rootNeg3, neg1SubRootNeg3 := curve.getFTHashParams() + + //w = sqrt(-3)*t / (1 + b + t^2) + w := new(big.Int) + w.Exp(t, two, q) + w.Add(w, one) + w.Add(w, b) + w.ModInverse(w, q) + w.Mul(w, t) + w.Mod(w, q) + w.Mul(w, rootNeg3) + w.Mod(w, q) + + alpha := int64(0) + beta := int64(0) + var i int + + for i = 0; i < 3; i++ { + if i == 0 { + //x[0] = (-1 + sqrt(-3))/2 - t*w + x[0] = new(big.Int) + x[0].Mul(t, w) + x[0].Mod(x[0], q) + x[0].Sub(q, x[0]) + x[0].Add(x[0], neg1SubRootNeg3) + x[0].Mod(x[0], q) + + // If blinding isn't needed, utilize conditional branches. + alpha = chkPoint(x[0], curve, q, blind) + if !blind && alpha == 1 { + break + } + } else if i == 1 { + //x[1] = -1 - x[1] + x[1] = new(big.Int) + x[1].Neg(x[0]) + x[1].Sub(x[1], one) + x[1].Mod(x[1], q) + + beta = chkPoint(x[1], curve, q, blind) + if !blind && beta == 1 { + break + } + } else { + //x[2] = 1 + 1/w^2 + x[2] = new(big.Int) + x[2].Exp(w, two, q) + x[2].ModInverse(x[2], q) + x[2].Add(x[2], one) + x[2].Mod(x[2], q) + break + } + } + + //i = first x[i] such that (x^3 + b) is square + if blind { + i = int((((alpha - 1) * beta) + 3) % 3) + } + + // TODO Add blinded form of this + y := calcQuadRes(curve.g1XToYSquared(x[i]), q) + if parity(y, q) != parity(t, q) { + y.Sub(q, y) + } + // Check is set to false since its guaranteed to be on the curve + return curve.MakeG1Point([]*big.Int{x[i], y}, false) +} + +func parity(x *big.Int, q *big.Int) bool { + neg := new(big.Int).Sub(q, x) + return x.Cmp(neg) > 0 +} + +// Currently implementing first method from +// http://mathworld.wolfram.com/QuadraticResidue.html +// Experimentally, this seems to always return the canonical square root, +// however I haven't seen a proof of this. +func calcQuadRes(ySqr *big.Int, q *big.Int) *big.Int { + resMod4 := new(big.Int).Mod(q, four) + if resMod4.Cmp(three) == 0 { + k := new(big.Int).Sub(q, three) + k.Div(k, four) + exp := new(big.Int).Add(k, one) + result := new(big.Int) + result.Exp(ySqr, exp, q) + return result + } + // TODO: ADD CODE TO CALC QUADRATIC RESIDUE IN OTHER CASES + return zero +} + +// Currently implementing method from Guide to Pairing Based Cryptography, Ch 5 algorithm 18. +// This in turn is cited from "Gora Adj and Francisco Rodriguez-Henriquez. +// Square root computation over even extension fields. +// IEEE Transactions on Computers, 63(11):2829-2841, 2014" +func calcComplexQuadRes(ySqr *complexNum, q *big.Int) *complexNum { + result := getComplexZero() + if ySqr.im.Cmp(zero) == 0 { + result.re = calcQuadRes(ySqr.re, q) + return result + } + lambda := new(big.Int).Exp(ySqr.re, two, q) + lambda.Add(lambda, new(big.Int).Exp(ySqr.im, two, q)) + lambda = calcQuadRes(lambda, q) + invtwo := new(big.Int).ModInverse(two, q) + delta := new(big.Int).Add(ySqr.re, lambda) + delta.Mod(delta, q) + delta.Mul(delta, invtwo) + delta.Mod(delta, q) + if !isQuadRes(delta, q) { + delta = new(big.Int).Sub(ySqr.re, lambda) + delta.Mul(delta, invtwo) + delta.Mod(delta, q) + } + result.re = calcQuadRes(delta, q) + invRe := new(big.Int).ModInverse(result.re, q) + result.im.Mul(invRe, invtwo) + result.im.Mod(result.im, q) + result.im.Mul(result.im, ySqr.im) + result.im.Mod(result.im, q) + result.re.Mod(result.re, q) + return result +} + +//generates a random member of Fq such that it is a square +func randSquare(q *big.Int) *big.Int { + var r, _ = rand.Int(rand.Reader, q) + return r.Exp(r, two, q) +} + +// If blind is true, this blinds k with a random square in Fq, +// and then returns square root. This can be done to limit timing leakage. +// This returns the quadratic character of k. +func quadraticCharacter(k *big.Int, q *big.Int, blind bool) int64 { + r := k + if blind { + r = randSquare(q) + r.Mul(r, k) + r.Mod(r, q) + } + res := isQuadRes(r, q) + if res { + return 1 + } + return -1 +} + +//checks that (x^3 + b) is a square in Fq +func chkPoint(x *big.Int, curve CurveSystem, q *big.Int, mask bool) int64 { + return quadraticCharacter(curve.g1XToYSquared(x), q, mask) +} + +// Implement Eulers Criterion +func isQuadRes(a *big.Int, q *big.Int) bool { + if a.Cmp(zero) == 0 { + return true + } + fieldOrder := new(big.Int).Sub(q, one) + res := new(big.Int).Div(fieldOrder, two) + res.Exp(a, res, q) + if res.Cmp(one) == 0 { + return true + } + return false +} diff --git a/curves/testcases/altbn128G1Hash.dat b/curves/testcases/altbn128G1Hash.dat new file mode 100644 index 0000000..f6d40e0 --- /dev/null +++ b/curves/testcases/altbn128G1Hash.dat @@ -0,0 +1,10 @@ +5dUM58z11blf7y96Geo6+rQYwnBgKTRkt7BibqlrzIGpEz9VVR7oeoWcLL+/Bpo9rUrMehW0q7/WvCWYJmJfgQ==,AKE5vuACi7SB3WGlJ5gZvTfa6+UnipVAwda2BtqqnXAvEXoBRlmHfnw/CmaLxRYYDgjmJtRXx3rW/qA7scZx1w== +fdhezFb6eZi8JgnAbakVtYS5oUBV3JlxUnK2d1RMwOuA03Ja10oURDTmcXlrra3CZgL+4sauYSXoHZiXNZsgmg==,CJwi+mNMB9xYvMboJmPxXHcfrNM06JIoY5v/CpTZ4goORbnbe+LOzf20oVQGb2kC8RVIVZZG2dFgoDOTLo+TYw== +SCEGCsII2NT+nFee9hlLd8Y/kLqrAj1rZOk1T/bHsj9EBYDXBPO4AQA9WfLAgHUIlXFGthfphEO/ICDMJdrpmA==,KC8uUbi+n7aCFl7UvBNwBfWa3HsD60ovwZ6FK8kQrU0BAF7jVh5K5qnYCjPXLhQMSGenSxJ4TFmS7Uw1F9xD3A== +aEOZGEtNtI4BX0+JXL4mYnxwBeoLjMZoQoKznziOvDEatj26wPlKssQPgCQIkznPUOYsFqw0cHKwMMM5WNlOcQ==,FxjKA5Wz8mmeMLSBZILHqWeETdt5dK8H3QlAOkQ02GoBMUgrZpZCYudioXbOc0b3nqHduSNHmDx+SJQVTfdx3A== +lqRVDselVbzrVwHQcWlwuA9kCb/OMj5hc78inq0ondx5msCou6l4AdaJaAc5urqGS+yoFCoa4ugRqI2zrIwYCA==,G/nKgl6uLWBT+GskF+Lbi3cPBkICV2qHyMJT+7D2MNAcfqiy1SAurkL1lXJQTjaSE0oj3ACapNpXmUf8GdmZDg== +bG/aqEej5ge+D0MDWP/mt9rMIql5mT5ylDasu3mo5voG/OBkKFiaNIox32VUrmHoWb+9LDqk+CWYgN6Ad4O3Bg==,JdW8DUuybB0yt5vPSvGBjHuLIAAoGK7Ez7EH7zmKrIQXHbfOur/Sd9i1O0yFrAiWyqry8KoCc19zmVW9m6ukNw== +Buz1jWS0K9BsYUni1rYOJR8IvqP3LUjHkpooDgz0dOT+9L+aGC8UdrpcfawA6Q56JSb9kr5Nl50w7K5WV6Vytg==,FKNEQtqU6g60Yh+g1U80NDK1jNOXwXuE2ecTLTRa6fYt5Uf6XsQ2HIyH/kvle86nPpUcCRtxzBZuj4g6a6Abug== +iIEZJALIAXDL4oXfR19fIaE5M6xr2s3WrwPgFsOlKPdOnVTWEmKDfQb7/xhyulN/d5r34+jmXaigidlT825xew==,HYmdMaMGmV19Ub+8NyvQwHKWslpvxU96YNWy9urTS74WHijkWe3ehtkZKgRUrhUkC1jIfL/+W158/mXpihKrWA== +6ARrFdSUwaqMpzg8BE/lcKlYP0LEJIjnDMu0NSyyoMtKJD87tkCP2lWJX8EPMs2J0G3gCf6ZeJL0k9d4DpE2qw==,Aa6zIw111qFkJyw6X01dPcn1/vVoa3KwUHqV+YrK1ukA/fIPx+fj1Hj7Bookt/K24Dj64vYTE2+w4snU2UGDcQ== +lERwMVlRKoq3xqpjWYEGuljFiiUlzF3iC7fs7pPJqk25D0AiYEYRow04N+CsQNMXRdOZvldnWryBGJgaXxQr6w==,BkMpmRD1syZtpAoIjUMKCDAGAzpItxKHJFPzUZ+hgfoFwNYQZoqmxXelknSA3MVNs4m8qbIFg66GNSLjCHhljQ== diff --git a/curves/testcases/bls12G1Hash.dat b/curves/testcases/bls12G1Hash.dat new file mode 100644 index 0000000..ce6a347 --- /dev/null +++ b/curves/testcases/bls12G1Hash.dat @@ -0,0 +1,10 @@ +pILd5T75v29QM8miiU989h9p3AeL/sZ26kKXIZBTI2pQxw+kz9BJslwkYWI0BcCAztUWFWOg1qBihoTGcTbYMQ==,BWbwoDakknCWV0rMOg9uXDymUxHChd9kA21FOHb+aI85+LoiGifSltv3vvFVFk1/B4vFVAQkZ3rxxLeNfU9fwf5yS3yiHvAeLU92oyAr8nj/kLvYso0Xf5eQ6Sei2P4R ++5e8u0J42YSrYGcH9EKwQiVazYCu3P1pLjv+T/ofKqbK0uRYSqLaAYdA6HbzDt/neCwhXR9yBH3ayXg9UNsfuQ==,FvuNMUBUmk4mXObUkE/K9wkVfJLra8ZPDpvEIwmCXPiSQrrpbUA4b/SmmfVd3Yh5A+5ZmYvLEFi4uStHOJYvorxyyBPcSvVONgQyVWi+ip+D8MhznX/eI9Hq7MQz/pp8 +FmMoLNca470Qk1uFfqwbTYw2Wu7lZf/aABrH2zLFxNt0qZY7CXaxeYditfiEq1e4e8TdPaqrVmlXeCxY277IEg==,DMTYoLjvg4grnOCFSQjQYO1xDsnO3rjs1ho/7eueGdLnF0md7tenpfYB7YEy2z/lFKljfNz6jptvtV2TzyKOt0KCcXUHyk4VGsTy3Ihoqu3NFAeXK0PHvAl46SnXYfU1 +P2WJh3PodLzPaqq4BpBAHGIYhUscGct8w1OjvCxuEcM0lr0f8oZ3/eLpVlpoUbmpWOMwS2FXI//5FcnS3bFRyg==,BqiQEQwObKjDvx8KFLoh5AcgtNhoAE5sExLnDu8jZeWPmXmEsF1KErCUBkYY4yNyDOSdTAv44xM7yLm2aPHJWRjEEuXTdeEvixTBEI4fVGQ2VFZry3VwOPZYAyCd0XjA +1Xu2emP46dc88PfduEjNRFuEz7IwFKPulCCPjQSV+xm6VrIsWqvehgFODyZklH1H+HuCaRr21z+dCH85jelTKg==,Dg89Z7lljF5lz/WI36TW3NVzaTqqfB9bOGB1VOsgR5B5EDvZe06HUSZeHueDvaLvCpvCdeXxQ3eWKFlKFkpuD7wpwOafY0XFeneH90Fu8qZFroh/wNdzxD3iUGrYTNxK +NOglcU3et3TmbtuXT+wsN1n+4cebhw8S26uplp/B9QJW+XSwmtNEltPSTa3PMSt0rOX2IafwR7oFSWzZX4DbRA==,FyPM31CJdraSkDOVrO+7kLPInD4G+msO5z/eGt3eLvLVmqFZ5SNS6Ud6s/b9Y+QoCf+Bjvqp2fr7uz977d1tXp2p7RMsuVRfjJv9o58K3NbCQlNTyRQybn5ZunLWjm5Z +3TIZ7qXzLD23KVnfELl6RkigSZG7mJI+6Zi+JduH/t6N8gpVNtg0eztjF+WduBowEwzmIXWfEqxg9GEmjqDyog==,EK1Vfuv5Cif55WAopgodKtSW0TfY1w3lDxzdbMlr1ZJiTu2cWVig4y5b7gTQLfvMCdgb/ib07FFru/gzuicVmvZM4OWF0RDKIJ94jVN3kNYeT9FDPq1zLiHVXcFdtVmz +qp7JvD1qO4YfRp72Cu122y5Ma3HCRwPLaB0cRASBoiBBF7NgqLfNmg1ix0zu/3qKll3K/Fml9llALYHg/CWLkQ==,FSxYShask6aJsmxUL4OCdppjEY6byUosXNQGerKRo3QHy3cnjfyKloTurCZr5ODgGEfEew4hic48PgLPInqwCIP08MPXqtb5aCuAO/lF8K4Z0QJ3QzXvqmIsq/WVFYEs +R8AUZvbdakjUrAXsblZ/ppvaIYoRQtjf6eg/Rh0S+RAb3AcPFM9cjN/+truCJ6SNP5Au6GwCB3mM617t8PT4BA==,ErEUmfCaDN0rMRdQHawCuB9DukyYO8vk0KTUL2330osMsVcK5K1eBjeNbC8aLTrcE/9p4EFAMtQFxmhpxpQARtr4Ytm5gRwF7QxXlNsMPolD7naENI37Igi7b3AuFRQJ +YTieaCHkvuidDQTrL5Z+pPuRx15TjdmpYnweAxo0dUr0H6bJEaJvDjTd8kIfCTBIk8B4qRAG1j5ICMAIBxoL6w==,GN37RA64/85DzCz16OVGrVU+xHlhGXIWFJ5XUFKP1FbUXRULq4jxTy98qK6tYgehBl8RjndgzEMmfER8/aNa2xUpvjTEW9QlYBDcDd3VGx8xepStyqoDXi58utyOdIVt diff --git a/hash.go b/hash.go deleted file mode 100644 index f0d980e..0000000 --- a/hash.go +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright (C) 2018 Authors -// distributed under Apache 2.0 license - -package bgls - -import ( - "math/big" - - "github.com/mimoo/GoKangarooTwelve/K12" -) - -var zero = big.NewInt(0) -var one = big.NewInt(1) -var two = big.NewInt(2) -var three = big.NewInt(3) -var four = big.NewInt(4) - -// 64 byte kangaroo twelve hash -func kang12_64(messageDat []byte) [64]byte { - inputByte := make([]byte, 1) - hashFunc := K12.NewK12(inputByte) - hashFunc.Write(messageDat) - out := make([]byte, 64) - hashFunc.Read(out) - x := [64]byte{} - copy(x[:], out[:64]) - return x -} - -// 64 byte hash -func hash64(message []byte, hashfunc func(message []byte) [64]byte, curve CurveSystem) (px, py *big.Int) { - c := 0 - px = new(big.Int) - py = new(big.Int) - q := curve.getG1Q() - for { - h := hashfunc(append(message, byte(c))) - px.SetBytes(h[:48]) - px.Mod(px, q) - ySqr := curve.g1XToYSquared(px) - if isQuadRes(ySqr, q) == true { - py = calcQuadRes(ySqr, q) - signY := int(h[48]) % 2 - if signY == 1 { - py.Sub(q, py) - } - break - } - c++ - } - return -} - -// 32 byte hash which complies with standards we are using in the solidity contract. -func hash32(message []byte, hashfunc func(message []byte) [32]byte, curve CurveSystem) (px, py *big.Int) { - c := 0 - px = new(big.Int) - py = new(big.Int) - q := curve.getG1Q() - for { - h := hashfunc(append(message, byte(c))) - px.SetBytes(h[:32]) - px.Mod(px, q) - ySqr := curve.g1XToYSquared(px) - if isQuadRes(ySqr, q) == true { - py = calcQuadRes(ySqr, q) - signY := hashfunc(append(message, byte(255)))[31] % 2 - if signY == 1 { - py.Sub(q, py) - } - break - } - c++ - } - return -} - -// Currently implementing first method from -// http://mathworld.wolfram.com/QuadraticResidue.html -func calcQuadRes(ySqr *big.Int, q *big.Int) *big.Int { - resMod4 := new(big.Int).Mod(q, four) - if resMod4.Cmp(three) == 0 { - k := new(big.Int).Sub(q, three) - k.Div(k, four) - exp := new(big.Int).Add(k, one) - result := new(big.Int) - result.Exp(ySqr, exp, q) - return result - } - // TODO: ADD CODE TO CALC QUADRATIC RESIDUE IN OTHER CASES - return zero -} - -// Currently implementing method from Guide to Pairing Based Cryptography, Ch 5 algorithm 18. -// This in turn is cited from "Gora Adj and Francisco Rodriguez-Henriquez. -// Square root computation over even extension fields. -// IEEE Transactions on Computers, 63(11):2829-2841, 2014" -func calcComplexQuadRes(ySqr *complexNum, q *big.Int) *complexNum { - result := getComplexZero() - if ySqr.im.Cmp(zero) == 0 { - result.re = calcQuadRes(ySqr.re, q) - return result - } - lambda := new(big.Int).Exp(ySqr.re, two, q) - lambda.Add(lambda, new(big.Int).Exp(ySqr.im, two, q)) - lambda = calcQuadRes(lambda, q) - invtwo := new(big.Int).ModInverse(two, q) - delta := new(big.Int).Add(ySqr.re, lambda) - delta.Mod(delta, q) - delta.Mul(delta, invtwo) - delta.Mod(delta, q) - if !isQuadRes(delta, q) { - delta = new(big.Int).Sub(ySqr.re, lambda) - delta.Mul(delta, invtwo) - delta.Mod(delta, q) - } - result.re = calcQuadRes(delta, q) - invRe := new(big.Int).ModInverse(result.re, q) - result.im.Mul(invRe, invtwo) - result.im.Mod(result.im, q) - result.im.Mul(result.im, ySqr.im) - result.im.Mod(result.im, q) - result.re.Mod(result.re, q) - return result -} - -// Implement Eulers Criterion -func isQuadRes(a *big.Int, q *big.Int) bool { - if a.Cmp(zero) == 0 { - return true - } - fieldOrder := new(big.Int).Sub(q, one) - res := new(big.Int).Div(fieldOrder, two) - res.Exp(a, res, q) - if res.Cmp(one) == 0 { - return true - } - return false -}