@noble/curves 0.5.2 → 0.6.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 +49 -5
- package/lib/_shortw_utils.d.ts +10 -21
- package/lib/abstract/bls.d.ts +39 -32
- package/lib/abstract/bls.js +74 -73
- package/lib/abstract/{group.d.ts → curve.d.ts} +31 -1
- package/lib/abstract/{group.js → curve.js} +39 -2
- package/lib/abstract/edwards.d.ts +30 -72
- package/lib/abstract/edwards.js +197 -375
- package/lib/abstract/hash-to-curve.d.ts +25 -6
- package/lib/abstract/hash-to-curve.js +40 -12
- package/lib/abstract/modular.d.ts +20 -7
- package/lib/abstract/modular.js +61 -35
- package/lib/abstract/montgomery.js +4 -5
- package/lib/abstract/poseidon.d.ts +29 -0
- package/lib/abstract/poseidon.js +115 -0
- package/lib/abstract/utils.d.ts +5 -36
- package/lib/abstract/utils.js +23 -71
- package/lib/abstract/weierstrass.d.ts +51 -74
- package/lib/abstract/weierstrass.js +455 -628
- package/lib/bls12-381.js +63 -58
- package/lib/bn.js +1 -1
- package/lib/ed25519.d.ts +7 -5
- package/lib/ed25519.js +82 -79
- package/lib/ed448.d.ts +3 -0
- package/lib/ed448.js +86 -83
- package/lib/esm/abstract/bls.js +75 -74
- package/lib/esm/abstract/{group.js → curve.js} +37 -1
- package/lib/esm/abstract/edwards.js +196 -374
- package/lib/esm/abstract/hash-to-curve.js +38 -11
- package/lib/esm/abstract/modular.js +58 -34
- package/lib/esm/abstract/montgomery.js +5 -6
- package/lib/esm/abstract/poseidon.js +109 -0
- package/lib/esm/abstract/utils.js +21 -66
- package/lib/esm/abstract/weierstrass.js +454 -627
- package/lib/esm/bls12-381.js +75 -70
- package/lib/esm/bn.js +1 -1
- package/lib/esm/ed25519.js +80 -78
- package/lib/esm/ed448.js +84 -82
- package/lib/esm/jubjub.js +1 -1
- package/lib/esm/p256.js +11 -9
- package/lib/esm/p384.js +11 -9
- package/lib/esm/p521.js +13 -12
- package/lib/esm/secp256k1.js +115 -151
- package/lib/esm/stark.js +104 -40
- package/lib/jubjub.d.ts +2 -2
- package/lib/jubjub.js +1 -1
- package/lib/p192.d.ts +20 -42
- package/lib/p224.d.ts +20 -42
- package/lib/p256.d.ts +23 -42
- package/lib/p256.js +13 -10
- package/lib/p384.d.ts +23 -42
- package/lib/p384.js +13 -10
- package/lib/p521.d.ts +23 -42
- package/lib/p521.js +15 -13
- package/lib/secp256k1.d.ts +25 -37
- package/lib/secp256k1.js +115 -151
- package/lib/stark.d.ts +36 -19
- package/lib/stark.js +107 -40
- package/package.json +13 -8
package/lib/esm/ed448.js
CHANGED
|
@@ -4,6 +4,7 @@ import { concatBytes, randomBytes, utf8ToBytes, wrapConstructor } from '@noble/h
|
|
|
4
4
|
import { twistedEdwards } from './abstract/edwards.js';
|
|
5
5
|
import { mod, pow2, Fp as Field } from './abstract/modular.js';
|
|
6
6
|
import { montgomery } from './abstract/montgomery.js';
|
|
7
|
+
import * as htf from './abstract/hash-to-curve.js';
|
|
7
8
|
/**
|
|
8
9
|
* Edwards448 (not Ed448-Goldilocks) curve with following addons:
|
|
9
10
|
* * X448 ECDH
|
|
@@ -46,79 +47,6 @@ function adjustScalarBytes(bytes) {
|
|
|
46
47
|
return bytes;
|
|
47
48
|
}
|
|
48
49
|
const Fp = Field(ed448P, 456, true);
|
|
49
|
-
// Hash To Curve Elligator2 Map
|
|
50
|
-
const ELL2_C1 = (Fp.ORDER - BigInt(3)) / BigInt(4); // 1. c1 = (q - 3) / 4 # Integer arithmetic
|
|
51
|
-
const ELL2_J = BigInt(156326);
|
|
52
|
-
function map_to_curve_elligator2_curve448(u) {
|
|
53
|
-
let tv1 = Fp.square(u); // 1. tv1 = u^2
|
|
54
|
-
let e1 = Fp.equals(tv1, Fp.ONE); // 2. e1 = tv1 == 1
|
|
55
|
-
tv1 = Fp.cmov(tv1, Fp.ZERO, e1); // 3. tv1 = CMOV(tv1, 0, e1) # If Z * u^2 == -1, set tv1 = 0
|
|
56
|
-
let xd = Fp.sub(Fp.ONE, tv1); // 4. xd = 1 - tv1
|
|
57
|
-
let x1n = Fp.negate(ELL2_J); // 5. x1n = -J
|
|
58
|
-
let tv2 = Fp.square(xd); // 6. tv2 = xd^2
|
|
59
|
-
let gxd = Fp.mul(tv2, xd); // 7. gxd = tv2 * xd # gxd = xd^3
|
|
60
|
-
let gx1 = Fp.mul(tv1, Fp.negate(ELL2_J)); // 8. gx1 = -J * tv1 # x1n + J * xd
|
|
61
|
-
gx1 = Fp.mul(gx1, x1n); // 9. gx1 = gx1 * x1n # x1n^2 + J * x1n * xd
|
|
62
|
-
gx1 = Fp.add(gx1, tv2); // 10. gx1 = gx1 + tv2 # x1n^2 + J * x1n * xd + xd^2
|
|
63
|
-
gx1 = Fp.mul(gx1, x1n); // 11. gx1 = gx1 * x1n # x1n^3 + J * x1n^2 * xd + x1n * xd^2
|
|
64
|
-
let tv3 = Fp.square(gxd); // 12. tv3 = gxd^2
|
|
65
|
-
tv2 = Fp.mul(gx1, gxd); // 13. tv2 = gx1 * gxd # gx1 * gxd
|
|
66
|
-
tv3 = Fp.mul(tv3, tv2); // 14. tv3 = tv3 * tv2 # gx1 * gxd^3
|
|
67
|
-
let y1 = Fp.pow(tv3, ELL2_C1); // 15. y1 = tv3^c1 # (gx1 * gxd^3)^((p - 3) / 4)
|
|
68
|
-
y1 = Fp.mul(y1, tv2); // 16. y1 = y1 * tv2 # gx1 * gxd * (gx1 * gxd^3)^((p - 3) / 4)
|
|
69
|
-
let x2n = Fp.mul(x1n, Fp.negate(tv1)); // 17. x2n = -tv1 * x1n # x2 = x2n / xd = -1 * u^2 * x1n / xd
|
|
70
|
-
let y2 = Fp.mul(y1, u); // 18. y2 = y1 * u
|
|
71
|
-
y2 = Fp.cmov(y2, Fp.ZERO, e1); // 19. y2 = CMOV(y2, 0, e1)
|
|
72
|
-
tv2 = Fp.square(y1); // 20. tv2 = y1^2
|
|
73
|
-
tv2 = Fp.mul(tv2, gxd); // 21. tv2 = tv2 * gxd
|
|
74
|
-
let e2 = Fp.equals(tv2, gx1); // 22. e2 = tv2 == gx1
|
|
75
|
-
let xn = Fp.cmov(x2n, x1n, e2); // 23. xn = CMOV(x2n, x1n, e2) # If e2, x = x1, else x = x2
|
|
76
|
-
let y = Fp.cmov(y2, y1, e2); // 24. y = CMOV(y2, y1, e2) # If e2, y = y1, else y = y2
|
|
77
|
-
let e3 = Fp.isOdd(y); // 25. e3 = sgn0(y) == 1 # Fix sign of y
|
|
78
|
-
y = Fp.cmov(y, Fp.negate(y), e2 !== e3); // 26. y = CMOV(y, -y, e2 XOR e3)
|
|
79
|
-
return { xn, xd, yn: y, yd: Fp.ONE }; // 27. return (xn, xd, y, 1)
|
|
80
|
-
}
|
|
81
|
-
function map_to_curve_elligator2_edwards448(u) {
|
|
82
|
-
let { xn, xd, yn, yd } = map_to_curve_elligator2_curve448(u); // 1. (xn, xd, yn, yd) = map_to_curve_elligator2_curve448(u)
|
|
83
|
-
let xn2 = Fp.square(xn); // 2. xn2 = xn^2
|
|
84
|
-
let xd2 = Fp.square(xd); // 3. xd2 = xd^2
|
|
85
|
-
let xd4 = Fp.square(xd2); // 4. xd4 = xd2^2
|
|
86
|
-
let yn2 = Fp.square(yn); // 5. yn2 = yn^2
|
|
87
|
-
let yd2 = Fp.square(yd); // 6. yd2 = yd^2
|
|
88
|
-
let xEn = Fp.sub(xn2, xd2); // 7. xEn = xn2 - xd2
|
|
89
|
-
let tv2 = Fp.sub(xEn, xd2); // 8. tv2 = xEn - xd2
|
|
90
|
-
xEn = Fp.mul(xEn, xd2); // 9. xEn = xEn * xd2
|
|
91
|
-
xEn = Fp.mul(xEn, yd); // 10. xEn = xEn * yd
|
|
92
|
-
xEn = Fp.mul(xEn, yn); // 11. xEn = xEn * yn
|
|
93
|
-
xEn = Fp.mul(xEn, 4n); // 12. xEn = xEn * 4
|
|
94
|
-
tv2 = Fp.mul(tv2, xn2); // 13. tv2 = tv2 * xn2
|
|
95
|
-
tv2 = Fp.mul(tv2, yd2); // 14. tv2 = tv2 * yd2
|
|
96
|
-
let tv3 = Fp.mul(yn2, 4n); // 15. tv3 = 4 * yn2
|
|
97
|
-
let tv1 = Fp.add(tv3, yd2); // 16. tv1 = tv3 + yd2
|
|
98
|
-
tv1 = Fp.mul(tv1, xd4); // 17. tv1 = tv1 * xd4
|
|
99
|
-
let xEd = Fp.add(tv1, tv2); // 18. xEd = tv1 + tv2
|
|
100
|
-
tv2 = Fp.mul(tv2, xn); // 19. tv2 = tv2 * xn
|
|
101
|
-
let tv4 = Fp.mul(xn, xd4); // 20. tv4 = xn * xd4
|
|
102
|
-
let yEn = Fp.sub(tv3, yd2); // 21. yEn = tv3 - yd2
|
|
103
|
-
yEn = Fp.mul(yEn, tv4); // 22. yEn = yEn * tv4
|
|
104
|
-
yEn = Fp.sub(yEn, tv2); // 23. yEn = yEn - tv2
|
|
105
|
-
tv1 = Fp.add(xn2, xd2); // 24. tv1 = xn2 + xd2
|
|
106
|
-
tv1 = Fp.mul(tv1, xd2); // 25. tv1 = tv1 * xd2
|
|
107
|
-
tv1 = Fp.mul(tv1, xd); // 26. tv1 = tv1 * xd
|
|
108
|
-
tv1 = Fp.mul(tv1, yn2); // 27. tv1 = tv1 * yn2
|
|
109
|
-
tv1 = Fp.mul(tv1, BigInt(-2)); // 28. tv1 = -2 * tv1
|
|
110
|
-
let yEd = Fp.add(tv2, tv1); // 29. yEd = tv2 + tv1
|
|
111
|
-
tv4 = Fp.mul(tv4, yd2); // 30. tv4 = tv4 * yd2
|
|
112
|
-
yEd = Fp.add(yEd, tv4); // 31. yEd = yEd + tv4
|
|
113
|
-
tv1 = Fp.mul(xEd, yEd); // 32. tv1 = xEd * yEd
|
|
114
|
-
let e = Fp.equals(tv1, Fp.ZERO); // 33. e = tv1 == 0
|
|
115
|
-
xEn = Fp.cmov(xEn, Fp.ZERO, e); // 34. xEn = CMOV(xEn, 0, e)
|
|
116
|
-
xEd = Fp.cmov(xEd, Fp.ONE, e); // 35. xEd = CMOV(xEd, 1, e)
|
|
117
|
-
yEn = Fp.cmov(yEn, Fp.ONE, e); // 36. yEn = CMOV(yEn, 1, e)
|
|
118
|
-
yEd = Fp.cmov(yEd, Fp.ONE, e); // 37. yEd = CMOV(yEd, 1, e)
|
|
119
|
-
const inv = Fp.invertBatch([xEd, yEd]); // batch division
|
|
120
|
-
return { x: Fp.mul(xEn, inv[0]), y: Fp.mul(yEn, inv[1]) }; // 38. return (xEn, xEd, yEn, yEd)
|
|
121
|
-
}
|
|
122
50
|
const ED448_DEF = {
|
|
123
51
|
// Param: a
|
|
124
52
|
a: BigInt(1),
|
|
@@ -166,15 +94,6 @@ const ED448_DEF = {
|
|
|
166
94
|
// square root exists, and the decoding fails.
|
|
167
95
|
return { isValid: mod(x2 * v, P) === u, value: x };
|
|
168
96
|
},
|
|
169
|
-
htfDefaults: {
|
|
170
|
-
DST: 'edwards448_XOF:SHAKE256_ELL2_RO_',
|
|
171
|
-
p: Fp.ORDER,
|
|
172
|
-
m: 1,
|
|
173
|
-
k: 224,
|
|
174
|
-
expand: 'xof',
|
|
175
|
-
hash: shake256,
|
|
176
|
-
},
|
|
177
|
-
mapToCurve: (scalars) => map_to_curve_elligator2_edwards448(scalars[0]),
|
|
178
97
|
};
|
|
179
98
|
export const ed448 = twistedEdwards(ED448_DEF);
|
|
180
99
|
// NOTE: there is no ed448ctx, since ed448 supports ctx by default
|
|
@@ -207,3 +126,86 @@ export const x448 = montgomery({
|
|
|
207
126
|
// return numberToBytesLE(u, 56);
|
|
208
127
|
// },
|
|
209
128
|
});
|
|
129
|
+
// Hash To Curve Elligator2 Map
|
|
130
|
+
const ELL2_C1 = (Fp.ORDER - BigInt(3)) / BigInt(4); // 1. c1 = (q - 3) / 4 # Integer arithmetic
|
|
131
|
+
const ELL2_J = BigInt(156326);
|
|
132
|
+
function map_to_curve_elligator2_curve448(u) {
|
|
133
|
+
let tv1 = Fp.sqr(u); // 1. tv1 = u^2
|
|
134
|
+
let e1 = Fp.eql(tv1, Fp.ONE); // 2. e1 = tv1 == 1
|
|
135
|
+
tv1 = Fp.cmov(tv1, Fp.ZERO, e1); // 3. tv1 = CMOV(tv1, 0, e1) # If Z * u^2 == -1, set tv1 = 0
|
|
136
|
+
let xd = Fp.sub(Fp.ONE, tv1); // 4. xd = 1 - tv1
|
|
137
|
+
let x1n = Fp.neg(ELL2_J); // 5. x1n = -J
|
|
138
|
+
let tv2 = Fp.sqr(xd); // 6. tv2 = xd^2
|
|
139
|
+
let gxd = Fp.mul(tv2, xd); // 7. gxd = tv2 * xd # gxd = xd^3
|
|
140
|
+
let gx1 = Fp.mul(tv1, Fp.neg(ELL2_J)); // 8. gx1 = -J * tv1 # x1n + J * xd
|
|
141
|
+
gx1 = Fp.mul(gx1, x1n); // 9. gx1 = gx1 * x1n # x1n^2 + J * x1n * xd
|
|
142
|
+
gx1 = Fp.add(gx1, tv2); // 10. gx1 = gx1 + tv2 # x1n^2 + J * x1n * xd + xd^2
|
|
143
|
+
gx1 = Fp.mul(gx1, x1n); // 11. gx1 = gx1 * x1n # x1n^3 + J * x1n^2 * xd + x1n * xd^2
|
|
144
|
+
let tv3 = Fp.sqr(gxd); // 12. tv3 = gxd^2
|
|
145
|
+
tv2 = Fp.mul(gx1, gxd); // 13. tv2 = gx1 * gxd # gx1 * gxd
|
|
146
|
+
tv3 = Fp.mul(tv3, tv2); // 14. tv3 = tv3 * tv2 # gx1 * gxd^3
|
|
147
|
+
let y1 = Fp.pow(tv3, ELL2_C1); // 15. y1 = tv3^c1 # (gx1 * gxd^3)^((p - 3) / 4)
|
|
148
|
+
y1 = Fp.mul(y1, tv2); // 16. y1 = y1 * tv2 # gx1 * gxd * (gx1 * gxd^3)^((p - 3) / 4)
|
|
149
|
+
let x2n = Fp.mul(x1n, Fp.neg(tv1)); // 17. x2n = -tv1 * x1n # x2 = x2n / xd = -1 * u^2 * x1n / xd
|
|
150
|
+
let y2 = Fp.mul(y1, u); // 18. y2 = y1 * u
|
|
151
|
+
y2 = Fp.cmov(y2, Fp.ZERO, e1); // 19. y2 = CMOV(y2, 0, e1)
|
|
152
|
+
tv2 = Fp.sqr(y1); // 20. tv2 = y1^2
|
|
153
|
+
tv2 = Fp.mul(tv2, gxd); // 21. tv2 = tv2 * gxd
|
|
154
|
+
let e2 = Fp.eql(tv2, gx1); // 22. e2 = tv2 == gx1
|
|
155
|
+
let xn = Fp.cmov(x2n, x1n, e2); // 23. xn = CMOV(x2n, x1n, e2) # If e2, x = x1, else x = x2
|
|
156
|
+
let y = Fp.cmov(y2, y1, e2); // 24. y = CMOV(y2, y1, e2) # If e2, y = y1, else y = y2
|
|
157
|
+
let e3 = Fp.isOdd(y); // 25. e3 = sgn0(y) == 1 # Fix sign of y
|
|
158
|
+
y = Fp.cmov(y, Fp.neg(y), e2 !== e3); // 26. y = CMOV(y, -y, e2 XOR e3)
|
|
159
|
+
return { xn, xd, yn: y, yd: Fp.ONE }; // 27. return (xn, xd, y, 1)
|
|
160
|
+
}
|
|
161
|
+
function map_to_curve_elligator2_edwards448(u) {
|
|
162
|
+
let { xn, xd, yn, yd } = map_to_curve_elligator2_curve448(u); // 1. (xn, xd, yn, yd) = map_to_curve_elligator2_curve448(u)
|
|
163
|
+
let xn2 = Fp.sqr(xn); // 2. xn2 = xn^2
|
|
164
|
+
let xd2 = Fp.sqr(xd); // 3. xd2 = xd^2
|
|
165
|
+
let xd4 = Fp.sqr(xd2); // 4. xd4 = xd2^2
|
|
166
|
+
let yn2 = Fp.sqr(yn); // 5. yn2 = yn^2
|
|
167
|
+
let yd2 = Fp.sqr(yd); // 6. yd2 = yd^2
|
|
168
|
+
let xEn = Fp.sub(xn2, xd2); // 7. xEn = xn2 - xd2
|
|
169
|
+
let tv2 = Fp.sub(xEn, xd2); // 8. tv2 = xEn - xd2
|
|
170
|
+
xEn = Fp.mul(xEn, xd2); // 9. xEn = xEn * xd2
|
|
171
|
+
xEn = Fp.mul(xEn, yd); // 10. xEn = xEn * yd
|
|
172
|
+
xEn = Fp.mul(xEn, yn); // 11. xEn = xEn * yn
|
|
173
|
+
xEn = Fp.mul(xEn, 4n); // 12. xEn = xEn * 4
|
|
174
|
+
tv2 = Fp.mul(tv2, xn2); // 13. tv2 = tv2 * xn2
|
|
175
|
+
tv2 = Fp.mul(tv2, yd2); // 14. tv2 = tv2 * yd2
|
|
176
|
+
let tv3 = Fp.mul(yn2, 4n); // 15. tv3 = 4 * yn2
|
|
177
|
+
let tv1 = Fp.add(tv3, yd2); // 16. tv1 = tv3 + yd2
|
|
178
|
+
tv1 = Fp.mul(tv1, xd4); // 17. tv1 = tv1 * xd4
|
|
179
|
+
let xEd = Fp.add(tv1, tv2); // 18. xEd = tv1 + tv2
|
|
180
|
+
tv2 = Fp.mul(tv2, xn); // 19. tv2 = tv2 * xn
|
|
181
|
+
let tv4 = Fp.mul(xn, xd4); // 20. tv4 = xn * xd4
|
|
182
|
+
let yEn = Fp.sub(tv3, yd2); // 21. yEn = tv3 - yd2
|
|
183
|
+
yEn = Fp.mul(yEn, tv4); // 22. yEn = yEn * tv4
|
|
184
|
+
yEn = Fp.sub(yEn, tv2); // 23. yEn = yEn - tv2
|
|
185
|
+
tv1 = Fp.add(xn2, xd2); // 24. tv1 = xn2 + xd2
|
|
186
|
+
tv1 = Fp.mul(tv1, xd2); // 25. tv1 = tv1 * xd2
|
|
187
|
+
tv1 = Fp.mul(tv1, xd); // 26. tv1 = tv1 * xd
|
|
188
|
+
tv1 = Fp.mul(tv1, yn2); // 27. tv1 = tv1 * yn2
|
|
189
|
+
tv1 = Fp.mul(tv1, BigInt(-2)); // 28. tv1 = -2 * tv1
|
|
190
|
+
let yEd = Fp.add(tv2, tv1); // 29. yEd = tv2 + tv1
|
|
191
|
+
tv4 = Fp.mul(tv4, yd2); // 30. tv4 = tv4 * yd2
|
|
192
|
+
yEd = Fp.add(yEd, tv4); // 31. yEd = yEd + tv4
|
|
193
|
+
tv1 = Fp.mul(xEd, yEd); // 32. tv1 = xEd * yEd
|
|
194
|
+
let e = Fp.eql(tv1, Fp.ZERO); // 33. e = tv1 == 0
|
|
195
|
+
xEn = Fp.cmov(xEn, Fp.ZERO, e); // 34. xEn = CMOV(xEn, 0, e)
|
|
196
|
+
xEd = Fp.cmov(xEd, Fp.ONE, e); // 35. xEd = CMOV(xEd, 1, e)
|
|
197
|
+
yEn = Fp.cmov(yEn, Fp.ONE, e); // 36. yEn = CMOV(yEn, 1, e)
|
|
198
|
+
yEd = Fp.cmov(yEd, Fp.ONE, e); // 37. yEd = CMOV(yEd, 1, e)
|
|
199
|
+
const inv = Fp.invertBatch([xEd, yEd]); // batch division
|
|
200
|
+
return { x: Fp.mul(xEn, inv[0]), y: Fp.mul(yEn, inv[1]) }; // 38. return (xEn, xEd, yEn, yEd)
|
|
201
|
+
}
|
|
202
|
+
const { hashToCurve, encodeToCurve } = htf.hashToCurve(ed448.ExtendedPoint, (scalars) => map_to_curve_elligator2_edwards448(scalars[0]), {
|
|
203
|
+
DST: 'edwards448_XOF:SHAKE256_ELL2_RO_',
|
|
204
|
+
encodeDST: 'edwards448_XOF:SHAKE256_ELL2_NU_',
|
|
205
|
+
p: Fp.ORDER,
|
|
206
|
+
m: 1,
|
|
207
|
+
k: 224,
|
|
208
|
+
expand: 'xof',
|
|
209
|
+
hash: shake256,
|
|
210
|
+
});
|
|
211
|
+
export { hashToCurve, encodeToCurve };
|
package/lib/esm/jubjub.js
CHANGED
|
@@ -33,7 +33,7 @@ export function groupHash(tag, personalization) {
|
|
|
33
33
|
h.update(GH_FIRST_BLOCK);
|
|
34
34
|
h.update(tag);
|
|
35
35
|
// NOTE: returns ExtendedPoint, in case it will be multiplied later
|
|
36
|
-
let p = jubjub.ExtendedPoint.
|
|
36
|
+
let p = jubjub.ExtendedPoint.fromHex(h.digest());
|
|
37
37
|
// NOTE: cannot replace with isSmallOrder, returns Point*8
|
|
38
38
|
p = p.multiply(jubjub.CURVE.h);
|
|
39
39
|
if (p.equals(jubjub.ExtendedPoint.ZERO))
|
package/lib/esm/p256.js
CHANGED
|
@@ -3,6 +3,7 @@ import { createCurve } from './_shortw_utils.js';
|
|
|
3
3
|
import { sha256 } from '@noble/hashes/sha256';
|
|
4
4
|
import { Fp as Field } from './abstract/modular.js';
|
|
5
5
|
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
|
6
|
+
import * as htf from './abstract/hash-to-curve.js';
|
|
6
7
|
// NIST secp256r1 aka P256
|
|
7
8
|
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-256
|
|
8
9
|
// Field over which we'll do calculations; 2n**224n * (2n**32n-1n) + 2n**192n + 2n**96n-1n
|
|
@@ -26,14 +27,15 @@ export const P256 = createCurve({
|
|
|
26
27
|
Gy: BigInt('0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5'),
|
|
27
28
|
h: BigInt(1),
|
|
28
29
|
lowS: false,
|
|
29
|
-
mapToCurve: (scalars) => mapSWU(scalars[0]),
|
|
30
|
-
htfDefaults: {
|
|
31
|
-
DST: 'P256_XMD:SHA-256_SSWU_RO_',
|
|
32
|
-
p: Fp.ORDER,
|
|
33
|
-
m: 1,
|
|
34
|
-
k: 128,
|
|
35
|
-
expand: 'xmd',
|
|
36
|
-
hash: sha256,
|
|
37
|
-
},
|
|
38
30
|
}, sha256);
|
|
39
31
|
export const secp256r1 = P256;
|
|
32
|
+
const { hashToCurve, encodeToCurve } = htf.hashToCurve(secp256r1.ProjectivePoint, (scalars) => mapSWU(scalars[0]), {
|
|
33
|
+
DST: 'P256_XMD:SHA-256_SSWU_RO_',
|
|
34
|
+
encodeDST: 'P256_XMD:SHA-256_SSWU_NU_',
|
|
35
|
+
p: Fp.ORDER,
|
|
36
|
+
m: 1,
|
|
37
|
+
k: 128,
|
|
38
|
+
expand: 'xmd',
|
|
39
|
+
hash: sha256,
|
|
40
|
+
});
|
|
41
|
+
export { hashToCurve, encodeToCurve };
|
package/lib/esm/p384.js
CHANGED
|
@@ -3,6 +3,7 @@ import { createCurve } from './_shortw_utils.js';
|
|
|
3
3
|
import { sha384 } from '@noble/hashes/sha512';
|
|
4
4
|
import { Fp as Field } from './abstract/modular.js';
|
|
5
5
|
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
|
6
|
+
import * as htf from './abstract/hash-to-curve.js';
|
|
6
7
|
// NIST secp384r1 aka P384
|
|
7
8
|
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-384
|
|
8
9
|
// Field over which we'll do calculations. 2n**384n - 2n**128n - 2n**96n + 2n**32n - 1n
|
|
@@ -31,14 +32,15 @@ export const P384 = createCurve({
|
|
|
31
32
|
Gy: BigInt('0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f'),
|
|
32
33
|
h: BigInt(1),
|
|
33
34
|
lowS: false,
|
|
34
|
-
mapToCurve: (scalars) => mapSWU(scalars[0]),
|
|
35
|
-
htfDefaults: {
|
|
36
|
-
DST: 'P384_XMD:SHA-384_SSWU_RO_',
|
|
37
|
-
p: Fp.ORDER,
|
|
38
|
-
m: 1,
|
|
39
|
-
k: 192,
|
|
40
|
-
expand: 'xmd',
|
|
41
|
-
hash: sha384,
|
|
42
|
-
},
|
|
43
35
|
}, sha384);
|
|
44
36
|
export const secp384r1 = P384;
|
|
37
|
+
const { hashToCurve, encodeToCurve } = htf.hashToCurve(secp384r1.ProjectivePoint, (scalars) => mapSWU(scalars[0]), {
|
|
38
|
+
DST: 'P384_XMD:SHA-384_SSWU_RO_',
|
|
39
|
+
encodeDST: 'P384_XMD:SHA-384_SSWU_NU_',
|
|
40
|
+
p: Fp.ORDER,
|
|
41
|
+
m: 1,
|
|
42
|
+
k: 192,
|
|
43
|
+
expand: 'xmd',
|
|
44
|
+
hash: sha384,
|
|
45
|
+
});
|
|
46
|
+
export { hashToCurve, encodeToCurve };
|
package/lib/esm/p521.js
CHANGED
|
@@ -4,6 +4,7 @@ import { sha512 } from '@noble/hashes/sha512';
|
|
|
4
4
|
import { bytesToHex } from './abstract/utils.js';
|
|
5
5
|
import { Fp as Field } from './abstract/modular.js';
|
|
6
6
|
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
|
7
|
+
import * as htf from './abstract/hash-to-curve.js';
|
|
7
8
|
// NIST secp521r1 aka P521
|
|
8
9
|
// Note that it's 521, which differs from 512 of its hash function.
|
|
9
10
|
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-521
|
|
@@ -32,8 +33,7 @@ export const P521 = createCurve({
|
|
|
32
33
|
Gy: BigInt('0x011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650'),
|
|
33
34
|
h: BigInt(1),
|
|
34
35
|
lowS: false,
|
|
35
|
-
// P521 keys could be 130, 131, 132 bytes
|
|
36
|
-
// We ensure all keys are 132 bytes.
|
|
36
|
+
// P521 keys could be 130, 131, 132 bytes. We normalize to 132 bytes.
|
|
37
37
|
// Does not replace validation; invalid keys would still be rejected.
|
|
38
38
|
normalizePrivateKey(key) {
|
|
39
39
|
if (typeof key === 'bigint')
|
|
@@ -43,16 +43,17 @@ export const P521 = createCurve({
|
|
|
43
43
|
if (typeof key !== 'string' || !([130, 131, 132].includes(key.length))) {
|
|
44
44
|
throw new Error('Invalid key');
|
|
45
45
|
}
|
|
46
|
-
return key.padStart(66 * 2, '0');
|
|
47
|
-
},
|
|
48
|
-
mapToCurve: (scalars) => mapSWU(scalars[0]),
|
|
49
|
-
htfDefaults: {
|
|
50
|
-
DST: 'P521_XMD:SHA-512_SSWU_RO_',
|
|
51
|
-
p: Fp.ORDER,
|
|
52
|
-
m: 1,
|
|
53
|
-
k: 256,
|
|
54
|
-
expand: 'xmd',
|
|
55
|
-
hash: sha512,
|
|
46
|
+
return key.padStart(66 * 2, '0'); // ensure it's always 132 bytes
|
|
56
47
|
},
|
|
57
48
|
}, sha512);
|
|
58
49
|
export const secp521r1 = P521;
|
|
50
|
+
const { hashToCurve, encodeToCurve } = htf.hashToCurve(secp521r1.ProjectivePoint, (scalars) => mapSWU(scalars[0]), {
|
|
51
|
+
DST: 'P521_XMD:SHA-512_SSWU_RO_',
|
|
52
|
+
encodeDST: 'P521_XMD:SHA-512_SSWU_NU_',
|
|
53
|
+
p: Fp.ORDER,
|
|
54
|
+
m: 1,
|
|
55
|
+
k: 256,
|
|
56
|
+
expand: 'xmd',
|
|
57
|
+
hash: sha512,
|
|
58
|
+
});
|
|
59
|
+
export { hashToCurve, encodeToCurve };
|
package/lib/esm/secp256k1.js
CHANGED
|
@@ -3,9 +3,9 @@ import { sha256 } from '@noble/hashes/sha256';
|
|
|
3
3
|
import { Fp as Field, mod, pow2 } from './abstract/modular.js';
|
|
4
4
|
import { createCurve } from './_shortw_utils.js';
|
|
5
5
|
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
|
6
|
-
import { ensureBytes, concatBytes,
|
|
6
|
+
import { ensureBytes, concatBytes, bytesToNumberBE as bytesToNum, numberToBytesBE, } from './abstract/utils.js';
|
|
7
7
|
import { randomBytes } from '@noble/hashes/utils';
|
|
8
|
-
import
|
|
8
|
+
import * as htf from './abstract/hash-to-curve.js';
|
|
9
9
|
/**
|
|
10
10
|
* secp256k1 belongs to Koblitz curves: it has efficiently computable endomorphism.
|
|
11
11
|
* Endomorphism uses 2x less RAM, speeds up precomputation by 2x and ECDH / key recovery by 20%.
|
|
@@ -19,10 +19,7 @@ const _1n = BigInt(1);
|
|
|
19
19
|
const _2n = BigInt(2);
|
|
20
20
|
const divNearest = (a, b) => (a + b / _2n) / b;
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
23
|
-
* To calculate √y, we need to exponentiate it to a very big number:
|
|
24
|
-
* `y² = x³ + ax + b; y = y² ^ (p+1)/4`
|
|
25
|
-
* We are unwrapping the loop and multiplying it bit-by-bit.
|
|
22
|
+
* √n = n^((p+1)/4) for fields p = 3 mod 4. We unwrap the loop and multiply bit-by-bit.
|
|
26
23
|
* (P+1n/4n).toString(2) would produce bits [223x 1, 0, 22x 1, 4x 0, 11, 00]
|
|
27
24
|
*/
|
|
28
25
|
function sqrtMod(y) {
|
|
@@ -45,45 +42,11 @@ function sqrtMod(y) {
|
|
|
45
42
|
const t1 = (pow2(b223, _23n, P) * b22) % P;
|
|
46
43
|
const t2 = (pow2(t1, _6n, P) * b2) % P;
|
|
47
44
|
const root = pow2(t2, _2n, P);
|
|
48
|
-
if (!Fp.
|
|
45
|
+
if (!Fp.eql(Fp.sqr(root), y))
|
|
49
46
|
throw new Error('Cannot find square root');
|
|
50
47
|
return root;
|
|
51
48
|
}
|
|
52
49
|
const Fp = Field(secp256k1P, undefined, undefined, { sqrt: sqrtMod });
|
|
53
|
-
const isoMap = isogenyMap(Fp, [
|
|
54
|
-
// xNum
|
|
55
|
-
[
|
|
56
|
-
'0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa8c7',
|
|
57
|
-
'0x7d3d4c80bc321d5b9f315cea7fd44c5d595d2fc0bf63b92dfff1044f17c6581',
|
|
58
|
-
'0x534c328d23f234e6e2a413deca25caece4506144037c40314ecbd0b53d9dd262',
|
|
59
|
-
'0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa88c',
|
|
60
|
-
],
|
|
61
|
-
// xDen
|
|
62
|
-
[
|
|
63
|
-
'0xd35771193d94918a9ca34ccbb7b640dd86cd409542f8487d9fe6b745781eb49b',
|
|
64
|
-
'0xedadc6f64383dc1df7c4b2d51b54225406d36b641f5e41bbc52a56612a8c6d14',
|
|
65
|
-
'0x0000000000000000000000000000000000000000000000000000000000000001', // LAST 1
|
|
66
|
-
],
|
|
67
|
-
// yNum
|
|
68
|
-
[
|
|
69
|
-
'0x4bda12f684bda12f684bda12f684bda12f684bda12f684bda12f684b8e38e23c',
|
|
70
|
-
'0xc75e0c32d5cb7c0fa9d0a54b12a0a6d5647ab046d686da6fdffc90fc201d71a3',
|
|
71
|
-
'0x29a6194691f91a73715209ef6512e576722830a201be2018a765e85a9ecee931',
|
|
72
|
-
'0x2f684bda12f684bda12f684bda12f684bda12f684bda12f684bda12f38e38d84',
|
|
73
|
-
],
|
|
74
|
-
// yDen
|
|
75
|
-
[
|
|
76
|
-
'0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffff93b',
|
|
77
|
-
'0x7a06534bb8bdb49fd5e9e6632722c2989467c1bfc8e8d978dfb425d2685c2573',
|
|
78
|
-
'0x6484aa716545ca2cf3a70c3fa8fe337e0a3d21162f0d6299a7bf8192bfd2a76f',
|
|
79
|
-
'0x0000000000000000000000000000000000000000000000000000000000000001', // LAST 1
|
|
80
|
-
],
|
|
81
|
-
].map((i) => i.map((j) => BigInt(j))));
|
|
82
|
-
const mapSWU = mapToCurveSimpleSWU(Fp, {
|
|
83
|
-
A: BigInt('0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533'),
|
|
84
|
-
B: BigInt('1771'),
|
|
85
|
-
Z: Fp.create(BigInt('-11')),
|
|
86
|
-
});
|
|
87
50
|
export const secp256k1 = createCurve({
|
|
88
51
|
// Params: a, b
|
|
89
52
|
// Seem to be rigid https://bitcointalk.org/index.php?topic=289795.msg3183975#msg3183975
|
|
@@ -126,51 +89,13 @@ export const secp256k1 = createCurve({
|
|
|
126
89
|
return { k1neg, k1, k2neg, k2 };
|
|
127
90
|
},
|
|
128
91
|
},
|
|
129
|
-
mapToCurve: (scalars) => {
|
|
130
|
-
const { x, y } = mapSWU(Fp.create(scalars[0]));
|
|
131
|
-
return isoMap(x, y);
|
|
132
|
-
},
|
|
133
|
-
htfDefaults: {
|
|
134
|
-
DST: 'secp256k1_XMD:SHA-256_SSWU_RO_',
|
|
135
|
-
p: Fp.ORDER,
|
|
136
|
-
m: 1,
|
|
137
|
-
k: 128,
|
|
138
|
-
expand: 'xmd',
|
|
139
|
-
hash: sha256,
|
|
140
|
-
},
|
|
141
92
|
}, sha256);
|
|
142
|
-
// Schnorr
|
|
93
|
+
// Schnorr signatures are superior to ECDSA from above.
|
|
94
|
+
// Below is Schnorr-specific code as per BIP0340.
|
|
95
|
+
// https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
|
|
143
96
|
const _0n = BigInt(0);
|
|
144
|
-
const
|
|
145
|
-
const
|
|
146
|
-
const normalizePrivateKey = secp256k1.utils._normalizePrivateKey;
|
|
147
|
-
// TODO: export?
|
|
148
|
-
function normalizePublicKey(publicKey) {
|
|
149
|
-
if (publicKey instanceof secp256k1.Point) {
|
|
150
|
-
publicKey.assertValidity();
|
|
151
|
-
return publicKey;
|
|
152
|
-
}
|
|
153
|
-
else {
|
|
154
|
-
const bytes = ensureBytes(publicKey);
|
|
155
|
-
// Schnorr is 32 bytes
|
|
156
|
-
if (bytes.length !== 32)
|
|
157
|
-
throw new Error('Schnorr pubkeys must be 32 bytes');
|
|
158
|
-
const x = bytesToNumberBE(bytes);
|
|
159
|
-
if (!isValidFieldElement(x))
|
|
160
|
-
throw new Error('Point is not on curve');
|
|
161
|
-
const y2 = secp256k1.utils._weierstrassEquation(x); // y² = x³ + ax + b
|
|
162
|
-
let y = sqrtMod(y2); // y = y² ^ (p+1)/4
|
|
163
|
-
const isYOdd = (y & _1n) === _1n;
|
|
164
|
-
// Schnorr
|
|
165
|
-
if (isYOdd)
|
|
166
|
-
y = secp256k1.CURVE.Fp.negate(y);
|
|
167
|
-
const point = new secp256k1.Point(x, y);
|
|
168
|
-
point.assertValidity();
|
|
169
|
-
return point;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
const isWithinCurveOrder = secp256k1.utils._isWithinCurveOrder;
|
|
173
|
-
const isValidFieldElement = secp256k1.utils._isValidFieldElement;
|
|
97
|
+
const fe = (x) => typeof x === 'bigint' && _0n < x && x < secp256k1P;
|
|
98
|
+
const ge = (x) => typeof x === 'bigint' && _0n < x && x < secp256k1N;
|
|
174
99
|
const TAGS = {
|
|
175
100
|
challenge: 'BIP0340/challenge',
|
|
176
101
|
aux: 'BIP0340/aux',
|
|
@@ -178,7 +103,7 @@ const TAGS = {
|
|
|
178
103
|
};
|
|
179
104
|
/** An object mapping tags to their tagged hash prefix of [SHA256(tag) | SHA256(tag)] */
|
|
180
105
|
const TAGGED_HASH_PREFIXES = {};
|
|
181
|
-
|
|
106
|
+
function taggedHash(tag, ...messages) {
|
|
182
107
|
let tagP = TAGGED_HASH_PREFIXES[tag];
|
|
183
108
|
if (tagP === undefined) {
|
|
184
109
|
const tagH = sha256(Uint8Array.from(tag, (c) => c.charCodeAt(0)));
|
|
@@ -188,44 +113,37 @@ export function taggedHash(tag, ...messages) {
|
|
|
188
113
|
return sha256(concatBytes(tagP, ...messages));
|
|
189
114
|
}
|
|
190
115
|
const toRawX = (point) => point.toRawBytes(true).slice(1);
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
// Do we need this at all for Schnorr?
|
|
197
|
-
class SchnorrSignature {
|
|
198
|
-
constructor(r, s) {
|
|
199
|
-
this.r = r;
|
|
200
|
-
this.s = s;
|
|
201
|
-
this.assertValidity();
|
|
202
|
-
}
|
|
203
|
-
static fromHex(hex) {
|
|
204
|
-
const bytes = ensureBytes(hex);
|
|
205
|
-
const len = 32; // group length
|
|
206
|
-
if (bytes.length !== 2 * len)
|
|
207
|
-
throw new TypeError(`SchnorrSignature.fromHex: expected ${2 * len} bytes, not ${bytes.length}`);
|
|
208
|
-
const r = bytesToNumberBE(bytes.subarray(0, len));
|
|
209
|
-
const s = bytesToNumberBE(bytes.subarray(len, 2 * len));
|
|
210
|
-
return new SchnorrSignature(r, s);
|
|
211
|
-
}
|
|
212
|
-
assertValidity() {
|
|
213
|
-
const { r, s } = this;
|
|
214
|
-
if (!isValidFieldElement(r) || !isWithinCurveOrder(s))
|
|
215
|
-
throw new Error('Invalid signature');
|
|
216
|
-
}
|
|
217
|
-
toHex() {
|
|
218
|
-
return numTo32bStr(this.r) + numTo32bStr(this.s);
|
|
219
|
-
}
|
|
220
|
-
toRawBytes() {
|
|
221
|
-
return hexToBytes(this.toHex());
|
|
222
|
-
}
|
|
223
|
-
}
|
|
116
|
+
const numTo32b = (n) => numberToBytesBE(n, 32);
|
|
117
|
+
const modN = (x) => mod(x, secp256k1N);
|
|
118
|
+
const _Point = secp256k1.ProjectivePoint;
|
|
119
|
+
const Gmul = (priv) => _Point.fromPrivateKey(priv);
|
|
120
|
+
const GmulAdd = (Q, a, b) => _Point.BASE.multiplyAndAddUnsafe(Q, a, b);
|
|
224
121
|
function schnorrGetScalar(priv) {
|
|
225
|
-
|
|
226
|
-
|
|
122
|
+
// Let d' = int(sk)
|
|
123
|
+
// Fail if d' = 0 or d' ≥ n
|
|
124
|
+
// Let P = d'⋅G
|
|
125
|
+
// Let d = d' if has_even_y(P), otherwise let d = n - d' .
|
|
126
|
+
const point = Gmul(priv);
|
|
127
|
+
const scalar = point.hasEvenY() ? priv : modN(-priv);
|
|
227
128
|
return { point, scalar, x: toRawX(point) };
|
|
228
129
|
}
|
|
130
|
+
function lift_x(x) {
|
|
131
|
+
if (!fe(x))
|
|
132
|
+
throw new Error('bad x: need 0 < x < p'); // Fail if x ≥ p.
|
|
133
|
+
const c = mod(x * x * x + BigInt(7), secp256k1P); // Let c = x³ + 7 mod p.
|
|
134
|
+
let y = sqrtMod(c); // Let y = c^(p+1)/4 mod p.
|
|
135
|
+
if (y % 2n !== 0n)
|
|
136
|
+
y = mod(-y, secp256k1P); // Return the unique point P such that x(P) = x and
|
|
137
|
+
const p = new _Point(x, y, _1n); // y(P) = y if y mod 2 = 0 or y(P) = p-y otherwise.
|
|
138
|
+
p.assertValidity();
|
|
139
|
+
return p;
|
|
140
|
+
}
|
|
141
|
+
function challenge(...args) {
|
|
142
|
+
return modN(bytesToNum(taggedHash(TAGS.challenge, ...args)));
|
|
143
|
+
}
|
|
144
|
+
function schnorrGetPublicKey(privateKey) {
|
|
145
|
+
return toRawX(Gmul(privateKey)); // Let d' = int(sk). Fail if d' = 0 or d' ≥ n. Return bytes(d'⋅G)
|
|
146
|
+
}
|
|
229
147
|
/**
|
|
230
148
|
* Synchronously creates Schnorr signature. Improved security: verifies itself before
|
|
231
149
|
* producing an output.
|
|
@@ -235,23 +153,23 @@ function schnorrGetScalar(priv) {
|
|
|
235
153
|
*/
|
|
236
154
|
function schnorrSign(message, privateKey, auxRand = randomBytes(32)) {
|
|
237
155
|
if (message == null)
|
|
238
|
-
throw new
|
|
156
|
+
throw new Error(`sign: Expected valid message, not "${message}"`);
|
|
239
157
|
const m = ensureBytes(message);
|
|
240
158
|
// checks for isWithinCurveOrder
|
|
241
|
-
const { x: px, scalar: d } = schnorrGetScalar(
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
const
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
159
|
+
const { x: px, scalar: d } = schnorrGetScalar(bytesToNum(ensureBytes(privateKey, 32)));
|
|
160
|
+
const a = ensureBytes(auxRand, 32); // Auxiliary random data a: a 32-byte array
|
|
161
|
+
// TODO: replace with proper xor?
|
|
162
|
+
const t = numTo32b(d ^ bytesToNum(taggedHash(TAGS.aux, a))); // Let t be the byte-wise xor of bytes(d) and hash/aux(a)
|
|
163
|
+
const rand = taggedHash(TAGS.nonce, t, px, m); // Let rand = hash/nonce(t || bytes(P) || m)
|
|
164
|
+
const k_ = modN(bytesToNum(rand)); // Let k' = int(rand) mod n
|
|
165
|
+
if (k_ === _0n)
|
|
166
|
+
throw new Error('sign failed: k is zero'); // Fail if k' = 0.
|
|
167
|
+
const { point: R, x: rx, scalar: k } = schnorrGetScalar(k_); // Let R = k'⋅G.
|
|
168
|
+
const e = challenge(rx, px, m); // Let e = int(hash/challenge(bytes(R) || bytes(P) || m)) mod n.
|
|
169
|
+
const sig = new Uint8Array(64); // Let sig = bytes(R) || bytes((k + ed) mod n).
|
|
170
|
+
sig.set(numTo32b(R.px), 0);
|
|
171
|
+
sig.set(numTo32b(modN(k + e * d)), 32);
|
|
172
|
+
// If Verify(bytes(P), m, sig) (see below) returns failure, abort
|
|
255
173
|
if (!schnorrVerify(sig, m, px))
|
|
256
174
|
throw new Error('sign: Invalid signature produced');
|
|
257
175
|
return sig;
|
|
@@ -261,30 +179,76 @@ function schnorrSign(message, privateKey, auxRand = randomBytes(32)) {
|
|
|
261
179
|
*/
|
|
262
180
|
function schnorrVerify(signature, message, publicKey) {
|
|
263
181
|
try {
|
|
264
|
-
const
|
|
265
|
-
const sig =
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
const e = schnorrChallengeFinalize(taggedHash(TAGS.challenge, numTo32b(r), toRawX(P), m));
|
|
272
|
-
// Finalize
|
|
273
|
-
// R = s⋅G - e⋅P
|
|
274
|
-
// -eP == (n-e)P
|
|
275
|
-
const R = secp256k1.Point.BASE.multiplyAndAddUnsafe(P, normalizePrivateKey(s), mod(-e, secp256k1.CURVE.n));
|
|
276
|
-
if (!R || !R.hasEvenY() || R.x !== r)
|
|
182
|
+
const P = lift_x(bytesToNum(ensureBytes(publicKey, 32))); // P = lift_x(int(pk)); fail if that fails
|
|
183
|
+
const sig = ensureBytes(signature, 64);
|
|
184
|
+
const r = bytesToNum(sig.subarray(0, 32)); // Let r = int(sig[0:32]); fail if r ≥ p.
|
|
185
|
+
if (!fe(r))
|
|
186
|
+
return false;
|
|
187
|
+
const s = bytesToNum(sig.subarray(32, 64)); // Let s = int(sig[32:64]); fail if s ≥ n.
|
|
188
|
+
if (!ge(s))
|
|
277
189
|
return false;
|
|
278
|
-
|
|
190
|
+
const m = ensureBytes(message);
|
|
191
|
+
const e = challenge(numTo32b(r), toRawX(P), m); // int(challenge(bytes(r)||bytes(P)||m)) mod n
|
|
192
|
+
const R = GmulAdd(P, s, modN(-e)); // R = s⋅G - e⋅P
|
|
193
|
+
if (!R || !R.hasEvenY() || R.toAffine().x !== r)
|
|
194
|
+
return false; // -eP == (n-e)P
|
|
195
|
+
return true; // Fail if is_infinite(R) / not has_even_y(R) / x(R) ≠ r.
|
|
279
196
|
}
|
|
280
197
|
catch (error) {
|
|
281
198
|
return false;
|
|
282
199
|
}
|
|
283
200
|
}
|
|
284
201
|
export const schnorr = {
|
|
285
|
-
Signature: SchnorrSignature,
|
|
286
202
|
// Schnorr's pubkey is just `x` of Point (BIP340)
|
|
287
|
-
getPublicKey:
|
|
203
|
+
getPublicKey: schnorrGetPublicKey,
|
|
288
204
|
sign: schnorrSign,
|
|
289
205
|
verify: schnorrVerify,
|
|
206
|
+
utils: { lift_x, int: bytesToNum, taggedHash },
|
|
290
207
|
};
|
|
208
|
+
const isoMap = htf.isogenyMap(Fp, [
|
|
209
|
+
// xNum
|
|
210
|
+
[
|
|
211
|
+
'0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa8c7',
|
|
212
|
+
'0x7d3d4c80bc321d5b9f315cea7fd44c5d595d2fc0bf63b92dfff1044f17c6581',
|
|
213
|
+
'0x534c328d23f234e6e2a413deca25caece4506144037c40314ecbd0b53d9dd262',
|
|
214
|
+
'0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa88c',
|
|
215
|
+
],
|
|
216
|
+
// xDen
|
|
217
|
+
[
|
|
218
|
+
'0xd35771193d94918a9ca34ccbb7b640dd86cd409542f8487d9fe6b745781eb49b',
|
|
219
|
+
'0xedadc6f64383dc1df7c4b2d51b54225406d36b641f5e41bbc52a56612a8c6d14',
|
|
220
|
+
'0x0000000000000000000000000000000000000000000000000000000000000001', // LAST 1
|
|
221
|
+
],
|
|
222
|
+
// yNum
|
|
223
|
+
[
|
|
224
|
+
'0x4bda12f684bda12f684bda12f684bda12f684bda12f684bda12f684b8e38e23c',
|
|
225
|
+
'0xc75e0c32d5cb7c0fa9d0a54b12a0a6d5647ab046d686da6fdffc90fc201d71a3',
|
|
226
|
+
'0x29a6194691f91a73715209ef6512e576722830a201be2018a765e85a9ecee931',
|
|
227
|
+
'0x2f684bda12f684bda12f684bda12f684bda12f684bda12f684bda12f38e38d84',
|
|
228
|
+
],
|
|
229
|
+
// yDen
|
|
230
|
+
[
|
|
231
|
+
'0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffff93b',
|
|
232
|
+
'0x7a06534bb8bdb49fd5e9e6632722c2989467c1bfc8e8d978dfb425d2685c2573',
|
|
233
|
+
'0x6484aa716545ca2cf3a70c3fa8fe337e0a3d21162f0d6299a7bf8192bfd2a76f',
|
|
234
|
+
'0x0000000000000000000000000000000000000000000000000000000000000001', // LAST 1
|
|
235
|
+
],
|
|
236
|
+
].map((i) => i.map((j) => BigInt(j))));
|
|
237
|
+
const mapSWU = mapToCurveSimpleSWU(Fp, {
|
|
238
|
+
A: BigInt('0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533'),
|
|
239
|
+
B: BigInt('1771'),
|
|
240
|
+
Z: Fp.create(BigInt('-11')),
|
|
241
|
+
});
|
|
242
|
+
const { hashToCurve, encodeToCurve } = htf.hashToCurve(secp256k1.ProjectivePoint, (scalars) => {
|
|
243
|
+
const { x, y } = mapSWU(Fp.create(scalars[0]));
|
|
244
|
+
return isoMap(x, y);
|
|
245
|
+
}, {
|
|
246
|
+
DST: 'secp256k1_XMD:SHA-256_SSWU_RO_',
|
|
247
|
+
encodeDST: 'secp256k1_XMD:SHA-256_SSWU_NU_',
|
|
248
|
+
p: Fp.ORDER,
|
|
249
|
+
m: 1,
|
|
250
|
+
k: 128,
|
|
251
|
+
expand: 'xmd',
|
|
252
|
+
hash: sha256,
|
|
253
|
+
});
|
|
254
|
+
export { hashToCurve, encodeToCurve };
|