1 // Copyright (c) 2013-2016 The btcsuite developers
2 // Copyright (c) 2015-2023 The Decred developers
3 // Use of this source code is governed by an ISC
4 // license that can be found in the LICENSE file.
5 6 package secp256k1
7 8 import (
9 "bytes"
10 "crypto/rand"
11 "errors"
12 "math/big"
13 "testing"
14 15 "next.orly.dev/pkg/nostr/utils"
16 )
17 18 // TestGenerateSecretKey ensures the key generation works as expected.
19 func TestGenerateSecretKey(t *testing.T) {
20 sec, err := GenerateSecretKey()
21 if err != nil {
22 t.Errorf("failed to generate secret key: %s", err)
23 return
24 }
25 pub := sec.PubKey()
26 if !isOnCurve(&pub.x, &pub.y) {
27 t.Error("public key is not on the curve")
28 }
29 }
30 31 // TestGenerateSecretKeyFromRand ensures generating a secret key from a random
32 // entropy source works as expected.
33 func TestGenerateSecretKeyFromRand(t *testing.T) {
34 sec, err := GenerateSecretKeyFromRand(rand.Reader)
35 if err != nil {
36 t.Errorf("failed to generate secret key: %s", err)
37 return
38 }
39 pub := sec.PubKey()
40 if !isOnCurve(&pub.x, &pub.y) {
41 t.Error("public key is not on the curve")
42 }
43 }
44 45 // mockSecretKeyReaderFunc is an adapter to allow the use of an ordinary
46 // function as an io.Reader.
47 type mockSecretKeyReaderFunc func([]byte) (int, error)
48 49 // Read calls the function with the provided parameter and returns the result.
50 func (f mockSecretKeyReaderFunc) Read(p []byte) (int, error) {
51 return f(p)
52 }
53 54 // TestGenerateSecretKeyCorners ensures random values that secret key
55 // generation correctly handles entropy values that are invalid for use as
56 // secret keys by creating a fake source of randomness to inject known bad
57 // values.
58 func TestGenerateSecretKeyCorners(t *testing.T) {
59 // Create a mock reader that returns the following sequence of values:
60 // 1st invocation: 0
61 // 2nd invocation: The curve order
62 // 3rd invocation: The curve order + 1
63 // 4th invocation: 1 (32-byte big endian)
64 oneModN := hexToModNScalar("01")
65 var numReads int
66 mockReader := mockSecretKeyReaderFunc(
67 func(p []byte) (int, error) {
68 numReads++
69 switch numReads {
70 case 1:
71 return copy(p, bytes.Repeat([]byte{0x00}, len(p))), nil
72 case 2:
73 return copy(p, curveParams.N.Bytes()), nil
74 case 3:
75 nPlusOne := new(big.Int).Add(curveParams.N, big.NewInt(1))
76 return copy(p, nPlusOne.Bytes()), nil
77 }
78 oneModNBytes := oneModN.Bytes()
79 return copy(p, oneModNBytes[:]), nil
80 },
81 )
82 // Generate a secret key using the mock reader and ensure the resulting key
83 // is the expected one. It should be the value "1" since the other values
84 // the sequence produces are invalid and thus should be rejected.
85 sec, err := GenerateSecretKeyFromRand(mockReader)
86 if err != nil {
87 t.Errorf("failed to generate secret key: %s", err)
88 return
89 }
90 if !sec.Key.Equals(oneModN) {
91 t.Fatalf(
92 "unexpected secret key -- got: %x, want %x", sec.Serialize(),
93 oneModN.Bytes(),
94 )
95 }
96 }
97 98 // TestGenerateSecretKeyError ensures the secret key generation properly
99 // handles errors when attempting to read from the source of randomness.
100 func TestGenerateSecretKeyError(t *testing.T) {
101 // Create a mock reader that returns an error.
102 errDisabled := errors.New("disabled")
103 mockReader := mockSecretKeyReaderFunc(
104 func(p []byte) (int, error) {
105 return 0, errDisabled
106 },
107 )
108 // Generate a secret key using the mock reader and ensure the expected
109 // error is returned.
110 _, err := GenerateSecretKeyFromRand(mockReader)
111 if !errors.Is(err, errDisabled) {
112 t.Fatalf("mismatched err -- got %v, want %v", err, errDisabled)
113 return
114 }
115 }
116 117 // TestSecKeys ensures a secret key created from bytes produces both the
118 // correct associated public key as well serializes back to the original bytes.
119 func TestSecKeys(t *testing.T) {
120 tests := []struct {
121 name string
122 sec string // hex encoded secret key to test
123 pub string // expected hex encoded serialized compressed public key
124 }{
125 {
126 name: "random secret key 1",
127 sec: "eaf02ca348c524e6392655ba4d29603cd1a7347d9d65cfe93ce1ebffdca22694",
128 pub: "025ceeba2ab4a635df2c0301a3d773da06ac5a18a7c3e0d09a795d7e57d233edf1",
129 }, {
130 name: "random secret key 2",
131 sec: "24b860d0651db83feba821e7a94ba8b87162665509cefef0cbde6a8fbbedfe7c",
132 pub: "032a6e51bf218085647d330eac2fafaeee07617a777ad9e8e7141b4cdae92cb637",
133 },
134 }
135 136 for _, test := range tests {
137 // Parse test data.
138 secKeyBytes := hexToBytes(test.sec)
139 wantPubKeyBytes := hexToBytes(test.pub)
140 141 sec := SecKeyFromBytes(secKeyBytes)
142 pub := sec.PubKey()
143 144 serializedPubKey := pub.SerializeCompressed()
145 if !utils.FastEqual(serializedPubKey, wantPubKeyBytes) {
146 t.Errorf(
147 "%s unexpected serialized public key - got: %x, want: %x",
148 test.name, serializedPubKey, wantPubKeyBytes,
149 )
150 }
151 152 serializedSecKey := sec.Serialize()
153 if !utils.FastEqual(serializedSecKey, secKeyBytes) {
154 t.Errorf(
155 "%s unexpected serialized secret key - got: %x, want: %x",
156 test.name, serializedSecKey, secKeyBytes,
157 )
158 }
159 }
160 }
161 162 // TestSecretKeyZero ensures that zeroing a secret key clears the memory
163 // associated with it.
164 func TestSecretKeyZero(t *testing.T) {
165 // Create a new secret key and zero the initial key material that is now
166 // copied into the secret key.
167 key := new(ModNScalar).SetHex("eaf02ca348c524e6392655ba4d29603cd1a7347d9d65cfe93ce1ebffdca22694")
168 secKey := NewSecretKey(key)
169 key.Zero()
170 // Ensure the secret key is non zero.
171 if secKey.Key.IsZero() {
172 t.Fatal("secret key is zero when it should be non zero")
173 }
174 // Zero the secret key and ensure it was properly zeroed.
175 secKey.Zero()
176 if !secKey.Key.IsZero() {
177 t.Fatal("secret key is non zero when it should be zero")
178 }
179 }
180