utils.go raw
1 package secp
2
3 import (
4 "crypto/rand"
5 "fmt"
6 )
7
8 // GeneratePrivateKey generates a random 32-byte private key
9 func GeneratePrivateKey() (privKey []byte, err error) {
10 privKey = make([]byte, PrivateKeySize)
11 if _, err = rand.Read(privKey); err != nil {
12 err = fmt.Errorf("failed to generate random key: %w", err)
13 return
14 }
15 return
16 }
17
18 // PublicKeyFromPrivate generates a public key from a private key
19 // Returns the serialized public key in compressed format
20 func PublicKeyFromPrivate(privKey []byte, compressed bool) (pubKey []byte, err error) {
21 ctx, err := NewContext(ContextSign)
22 if err != nil {
23 return
24 }
25 defer ctx.Destroy()
26
27 internalPubKey, err := ctx.CreatePublicKey(privKey)
28 if err != nil {
29 return
30 }
31
32 pubKey, err = ctx.SerializePublicKey(internalPubKey, compressed)
33 return
34 }
35
36 // SignMessage signs a 32-byte message hash with a private key
37 // Returns the signature in compact format (64 bytes)
38 func SignMessage(msgHash []byte, privKey []byte) (sig []byte, err error) {
39 ctx, err := NewContext(ContextSign)
40 if err != nil {
41 return
42 }
43 defer ctx.Destroy()
44
45 internalSig, err := ctx.Sign(msgHash, privKey)
46 if err != nil {
47 return
48 }
49
50 sig, err = ctx.SerializeSignatureCompact(internalSig)
51 return
52 }
53
54 // VerifyMessage verifies a compact signature against a message hash and serialized public key
55 func VerifyMessage(msgHash []byte, compactSig []byte, serializedPubKey []byte) (valid bool, err error) {
56 ctx, err := NewContext(ContextVerify)
57 if err != nil {
58 return
59 }
60 defer ctx.Destroy()
61
62 pubKey, err := ctx.ParsePublicKey(serializedPubKey)
63 if err != nil {
64 return
65 }
66
67 sig, err := ctx.ParseSignatureCompact(compactSig)
68 if err != nil {
69 return
70 }
71
72 valid, err = ctx.Verify(msgHash, sig, pubKey)
73 return
74 }
75
76 // SignMessageDER signs a message and returns DER-encoded signature
77 func SignMessageDER(msgHash []byte, privKey []byte) (derSig []byte, err error) {
78 ctx, err := NewContext(ContextSign)
79 if err != nil {
80 return
81 }
82 defer ctx.Destroy()
83
84 internalSig, err := ctx.Sign(msgHash, privKey)
85 if err != nil {
86 return
87 }
88
89 derSig, err = ctx.SerializeSignatureDER(internalSig)
90 return
91 }
92
93 // VerifyMessageDER verifies a DER-encoded signature
94 func VerifyMessageDER(msgHash []byte, derSig []byte, serializedPubKey []byte) (valid bool, err error) {
95 ctx, err := NewContext(ContextVerify)
96 if err != nil {
97 return
98 }
99 defer ctx.Destroy()
100
101 pubKey, err := ctx.ParsePublicKey(serializedPubKey)
102 if err != nil {
103 return
104 }
105
106 sig, err := ctx.ParseSignatureDER(derSig)
107 if err != nil {
108 return
109 }
110
111 valid, err = ctx.Verify(msgHash, sig, pubKey)
112 return
113 }
114
115 // SchnorrSign signs a message with Schnorr signature (BIP-340)
116 // Returns 64-byte Schnorr signature
117 func SchnorrSign(msgHash []byte, privKey []byte, auxRand []byte) (sig []byte, err error) {
118 ctx, err := NewContext(ContextSign)
119 if err != nil {
120 return
121 }
122 defer ctx.Destroy()
123
124 keypair, err := ctx.CreateKeypair(privKey)
125 if err != nil {
126 return
127 }
128
129 sig, err = ctx.SchnorrSign(msgHash, keypair, auxRand)
130 return
131 }
132
133 // SchnorrVerifyWithPubKey verifies a Schnorr signature (BIP-340)
134 // xonlyPubKey should be 32 bytes
135 func SchnorrVerifyWithPubKey(msgHash []byte, sig []byte, xonlyPubKey []byte) (valid bool, err error) {
136 ctx, err := NewContext(ContextVerify)
137 if err != nil {
138 return
139 }
140 defer ctx.Destroy()
141
142 valid, err = ctx.SchnorrVerify(sig, msgHash, xonlyPubKey)
143 return
144 }
145
146 // XOnlyPubKeyFromPrivate generates an x-only public key from a private key
147 func XOnlyPubKeyFromPrivate(privKey []byte) (xonly []byte, pkParity int32, err error) {
148 ctx, err := NewContext(ContextSign)
149 if err != nil {
150 return
151 }
152 defer ctx.Destroy()
153
154 keypair, err := ctx.CreateKeypair(privKey)
155 if err != nil {
156 return
157 }
158
159 var xonlyInternal XOnlyPublicKey
160 xonlyInternal, pkParity, err = ctx.KeypairXOnlyPub(keypair)
161 if err != nil {
162 return
163 }
164
165 xonly = xonlyInternal[:]
166 return
167 }
168
169 // ComputeECDH computes an ECDH shared secret
170 func ComputeECDH(serializedPubKey []byte, privKey []byte) (secret []byte, err error) {
171 ctx, err := NewContext(ContextSign)
172 if err != nil {
173 return
174 }
175 defer ctx.Destroy()
176
177 pubKey, err := ctx.ParsePublicKey(serializedPubKey)
178 if err != nil {
179 return
180 }
181
182 secret, err = ctx.ECDH(pubKey, privKey)
183 return
184 }
185
186 // SignRecoverableCompact signs a message with a recoverable signature
187 // Returns compact signature (64 bytes) and recovery ID
188 func SignRecoverableCompact(msgHash []byte, privKey []byte) (sig []byte, recID int32, err error) {
189 ctx, err := NewContext(ContextSign)
190 if err != nil {
191 return
192 }
193 defer ctx.Destroy()
194
195 recSig, err := ctx.SignRecoverable(msgHash, privKey)
196 if err != nil {
197 return
198 }
199
200 sig, recID, err = ctx.SerializeRecoverableSignatureCompact(recSig)
201 return
202 }
203
204 // RecoverPubKey recovers a public key from a recoverable signature
205 // Returns serialized public key in compressed format
206 func RecoverPubKey(msgHash []byte, compactSig []byte, recID int32, compressed bool) (pubKey []byte, err error) {
207 ctx, err := NewContext(ContextSign)
208 if err != nil {
209 return
210 }
211 defer ctx.Destroy()
212
213 recSig, err := ctx.ParseRecoverableSignatureCompact(compactSig, recID)
214 if err != nil {
215 return
216 }
217
218 recoveredPubKey, err := ctx.Recover(recSig, msgHash)
219 if err != nil {
220 return
221 }
222
223 pubKey, err = ctx.SerializePublicKey(recoveredPubKey, compressed)
224 return
225 }
226
227 // ValidatePrivateKey checks if a private key is valid
228 func ValidatePrivateKey(privKey []byte) (valid bool, err error) {
229 if len(privKey) != PrivateKeySize {
230 err = fmt.Errorf("private key must be %d bytes", PrivateKeySize)
231 return
232 }
233
234 ctx, err := NewContext(ContextSign)
235 if err != nil {
236 return
237 }
238 defer ctx.Destroy()
239
240 _, err = ctx.CreatePublicKey(privKey)
241 if err != nil {
242 valid = false
243 err = nil
244 return
245 }
246
247 valid = true
248 return
249 }
250
251 // IsPublicKeyValid checks if a serialized public key is valid
252 func IsPublicKeyValid(serializedPubKey []byte) (valid bool, err error) {
253 ctx, err := NewContext(ContextVerify)
254 if err != nil {
255 return
256 }
257 defer ctx.Destroy()
258
259 _, err = ctx.ParsePublicKey(serializedPubKey)
260 if err != nil {
261 valid = false
262 err = nil
263 return
264 }
265
266 valid = true
267 return
268 }
269