@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.
- package/README.md +12 -13
- package/dist/lightweight/argon2id.min.mjs +2 -2
- package/dist/lightweight/argon2id.min.mjs.map +1 -1
- package/dist/lightweight/argon2id.mjs +1 -1
- package/dist/lightweight/legacy_ciphers.min.mjs +2 -2
- package/dist/lightweight/legacy_ciphers.min.mjs.map +1 -1
- package/dist/lightweight/legacy_ciphers.mjs +1 -1
- package/dist/lightweight/noble_curves.min.mjs +11 -11
- package/dist/lightweight/noble_curves.min.mjs.map +1 -1
- package/dist/lightweight/noble_curves.mjs +416 -206
- package/dist/lightweight/noble_hashes.min.mjs +1 -1
- package/dist/lightweight/noble_hashes.min.mjs.map +1 -1
- package/dist/lightweight/noble_hashes.mjs +7 -2
- package/dist/lightweight/openpgp.min.mjs +4 -3
- package/dist/lightweight/openpgp.min.mjs.map +1 -1
- package/dist/lightweight/openpgp.mjs +4107 -4520
- package/dist/lightweight/sha3.min.mjs +3 -3
- package/dist/lightweight/sha3.min.mjs.map +1 -1
- package/dist/lightweight/sha3.mjs +17 -4
- package/dist/node/openpgp.cjs +3601 -3782
- package/dist/node/openpgp.min.cjs +14 -12
- package/dist/node/openpgp.min.cjs.map +1 -1
- package/dist/node/openpgp.min.mjs +14 -12
- package/dist/node/openpgp.min.mjs.map +1 -1
- package/dist/node/openpgp.mjs +3601 -3782
- package/dist/openpgp.js +3624 -3809
- package/dist/openpgp.min.js +14 -12
- package/dist/openpgp.min.js.map +1 -1
- package/dist/openpgp.min.mjs +14 -12
- package/dist/openpgp.min.mjs.map +1 -1
- package/dist/openpgp.mjs +3624 -3809
- package/openpgp.d.ts +1 -0
- package/package.json +19 -19
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! OpenPGP.js v6.0.0-beta.
|
|
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,
|
|
868
|
-
|
|
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 =
|
|
935
|
+
let comp = pointPrecomputes.get(P);
|
|
872
936
|
if (!comp) {
|
|
873
937
|
comp = this.precomputeWindow(P, W);
|
|
874
|
-
if (W !== 1)
|
|
875
|
-
|
|
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
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
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
|
-
|
|
963
|
-
if (
|
|
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
|
-
|
|
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
|
-
|
|
975
|
-
const
|
|
976
|
-
|
|
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
|
|
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,
|
|
1057
|
-
|
|
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
|
|
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
|
-
|
|
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,
|
|
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(
|
|
1499
|
+
multiplyUnsafe(sc) {
|
|
1500
|
+
aInRange('scalar', sc, _0n$2, CURVE.n);
|
|
1288
1501
|
const I = Point.ZERO;
|
|
1289
|
-
if (
|
|
1502
|
+
if (sc === _0n$2)
|
|
1290
1503
|
return I;
|
|
1291
|
-
|
|
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,
|
|
1508
|
+
return wnaf.unsafeLadder(this, sc);
|
|
1297
1509
|
// Apply endomorphism
|
|
1298
|
-
let { k1neg, k1, k2neg, k2 } = endo.splitScalar(
|
|
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
|
-
|
|
1329
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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 (!
|
|
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
|
-
//
|
|
1528
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
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
|
|
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
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
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
|
-
|
|
2018
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
2360
|
+
return wnaf.wNAFCached(this, n, Point.normalizeZ);
|
|
2141
2361
|
}
|
|
2142
2362
|
// Constant-time multiplication.
|
|
2143
2363
|
multiply(scalar) {
|
|
2144
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
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
|
-
|
|
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(
|
|
2405
|
-
|
|
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 =
|
|
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
|