@noble/curves 1.9.1 → 1.9.3

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.
Files changed (223) hide show
  1. package/README.md +238 -227
  2. package/_shortw_utils.d.ts +8 -5
  3. package/_shortw_utils.d.ts.map +1 -1
  4. package/_shortw_utils.js +3 -8
  5. package/_shortw_utils.js.map +1 -1
  6. package/abstract/bls.d.ts +123 -62
  7. package/abstract/bls.d.ts.map +1 -1
  8. package/abstract/bls.js +219 -163
  9. package/abstract/bls.js.map +1 -1
  10. package/abstract/curve.d.ts +142 -21
  11. package/abstract/curve.d.ts.map +1 -1
  12. package/abstract/curve.js +224 -143
  13. package/abstract/curve.js.map +1 -1
  14. package/abstract/edwards.d.ts +190 -49
  15. package/abstract/edwards.d.ts.map +1 -1
  16. package/abstract/edwards.js +322 -136
  17. package/abstract/edwards.js.map +1 -1
  18. package/abstract/fft.d.ts +12 -10
  19. package/abstract/fft.d.ts.map +1 -1
  20. package/abstract/fft.js +12 -13
  21. package/abstract/fft.js.map +1 -1
  22. package/abstract/hash-to-curve.d.ts +31 -13
  23. package/abstract/hash-to-curve.d.ts.map +1 -1
  24. package/abstract/hash-to-curve.js +34 -19
  25. package/abstract/hash-to-curve.js.map +1 -1
  26. package/abstract/modular.d.ts +31 -13
  27. package/abstract/modular.d.ts.map +1 -1
  28. package/abstract/modular.js +125 -52
  29. package/abstract/modular.js.map +1 -1
  30. package/abstract/montgomery.d.ts +18 -5
  31. package/abstract/montgomery.d.ts.map +1 -1
  32. package/abstract/montgomery.js +23 -6
  33. package/abstract/montgomery.js.map +1 -1
  34. package/abstract/poseidon.d.ts +5 -13
  35. package/abstract/poseidon.d.ts.map +1 -1
  36. package/abstract/poseidon.js +12 -7
  37. package/abstract/poseidon.js.map +1 -1
  38. package/abstract/tower.d.ts +23 -49
  39. package/abstract/tower.d.ts.map +1 -1
  40. package/abstract/tower.js +9 -3
  41. package/abstract/tower.js.map +1 -1
  42. package/abstract/utils.d.ts +1 -115
  43. package/abstract/utils.d.ts.map +1 -1
  44. package/abstract/utils.js +17 -371
  45. package/abstract/utils.js.map +1 -1
  46. package/abstract/weierstrass.d.ts +206 -124
  47. package/abstract/weierstrass.d.ts.map +1 -1
  48. package/abstract/weierstrass.js +747 -604
  49. package/abstract/weierstrass.js.map +1 -1
  50. package/bls12-381.d.ts +2 -0
  51. package/bls12-381.d.ts.map +1 -1
  52. package/bls12-381.js +504 -466
  53. package/bls12-381.js.map +1 -1
  54. package/bn254.d.ts +2 -0
  55. package/bn254.d.ts.map +1 -1
  56. package/bn254.js +44 -32
  57. package/bn254.js.map +1 -1
  58. package/ed25519.d.ts +55 -66
  59. package/ed25519.d.ts.map +1 -1
  60. package/ed25519.js +172 -186
  61. package/ed25519.js.map +1 -1
  62. package/ed448.d.ts +60 -57
  63. package/ed448.d.ts.map +1 -1
  64. package/ed448.js +172 -166
  65. package/ed448.js.map +1 -1
  66. package/esm/_shortw_utils.d.ts +8 -5
  67. package/esm/_shortw_utils.d.ts.map +1 -1
  68. package/esm/_shortw_utils.js +3 -8
  69. package/esm/_shortw_utils.js.map +1 -1
  70. package/esm/abstract/bls.d.ts +123 -62
  71. package/esm/abstract/bls.d.ts.map +1 -1
  72. package/esm/abstract/bls.js +220 -164
  73. package/esm/abstract/bls.js.map +1 -1
  74. package/esm/abstract/curve.d.ts +142 -21
  75. package/esm/abstract/curve.d.ts.map +1 -1
  76. package/esm/abstract/curve.js +219 -143
  77. package/esm/abstract/curve.js.map +1 -1
  78. package/esm/abstract/edwards.d.ts +190 -49
  79. package/esm/abstract/edwards.d.ts.map +1 -1
  80. package/esm/abstract/edwards.js +320 -138
  81. package/esm/abstract/edwards.js.map +1 -1
  82. package/esm/abstract/fft.d.ts +12 -10
  83. package/esm/abstract/fft.d.ts.map +1 -1
  84. package/esm/abstract/fft.js +10 -11
  85. package/esm/abstract/fft.js.map +1 -1
  86. package/esm/abstract/hash-to-curve.d.ts +31 -13
  87. package/esm/abstract/hash-to-curve.d.ts.map +1 -1
  88. package/esm/abstract/hash-to-curve.js +33 -19
  89. package/esm/abstract/hash-to-curve.js.map +1 -1
  90. package/esm/abstract/modular.d.ts +31 -13
  91. package/esm/abstract/modular.d.ts.map +1 -1
  92. package/esm/abstract/modular.js +124 -51
  93. package/esm/abstract/modular.js.map +1 -1
  94. package/esm/abstract/montgomery.d.ts +18 -5
  95. package/esm/abstract/montgomery.d.ts.map +1 -1
  96. package/esm/abstract/montgomery.js +23 -6
  97. package/esm/abstract/montgomery.js.map +1 -1
  98. package/esm/abstract/poseidon.d.ts +5 -13
  99. package/esm/abstract/poseidon.d.ts.map +1 -1
  100. package/esm/abstract/poseidon.js +12 -7
  101. package/esm/abstract/poseidon.js.map +1 -1
  102. package/esm/abstract/tower.d.ts +23 -49
  103. package/esm/abstract/tower.d.ts.map +1 -1
  104. package/esm/abstract/tower.js +9 -3
  105. package/esm/abstract/tower.js.map +1 -1
  106. package/esm/abstract/utils.d.ts +1 -115
  107. package/esm/abstract/utils.d.ts.map +1 -1
  108. package/esm/abstract/utils.js +3 -344
  109. package/esm/abstract/utils.js.map +1 -1
  110. package/esm/abstract/weierstrass.d.ts +206 -124
  111. package/esm/abstract/weierstrass.d.ts.map +1 -1
  112. package/esm/abstract/weierstrass.js +743 -605
  113. package/esm/abstract/weierstrass.js.map +1 -1
  114. package/esm/bls12-381.d.ts +2 -0
  115. package/esm/bls12-381.d.ts.map +1 -1
  116. package/esm/bls12-381.js +503 -465
  117. package/esm/bls12-381.js.map +1 -1
  118. package/esm/bn254.d.ts +2 -0
  119. package/esm/bn254.d.ts.map +1 -1
  120. package/esm/bn254.js +41 -29
  121. package/esm/bn254.js.map +1 -1
  122. package/esm/ed25519.d.ts +55 -66
  123. package/esm/ed25519.d.ts.map +1 -1
  124. package/esm/ed25519.js +170 -183
  125. package/esm/ed25519.js.map +1 -1
  126. package/esm/ed448.d.ts +60 -57
  127. package/esm/ed448.d.ts.map +1 -1
  128. package/esm/ed448.js +169 -162
  129. package/esm/ed448.js.map +1 -1
  130. package/esm/index.js +7 -9
  131. package/esm/index.js.map +1 -1
  132. package/esm/jubjub.d.ts +3 -3
  133. package/esm/jubjub.d.ts.map +1 -1
  134. package/esm/jubjub.js +3 -3
  135. package/esm/jubjub.js.map +1 -1
  136. package/esm/misc.d.ts +3 -5
  137. package/esm/misc.d.ts.map +1 -1
  138. package/esm/misc.js +31 -29
  139. package/esm/misc.js.map +1 -1
  140. package/esm/nist.d.ts +7 -22
  141. package/esm/nist.d.ts.map +1 -1
  142. package/esm/nist.js +106 -101
  143. package/esm/nist.js.map +1 -1
  144. package/esm/p256.d.ts +7 -3
  145. package/esm/p256.d.ts.map +1 -1
  146. package/esm/p256.js +4 -0
  147. package/esm/p256.js.map +1 -1
  148. package/esm/p384.d.ts +7 -4
  149. package/esm/p384.d.ts.map +1 -1
  150. package/esm/p384.js +4 -1
  151. package/esm/p384.js.map +1 -1
  152. package/esm/p521.d.ts +7 -3
  153. package/esm/p521.d.ts.map +1 -1
  154. package/esm/p521.js +4 -0
  155. package/esm/p521.js.map +1 -1
  156. package/esm/secp256k1.d.ts +38 -21
  157. package/esm/secp256k1.d.ts.map +1 -1
  158. package/esm/secp256k1.js +112 -104
  159. package/esm/secp256k1.js.map +1 -1
  160. package/esm/utils.d.ts +96 -0
  161. package/esm/utils.d.ts.map +1 -0
  162. package/esm/utils.js +279 -0
  163. package/esm/utils.js.map +1 -0
  164. package/index.js +7 -9
  165. package/index.js.map +1 -1
  166. package/jubjub.d.ts +3 -3
  167. package/jubjub.d.ts.map +1 -1
  168. package/jubjub.js +3 -3
  169. package/jubjub.js.map +1 -1
  170. package/misc.d.ts +3 -5
  171. package/misc.d.ts.map +1 -1
  172. package/misc.js +35 -33
  173. package/misc.js.map +1 -1
  174. package/nist.d.ts +7 -22
  175. package/nist.d.ts.map +1 -1
  176. package/nist.js +106 -101
  177. package/nist.js.map +1 -1
  178. package/p256.d.ts +7 -3
  179. package/p256.d.ts.map +1 -1
  180. package/p256.js +4 -0
  181. package/p256.js.map +1 -1
  182. package/p384.d.ts +7 -4
  183. package/p384.d.ts.map +1 -1
  184. package/p384.js +4 -1
  185. package/p384.js.map +1 -1
  186. package/p521.d.ts +7 -3
  187. package/p521.d.ts.map +1 -1
  188. package/p521.js +4 -0
  189. package/p521.js.map +1 -1
  190. package/package.json +17 -6
  191. package/secp256k1.d.ts +38 -21
  192. package/secp256k1.d.ts.map +1 -1
  193. package/secp256k1.js +112 -104
  194. package/secp256k1.js.map +1 -1
  195. package/src/_shortw_utils.ts +6 -15
  196. package/src/abstract/bls.ts +428 -251
  197. package/src/abstract/curve.ts +307 -149
  198. package/src/abstract/edwards.ts +555 -203
  199. package/src/abstract/fft.ts +30 -19
  200. package/src/abstract/hash-to-curve.ts +75 -34
  201. package/src/abstract/modular.ts +131 -59
  202. package/src/abstract/montgomery.ts +44 -15
  203. package/src/abstract/poseidon.ts +22 -18
  204. package/src/abstract/tower.ts +40 -71
  205. package/src/abstract/utils.ts +3 -378
  206. package/src/abstract/weierstrass.ts +1086 -746
  207. package/src/bls12-381.ts +549 -490
  208. package/src/bn254.ts +47 -35
  209. package/src/ed25519.ts +214 -216
  210. package/src/ed448.ts +251 -220
  211. package/src/index.ts +7 -9
  212. package/src/jubjub.ts +3 -3
  213. package/src/misc.ts +41 -40
  214. package/src/nist.ts +161 -126
  215. package/src/p256.ts +7 -3
  216. package/src/p384.ts +7 -5
  217. package/src/p521.ts +7 -3
  218. package/src/secp256k1.ts +145 -115
  219. package/src/utils.ts +328 -0
  220. package/utils.d.ts +96 -0
  221. package/utils.d.ts.map +1 -0
  222. package/utils.js +313 -0
  223. package/utils.js.map +1 -0
@@ -1,182 +1,264 @@
1
1
  /**
2
2
  * Twisted Edwards curve. The formula is: ax² + y² = 1 + dx²y².
3
3
  * For design rationale of types / exports, see weierstrass module documentation.
4
+ * Untwisted Edwards curves exist, but they aren't used in real-world protocols.
4
5
  * @module
5
6
  */
6
7
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
7
- // prettier-ignore
8
8
  import {
9
- pippenger, validateBasic, wNAF,
10
- type AffinePoint, type BasicCurve, type Group, type GroupConstructor
11
- } from './curve.ts';
12
- import { Field, FpInvertBatch, mod } from './modular.ts';
13
- // prettier-ignore
9
+ _validateObject,
10
+ abool,
11
+ abytes,
12
+ aInRange,
13
+ bytesToHex,
14
+ bytesToNumberLE,
15
+ concatBytes,
16
+ ensureBytes,
17
+ memoized,
18
+ numberToBytesLE,
19
+ randomBytes,
20
+ type FHash,
21
+ type Hex,
22
+ } from '../utils.ts';
14
23
  import {
15
- abool, aInRange, bytesToHex, bytesToNumberLE, concatBytes,
16
- ensureBytes, memoized, numberToBytesLE, validateObject,
17
- type FHash, type Hex
18
- } from './utils.ts';
24
+ _createCurveFields,
25
+ normalizeZ,
26
+ pippenger,
27
+ wNAF,
28
+ type AffinePoint,
29
+ type BasicCurve,
30
+ type CurveInfo,
31
+ type CurvePoint,
32
+ type CurvePointCons,
33
+ } from './curve.ts';
34
+ import { Field, type IField, type NLength } from './modular.ts';
19
35
 
20
36
  // Be friendly to bad ECMAScript parsers by not using bigint literals
21
37
  // prettier-ignore
22
38
  const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _8n = BigInt(8);
23
39
 
24
- /** Edwards curves must declare params a & d. */
40
+ export type UVRatio = (u: bigint, v: bigint) => { isValid: boolean; value: bigint };
41
+
42
+ // TODO: remove
25
43
  export type CurveType = BasicCurve<bigint> & {
26
44
  a: bigint; // curve param a
27
45
  d: bigint; // curve param d
28
46
  hash: FHash; // Hashing
29
- randomBytes: (bytesLength?: number) => Uint8Array; // CSPRNG
47
+ randomBytes?: (bytesLength?: number) => Uint8Array; // CSPRNG
30
48
  adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array; // clears bits to get valid field elemtn
31
49
  domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array; // Used for hashing
32
- uvRatio?: (u: bigint, v: bigint) => { isValid: boolean; value: bigint }; // Ratio √(u/v)
50
+ uvRatio?: UVRatio; // Ratio √(u/v)
33
51
  prehash?: FHash; // RFC 8032 pre-hashing of messages to sign() / verify()
34
52
  mapToCurve?: (scalar: bigint[]) => AffinePoint<bigint>; // for hash-to-curve standard
35
53
  };
36
54
 
37
- export type CurveTypeWithLength = Readonly<CurveType & { nByteLength: number; nBitLength: number }>;
38
-
39
- // verification rule is either zip215 or rfc8032 / nist186-5. Consult fromHex:
40
- const VERIFY_DEFAULT = { zip215: true };
41
-
42
- function validateOpts(curve: CurveType): CurveTypeWithLength {
43
- const opts = validateBasic(curve);
44
- validateObject(
45
- curve,
46
- {
47
- hash: 'function',
48
- a: 'bigint',
49
- d: 'bigint',
50
- randomBytes: 'function',
51
- },
52
- {
53
- adjustScalarBytes: 'function',
54
- domain: 'function',
55
- uvRatio: 'function',
56
- mapToCurve: 'function',
57
- }
58
- );
59
- // Set defaults
60
- return Object.freeze({ ...opts } as const);
61
- }
55
+ // TODO: remove
56
+ export type CurveTypeWithLength = Readonly<CurveType & Partial<NLength>>;
62
57
 
63
58
  /** Instance of Extended Point with coordinates in X, Y, Z, T. */
64
- export interface ExtPointType extends Group<ExtPointType> {
59
+ export interface EdwardsPoint extends CurvePoint<bigint, EdwardsPoint> {
60
+ /** extended X coordinate. Different from affine x. */
61
+ readonly X: bigint;
62
+ /** extended Y coordinate. Different from affine y. */
63
+ readonly Y: bigint;
64
+ /** extended Z coordinate */
65
+ readonly Z: bigint;
66
+ /** extended T coordinate */
67
+ readonly T: bigint;
68
+
69
+ /** @deprecated use `toBytes` */
70
+ toRawBytes(): Uint8Array;
71
+ /** @deprecated use `p.precompute(windowSize)` */
72
+ _setWindowSize(windowSize: number): void;
73
+ /** @deprecated use .X */
65
74
  readonly ex: bigint;
75
+ /** @deprecated use .Y */
66
76
  readonly ey: bigint;
77
+ /** @deprecated use .Z */
67
78
  readonly ez: bigint;
79
+ /** @deprecated use .T */
68
80
  readonly et: bigint;
69
- get x(): bigint;
70
- get y(): bigint;
71
- assertValidity(): void;
72
- multiply(scalar: bigint): ExtPointType;
73
- multiplyUnsafe(scalar: bigint): ExtPointType;
74
- isSmallOrder(): boolean;
75
- isTorsionFree(): boolean;
76
- clearCofactor(): ExtPointType;
77
- toAffine(iz?: bigint): AffinePoint<bigint>;
78
- toRawBytes(isCompressed?: boolean): Uint8Array;
79
- toHex(isCompressed?: boolean): string;
80
- _setWindowSize(windowSize: number): void;
81
81
  }
82
82
  /** Static methods of Extended Point with coordinates in X, Y, Z, T. */
83
- export interface ExtPointConstructor extends GroupConstructor<ExtPointType> {
84
- new (x: bigint, y: bigint, z: bigint, t: bigint): ExtPointType;
85
- fromAffine(p: AffinePoint<bigint>): ExtPointType;
86
- fromHex(hex: Hex): ExtPointType;
87
- fromPrivateKey(privateKey: Hex): ExtPointType;
88
- msm(points: ExtPointType[], scalars: bigint[]): ExtPointType;
83
+ export interface EdwardsPointCons extends CurvePointCons<bigint, EdwardsPoint> {
84
+ new (X: bigint, Y: bigint, Z: bigint, T: bigint): EdwardsPoint;
85
+ fromBytes(bytes: Uint8Array, zip215?: boolean): EdwardsPoint;
86
+ fromHex(hex: Hex, zip215?: boolean): EdwardsPoint;
87
+ /** @deprecated use `import { pippenger } from '@noble/curves/abstract/curve.js';` */
88
+ msm(points: EdwardsPoint[], scalars: bigint[]): EdwardsPoint;
89
89
  }
90
+ /** @deprecated use EdwardsPoint */
91
+ export type ExtPointType = EdwardsPoint;
92
+ /** @deprecated use EdwardsPointCons */
93
+ export type ExtPointConstructor = EdwardsPointCons;
90
94
 
91
95
  /**
92
- * Edwards Curve interface.
93
- * Main methods: `getPublicKey(priv)`, `sign(msg, priv)`, `verify(sig, msg, pub)`.
96
+ * Twisted Edwards curve options.
97
+ *
98
+ * * a: formula param
99
+ * * d: formula param
100
+ * * p: prime characteristic (order) of finite field, in which arithmetics is done
101
+ * * n: order of prime subgroup a.k.a total amount of valid curve points
102
+ * * h: cofactor. h*n is group order; n is subgroup order
103
+ * * Gx: x coordinate of generator point a.k.a. base point
104
+ * * Gy: y coordinate of generator point
94
105
  */
95
- export type CurveFn = {
96
- CURVE: ReturnType<typeof validateOpts>;
97
- getPublicKey: (privateKey: Hex) => Uint8Array;
98
- sign: (message: Hex, privateKey: Hex, options?: { context?: Hex }) => Uint8Array;
106
+ export type EdwardsOpts = Readonly<{
107
+ p: bigint;
108
+ n: bigint;
109
+ h: bigint;
110
+ a: bigint;
111
+ d: bigint;
112
+ Gx: bigint;
113
+ Gy: bigint;
114
+ }>;
115
+
116
+ /**
117
+ * Extra curve options for Twisted Edwards.
118
+ *
119
+ * * Fp: redefined Field over curve.p
120
+ * * Fn: redefined Field over curve.n
121
+ * * uvRatio: helper function for decompression, calculating √(u/v)
122
+ */
123
+ export type EdwardsExtraOpts = Partial<{
124
+ Fp: IField<bigint>;
125
+ Fn: IField<bigint>;
126
+ uvRatio: (u: bigint, v: bigint) => { isValid: boolean; value: bigint };
127
+ }>;
128
+
129
+ /**
130
+ * EdDSA (Edwards Digital Signature algorithm) options.
131
+ *
132
+ * * hash: hash function used to hash secret keys and messages
133
+ * * adjustScalarBytes: clears bits to get valid field element
134
+ * * domain: Used for hashing
135
+ * * mapToCurve: for hash-to-curve standard
136
+ * * prehash: RFC 8032 pre-hashing of messages to sign() / verify()
137
+ * * randomBytes: function generating random bytes, used for randomSecretKey
138
+ */
139
+ export type EdDSAOpts = Partial<{
140
+ adjustScalarBytes: (bytes: Uint8Array) => Uint8Array;
141
+ domain: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array;
142
+ mapToCurve: (scalar: bigint[]) => AffinePoint<bigint>;
143
+ prehash: FHash;
144
+ randomBytes: (bytesLength?: number) => Uint8Array;
145
+ }>;
146
+
147
+ /**
148
+ * EdDSA (Edwards Digital Signature algorithm) interface.
149
+ *
150
+ * Allows to create and verify signatures, create public and secret keys.
151
+ */
152
+ export interface EdDSA {
153
+ keygen: (seed?: Uint8Array) => { secretKey: Uint8Array; publicKey: Uint8Array };
154
+ getPublicKey: (secretKey: Hex) => Uint8Array;
155
+ sign: (message: Hex, secretKey: Hex, options?: { context?: Hex }) => Uint8Array;
99
156
  verify: (
100
157
  sig: Hex,
101
158
  message: Hex,
102
159
  publicKey: Hex,
103
160
  options?: { context?: Hex; zip215: boolean }
104
161
  ) => boolean;
105
- ExtendedPoint: ExtPointConstructor;
162
+ Point: EdwardsPointCons;
106
163
  utils: {
107
- randomPrivateKey: () => Uint8Array;
164
+ randomSecretKey: (seed?: Uint8Array) => Uint8Array;
165
+ isValidSecretKey: (secretKey: Uint8Array) => boolean;
166
+ isValidPublicKey: (publicKey: Uint8Array, zip215?: boolean) => boolean;
167
+
168
+ /**
169
+ * Converts ed public key to x public key.
170
+ * @example
171
+ * ```js
172
+ * const someonesPub = ed25519.getPublicKey(ed25519.utils.randomSecretKey());
173
+ * const aPriv = x25519.utils.randomSecretKey();
174
+ * x25519.getSharedSecret(aPriv, ed25519.utils.toMontgomery(someonesPub))
175
+ * ```
176
+ */
177
+ toMontgomery: (publicKey: Uint8Array) => Uint8Array;
178
+ /**
179
+ * Converts ed secret key to x secret key.
180
+ * @example
181
+ * ```js
182
+ * const someonesPub = x25519.getPublicKey(x25519.utils.randomSecretKey());
183
+ * const aPriv = ed25519.utils.randomSecretKey();
184
+ * x25519.getSharedSecret(ed25519.utils.toMontgomeryPriv(aPriv), someonesPub)
185
+ * ```
186
+ */
187
+ toMontgomeryPriv: (privateKey: Uint8Array) => Uint8Array;
108
188
  getExtendedPublicKey: (key: Hex) => {
109
189
  head: Uint8Array;
110
190
  prefix: Uint8Array;
111
191
  scalar: bigint;
112
- point: ExtPointType;
192
+ point: EdwardsPoint;
113
193
  pointBytes: Uint8Array;
114
194
  };
115
- precompute: (windowSize?: number, point?: ExtPointType) => ExtPointType;
195
+
196
+ /** @deprecated use `randomSecretKey` */
197
+ randomPrivateKey: (seed?: Uint8Array) => Uint8Array;
198
+ /** @deprecated use `point.precompute()` */
199
+ precompute: (windowSize?: number, point?: EdwardsPoint) => EdwardsPoint;
116
200
  };
201
+ info: CurveInfo;
202
+ }
203
+
204
+ // Legacy params. TODO: remove
205
+ export type CurveFn = {
206
+ CURVE: CurveType;
207
+ keygen: EdDSA['keygen'];
208
+ getPublicKey: EdDSA['getPublicKey'];
209
+ sign: EdDSA['sign'];
210
+ verify: EdDSA['verify'];
211
+ Point: EdwardsPointCons;
212
+ /** @deprecated use `Point` */
213
+ ExtendedPoint: EdwardsPointCons;
214
+ utils: EdDSA['utils'];
215
+ info: CurveInfo;
117
216
  };
118
217
 
119
- /**
120
- * Creates Twisted Edwards curve with EdDSA signatures.
121
- * @example
122
- * import { Field } from '@noble/curves/abstract/modular';
123
- * // Before that, define BigInt-s: a, d, p, n, Gx, Gy, h
124
- * const curve = twistedEdwards({ a, d, Fp: Field(p), n, Gx, Gy, h })
125
- */
126
- export function twistedEdwards(curveDef: CurveType): CurveFn {
127
- const CURVE = validateOpts(curveDef) as ReturnType<typeof validateOpts>;
128
- const {
129
- Fp,
130
- n: CURVE_ORDER,
131
- prehash: prehash,
132
- hash: cHash,
133
- randomBytes,
134
- nByteLength,
135
- h: cofactor,
136
- } = CURVE;
218
+ function isEdValidXY(Fp: IField<bigint>, CURVE: EdwardsOpts, x: bigint, y: bigint): boolean {
219
+ const x2 = Fp.sqr(x);
220
+ const y2 = Fp.sqr(y);
221
+ const left = Fp.add(Fp.mul(CURVE.a, x2), y2);
222
+ const right = Fp.add(Fp.ONE, Fp.mul(CURVE.d, Fp.mul(x2, y2)));
223
+ return Fp.eql(left, right);
224
+ }
225
+
226
+ export function edwards(CURVE: EdwardsOpts, curveOpts: EdwardsExtraOpts = {}): EdwardsPointCons {
227
+ const { Fp, Fn } = _createCurveFields('edwards', CURVE, curveOpts);
228
+ const { h: cofactor, n: CURVE_ORDER } = CURVE;
229
+ _validateObject(curveOpts, {}, { uvRatio: 'function' });
230
+
137
231
  // Important:
138
232
  // There are some places where Fp.BYTES is used instead of nByteLength.
139
233
  // So far, everything has been tested with curves of Fp.BYTES == nByteLength.
140
234
  // TODO: test and find curves which behave otherwise.
141
- const MASK = _2n << (BigInt(nByteLength * 8) - _1n);
142
- const modP = Fp.create; // Function overrides
143
- const Fn = Field(CURVE.n, CURVE.nBitLength);
144
-
145
- function isEdValidXY(x: bigint, y: bigint): boolean {
146
- const x2 = Fp.sqr(x);
147
- const y2 = Fp.sqr(y);
148
- const left = Fp.add(Fp.mul(CURVE.a, x2), y2);
149
- const right = Fp.add(Fp.ONE, Fp.mul(CURVE.d, Fp.mul(x2, y2)));
150
- return Fp.eql(left, right);
151
- }
152
-
153
- // Validate whether the passed curve params are valid.
154
- // equation ax² + y² = 1 + dx²y² should work for generator point.
155
- if (!isEdValidXY(CURVE.Gx, CURVE.Gy)) throw new Error('bad curve params: generator point');
235
+ const MASK = _2n << (BigInt(Fn.BYTES * 8) - _1n);
236
+ const modP = (n: bigint) => Fp.create(n); // Function overrides
156
237
 
157
238
  // sqrt(u/v)
158
239
  const uvRatio =
159
- CURVE.uvRatio ||
240
+ curveOpts.uvRatio ||
160
241
  ((u: bigint, v: bigint) => {
161
242
  try {
162
- return { isValid: true, value: Fp.sqrt(u * Fp.inv(v)) };
243
+ return { isValid: true, value: Fp.sqrt(Fp.div(u, v)) };
163
244
  } catch (e) {
164
245
  return { isValid: false, value: _0n };
165
246
  }
166
247
  });
167
- const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes: Uint8Array) => bytes); // NOOP
168
- const domain =
169
- CURVE.domain ||
170
- ((data: Uint8Array, ctx: Uint8Array, phflag: boolean) => {
171
- abool('phflag', phflag);
172
- if (ctx.length || phflag) throw new Error('Contexts/pre-hash are not supported');
173
- return data;
174
- }); // NOOP
175
- // 0 <= n < MASK
176
- // Coordinates larger than Fp.ORDER are allowed for zip215
177
- function aCoordinate(title: string, n: bigint, banZero = false) {
248
+
249
+ // Validate whether the passed curve params are valid.
250
+ // equation ax² + y² = 1 + dx²y² should work for generator point.
251
+ if (!isEdValidXY(Fp, CURVE, CURVE.Gx, CURVE.Gy))
252
+ throw new Error('bad curve params: generator point');
253
+
254
+ /**
255
+ * Asserts coordinate is valid: 0 <= n < MASK.
256
+ * Coordinates >= Fp.ORDER are allowed for zip215.
257
+ */
258
+ function acoord(title: string, n: bigint, banZero = false) {
178
259
  const min = banZero ? _1n : _0n;
179
260
  aInRange('coordinate ' + title, n, min, MASK);
261
+ return n;
180
262
  }
181
263
 
182
264
  function aextpoint(other: unknown) {
@@ -185,22 +267,22 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
185
267
  // Converts Extended point to default (x, y) coordinates.
186
268
  // Can accept precomputed Z^-1 - for example, from invertBatch.
187
269
  const toAffineMemo = memoized((p: Point, iz?: bigint): AffinePoint<bigint> => {
188
- const { ex: x, ey: y, ez: z } = p;
270
+ const { X, Y, Z } = p;
189
271
  const is0 = p.is0();
190
- if (iz == null) iz = is0 ? _8n : (Fp.inv(z) as bigint); // 8 was chosen arbitrarily
191
- const ax = modP(x * iz);
192
- const ay = modP(y * iz);
193
- const zz = modP(z * iz);
272
+ if (iz == null) iz = is0 ? _8n : (Fp.inv(Z) as bigint); // 8 was chosen arbitrarily
273
+ const x = modP(X * iz);
274
+ const y = modP(Y * iz);
275
+ const zz = Fp.mul(Z, iz);
194
276
  if (is0) return { x: _0n, y: _1n };
195
277
  if (zz !== _1n) throw new Error('invZ was invalid');
196
- return { x: ax, y: ay };
278
+ return { x, y };
197
279
  });
198
280
  const assertValidMemo = memoized((p: Point) => {
199
281
  const { a, d } = CURVE;
200
282
  if (p.is0()) throw new Error('bad point: ZERO'); // TODO: optimize, with vars below?
201
283
  // Equation in affine coordinates: ax² + y² = 1 + dx²y²
202
284
  // Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²
203
- const { ex: X, ey: Y, ez: Z, et: T } = p;
285
+ const { X, Y, Z, T } = p;
204
286
  const X2 = modP(X * X); // X²
205
287
  const Y2 = modP(Y * Y); // Y²
206
288
  const Z2 = modP(Z * Z); // Z²
@@ -218,25 +300,25 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
218
300
 
219
301
  // Extended Point works in extended coordinates: (X, Y, Z, T) ∋ (x=X/Z, y=Y/Z, T=xy).
220
302
  // https://en.wikipedia.org/wiki/Twisted_Edwards_curve#Extended_coordinates
221
- class Point implements ExtPointType {
303
+ class Point implements EdwardsPoint {
222
304
  // base / generator point
223
305
  static readonly BASE = new Point(CURVE.Gx, CURVE.Gy, _1n, modP(CURVE.Gx * CURVE.Gy));
224
306
  // zero / infinity / identity point
225
307
  static readonly ZERO = new Point(_0n, _1n, _1n, _0n); // 0, 1, 1, 0
226
- readonly ex: bigint;
227
- readonly ey: bigint;
228
- readonly ez: bigint;
229
- readonly et: bigint;
230
-
231
- constructor(ex: bigint, ey: bigint, ez: bigint, et: bigint) {
232
- aCoordinate('x', ex);
233
- aCoordinate('y', ey);
234
- aCoordinate('z', ez, true);
235
- aCoordinate('t', et);
236
- this.ex = ex;
237
- this.ey = ey;
238
- this.ez = ez;
239
- this.et = et;
308
+ // fields
309
+ static readonly Fp = Fp;
310
+ static readonly Fn = Fn;
311
+
312
+ readonly X: bigint;
313
+ readonly Y: bigint;
314
+ readonly Z: bigint;
315
+ readonly T: bigint;
316
+
317
+ constructor(X: bigint, Y: bigint, Z: bigint, T: bigint) {
318
+ this.X = acoord('x', X);
319
+ this.Y = acoord('y', Y);
320
+ this.Z = acoord('z', Z, true);
321
+ this.T = acoord('t', T);
240
322
  Object.freeze(this);
241
323
  }
242
324
 
@@ -247,31 +329,44 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
247
329
  return this.toAffine().y;
248
330
  }
249
331
 
250
- static fromAffine(p: AffinePoint<bigint>): Point {
251
- if (p instanceof Point) throw new Error('extended point not allowed');
252
- const { x, y } = p || {};
253
- aCoordinate('x', x);
254
- aCoordinate('y', y);
255
- return new Point(x, y, _1n, modP(x * y));
332
+ // TODO: remove
333
+ get ex(): bigint {
334
+ return this.X;
335
+ }
336
+ get ey(): bigint {
337
+ return this.Y;
338
+ }
339
+ get ez(): bigint {
340
+ return this.Z;
341
+ }
342
+ get et(): bigint {
343
+ return this.T;
256
344
  }
257
345
  static normalizeZ(points: Point[]): Point[] {
258
- const toInv = FpInvertBatch(
259
- Fp,
260
- points.map((p) => p.ez)
261
- );
262
- return points.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
346
+ return normalizeZ(Point, points);
263
347
  }
264
- // Multiscalar Multiplication
265
348
  static msm(points: Point[], scalars: bigint[]): Point {
266
349
  return pippenger(Point, Fn, points, scalars);
267
350
  }
268
-
269
- // "Private method", don't use it directly
270
351
  _setWindowSize(windowSize: number) {
271
- wnaf.setWindowSize(this, windowSize);
352
+ this.precompute(windowSize);
353
+ }
354
+
355
+ static fromAffine(p: AffinePoint<bigint>): Point {
356
+ if (p instanceof Point) throw new Error('extended point not allowed');
357
+ const { x, y } = p || {};
358
+ acoord('x', x);
359
+ acoord('y', y);
360
+ return new Point(x, y, _1n, modP(x * y));
361
+ }
362
+
363
+ precompute(windowSize: number = 8, isLazy = true) {
364
+ wnaf.createCache(this, windowSize);
365
+ if (!isLazy) this.multiply(_2n); // random number
366
+ return this;
272
367
  }
273
- // Not required for fromHex(), which always creates valid points.
274
- // Could be useful for fromAffine().
368
+
369
+ // Useful in fromAffine() - not for fromBytes(), which always created valid points.
275
370
  assertValidity(): void {
276
371
  assertValidMemo(this);
277
372
  }
@@ -279,8 +374,8 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
279
374
  // Compare one point to another.
280
375
  equals(other: Point): boolean {
281
376
  aextpoint(other);
282
- const { ex: X1, ey: Y1, ez: Z1 } = this;
283
- const { ex: X2, ey: Y2, ez: Z2 } = other;
377
+ const { X: X1, Y: Y1, Z: Z1 } = this;
378
+ const { X: X2, Y: Y2, Z: Z2 } = other;
284
379
  const X1Z2 = modP(X1 * Z2);
285
380
  const X2Z1 = modP(X2 * Z1);
286
381
  const Y1Z2 = modP(Y1 * Z2);
@@ -294,7 +389,7 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
294
389
 
295
390
  negate(): Point {
296
391
  // Flips point sign to a negative one (-x, y in affine coords)
297
- return new Point(modP(-this.ex), this.ey, this.ez, modP(-this.et));
392
+ return new Point(modP(-this.X), this.Y, this.Z, modP(-this.T));
298
393
  }
299
394
 
300
395
  // Fast algo for doubling Extended Point.
@@ -302,7 +397,7 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
302
397
  // Cost: 4M + 4S + 1*a + 6add + 1*2.
303
398
  double(): Point {
304
399
  const { a } = CURVE;
305
- const { ex: X1, ey: Y1, ez: Z1 } = this;
400
+ const { X: X1, Y: Y1, Z: Z1 } = this;
306
401
  const A = modP(X1 * X1); // A = X12
307
402
  const B = modP(Y1 * Y1); // B = Y12
308
403
  const C = modP(_2n * modP(Z1 * Z1)); // C = 2*Z12
@@ -325,8 +420,8 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
325
420
  add(other: Point) {
326
421
  aextpoint(other);
327
422
  const { a, d } = CURVE;
328
- const { ex: X1, ey: Y1, ez: Z1, et: T1 } = this;
329
- const { ex: X2, ey: Y2, ez: Z2, et: T2 } = other;
423
+ const { X: X1, Y: Y1, Z: Z1, T: T1 } = this;
424
+ const { X: X2, Y: Y2, Z: Z2, T: T2 } = other;
330
425
  const A = modP(X1 * X2); // A = X1*X2
331
426
  const B = modP(Y1 * Y2); // B = Y1*Y2
332
427
  const C = modP(T1 * d * T2); // C = T1*d*T2
@@ -346,16 +441,12 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
346
441
  return this.add(other.negate());
347
442
  }
348
443
 
349
- private wNAF(n: bigint): { p: Point; f: Point } {
350
- return wnaf.wNAFCached(this, n, Point.normalizeZ);
351
- }
352
-
353
444
  // Constant-time multiplication.
354
445
  multiply(scalar: bigint): Point {
355
446
  const n = scalar;
356
447
  aInRange('scalar', n, _1n, CURVE_ORDER); // 1 <= scalar < L
357
- const { p, f } = this.wNAF(n);
358
- return Point.normalizeZ([p, f])[0];
448
+ const { p, f } = wnaf.cached(this, n, (p) => normalizeZ(Point, p));
449
+ return normalizeZ(Point, [p, f])[0];
359
450
  }
360
451
 
361
452
  // Non-constant-time multiplication. Uses double-and-add algorithm.
@@ -366,9 +457,9 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
366
457
  multiplyUnsafe(scalar: bigint, acc = Point.ZERO): Point {
367
458
  const n = scalar;
368
459
  aInRange('scalar', n, _0n, CURVE_ORDER); // 0 <= scalar < L
369
- if (n === _0n) return I;
460
+ if (n === _0n) return Point.ZERO;
370
461
  if (this.is0() || n === _1n) return this;
371
- return wnaf.wNAFCachedUnsafe(this, n, Point.normalizeZ, acc);
462
+ return wnaf.unsafe(this, n, (p) => normalizeZ(Point, p), acc);
372
463
  }
373
464
 
374
465
  // Checks if point is of small order.
@@ -382,21 +473,25 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
382
473
  // Multiplies point by curve order and checks if the result is 0.
383
474
  // Returns `false` is the point is dirty.
384
475
  isTorsionFree(): boolean {
385
- return wnaf.unsafeLadder(this, CURVE_ORDER).is0();
476
+ return wnaf.unsafe(this, CURVE_ORDER).is0();
386
477
  }
387
478
 
388
479
  // Converts Extended point to default (x, y) coordinates.
389
480
  // Can accept precomputed Z^-1 - for example, from invertBatch.
390
- toAffine(iz?: bigint): AffinePoint<bigint> {
391
- return toAffineMemo(this, iz);
481
+ toAffine(invertedZ?: bigint): AffinePoint<bigint> {
482
+ return toAffineMemo(this, invertedZ);
392
483
  }
393
484
 
394
485
  clearCofactor(): Point {
395
- const { h: cofactor } = CURVE;
396
486
  if (cofactor === _1n) return this;
397
487
  return this.multiplyUnsafe(cofactor);
398
488
  }
399
489
 
490
+ static fromBytes(bytes: Uint8Array, zip215 = false): Point {
491
+ abytes(bytes);
492
+ return Point.fromHex(bytes, zip215);
493
+ }
494
+
400
495
  // Converts hash string or Uint8Array to Point.
401
496
  // Uses algo from RFC8032 5.1.3.
402
497
  static fromHex(hex: Hex, zip215 = false): Point {
@@ -431,28 +526,176 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
431
526
  if (isLastByteOdd !== isXOdd) x = modP(-x); // if x_0 != x mod 2, set x = p-x
432
527
  return Point.fromAffine({ x, y });
433
528
  }
434
- static fromPrivateKey(privKey: Hex): Point {
435
- const { scalar } = getPrivateScalar(privKey);
436
- return G.multiply(scalar); // reduced one call of `toRawBytes`
437
- }
438
- toRawBytes(): Uint8Array {
529
+ toBytes(): Uint8Array {
439
530
  const { x, y } = this.toAffine();
440
531
  const bytes = numberToBytesLE(y, Fp.BYTES); // each y has 2 x values (x, -y)
441
532
  bytes[bytes.length - 1] |= x & _1n ? 0x80 : 0; // when compressing, it's enough to store y
442
533
  return bytes; // and use the last byte to encode sign of x
443
534
  }
535
+ /** @deprecated use `toBytes` */
536
+ toRawBytes(): Uint8Array {
537
+ return this.toBytes();
538
+ }
444
539
  toHex(): string {
445
- return bytesToHex(this.toRawBytes()); // Same as toRawBytes, but returns string.
540
+ return bytesToHex(this.toBytes());
541
+ }
542
+
543
+ toString() {
544
+ return `<Point ${this.is0() ? 'ZERO' : this.toHex()}>`;
446
545
  }
447
546
  }
448
- const { BASE: G, ZERO: I } = Point;
449
- const wnaf = wNAF(Point, nByteLength * 8);
547
+ const wnaf = new wNAF(Point, Fn.BYTES * 8); // Fn.BITS?
548
+ return Point;
549
+ }
550
+
551
+ /**
552
+ * Base class for prime-order points like Ristretto255 and Decaf448.
553
+ * These points eliminate cofactor issues by representing equivalence classes
554
+ * of Edwards curve points.
555
+ */
556
+ export abstract class PrimeEdwardsPoint<T extends PrimeEdwardsPoint<T>>
557
+ implements CurvePoint<bigint, T>
558
+ {
559
+ static BASE: PrimeEdwardsPoint<any>;
560
+ static ZERO: PrimeEdwardsPoint<any>;
561
+ static Fp: IField<bigint>;
562
+ static Fn: IField<bigint>;
563
+
564
+ protected readonly ep: EdwardsPoint;
565
+
566
+ constructor(ep: EdwardsPoint) {
567
+ this.ep = ep;
568
+ }
569
+
570
+ // Abstract methods that must be implemented by subclasses
571
+ abstract toBytes(): Uint8Array;
572
+ abstract equals(other: T): boolean;
573
+
574
+ // Static methods that must be implemented by subclasses
575
+ static fromBytes(_bytes: Uint8Array): any {
576
+ throw new Error('fromBytes must be implemented by subclass');
577
+ }
578
+
579
+ static fromHex(_hex: Hex): any {
580
+ throw new Error('fromHex must be implemented by subclass');
581
+ }
582
+
583
+ get x(): bigint {
584
+ return this.toAffine().x;
585
+ }
586
+ get y(): bigint {
587
+ return this.toAffine().y;
588
+ }
589
+
590
+ // Common implementations
591
+ clearCofactor(): T {
592
+ // no-op for prime-order groups
593
+ return this as any;
594
+ }
595
+
596
+ assertValidity(): void {
597
+ this.ep.assertValidity();
598
+ }
599
+
600
+ toAffine(invertedZ?: bigint): AffinePoint<bigint> {
601
+ return this.ep.toAffine(invertedZ);
602
+ }
603
+
604
+ /** @deprecated use `toBytes` */
605
+ toRawBytes(): Uint8Array {
606
+ return this.toBytes();
607
+ }
608
+
609
+ toHex(): string {
610
+ return bytesToHex(this.toBytes());
611
+ }
612
+
613
+ toString(): string {
614
+ return this.toHex();
615
+ }
616
+
617
+ isTorsionFree(): boolean {
618
+ return true;
619
+ }
620
+
621
+ isSmallOrder(): boolean {
622
+ return false;
623
+ }
624
+
625
+ add(other: T): T {
626
+ this.assertSame(other);
627
+ return this.init(this.ep.add(other.ep));
628
+ }
629
+
630
+ subtract(other: T): T {
631
+ this.assertSame(other);
632
+ return this.init(this.ep.subtract(other.ep));
633
+ }
634
+
635
+ multiply(scalar: bigint): T {
636
+ return this.init(this.ep.multiply(scalar));
637
+ }
638
+
639
+ multiplyUnsafe(scalar: bigint): T {
640
+ return this.init(this.ep.multiplyUnsafe(scalar));
641
+ }
642
+
643
+ double(): T {
644
+ return this.init(this.ep.double());
645
+ }
646
+
647
+ negate(): T {
648
+ return this.init(this.ep.negate());
649
+ }
650
+
651
+ precompute(windowSize?: number, isLazy?: boolean): T {
652
+ return this.init(this.ep.precompute(windowSize, isLazy));
653
+ }
654
+
655
+ // Helper methods
656
+ abstract is0(): boolean;
657
+ protected abstract assertSame(other: T): void;
658
+ protected abstract init(ep: EdwardsPoint): T;
659
+ }
660
+
661
+ /**
662
+ * Initializes EdDSA signatures over given Edwards curve.
663
+ */
664
+ export function eddsa(Point: EdwardsPointCons, cHash: FHash, eddsaOpts: EdDSAOpts): EdDSA {
665
+ if (typeof cHash !== 'function') throw new Error('"hash" function param is required');
666
+ _validateObject(
667
+ eddsaOpts,
668
+ {},
669
+ {
670
+ adjustScalarBytes: 'function',
671
+ randomBytes: 'function',
672
+ domain: 'function',
673
+ prehash: 'function',
674
+ mapToCurve: 'function',
675
+ }
676
+ );
677
+
678
+ const { prehash } = eddsaOpts;
679
+ const { BASE: G, Fp, Fn } = Point;
680
+ const CURVE_ORDER = Fn.ORDER;
681
+
682
+ const randomBytes_ = eddsaOpts.randomBytes || randomBytes;
683
+ const adjustScalarBytes = eddsaOpts.adjustScalarBytes || ((bytes: Uint8Array) => bytes); // NOOP
684
+ const domain =
685
+ eddsaOpts.domain ||
686
+ ((data: Uint8Array, ctx: Uint8Array, phflag: boolean) => {
687
+ abool('phflag', phflag);
688
+ if (ctx.length || phflag) throw new Error('Contexts/pre-hash are not supported');
689
+ return data;
690
+ }); // NOOP
450
691
 
451
692
  function modN(a: bigint) {
452
- return mod(a, CURVE_ORDER);
693
+ return Fn.create(a);
453
694
  }
695
+
454
696
  // Little-endian SHA512 with modulo n
455
697
  function modN_LE(hash: Uint8Array): bigint {
698
+ // Not using Fn.fromBytes: hash can be 2*Fn.BYTES
456
699
  return modN(bytesToNumberLE(hash));
457
700
  }
458
701
 
@@ -469,17 +712,17 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
469
712
  return { head, prefix, scalar };
470
713
  }
471
714
 
472
- // Convenience method that creates public key from scalar. RFC8032 5.1.5
473
- function getExtendedPublicKey(key: Hex) {
474
- const { head, prefix, scalar } = getPrivateScalar(key);
715
+ /** Convenience method that creates public key from scalar. RFC8032 5.1.5 */
716
+ function getExtendedPublicKey(secretKey: Hex) {
717
+ const { head, prefix, scalar } = getPrivateScalar(secretKey);
475
718
  const point = G.multiply(scalar); // Point on Edwards curve aka public key
476
- const pointBytes = point.toRawBytes(); // Uint8Array representation
719
+ const pointBytes = point.toBytes();
477
720
  return { head, prefix, scalar, point, pointBytes };
478
721
  }
479
722
 
480
- // Calculates EdDSA pub key. RFC8032 5.1.5. Privkey is hashed. Use first half with 3 bits cleared
481
- function getPublicKey(privKey: Hex): Uint8Array {
482
- return getExtendedPublicKey(privKey).pointBytes;
723
+ /** Calculates EdDSA pub key. RFC8032 5.1.5. */
724
+ function getPublicKey(secretKey: Hex): Uint8Array {
725
+ return getExtendedPublicKey(secretKey).pointBytes;
483
726
  }
484
727
 
485
728
  // int('LE', SHA512(dom2(F, C) || msgs)) mod N
@@ -489,20 +732,22 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
489
732
  }
490
733
 
491
734
  /** Signs message with privateKey. RFC8032 5.1.6 */
492
- function sign(msg: Hex, privKey: Hex, options: { context?: Hex } = {}): Uint8Array {
735
+ function sign(msg: Hex, secretKey: Hex, options: { context?: Hex } = {}): Uint8Array {
493
736
  msg = ensureBytes('message', msg);
494
737
  if (prehash) msg = prehash(msg); // for ed25519ph etc.
495
- const { prefix, scalar, pointBytes } = getExtendedPublicKey(privKey);
738
+ const { prefix, scalar, pointBytes } = getExtendedPublicKey(secretKey);
496
739
  const r = hashDomainToScalar(options.context, prefix, msg); // r = dom2(F, C) || prefix || PH(M)
497
- const R = G.multiply(r).toRawBytes(); // R = rG
740
+ const R = G.multiply(r).toBytes(); // R = rG
498
741
  const k = hashDomainToScalar(options.context, R, pointBytes, msg); // R || A || PH(M)
499
742
  const s = modN(r + k * scalar); // S = (r + k * s) mod L
500
743
  aInRange('signature.s', s, _0n, CURVE_ORDER); // 0 <= s < l
501
- const res = concatBytes(R, numberToBytesLE(s, Fp.BYTES));
502
- return ensureBytes('result', res, Fp.BYTES * 2); // 64-byte signature
744
+ const L = Fp.BYTES;
745
+ const res = concatBytes(R, numberToBytesLE(s, L));
746
+ return ensureBytes('result', res, L * 2); // 64-byte signature
503
747
  }
504
748
 
505
- const verifyOpts: { context?: Hex; zip215?: boolean } = VERIFY_DEFAULT;
749
+ // verification rule is either zip215 or rfc8032 / nist186-5. Consult fromHex:
750
+ const verifyOpts: { context?: Hex; zip215?: boolean } = { zip215: true };
506
751
 
507
752
  /**
508
753
  * Verifies EdDSA signature against message and public key. RFC8032 5.1.7.
@@ -531,19 +776,63 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
531
776
  }
532
777
  if (!zip215 && A.isSmallOrder()) return false;
533
778
 
534
- const k = hashDomainToScalar(context, R.toRawBytes(), A.toRawBytes(), msg);
779
+ const k = hashDomainToScalar(context, R.toBytes(), A.toBytes(), msg);
535
780
  const RkA = R.add(A.multiplyUnsafe(k));
536
781
  // Extended group equation
537
782
  // [8][S]B = [8]R + [8][k]A'
538
- return RkA.subtract(SB).clearCofactor().equals(Point.ZERO);
783
+ return RkA.subtract(SB).clearCofactor().is0();
539
784
  }
540
785
 
541
- G._setWindowSize(8); // Enable precomputes. Slows down first publicKey computation by 20ms.
786
+ G.precompute(8); // Enable precomputes. Slows down first publicKey computation by 20ms.
787
+
788
+ const size = Fp.BYTES;
789
+ const lengths = {
790
+ secret: size,
791
+ public: size,
792
+ signature: 2 * size,
793
+ seed: size,
794
+ };
795
+ function randomSecretKey(seed = randomBytes_!(lengths.seed)): Uint8Array {
796
+ return seed;
797
+ }
542
798
 
543
799
  const utils = {
544
800
  getExtendedPublicKey,
545
801
  /** ed25519 priv keys are uniform 32b. No need to check for modulo bias, like in secp256k1. */
546
- randomPrivateKey: (): Uint8Array => randomBytes(Fp.BYTES),
802
+ randomSecretKey,
803
+
804
+ isValidSecretKey,
805
+ isValidPublicKey,
806
+
807
+ randomPrivateKey: randomSecretKey,
808
+
809
+ /**
810
+ * Converts ed public key to x public key. Uses formula:
811
+ * - ed25519:
812
+ * - `(u, v) = ((1+y)/(1-y), sqrt(-486664)*u/x)`
813
+ * - `(x, y) = (sqrt(-486664)*u/v, (u-1)/(u+1))`
814
+ * - ed448:
815
+ * - `(u, v) = ((y-1)/(y+1), sqrt(156324)*u/x)`
816
+ * - `(x, y) = (sqrt(156324)*u/v, (1+u)/(1-u))`
817
+ *
818
+ * There is NO `fromMontgomery`:
819
+ * - There are 2 valid ed25519 points for every x25519, with flipped coordinate
820
+ * - Sometimes there are 0 valid ed25519 points, because x25519 *additionally*
821
+ * accepts inputs on the quadratic twist, which can't be moved to ed25519
822
+ */
823
+ toMontgomery(publicKey: Uint8Array): Uint8Array {
824
+ const { y } = Point.fromBytes(publicKey);
825
+ const is25519 = size === 32;
826
+ if (!is25519 && size !== 57) throw new Error('only defined for 25519 and 448');
827
+ const u = is25519 ? Fp.div(_1n + y, _1n - y) : Fp.div(y - _1n, y + _1n);
828
+ return Fp.toBytes(u);
829
+ },
830
+
831
+ toMontgomeryPriv(privateKey: Uint8Array): Uint8Array {
832
+ abytes(privateKey, size);
833
+ const hashed = cHash(privateKey.subarray(0, size));
834
+ return adjustScalarBytes(hashed).subarray(0, size);
835
+ },
547
836
 
548
837
  /**
549
838
  * We're doing scalar multiplication (used in getPublicKey etc) with precomputed BASE_POINT
@@ -551,19 +840,82 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
551
840
  * but allows to speed-up subsequent getPublicKey() calls up to 20x.
552
841
  * @param windowSize 2, 4, 8, 16
553
842
  */
554
- precompute(windowSize = 8, point: ExtPointType = Point.BASE): ExtPointType {
555
- point._setWindowSize(windowSize);
556
- point.multiply(BigInt(3));
557
- return point;
843
+ precompute(windowSize = 8, point: EdwardsPoint = Point.BASE): EdwardsPoint {
844
+ return point.precompute(windowSize, false);
558
845
  },
559
846
  };
560
847
 
561
- return {
562
- CURVE,
848
+ function keygen(seed?: Uint8Array) {
849
+ const secretKey = utils.randomSecretKey(seed);
850
+ return { secretKey, publicKey: getPublicKey(secretKey) };
851
+ }
852
+
853
+ function isValidSecretKey(key: Uint8Array): boolean {
854
+ try {
855
+ return !!Fn.fromBytes(key, false);
856
+ } catch (error) {
857
+ return false;
858
+ }
859
+ }
860
+
861
+ function isValidPublicKey(key: Uint8Array, zip215?: boolean): boolean {
862
+ try {
863
+ return !!Point.fromBytes(key, zip215);
864
+ } catch (error) {
865
+ return false;
866
+ }
867
+ }
868
+
869
+ return Object.freeze({
870
+ keygen,
563
871
  getPublicKey,
564
872
  sign,
565
873
  verify,
566
- ExtendedPoint: Point,
567
874
  utils,
875
+ Point,
876
+ info: { type: 'edwards' as const, lengths },
877
+ });
878
+ }
879
+
880
+ // TODO: remove
881
+ export type EdComposed = {
882
+ CURVE: EdwardsOpts;
883
+ curveOpts: EdwardsExtraOpts;
884
+ hash: FHash;
885
+ eddsaOpts: EdDSAOpts;
886
+ };
887
+ // TODO: remove
888
+ function _eddsa_legacy_opts_to_new(c: CurveTypeWithLength): EdComposed {
889
+ const CURVE: EdwardsOpts = {
890
+ a: c.a,
891
+ d: c.d,
892
+ p: c.Fp.ORDER,
893
+ n: c.n,
894
+ h: c.h,
895
+ Gx: c.Gx,
896
+ Gy: c.Gy,
897
+ };
898
+ const Fp = c.Fp;
899
+ const Fn = Field(CURVE.n, c.nBitLength, true);
900
+ const curveOpts: EdwardsExtraOpts = { Fp, Fn, uvRatio: c.uvRatio };
901
+ const eddsaOpts: EdDSAOpts = {
902
+ randomBytes: c.randomBytes,
903
+ adjustScalarBytes: c.adjustScalarBytes,
904
+ domain: c.domain,
905
+ prehash: c.prehash,
906
+ mapToCurve: c.mapToCurve,
568
907
  };
908
+ return { CURVE, curveOpts, hash: c.hash, eddsaOpts };
909
+ }
910
+ // TODO: remove
911
+ function _eddsa_new_output_to_legacy(c: CurveTypeWithLength, eddsa: EdDSA): CurveFn {
912
+ const legacy = Object.assign({}, eddsa, { ExtendedPoint: eddsa.Point, CURVE: c });
913
+ return legacy;
914
+ }
915
+ // TODO: remove. Use eddsa
916
+ export function twistedEdwards(c: CurveTypeWithLength): CurveFn {
917
+ const { CURVE, curveOpts, hash, eddsaOpts } = _eddsa_legacy_opts_to_new(c);
918
+ const Point = edwards(CURVE, curveOpts);
919
+ const EDDSA = eddsa(Point, hash, eddsaOpts);
920
+ return _eddsa_new_output_to_legacy(c, EDDSA);
569
921
  }