forked from tornado-packages/noble-curves
Refactor
This commit is contained in:
parent
2bd5e9ac16
commit
5600629bca
@ -8,6 +8,7 @@
|
|||||||
// 2. Different sqrt() function
|
// 2. Different sqrt() function
|
||||||
// 3. truncateHash() truncateOnly mode
|
// 3. truncateHash() truncateOnly mode
|
||||||
// 4. DRBG supports outputLen bigger than outputLen of hmac
|
// 4. DRBG supports outputLen bigger than outputLen of hmac
|
||||||
|
// 5. Support for different hash functions
|
||||||
|
|
||||||
import * as mod from './modular.js';
|
import * as mod from './modular.js';
|
||||||
import {
|
import {
|
||||||
@ -238,6 +239,12 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
return _0n < num && num < CURVE.n;
|
return _0n < num && num < CURVE.n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates if a private key is valid and converts it to bigint form.
|
||||||
|
* Supports two options, that are passed when CURVE is initialized:
|
||||||
|
* - `normalizePrivateKey()` executed before all checks. Useful for P521 with var-length priv key
|
||||||
|
* - `wrapPrivateKey()` executed after most checks, but before `0 < key < n` check. Useful for BLS12-381 with cofactor higher than 1.
|
||||||
|
*/
|
||||||
function normalizePrivateKey(key: PrivKey): bigint {
|
function normalizePrivateKey(key: PrivKey): bigint {
|
||||||
if (typeof CURVE.normalizePrivateKey === 'function') {
|
if (typeof CURVE.normalizePrivateKey === 'function') {
|
||||||
key = CURVE.normalizePrivateKey(key);
|
key = CURVE.normalizePrivateKey(key);
|
||||||
@ -261,6 +268,10 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates if a scalar ("private number") is valid.
|
||||||
|
* Scalars are valid only if they are less than curve order.
|
||||||
|
*/
|
||||||
function normalizeScalar(num: number | bigint): bigint {
|
function normalizeScalar(num: number | bigint): bigint {
|
||||||
if (typeof num === 'number' && Number.isSafeInteger(num) && num > 0) return BigInt(num);
|
if (typeof num === 'number' && Number.isSafeInteger(num) && num > 0) return BigInt(num);
|
||||||
if (typeof num === 'bigint' && isWithinCurveOrder(num)) return num;
|
if (typeof num === 'bigint' && isWithinCurveOrder(num)) return num;
|
||||||
@ -289,7 +300,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes a bunch of Projective Points but executes only one
|
* Takes a bunch of Projective Points but executes only one
|
||||||
* invert on all of them. invert is very slow operation,
|
* inversion on all of them. Inversion is very slow operation,
|
||||||
* so this improves performance massively.
|
* so this improves performance massively.
|
||||||
*/
|
*/
|
||||||
static toAffineBatch(points: ProjectivePoint[]): Point[] {
|
static toAffineBatch(points: ProjectivePoint[]): Point[] {
|
||||||
@ -297,6 +308,9 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
return points.map((p, i) => p.toAffine(toInv[i]));
|
return points.map((p, i) => p.toAffine(toInv[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optimization: converts a list of projective points to a list of identical points with Z=1.
|
||||||
|
*/
|
||||||
static normalizeZ(points: ProjectivePoint[]): ProjectivePoint[] {
|
static normalizeZ(points: ProjectivePoint[]): ProjectivePoint[] {
|
||||||
return ProjectivePoint.toAffineBatch(points).map(ProjectivePoint.fromAffine);
|
return ProjectivePoint.toAffineBatch(points).map(ProjectivePoint.fromAffine);
|
||||||
}
|
}
|
||||||
@ -320,10 +334,6 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
return new ProjectivePoint(this.x, Fp.negate(this.y), this.z);
|
return new ProjectivePoint(this.x, Fp.negate(this.y), this.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
doubleAdd(): ProjectivePoint {
|
|
||||||
return this.add(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Renes-Costello-Batina exception-free doubling formula.
|
// Renes-Costello-Batina exception-free doubling formula.
|
||||||
// There is 30% faster Jacobian formula, but it is not complete.
|
// There is 30% faster Jacobian formula, but it is not complete.
|
||||||
// https://eprint.iacr.org/2015/1060, algorithm 3
|
// https://eprint.iacr.org/2015/1060, algorithm 3
|
||||||
@ -551,11 +561,11 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
*/
|
*/
|
||||||
class Point implements PointType<T> {
|
class Point implements PointType<T> {
|
||||||
/**
|
/**
|
||||||
* Base point aka generator. public_key = Point.BASE * private_key
|
* Base point aka generator. Any public_key = Point.BASE * private_key
|
||||||
*/
|
*/
|
||||||
static BASE: Point = new Point(CURVE.Gx, CURVE.Gy);
|
static BASE: Point = new Point(CURVE.Gx, CURVE.Gy);
|
||||||
/**
|
/**
|
||||||
* Identity point aka point at infinity. point = point + zero_point
|
* Identity point aka point at infinity. p - p = zero_p; p + zero_p = p
|
||||||
*/
|
*/
|
||||||
static ZERO: Point = new Point(Fp.ZERO, Fp.ZERO);
|
static ZERO: Point = new Point(Fp.ZERO, Fp.ZERO);
|
||||||
|
|
||||||
@ -835,8 +845,8 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
const CURVE = validateOpts(curveDef) as ReturnType<typeof validateOpts>;
|
const CURVE = validateOpts(curveDef) as ReturnType<typeof validateOpts>;
|
||||||
const CURVE_ORDER = CURVE.n;
|
const CURVE_ORDER = CURVE.n;
|
||||||
const Fp = CURVE.Fp;
|
const Fp = CURVE.Fp;
|
||||||
const compressedLen = Fp.BYTES + 1; // 33
|
const compressedLen = Fp.BYTES + 1; // e.g. 33 for 32
|
||||||
const uncompressedLen = 2 * Fp.BYTES + 1; // 65
|
const uncompressedLen = 2 * Fp.BYTES + 1; // e.g. 65 for 32
|
||||||
|
|
||||||
function isValidFieldElement(num: bigint): boolean {
|
function isValidFieldElement(num: bigint): boolean {
|
||||||
// 0 is disallowed by arbitrary reasons. Probably because infinity point?
|
// 0 is disallowed by arbitrary reasons. Probably because infinity point?
|
||||||
@ -910,13 +920,16 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
return isBiggerThanHalfOrder(s) ? mod.mod(-s, CURVE_ORDER) : s;
|
return isBiggerThanHalfOrder(s) ? mod.mod(-s, CURVE_ORDER) : s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function bits2int_2(bytes: Uint8Array): bigint {
|
||||||
|
const delta = bytes.length * 8 - CURVE.nBitLength;
|
||||||
|
const big = bytesToNumberBE(bytes);
|
||||||
|
return delta > 0 ? big >> BigInt(delta) : big;
|
||||||
|
}
|
||||||
|
|
||||||
// Ensures ECDSA message hashes are 32 bytes and < curve order
|
// Ensures ECDSA message hashes are 32 bytes and < curve order
|
||||||
function _truncateHash(hash: Uint8Array, truncateOnly = false): bigint {
|
function _truncateHash(hash: Uint8Array, truncateOnly = false): bigint {
|
||||||
const { n, nBitLength } = CURVE;
|
let h = bits2int_2(hash);
|
||||||
const byteLength = hash.length;
|
const { n } = CURVE;
|
||||||
const delta = byteLength * 8 - nBitLength; // size of curve.n (252 bits)
|
|
||||||
let h = bytesToNumberBE(hash);
|
|
||||||
if (delta > 0) h = h >> BigInt(delta);
|
|
||||||
if (!truncateOnly && h >= n) h -= n;
|
if (!truncateOnly && h >= n) h -= n;
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
@ -1009,16 +1022,17 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DER-encoded
|
// DER-encoded
|
||||||
toDERRawBytes(isCompressed = false) {
|
toDERRawBytes() {
|
||||||
return hexToBytes(this.toDERHex(isCompressed));
|
return hexToBytes(this.toDERHex());
|
||||||
}
|
}
|
||||||
toDERHex(isCompressed = false) {
|
toDERHex() {
|
||||||
const sHex = sliceDER(numberToHexUnpadded(this.s));
|
const sHex = sliceDER(numberToHexUnpadded(this.s));
|
||||||
if (isCompressed) return sHex;
|
|
||||||
const rHex = sliceDER(numberToHexUnpadded(this.r));
|
const rHex = sliceDER(numberToHexUnpadded(this.r));
|
||||||
const rLen = numberToHexUnpadded(rHex.length / 2);
|
const sHexL = sHex.length / 2;
|
||||||
const sLen = numberToHexUnpadded(sHex.length / 2);
|
const rHexL = rHex.length / 2;
|
||||||
const length = numberToHexUnpadded(rHex.length / 2 + sHex.length / 2 + 4);
|
const sLen = numberToHexUnpadded(sHexL);
|
||||||
|
const rLen = numberToHexUnpadded(rHexL);
|
||||||
|
const length = numberToHexUnpadded(rHexL + sHexL + 4);
|
||||||
return `30${length}02${rLen}${rHex}02${sLen}${sHex}`;
|
return `30${length}02${rLen}${rHex}02${sLen}${sHex}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1078,10 +1092,10 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computes public key for a private key.
|
* Computes public key for a private key. Checks for validity of the private key.
|
||||||
* @param privateKey private key
|
* @param privateKey private key
|
||||||
* @param isCompressed whether to return compact, or full key
|
* @param isCompressed whether to return compact (default), or full key
|
||||||
* @returns Public key, full by default; short when isCompressed=true
|
* @returns Public key, full when isCompressed=false; short when isCompressed=true
|
||||||
*/
|
*/
|
||||||
function getPublicKey(privateKey: PrivKey, isCompressed = false): Uint8Array {
|
function getPublicKey(privateKey: PrivKey, isCompressed = false): Uint8Array {
|
||||||
return Point.fromPrivateKey(privateKey).toRawBytes(isCompressed);
|
return Point.fromPrivateKey(privateKey).toRawBytes(isCompressed);
|
||||||
@ -1101,12 +1115,12 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ECDH (Elliptic Curve Diffie Hellman) implementation.
|
* ECDH (Elliptic Curve Diffie Hellman).
|
||||||
* 1. Checks for validity of private key
|
* Computes shared public key from private key and public key.
|
||||||
* 2. Checks for the public key of being on-curve
|
* Checks: 1) private key validity 2) shared key is on-curve
|
||||||
* @param privateA private key
|
* @param privateA private key
|
||||||
* @param publicB different public key
|
* @param publicB different public key
|
||||||
* @param isCompressed whether to return compact (33-byte), or full (65-byte) key
|
* @param isCompressed whether to return compact (default), or full key
|
||||||
* @returns shared public key
|
* @returns shared public key
|
||||||
*/
|
*/
|
||||||
function getSharedSecret(privateA: PrivKey, publicB: PubKey, isCompressed = false): Uint8Array {
|
function getSharedSecret(privateA: PrivKey, publicB: PubKey, isCompressed = false): Uint8Array {
|
||||||
@ -1118,8 +1132,9 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RFC6979 methods
|
// RFC6979 methods
|
||||||
function bits2int(bytes: Uint8Array) {
|
function bits2int(bytes: Uint8Array): bigint {
|
||||||
const slice = bytes.length > Fp.BYTES ? bytes.slice(0, Fp.BYTES) : bytes;
|
const { nByteLength } = CURVE;
|
||||||
|
const slice = bytes.length > nByteLength ? bytes.slice(0, nByteLength) : bytes;
|
||||||
return bytesToNumberBE(slice);
|
return bytesToNumberBE(slice);
|
||||||
}
|
}
|
||||||
function bits2octets(bytes: Uint8Array): Uint8Array {
|
function bits2octets(bytes: Uint8Array): Uint8Array {
|
||||||
|
@ -370,7 +370,7 @@ should('secp256k1.recoverPublicKey()/should recover public key from recovery bit
|
|||||||
const recoveredPubkey = sig.recoverPublicKey(message);
|
const recoveredPubkey = sig.recoverPublicKey(message);
|
||||||
// const recoveredPubkey = secp.recoverPublicKey(message, signature, recovery);
|
// const recoveredPubkey = secp.recoverPublicKey(message, signature, recovery);
|
||||||
deepStrictEqual(recoveredPubkey !== null, true);
|
deepStrictEqual(recoveredPubkey !== null, true);
|
||||||
deepStrictEqual(recoveredPubkey.toHex(), publicKey);
|
deepStrictEqual(recoveredPubkey.toHex(false), publicKey);
|
||||||
deepStrictEqual(secp.verify(sig, message, publicKey), true);
|
deepStrictEqual(secp.verify(sig, message, publicKey), true);
|
||||||
});
|
});
|
||||||
should('secp256k1.recoverPublicKey()/should not recover zero points', () => {
|
should('secp256k1.recoverPublicKey()/should not recover zero points', () => {
|
||||||
|
Loading…
Reference in New Issue
Block a user