@noble/curves 1.1.0 → 1.3.0
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 +295 -258
- package/abstract/bls.d.ts +27 -10
- package/abstract/bls.d.ts.map +1 -1
- package/abstract/bls.js +60 -10
- package/abstract/bls.js.map +1 -1
- package/abstract/curve.js.map +1 -1
- package/abstract/edwards.js.map +1 -1
- package/abstract/hash-to-curve.d.ts +2 -2
- package/abstract/hash-to-curve.d.ts.map +1 -1
- package/abstract/hash-to-curve.js +22 -16
- package/abstract/hash-to-curve.js.map +1 -1
- package/abstract/modular.d.ts +51 -11
- package/abstract/modular.d.ts.map +1 -1
- package/abstract/modular.js +79 -21
- package/abstract/modular.js.map +1 -1
- package/abstract/montgomery.d.ts.map +1 -1
- package/abstract/montgomery.js +5 -7
- package/abstract/montgomery.js.map +1 -1
- package/abstract/poseidon.d.ts.map +1 -1
- package/abstract/poseidon.js +39 -41
- package/abstract/poseidon.js.map +1 -1
- package/abstract/utils.d.ts +3 -1
- package/abstract/utils.d.ts.map +1 -1
- package/abstract/utils.js +56 -31
- package/abstract/utils.js.map +1 -1
- package/abstract/weierstrass.d.ts +25 -28
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +17 -15
- package/abstract/weierstrass.js.map +1 -1
- package/bls12-381.d.ts.map +1 -1
- package/bls12-381.js +142 -88
- package/bls12-381.js.map +1 -1
- package/bn254.d.ts +3 -2
- package/bn254.d.ts.map +1 -1
- package/bn254.js +3 -2
- package/bn254.js.map +1 -1
- package/ed25519.d.ts +5 -2
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +17 -8
- package/ed25519.js.map +1 -1
- package/ed448.d.ts +53 -2
- package/ed448.d.ts.map +1 -1
- package/ed448.js +216 -29
- package/ed448.js.map +1 -1
- package/esm/abstract/bls.js +61 -11
- package/esm/abstract/bls.js.map +1 -1
- package/esm/abstract/curve.js.map +1 -1
- package/esm/abstract/edwards.js.map +1 -1
- package/esm/abstract/hash-to-curve.js +23 -17
- package/esm/abstract/hash-to-curve.js.map +1 -1
- package/esm/abstract/modular.js +75 -20
- package/esm/abstract/modular.js.map +1 -1
- package/esm/abstract/montgomery.js +5 -7
- package/esm/abstract/montgomery.js.map +1 -1
- package/esm/abstract/poseidon.js +39 -41
- package/esm/abstract/poseidon.js.map +1 -1
- package/esm/abstract/utils.js +54 -30
- package/esm/abstract/utils.js.map +1 -1
- package/esm/abstract/weierstrass.js +17 -15
- package/esm/abstract/weierstrass.js.map +1 -1
- package/esm/bls12-381.js +143 -89
- package/esm/bls12-381.js.map +1 -1
- package/esm/bn254.js +3 -2
- package/esm/bn254.js.map +1 -1
- package/esm/ed25519.js +17 -8
- package/esm/ed25519.js.map +1 -1
- package/esm/ed448.js +218 -32
- package/esm/ed448.js.map +1 -1
- package/esm/jubjub.js +1 -1
- package/esm/jubjub.js.map +1 -1
- package/esm/p256.js +2 -2
- package/esm/p256.js.map +1 -1
- package/esm/p384.js +2 -2
- package/esm/p384.js.map +1 -1
- package/esm/p521.js +3 -3
- package/esm/p521.js.map +1 -1
- package/esm/package.json +1 -4
- package/esm/secp256k1.js +6 -6
- package/esm/secp256k1.js.map +1 -1
- package/jubjub.js.map +1 -1
- package/p256.js +2 -2
- package/p256.js.map +1 -1
- package/p384.js +2 -2
- package/p384.js.map +1 -1
- package/p521.js +3 -3
- package/p521.js.map +1 -1
- package/package.json +7 -6
- package/secp256k1.js +6 -6
- package/secp256k1.js.map +1 -1
- package/src/abstract/bls.ts +120 -22
- package/src/abstract/hash-to-curve.ts +24 -17
- package/src/abstract/modular.ts +81 -22
- package/src/abstract/montgomery.ts +4 -6
- package/src/abstract/poseidon.ts +39 -40
- package/src/abstract/utils.ts +55 -26
- package/src/abstract/weierstrass.ts +29 -18
- package/src/bls12-381.ts +132 -75
- package/src/bn254.ts +3 -2
- package/src/ed25519.ts +19 -8
- package/src/ed448.ts +267 -34
- package/src/jubjub.ts +1 -1
package/src/bls12-381.ts
CHANGED
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2
2
|
|
|
3
|
-
// bls12-381 pairing-friendly Barreto-Lynn-Scott elliptic curve construction
|
|
4
|
-
// - Construct zk-SNARKs at the
|
|
5
|
-
// -
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
// The library uses G1 for public keys and G2 for signatures. Support for G1 signatures is planned.
|
|
9
|
-
// Compatible with Algorand, Chia, Dfinity, Ethereum, FIL, Zcash. Matches specs
|
|
10
|
-
// [pairing-curves-11](https://tools.ietf.org/html/draft-irtf-cfrg-pairing-friendly-curves-11),
|
|
11
|
-
// [bls-sigs-04](https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04),
|
|
12
|
-
// [hash-to-curve-12](https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-12).
|
|
3
|
+
// bls12-381 is pairing-friendly Barreto-Lynn-Scott elliptic curve construction allowing to:
|
|
4
|
+
// - Construct zk-SNARKs at the 120-bit security
|
|
5
|
+
// - Efficiently verify N aggregate signatures with 1 pairing and N ec additions:
|
|
6
|
+
// the Boneh-Lynn-Shacham signature scheme is orders of magnitude more efficient than Schnorr
|
|
13
7
|
//
|
|
14
8
|
// ### Summary
|
|
15
9
|
// 1. BLS Relies on Bilinear Pairing (expensive)
|
|
@@ -25,8 +19,17 @@
|
|
|
25
19
|
// - `S = pk x H(m)` - signing
|
|
26
20
|
// - `e(P, H(m)) == e(G, S)` - verification using pairings
|
|
27
21
|
// - `e(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))` - signature aggregation
|
|
28
|
-
//
|
|
29
|
-
//
|
|
22
|
+
//
|
|
23
|
+
// ### Compatibility and notes
|
|
24
|
+
// 1. It is compatible with Algorand, Chia, Dfinity, Ethereum, Filecoin, ZEC
|
|
25
|
+
// Filecoin uses little endian byte arrays for private keys - make sure to reverse byte order.
|
|
26
|
+
// 2. Some projects use G2 for public keys and G1 for signatures. It's called "short signature"
|
|
27
|
+
// 3. Curve security level is about 120 bits as per Barbulescu-Duquesne 2017
|
|
28
|
+
// https://hal.science/hal-01534101/file/main.pdf
|
|
29
|
+
// 4. Compatible with specs:
|
|
30
|
+
// [cfrg-pairing-friendly-curves-11](https://tools.ietf.org/html/draft-irtf-cfrg-pairing-friendly-curves-11),
|
|
31
|
+
// [cfrg-bls-signature-05](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bls-signature-05),
|
|
32
|
+
// [RFC 9380](https://www.rfc-editor.org/rfc/rfc9380).
|
|
30
33
|
import { sha256 } from '@noble/hashes/sha256';
|
|
31
34
|
import { randomBytes } from '@noble/hashes/utils';
|
|
32
35
|
import { bls, CurveFn } from './abstract/bls.js';
|
|
@@ -37,7 +40,6 @@ import {
|
|
|
37
40
|
numberToBytesBE,
|
|
38
41
|
bytesToNumberBE,
|
|
39
42
|
bitLen,
|
|
40
|
-
bitSet,
|
|
41
43
|
bitGet,
|
|
42
44
|
Hex,
|
|
43
45
|
bitMask,
|
|
@@ -177,7 +179,7 @@ const Fp2: mod.IField<Fp2> & Fp2Utils = {
|
|
|
177
179
|
if (im1 > im2 || (im1 === im2 && re1 > re2)) return x1;
|
|
178
180
|
return x2;
|
|
179
181
|
},
|
|
180
|
-
// Same as
|
|
182
|
+
// Same as sgn0_m_eq_2 in RFC 9380
|
|
181
183
|
isOdd: (x: Fp2) => {
|
|
182
184
|
const { re: x0, im: x1 } = Fp2.reim(x);
|
|
183
185
|
const sign_0 = x0 % _2n;
|
|
@@ -780,8 +782,7 @@ const FP12_FROBENIUS_COEFFICIENTS = [
|
|
|
780
782
|
|
|
781
783
|
// HashToCurve
|
|
782
784
|
|
|
783
|
-
// 3-isogeny map from E' to E
|
|
784
|
-
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-E.3
|
|
785
|
+
// 3-isogeny map from E' to E https://www.rfc-editor.org/rfc/rfc9380#appendix-E.3
|
|
785
786
|
const isogenyMapG2 = isogenyMap(
|
|
786
787
|
Fp2,
|
|
787
788
|
[
|
|
@@ -985,7 +986,7 @@ function G2psi2(c: ProjConstructor<Fp2>, P: ProjPointType<Fp2>) {
|
|
|
985
986
|
//
|
|
986
987
|
// Parameter definitions are in section 5.3 of the spec unless otherwise noted.
|
|
987
988
|
// Parameter values come from section 8.8.2 of the spec.
|
|
988
|
-
// https://
|
|
989
|
+
// https://www.rfc-editor.org/rfc/rfc9380#section-8.8.2
|
|
989
990
|
//
|
|
990
991
|
// Base field F is GF(p^m)
|
|
991
992
|
// p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
|
|
@@ -1017,11 +1018,41 @@ const htfDefaults = Object.freeze({
|
|
|
1017
1018
|
|
|
1018
1019
|
// Encoding utils
|
|
1019
1020
|
// Point on G1 curve: (x, y)
|
|
1020
|
-
|
|
1021
|
-
const I_BIT_POS = Fp.BITS + 1; // I_bit, point-at-infinity bit for serialization flag
|
|
1022
|
-
const S_BIT_POS = Fp.BITS + 2; // S_bit, sign bit for serialization flag
|
|
1021
|
+
|
|
1023
1022
|
// Compressed point of infinity
|
|
1024
|
-
const COMPRESSED_ZERO = Fp.toBytes(
|
|
1023
|
+
const COMPRESSED_ZERO = setMask(Fp.toBytes(_0n), { infinity: true, compressed: true }); // set compressed & point-at-infinity bits
|
|
1024
|
+
|
|
1025
|
+
function parseMask(bytes: Uint8Array) {
|
|
1026
|
+
// Copy, so we can remove mask data. It will be removed also later, when Fp.create will call modulo.
|
|
1027
|
+
bytes = bytes.slice();
|
|
1028
|
+
const mask = bytes[0] & 0b1110_0000;
|
|
1029
|
+
const compressed = !!((mask >> 7) & 1); // compression bit (0b1000_0000)
|
|
1030
|
+
const infinity = !!((mask >> 6) & 1); // point at infinity bit (0b0100_0000)
|
|
1031
|
+
const sort = !!((mask >> 5) & 1); // sort bit (0b0010_0000)
|
|
1032
|
+
bytes[0] &= 0b0001_1111; // clear mask (zero first 3 bits)
|
|
1033
|
+
return { compressed, infinity, sort, value: bytes };
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
function setMask(
|
|
1037
|
+
bytes: Uint8Array,
|
|
1038
|
+
mask: { compressed?: boolean; infinity?: boolean; sort?: boolean }
|
|
1039
|
+
) {
|
|
1040
|
+
if (bytes[0] & 0b1110_0000) throw new Error('setMask: non-empty mask');
|
|
1041
|
+
if (mask.compressed) bytes[0] |= 0b1000_0000;
|
|
1042
|
+
if (mask.infinity) bytes[0] |= 0b0100_0000;
|
|
1043
|
+
if (mask.sort) bytes[0] |= 0b0010_0000;
|
|
1044
|
+
return bytes;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
function signatureG1ToRawBytes(point: ProjPointType<Fp>) {
|
|
1048
|
+
point.assertValidity();
|
|
1049
|
+
const isZero = point.equals(bls12_381.G1.ProjectivePoint.ZERO);
|
|
1050
|
+
const { x, y } = point.toAffine();
|
|
1051
|
+
if (isZero) return COMPRESSED_ZERO.slice();
|
|
1052
|
+
const P = Fp.ORDER;
|
|
1053
|
+
const sort = Boolean((y * _2n) / P);
|
|
1054
|
+
return setMask(numberToBytesBE(x, Fp.BYTES), { compressed: true, sort });
|
|
1055
|
+
}
|
|
1025
1056
|
|
|
1026
1057
|
function signatureG2ToRawBytes(point: ProjPointType<Fp2>) {
|
|
1027
1058
|
// NOTE: by some reasons it was missed in bls12-381, looks like bug
|
|
@@ -1033,14 +1064,16 @@ function signatureG2ToRawBytes(point: ProjPointType<Fp2>) {
|
|
|
1033
1064
|
const { re: x0, im: x1 } = Fp2.reim(x);
|
|
1034
1065
|
const { re: y0, im: y1 } = Fp2.reim(y);
|
|
1035
1066
|
const tmp = y1 > _0n ? y1 * _2n : y0 * _2n;
|
|
1036
|
-
const
|
|
1037
|
-
const z1 = bitSet(bitSet(x1, 381, aflag1), S_BIT_POS, true);
|
|
1067
|
+
const sort = Boolean((tmp / Fp.ORDER) & _1n);
|
|
1038
1068
|
const z2 = x0;
|
|
1039
|
-
return concatB(
|
|
1069
|
+
return concatB(
|
|
1070
|
+
setMask(numberToBytesBE(x1, len), { sort, compressed: true }),
|
|
1071
|
+
numberToBytesBE(z2, len)
|
|
1072
|
+
);
|
|
1040
1073
|
}
|
|
1041
1074
|
|
|
1042
1075
|
// To verify curve parameters, see pairing-friendly-curves spec:
|
|
1043
|
-
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-
|
|
1076
|
+
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-11
|
|
1044
1077
|
// Basic math is done over finite fields over p.
|
|
1045
1078
|
// More complicated math is done over polynominal extension fields.
|
|
1046
1079
|
// To simplify calculations in Fp12, we construct extension tower:
|
|
@@ -1075,7 +1108,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|
|
1075
1108
|
),
|
|
1076
1109
|
a: Fp.ZERO,
|
|
1077
1110
|
b: _4n,
|
|
1078
|
-
htfDefaults: { ...htfDefaults, m: 1 },
|
|
1111
|
+
htfDefaults: { ...htfDefaults, m: 1, DST: 'BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_' },
|
|
1079
1112
|
wrapPrivateKey: true,
|
|
1080
1113
|
allowInfinityPoint: true,
|
|
1081
1114
|
// Checks is the point resides in prime-order subgroup.
|
|
@@ -1108,7 +1141,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|
|
1108
1141
|
},
|
|
1109
1142
|
// Clear cofactor of G1
|
|
1110
1143
|
// https://eprint.iacr.org/2019/403
|
|
1111
|
-
clearCofactor: (
|
|
1144
|
+
clearCofactor: (_c, point) => {
|
|
1112
1145
|
// return this.multiplyUnsafe(CURVE.h);
|
|
1113
1146
|
return point.multiplyUnsafe(bls12_381.params.x).add(point); // x*P + P
|
|
1114
1147
|
},
|
|
@@ -1117,26 +1150,30 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|
|
1117
1150
|
return isogenyMapG1(x, y);
|
|
1118
1151
|
},
|
|
1119
1152
|
fromBytes: (bytes: Uint8Array): AffinePoint<Fp> => {
|
|
1120
|
-
|
|
1121
|
-
if (
|
|
1153
|
+
const { compressed, infinity, sort, value } = parseMask(bytes);
|
|
1154
|
+
if (value.length === 48 && compressed) {
|
|
1122
1155
|
// TODO: Fp.bytes
|
|
1123
1156
|
const P = Fp.ORDER;
|
|
1124
|
-
const compressedValue = bytesToNumberBE(
|
|
1125
|
-
const bflag = bitGet(compressedValue, I_BIT_POS);
|
|
1157
|
+
const compressedValue = bytesToNumberBE(value);
|
|
1126
1158
|
// Zero
|
|
1127
|
-
if (bflag === _1n) return { x: _0n, y: _0n };
|
|
1128
1159
|
const x = Fp.create(compressedValue & Fp.MASK);
|
|
1160
|
+
if (infinity) {
|
|
1161
|
+
if (x !== _0n) throw new Error('G1: non-empty compressed point at infinity');
|
|
1162
|
+
return { x: _0n, y: _0n };
|
|
1163
|
+
}
|
|
1129
1164
|
const right = Fp.add(Fp.pow(x, _3n), Fp.create(bls12_381.params.G1b)); // y² = x³ + b
|
|
1130
1165
|
let y = Fp.sqrt(right);
|
|
1131
1166
|
if (!y) throw new Error('Invalid compressed G1 point');
|
|
1132
|
-
|
|
1133
|
-
if ((y * _2n) / P !== aflag) y = Fp.neg(y);
|
|
1167
|
+
if ((y * _2n) / P !== BigInt(sort)) y = Fp.neg(y);
|
|
1134
1168
|
return { x: Fp.create(x), y: Fp.create(y) };
|
|
1135
|
-
} else if (
|
|
1169
|
+
} else if (value.length === 96 && !compressed) {
|
|
1136
1170
|
// Check if the infinity flag is set
|
|
1137
|
-
|
|
1138
|
-
const
|
|
1139
|
-
|
|
1171
|
+
const x = bytesToNumberBE(value.subarray(0, Fp.BYTES));
|
|
1172
|
+
const y = bytesToNumberBE(value.subarray(Fp.BYTES));
|
|
1173
|
+
if (infinity) {
|
|
1174
|
+
if (x !== _0n || y !== _0n) throw new Error('G1: non-empty point at infinity');
|
|
1175
|
+
return bls12_381.G1.ProjectivePoint.ZERO.toAffine();
|
|
1176
|
+
}
|
|
1140
1177
|
return { x: Fp.create(x), y: Fp.create(y) };
|
|
1141
1178
|
} else {
|
|
1142
1179
|
throw new Error('Invalid point G1, expected 48/96 bytes');
|
|
@@ -1148,10 +1185,8 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|
|
1148
1185
|
if (isCompressed) {
|
|
1149
1186
|
if (isZero) return COMPRESSED_ZERO.slice();
|
|
1150
1187
|
const P = Fp.ORDER;
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
num = bitSet(num, S_BIT_POS, true);
|
|
1154
|
-
return numberToBytesBE(num, Fp.BYTES);
|
|
1188
|
+
const sort = Boolean((y * _2n) / P);
|
|
1189
|
+
return setMask(numberToBytesBE(x, Fp.BYTES), { compressed: true, sort });
|
|
1155
1190
|
} else {
|
|
1156
1191
|
if (isZero) {
|
|
1157
1192
|
// 2x PUBLIC_KEY_LENGTH
|
|
@@ -1162,6 +1197,30 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|
|
1162
1197
|
}
|
|
1163
1198
|
}
|
|
1164
1199
|
},
|
|
1200
|
+
ShortSignature: {
|
|
1201
|
+
fromHex(hex: Hex): ProjPointType<Fp> {
|
|
1202
|
+
const { infinity, sort, value } = parseMask(ensureBytes('signatureHex', hex, 48));
|
|
1203
|
+
const P = Fp.ORDER;
|
|
1204
|
+
const compressedValue = bytesToNumberBE(value);
|
|
1205
|
+
// Zero
|
|
1206
|
+
if (infinity) return bls12_381.G1.ProjectivePoint.ZERO;
|
|
1207
|
+
const x = Fp.create(compressedValue & Fp.MASK);
|
|
1208
|
+
const right = Fp.add(Fp.pow(x, _3n), Fp.create(bls12_381.params.G1b)); // y² = x³ + b
|
|
1209
|
+
let y = Fp.sqrt(right);
|
|
1210
|
+
if (!y) throw new Error('Invalid compressed G1 point');
|
|
1211
|
+
const aflag = BigInt(sort);
|
|
1212
|
+
if ((y * _2n) / P !== aflag) y = Fp.neg(y);
|
|
1213
|
+
const point = bls12_381.G1.ProjectivePoint.fromAffine({ x, y });
|
|
1214
|
+
point.assertValidity();
|
|
1215
|
+
return point;
|
|
1216
|
+
},
|
|
1217
|
+
toRawBytes(point: ProjPointType<Fp>) {
|
|
1218
|
+
return signatureG1ToRawBytes(point);
|
|
1219
|
+
},
|
|
1220
|
+
toHex(point: ProjPointType<Fp>) {
|
|
1221
|
+
return bytesToHex(signatureG1ToRawBytes(point));
|
|
1222
|
+
},
|
|
1223
|
+
},
|
|
1165
1224
|
},
|
|
1166
1225
|
// G2 is the order-q subgroup of E2(Fp²) : y² = x³+4(1+√−1),
|
|
1167
1226
|
// where Fp2 is Fp[√−1]/(x2+1). #E2(Fp2 ) = h2q, where
|
|
@@ -1233,45 +1292,45 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|
|
1233
1292
|
return Q; // [x²-x-1]P + [x-1]Ψ(P) + Ψ²(2P)
|
|
1234
1293
|
},
|
|
1235
1294
|
fromBytes: (bytes: Uint8Array): AffinePoint<Fp2> => {
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1295
|
+
const { compressed, infinity, sort, value } = parseMask(bytes);
|
|
1296
|
+
if (
|
|
1297
|
+
(!compressed && !infinity && sort) || // 00100000
|
|
1298
|
+
(!compressed && infinity && sort) || // 01100000
|
|
1299
|
+
(sort && infinity && compressed) // 11100000
|
|
1300
|
+
) {
|
|
1301
|
+
throw new Error('Invalid encoding flag: ' + (bytes[0] & 0b1110_0000));
|
|
1240
1302
|
}
|
|
1241
|
-
const bitC = m_byte & 0x80; // compression bit
|
|
1242
|
-
const bitI = m_byte & 0x40; // point at infinity bit
|
|
1243
|
-
const bitS = m_byte & 0x20; // sign bit
|
|
1244
1303
|
const L = Fp.BYTES;
|
|
1245
1304
|
const slc = (b: Uint8Array, from: number, to?: number) => bytesToNumberBE(b.slice(from, to));
|
|
1246
|
-
if (
|
|
1305
|
+
if (value.length === 96 && compressed) {
|
|
1247
1306
|
const b = bls12_381.params.G2b;
|
|
1248
1307
|
const P = Fp.ORDER;
|
|
1249
|
-
|
|
1250
|
-
bytes[0] = bytes[0] & 0x1f; // clear flags
|
|
1251
|
-
if (bitI) {
|
|
1308
|
+
if (infinity) {
|
|
1252
1309
|
// check that all bytes are 0
|
|
1253
|
-
if (
|
|
1310
|
+
if (value.reduce((p, c) => (p !== 0 ? c + 1 : c), 0) > 0) {
|
|
1254
1311
|
throw new Error('Invalid compressed G2 point');
|
|
1255
1312
|
}
|
|
1256
1313
|
return { x: Fp2.ZERO, y: Fp2.ZERO };
|
|
1257
1314
|
}
|
|
1258
|
-
const x_1 = slc(
|
|
1259
|
-
const x_0 = slc(
|
|
1315
|
+
const x_1 = slc(value, 0, L);
|
|
1316
|
+
const x_0 = slc(value, L, 2 * L);
|
|
1260
1317
|
const x = Fp2.create({ c0: Fp.create(x_0), c1: Fp.create(x_1) });
|
|
1261
1318
|
const right = Fp2.add(Fp2.pow(x, _3n), b); // y² = x³ + 4 * (u+1) = x³ + b
|
|
1262
1319
|
let y = Fp2.sqrt(right);
|
|
1263
1320
|
const Y_bit = y.c1 === _0n ? (y.c0 * _2n) / P : (y.c1 * _2n) / P ? _1n : _0n;
|
|
1264
|
-
y =
|
|
1321
|
+
y = sort && Y_bit > 0 ? y : Fp2.neg(y);
|
|
1265
1322
|
return { x, y };
|
|
1266
|
-
} else if (
|
|
1267
|
-
|
|
1268
|
-
|
|
1323
|
+
} else if (value.length === 192 && !compressed) {
|
|
1324
|
+
if (infinity) {
|
|
1325
|
+
if (value.reduce((p, c) => (p !== 0 ? c + 1 : c), 0) > 0) {
|
|
1326
|
+
throw new Error('Invalid uncompressed G2 point');
|
|
1327
|
+
}
|
|
1269
1328
|
return { x: Fp2.ZERO, y: Fp2.ZERO };
|
|
1270
1329
|
}
|
|
1271
|
-
const x1 = slc(
|
|
1272
|
-
const x0 = slc(
|
|
1273
|
-
const y1 = slc(
|
|
1274
|
-
const y0 = slc(
|
|
1330
|
+
const x1 = slc(value, 0, L);
|
|
1331
|
+
const x0 = slc(value, L, 2 * L);
|
|
1332
|
+
const y1 = slc(value, 2 * L, 3 * L);
|
|
1333
|
+
const y0 = slc(value, 3 * L, 4 * L);
|
|
1275
1334
|
return { x: Fp2.fromBigTuple([x0, x1]), y: Fp2.fromBigTuple([y0, y1]) };
|
|
1276
1335
|
} else {
|
|
1277
1336
|
throw new Error('Invalid point G2, expected 96/192 bytes');
|
|
@@ -1284,10 +1343,10 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|
|
1284
1343
|
if (isCompressed) {
|
|
1285
1344
|
if (isZero) return concatB(COMPRESSED_ZERO, numberToBytesBE(_0n, len));
|
|
1286
1345
|
const flag = Boolean(y.c1 === _0n ? (y.c0 * _2n) / P : (y.c1 * _2n) / P);
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1346
|
+
return concatB(
|
|
1347
|
+
setMask(numberToBytesBE(x.c1, len), { compressed: true, sort: flag }),
|
|
1348
|
+
numberToBytesBE(x.c0, len)
|
|
1349
|
+
);
|
|
1291
1350
|
} else {
|
|
1292
1351
|
if (isZero) return concatB(new Uint8Array([0x40]), new Uint8Array(4 * len - 1)); // bytes[0] |= 1 << 6;
|
|
1293
1352
|
const { re: x0, im: x1 } = Fp2.reim(x);
|
|
@@ -1303,17 +1362,15 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|
|
1303
1362
|
Signature: {
|
|
1304
1363
|
// TODO: Optimize, it's very slow because of sqrt.
|
|
1305
1364
|
fromHex(hex: Hex): ProjPointType<Fp2> {
|
|
1306
|
-
|
|
1365
|
+
const { infinity, sort, value } = parseMask(ensureBytes('signatureHex', hex));
|
|
1307
1366
|
const P = Fp.ORDER;
|
|
1308
1367
|
const half = hex.length / 2;
|
|
1309
1368
|
if (half !== 48 && half !== 96)
|
|
1310
1369
|
throw new Error('Invalid compressed signature length, must be 96 or 192');
|
|
1311
|
-
const z1 = bytesToNumberBE(
|
|
1312
|
-
const z2 = bytesToNumberBE(
|
|
1370
|
+
const z1 = bytesToNumberBE(value.slice(0, half));
|
|
1371
|
+
const z2 = bytesToNumberBE(value.slice(half));
|
|
1313
1372
|
// Indicates the infinity point
|
|
1314
|
-
|
|
1315
|
-
if (bflag1 === _1n) return bls12_381.G2.ProjectivePoint.ZERO;
|
|
1316
|
-
|
|
1373
|
+
if (infinity) return bls12_381.G2.ProjectivePoint.ZERO;
|
|
1317
1374
|
const x1 = Fp.create(z1 & Fp.MASK);
|
|
1318
1375
|
const x2 = Fp.create(z2);
|
|
1319
1376
|
const x = Fp2.create({ c0: x2, c1: x1 });
|
|
@@ -1325,7 +1382,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|
|
1325
1382
|
// Choose the y whose leftmost bit of the imaginary part is equal to the a_flag1
|
|
1326
1383
|
// If y1 happens to be zero, then use the bit of y0
|
|
1327
1384
|
const { re: y0, im: y1 } = Fp2.reim(y);
|
|
1328
|
-
const aflag1 =
|
|
1385
|
+
const aflag1 = BigInt(sort);
|
|
1329
1386
|
const isGreater = y1 > _0n && (y1 * _2n) / P !== aflag1;
|
|
1330
1387
|
const isZero = y1 === _0n && (y0 * _2n) / P !== aflag1;
|
|
1331
1388
|
if (isGreater || isZero) y = Fp2.neg(y);
|
package/src/bn254.ts
CHANGED
|
@@ -6,8 +6,9 @@ import { Field } from './abstract/modular.js';
|
|
|
6
6
|
/**
|
|
7
7
|
* bn254 pairing-friendly curve.
|
|
8
8
|
* Previously known as alt_bn_128, when it had 128-bit security.
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* Barbulescu-Duquesne 2017 shown it's weaker: just about 100 bits,
|
|
10
|
+
* so the naming has been adjusted to its prime bit count
|
|
11
|
+
* https://hal.science/hal-01534101/file/main.pdf
|
|
11
12
|
*/
|
|
12
13
|
export const bn254 = weierstrass({
|
|
13
14
|
a: BigInt(0),
|
package/src/ed25519.ts
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
numberToBytesLE,
|
|
14
14
|
} from './abstract/utils.js';
|
|
15
15
|
import { createHasher, htfBasicOpts, expand_message_xmd } from './abstract/hash-to-curve.js';
|
|
16
|
-
import { AffinePoint } from './abstract/curve.js';
|
|
16
|
+
import { AffinePoint, Group } from './abstract/curve.js';
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* ed25519 Twisted Edwards curve with following addons:
|
|
@@ -123,7 +123,7 @@ const ed25519Defaults = {
|
|
|
123
123
|
uvRatio,
|
|
124
124
|
} as const;
|
|
125
125
|
|
|
126
|
-
export const ed25519 = twistedEdwards(ed25519Defaults);
|
|
126
|
+
export const ed25519 = /* @__PURE__ */ twistedEdwards(ed25519Defaults);
|
|
127
127
|
|
|
128
128
|
function ed25519_domain(data: Uint8Array, ctx: Uint8Array, phflag: boolean) {
|
|
129
129
|
if (ctx.length > 255) throw new Error('Context is too big');
|
|
@@ -135,8 +135,11 @@ function ed25519_domain(data: Uint8Array, ctx: Uint8Array, phflag: boolean) {
|
|
|
135
135
|
);
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
export const ed25519ctx = twistedEdwards({
|
|
139
|
-
|
|
138
|
+
export const ed25519ctx = /* @__PURE__ */ twistedEdwards({
|
|
139
|
+
...ed25519Defaults,
|
|
140
|
+
domain: ed25519_domain,
|
|
141
|
+
});
|
|
142
|
+
export const ed25519ph = /* @__PURE__ */ twistedEdwards({
|
|
140
143
|
...ed25519Defaults,
|
|
141
144
|
domain: ed25519_domain,
|
|
142
145
|
prehash: sha512,
|
|
@@ -340,7 +343,7 @@ function calcElligatorRistrettoMap(r0: bigint): ExtendedPoint {
|
|
|
340
343
|
* but it should work in its own namespace: do not combine those two.
|
|
341
344
|
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448
|
|
342
345
|
*/
|
|
343
|
-
class RistPoint {
|
|
346
|
+
class RistPoint implements Group<RistPoint> {
|
|
344
347
|
static BASE: RistPoint;
|
|
345
348
|
static ZERO: RistPoint;
|
|
346
349
|
// Private property to discourage combining ExtendedPoint + RistrettoPoint
|
|
@@ -468,6 +471,14 @@ class RistPoint {
|
|
|
468
471
|
multiplyUnsafe(scalar: bigint): RistPoint {
|
|
469
472
|
return new RistPoint(this.ep.multiplyUnsafe(scalar));
|
|
470
473
|
}
|
|
474
|
+
|
|
475
|
+
double(): RistPoint {
|
|
476
|
+
return new RistPoint(this.ep.double());
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
negate(): RistPoint {
|
|
480
|
+
return new RistPoint(this.ep.negate());
|
|
481
|
+
}
|
|
471
482
|
}
|
|
472
483
|
export const RistrettoPoint = /* @__PURE__ */ (() => {
|
|
473
484
|
if (!RistPoint.BASE) RistPoint.BASE = new RistPoint(ed25519.ExtendedPoint.BASE);
|
|
@@ -475,12 +486,12 @@ export const RistrettoPoint = /* @__PURE__ */ (() => {
|
|
|
475
486
|
return RistPoint;
|
|
476
487
|
})();
|
|
477
488
|
|
|
478
|
-
// https://
|
|
479
|
-
|
|
480
|
-
export const hash_to_ristretto255 = (msg: Uint8Array, options: htfBasicOpts) => {
|
|
489
|
+
// Hashing to ristretto255. https://www.rfc-editor.org/rfc/rfc9380#appendix-B
|
|
490
|
+
export const hashToRistretto255 = (msg: Uint8Array, options: htfBasicOpts) => {
|
|
481
491
|
const d = options.DST;
|
|
482
492
|
const DST = typeof d === 'string' ? utf8ToBytes(d) : d;
|
|
483
493
|
const uniform_bytes = expand_message_xmd(msg, DST, 64, sha512);
|
|
484
494
|
const P = RistPoint.hashToCurve(uniform_bytes);
|
|
485
495
|
return P;
|
|
486
496
|
};
|
|
497
|
+
export const hash_to_ristretto255 = hashToRistretto255; // legacy
|