Skip to content

Commit 3b889ab

Browse files
authored
Merge pull request #2389 from CortexFoundation/dev
fix gnark deserialisation
2 parents c1fefe0 + e44ee8d commit 3b889ab

3 files changed

Lines changed: 147 additions & 16 deletions

File tree

crypto/bn256/gnark/g1.go

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package bn256
22

33
import (
4+
"errors"
45
"math/big"
56

67
"github.com/consensys/gnark-crypto/ecc/bn254"
@@ -31,21 +32,62 @@ func (g *G1) ScalarMult(a *G1, scalar *big.Int) {
3132

3233
// Unmarshal deserializes `buf` into `g`
3334
//
34-
// Note: whether the deserialization is of a compressed
35-
// or an uncompressed point, is encoded in the bytes.
36-
//
37-
// For our purpose, the point will always be serialized
38-
// as uncompressed, ie 64 bytes.
35+
// The input is expected to be in the EVM format:
36+
// 64 bytes: [32-byte x coordinate][32-byte y coordinate]
37+
// where each coordinate is in big-endian format.
3938
//
4039
// This method also checks whether the point is on the
4140
// curve and in the prime order subgroup.
4241
func (g *G1) Unmarshal(buf []byte) (int, error) {
43-
return g.inner.SetBytes(buf)
42+
if len(buf) < 64 {
43+
return 0, errors.New("invalid G1 point size")
44+
}
45+
46+
if allZeroes(buf[:64]) {
47+
// point at infinity
48+
g.inner.X.SetZero()
49+
g.inner.Y.SetZero()
50+
return 64, nil
51+
}
52+
53+
if err := g.inner.X.SetBytesCanonical(buf[:32]); err != nil {
54+
return 0, err
55+
}
56+
if err := g.inner.Y.SetBytesCanonical(buf[32:64]); err != nil {
57+
return 0, err
58+
}
59+
60+
if !g.inner.IsOnCurve() {
61+
return 0, errors.New("point is not on curve")
62+
}
63+
if !g.inner.IsInSubGroup() {
64+
return 0, errors.New("point is not in correct subgroup")
65+
}
66+
return 64, nil
4467
}
4568

4669
// Marshal serializes the point into a byte slice.
4770
//
48-
// Note: The point is serialized as uncompressed.
71+
// The output is in EVM format: 64 bytes total.
72+
// [32-byte x coordinate][32-byte y coordinate]
73+
// where each coordinate is a big-endian integer padded to 32 bytes.
4974
func (p *G1) Marshal() []byte {
50-
return p.inner.Marshal()
75+
output := make([]byte, 64)
76+
77+
xBytes := p.inner.X.Bytes()
78+
copy(output[:32], xBytes[:])
79+
80+
yBytes := p.inner.Y.Bytes()
81+
copy(output[32:64], yBytes[:])
82+
83+
return output
84+
}
85+
86+
func allZeroes(buf []byte) bool {
87+
for i := range buf {
88+
if buf[i] != 0 {
89+
return false
90+
}
91+
}
92+
return true
5193
}

crypto/bn256/gnark/g2.go

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package bn256
22

33
import (
4+
"errors"
5+
46
"github.com/consensys/gnark-crypto/ecc/bn254"
57
)
68

@@ -18,21 +20,66 @@ type G2 struct {
1820

1921
// Unmarshal deserializes `buf` into `g`
2022
//
21-
// Note: whether the deserialization is of a compressed
22-
// or an uncompressed point, is encoded in the bytes.
23-
//
24-
// For our purpose, the point will always be serialized
25-
// as uncompressed, ie 128 bytes.
23+
// The input is expected to be in the EVM format:
24+
// 128 bytes: [32-byte x.0][32-byte x.1][32-byte y.0][32-byte y.1]
25+
// where each value is a big-endian integer.
2626
//
2727
// This method also checks whether the point is on the
2828
// curve and in the prime order subgroup.
2929
func (g *G2) Unmarshal(buf []byte) (int, error) {
30-
return g.inner.SetBytes(buf)
30+
if len(buf) < 128 {
31+
return 0, errors.New("invalid G2 point size")
32+
}
33+
34+
if allZeroes(buf[:128]) {
35+
// point at infinity
36+
g.inner.X.A0.SetZero()
37+
g.inner.X.A1.SetZero()
38+
g.inner.Y.A0.SetZero()
39+
g.inner.Y.A1.SetZero()
40+
return 128, nil
41+
}
42+
if err := g.inner.X.A0.SetBytesCanonical(buf[0:32]); err != nil {
43+
return 0, err
44+
}
45+
if err := g.inner.X.A1.SetBytesCanonical(buf[32:64]); err != nil {
46+
return 0, err
47+
}
48+
if err := g.inner.Y.A0.SetBytesCanonical(buf[64:96]); err != nil {
49+
return 0, err
50+
}
51+
if err := g.inner.Y.A1.SetBytesCanonical(buf[96:128]); err != nil {
52+
return 0, err
53+
}
54+
55+
if !g.inner.IsOnCurve() {
56+
return 0, errors.New("point is not on curve")
57+
}
58+
if !g.inner.IsInSubGroup() {
59+
return 0, errors.New("point is not in correct subgroup")
60+
}
61+
return 128, nil
3162
}
3263

3364
// Marshal serializes the point into a byte slice.
3465
//
35-
// Note: The point is serialized as uncompressed.
66+
// The output is in EVM format: 128 bytes total.
67+
// [32-byte x.0][32-byte x.1][32-byte y.0][32-byte y.1]
68+
// where each value is a big-endian integer.
3669
func (g *G2) Marshal() []byte {
37-
return g.inner.Marshal()
70+
output := make([]byte, 128)
71+
72+
xA0Bytes := g.inner.X.A0.Bytes()
73+
copy(output[:32], xA0Bytes[:])
74+
75+
xA1Bytes := g.inner.X.A1.Bytes()
76+
copy(output[32:64], xA1Bytes[:])
77+
78+
yA0Bytes := g.inner.Y.A0.Bytes()
79+
copy(output[64:96], yA0Bytes[:])
80+
81+
yA1Bytes := g.inner.Y.A1.Bytes()
82+
copy(output[96:128], yA1Bytes[:])
83+
84+
return output
3885
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package bn256
2+
3+
import (
4+
"testing"
5+
6+
"github.com/consensys/gnark-crypto/ecc/bn254"
7+
)
8+
9+
func TestNativeGnarkFormatIncompatibility(t *testing.T) {
10+
// Use official gnark serialization
11+
_, _, g1Gen, _ := bn254.Generators()
12+
wrongSer := g1Gen.Bytes()
13+
14+
var evmG1 G1
15+
_, err := evmG1.Unmarshal(wrongSer[:])
16+
if err == nil {
17+
t.Fatalf("points serialized using the official bn254 serialization algorithm, should not work with the evm format")
18+
}
19+
}
20+
21+
func TestSerRoundTrip(t *testing.T) {
22+
_, _, g1Gen, g2Gen := bn254.Generators()
23+
24+
expectedG1 := G1{inner: g1Gen}
25+
bytesG1 := expectedG1.Marshal()
26+
27+
expectedG2 := G2{inner: g2Gen}
28+
bytesG2 := expectedG2.Marshal()
29+
30+
var gotG1 G1
31+
gotG1.Unmarshal(bytesG1)
32+
33+
var gotG2 G2
34+
gotG2.Unmarshal(bytesG2)
35+
36+
if !expectedG1.inner.Equal(&gotG1.inner) {
37+
t.Errorf("serialization roundtrip failed for G1")
38+
}
39+
if !expectedG2.inner.Equal(&gotG2.inner) {
40+
t.Errorf("serialization roundtrip failed for G2")
41+
}
42+
}

0 commit comments

Comments
 (0)