secp256k1_test_support.mx raw
1 package secp256k1
2
3 import "errors"
4
5 // TestSignedRadix16 verifies the constant-time ScalarMult handles the
6 // digits[64]==1 edge case. The scalar n-1 (group order minus one) has top
7 // two nibbles 0xF/0xF, so carry propagates all the way through digit 63
8 // into digit 64 during scalarToSignedRadix16 decomposition.
9 //
10 // (n-1)*G == -G, which has the same x-coordinate as G itself.
11 // PubKeyFromSecKey(n-1) must therefore equal PubKeyFromSecKey(1) == Gx.
12 func TestSignedRadix16() error {
13 // n-1 in big-endian (32 bytes): FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140
14 nMinus1 := [32]byte{
15 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
16 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
17 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B,
18 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x40,
19 }
20
21 // Verify digits[64] == 1 for n-1.
22 k := scalarFromBytes(nMinus1[:])
23 digits := scalarToSignedRadix16(k)
24 if digits[64] != 1 {
25 return errors.New("scalarToSignedRadix16: expected digits[64]==1 for scalar n-1")
26 }
27
28 // Verify all digits[0..63] are in [-8, 7].
29 for i := 0; i < 64; i++ {
30 if digits[i] < -8 || digits[i] > 7 {
31 return errors.New("scalarToSignedRadix16: digit out of range")
32 }
33 }
34
35 // (n-1)*G == -G, x-coordinate equals Gx.
36 // PubKeyFromSecKey returns the x-coordinate of seckey*G.
37 pubNM1, ok := PubKeyFromSecKey(nMinus1)
38 if !ok {
39 return errors.New("PubKeyFromSecKey(n-1) failed")
40 }
41
42 // seckey 1: 1*G = G.
43 var one [32]byte
44 one[31] = 1
45 pub1, ok := PubKeyFromSecKey(one)
46 if !ok {
47 return errors.New("PubKeyFromSecKey(1) failed")
48 }
49
50 if pubNM1 != pub1 {
51 return errors.New("PubKeyFromSecKey(n-1) != PubKeyFromSecKey(1): x-coordinate mismatch")
52 }
53
54 // Also verify against the known Gx constant.
55 gx := feToBytes(G().X)
56 if pubNM1 != gx {
57 return errors.New("PubKeyFromSecKey(n-1) != Gx: wrong x-coordinate")
58 }
59
60 return nil
61 }
62