Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace kilic/bls12-381 with gnark-crypto/ecc/bls12-381 #2629

Open
wants to merge 24 commits into
base: gligneul/fix-update-gethpin-v1.14.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
5c668c0
Replace kilic/bls12-381 with gnark-crypto/ecc/bls12-381
amsanghi Sep 3, 2024
f786bfe
Merge branch 'update-gethpin-v1.14.0' into replace_bls
amsanghi Sep 3, 2024
3ccc63c
Merge branch 'update-gethpin-v1.14.0' into replace_bls
amsanghi Sep 3, 2024
addf871
Merge branch 'update-gethpin-v1.14.0' into replace_bls
amsanghi Sep 3, 2024
18c11c6
fix lint
amsanghi Sep 3, 2024
509cc7a
Merge branch 'update-gethpin-v1.14.0' into replace_bls
amsanghi Oct 7, 2024
443d864
Merge branch 'update-gethpin-v1.14.0' into replace_bls
amsanghi Oct 7, 2024
841506e
Changes based on PR comments
amsanghi Oct 7, 2024
8abaec7
Changes based on PR comments
amsanghi Oct 8, 2024
2430153
Changes based on PR comments
amsanghi Oct 8, 2024
2a8f511
Merge branch 'update-gethpin-v1.14.0' into replace_bls
amsanghi Oct 11, 2024
df69097
TestPublicKeyFromPrivateKey
amsanghi Oct 11, 2024
010ca1e
TestPublicKeyToBytes
amsanghi Oct 11, 2024
0014823
TestHashToG1Curve
amsanghi Oct 11, 2024
f3b9c2a
Merge branch 'gligneul/fix-update-gethpin-v1.14.0' into replace_bls
amsanghi Oct 11, 2024
d6fe4ae
Merge branch 'gligneul/fix-update-gethpin-v1.14.0' into replace_bls
amsanghi Oct 11, 2024
3c003e6
Merge branch 'gligneul/fix-update-gethpin-v1.14.0' into replace_bls
amsanghi Oct 11, 2024
268f82a
Changes based on PR comments
amsanghi Oct 16, 2024
d4efeec
Changes based on PR comments
amsanghi Oct 16, 2024
8994574
update
amsanghi Oct 17, 2024
4832a12
update
amsanghi Oct 17, 2024
0aacc8c
update
amsanghi Oct 17, 2024
fb6a955
Merge branch 'gligneul/fix-update-gethpin-v1.14.0' into replace_bls
amsanghi Oct 17, 2024
08a377c
fix lint
amsanghi Oct 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 103 additions & 65 deletions blsSignatures/blsSignatures.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,27 @@
package blsSignatures

import (
cryptorand "crypto/rand"
"bytes"
"encoding/base64"
"errors"
bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381"
"github.com/consensys/gnark-crypto/ecc/bls12-381/fp"
"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
"github.com/ethereum/go-ethereum/crypto"
bls12381 "github.com/kilic/bls12-381"
"math/big"
)

type PublicKey struct {
key *bls12381.PointG2
validityProof *bls12381.PointG1 // if this is nil, key came from a trusted source
key *bls12381.G2Affine
validityProof *bls12381.G1Affine // if this is nil, key came from a trusted source
}

type PrivateKey *bls12381.Fr
type PrivateKey *fr.Element

type Signature *bls12381.PointG1
type Signature *bls12381.G1Affine

func GeneratePrivKeyString() (string, error) {
fr := bls12381.NewFr()
privKey, err := fr.Rand(cryptorand.Reader)
privKey, err := new(fr.Element).SetRandom()
if err != nil {
return "", err
}
Expand All @@ -33,8 +35,7 @@ func GeneratePrivKeyString() (string, error) {
}

func GenerateKeys() (PublicKey, PrivateKey, error) {
fr := bls12381.NewFr()
privateKey, err := fr.Rand(cryptorand.Reader)
privateKey, err := new(fr.Element).SetRandom()
if err != nil {
return PublicKey{}, nil, err
}
Expand All @@ -43,9 +44,9 @@ func GenerateKeys() (PublicKey, PrivateKey, error) {
}

func PublicKeyFromPrivateKey(privateKey PrivateKey) (PublicKey, error) {
pubKey := &bls12381.PointG2{}
g2 := bls12381.NewG2()
g2.MulScalar(pubKey, g2.One(), privateKey)
pubKey := new(bls12381.G2Affine)
_, _, _, g2 := bls12381.Generators()
pubKey.ScalarMultiplication(&g2, (*privateKey).BigInt(new(big.Int)))
amsanghi marked this conversation as resolved.
Show resolved Hide resolved
proof, err := KeyValidityProof(pubKey, privateKey)
if err != nil {
return PublicKey{}, err
Expand All @@ -64,15 +65,15 @@ func PublicKeyFromPrivateKey(privateKey PrivateKey) (PublicKey, error) {
//
// For a proof that this is sufficient, see Theorem 1 in
// Ristenpart & Yilek, "The Power of Proofs-of-Possession: ..." from EUROCRYPT 2007.
func KeyValidityProof(pubKey *bls12381.PointG2, privateKey PrivateKey) (Signature, error) {
g2 := bls12381.NewG2()
return signMessage2(privateKey, g2.ToBytes(pubKey), true)
func KeyValidityProof(pubKey *bls12381.G2Affine, privateKey PrivateKey) (Signature, error) {
message := pubKey.Bytes()
return signMessage2(privateKey, message[:], true)
amsanghi marked this conversation as resolved.
Show resolved Hide resolved
}

func NewPublicKey(pubKey *bls12381.PointG2, validityProof *bls12381.PointG1) (PublicKey, error) {
g2 := bls12381.NewG2()
func NewPublicKey(pubKey *bls12381.G2Affine, validityProof *bls12381.G1Affine) (PublicKey, error) {
message := pubKey.Bytes()
unverifiedPublicKey := PublicKey{pubKey, validityProof}
verified, err := verifySignature2(validityProof, g2.ToBytes(pubKey), unverifiedPublicKey, true)
verified, err := verifySignature2(validityProof, message[:], unverifiedPublicKey, true)
if err != nil {
return PublicKey{}, err
}
Expand All @@ -82,7 +83,7 @@ func NewPublicKey(pubKey *bls12381.PointG2, validityProof *bls12381.PointG1) (Pu
return unverifiedPublicKey, nil
}

func NewTrustedPublicKey(pubKey *bls12381.PointG2) PublicKey {
func NewTrustedPublicKey(pubKey *bls12381.G2Affine) PublicKey {
return PublicKey{pubKey, nil}
}

Expand All @@ -102,9 +103,8 @@ func signMessage2(priv PrivateKey, message []byte, keyValidationMode bool) (Sign
if err != nil {
return nil, err
}
g1 := bls12381.NewG1()
result := &bls12381.PointG1{}
g1.MulScalar(result, pointOnCurve, priv)
result := new(bls12381.G1Affine)
result.ScalarMultiplication(pointOnCurve, (*priv).BigInt(new(big.Int)))
return result, nil
}

Expand All @@ -118,31 +118,32 @@ func verifySignature2(sig Signature, message []byte, publicKey PublicKey, keyVal
return false, err
}

engine := bls12381.NewEngine()
engine.Reset()
engine.AddPair(pointOnCurve, publicKey.key)
leftSide := engine.Result()
engine.AddPair(sig, engine.G2.One())
rightSide := engine.Result()
return leftSide.Equal(rightSide), nil
leftSide, err := bls12381.Pair([]bls12381.G1Affine{*pointOnCurve}, []bls12381.G2Affine{*publicKey.key})
if err != nil {
return false, err
}
_, _, _, g2 := bls12381.Generators()
rightSide, err := bls12381.Pair([]bls12381.G1Affine{*sig}, []bls12381.G2Affine{g2})
if err != nil {
return false, err
}
return leftSide.Equal(&rightSide), nil
}

func AggregatePublicKeys(pubKeys []PublicKey) PublicKey {
g2 := bls12381.NewG2()
ret := g2.Zero()
g2 := new(bls12381.G2Affine)
for _, pk := range pubKeys {
g2.Add(ret, ret, pk.key)
g2.Add(g2, pk.key)
}
return NewTrustedPublicKey(ret)
return NewTrustedPublicKey(g2)
}

func AggregateSignatures(sigs []Signature) Signature {
g1 := bls12381.NewG1()
ret := g1.Zero()
g1 := new(bls12381.G1Affine)
for _, s := range sigs {
g1.Add(ret, ret, s)
g1.Add(g1, s)
}
return ret
return g1
}

func VerifyAggregatedSignatureSameMessage(sig Signature, message []byte, pubKeys []PublicKey) (bool, error) {
Expand All @@ -154,21 +155,26 @@ func VerifyAggregatedSignatureDifferentMessages(sig Signature, messages [][]byte
if len(messages) != len(pubKeys) {
return false, errors.New("len(messages) does not match (len(pub keys) in verification")
}
engine := bls12381.NewEngine()
engine.Reset()
var p []bls12381.G1Affine
var q []bls12381.G2Affine
for i, msg := range messages {
pointOnCurve, err := hashToG1Curve(msg, false)
if err != nil {
return false, err
}
engine.AddPair(pointOnCurve, pubKeys[i].key)
p = append(p, *pointOnCurve)
q = append(q, *pubKeys[i].key)
}
leftSide := engine.Result()

engine.Reset()
engine.AddPair(sig, engine.G2.One())
rightSide := engine.Result()
return leftSide.Equal(rightSide), nil
leftSide, err := bls12381.Pair(p, q)
if err != nil {
return false, err
}
_, _, _, g2 := bls12381.Generators()
rightSide, err := bls12381.Pair([]bls12381.G1Affine{*sig}, []bls12381.G2Affine{g2})
if err != nil {
return false, err
}
return leftSide.Equal(&rightSide), nil
}

// This hashes a message to a [32]byte, then maps the result to the G1 curve using
Expand All @@ -177,41 +183,46 @@ func VerifyAggregatedSignatureDifferentMessages(sig Signature, messages [][]byte
//
// If keyValidationMode is true, this uses a tweaked version of the padding,
// so that the result will not collide with a result generated in an ordinary signature.
func hashToG1Curve(message []byte, keyValidationMode bool) (*bls12381.PointG1, error) {
func hashToG1Curve(message []byte, keyValidationMode bool) (*bls12381.G1Affine, error) {
amsanghi marked this conversation as resolved.
Show resolved Hide resolved
var padding [16]byte
h := crypto.Keccak256(message)
if keyValidationMode {
// modify padding, for domain separation
padding[0] = 1
}
g1 := bls12381.NewG1()
return g1.MapToCurve(append(padding[:], h...))
fp := new(fp.Element)
fp.Unmarshal(append(padding[:], h...))
res := bls12381.MapToG1(*fp)
return &res, nil
}

func PublicKeyToBytes(pub PublicKey) []byte {
g2 := bls12381.NewG2()
keyBytes := pub.key.RawBytes()
if pub.validityProof == nil {
return append([]byte{0}, g2.ToBytes(pub.key)...)
return append([]byte{0}, keyBytes[:]...)
}
keyBytes := g2.ToBytes(pub.key)
sigBytes := SignatureToBytes(pub.validityProof)
if len(sigBytes) > 255 {
panic("validity proof too large to serialize")
}
return append(append([]byte{byte(len(sigBytes))}, sigBytes...), keyBytes...)
return append(append([]byte{byte(len(sigBytes))}, sigBytes...), keyBytes[:]...)
}

func PublicKeyFromBytes(in []byte, trustedSource bool) (PublicKey, error) {
if len(in) == 0 {
return PublicKey{}, errors.New("tried to deserialize empty public key")
}
g2 := bls12381.NewG2()
key := new(bls12381.G2Affine)
proofLen := int(in[0])
if proofLen == 0 {
if !trustedSource {
return PublicKey{}, errors.New("tried to deserialize unvalidated public key from untrusted source")
}
key, err := g2.FromBytes(in[1:])
// The most significant bit, when set, indicates that the point is in compressed form. Otherwise, the point is in uncompressed form.
if len(in) == 1 || (in[1]&(1<<7)) != 0 {
return PublicKey{}, errors.New("invalid serialized public key")
}
err := key.Unmarshal(in[1:])
if err != nil {
return PublicKey{}, err
}
Expand All @@ -220,14 +231,22 @@ func PublicKeyFromBytes(in []byte, trustedSource bool) (PublicKey, error) {
if len(in) < 1+proofLen {
return PublicKey{}, errors.New("invalid serialized public key")
}
g1 := bls12381.NewG1()
validityProof := new(bls12381.G1Affine)
proofBytes := in[1 : 1+proofLen]
validityProof, err := g1.FromBytes(proofBytes)
// The most significant bit, when set, indicates that the point is in compressed form. Otherwise, the point is in uncompressed form.
if len(proofBytes) == 0 || (proofBytes[0]&(1<<7)) != 0 {
return PublicKey{}, errors.New("invalid serialized validity proof")
}
err := validityProof.Unmarshal(proofBytes)
if err != nil {
return PublicKey{}, err
}
keyBytes := in[1+proofLen:]
key, err := g2.FromBytes(keyBytes)
// The most significant bit, when set, indicates that the point is in compressed form. Otherwise, the point is in uncompressed form.
if len(keyBytes) == 0 || (keyBytes[0]&(1<<7)) != 0 {
return PublicKey{}, errors.New("invalid serialized public key")
}
err = key.Unmarshal(keyBytes)
if err != nil {
return PublicKey{}, err
}
Expand All @@ -240,19 +259,38 @@ func PublicKeyFromBytes(in []byte, trustedSource bool) (PublicKey, error) {
}

func PrivateKeyToBytes(priv PrivateKey) []byte {
return bls12381.NewFr().Set(priv).ToBytes()
bytes := (*priv).Bytes()
return bytes[:]
}

func PrivateKeyFromBytes(in []byte) (PrivateKey, error) {
return bls12381.NewFr().FromBytes(in), nil
return new(fr.Element).SetBytes(in), nil
}

func SignatureToBytes(sig Signature) []byte {
g1 := bls12381.NewG1()
return g1.ToBytes(sig)
buf := new(bytes.Buffer)
err := bls12381.NewEncoder(buf).Encode(sig)
if err != nil {
panic("failed to serialize signature")
}
return buf.Bytes()
}

func SignatureFromBytes(in []byte) (Signature, error) {
g1 := bls12381.NewG1()
return g1.FromBytes(in)
// The most significant bit, when set, indicates that the point is in compressed form. Otherwise, the point is in uncompressed form.
if len(in) == 0 || (in[0]&(1<<7)) != 0 {
return nil, errors.New("invalid serialized signature")
}
if len(in) != 96 {
return nil, errors.New("input string should be equal or larger than 96")
}
if new(big.Int).SetBytes(in).Cmp(fp.Modulus()) >= 0 {
return nil, errors.New("must be less than modulus")
}
g1 := new(bls12381.G1Affine)
err := bls12381.NewDecoder(bytes.NewReader(in)).Decode(g1)
if err != nil {
return nil, err
}
return g1, nil
}
45 changes: 45 additions & 0 deletions blsSignatures/blsSignatures_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,58 @@
package blsSignatures

import (
"bytes"
"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
"math/rand"
"testing"
"time"

"github.com/offchainlabs/nitro/util/testhelpers"
)

func TestPublicKeyFromPrivateKey(t *testing.T) {
// Hardcoded private key for testing
privateKey := new(fr.Element).SetBytes([]byte{54, 16, 51, 77, 200, 74, 139, 205, 66, 197, 218, 43, 163, 239, 159, 127, 31, 250, 204, 181, 30, 57, 125, 217, 57, 198, 145, 143, 232, 224, 117, 185})
publicKey, err := PublicKeyFromPrivateKey(privateKey)
Require(t, err)
publicKeyBytes := publicKey.key.RawBytes()
// Compare to the public key generated by the Ethereum BLS library
if !bytes.Equal(publicKeyBytes[:], []byte{5, 26, 203, 151, 1, 124, 221, 162, 29, 13, 15, 227, 78, 232, 125, 200, 232, 245, 251, 196, 153, 185, 66, 74, 49, 126, 168, 225, 101, 252, 124, 184, 52, 101, 97, 135, 1, 36, 242, 88, 206, 106, 47, 226, 161, 148, 35, 61, 8, 234, 6, 124, 238, 224, 58, 64, 92, 163, 210, 25, 221, 204, 20, 149, 121, 193, 175, 168, 157, 184, 16, 216, 30, 181, 114, 184, 201, 251, 46, 246, 7, 80, 87, 34, 101, 34, 123, 51, 58, 176, 132, 118, 190, 53, 158, 161, 19, 144, 72, 109, 52, 189, 109, 245, 80, 64, 229, 196, 99, 200, 215, 204, 77, 156, 60, 196, 6, 167, 27, 227, 96, 190, 228, 57, 53, 32, 128, 67, 192, 155, 233, 163, 171, 83, 86, 81, 93, 20, 221, 52, 75, 254, 66, 42, 17, 79, 254, 35, 80, 175, 30, 100, 210, 109, 164, 150, 197, 88, 104, 152, 160, 178, 69, 78, 56, 215, 38, 180, 215, 212, 202, 233, 219, 224, 245, 184, 223, 248, 166, 91, 147, 62, 53, 61, 251, 83, 155, 92, 68, 201, 65, 92}) {
Fail(t, "public key is incorrect")
}
// Use the validity proof generated by the Ethereum BLS library
_, err = publicKey.validityProof.SetBytes([]byte{24, 152, 185, 33, 240, 229, 254, 108, 130, 235, 47, 25, 45, 224, 93, 56, 103, 226, 157, 91, 233, 2, 73, 218, 179, 213, 171, 7, 54, 4, 113, 43, 19, 25, 188, 71, 45, 232, 233, 95, 223, 113, 104, 118, 210, 115, 248, 126, 18, 80, 5, 160, 54, 207, 82, 154, 150, 84, 98, 19, 68, 17, 230, 124, 32, 106, 80, 143, 74, 214, 105, 109, 69, 114, 47, 239, 145, 131, 19, 145, 77, 207, 249, 122, 229, 239, 228, 89, 42, 207, 97, 244, 39, 21, 115, 60})
Require(t, err)
message := []byte("The quick brown fox jumped over the lazy dog.")
sig, err := SignMessage(privateKey, message)
Require(t, err)

verified, err := VerifySignature(sig, message, publicKey)
Require(t, err)
if !verified {
Fail(t, "valid signature failed to verify")
}
}

func TestPublicKeyToBytes(t *testing.T) {
expectedPublicKeyBytes := []byte{0, 5, 26, 203, 151, 1, 124, 221, 162, 29, 13, 15, 227, 78, 232, 125, 200, 232, 245, 251, 196, 153, 185, 66, 74, 49, 126, 168, 225, 101, 252, 124, 184, 52, 101, 97, 135, 1, 36, 242, 88, 206, 106, 47, 226, 161, 148, 35, 61, 8, 234, 6, 124, 238, 224, 58, 64, 92, 163, 210, 25, 221, 204, 20, 149, 121, 193, 175, 168, 157, 184, 16, 216, 30, 181, 114, 184, 201, 251, 46, 246, 7, 80, 87, 34, 101, 34, 123, 51, 58, 176, 132, 118, 190, 53, 158, 161, 19, 144, 72, 109, 52, 189, 109, 245, 80, 64, 229, 196, 99, 200, 215, 204, 77, 156, 60, 196, 6, 167, 27, 227, 96, 190, 228, 57, 53, 32, 128, 67, 192, 155, 233, 163, 171, 83, 86, 81, 93, 20, 221, 52, 75, 254, 66, 42, 17, 79, 254, 35, 80, 175, 30, 100, 210, 109, 164, 150, 197, 88, 104, 152, 160, 178, 69, 78, 56, 215, 38, 180, 215, 212, 202, 233, 219, 224, 245, 184, 223, 248, 166, 91, 147, 62, 53, 61, 251, 83, 155, 92, 68, 201, 65, 92}
publicKey, err := PublicKeyFromBytes(expectedPublicKeyBytes, true)
Require(t, err)
publicKeyBytes := PublicKeyToBytes(publicKey)
if !bytes.Equal(publicKeyBytes, expectedPublicKeyBytes) {
Fail(t, "public key to bytes failed")
}
}

func TestHashToG1Curve(t *testing.T) {
g1, err := hashToG1Curve([]byte{5, 26, 203, 151, 1, 124, 221, 162, 29, 13, 15, 227, 78, 232, 125, 200, 232, 245, 251, 196, 153, 185, 66, 74, 49, 126, 168, 225, 101, 252, 124, 184, 52, 101, 97, 135, 1, 36, 242, 88, 206, 106, 47, 226, 161, 148, 35, 61, 8, 234, 6, 124, 238, 224, 58, 64, 92, 163, 210, 25, 221, 204, 20, 149, 121, 193, 175, 168, 157, 184, 16, 216, 30, 181, 114, 184, 201, 251, 46, 246, 7, 80, 87, 34, 101, 34, 123, 51, 58, 176, 132, 118, 190, 53, 158, 161, 19, 144, 72, 109, 52, 189, 109, 245, 80, 64, 229, 196, 99, 200, 215, 204, 77, 156, 60, 196, 6, 167, 27, 227, 96, 190, 228, 57, 53, 32, 128, 67, 192, 155, 233, 163, 171, 83, 86, 81, 93, 20, 221, 52, 75, 254, 66, 42, 17, 79, 254, 35, 80, 175, 30, 100, 210, 109, 164, 150, 197, 88, 104, 152, 160, 178, 69, 78, 56, 215, 38, 180, 215, 212, 202, 233, 219, 224, 245, 184, 223, 248, 166, 91, 147, 62, 53, 61, 251, 83, 155, 92, 68, 201, 65, 92}, true)
Require(t, err)
g1Bytes := g1.RawBytes()
if !bytes.Equal(g1Bytes[:], []byte{22, 205, 206, 121, 144, 143, 115, 232, 210, 74, 148, 73, 40, 222, 234, 186, 113, 228, 107, 174, 195, 131, 199, 48, 158, 20, 232, 188, 206, 210, 78, 82, 117, 237, 138, 249, 30, 26, 199, 135, 41, 65, 4, 73, 164, 235, 55, 179, 1, 84, 24, 92, 0, 215, 232, 26, 75, 112, 160, 84, 189, 124, 183, 97, 173, 171, 99, 181, 115, 96, 178, 31, 130, 107, 83, 3, 56, 126, 181, 146, 250, 249, 4, 85, 66, 241, 120, 209, 66, 71, 195, 138, 145, 19, 165, 26}) {
Fail(t, "hash to G1 curve failed")
}
}

func TestValidSignature(t *testing.T) {
pub, priv, err := GenerateKeys()
Require(t, err)
Expand Down
Loading