-
-
Notifications
You must be signed in to change notification settings - Fork 126
Expand file tree
/
Copy pathindex.ts
More file actions
1670 lines (1622 loc) · 69.2 KB
/
Copy pathindex.ts
File metadata and controls
1670 lines (1622 loc) · 69.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*! noble-secp256k1 - MIT License (c) 2019 Paul Miller (paulmillr.com) */
/**
* 5KB JS implementation of secp256k1 ECDSA / Schnorr signatures & ECDH.
* Compliant with RFC6979 & BIP340.
* @module
*/
/**
* Curve params from SEC 2 v2 §2.4.1.
* secp256k1 is a short Weierstrass / Koblitz curve with equation
* `y² == x³ + ax + b`.
* * P = `2n**256n - 2n**32n - 977n` // field over which calculations are done
* * N = `2n**256n - 0x14551231950b75fc4402da1732fc9bebfn` // group order, amount of curve points
* * h = `1n` // cofactor
* * a = `0n` // equation param
* * b = `7n` // equation param
* * Gx, Gy are coordinates of Generator / base point
*/
const freeze = Object.freeze;
const P = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2fn;
const N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n;
const Gx = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798n;
const Gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8n;
// Mirror noble-curves: Point.CURVE() returns shared params, but those params must stay frozen so
// callers cannot mutate them out from under the arithmetic constants captured below.
const secp256k1_CURVE: WeierstrassOpts<bigint> = freeze({
p: P,
n: N,
h: 1n,
a: 0n,
b: 7n,
Gx,
Gy,
});
// 32-byte field / scalar width, and the SHA-256 / HMAC-DRBG output width used
// by the RFC6979 paths here.
const L = 32;
/** Alias to Uint8Array. */
export type Bytes = Uint8Array;
// ## TS compatibility types
// -------------------------
// Type-level only: nothing here survives compilation. Skip to "End of TS
// compatibility types" for the actual crypto code.
/**
* Uint8Array API type helpers for old + new TypeScript.
*
* TS 5.6 has `Uint8Array`, while TS 5.9+ made it generic `Uint8Array<ArrayBuffer>`.
* We can't use specific return type, because TS 5.6 will error.
* We can't use generic return type, because most TS 5.9 software will expect specific type.
*
* Maps typed-array input leaves to broad forms.
* These are compatibility adapters, not ownership guarantees.
*
* - `TArg` keeps byte inputs broad.
* - `TRet` marks byte outputs for TS 5.6 and TS 5.9+ compatibility.
*/
export type TypedArg<T> = T extends BigInt64Array
? BigInt64Array
: T extends BigUint64Array
? BigUint64Array
: T extends Float32Array
? Float32Array
: T extends Float64Array
? Float64Array
: T extends Int16Array
? Int16Array
: T extends Int32Array
? Int32Array
: T extends Int8Array
? Int8Array
: T extends Uint16Array
? Uint16Array
: T extends Uint32Array
? Uint32Array
: T extends Uint8ClampedArray
? Uint8ClampedArray
: T extends Uint8Array
? Uint8Array
: never;
/** Maps typed-array output leaves to narrow TS-compatible forms. */
export type TypedRet<T> = T extends BigInt64Array
? ReturnType<typeof BigInt64Array.of>
: T extends BigUint64Array
? ReturnType<typeof BigUint64Array.of>
: T extends Float32Array
? ReturnType<typeof Float32Array.of>
: T extends Float64Array
? ReturnType<typeof Float64Array.of>
: T extends Int16Array
? ReturnType<typeof Int16Array.of>
: T extends Int32Array
? ReturnType<typeof Int32Array.of>
: T extends Int8Array
? ReturnType<typeof Int8Array.of>
: T extends Uint16Array
? ReturnType<typeof Uint16Array.of>
: T extends Uint32Array
? ReturnType<typeof Uint32Array.of>
: T extends Uint8ClampedArray
? ReturnType<typeof Uint8ClampedArray.of>
: T extends Uint8Array
? ReturnType<typeof Uint8Array.of>
: never;
/** Recursively adapts byte-carrying API input types. See {@link TypedArg}. */
export type TArg<T> =
| T
| ([TypedArg<T>] extends [never]
? T extends (...args: infer A) => infer R
? ((...args: { [K in keyof A]: TRet<A[K]> }) => TArg<R>) & {
[K in keyof T]: T[K] extends (...args: any) => any ? T[K] : TArg<T[K]>;
}
: T extends [infer A, ...infer R]
? [TArg<A>, ...{ [K in keyof R]: TArg<R[K]> }]
: T extends readonly [infer A, ...infer R]
? readonly [TArg<A>, ...{ [K in keyof R]: TArg<R[K]> }]
: T extends (infer A)[]
? TArg<A>[]
: T extends readonly (infer A)[]
? readonly TArg<A>[]
: T extends Promise<infer A>
? Promise<TArg<A>>
: T extends object
? { [K in keyof T]: TArg<T[K]> }
: T
: TypedArg<T>);
/** Recursively adapts byte-carrying API output types. See {@link TypedArg}. */
export type TRet<T> = T extends unknown
? T &
([TypedRet<T>] extends [never]
? T extends (...args: infer A) => infer R
? ((...args: { [K in keyof A]: TArg<A[K]> }) => TRet<R>) & {
[K in keyof T]: T[K] extends (...args: any) => any ? T[K] : TRet<T[K]>;
}
: T extends [infer A, ...infer R]
? [TRet<A>, ...{ [K in keyof R]: TRet<R[K]> }]
: T extends readonly [infer A, ...infer R]
? readonly [TRet<A>, ...{ [K in keyof R]: TRet<R[K]> }]
: T extends (infer A)[]
? TRet<A>[]
: T extends readonly (infer A)[]
? readonly TRet<A>[]
: T extends Promise<infer A>
? Promise<TRet<A>>
: T extends object
? { [K in keyof T]: TRet<T[K]> }
: T
: TypedRet<T>)
: never;
// ## End of TS compatibility types
// --------------------------------
/** Signature instance, which allows recovering pubkey from it. */
export type RecoveredSignature = Signature & { recovery: number };
/** Weierstrass elliptic curve options. */
export type WeierstrassOpts<T> = Readonly<{
p: bigint;
n: bigint;
h: bigint;
a: T;
b: T;
Gx: T;
Gy: T;
}>;
// Helpers and Precomputes sections are reused between libraries
// ## Helpers
// ----------
/** Checks if something is Uint8Array. Be careful: nodejs Buffer will return true. */
const isBytes = (a: unknown): a is Uint8Array => {
// Plain `instanceof Uint8Array` is too strict for some Buffer / proxy / cross-realm cases.
// The fallback still requires a real ArrayBuffer view, so plain
// JSON-deserialized `{ constructor: ... }` spoofing is rejected, and
// `BYTES_PER_ELEMENT === 1` keeps the fallback on byte-oriented views.
return (
a instanceof Uint8Array ||
(ArrayBuffer.isView(a) &&
a.constructor.name === 'Uint8Array' &&
(a as Uint8Array).BYTES_PER_ELEMENT === 1)
);
};
/** Asserts something is Uint8Array. */
const abytes = (value: TArg<Uint8Array>, length?: number, title: string = ''): TRet<Uint8Array> => {
// Success path first: this runs at the start of every update() / digestInto(), and the
// common `abytes(data)` form must not pay for length handling it does not use.
if (isBytes(value) && (length === undefined || value.length === length))
return value as TRet<Uint8Array>;
// Error path: recompute freely to build the exact message.
const bytes = isBytes(value);
const ofLen = length !== undefined ? ` of length ${length}` : '';
const got = bytes ? `length=${value.length}` : `type=${typeof value}`;
const message = (title ? `"${title}" ` : '') + 'expected Uint8Array' + ofLen + ', got ' + got;
if (!bytes) throw new TypeError(message);
throw new RangeError(message);
};
// Signing can retain the message across hash callbacks / awaits, and Schnorr hashes it more than
// once. Take one owned snapshot so caller mutation cannot change the signing transcript.
// cloneBytes expects an already-validated view; snapshotBytes validates, then copies.
const cloneBytes = (value: Uint8Array): TRet<Uint8Array> => Uint8Array.from(value);
const snapshotBytes = (value: TArg<Uint8Array>, title: string, length?: number): TRet<Uint8Array> =>
cloneBytes(abytes(value, length, title));
// Callers keep values non-negative and within the requested width; padStart() won't truncate over-wide inputs.
const padh = (n: number | bigint, pad: number) => n.toString(16).padStart(pad, '0');
/** Convert byte array to hex string. */
const bytesToHex = (bytes: TArg<Uint8Array>): string => {
let hex = '';
for (const byte of abytes(bytes)) hex += padh(byte, 2);
return hex;
};
/** Convert hex string to byte array. */
const hexToBytes = (hex: string): TRet<Uint8Array> => {
const e = 'hex invalid'; // Strict ASCII hex only, with one generic error for parse failures.
if (typeof hex !== 'string') throw new TypeError(e);
if (hex.length % 2 || !/^[\da-f]*$/i.test(hex)) throw new RangeError(e);
const array = new Uint8Array(hex.length / 2);
for (let ai = 0, hi = 0; ai < array.length; ai++, hi += 2) {
const n1 = hex.charCodeAt(hi);
const n2 = hex.charCodeAt(hi + 1);
// Regex guarantees ASCII. For 0..9/A..F/a..f, this maps char codes to 0..15.
array[ai] = ((n1 & 15) + (n1 >> 6) * 9) * 16 + (n2 & 15) + (n2 >> 6) * 9;
}
return array as TRet<Uint8Array>;
};
declare const globalThis: Record<string, any> | undefined; // Typescript symbol present in browsers
// WebCrypto is available in all modern environments
const subtle = () => {
const s = globalThis?.crypto?.subtle;
if (s) return s;
throw new Error('crypto.subtle must be defined, consider polyfill');
};
/** Copies several Uint8Arrays into one. */
const concatBytes = (...arrays: TArg<Uint8Array[]>): TRet<Uint8Array> => {
let sum = 0;
for (const a of arrays) sum += abytes(a).length;
const res = new Uint8Array(sum);
let pad = 0;
for (const a of arrays) {
res.set(a, pad);
pad += a.length;
}
return res;
};
/**
* WebCrypto OS-level CSPRNG (random number generator).
* Will throw when not available; large-request ceilings are delegated to getRandomValues().
*/
const randomBytes = (len: number = L): TRet<Uint8Array> => {
const c = globalThis?.crypto;
if (typeof c?.getRandomValues !== 'function')
throw new Error('crypto.getRandomValues must be defined, consider polyfill');
return c.getRandomValues(new Uint8Array(len)) as TRet<Uint8Array>;
};
const big = BigInt;
const arange = (n: bigint, min: bigint, max: bigint, msg = 'bad number: out of range'): bigint => {
if (typeof n !== 'bigint') throw new TypeError(msg);
if (min <= n && n < max) return n;
throw new RangeError(msg);
};
/** Canonical modular reduction. Callers must provide a positive modulus. */
const M = (a: bigint, b: bigint = P) => ((a %= b) >= 0n ? a : b + a);
const modN = (a: bigint) => M(a, N);
/** Modular inversion using extended euclidean GCD. Variable-time (non-CT). */
const invert = (number: bigint, modulo: bigint): bigint => {
if (number === 0n) throw new Error('invert: expected non-zero number');
// modulo = 1 is the zero ring: gcd(x, 1) = 1 makes the loop below "succeed" and return the
// useless inverse 0. Reject it.
if (modulo <= 1n) throw new Error('invert: expected modulus > 1, got ' + modulo);
// This is variable-time: the loop count depends on `number`.
let a = M(number, modulo);
let b = modulo;
// Only the Bézout coefficient of `number` (x/u chain) is tracked; the coefficient of `modulo`
// never affects the output, so it is not computed.
// prettier-ignore
let x = 0n, u = 1n;
while (a !== 0n) {
const q = b / a;
const r = b - a * q;
const m = x - u * q;
// prettier-ignore
b = a, a = r, x = u, u = m;
}
const gcd = b;
if (gcd !== 1n) throw new Error('invert: does not exist');
return M(x, modulo);
};
const _hash = (name: string) => {
// @ts-ignore
const fn = hashes[name];
if (typeof fn !== 'function') throw new Error('hashes.' + name + ' not set');
return fn;
};
// All exported provider slots are caller-configurable and may be unset or return arbitrary values,
// so wrapper helpers must enforce the exact 32-byte digest contract instead of trusting providers.
const callHash = (name: string, a: TArg<Uint8Array>, b?: TArg<Uint8Array>): TRet<Uint8Array> =>
abytes(_hash(name)(a, b), L, 'digest');
const callHashAsync = async (
name: string,
a: TArg<Uint8Array>,
b?: TArg<Uint8Array>
): Promise<TRet<Uint8Array>> => abytes(await _hash(name)(a, b), L, 'digest');
/**
* SHA-256 helper used by the synchronous API.
* @param msg - message bytes to hash
* @returns 32-byte SHA-256 digest.
* @example
* Hash message bytes after wiring the synchronous SHA-256 implementation.
* ```ts
* import * as secp from '@noble/secp256k1';
* import { sha256 } from '@noble/hashes/sha2.js';
* secp.hashes.sha256 = sha256;
* const digest = secp.hash(new Uint8Array([1, 2, 3]));
* ```
*/
// Public helper validates the message boundary explicitly; the configured provider is still looked
// up dynamically and its output is checked with `gh(...)`.
const hash = (msg: TArg<Uint8Array>): TRet<Uint8Array> =>
callHash('sha256', abytes(msg, undefined, 'message'));
// also rejects structurally similar Point values from other realms / bundled copies
const apoint = (p: unknown) => {
if (p instanceof Point) return p;
throw new TypeError('Point expected');
};
/** Point in 2d xy affine coordinates. */
export type AffinePoint = {
/** Affine x coordinate. */
x: bigint;
/** Affine y coordinate. */
y: bigint;
};
// ## End of Helpers
// -----------------
const E_BADPOINT = 'bad point: not on curve';
/**
* secp256k1 formula. Koblitz curves are subclass of weierstrass curves with a=0,
* making it x³+b; callers validate x first.
*/
const koblitz = (x: bigint) => M(M(x * x) * x + 7n);
/** assert is element of field mod P (incl. 0 for projective infinity coordinates) */
const FpIsValid = (n: bigint) => arange(n, 0n, P);
/** assert is element of field mod P (excl. 0 where current callers need a non-zero coordinate) */
const FpIsValidNot0 = (n: bigint) => arange(n, 1n, P);
/** assert is element of field mod N (excl. 0), matching the shared BIP340 scalar-failure rule used here.
* There is deliberately no FnIsValid: no caller accepts the scalar 0. */
const FnIsValidNot0 = (n: bigint) => arange(n, 1n, N);
// Shared parity primitive for BIP340 even-y checks and SEC 1 compressed prefixes.
const isEven = (y: bigint) => !(y & 1n);
/** SEC 1 compressed-prefix helper. Parity only: callers validate y before asking for the prefix byte. */
const getPrefix = (y: bigint) => Uint8Array.of(isEven(y) ? 0x02 : 0x03);
/** lift_x from BIP340 returns the unique point with x and an even square root of x³+7.
* SEC 1 callers still negate it for the odd-prefix branch. */
const lift_x = (x: bigint) => {
// Let c = x³ + 7 mod p. Fail if x ≥ p. (also fail if x < 1)
const c = koblitz(FpIsValidNot0(x));
// r = √c candidate
// r = c^((p+1)/4) mod p
// This formula works for fields p = 3 mod 4 -- a special, fast case.
// Paper: "Square Roots from 1;24,51,10 to Dan Shanks".
let r = 1n;
for (let num = c, e = (P + 1n) / 4n; e > 0n; e >>= 1n) {
// powMod: modular exponentiation.
if (e & 1n) r = (r * num) % P; // Uses exponentiation by squaring.
num = (num * num) % P; // Not constant-time.
}
if (M(r * r) !== c) throw new Error('sqrt invalid'); // check if result is valid
return new Point(x, isEven(r) ? r : M(-r), 1n);
};
/**
* Point in 3d xyz projective coordinates. 3d takes less inversions than 2d.
* @param X - X coordinate.
* @param Y - Y coordinate.
* @param Z - projective Z coordinate.
* @example
* Do point arithmetic with the base point and encode the result as hex.
* ```ts
* import { Point } from '@noble/secp256k1';
* const hex = Point.BASE.double().toHex();
* ```
*/
class Point {
static BASE: Point;
static ZERO: Point;
readonly X: bigint;
readonly Y: bigint;
readonly Z: bigint;
constructor(X: bigint, Y: bigint, Z: bigint) {
this.X = FpIsValid(X);
this.Y = FpIsValidNot0(Y); // Y can't be 0 in Projective
this.Z = FpIsValid(Z);
freeze(this);
}
/** Returns the shared curve metadata object by reference.
* It is readonly only at type level, and mutating it won't retarget arithmetic,
* which already uses module-load snapshots. */
static CURVE(): WeierstrassOpts<bigint> {
return secp256k1_CURVE;
}
/** Create 3d xyz point from 2d xy. (0, 0) => (0, 1, 0), not (0, 0, 1) */
static fromAffine(ap: AffinePoint): Point {
const { x, y } = ap;
return x === 0n && y === 0n ? I : new Point(x, y, 1n);
}
/** Convert Uint8Array or hex string to Point. */
static fromBytes(bytes: TArg<Uint8Array>): Point {
abytes(bytes);
const length = bytes.length;
const head = bytes[0];
const x = sliceBytesNumBE(bytes, 1, 33);
// SEC 1 defines the rare infinity encoding 0x00, but SEC 1 public-key validation rejects
// infinity. We keep 0x00 rejected here because this parser is reused by verify(), ECDH,
// and public-key validation helpers, so strict handling applies to all callers by default.
// Local secp256k1 crosstests show OpenSSL raw point codecs accept 0x00 too.
// Parse and validate SEC 1 compressed/uncompressed encodings before returning.
try {
if (length === 33 && (head === 0x02 || head === 0x03)) {
// Equation is y² == x³ + ax + b. We calculate y from x.
// lift_x() returns the even point; SEC 1 0x03 still needs the odd point.
const p = lift_x(x);
return head === 0x03 ? p.negate() : p;
}
// Uncompressed 65-byte point, 0x04 prefix
if (length === 65 && head === 0x04)
return new Point(x, sliceBytesNumBE(bytes, 33, 65), 1n).assertValidity();
} catch (error) {
// Out-of-range coordinates and non-residue x report the same error as wrong
// prefixes / off-curve points, instead of generic range-check messages.
throw new Error(E_BADPOINT);
}
throw new Error(E_BADPOINT);
}
static fromHex(hex: string): Point {
return Point.fromBytes(hexToBytes(hex));
}
get x(): bigint {
return this.toAffine().x;
}
get y(): bigint {
return this.toAffine().y;
}
/** Equality check: compare points P&Q. */
equals(other: Point): boolean {
const { X: X1, Y: Y1, Z: Z1 } = this;
const { X: X2, Y: Y2, Z: Z2 } = apoint(other); // checks class equality
return M(X1 * Z2) === M(X2 * Z1) && M(Y1 * Z2) === M(Y2 * Z1);
}
is0(): boolean {
return this.Z === 0n;
}
/** Flip point over y coordinate. */
negate(): Point {
return new Point(this.X, M(-this.Y), this.Z);
}
/** Point doubling: P+P, complete formula. */
double(): Point {
return this.add(this);
}
/**
* Point addition: P+Q, complete, exception-free formula
* (Renes-Costello-Batina, algo 1 of [2015/1060](https://eprint.iacr.org/2015/1060)).
* Cost: `12M + 0S + 3*a + 3*b3 + 23add`.
*/
// prettier-ignore
add(other: Point): Point {
const { X: X1, Y: Y1, Z: Z1 } = this;
const { X: X2, Y: Y2, Z: Z2 } = apoint(other);
const a = 0n;
const b = 7n;
let X3 = 0n, Y3 = 0n, Z3 = 0n;
const b3 = M(b * 3n);
let t0 = M(X1 * X2), t1 = M(Y1 * Y2), t2 = M(Z1 * Z2), t3 = M(X1 + Y1); // step 1
let t4 = M(X2 + Y2); // step 5
t3 = M(t3 * t4); t4 = M(t0 + t1); t3 = M(t3 - t4); t4 = M(X1 + Z1);
let t5 = M(X2 + Z2); // step 10
t4 = M(t4 * t5); t5 = M(t0 + t2); t4 = M(t4 - t5); t5 = M(Y1 + Z1);
X3 = M(Y2 + Z2); // step 15
t5 = M(t5 * X3); X3 = M(t1 + t2); t5 = M(t5 - X3); Z3 = M(a * t4);
X3 = M(b3 * t2); // step 20
Z3 = M(X3 + Z3); X3 = M(t1 - Z3); Z3 = M(t1 + Z3); Y3 = M(X3 * Z3);
t1 = M(t0 + t0); // step 25
t1 = M(t1 + t0); t2 = M(a * t2); t4 = M(b3 * t4); t1 = M(t1 + t2);
t2 = M(t0 - t2); // step 30
t2 = M(a * t2); t4 = M(t4 + t2); t0 = M(t1 * t4); Y3 = M(Y3 + t0);
t0 = M(t5 * t4); // step 35
X3 = M(t3 * X3); X3 = M(X3 - t0); t0 = M(t3 * t1); Z3 = M(t5 * Z3);
Z3 = M(Z3 + t0); // step 40
return new Point(X3, Y3, Z3);
}
subtract(other: Point): Point {
return this.add(apoint(other).negate());
}
/**
* Point-by-scalar multiplication. Scalar must be in range 1 <= n < CURVE.n.
* Uses {@link wNAF} for base point.
* Uses fake point to mitigate leakage shape in JS, not as a hard constant-time guarantee.
* @param n scalar by which point is multiplied
* @param safe safe mode guards against timing attacks; unsafe mode is faster
*/
multiply(n: bigint, safe = true): Point {
// Unsafe internal callers may legitimately need 0*P = O during double-scalar multiplication.
if (!safe && n === 0n) return I;
FnIsValidNot0(n);
if (n === 1n) return this;
if (this.equals(G)) return wNAF(n).p;
// init result point & fake point
let p = I;
let f = G;
let d: Point = this;
// Safe mode always runs 256 iterations so ladder length can't leak the scalar's
// leading zero bits; unsafe mode stops at the top set bit for speed.
for (let i = 0; safe ? i < 256 : n > 0n; i++) {
// if bit is present, add to point
// if not present, add to fake, for timing safety
if (n & 1n) p = p.add(d);
else if (safe) f = f.add(d);
d = d.double();
n >>= 1n;
}
return p;
}
multiplyUnsafe(scalar: bigint): Point {
return this.multiply(scalar, false);
}
/** Convert point to 2d xy affine point. (X, Y, Z) ∋ (x=X/Z, y=Y/Z) */
toAffine(): AffinePoint {
const { X: x, Y: y, Z: z } = this;
// fast-paths for ZERO point OR Z=1
if (z === 0n) return { x: 0n, y: 0n };
if (z === 1n) return { x, y };
const iz = invert(z, P);
// (Z * Z^-1) must be 1, otherwise bad math
if (M(z * iz) !== 1n) throw new Error('inverse invalid');
// x = X*Z^-1; y = Y*Z^-1
return { x: M(x * iz), y: M(y * iz) };
}
/** Checks if the point is valid and on-curve. */
assertValidity(): Point {
const { x, y } = this.toAffine(); // convert to 2d xy affine point.
FpIsValidNot0(x); // must be in range 1 <= x,y < P
FpIsValidNot0(y);
// y² == x³ + ax + b, equation sides must be equal
if (M(y * y) !== koblitz(x)) throw new Error(E_BADPOINT);
return this;
}
/** Converts point to 33/65-byte Uint8Array. */
toBytes(isCompressed = true): TRet<Uint8Array> {
// Same policy as fromBytes(): SEC 1 has the rare infinity encoding 0x00, but we keep ZERO
// out of this byte surface because callers treat these encodings as public keys by default.
const { x, y } = this.assertValidity().toAffine();
const x32b = numTo32b(x);
if (isCompressed) return concatBytes(getPrefix(y), x32b);
return concatBytes(Uint8Array.of(0x04), x32b, numTo32b(y));
}
toHex(isCompressed?: boolean): string {
return bytesToHex(this.toBytes(isCompressed));
}
}
/** Generator / base point */
const G: Point = new Point(Gx, Gy, 1n);
/** Identity / zero point */
const I: Point = new Point(0n, 1n, 0n);
// Static aliases
Point.BASE = G;
Point.ZERO = I;
/** `Q = u1⋅G + u2⋅R`. Verifies Q is not ZERO. Unsafe: non-CT. */
const doubleScalarMulUns = (R: Point, u1: bigint, u2: bigint): Point => {
return G.multiply(u1, false).add(R.multiply(u2, false)).assertValidity();
};
// Inherits byte validation from bytesToHex(); the || '0' fallback keeps empty input mapped to 0n.
const bytesToNumBE = (b: TArg<Uint8Array>): bigint => big('0x' + (bytesToHex(b) || '0'));
// Callers provide monotone slice bounds; subarray() would otherwise clamp or reinterpret them silently.
const sliceBytesNumBE = (b: TArg<Uint8Array>, from: number, to: number) =>
bytesToNumBE(b.subarray(from, to));
/** Generic 32-byte big-endian encoder. Must be 0 <= num < 2²⁵⁶; call sites need not be field/scalar elements. */
const numTo32b = (num: bigint): TRet<Uint8Array> =>
hexToBytes(padh(arange(num, 0n, 2n ** 256n), L * 2)); // L*2 = 64 hex chars for a zero-padded 32-byte value
/** Normalize private key to scalar (bigint). Verifies scalar is in range 1 <= d < N. */
const secretKeyToScalar = (secretKey: TArg<Uint8Array>): bigint => {
const num = bytesToNumBE(abytes(secretKey, L, 'secret key'));
return arange(num, 1n, N, 'invalid secret key: outside of range');
};
/** For signature malleability, checks the strict upper-half predicate s > floor(N/2). */
const highS = (n: bigint): boolean => n > N >> 1n;
// Recovery id of an affine nonce point: bit 0 is y parity, bit 1 is x = r + n.
const getRecoveryBit = (x: bigint, y: bigint, r: bigint): number =>
(x === r ? 0 : 2) | Number(y & 1n);
/**
* Creates a SEC 1 public key from a 32-byte private key.
* @param privKey - 32-byte secret key.
* @param isCompressed - return 33-byte compressed SEC 1 encoding when `true`, otherwise 65-byte uncompressed.
* @returns serialized secp256k1 public key in SEC 1 encoding.
* @example
* Derive the serialized public key for a secp256k1 secret key.
* ```ts
* import * as secp from '@noble/secp256k1';
* const secretKey = secp.utils.randomSecretKey();
* const publicKey = secp.getPublicKey(secretKey);
* ```
*/
const getPublicKey = (privKey: TArg<Uint8Array>, isCompressed = true): TRet<Uint8Array> => {
return G.multiply(secretKeyToScalar(privKey)).toBytes(isCompressed);
};
const isValidSecretKey = (secretKey: TArg<Uint8Array>): boolean => {
try {
return !!secretKeyToScalar(secretKey);
} catch (error) {
return false;
}
};
const isValidPublicKey = (publicKey: TArg<Uint8Array>, isCompressed?: boolean): boolean => {
try {
const l = publicKey.length;
if (isCompressed === true && l !== 33) return false;
if (isCompressed === false && l !== 65) return false;
return !!Point.fromBytes(publicKey);
} catch (error) {
return false;
}
};
const assertRecoveryBit = (recovery?: number): number => {
if (recovery != null && [0, 1, 2, 3].includes(recovery)) return recovery;
throw new Error('invalid recovery id');
};
const assertSigFormat = (format?: ECDSASignatureFormat) => {
if (format === 'der')
throw new Error('Signature format "der" is not supported: switch to noble-curves');
if (format != null && format !== SIG_COMPACT && format !== SIG_RECOVERED)
throw new Error('Signature format must be one of: compact, recovered, der');
};
const assertSigLength = (
sig: TArg<Uint8Array>,
format: ECDSASignatureFormat = SIG_COMPACT
): TRet<Uint8Array> => {
assertSigFormat(format);
const bytes = abytes(sig, undefined, 'signature');
const len = 64 + Number(format === SIG_RECOVERED);
if (bytes.length !== len)
throw new Error(`Signature format "${format}" expects Uint8Array with length ${len}`);
return bytes;
};
/**
* ECDSA Signature class. Supports only compact 64-byte representation, not DER.
* @param r - signature `r` scalar.
* @param s - signature `s` scalar.
* @param recovery - optional recovery id.
* @example
* Build a recovered-format signature object and serialize it.
* ```ts
* import { Signature } from '@noble/secp256k1';
* const bytes = new Signature(1n, 2n, 0).toBytes('recovered');
* ```
*/
class Signature {
readonly r: bigint;
readonly s: bigint;
readonly recovery?: number;
constructor(r: bigint, s: bigint, recovery?: number) {
this.r = FnIsValidNot0(r); // 1 <= r < N
this.s = FnIsValidNot0(s); // 1 <= s < N
// Keep recovered Signature objects internally consistent across all construction paths.
if (recovery != null) this.recovery = assertRecoveryBit(recovery);
freeze(this);
}
static fromBytes(b: TArg<Uint8Array>, format: ECDSASignatureFormat = SIG_COMPACT): Signature {
b = assertSigLength(b, format);
let rec: number | undefined;
if (format === SIG_RECOVERED) {
rec = b[0];
b = b.subarray(1);
}
const r = sliceBytesNumBE(b, 0, L);
const s = sliceBytesNumBE(b, L, 64);
return new Signature(r, s, rec);
}
addRecoveryBit(bit: number): RecoveredSignature {
return new Signature(this.r, this.s, bit) as RecoveredSignature;
}
hasHighS(): boolean {
return highS(this.s);
}
toBytes(format: ECDSASignatureFormat = SIG_COMPACT): TRet<Uint8Array> {
// Standalone noble-secp256k1 does not implement DER; reject here so direct Signature users
// don't silently get compact bytes for an unsupported format.
assertSigFormat(format);
const { r, s, recovery } = this;
const res = concatBytes(numTo32b(r), numTo32b(s));
if (format === SIG_RECOVERED) {
return concatBytes(Uint8Array.of(assertRecoveryBit(recovery)), res);
}
return res;
}
}
/**
* RFC6979: ensure ECDSA msg is X bytes, convert to BigInt.
* RFC 6979 §2.3.2 says bits2int keeps the leftmost qlen bits and discards the rest.
* FIPS 186-4 4.6 gives the same leftmost-bit truncation rule. bits2int can produce res>N.
*/
// The 8 KiB cap is only a local DoS guard. Longer ordinary prehashes must still follow
// RFC 6979 §2.3.2 truncation instead of being rejected just because blen > qlen.
const MAX_PREHASHED_BYTES = 8192;
const E_MSGBIG = 'input is too large';
const oversizedMsg = (bytes: TArg<Uint8Array>, prehash?: boolean): boolean =>
!prehash && bytes.length > MAX_PREHASHED_BYTES;
const bits2int = (bytes: TArg<Uint8Array>): bigint => {
if (oversizedMsg(bytes)) throw new Error(E_MSGBIG);
const delta = bytes.length * 8 - 256;
const num = bytesToNumBE(bytes);
return delta > 0 ? num >> big(delta) : num;
};
/** int2octets can't be used; pads small msgs with 0: BAD for truncation as per RFC vectors */
const bits2int_modN = (bytes: TArg<Uint8Array>): bigint => modN(bits2int(abytes(bytes)));
// Async entry points snapshot the message before their first await. An oversized prehash is
// rejected before the copy, so an attacker-controlled view is never cloned just to fail in
// bits2int().
const snapshotMsg = (message: TArg<Uint8Array>, prehash?: boolean): TRet<Uint8Array> => {
const view = abytes(message, undefined, 'message');
if (oversizedMsg(view, prehash)) throw new Error(E_MSGBIG);
return cloneBytes(view);
};
/**
* Option to enable hedged signatures with improved security.
*
* * Randomly generated k is bad, because broken CSPRNG would leak private keys.
* * Deterministic k (RFC6979) is better; but is suspectible to fault attacks.
*
* We allow using technique described in RFC6979 3.6: additional k', a.k.a. adding randomness
* to deterministic sig. If CSPRNG is broken & randomness is weak, it would STILL be as secure
* as ordinary sig without ExtraEntropy.
*
* * `true` means "fetch data, from CSPRNG, incorporate it into k generation"
* * `false` means "disable extra entropy, use purely deterministic k"
* * `Uint8Array` passed means "incorporate following data into k generation"
*
* See {@link https://paulmillr.com/posts/deterministic-signatures/ | Deterministic signatures}.
*/
export type ECDSAExtraEntropy = boolean | Uint8Array;
const SIG_COMPACT = 'compact';
const SIG_RECOVERED = 'recovered';
/**
* - `compact` is the default format
* - `recovered` is the same as compact, but with an extra byte indicating recovery byte
* - `der` is not supported; it is included only so unsupported requests can be rejected consistently.
* Switch to noble-curves if you need der.
*/
export type ECDSASignatureFormat = 'compact' | 'recovered' | 'der';
/**
* - `prehash`: (default: true) indicates whether to do sha256(message).
* When a custom hash is used, it must be set to `false`.
*/
export type ECDSARecoverOpts = {
/** Set to `false` when the message is already hashed with a custom digest. */
prehash?: boolean;
/** Set to `false` to return a 65-byte uncompressed public key instead of the 33-byte default. */
isCompressed?: boolean;
};
/**
* - `prehash`: (default: true) indicates whether to do sha256(message).
* When a custom hash is used, it must be set to `false`.
* - `lowS`: (default: true) prohibits signatures in the strict upper half (`sig.s > floor(CURVE.n / 2n)`).
* Compatible with BTC/ETH. Setting `lowS: false` allows to create malleable signatures,
* which is default openssl behavior.
* Non-malleable signatures can still be successfully verified in openssl.
* - `format`: (default: 'compact') 'compact' or 'recovered' with recovery byte
*/
export type ECDSAVerifyOpts = {
/** Set to `false` when the message is already hashed with a custom digest. */
prehash?: boolean;
/** Set to `false` to accept high-S signatures instead of enforcing canonical low-S ones. */
lowS?: boolean;
/** Signature encoding accepted by the verifier. */
format?: ECDSASignatureFormat;
};
/**
* - `prehash`: (default: true) indicates whether to do sha256(message).
* When a custom hash is used, it must be set to `false`.
* - `lowS`: (default: true) prohibits signatures in the strict upper half (`sig.s > floor(CURVE.n / 2n)`).
* Compatible with BTC/ETH. Setting `lowS: false` allows to create malleable signatures,
* which is default openssl behavior.
* Non-malleable signatures can still be successfully verified in openssl.
* - `format`: (default: 'compact') 'compact' or 'recovered' with recovery byte
* - `extraEntropy`: (default: false) creates sigs with increased security, see {@link ECDSAExtraEntropy}
*/
export type ECDSASignOpts = {
/** Set to `false` when the message is already hashed with a custom digest. */
prehash?: boolean;
/** Set to `false` to allow high-S signatures instead of normalizing to low-S form. */
lowS?: boolean;
/** Signature encoding produced by the signer. */
format?: ECDSASignatureFormat;
/** Extra entropy mixed into RFC6979 nonce generation for hedged signatures. */
extraEntropy?: ECDSAExtraEntropy;
};
const _sha = 'SHA-256';
/**
* Hash implementations used by the synchronous and async ECDSA / Schnorr helpers.
* All slots are configurable API surface; wrapper helpers revalidate that SHA-256 and HMAC-SHA256
* providers still return exact 32-byte Uint8Array digests.
* @example
* Provide sync hash helpers before calling the synchronous signing API.
* ```ts
* import * as secp from '@noble/secp256k1';
* import { hmac } from '@noble/hashes/hmac.js';
* import { sha256 } from '@noble/hashes/sha2.js';
* secp.hashes.sha256 = sha256;
* secp.hashes.hmacSha256 = (key, msg) => hmac(sha256, key, msg);
* const secretKey = secp.utils.randomSecretKey();
* const sig = secp.sign(new Uint8Array([1, 2, 3]), secretKey);
* ```
*/
const hashes = {
hmacSha256Async: async (
key: TArg<Uint8Array>,
message: TArg<Uint8Array>
): Promise<TRet<Uint8Array>> => {
const s = subtle();
const k = await s.importKey('raw', key, { name: 'HMAC', hash: _sha }, false, ['sign']);
return new Uint8Array(await s.sign('HMAC', k, message)) as TRet<Uint8Array>;
},
hmacSha256: undefined as
undefined | ((key: TArg<Uint8Array>, message: TArg<Uint8Array>) => TRet<Uint8Array>),
sha256Async: async (msg: TArg<Uint8Array>): Promise<TRet<Uint8Array>> =>
new Uint8Array(await subtle().digest(_sha, msg)) as TRet<Uint8Array>,
sha256: undefined as undefined | ((message: TArg<Uint8Array>) => TRet<Uint8Array>),
};
// prehash=false means the caller already supplies the digest bytes
// used by sign/verify/recover, and this helper returns the same reference unchanged.
const prepMsg = (
msg: TArg<Uint8Array>,
prehash: boolean,
async_: boolean
): TRet<Uint8Array | Promise<Uint8Array>> => {
const message = abytes(msg, undefined, 'message');
if (!prehash) return message;
return async_ ? callHashAsync('sha256Async', message) : callHash('sha256', message);
};
type Pred<T> = (v: Uint8Array) => T | undefined;
type ECDSAOpts = readonly [
boolean, // lowS
boolean, // prehash
ECDSASignatureFormat,
ECDSAExtraEntropy | undefined,
];
const NULL = /* @__PURE__ */ new Uint8Array(0);
const byte0 = /* @__PURE__ */ Uint8Array.of(0x00);
const byte1 = /* @__PURE__ */ Uint8Array.of(0x01);
const _drbgErr = 'drbg: tried max amount of iterations';
// HMAC-DRBG from NIST 800-90. Minimal, non-full-spec - used for RFC6979 signatures.
const hmacDrbg = <T>(seed: Uint8Array, pred: Pred<T>): T => {
let v = new Uint8Array(L); // Steps B, C of RFC6979 3.2: set hashLen
let k = new Uint8Array(L); // In our case, it's always equal to L
let i = 0; // Iterations counter, will throw when over max
const reset = () => {
v.fill(1);
k.fill(0);
};
// h = hmac(k || v || ...). The configured provider is still checked on every call because the
// exported slot can be replaced or unset at runtime.
const h = (...b: Uint8Array[]) => callHash('hmacSha256', k, concatBytes(v, ...b));
const reseed = (seed: Uint8Array = NULL) => {
// HMAC-DRBG reseed() function. Steps D-G
k = h(byte0, seed); // k = hmac(k || v || 0x00 || seed)
v = h(); // v = hmac(k || v)
if (seed.length === 0) return;
k = h(byte1, seed); // k = hmac(k || v || 0x01 || seed)
v = h(); // v = hmac(k || v)
};
// HMAC-DRBG generate() function
const gen = () => {
if (i++ >= 1000) throw new Error(_drbgErr);
v = h(); // v = hmac(k || v)
return v; // One block is enough here because secp256k1 qlen and SHA-256 hlen are both 32 bytes.
};
reset();
reseed(seed); // Steps D-G
let res: T | undefined = undefined; // Step H: grind until k is in [1..n-1]
// `pred` receives the live V buffer from gen(); it must treat that input as read-only and
// return independent bytes, because reset() scrubs the DRBG state before hmacDrbg returns.
while (!(res = pred(gen()))) reseed(); // test predicate until it returns ok
reset();
return res!;
};
// Identical to hmacDrbg, but async: uses built-in WebCrypto
const hmacDrbgAsync = async <T>(seed: Uint8Array, pred: Pred<T>): Promise<T> => {
let v = new Uint8Array(L); // Steps B, C of RFC6979 3.2: set hashLen
let k = new Uint8Array(L); // In our case, it's always equal to L
let i = 0; // Iterations counter, will throw when over max
const reset = () => {
v.fill(1);
k.fill(0);
};
// h = hmac(k || v || ...). Async provider lookup still goes through `callHash(...)` because the
// exported slot can be replaced or unset at runtime.
const h = (...b: Uint8Array[]) => callHashAsync('hmacSha256Async', k, concatBytes(v, ...b));
const reseed = async (seed: Uint8Array = NULL) => {
// HMAC-DRBG reseed() function. Steps D-G
k = await h(byte0, seed); // k = hmac(k || v || 0x00 || seed)
v = await h(); // v = hmac(k || v)
if (seed.length === 0) return;
k = await h(byte1, seed); // k = hmac(k || v || 0x01 || seed)
v = await h(); // v = hmac(k || v)
};
// HMAC-DRBG generate() function
const gen = async () => {
if (i++ >= 1000) throw new Error(_drbgErr);
v = await h(); // v = hmac(k || v)
return v; // Same one-block shortcut: secp256k1 qlen and SHA-256 hlen are both 32 bytes here.
};
reset();
await reseed(seed); // Steps D-G
let res: T | undefined = undefined; // Step H: grind until k is in [1..n-1]
// Same contract as sync hmacDrbg(): pred sees the live V buffer and must not mutate or return it.
while (!(res = pred(await gen()))) await reseed(); // test predicate until it returns ok
reset();
return res!;
};
// RFC6979 signature generation, preparation step.
// Follows [SEC1](https://secg.org/sec1-v2.pdf) 4.1.3 & RFC6979.
const _sign = <T>(
messageHash: Uint8Array,
secretKey: Uint8Array,
opts: ECDSAOpts,
drbg: (seed: Uint8Array, pred: Pred<TRet<Uint8Array>>) => T
): T => {
const [lowS, , format, extraEntropy] = opts; // generates low-s sigs by default
// RFC6979 3.2: we skip step A
const h1i = bits2int_modN(messageHash); // msg bigint
const d = secretKeyToScalar(secretKey); // validate private key, convert to bigint
const seedArgs: Uint8Array[] = [numTo32b(d), numTo32b(h1i)]; // Step D of RFC6979 3.2
/** RFC6979 3.6: additional k' (optional). See {@link ECDSAExtraEntropy}. */
if (extraEntropy != null && extraEntropy !== false) {
// K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
// gen random bytes OR pass as-is
seedArgs.push(
abytes(extraEntropy === true ? randomBytes(L) : extraEntropy, undefined, 'extraEntropy')
);
}
// Converts signature params into point w r/s, checks result for validity.
// To transform k => Signature:
// q = k⋅G
// r = q.x mod n
// s = k^-1(m + rd) mod n
// Can use scalar blinding b^-1(bm + bdr) where b ∈ [1,q−1] according to
// https://tches.iacr.org/index.php/TCHES/article/view/7337/6509. We've decided against it:
// a) dependency on CSPRNG b) 15% slowdown c) doesn't really help since bigints are not CT
const k2sig = (kBytes: Uint8Array): TRet<Uint8Array> | undefined => {
// RFC 6979 Section 3.2, step 3: k = bits2int(T)
// Important: all mod() calls here must be done over N
const k = bits2int(kBytes);
if (!(1n <= k && k < N)) return; // Valid scalars (including k) must be in 1..N-1
const ik = invert(k, N); // k^-1 mod n
const q = G.multiply(k).toAffine(); // q = k⋅G
const r = modN(q.x); // r = q.x mod n
// RFC 6979 §2.4 step 3 / §3.4 only spell out retry for r = 0.
// FIPS 186-5 §6.4.1 step 11 says deterministic ECDSA should fail on r = 0 or s = 0, but
// that restart-from-scratch note does not apply here: hmacDrbg() keeps advancing through one
// RFC6979 stream until k2sig() accepts a candidate, instead of restarting from the same seed.
if (r === 0n) return;
const s = modN(ik * (h1i + r * d)); // s = k^-1(m + rd) mod n
if (s === 0n) return;
let recovery = getRecoveryBit(q.x, q.y, r); // recovery bit (2 or 3, when q.x > n)
let normS = s; // normalized S
if (lowS && highS(s)) {
// if lowS was passed, ensure s is always
normS = N - s; // in the bottom half of CURVE.n
recovery ^= 1;
}
const sig = new Signature(r, normS, recovery) as RecoveredSignature; // use normS, not s
return sig.toBytes(format);
};
return drbg(concatBytes(...seedArgs), k2sig);
};
// Follows [SEC1](https://secg.org/sec1-v2.pdf) 4.1.4.
const _verify = (
sig: TArg<Uint8Array>,
messageHash: TArg<Uint8Array>,
publicKey: TArg<Uint8Array>,
opts: ECDSAOpts
) => {
const [lowS, , format] = opts;
if (sig instanceof Signature) throw new Error('Signature must be in Uint8Array, use .toBytes()');
// Deliberately outside the try: wrong-length / wrongly-typed inputs are caller bugs which
// throw loudly, while only well-formed signatures failing crypto checks return false.
assertSigLength(sig, format);
abytes(publicKey, undefined, 'publicKey');
try {
const { r, s, recovery } = Signature.fromBytes(sig, format);
const h = bits2int_modN(messageHash); // Truncate hash
const Q = Point.fromBytes(publicKey); // Validate public key. Q, not P: P is the field prime
if (lowS && highS(s)) return false; // lowS bans sig.s >= CURVE.n/2
const is = invert(s, N); // s^-1