@protontech/openpgp 6.0.0-beta.2 → 6.0.0-beta.3.patch.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- /*! OpenPGP.js v6.0.0-beta.2 - 2024-07-05 - this is LGPL licensed code, see LICENSE/our website https://openpgpjs.org/ for more information. */
1
+ /*! OpenPGP.js v6.0.0-beta.3.patch.1 - 2024-09-11 - this is LGPL licensed code, see LICENSE/our website https://openpgpjs.org/ for more information. */
2
2
  const globalThis = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
3
3
 
4
4
  import { H as Hash, h as hash, t as toBytes, e as exists, b as bytes, c as concatBytes$1, r as randomBytes, s as sha256, a as sha384, d as sha512, w as wrapConstructor, u as utf8ToBytes$1, f as shake256 } from './sha3.mjs';
@@ -74,6 +74,10 @@ class HMAC extends Hash {
74
74
  * @param hash - function that would be used e.g. sha256
75
75
  * @param key - message key
76
76
  * @param message - message data
77
+ * @example
78
+ * import { hmac } from '@noble/hashes/hmac';
79
+ * import { sha256 } from '@noble/hashes/sha2';
80
+ * const mac1 = hmac(sha256, 'key', 'message');
77
81
  */
78
82
  const hmac = (hash, key, message) => new HMAC(hash, key).update(message).digest();
79
83
  hmac.create = (hash, key) => new HMAC(hash, key);
@@ -83,9 +87,9 @@ hmac.create = (hash, key) => new HMAC(hash, key);
83
87
  // This is OK: `abstract` directory does not use noble-hashes.
84
88
  // User may opt-in into using different hashing library. This way, noble-hashes
85
89
  // won't be included into their bundle.
86
- const _0n$5 = BigInt(0);
87
- const _1n$7 = BigInt(1);
88
- const _2n$4 = BigInt(2);
90
+ const _0n$5 = /* @__PURE__ */ BigInt(0);
91
+ const _1n$7 = /* @__PURE__ */ BigInt(1);
92
+ const _2n$4 = /* @__PURE__ */ BigInt(2);
89
93
  function isBytes(a) {
90
94
  return (a instanceof Uint8Array ||
91
95
  (a != null && typeof a === 'object' && a.constructor.name === 'Uint8Array'));
@@ -94,6 +98,10 @@ function abytes(item) {
94
98
  if (!isBytes(item))
95
99
  throw new Error('Uint8Array expected');
96
100
  }
101
+ function abool(title, value) {
102
+ if (typeof value !== 'boolean')
103
+ throw new Error(`${title} must be valid boolean, got "${value}".`);
104
+ }
97
105
  // Array where index 0xf0 (240) is mapped to string 'f0'
98
106
  const hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0'));
99
107
  /**
@@ -236,6 +244,25 @@ function utf8ToBytes(str) {
236
244
  throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
237
245
  return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809
238
246
  }
247
+ // Is positive bigint
248
+ const isPosBig = (n) => typeof n === 'bigint' && _0n$5 <= n;
249
+ function inRange(n, min, max) {
250
+ return isPosBig(n) && isPosBig(min) && isPosBig(max) && min <= n && n < max;
251
+ }
252
+ /**
253
+ * Asserts min <= n < max. NOTE: It's < max and not <= max.
254
+ * @example
255
+ * aInRange('x', x, 1n, 256n); // would assume x is in (1n..255n)
256
+ */
257
+ function aInRange(title, n, min, max) {
258
+ // Why min <= n < max and not a (min < n < max) OR b (min <= n <= max)?
259
+ // consider P=256n, min=0n, max=P
260
+ // - a for min=0 would require -1: `inRange('x', x, -1n, P)`
261
+ // - b would commonly require subtraction: `inRange('x', x, 0n, P - 1n)`
262
+ // - our way is the cleanest: `inRange('x', x, 0n, P)
263
+ if (!inRange(n, min, max))
264
+ throw new Error(`expected valid ${title}: ${min} <= n < ${max}, got ${typeof n} ${n}`);
265
+ }
239
266
  // Bit operations
240
267
  /**
241
268
  * Calculates amount of bits in a bigint.
@@ -366,9 +393,32 @@ function validateObject(object, validators, optValidators = {}) {
366
393
  // const z2 = validateObject(o, { a: 'isSafeInteger' }, { c: 'zz' });
367
394
  // const z3 = validateObject(o, { test: 'boolean', z: 'bug' });
368
395
  // const z4 = validateObject(o, { a: 'boolean', z: 'bug' });
396
+ /**
397
+ * throws not implemented error
398
+ */
399
+ const notImplemented = () => {
400
+ throw new Error('not implemented');
401
+ };
402
+ /**
403
+ * Memoizes (caches) computation result.
404
+ * Uses WeakMap: the value is going auto-cleaned by GC after last reference is removed.
405
+ */
406
+ function memoized(fn) {
407
+ const map = new WeakMap();
408
+ return (arg, ...args) => {
409
+ const val = map.get(arg);
410
+ if (val !== undefined)
411
+ return val;
412
+ const computed = fn(arg, ...args);
413
+ map.set(arg, computed);
414
+ return computed;
415
+ };
416
+ }
369
417
 
370
418
  var ut = /*#__PURE__*/Object.freeze({
371
419
  __proto__: null,
420
+ aInRange: aInRange,
421
+ abool: abool,
372
422
  abytes: abytes,
373
423
  bitGet: bitGet,
374
424
  bitLen: bitLen,
@@ -383,7 +433,10 @@ var ut = /*#__PURE__*/Object.freeze({
383
433
  equalBytes: equalBytes,
384
434
  hexToBytes: hexToBytes,
385
435
  hexToNumber: hexToNumber,
436
+ inRange: inRange,
386
437
  isBytes: isBytes,
438
+ memoized: memoized,
439
+ notImplemented: notImplemented,
387
440
  numberToBytesBE: numberToBytesBE,
388
441
  numberToBytesLE: numberToBytesLE,
389
442
  numberToHexUnpadded: numberToHexUnpadded,
@@ -640,6 +693,9 @@ function nLength(n, nBitLength) {
640
693
  * * a) denormalized operations like mulN instead of mul
641
694
  * * b) same object shape: never add or remove keys
642
695
  * * c) Object.freeze
696
+ * NOTE: operations don't check 'isValid' for all elements for performance reasons,
697
+ * it is caller responsibility to check this.
698
+ * This is low-level code, please make sure you know what you doing.
643
699
  * @param ORDER prime positive bigint
644
700
  * @param bitLen how many bits the field consumes
645
701
  * @param isLE (def: false) if encoding / decoding should be in little-endian
@@ -748,6 +804,10 @@ function mapHashToField(key, fieldOrder, isLE = false) {
748
804
  // Abelian group utilities
749
805
  const _0n$3 = BigInt(0);
750
806
  const _1n$5 = BigInt(1);
807
+ // Since points in different groups cannot be equal (different object constructor),
808
+ // we can have single place to store precomputes
809
+ const pointPrecomputes = new WeakMap();
810
+ const pointWindowSizes = new WeakMap(); // This allows use make points immutable (nothing changes inside)
751
811
  // Elliptic curve multiplication of Point by scalar. Fragile.
752
812
  // Scalars should always be less than curve order: this should be checked inside of a curve itself.
753
813
  // Creates precomputation tables for fast multiplication:
@@ -764,7 +824,12 @@ function wNAF(c, bits) {
764
824
  const neg = item.negate();
765
825
  return condition ? neg : item;
766
826
  };
827
+ const validateW = (W) => {
828
+ if (!Number.isSafeInteger(W) || W <= 0 || W > bits)
829
+ throw new Error(`Wrong window size=${W}, should be [1..${bits}]`);
830
+ };
767
831
  const opts = (W) => {
832
+ validateW(W);
768
833
  const windows = Math.ceil(bits / W) + 1; // +1, because
769
834
  const windowSize = 2 ** (W - 1); // -1 because we skip zero
770
835
  return { windows, windowSize };
@@ -864,21 +929,81 @@ function wNAF(c, bits) {
864
929
  // which makes it less const-time: around 1 bigint multiply.
865
930
  return { p, f };
866
931
  },
867
- wNAFCached(P, precomputesMap, n, transform) {
868
- // @ts-ignore
869
- const W = P._WINDOW_SIZE || 1;
932
+ wNAFCached(P, n, transform) {
933
+ const W = pointWindowSizes.get(P) || 1;
870
934
  // Calculate precomputes on a first run, reuse them after
871
- let comp = precomputesMap.get(P);
935
+ let comp = pointPrecomputes.get(P);
872
936
  if (!comp) {
873
937
  comp = this.precomputeWindow(P, W);
874
- if (W !== 1) {
875
- precomputesMap.set(P, transform(comp));
876
- }
938
+ if (W !== 1)
939
+ pointPrecomputes.set(P, transform(comp));
877
940
  }
878
941
  return this.wNAF(W, comp, n);
879
942
  },
943
+ // We calculate precomputes for elliptic curve point multiplication
944
+ // using windowed method. This specifies window size and
945
+ // stores precomputed values. Usually only base point would be precomputed.
946
+ setWindowSize(P, W) {
947
+ validateW(W);
948
+ pointWindowSizes.set(P, W);
949
+ pointPrecomputes.delete(P);
950
+ },
880
951
  };
881
952
  }
953
+ /**
954
+ * Pippenger algorithm for multi-scalar multiplication (MSM).
955
+ * MSM is basically (Pa + Qb + Rc + ...).
956
+ * 30x faster vs naive addition on L=4096, 10x faster with precomputes.
957
+ * For N=254bit, L=1, it does: 1024 ADD + 254 DBL. For L=5: 1536 ADD + 254 DBL.
958
+ * Algorithmically constant-time (for same L), even when 1 point + scalar, or when scalar = 0.
959
+ * @param c Curve Point constructor
960
+ * @param field field over CURVE.N - important that it's not over CURVE.P
961
+ * @param points array of L curve points
962
+ * @param scalars array of L scalars (aka private keys / bigints)
963
+ */
964
+ function pippenger(c, field, points, scalars) {
965
+ // If we split scalars by some window (let's say 8 bits), every chunk will only
966
+ // take 256 buckets even if there are 4096 scalars, also re-uses double.
967
+ // TODO:
968
+ // - https://eprint.iacr.org/2024/750.pdf
969
+ // - https://tches.iacr.org/index.php/TCHES/article/view/10287
970
+ // 0 is accepted in scalars
971
+ if (!Array.isArray(points) || !Array.isArray(scalars) || scalars.length !== points.length)
972
+ throw new Error('arrays of points and scalars must have equal length');
973
+ scalars.forEach((s, i) => {
974
+ if (!field.isValid(s))
975
+ throw new Error(`wrong scalar at index ${i}`);
976
+ });
977
+ points.forEach((p, i) => {
978
+ if (!(p instanceof c))
979
+ throw new Error(`wrong point at index ${i}`);
980
+ });
981
+ const wbits = bitLen(BigInt(points.length));
982
+ const windowSize = wbits > 12 ? wbits - 3 : wbits > 4 ? wbits - 2 : wbits ? 2 : 1; // in bits
983
+ const MASK = (1 << windowSize) - 1;
984
+ const buckets = new Array(MASK + 1).fill(c.ZERO); // +1 for zero array
985
+ const lastBits = Math.floor((field.BITS - 1) / windowSize) * windowSize;
986
+ let sum = c.ZERO;
987
+ for (let i = lastBits; i >= 0; i -= windowSize) {
988
+ buckets.fill(c.ZERO);
989
+ for (let j = 0; j < scalars.length; j++) {
990
+ const scalar = scalars[j];
991
+ const wbits = Number((scalar >> BigInt(i)) & BigInt(MASK));
992
+ buckets[wbits] = buckets[wbits].add(points[j]);
993
+ }
994
+ let resI = c.ZERO; // not using this will do small speed-up, but will lose ct
995
+ // Skip first bucket, because it is zero
996
+ for (let j = buckets.length - 1, sumI = c.ZERO; j > 0; j--) {
997
+ sumI = sumI.add(buckets[j]);
998
+ resI = resI.add(sumI);
999
+ }
1000
+ sum = sum.add(resI);
1001
+ if (i !== 0)
1002
+ for (let j = 0; j < windowSize; j++)
1003
+ sum = sum.double();
1004
+ }
1005
+ return sum;
1006
+ }
882
1007
  function validateBasic(curve) {
883
1008
  validateField(curve.Fp);
884
1009
  validateObject(curve, {
@@ -900,6 +1025,12 @@ function validateBasic(curve) {
900
1025
 
901
1026
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
902
1027
  // Short Weierstrass curve. The formula is: y² = x³ + ax + b
1028
+ function validateSigVerOpts(opts) {
1029
+ if (opts.lowS !== undefined)
1030
+ abool('lowS', opts.lowS);
1031
+ if (opts.prehash !== undefined)
1032
+ abool('prehash', opts.prehash);
1033
+ }
903
1034
  function validatePointOpts(curve) {
904
1035
  const opts = validateBasic(curve);
905
1036
  validateObject(opts, {
@@ -927,8 +1058,14 @@ function validatePointOpts(curve) {
927
1058
  }
928
1059
  return Object.freeze({ ...opts });
929
1060
  }
930
- // ASN.1 DER encoding utilities
931
1061
  const { bytesToNumberBE: b2n, hexToBytes: h2b } = ut;
1062
+ /**
1063
+ * ASN.1 DER encoding utilities. ASN is very complex & fragile. Format:
1064
+ *
1065
+ * [0x30 (SEQUENCE), bytelength, 0x02 (INTEGER), intLength, R, 0x02 (INTEGER), intLength, S]
1066
+ *
1067
+ * Docs: https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/, https://luca.ntop.org/Teaching/Appunti/asn1.html
1068
+ */
932
1069
  const DER = {
933
1070
  // asn.1 DER encoding utils
934
1071
  Err: class DERErr extends Error {
@@ -936,54 +1073,103 @@ const DER = {
936
1073
  super(m);
937
1074
  }
938
1075
  },
939
- _parseInt(data) {
940
- const { Err: E } = DER;
941
- if (data.length < 2 || data[0] !== 0x02)
942
- throw new E('Invalid signature integer tag');
943
- const len = data[1];
944
- const res = data.subarray(2, len + 2);
945
- if (!len || res.length !== len)
946
- throw new E('Invalid signature integer: wrong length');
947
- // https://crypto.stackexchange.com/a/57734 Leftmost bit of first byte is 'negative' flag,
948
- // since we always use positive integers here. It must always be empty:
949
- // - add zero byte if exists
950
- // - if next byte doesn't have a flag, leading zero is not allowed (minimal encoding)
951
- if (res[0] & 0b10000000)
952
- throw new E('Invalid signature integer: negative');
953
- if (res[0] === 0x00 && !(res[1] & 0b10000000))
954
- throw new E('Invalid signature integer: unnecessary leading zero');
955
- return { d: b2n(res), l: data.subarray(len + 2) }; // d is data, l is left
1076
+ // Basic building block is TLV (Tag-Length-Value)
1077
+ _tlv: {
1078
+ encode: (tag, data) => {
1079
+ const { Err: E } = DER;
1080
+ if (tag < 0 || tag > 256)
1081
+ throw new E('tlv.encode: wrong tag');
1082
+ if (data.length & 1)
1083
+ throw new E('tlv.encode: unpadded data');
1084
+ const dataLen = data.length / 2;
1085
+ const len = numberToHexUnpadded(dataLen);
1086
+ if ((len.length / 2) & 128)
1087
+ throw new E('tlv.encode: long form length too big');
1088
+ // length of length with long form flag
1089
+ const lenLen = dataLen > 127 ? numberToHexUnpadded((len.length / 2) | 128) : '';
1090
+ return `${numberToHexUnpadded(tag)}${lenLen}${len}${data}`;
1091
+ },
1092
+ // v - value, l - left bytes (unparsed)
1093
+ decode(tag, data) {
1094
+ const { Err: E } = DER;
1095
+ let pos = 0;
1096
+ if (tag < 0 || tag > 256)
1097
+ throw new E('tlv.encode: wrong tag');
1098
+ if (data.length < 2 || data[pos++] !== tag)
1099
+ throw new E('tlv.decode: wrong tlv');
1100
+ const first = data[pos++];
1101
+ const isLong = !!(first & 128); // First bit of first length byte is flag for short/long form
1102
+ let length = 0;
1103
+ if (!isLong)
1104
+ length = first;
1105
+ else {
1106
+ // Long form: [longFlag(1bit), lengthLength(7bit), length (BE)]
1107
+ const lenLen = first & 127;
1108
+ if (!lenLen)
1109
+ throw new E('tlv.decode(long): indefinite length not supported');
1110
+ if (lenLen > 4)
1111
+ throw new E('tlv.decode(long): byte length is too big'); // this will overflow u32 in js
1112
+ const lengthBytes = data.subarray(pos, pos + lenLen);
1113
+ if (lengthBytes.length !== lenLen)
1114
+ throw new E('tlv.decode: length bytes not complete');
1115
+ if (lengthBytes[0] === 0)
1116
+ throw new E('tlv.decode(long): zero leftmost byte');
1117
+ for (const b of lengthBytes)
1118
+ length = (length << 8) | b;
1119
+ pos += lenLen;
1120
+ if (length < 128)
1121
+ throw new E('tlv.decode(long): not minimal encoding');
1122
+ }
1123
+ const v = data.subarray(pos, pos + length);
1124
+ if (v.length !== length)
1125
+ throw new E('tlv.decode: wrong value length');
1126
+ return { v, l: data.subarray(pos + length) };
1127
+ },
1128
+ },
1129
+ // https://crypto.stackexchange.com/a/57734 Leftmost bit of first byte is 'negative' flag,
1130
+ // since we always use positive integers here. It must always be empty:
1131
+ // - add zero byte if exists
1132
+ // - if next byte doesn't have a flag, leading zero is not allowed (minimal encoding)
1133
+ _int: {
1134
+ encode(num) {
1135
+ const { Err: E } = DER;
1136
+ if (num < _0n$2)
1137
+ throw new E('integer: negative integers are not allowed');
1138
+ let hex = numberToHexUnpadded(num);
1139
+ // Pad with zero byte if negative flag is present
1140
+ if (Number.parseInt(hex[0], 16) & 0b1000)
1141
+ hex = '00' + hex;
1142
+ if (hex.length & 1)
1143
+ throw new E('unexpected assertion');
1144
+ return hex;
1145
+ },
1146
+ decode(data) {
1147
+ const { Err: E } = DER;
1148
+ if (data[0] & 128)
1149
+ throw new E('Invalid signature integer: negative');
1150
+ if (data[0] === 0x00 && !(data[1] & 128))
1151
+ throw new E('Invalid signature integer: unnecessary leading zero');
1152
+ return b2n(data);
1153
+ },
956
1154
  },
957
1155
  toSig(hex) {
958
1156
  // parse DER signature
959
- const { Err: E } = DER;
1157
+ const { Err: E, _int: int, _tlv: tlv } = DER;
960
1158
  const data = typeof hex === 'string' ? h2b(hex) : hex;
961
1159
  abytes(data);
962
- let l = data.length;
963
- if (l < 2 || data[0] != 0x30)
964
- throw new E('Invalid signature tag');
965
- if (data[1] !== l - 2)
966
- throw new E('Invalid signature: incorrect length');
967
- const { d: r, l: sBytes } = DER._parseInt(data.subarray(2));
968
- const { d: s, l: rBytesLeft } = DER._parseInt(sBytes);
969
- if (rBytesLeft.length)
1160
+ const { v: seqBytes, l: seqLeftBytes } = tlv.decode(0x30, data);
1161
+ if (seqLeftBytes.length)
970
1162
  throw new E('Invalid signature: left bytes after parsing');
971
- return { r, s };
1163
+ const { v: rBytes, l: rLeftBytes } = tlv.decode(0x02, seqBytes);
1164
+ const { v: sBytes, l: sLeftBytes } = tlv.decode(0x02, rLeftBytes);
1165
+ if (sLeftBytes.length)
1166
+ throw new E('Invalid signature: left bytes after parsing');
1167
+ return { r: int.decode(rBytes), s: int.decode(sBytes) };
972
1168
  },
973
1169
  hexFromSig(sig) {
974
- // Add leading zero if first byte has negative bit enabled. More details in '_parseInt'
975
- const slice = (s) => (Number.parseInt(s[0], 16) & 0b1000 ? '00' + s : s);
976
- const h = (num) => {
977
- const hex = num.toString(16);
978
- return hex.length & 1 ? `0${hex}` : hex;
979
- };
980
- const s = slice(h(sig.s));
981
- const r = slice(h(sig.r));
982
- const shl = s.length / 2;
983
- const rhl = r.length / 2;
984
- const sl = h(shl);
985
- const rl = h(rhl);
986
- return `30${h(rhl + shl + 4)}02${rl}${r}02${sl}${s}`;
1170
+ const { _tlv: tlv, _int: int } = DER;
1171
+ const seq = `${tlv.encode(0x02, int.encode(sig.r))}${tlv.encode(0x02, int.encode(sig.s))}`;
1172
+ return tlv.encode(0x30, seq);
987
1173
  },
988
1174
  };
989
1175
  // Be friendly to bad ECMAScript parsers by not using bigint literals
@@ -992,6 +1178,7 @@ const _0n$2 = BigInt(0), _1n$4 = BigInt(1); BigInt(2); const _3n$1 = BigInt(3);
992
1178
  function weierstrassPoints(opts) {
993
1179
  const CURVE = validatePointOpts(opts);
994
1180
  const { Fp } = CURVE; // All curves has same field / group length as for now, but they can differ
1181
+ const Fn = Field(CURVE.n, CURVE.nBitLength);
995
1182
  const toBytes = CURVE.toBytes ||
996
1183
  ((_c, point, _isCompressed) => {
997
1184
  const a = point.toAffine();
@@ -1024,16 +1211,12 @@ function weierstrassPoints(opts) {
1024
1211
  throw new Error('bad generator point: equation left != right');
1025
1212
  // Valid group elements reside in range 1..n-1
1026
1213
  function isWithinCurveOrder(num) {
1027
- return typeof num === 'bigint' && _0n$2 < num && num < CURVE.n;
1028
- }
1029
- function assertGE(num) {
1030
- if (!isWithinCurveOrder(num))
1031
- throw new Error('Expected valid bigint: 0 < bigint < curve.n');
1214
+ return inRange(num, _1n$4, CURVE.n);
1032
1215
  }
1033
1216
  // Validates if priv key is valid and converts it to bigint.
1034
1217
  // Supports options allowedPrivateKeyLengths and wrapPrivateKey.
1035
1218
  function normPrivateKeyToScalar(key) {
1036
- const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n } = CURVE;
1219
+ const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n: N } = CURVE;
1037
1220
  if (lengths && typeof key !== 'bigint') {
1038
1221
  if (isBytes(key))
1039
1222
  key = bytesToHex(key);
@@ -1053,15 +1236,61 @@ function weierstrassPoints(opts) {
1053
1236
  throw new Error(`private key must be ${nByteLength} bytes, hex or bigint, not ${typeof key}`);
1054
1237
  }
1055
1238
  if (wrapPrivateKey)
1056
- num = mod(num, n); // disabled by default, enabled for BLS
1057
- assertGE(num); // num in range [1..N-1]
1239
+ num = mod(num, N); // disabled by default, enabled for BLS
1240
+ aInRange('private key', num, _1n$4, N); // num in range [1..N-1]
1058
1241
  return num;
1059
1242
  }
1060
- const pointPrecomputes = new Map();
1061
1243
  function assertPrjPoint(other) {
1062
1244
  if (!(other instanceof Point))
1063
1245
  throw new Error('ProjectivePoint expected');
1064
1246
  }
1247
+ // Memoized toAffine / validity check. They are heavy. Points are immutable.
1248
+ // Converts Projective point to affine (x, y) coordinates.
1249
+ // Can accept precomputed Z^-1 - for example, from invertBatch.
1250
+ // (x, y, z) ∋ (x=x/z, y=y/z)
1251
+ const toAffineMemo = memoized((p, iz) => {
1252
+ const { px: x, py: y, pz: z } = p;
1253
+ // Fast-path for normalized points
1254
+ if (Fp.eql(z, Fp.ONE))
1255
+ return { x, y };
1256
+ const is0 = p.is0();
1257
+ // If invZ was 0, we return zero point. However we still want to execute
1258
+ // all operations, so we replace invZ with a random number, 1.
1259
+ if (iz == null)
1260
+ iz = is0 ? Fp.ONE : Fp.inv(z);
1261
+ const ax = Fp.mul(x, iz);
1262
+ const ay = Fp.mul(y, iz);
1263
+ const zz = Fp.mul(z, iz);
1264
+ if (is0)
1265
+ return { x: Fp.ZERO, y: Fp.ZERO };
1266
+ if (!Fp.eql(zz, Fp.ONE))
1267
+ throw new Error('invZ was invalid');
1268
+ return { x: ax, y: ay };
1269
+ });
1270
+ // NOTE: on exception this will crash 'cached' and no value will be set.
1271
+ // Otherwise true will be return
1272
+ const assertValidMemo = memoized((p) => {
1273
+ if (p.is0()) {
1274
+ // (0, 1, 0) aka ZERO is invalid in most contexts.
1275
+ // In BLS, ZERO can be serialized, so we allow it.
1276
+ // (0, 0, 0) is wrong representation of ZERO and is always invalid.
1277
+ if (CURVE.allowInfinityPoint && !Fp.is0(p.py))
1278
+ return;
1279
+ throw new Error('bad point: ZERO');
1280
+ }
1281
+ // Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
1282
+ const { x, y } = p.toAffine();
1283
+ // Check if x, y are valid field elements
1284
+ if (!Fp.isValid(x) || !Fp.isValid(y))
1285
+ throw new Error('bad point: x or y not FE');
1286
+ const left = Fp.sqr(y); // y²
1287
+ const right = weierstrassEquation(x); // x³ + ax + b
1288
+ if (!Fp.eql(left, right))
1289
+ throw new Error('bad point: equation left != right');
1290
+ if (!p.isTorsionFree())
1291
+ throw new Error('bad point: not in prime-order subgroup');
1292
+ return true;
1293
+ });
1065
1294
  /**
1066
1295
  * Projective Point works in 3d / projective (homogeneous) coordinates: (x, y, z) ∋ (x=x/z, y=y/z)
1067
1296
  * Default Point works in 2d / affine coordinates: (x, y)
@@ -1078,6 +1307,7 @@ function weierstrassPoints(opts) {
1078
1307
  throw new Error('y required');
1079
1308
  if (pz == null || !Fp.isValid(pz))
1080
1309
  throw new Error('z required');
1310
+ Object.freeze(this);
1081
1311
  }
1082
1312
  // Does not validate if the point is on-curve.
1083
1313
  // Use fromHex instead, or call assertValidity() later.
@@ -1122,32 +1352,17 @@ function weierstrassPoints(opts) {
1122
1352
  static fromPrivateKey(privateKey) {
1123
1353
  return Point.BASE.multiply(normPrivateKeyToScalar(privateKey));
1124
1354
  }
1355
+ // Multiscalar Multiplication
1356
+ static msm(points, scalars) {
1357
+ return pippenger(Point, Fn, points, scalars);
1358
+ }
1125
1359
  // "Private method", don't use it directly
1126
1360
  _setWindowSize(windowSize) {
1127
- this._WINDOW_SIZE = windowSize;
1128
- pointPrecomputes.delete(this);
1361
+ wnaf.setWindowSize(this, windowSize);
1129
1362
  }
1130
1363
  // A point on curve is valid if it conforms to equation.
1131
1364
  assertValidity() {
1132
- if (this.is0()) {
1133
- // (0, 1, 0) aka ZERO is invalid in most contexts.
1134
- // In BLS, ZERO can be serialized, so we allow it.
1135
- // (0, 0, 0) is wrong representation of ZERO and is always invalid.
1136
- if (CURVE.allowInfinityPoint && !Fp.is0(this.py))
1137
- return;
1138
- throw new Error('bad point: ZERO');
1139
- }
1140
- // Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
1141
- const { x, y } = this.toAffine();
1142
- // Check if x, y are valid field elements
1143
- if (!Fp.isValid(x) || !Fp.isValid(y))
1144
- throw new Error('bad point: x or y not FE');
1145
- const left = Fp.sqr(y); // y²
1146
- const right = weierstrassEquation(x); // x³ + ax + b
1147
- if (!Fp.eql(left, right))
1148
- throw new Error('bad point: equation left != right');
1149
- if (!this.isTorsionFree())
1150
- throw new Error('bad point: not in prime-order subgroup');
1365
+ assertValidMemo(this);
1151
1366
  }
1152
1367
  hasEvenY() {
1153
1368
  const { y } = this.toAffine();
@@ -1274,28 +1489,25 @@ function weierstrassPoints(opts) {
1274
1489
  return this.equals(Point.ZERO);
1275
1490
  }
1276
1491
  wNAF(n) {
1277
- return wnaf.wNAFCached(this, pointPrecomputes, n, (comp) => {
1278
- const toInv = Fp.invertBatch(comp.map((p) => p.pz));
1279
- return comp.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
1280
- });
1492
+ return wnaf.wNAFCached(this, n, Point.normalizeZ);
1281
1493
  }
1282
1494
  /**
1283
1495
  * Non-constant-time multiplication. Uses double-and-add algorithm.
1284
1496
  * It's faster, but should only be used when you don't care about
1285
1497
  * an exposed private key e.g. sig verification, which works over *public* keys.
1286
1498
  */
1287
- multiplyUnsafe(n) {
1499
+ multiplyUnsafe(sc) {
1500
+ aInRange('scalar', sc, _0n$2, CURVE.n);
1288
1501
  const I = Point.ZERO;
1289
- if (n === _0n$2)
1502
+ if (sc === _0n$2)
1290
1503
  return I;
1291
- assertGE(n); // Will throw on 0
1292
- if (n === _1n$4)
1504
+ if (sc === _1n$4)
1293
1505
  return this;
1294
1506
  const { endo } = CURVE;
1295
1507
  if (!endo)
1296
- return wnaf.unsafeLadder(this, n);
1508
+ return wnaf.unsafeLadder(this, sc);
1297
1509
  // Apply endomorphism
1298
- let { k1neg, k1, k2neg, k2 } = endo.splitScalar(n);
1510
+ let { k1neg, k1, k2neg, k2 } = endo.splitScalar(sc);
1299
1511
  let k1p = I;
1300
1512
  let k2p = I;
1301
1513
  let d = this;
@@ -1325,12 +1537,11 @@ function weierstrassPoints(opts) {
1325
1537
  * @returns New point
1326
1538
  */
1327
1539
  multiply(scalar) {
1328
- assertGE(scalar);
1329
- let n = scalar;
1540
+ const { endo, n: N } = CURVE;
1541
+ aInRange('scalar', scalar, _1n$4, N);
1330
1542
  let point, fake; // Fake point is used to const-time mult
1331
- const { endo } = CURVE;
1332
1543
  if (endo) {
1333
- const { k1neg, k1, k2neg, k2 } = endo.splitScalar(n);
1544
+ const { k1neg, k1, k2neg, k2 } = endo.splitScalar(scalar);
1334
1545
  let { p: k1p, f: f1p } = this.wNAF(k1);
1335
1546
  let { p: k2p, f: f2p } = this.wNAF(k2);
1336
1547
  k1p = wnaf.constTimeNegate(k1neg, k1p);
@@ -1340,7 +1551,7 @@ function weierstrassPoints(opts) {
1340
1551
  fake = f1p.add(f2p);
1341
1552
  }
1342
1553
  else {
1343
- const { p, f } = this.wNAF(n);
1554
+ const { p, f } = this.wNAF(scalar);
1344
1555
  point = p;
1345
1556
  fake = f;
1346
1557
  }
@@ -1364,20 +1575,7 @@ function weierstrassPoints(opts) {
1364
1575
  // Can accept precomputed Z^-1 - for example, from invertBatch.
1365
1576
  // (x, y, z) ∋ (x=x/z, y=y/z)
1366
1577
  toAffine(iz) {
1367
- const { px: x, py: y, pz: z } = this;
1368
- const is0 = this.is0();
1369
- // If invZ was 0, we return zero point. However we still want to execute
1370
- // all operations, so we replace invZ with a random number, 1.
1371
- if (iz == null)
1372
- iz = is0 ? Fp.ONE : Fp.inv(z);
1373
- const ax = Fp.mul(x, iz);
1374
- const ay = Fp.mul(y, iz);
1375
- const zz = Fp.mul(z, iz);
1376
- if (is0)
1377
- return { x: Fp.ZERO, y: Fp.ZERO };
1378
- if (!Fp.eql(zz, Fp.ONE))
1379
- throw new Error('invZ was invalid');
1380
- return { x: ax, y: ay };
1578
+ return toAffineMemo(this, iz);
1381
1579
  }
1382
1580
  isTorsionFree() {
1383
1581
  const { h: cofactor, isTorsionFree } = CURVE;
@@ -1396,10 +1594,12 @@ function weierstrassPoints(opts) {
1396
1594
  return this.multiplyUnsafe(CURVE.h);
1397
1595
  }
1398
1596
  toRawBytes(isCompressed = true) {
1597
+ abool('isCompressed', isCompressed);
1399
1598
  this.assertValidity();
1400
1599
  return toBytes(Point, this, isCompressed);
1401
1600
  }
1402
1601
  toHex(isCompressed = true) {
1602
+ abool('isCompressed', isCompressed);
1403
1603
  return bytesToHex(this.toRawBytes(isCompressed));
1404
1604
  }
1405
1605
  }
@@ -1429,14 +1629,18 @@ function validateOpts$2(curve) {
1429
1629
  });
1430
1630
  return Object.freeze({ lowS: true, ...opts });
1431
1631
  }
1632
+ /**
1633
+ * Creates short weierstrass curve and ECDSA signature methods for it.
1634
+ * @example
1635
+ * import { Field } from '@noble/curves/abstract/modular';
1636
+ * // Before that, define BigInt-s: a, b, p, n, Gx, Gy
1637
+ * const curve = weierstrass({ a, b, Fp: Field(p), n, Gx, Gy, h: 1n })
1638
+ */
1432
1639
  function weierstrass(curveDef) {
1433
1640
  const CURVE = validateOpts$2(curveDef);
1434
1641
  const { Fp, n: CURVE_ORDER } = CURVE;
1435
1642
  const compressedLen = Fp.BYTES + 1; // e.g. 33 for 32
1436
1643
  const uncompressedLen = 2 * Fp.BYTES + 1; // e.g. 65 for 32
1437
- function isValidFieldElement(num) {
1438
- return _0n$2 < num && num < Fp.ORDER; // 0 is banned since it's not invertible FE
1439
- }
1440
1644
  function modN(a) {
1441
1645
  return mod(a, CURVE_ORDER);
1442
1646
  }
@@ -1449,6 +1653,7 @@ function weierstrass(curveDef) {
1449
1653
  const a = point.toAffine();
1450
1654
  const x = Fp.toBytes(a.x);
1451
1655
  const cat = concatBytes;
1656
+ abool('isCompressed', isCompressed);
1452
1657
  if (isCompressed) {
1453
1658
  return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x);
1454
1659
  }
@@ -1463,7 +1668,7 @@ function weierstrass(curveDef) {
1463
1668
  // this.assertValidity() is done inside of fromHex
1464
1669
  if (len === compressedLen && (head === 0x02 || head === 0x03)) {
1465
1670
  const x = bytesToNumberBE(tail);
1466
- if (!isValidFieldElement(x))
1671
+ if (!inRange(x, _1n$4, Fp.ORDER))
1467
1672
  throw new Error('Point is not on curve');
1468
1673
  const y2 = weierstrassEquation(x); // y² = x³ + ax + b
1469
1674
  let y;
@@ -1524,11 +1729,8 @@ function weierstrass(curveDef) {
1524
1729
  return new Signature(r, s);
1525
1730
  }
1526
1731
  assertValidity() {
1527
- // can use assertGE here
1528
- if (!isWithinCurveOrder(this.r))
1529
- throw new Error('r must be 0 < r < CURVE.n');
1530
- if (!isWithinCurveOrder(this.s))
1531
- throw new Error('s must be 0 < s < CURVE.n');
1732
+ aInRange('r', this.r, _1n$4, CURVE_ORDER); // r in [1..N]
1733
+ aInRange('s', this.s, _1n$4, CURVE_ORDER); // s in [1..N]
1532
1734
  }
1533
1735
  addRecoveryBit(recovery) {
1534
1736
  return new Signature(this.r, this.s, recovery);
@@ -1671,10 +1873,7 @@ function weierstrass(curveDef) {
1671
1873
  * Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`.
1672
1874
  */
1673
1875
  function int2octets(num) {
1674
- if (typeof num !== 'bigint')
1675
- throw new Error('bigint expected');
1676
- if (!(_0n$2 <= num && num < ORDER_MASK))
1677
- throw new Error(`bigint expected < 2^${CURVE.nBitLength}`);
1876
+ aInRange(`num < 2^${CURVE.nBitLength}`, num, _0n$2, ORDER_MASK);
1678
1877
  // works with order, can have different size than numToField!
1679
1878
  return numberToBytesBE(num, CURVE.nByteLength);
1680
1879
  }
@@ -1691,6 +1890,7 @@ function weierstrass(curveDef) {
1691
1890
  if (lowS == null)
1692
1891
  lowS = true; // RFC6979 3.2: we skip step A, because we already provide hash
1693
1892
  msgHash = ensureBytes('msgHash', msgHash);
1893
+ validateSigVerOpts(opts);
1694
1894
  if (prehash)
1695
1895
  msgHash = ensureBytes('prehashed msgHash', hash(msgHash));
1696
1896
  // We can't later call bits2octets, since nested bits2int is broken for curves
@@ -1777,6 +1977,7 @@ function weierstrass(curveDef) {
1777
1977
  publicKey = ensureBytes('publicKey', publicKey);
1778
1978
  if ('strict' in opts)
1779
1979
  throw new Error('options.strict was renamed to lowS');
1980
+ validateSigVerOpts(opts);
1780
1981
  const { lowS, prehash } = opts;
1781
1982
  let _sig = undefined;
1782
1983
  let P;
@@ -1946,12 +2147,19 @@ function validateOpts$1(curve) {
1946
2147
  // Set defaults
1947
2148
  return Object.freeze({ ...opts });
1948
2149
  }
1949
- // It is not generic twisted curve for now, but ed25519/ed448 generic implementation
2150
+ /**
2151
+ * Creates Twisted Edwards curve with EdDSA signatures.
2152
+ * @example
2153
+ * import { Field } from '@noble/curves/abstract/modular';
2154
+ * // Before that, define BigInt-s: a, d, p, n, Gx, Gy, h
2155
+ * const curve = twistedEdwards({ a, d, Fp: Field(p), n, Gx, Gy, h })
2156
+ */
1950
2157
  function twistedEdwards(curveDef) {
1951
2158
  const CURVE = validateOpts$1(curveDef);
1952
2159
  const { Fp, n: CURVE_ORDER, prehash: prehash, hash: cHash, randomBytes, nByteLength, h: cofactor, } = CURVE;
1953
2160
  const MASK = _2n$2 << (BigInt(nByteLength * 8) - _1n$3);
1954
2161
  const modP = Fp.create; // Function overrides
2162
+ const Fn = Field(CURVE.n, CURVE.nBitLength);
1955
2163
  // sqrt(u/v)
1956
2164
  const uvRatio = CURVE.uvRatio ||
1957
2165
  ((u, v) => {
@@ -1965,28 +2173,59 @@ function twistedEdwards(curveDef) {
1965
2173
  const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes) => bytes); // NOOP
1966
2174
  const domain = CURVE.domain ||
1967
2175
  ((data, ctx, phflag) => {
2176
+ abool('phflag', phflag);
1968
2177
  if (ctx.length || phflag)
1969
2178
  throw new Error('Contexts/pre-hash are not supported');
1970
2179
  return data;
1971
2180
  }); // NOOP
1972
- const inBig = (n) => typeof n === 'bigint' && _0n$1 < n; // n in [1..]
1973
- const inRange = (n, max) => inBig(n) && inBig(max) && n < max; // n in [1..max-1]
1974
- const in0MaskRange = (n) => n === _0n$1 || inRange(n, MASK); // n in [0..MASK-1]
1975
- function assertInRange(n, max) {
1976
- // n in [1..max-1]
1977
- if (inRange(n, max))
1978
- return n;
1979
- throw new Error(`Expected valid scalar < ${max}, got ${typeof n} ${n}`);
2181
+ // 0 <= n < MASK
2182
+ // Coordinates larger than Fp.ORDER are allowed for zip215
2183
+ function aCoordinate(title, n) {
2184
+ aInRange('coordinate ' + title, n, _0n$1, MASK);
1980
2185
  }
1981
- function assertGE0(n) {
1982
- // n in [0..CURVE_ORDER-1]
1983
- return n === _0n$1 ? n : assertInRange(n, CURVE_ORDER); // GE = prime subgroup, not full group
1984
- }
1985
- const pointPrecomputes = new Map();
1986
- function isPoint(other) {
2186
+ function assertPoint(other) {
1987
2187
  if (!(other instanceof Point))
1988
2188
  throw new Error('ExtendedPoint expected');
1989
2189
  }
2190
+ // Converts Extended point to default (x, y) coordinates.
2191
+ // Can accept precomputed Z^-1 - for example, from invertBatch.
2192
+ const toAffineMemo = memoized((p, iz) => {
2193
+ const { ex: x, ey: y, ez: z } = p;
2194
+ const is0 = p.is0();
2195
+ if (iz == null)
2196
+ iz = is0 ? _8n : Fp.inv(z); // 8 was chosen arbitrarily
2197
+ const ax = modP(x * iz);
2198
+ const ay = modP(y * iz);
2199
+ const zz = modP(z * iz);
2200
+ if (is0)
2201
+ return { x: _0n$1, y: _1n$3 };
2202
+ if (zz !== _1n$3)
2203
+ throw new Error('invZ was invalid');
2204
+ return { x: ax, y: ay };
2205
+ });
2206
+ const assertValidMemo = memoized((p) => {
2207
+ const { a, d } = CURVE;
2208
+ if (p.is0())
2209
+ throw new Error('bad point: ZERO'); // TODO: optimize, with vars below?
2210
+ // Equation in affine coordinates: ax² + y² = 1 + dx²y²
2211
+ // Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²
2212
+ const { ex: X, ey: Y, ez: Z, et: T } = p;
2213
+ const X2 = modP(X * X); // X²
2214
+ const Y2 = modP(Y * Y); // Y²
2215
+ const Z2 = modP(Z * Z); // Z²
2216
+ const Z4 = modP(Z2 * Z2); // Z⁴
2217
+ const aX2 = modP(X2 * a); // aX²
2218
+ const left = modP(Z2 * modP(aX2 + Y2)); // (aX² + Y²)Z²
2219
+ const right = modP(Z4 + modP(d * modP(X2 * Y2))); // Z⁴ + dX²Y²
2220
+ if (left !== right)
2221
+ throw new Error('bad point: equation left != right (1)');
2222
+ // In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T
2223
+ const XY = modP(X * Y);
2224
+ const ZT = modP(Z * T);
2225
+ if (XY !== ZT)
2226
+ throw new Error('bad point: equation left != right (2)');
2227
+ return true;
2228
+ });
1990
2229
  // Extended Point works in extended coordinates: (x, y, z, t) ∋ (x=x/z, y=y/z, t=xy).
1991
2230
  // https://en.wikipedia.org/wiki/Twisted_Edwards_curve#Extended_coordinates
1992
2231
  class Point {
@@ -1995,14 +2234,11 @@ function twistedEdwards(curveDef) {
1995
2234
  this.ey = ey;
1996
2235
  this.ez = ez;
1997
2236
  this.et = et;
1998
- if (!in0MaskRange(ex))
1999
- throw new Error('x required');
2000
- if (!in0MaskRange(ey))
2001
- throw new Error('y required');
2002
- if (!in0MaskRange(ez))
2003
- throw new Error('z required');
2004
- if (!in0MaskRange(et))
2005
- throw new Error('t required');
2237
+ aCoordinate('x', ex);
2238
+ aCoordinate('y', ey);
2239
+ aCoordinate('z', ez);
2240
+ aCoordinate('t', et);
2241
+ Object.freeze(this);
2006
2242
  }
2007
2243
  get x() {
2008
2244
  return this.toAffine().x;
@@ -2014,46 +2250,30 @@ function twistedEdwards(curveDef) {
2014
2250
  if (p instanceof Point)
2015
2251
  throw new Error('extended point not allowed');
2016
2252
  const { x, y } = p || {};
2017
- if (!in0MaskRange(x) || !in0MaskRange(y))
2018
- throw new Error('invalid affine point');
2253
+ aCoordinate('x', x);
2254
+ aCoordinate('y', y);
2019
2255
  return new Point(x, y, _1n$3, modP(x * y));
2020
2256
  }
2021
2257
  static normalizeZ(points) {
2022
2258
  const toInv = Fp.invertBatch(points.map((p) => p.ez));
2023
2259
  return points.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
2024
2260
  }
2261
+ // Multiscalar Multiplication
2262
+ static msm(points, scalars) {
2263
+ return pippenger(Point, Fn, points, scalars);
2264
+ }
2025
2265
  // "Private method", don't use it directly
2026
2266
  _setWindowSize(windowSize) {
2027
- this._WINDOW_SIZE = windowSize;
2028
- pointPrecomputes.delete(this);
2267
+ wnaf.setWindowSize(this, windowSize);
2029
2268
  }
2030
2269
  // Not required for fromHex(), which always creates valid points.
2031
2270
  // Could be useful for fromAffine().
2032
2271
  assertValidity() {
2033
- const { a, d } = CURVE;
2034
- if (this.is0())
2035
- throw new Error('bad point: ZERO'); // TODO: optimize, with vars below?
2036
- // Equation in affine coordinates: ax² + y² = 1 + dx²y²
2037
- // Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²
2038
- const { ex: X, ey: Y, ez: Z, et: T } = this;
2039
- const X2 = modP(X * X); // X²
2040
- const Y2 = modP(Y * Y); // Y²
2041
- const Z2 = modP(Z * Z); // Z²
2042
- const Z4 = modP(Z2 * Z2); // Z⁴
2043
- const aX2 = modP(X2 * a); // aX²
2044
- const left = modP(Z2 * modP(aX2 + Y2)); // (aX² + Y²)Z²
2045
- const right = modP(Z4 + modP(d * modP(X2 * Y2))); // Z⁴ + dX²Y²
2046
- if (left !== right)
2047
- throw new Error('bad point: equation left != right (1)');
2048
- // In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T
2049
- const XY = modP(X * Y);
2050
- const ZT = modP(Z * T);
2051
- if (XY !== ZT)
2052
- throw new Error('bad point: equation left != right (2)');
2272
+ assertValidMemo(this);
2053
2273
  }
2054
2274
  // Compare one point to another.
2055
2275
  equals(other) {
2056
- isPoint(other);
2276
+ assertPoint(other);
2057
2277
  const { ex: X1, ey: Y1, ez: Z1 } = this;
2058
2278
  const { ex: X2, ey: Y2, ez: Z2 } = other;
2059
2279
  const X1Z2 = modP(X1 * Z2);
@@ -2094,7 +2314,7 @@ function twistedEdwards(curveDef) {
2094
2314
  // https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#addition-add-2008-hwcd
2095
2315
  // Cost: 9M + 1*a + 1*d + 7add.
2096
2316
  add(other) {
2097
- isPoint(other);
2317
+ assertPoint(other);
2098
2318
  const { a, d } = CURVE;
2099
2319
  const { ex: X1, ey: Y1, ez: Z1, et: T1 } = this;
2100
2320
  const { ex: X2, ey: Y2, ez: Z2, et: T2 } = other;
@@ -2137,11 +2357,13 @@ function twistedEdwards(curveDef) {
2137
2357
  return this.add(other.negate());
2138
2358
  }
2139
2359
  wNAF(n) {
2140
- return wnaf.wNAFCached(this, pointPrecomputes, n, Point.normalizeZ);
2360
+ return wnaf.wNAFCached(this, n, Point.normalizeZ);
2141
2361
  }
2142
2362
  // Constant-time multiplication.
2143
2363
  multiply(scalar) {
2144
- const { p, f } = this.wNAF(assertInRange(scalar, CURVE_ORDER));
2364
+ const n = scalar;
2365
+ aInRange('scalar', n, _1n$3, CURVE_ORDER); // 1 <= scalar < L
2366
+ const { p, f } = this.wNAF(n);
2145
2367
  return Point.normalizeZ([p, f])[0];
2146
2368
  }
2147
2369
  // Non-constant-time multiplication. Uses double-and-add algorithm.
@@ -2149,7 +2371,8 @@ function twistedEdwards(curveDef) {
2149
2371
  // an exposed private key e.g. sig verification.
2150
2372
  // Does NOT allow scalars higher than CURVE.n.
2151
2373
  multiplyUnsafe(scalar) {
2152
- let n = assertGE0(scalar); // 0 <= scalar < CURVE.n
2374
+ const n = scalar;
2375
+ aInRange('scalar', n, _0n$1, CURVE_ORDER); // 0 <= scalar < L
2153
2376
  if (n === _0n$1)
2154
2377
  return I;
2155
2378
  if (this.equals(I) || n === _1n$3)
@@ -2173,18 +2396,7 @@ function twistedEdwards(curveDef) {
2173
2396
  // Converts Extended point to default (x, y) coordinates.
2174
2397
  // Can accept precomputed Z^-1 - for example, from invertBatch.
2175
2398
  toAffine(iz) {
2176
- const { ex: x, ey: y, ez: z } = this;
2177
- const is0 = this.is0();
2178
- if (iz == null)
2179
- iz = is0 ? _8n : Fp.inv(z); // 8 was chosen arbitrarily
2180
- const ax = modP(x * iz);
2181
- const ay = modP(y * iz);
2182
- const zz = modP(z * iz);
2183
- if (is0)
2184
- return { x: _0n$1, y: _1n$3 };
2185
- if (zz !== _1n$3)
2186
- throw new Error('invZ was invalid');
2187
- return { x: ax, y: ay };
2399
+ return toAffineMemo(this, iz);
2188
2400
  }
2189
2401
  clearCofactor() {
2190
2402
  const { h: cofactor } = CURVE;
@@ -2198,18 +2410,16 @@ function twistedEdwards(curveDef) {
2198
2410
  const { d, a } = CURVE;
2199
2411
  const len = Fp.BYTES;
2200
2412
  hex = ensureBytes('pointHex', hex, len); // copy hex to a new array
2413
+ abool('zip215', zip215);
2201
2414
  const normed = hex.slice(); // copy again, we'll manipulate it
2202
2415
  const lastByte = hex[len - 1]; // select last byte
2203
2416
  normed[len - 1] = lastByte & ~0x80; // clear last bit
2204
2417
  const y = bytesToNumberLE(normed);
2205
- if (y === _0n$1) ;
2206
- else {
2207
- // RFC8032 prohibits >= p, but ZIP215 doesn't
2208
- if (zip215)
2209
- assertInRange(y, MASK); // zip215=true [1..P-1] (2^255-19-1 for ed25519)
2210
- else
2211
- assertInRange(y, Fp.ORDER); // zip215=false [1..MASK-1] (2^256-1 for ed25519)
2212
- }
2418
+ // RFC8032 prohibits >= p, but ZIP215 doesn't
2419
+ // zip215=true: 0 <= y < MASK (2^256 for ed25519)
2420
+ // zip215=false: 0 <= y < P (2^255-19 for ed25519)
2421
+ const max = zip215 ? MASK : Fp.ORDER;
2422
+ aInRange('pointHex.y', y, _0n$1, max);
2213
2423
  // Ed25519: x² = (y²-1)/(dy²+1) mod p. Ed448: x² = (y²-1)/(dy²-1) mod p. Generic case:
2214
2424
  // ax²+y²=1+dx²y² => y²-1=dx²y²-ax² => y²-1=x²(dy²-a) => x²=(y²-1)/(dy²-a)
2215
2425
  const y2 = modP(y * y); // denominator is always non-0 mod p.
@@ -2284,7 +2494,7 @@ function twistedEdwards(curveDef) {
2284
2494
  const R = G.multiply(r).toRawBytes(); // R = rG
2285
2495
  const k = hashDomainToScalar(options.context, R, pointBytes, msg); // R || A || PH(M)
2286
2496
  const s = modN(r + k * scalar); // S = (r + k * s) mod L
2287
- assertGE0(s); // 0 <= s < l
2497
+ aInRange('signature.s', s, _0n$1, CURVE_ORDER); // 0 <= s < l
2288
2498
  const res = concatBytes(R, numberToBytesLE(s, Fp.BYTES));
2289
2499
  return ensureBytes('result', res, nByteLength * 2); // 64-byte signature
2290
2500
  }
@@ -2294,6 +2504,8 @@ function twistedEdwards(curveDef) {
2294
2504
  const len = Fp.BYTES; // Verifies EdDSA signature against message and public key. RFC8032 5.1.7.
2295
2505
  sig = ensureBytes('signature', sig, 2 * len); // An extended group equation is checked.
2296
2506
  msg = ensureBytes('message', msg);
2507
+ if (zip215 !== undefined)
2508
+ abool('zip215', zip215);
2297
2509
  if (prehash)
2298
2510
  msg = prehash(msg); // for ed25519ph, etc
2299
2511
  const s = bytesToNumberLE(sig.slice(len, 2 * len));
@@ -2386,12 +2598,6 @@ function montgomery(curveDef) {
2386
2598
  x_3 = modP(x_3 + dummy);
2387
2599
  return [x_2, x_3];
2388
2600
  }
2389
- // Accepts 0 as well
2390
- function assertFieldElement(n) {
2391
- if (typeof n === 'bigint' && _0n <= n && n < P)
2392
- return n;
2393
- throw new Error('Expected valid scalar 0 < scalar < CURVE.P');
2394
- }
2395
2601
  // x25519 from 4
2396
2602
  // The constant a24 is (486662 - 2) / 4 = 121665 for curve25519/X25519
2397
2603
  const a24 = (CURVE.a - BigInt(2)) / BigInt(4);
@@ -2401,11 +2607,12 @@ function montgomery(curveDef) {
2401
2607
  * @param scalar by which the point would be multiplied
2402
2608
  * @returns new Point on Montgomery curve
2403
2609
  */
2404
- function montgomeryLadder(pointU, scalar) {
2405
- const u = assertFieldElement(pointU);
2610
+ function montgomeryLadder(u, scalar) {
2611
+ aInRange('u', u, _0n, P);
2612
+ aInRange('scalar', scalar, _0n, P);
2406
2613
  // Section 5: Implementations MUST accept non-canonical values and process them as
2407
2614
  // if they had been reduced modulo the field prime.
2408
- const k = assertFieldElement(scalar);
2615
+ const k = scalar;
2409
2616
  const x_1 = u;
2410
2617
  let x_2 = _1n$2;
2411
2618
  let z_2 = _0n;
@@ -2658,6 +2865,9 @@ function sqrtMod(y) {
2658
2865
  return root;
2659
2866
  }
2660
2867
  const Fp$3 = Field(secp256k1P, undefined, undefined, { sqrt: sqrtMod });
2868
+ /**
2869
+ * secp256k1 short weierstrass curve and ECDSA signatures over it.
2870
+ */
2661
2871
  const secp256k1 = createCurve({
2662
2872
  a: BigInt(0), // equation params: a, b
2663
2873
  b: BigInt(7), // Seem to be rigid: bitcointalk.org/index.php?topic=289795.msg3183975#msg3183975