@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.
- package/README.md +238 -227
- package/_shortw_utils.d.ts +8 -5
- package/_shortw_utils.d.ts.map +1 -1
- package/_shortw_utils.js +3 -8
- package/_shortw_utils.js.map +1 -1
- package/abstract/bls.d.ts +123 -62
- package/abstract/bls.d.ts.map +1 -1
- package/abstract/bls.js +219 -163
- package/abstract/bls.js.map +1 -1
- package/abstract/curve.d.ts +142 -21
- package/abstract/curve.d.ts.map +1 -1
- package/abstract/curve.js +224 -143
- package/abstract/curve.js.map +1 -1
- package/abstract/edwards.d.ts +190 -49
- package/abstract/edwards.d.ts.map +1 -1
- package/abstract/edwards.js +322 -136
- package/abstract/edwards.js.map +1 -1
- package/abstract/fft.d.ts +12 -10
- package/abstract/fft.d.ts.map +1 -1
- package/abstract/fft.js +12 -13
- package/abstract/fft.js.map +1 -1
- package/abstract/hash-to-curve.d.ts +31 -13
- package/abstract/hash-to-curve.d.ts.map +1 -1
- package/abstract/hash-to-curve.js +34 -19
- package/abstract/hash-to-curve.js.map +1 -1
- package/abstract/modular.d.ts +31 -13
- package/abstract/modular.d.ts.map +1 -1
- package/abstract/modular.js +125 -52
- package/abstract/modular.js.map +1 -1
- package/abstract/montgomery.d.ts +18 -5
- package/abstract/montgomery.d.ts.map +1 -1
- package/abstract/montgomery.js +23 -6
- package/abstract/montgomery.js.map +1 -1
- package/abstract/poseidon.d.ts +5 -13
- package/abstract/poseidon.d.ts.map +1 -1
- package/abstract/poseidon.js +12 -7
- package/abstract/poseidon.js.map +1 -1
- package/abstract/tower.d.ts +23 -49
- package/abstract/tower.d.ts.map +1 -1
- package/abstract/tower.js +9 -3
- package/abstract/tower.js.map +1 -1
- package/abstract/utils.d.ts +1 -115
- package/abstract/utils.d.ts.map +1 -1
- package/abstract/utils.js +17 -371
- package/abstract/utils.js.map +1 -1
- package/abstract/weierstrass.d.ts +206 -124
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +747 -604
- package/abstract/weierstrass.js.map +1 -1
- package/bls12-381.d.ts +2 -0
- package/bls12-381.d.ts.map +1 -1
- package/bls12-381.js +504 -466
- package/bls12-381.js.map +1 -1
- package/bn254.d.ts +2 -0
- package/bn254.d.ts.map +1 -1
- package/bn254.js +44 -32
- package/bn254.js.map +1 -1
- package/ed25519.d.ts +55 -66
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +172 -186
- package/ed25519.js.map +1 -1
- package/ed448.d.ts +60 -57
- package/ed448.d.ts.map +1 -1
- package/ed448.js +172 -166
- package/ed448.js.map +1 -1
- package/esm/_shortw_utils.d.ts +8 -5
- package/esm/_shortw_utils.d.ts.map +1 -1
- package/esm/_shortw_utils.js +3 -8
- package/esm/_shortw_utils.js.map +1 -1
- package/esm/abstract/bls.d.ts +123 -62
- package/esm/abstract/bls.d.ts.map +1 -1
- package/esm/abstract/bls.js +220 -164
- package/esm/abstract/bls.js.map +1 -1
- package/esm/abstract/curve.d.ts +142 -21
- package/esm/abstract/curve.d.ts.map +1 -1
- package/esm/abstract/curve.js +219 -143
- package/esm/abstract/curve.js.map +1 -1
- package/esm/abstract/edwards.d.ts +190 -49
- package/esm/abstract/edwards.d.ts.map +1 -1
- package/esm/abstract/edwards.js +320 -138
- package/esm/abstract/edwards.js.map +1 -1
- package/esm/abstract/fft.d.ts +12 -10
- package/esm/abstract/fft.d.ts.map +1 -1
- package/esm/abstract/fft.js +10 -11
- package/esm/abstract/fft.js.map +1 -1
- package/esm/abstract/hash-to-curve.d.ts +31 -13
- package/esm/abstract/hash-to-curve.d.ts.map +1 -1
- package/esm/abstract/hash-to-curve.js +33 -19
- package/esm/abstract/hash-to-curve.js.map +1 -1
- package/esm/abstract/modular.d.ts +31 -13
- package/esm/abstract/modular.d.ts.map +1 -1
- package/esm/abstract/modular.js +124 -51
- package/esm/abstract/modular.js.map +1 -1
- package/esm/abstract/montgomery.d.ts +18 -5
- package/esm/abstract/montgomery.d.ts.map +1 -1
- package/esm/abstract/montgomery.js +23 -6
- package/esm/abstract/montgomery.js.map +1 -1
- package/esm/abstract/poseidon.d.ts +5 -13
- package/esm/abstract/poseidon.d.ts.map +1 -1
- package/esm/abstract/poseidon.js +12 -7
- package/esm/abstract/poseidon.js.map +1 -1
- package/esm/abstract/tower.d.ts +23 -49
- package/esm/abstract/tower.d.ts.map +1 -1
- package/esm/abstract/tower.js +9 -3
- package/esm/abstract/tower.js.map +1 -1
- package/esm/abstract/utils.d.ts +1 -115
- package/esm/abstract/utils.d.ts.map +1 -1
- package/esm/abstract/utils.js +3 -344
- package/esm/abstract/utils.js.map +1 -1
- package/esm/abstract/weierstrass.d.ts +206 -124
- package/esm/abstract/weierstrass.d.ts.map +1 -1
- package/esm/abstract/weierstrass.js +743 -605
- package/esm/abstract/weierstrass.js.map +1 -1
- package/esm/bls12-381.d.ts +2 -0
- package/esm/bls12-381.d.ts.map +1 -1
- package/esm/bls12-381.js +503 -465
- package/esm/bls12-381.js.map +1 -1
- package/esm/bn254.d.ts +2 -0
- package/esm/bn254.d.ts.map +1 -1
- package/esm/bn254.js +41 -29
- package/esm/bn254.js.map +1 -1
- package/esm/ed25519.d.ts +55 -66
- package/esm/ed25519.d.ts.map +1 -1
- package/esm/ed25519.js +170 -183
- package/esm/ed25519.js.map +1 -1
- package/esm/ed448.d.ts +60 -57
- package/esm/ed448.d.ts.map +1 -1
- package/esm/ed448.js +169 -162
- package/esm/ed448.js.map +1 -1
- package/esm/index.js +7 -9
- package/esm/index.js.map +1 -1
- package/esm/jubjub.d.ts +3 -3
- package/esm/jubjub.d.ts.map +1 -1
- package/esm/jubjub.js +3 -3
- package/esm/jubjub.js.map +1 -1
- package/esm/misc.d.ts +3 -5
- package/esm/misc.d.ts.map +1 -1
- package/esm/misc.js +31 -29
- package/esm/misc.js.map +1 -1
- package/esm/nist.d.ts +7 -22
- package/esm/nist.d.ts.map +1 -1
- package/esm/nist.js +106 -101
- package/esm/nist.js.map +1 -1
- package/esm/p256.d.ts +7 -3
- package/esm/p256.d.ts.map +1 -1
- package/esm/p256.js +4 -0
- package/esm/p256.js.map +1 -1
- package/esm/p384.d.ts +7 -4
- package/esm/p384.d.ts.map +1 -1
- package/esm/p384.js +4 -1
- package/esm/p384.js.map +1 -1
- package/esm/p521.d.ts +7 -3
- package/esm/p521.d.ts.map +1 -1
- package/esm/p521.js +4 -0
- package/esm/p521.js.map +1 -1
- package/esm/secp256k1.d.ts +38 -21
- package/esm/secp256k1.d.ts.map +1 -1
- package/esm/secp256k1.js +112 -104
- package/esm/secp256k1.js.map +1 -1
- package/esm/utils.d.ts +96 -0
- package/esm/utils.d.ts.map +1 -0
- package/esm/utils.js +279 -0
- package/esm/utils.js.map +1 -0
- package/index.js +7 -9
- package/index.js.map +1 -1
- package/jubjub.d.ts +3 -3
- package/jubjub.d.ts.map +1 -1
- package/jubjub.js +3 -3
- package/jubjub.js.map +1 -1
- package/misc.d.ts +3 -5
- package/misc.d.ts.map +1 -1
- package/misc.js +35 -33
- package/misc.js.map +1 -1
- package/nist.d.ts +7 -22
- package/nist.d.ts.map +1 -1
- package/nist.js +106 -101
- package/nist.js.map +1 -1
- package/p256.d.ts +7 -3
- package/p256.d.ts.map +1 -1
- package/p256.js +4 -0
- package/p256.js.map +1 -1
- package/p384.d.ts +7 -4
- package/p384.d.ts.map +1 -1
- package/p384.js +4 -1
- package/p384.js.map +1 -1
- package/p521.d.ts +7 -3
- package/p521.d.ts.map +1 -1
- package/p521.js +4 -0
- package/p521.js.map +1 -1
- package/package.json +17 -6
- package/secp256k1.d.ts +38 -21
- package/secp256k1.d.ts.map +1 -1
- package/secp256k1.js +112 -104
- package/secp256k1.js.map +1 -1
- package/src/_shortw_utils.ts +6 -15
- package/src/abstract/bls.ts +428 -251
- package/src/abstract/curve.ts +307 -149
- package/src/abstract/edwards.ts +555 -203
- package/src/abstract/fft.ts +30 -19
- package/src/abstract/hash-to-curve.ts +75 -34
- package/src/abstract/modular.ts +131 -59
- package/src/abstract/montgomery.ts +44 -15
- package/src/abstract/poseidon.ts +22 -18
- package/src/abstract/tower.ts +40 -71
- package/src/abstract/utils.ts +3 -378
- package/src/abstract/weierstrass.ts +1086 -746
- package/src/bls12-381.ts +549 -490
- package/src/bn254.ts +47 -35
- package/src/ed25519.ts +214 -216
- package/src/ed448.ts +251 -220
- package/src/index.ts +7 -9
- package/src/jubjub.ts +3 -3
- package/src/misc.ts +41 -40
- package/src/nist.ts +161 -126
- package/src/p256.ts +7 -3
- package/src/p384.ts +7 -5
- package/src/p521.ts +7 -3
- package/src/secp256k1.ts +145 -115
- package/src/utils.ts +328 -0
- package/utils.d.ts +96 -0
- package/utils.d.ts.map +1 -0
- package/utils.js +313 -0
- package/utils.js.map +1 -0
package/src/abstract/edwards.ts
CHANGED
|
@@ -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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
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
|
|
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?:
|
|
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
|
-
|
|
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
|
|
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
|
|
84
|
-
new (
|
|
85
|
-
|
|
86
|
-
fromHex(hex: Hex):
|
|
87
|
-
|
|
88
|
-
msm(points:
|
|
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
|
|
93
|
-
*
|
|
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
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
162
|
+
Point: EdwardsPointCons;
|
|
106
163
|
utils: {
|
|
107
|
-
|
|
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:
|
|
192
|
+
point: EdwardsPoint;
|
|
113
193
|
pointBytes: Uint8Array;
|
|
114
194
|
};
|
|
115
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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(
|
|
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
|
-
|
|
240
|
+
curveOpts.uvRatio ||
|
|
160
241
|
((u: bigint, v: bigint) => {
|
|
161
242
|
try {
|
|
162
|
-
return { isValid: true, value: Fp.sqrt(
|
|
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
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
function
|
|
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 {
|
|
270
|
+
const { X, Y, Z } = p;
|
|
189
271
|
const is0 = p.is0();
|
|
190
|
-
if (iz == null) iz = is0 ? _8n : (Fp.inv(
|
|
191
|
-
const
|
|
192
|
-
const
|
|
193
|
-
const zz =
|
|
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
|
|
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 {
|
|
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
|
|
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
|
-
|
|
227
|
-
readonly
|
|
228
|
-
readonly
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
this.
|
|
237
|
-
this.
|
|
238
|
-
this.
|
|
239
|
-
this.
|
|
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
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
274
|
-
//
|
|
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 {
|
|
283
|
-
const {
|
|
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.
|
|
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 {
|
|
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 {
|
|
329
|
-
const {
|
|
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 } =
|
|
358
|
-
return
|
|
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
|
|
460
|
+
if (n === _0n) return Point.ZERO;
|
|
370
461
|
if (this.is0() || n === _1n) return this;
|
|
371
|
-
return wnaf.
|
|
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.
|
|
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(
|
|
391
|
-
return toAffineMemo(this,
|
|
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
|
-
|
|
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.
|
|
540
|
+
return bytesToHex(this.toBytes());
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
toString() {
|
|
544
|
+
return `<Point ${this.is0() ? 'ZERO' : this.toHex()}>`;
|
|
446
545
|
}
|
|
447
546
|
}
|
|
448
|
-
const
|
|
449
|
-
|
|
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
|
|
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
|
-
|
|
473
|
-
function getExtendedPublicKey(
|
|
474
|
-
const { head, prefix, scalar } = getPrivateScalar(
|
|
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.
|
|
719
|
+
const pointBytes = point.toBytes();
|
|
477
720
|
return { head, prefix, scalar, point, pointBytes };
|
|
478
721
|
}
|
|
479
722
|
|
|
480
|
-
|
|
481
|
-
function getPublicKey(
|
|
482
|
-
return getExtendedPublicKey(
|
|
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,
|
|
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(
|
|
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).
|
|
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
|
|
502
|
-
|
|
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
|
-
|
|
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.
|
|
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().
|
|
783
|
+
return RkA.subtract(SB).clearCofactor().is0();
|
|
539
784
|
}
|
|
540
785
|
|
|
541
|
-
G.
|
|
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
|
-
|
|
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:
|
|
555
|
-
point.
|
|
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
|
-
|
|
562
|
-
|
|
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
|
}
|