verify_wasm.go raw
1 //go:build js || wasm || tinygo || wasm32
2
3 package p256k1
4
5 import (
6 "sync"
7 "unsafe"
8 )
9
10 // WASM-compatible secp256k1 verification types and functions
11 // These use the 32-bit field/scalar representations
12
13 // secp256k1_context represents a context
14 type secp256k1_context struct {
15 ecmult_gen_ctx secp256k1_ecmult_gen_context
16 declassify int
17 }
18
19 type secp256k1_ecmult_gen_context struct {
20 built int
21 }
22
23 // secp256k1_declassify declassifies data (no-op in non-VERIFY builds)
24 func secp256k1_declassify(ctx *secp256k1_context, p unsafe.Pointer, len uintptr) {
25 // No-op
26 }
27
28 // secp256k1_xonly_pubkey represents an x-only public key
29 type secp256k1_xonly_pubkey struct {
30 data [32]byte
31 }
32
33 // secp256k1_scalar wraps 32-bit Scalar for C-like API
34 type secp256k1_scalar struct {
35 s Scalar
36 }
37
38 // secp256k1_fe wraps 32-bit FieldElement for C-like API
39 type secp256k1_fe struct {
40 fe FieldElement
41 }
42
43 // secp256k1_ge represents a group element in affine coordinates
44 type secp256k1_ge struct {
45 x, y secp256k1_fe
46 infinity int
47 }
48
49 // secp256k1_gej represents a group element in Jacobian coordinates
50 type secp256k1_gej struct {
51 x, y, z secp256k1_fe
52 infinity int
53 }
54
55 // ============================================================================
56 // SCALAR OPERATIONS
57 // ============================================================================
58
59 // secp256k1_scalar_set_b32 sets scalar from 32 bytes
60 func secp256k1_scalar_set_b32(r *secp256k1_scalar, b32 []byte, overflow *int) {
61 of := r.s.setB32(b32)
62 if overflow != nil {
63 if of {
64 *overflow = 1
65 } else {
66 *overflow = 0
67 }
68 }
69 }
70
71 // secp256k1_scalar_negate negates a scalar
72 func secp256k1_scalar_negate(r *secp256k1_scalar, a *secp256k1_scalar) {
73 r.s = a.s
74 r.s.negate(&r.s)
75 }
76
77 // secp256k1_scalar_is_zero checks if scalar is zero
78 func secp256k1_scalar_is_zero(a *secp256k1_scalar) bool {
79 return a.s.isZero()
80 }
81
82 // ============================================================================
83 // FIELD OPERATIONS
84 // ============================================================================
85
86 // secp256k1_fe_set_b32_limit sets field element from 32 bytes, checking limit
87 func secp256k1_fe_set_b32_limit(r *secp256k1_fe, b32 []byte) bool {
88 return r.fe.setB32(b32) == nil
89 }
90
91 // secp256k1_fe_normalize_var normalizes field element
92 func secp256k1_fe_normalize_var(r *secp256k1_fe) {
93 r.fe.normalize()
94 }
95
96 // secp256k1_fe_is_odd checks if field element is odd
97 func secp256k1_fe_is_odd(a *secp256k1_fe) bool {
98 return a.fe.n[0]&1 == 1
99 }
100
101 // secp256k1_fe_get_b32 gets 32 bytes from field element
102 func secp256k1_fe_get_b32(r []byte, a *secp256k1_fe) {
103 a.fe.getB32(r)
104 }
105
106 // secp256k1_fe_set_int sets field element to integer
107 func secp256k1_fe_set_int(r *secp256k1_fe, a int) {
108 r.fe.setInt(a)
109 }
110
111 // secp256k1_fe_equal compares two field elements
112 func secp256k1_fe_equal(a, b *secp256k1_fe) bool {
113 // Compare all 10 limbs
114 for i := 0; i < 10; i++ {
115 if a.fe.n[i] != b.fe.n[i] {
116 return false
117 }
118 }
119 return true
120 }
121
122 // ============================================================================
123 // GROUP OPERATIONS
124 // ============================================================================
125
126 // secp256k1_ge_is_infinity checks if group element is infinity
127 func secp256k1_ge_is_infinity(a *secp256k1_ge) bool {
128 return a.infinity != 0
129 }
130
131 // secp256k1_gej_set_ge sets Jacobian from affine
132 func secp256k1_gej_set_ge(r *secp256k1_gej, a *secp256k1_ge) {
133 r.infinity = a.infinity
134 r.x = a.x
135 r.y = a.y
136 secp256k1_fe_set_int(&r.z, 1)
137 }
138
139 // secp256k1_ge_set_gej_var sets affine from Jacobian
140 func secp256k1_ge_set_gej_var(r *secp256k1_ge, a *secp256k1_gej) {
141 if a.infinity != 0 {
142 r.infinity = 1
143 return
144 }
145 r.infinity = 0
146
147 // Convert from Jacobian to affine
148 var gej GroupElementJacobian
149 gej.x = a.x.fe
150 gej.y = a.y.fe
151 gej.z = a.z.fe
152 gej.infinity = a.infinity != 0
153
154 var ge GroupElementAffine
155 ge.setGEJ(&gej)
156
157 r.x.fe = ge.x
158 r.y.fe = ge.y
159 }
160
161 // secp256k1_xonly_pubkey_load loads x-only public key
162 func secp256k1_xonly_pubkey_load(ctx *secp256k1_context, ge *secp256k1_ge, pubkey *secp256k1_xonly_pubkey) bool {
163 // Reconstruct point from X coordinate (x-only pubkey only has X)
164 var x FieldElement
165 if err := x.setB32(pubkey.data[:]); err != nil {
166 return false
167 }
168
169 // Try to recover Y coordinate (use even Y for BIP-340)
170 var gep GroupElementAffine
171 if !gep.setXOVar(&x, false) {
172 return false
173 }
174
175 ge.x.fe = gep.x
176 ge.y.fe = gep.y
177 if gep.infinity {
178 ge.infinity = 1
179 } else {
180 ge.infinity = 0
181 }
182
183 return true
184 }
185
186 // secp256k1_ecmult performs scalar multiplication: r = a*s + G*t
187 func secp256k1_ecmult(r *secp256k1_gej, a *secp256k1_gej, na *secp256k1_scalar, ng *secp256k1_scalar) {
188 // Convert types
189 var aJac GroupElementJacobian
190 aJac.x = a.x.fe
191 aJac.y = a.y.fe
192 aJac.z = a.z.fe
193 aJac.infinity = a.infinity != 0
194
195 var aAff GroupElementAffine
196 aAff.setGEJ(&aJac)
197
198 var result GroupElementJacobian
199
200 // Use GLV/Strauss/wNAF for the scalar multiplication
201 // r = na*a + ng*G
202 if !na.s.isZero() && !ng.s.isZero() {
203 // Both scalars non-zero: compute na*a, then add ng*G
204 ecmultStraussWNAFGLV(&result, &aAff, &na.s)
205
206 var gMul GroupElementJacobian
207 ecmultGenGLV(&gMul, &ng.s)
208
209 result.addVar(&result, &gMul)
210 } else if !na.s.isZero() {
211 // Only na*a
212 ecmultStraussWNAFGLV(&result, &aAff, &na.s)
213 } else if !ng.s.isZero() {
214 // Only ng*G
215 ecmultGenGLV(&result, &ng.s)
216 } else {
217 result.infinity = true
218 }
219
220 // Convert back
221 r.x.fe = result.x
222 r.y.fe = result.y
223 r.z.fe = result.z
224 if result.infinity {
225 r.infinity = 1
226 } else {
227 r.infinity = 0
228 }
229 }
230
231 // ============================================================================
232 // SCHNORR CHALLENGE
233 // ============================================================================
234
235 // secp256k1_schnorrsig_challenge computes BIP-340 challenge
236 func secp256k1_schnorrsig_challenge(e *secp256k1_scalar, r32 []byte, msg []byte, msglen int, pk32 []byte) {
237 // TaggedHash("BIP0340/challenge", r32 || pk32 || msg)
238 var input []byte
239 input = append(input, r32[:32]...)
240 input = append(input, pk32[:32]...)
241 input = append(input, msg[:msglen]...)
242
243 hash := TaggedHash([]byte("BIP0340/challenge"), input)
244
245 var overflow int
246 secp256k1_scalar_set_b32(e, hash[:], &overflow)
247 }
248
249 // ============================================================================
250 // SCHNORR VERIFICATION
251 // ============================================================================
252
253 // Global precomputed context for Schnorr verification
254 var (
255 schnorrVerifyContext *secp256k1_context
256 schnorrVerifyContextOnce sync.Once
257 )
258
259 // initSchnorrVerifyContext initializes the global Schnorr verification context
260 func initSchnorrVerifyContext() {
261 schnorrVerifyContext = &secp256k1_context{
262 ecmult_gen_ctx: secp256k1_ecmult_gen_context{built: 1},
263 declassify: 0,
264 }
265 }
266
267 // getSchnorrVerifyContext returns the precomputed Schnorr verification context
268 func getSchnorrVerifyContext() *secp256k1_context {
269 schnorrVerifyContextOnce.Do(initSchnorrVerifyContext)
270 return schnorrVerifyContext
271 }
272
273 // secp256k1_schnorrsig_verify verifies a BIP-340 Schnorr signature
274 func secp256k1_schnorrsig_verify(ctx *secp256k1_context, sig64 []byte, msg []byte, msglen int, pubkey *secp256k1_xonly_pubkey) int {
275 var s secp256k1_scalar
276 var e secp256k1_scalar
277 var rj secp256k1_gej
278 var pk secp256k1_ge
279 var pkj secp256k1_gej
280 var rx secp256k1_fe
281 var r secp256k1_ge
282 var overflow int
283
284 if ctx == nil {
285 return 0
286 }
287 if sig64 == nil {
288 return 0
289 }
290 if msg == nil && msglen != 0 {
291 return 0
292 }
293 if pubkey == nil {
294 return 0
295 }
296
297 // Check signature length
298 if len(sig64) < 64 {
299 return 0
300 }
301
302 if !secp256k1_fe_set_b32_limit(&rx, sig64[:32]) {
303 return 0
304 }
305
306 secp256k1_scalar_set_b32(&s, sig64[32:], &overflow)
307 if overflow != 0 {
308 return 0
309 }
310
311 if !secp256k1_xonly_pubkey_load(ctx, &pk, pubkey) {
312 return 0
313 }
314
315 // Compute e - extract normalized pk.x bytes efficiently
316 secp256k1_fe_normalize_var(&pk.x)
317 var pkXBytes [32]byte
318 secp256k1_fe_get_b32(pkXBytes[:], &pk.x)
319 secp256k1_schnorrsig_challenge(&e, sig64[:32], msg, msglen, pkXBytes[:])
320
321 // Compute rj = s*G + (-e)*pkj
322 secp256k1_scalar_negate(&e, &e)
323 secp256k1_gej_set_ge(&pkj, &pk)
324 secp256k1_ecmult(&rj, &pkj, &e, &s)
325
326 secp256k1_ge_set_gej_var(&r, &rj)
327 if secp256k1_ge_is_infinity(&r) {
328 return 0
329 }
330
331 // Normalize r.y and check if odd
332 secp256k1_fe_normalize_var(&r.y)
333 if secp256k1_fe_is_odd(&r.y) {
334 return 0
335 }
336
337 // Normalize r.x and rx, then compare
338 secp256k1_fe_normalize_var(&r.x)
339 secp256k1_fe_normalize_var(&rx)
340
341 if !secp256k1_fe_equal(&rx, &r.x) {
342 return 0
343 }
344
345 return 1
346 }
347