github.com/cloudflare/circl@v1.5.0/ecc/bls12381/g2_test.go (about)

     1  package bls12381
     2  
     3  import (
     4  	"crypto/rand"
     5  	"fmt"
     6  	"testing"
     7  
     8  	"github.com/cloudflare/circl/ecc/bls12381/ff"
     9  	"github.com/cloudflare/circl/internal/test"
    10  )
    11  
    12  func randomG2(t testing.TB) *G2 {
    13  	var P G2
    14  	k := randomScalar(t)
    15  	P.ScalarMult(k, G2Generator())
    16  	if !P.isOnCurve() {
    17  		t.Helper()
    18  		t.Fatal("not on curve")
    19  	}
    20  	return &P
    21  }
    22  
    23  func TestG2Add(t *testing.T) {
    24  	const testTimes = 1 << 6
    25  	var Q, R G2
    26  	for i := 0; i < testTimes; i++ {
    27  		P := randomG2(t)
    28  		Q = *P
    29  		R = *P
    30  		R.Add(&R, &R)
    31  		R.Neg()
    32  		Q.Double()
    33  		Q.Neg()
    34  		got := R
    35  		want := Q
    36  		if !got.IsEqual(&want) {
    37  			test.ReportError(t, got, want, P)
    38  		}
    39  	}
    40  }
    41  
    42  func TestG2ScalarMult(t *testing.T) {
    43  	const testTimes = 1 << 6
    44  	var Q G2
    45  	for i := 0; i < testTimes; i++ {
    46  		P := randomG2(t)
    47  		k := randomScalar(t)
    48  		Q.ScalarMult(k, P)
    49  		Q.toAffine()
    50  		got := Q.IsOnG2()
    51  		want := true
    52  		if got != want {
    53  			test.ReportError(t, got, want, P)
    54  		}
    55  	}
    56  }
    57  
    58  func TestG2Hash(t *testing.T) {
    59  	const testTimes = 1 << 8
    60  
    61  	for _, e := range [...]struct {
    62  		Name string
    63  		Enc  func(p *G2, input, dst []byte)
    64  	}{
    65  		{"Encode", func(p *G2, input, dst []byte) { p.Encode(input, dst) }},
    66  		{"Hash", func(p *G2, input, dst []byte) { p.Hash(input, dst) }},
    67  	} {
    68  		var msg, dst [4]byte
    69  		var p G2
    70  		t.Run(e.Name, func(t *testing.T) {
    71  			for i := 0; i < testTimes; i++ {
    72  				_, _ = rand.Read(msg[:])
    73  				_, _ = rand.Read(dst[:])
    74  				e.Enc(&p, msg[:], dst[:])
    75  
    76  				got := p.isRTorsion()
    77  				want := true
    78  				if got != want {
    79  					test.ReportError(t, got, want, e.Name, msg, dst)
    80  				}
    81  			}
    82  		})
    83  	}
    84  }
    85  
    86  func TestG2Serial(t *testing.T) {
    87  	mustOk := "must be ok"
    88  	mustErr := "must be an error"
    89  	t.Run("valid", func(t *testing.T) {
    90  		testTimes := 1 << 6
    91  		var got, want G2
    92  		want.SetIdentity()
    93  		for i := 0; i < testTimes; i++ {
    94  			for _, b := range [][]byte{want.Bytes(), want.BytesCompressed()} {
    95  				err := got.SetBytes(b)
    96  				test.CheckNoErr(t, err, fmt.Sprintf("failure to deserialize: (P:%v b:%x)", want, b))
    97  
    98  				if !got.IsEqual(&want) {
    99  					test.ReportError(t, got, want, b)
   100  				}
   101  			}
   102  			want = *randomG2(t)
   103  		}
   104  	})
   105  	t.Run("badPrefix", func(t *testing.T) {
   106  		q := new(G2)
   107  		b := make([]byte, G2Size)
   108  		for _, b[0] = range []byte{0x20, 0x60, 0xE0} {
   109  			test.CheckIsErr(t, q.SetBytes(b), mustErr)
   110  		}
   111  	})
   112  	t.Run("badLength", func(t *testing.T) {
   113  		q := new(G2)
   114  		p := randomG2(t)
   115  		b := p.Bytes()
   116  		test.CheckIsErr(t, q.SetBytes(b[:0]), mustErr)
   117  		test.CheckIsErr(t, q.SetBytes(b[:1]), mustErr)
   118  		test.CheckIsErr(t, q.SetBytes(b[:G2Size-1]), mustErr)
   119  		test.CheckIsErr(t, q.SetBytes(b[:G2SizeCompressed]), mustErr)
   120  		test.CheckNoErr(t, q.SetBytes(b), mustOk)
   121  		test.CheckNoErr(t, q.SetBytes(append(b, 0)), mustOk)
   122  		b = p.BytesCompressed()
   123  		test.CheckIsErr(t, q.SetBytes(b[:0]), mustErr)
   124  		test.CheckIsErr(t, q.SetBytes(b[:1]), mustErr)
   125  		test.CheckIsErr(t, q.SetBytes(b[:G2SizeCompressed-1]), mustErr)
   126  		test.CheckNoErr(t, q.SetBytes(b), mustOk)
   127  		test.CheckNoErr(t, q.SetBytes(append(b, 0)), mustOk)
   128  	})
   129  	t.Run("badInfinity", func(t *testing.T) {
   130  		var badInf, p G2
   131  		badInf.SetIdentity()
   132  		b := badInf.Bytes()
   133  		b[0] |= 0x1F
   134  		err := p.SetBytes(b)
   135  		test.CheckIsErr(t, err, mustErr)
   136  		b[0] &= 0xE0
   137  		b[1] = 0xFF
   138  		err = p.SetBytes(b)
   139  		test.CheckIsErr(t, err, mustErr)
   140  	})
   141  	t.Run("badCoords", func(t *testing.T) {
   142  		bad := (&[ff.Fp2Size]byte{})[:]
   143  		for i := range bad {
   144  			bad[i] = 0xFF
   145  		}
   146  		var e ff.Fp2
   147  		_ = e[0].Random(rand.Reader)
   148  		_ = e[1].Random(rand.Reader)
   149  		good, err := e.MarshalBinary()
   150  		test.CheckNoErr(t, err, mustOk)
   151  
   152  		// bad x, good y
   153  		b := append(bad, good...)
   154  		b[0] = b[0]&0x1F | headerEncoding(0, 0, 0)
   155  		test.CheckIsErr(t, new(G2).SetBytes(b), mustErr)
   156  
   157  		// good x, bad y
   158  		b = append(good, bad...)
   159  		b[0] = b[0]&0x1F | headerEncoding(0, 0, 0)
   160  		test.CheckIsErr(t, new(G2).SetBytes(b), mustErr)
   161  	})
   162  	t.Run("noQR", func(t *testing.T) {
   163  		var x ff.Fp2
   164  		// Let x=0, so x^3+4*(u+1) = 4*(u+1), which is not QR because (u+1) is not.
   165  		b, err := x.MarshalBinary()
   166  		test.CheckNoErr(t, err, mustOk)
   167  		b[0] = b[0]&0x1F | headerEncoding(1, 0, 0)
   168  		test.CheckIsErr(t, new(G2).SetBytes(b), mustErr)
   169  	})
   170  	t.Run("notInG2", func(t *testing.T) {
   171  		// p=(0,1) is not on curve.
   172  		var x, y ff.Fp2
   173  		y[0].SetUint64(1)
   174  		bx, err := x.MarshalBinary()
   175  		test.CheckNoErr(t, err, mustOk)
   176  		by, err := y.MarshalBinary()
   177  		test.CheckNoErr(t, err, mustOk)
   178  		b := append(bx, by...)
   179  		b[0] = b[0]&0x1F | headerEncoding(0, 0, 0)
   180  		test.CheckIsErr(t, new(G2).SetBytes(b), mustErr)
   181  	})
   182  }
   183  
   184  func BenchmarkG2(b *testing.B) {
   185  	P := randomG2(b)
   186  	Q := randomG2(b)
   187  	k := randomScalar(b)
   188  	var msg, dst [4]byte
   189  	_, _ = rand.Read(msg[:])
   190  	_, _ = rand.Read(dst[:])
   191  
   192  	b.Run("Add", func(b *testing.B) {
   193  		for i := 0; i < b.N; i++ {
   194  			P.Add(P, Q)
   195  		}
   196  	})
   197  	b.Run("Mul", func(b *testing.B) {
   198  		for i := 0; i < b.N; i++ {
   199  			P.ScalarMult(k, P)
   200  		}
   201  	})
   202  	b.Run("Hash", func(b *testing.B) {
   203  		for i := 0; i < b.N; i++ {
   204  			P.Hash(msg[:], dst[:])
   205  		}
   206  	})
   207  }
   208  
   209  func TestG2Torsion(t *testing.T) {
   210  	if !G2Generator().isRTorsion() {
   211  		t.Fatalf("G2 generator is not r-torsion")
   212  	}
   213  }
   214  
   215  func TestG2Bytes(t *testing.T) {
   216  	got := new(G2)
   217  	id := new(G2)
   218  	id.SetIdentity()
   219  	g := G2Generator()
   220  	minusG := G2Generator()
   221  	minusG.Neg()
   222  
   223  	type testCase struct {
   224  		header  byte
   225  		length  int
   226  		point   *G2
   227  		toBytes func(G2) []byte
   228  	}
   229  
   230  	for i, v := range []testCase{
   231  		{headerEncoding(0, 0, 0), G2Size, randomG2(t), (G2).Bytes},
   232  		{headerEncoding(0, 0, 0), G2Size, g, (G2).Bytes},
   233  		{headerEncoding(1, 0, 0), G2SizeCompressed, g, (G2).BytesCompressed},
   234  		{headerEncoding(1, 0, 1), G2SizeCompressed, minusG, (G2).BytesCompressed},
   235  		{headerEncoding(0, 1, 0), G2Size, id, (G2).Bytes},
   236  		{headerEncoding(1, 1, 0), G2SizeCompressed, id, (G2).BytesCompressed},
   237  	} {
   238  		b := v.toBytes(*v.point)
   239  		test.CheckOk(len(b) == v.length, fmt.Sprintf("bad encoding size (case:%v point:%v b:%x)", i, v.point, b), t)
   240  		test.CheckOk(b[0]&0xE0 == v.header, fmt.Sprintf("bad encoding header (case:%v point:%v b:%x)", i, v.point, b), t)
   241  
   242  		err := got.SetBytes(b)
   243  		want := v.point
   244  		if err != nil || !got.IsEqual(want) {
   245  			test.ReportError(t, got, want, i, b)
   246  		}
   247  	}
   248  }