@noble/curves
Advanced tools
Comparing version 0.6.1 to 0.6.2
@@ -112,3 +112,26 @@ "use strict"; | ||
} | ||
assertValidity() { } | ||
// Not required for fromHex(), which always creates valid points. | ||
// Could be useful for fromAffine(). | ||
assertValidity() { | ||
const { a, d } = CURVE; | ||
if (this.is0()) | ||
throw new Error('bad point: ZERO'); // TODO: optimize, with vars below? | ||
// Equation in affine coordinates: ax² + y² = 1 + dx²y² | ||
// Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y² | ||
const { ex: X, ey: Y, ez: Z, et: T } = this; | ||
const X2 = modP(X * X); // X² | ||
const Y2 = modP(Y * Y); // Y² | ||
const Z2 = modP(Z * Z); // Z² | ||
const Z4 = modP(Z2 * Z2); // Z⁴ | ||
const aX2 = modP(X2 * a); // aX² | ||
const left = modP(Z2 * modP(aX2 + Y2)); // (aX² + Y²)Z² | ||
const right = modP(Z4 + modP(d * modP(X2 * Y2))); // Z⁴ + dX²Y² | ||
if (left !== right) | ||
throw new Error('bad point: equation left != right (1)'); | ||
// In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T | ||
const XY = modP(X * Y); | ||
const ZT = modP(Z * T); | ||
if (XY !== ZT) | ||
throw new Error('bad point: equation left != right (2)'); | ||
} | ||
// Compare one point to another. | ||
@@ -115,0 +138,0 @@ equals(other) { |
@@ -45,3 +45,3 @@ export declare function mod(a: bigint, b: bigint): bigint; | ||
} | ||
export declare function validateField<T>(field: Field<T>): object; | ||
export declare function validateField<T>(field: Field<T>): Field<T>; | ||
export declare function FpPow<T>(f: Field<T>, num: T, power: bigint): T; | ||
@@ -48,0 +48,0 @@ export declare function FpInvertBatch<T>(f: Field<T>, nums: T[]): T[]; |
@@ -124,3 +124,5 @@ "use strict"; | ||
const u = (0, utils_js_1.ensureBytes)(uEnc, montgomeryBytes); | ||
u[fieldLen - 1] &= 127; // 0b0111_1111 | ||
// u[fieldLen-1] crashes QuickJS (TypeError: out-of-bound numeric index) | ||
if (fieldLen === montgomeryBytes) | ||
u[fieldLen - 1] &= 127; // 0b0111_1111 | ||
return (0, utils_js_1.bytesToNumberLE)(u); | ||
@@ -127,0 +129,0 @@ } |
@@ -28,4 +28,17 @@ export declare type Hex = Uint8Array | string; | ||
export declare const bitMask: (n: number) => bigint; | ||
declare type ValMap = Record<string, string>; | ||
export declare function validateObject(object: object, validators: ValMap, optValidators?: ValMap): object; | ||
declare const validatorFns: { | ||
readonly bigint: (val: any) => boolean; | ||
readonly function: (val: any) => boolean; | ||
readonly boolean: (val: any) => boolean; | ||
readonly string: (val: any) => boolean; | ||
readonly isSafeInteger: (val: any) => boolean; | ||
readonly array: (val: any) => boolean; | ||
readonly field: (val: any, object: any) => any; | ||
readonly hash: (val: any) => boolean; | ||
}; | ||
declare type Validator = keyof typeof validatorFns; | ||
declare type ValMap<T extends Record<string, any>> = { | ||
[K in keyof T]?: Validator; | ||
}; | ||
export declare function validateObject<T extends Record<string, any>>(object: T, validators: ValMap<T>, optValidators?: ValMap<T>): T; | ||
export {}; |
@@ -30,3 +30,3 @@ "use strict"; | ||
// Big Endian | ||
return BigInt(`0x${hex}`); | ||
return BigInt(hex === '' ? '0' : `0x${hex}`); | ||
} | ||
@@ -122,14 +122,14 @@ exports.hexToNumber = hexToNumber; | ||
exports.bitMask = bitMask; | ||
const validatorFns = { | ||
bigint: (val) => typeof val === 'bigint', | ||
function: (val) => typeof val === 'function', | ||
boolean: (val) => typeof val === 'boolean', | ||
string: (val) => typeof val === 'string', | ||
isSafeInteger: (val) => Number.isSafeInteger(val), | ||
array: (val) => Array.isArray(val), | ||
field: (val, object) => object.Fp.isValid(val), | ||
hash: (val) => typeof val === 'function' && Number.isSafeInteger(val.outputLen), | ||
}; | ||
// type Record<K extends string | number | symbol, T> = { [P in K]: T; } | ||
function validateObject(object, validators, optValidators = {}) { | ||
const validatorFns = { | ||
bigint: (val) => typeof val === 'bigint', | ||
function: (val) => typeof val === 'function', | ||
boolean: (val) => typeof val === 'boolean', | ||
string: (val) => typeof val === 'string', | ||
isSafeInteger: (val) => Number.isSafeInteger(val), | ||
array: (val) => Array.isArray(val), | ||
field: (val) => object.Fp.isValid(val), | ||
hash: (val) => typeof val === 'function' && Number.isSafeInteger(val.outputLen), | ||
}; | ||
// type Key = keyof typeof validators; | ||
const checkField = (fieldName, type, isOptional) => { | ||
@@ -142,9 +142,9 @@ const checkVal = validatorFns[type]; | ||
return; | ||
if (!checkVal(val)) { | ||
throw new Error(`Invalid param ${fieldName}=${val} (${typeof val}), expected ${type}`); | ||
if (!checkVal(val, object)) { | ||
throw new Error(`Invalid param ${String(fieldName)}=${val} (${typeof val}), expected ${type}`); | ||
} | ||
}; | ||
for (let [fieldName, type] of Object.entries(validators)) | ||
for (const [fieldName, type] of Object.entries(validators)) | ||
checkField(fieldName, type, false); | ||
for (let [fieldName, type] of Object.entries(optValidators)) | ||
for (const [fieldName, type] of Object.entries(optValidators)) | ||
checkField(fieldName, type, true); | ||
@@ -154,1 +154,9 @@ return object; | ||
exports.validateObject = validateObject; | ||
// validate type tests | ||
// const o: { a: number; b: number; c: number } = { a: 1, b: 5, c: 6 }; | ||
// const z0 = validateObject(o, { a: 'isSafeInteger' }, { c: 'bigint' }); // Ok! | ||
// // Should fail type-check | ||
// const z1 = validateObject(o, { a: 'tmp' }, { c: 'zz' }); | ||
// const z2 = validateObject(o, { a: 'isSafeInteger' }, { c: 'zz' }); | ||
// const z3 = validateObject(o, { test: 'boolean', z: 'bug' }); | ||
// const z4 = validateObject(o, { a: 'boolean', z: 'bug' }); |
@@ -22,2 +22,3 @@ "use strict"; | ||
clearCofactor: 'function', | ||
allowInfinityPoint: 'boolean', | ||
}); | ||
@@ -161,2 +162,4 @@ const { endo, Fp, a } = opts; | ||
} | ||
// Does not validate if the point is on-curve. | ||
// Use fromHex instead, or call assertValidity() later. | ||
static fromAffine(p) { | ||
@@ -163,0 +166,0 @@ const { x, y } = p || {}; |
@@ -109,3 +109,26 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
} | ||
assertValidity() { } | ||
// Not required for fromHex(), which always creates valid points. | ||
// Could be useful for fromAffine(). | ||
assertValidity() { | ||
const { a, d } = CURVE; | ||
if (this.is0()) | ||
throw new Error('bad point: ZERO'); // TODO: optimize, with vars below? | ||
// Equation in affine coordinates: ax² + y² = 1 + dx²y² | ||
// Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y² | ||
const { ex: X, ey: Y, ez: Z, et: T } = this; | ||
const X2 = modP(X * X); // X² | ||
const Y2 = modP(Y * Y); // Y² | ||
const Z2 = modP(Z * Z); // Z² | ||
const Z4 = modP(Z2 * Z2); // Z⁴ | ||
const aX2 = modP(X2 * a); // aX² | ||
const left = modP(Z2 * modP(aX2 + Y2)); // (aX² + Y²)Z² | ||
const right = modP(Z4 + modP(d * modP(X2 * Y2))); // Z⁴ + dX²Y² | ||
if (left !== right) | ||
throw new Error('bad point: equation left != right (1)'); | ||
// In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T | ||
const XY = modP(X * Y); | ||
const ZT = modP(Z * T); | ||
if (XY !== ZT) | ||
throw new Error('bad point: equation left != right (2)'); | ||
} | ||
// Compare one point to another. | ||
@@ -112,0 +135,0 @@ equals(other) { |
@@ -121,3 +121,5 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
const u = ensureBytes(uEnc, montgomeryBytes); | ||
u[fieldLen - 1] &= 127; // 0b0111_1111 | ||
// u[fieldLen-1] crashes QuickJS (TypeError: out-of-bound numeric index) | ||
if (fieldLen === montgomeryBytes) | ||
u[fieldLen - 1] &= 127; // 0b0111_1111 | ||
return bytesToNumberLE(u); | ||
@@ -124,0 +126,0 @@ } |
@@ -25,3 +25,3 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
// Big Endian | ||
return BigInt(`0x${hex}`); | ||
return BigInt(hex === '' ? '0' : `0x${hex}`); | ||
} | ||
@@ -103,14 +103,14 @@ // Caching slows it down 2-3x | ||
export const bitMask = (n) => (_2n << BigInt(n - 1)) - _1n; | ||
const validatorFns = { | ||
bigint: (val) => typeof val === 'bigint', | ||
function: (val) => typeof val === 'function', | ||
boolean: (val) => typeof val === 'boolean', | ||
string: (val) => typeof val === 'string', | ||
isSafeInteger: (val) => Number.isSafeInteger(val), | ||
array: (val) => Array.isArray(val), | ||
field: (val, object) => object.Fp.isValid(val), | ||
hash: (val) => typeof val === 'function' && Number.isSafeInteger(val.outputLen), | ||
}; | ||
// type Record<K extends string | number | symbol, T> = { [P in K]: T; } | ||
export function validateObject(object, validators, optValidators = {}) { | ||
const validatorFns = { | ||
bigint: (val) => typeof val === 'bigint', | ||
function: (val) => typeof val === 'function', | ||
boolean: (val) => typeof val === 'boolean', | ||
string: (val) => typeof val === 'string', | ||
isSafeInteger: (val) => Number.isSafeInteger(val), | ||
array: (val) => Array.isArray(val), | ||
field: (val) => object.Fp.isValid(val), | ||
hash: (val) => typeof val === 'function' && Number.isSafeInteger(val.outputLen), | ||
}; | ||
// type Key = keyof typeof validators; | ||
const checkField = (fieldName, type, isOptional) => { | ||
@@ -123,11 +123,19 @@ const checkVal = validatorFns[type]; | ||
return; | ||
if (!checkVal(val)) { | ||
throw new Error(`Invalid param ${fieldName}=${val} (${typeof val}), expected ${type}`); | ||
if (!checkVal(val, object)) { | ||
throw new Error(`Invalid param ${String(fieldName)}=${val} (${typeof val}), expected ${type}`); | ||
} | ||
}; | ||
for (let [fieldName, type] of Object.entries(validators)) | ||
for (const [fieldName, type] of Object.entries(validators)) | ||
checkField(fieldName, type, false); | ||
for (let [fieldName, type] of Object.entries(optValidators)) | ||
for (const [fieldName, type] of Object.entries(optValidators)) | ||
checkField(fieldName, type, true); | ||
return object; | ||
} | ||
// validate type tests | ||
// const o: { a: number; b: number; c: number } = { a: 1, b: 5, c: 6 }; | ||
// const z0 = validateObject(o, { a: 'isSafeInteger' }, { c: 'bigint' }); // Ok! | ||
// // Should fail type-check | ||
// const z1 = validateObject(o, { a: 'tmp' }, { c: 'zz' }); | ||
// const z2 = validateObject(o, { a: 'isSafeInteger' }, { c: 'zz' }); | ||
// const z3 = validateObject(o, { test: 'boolean', z: 'bug' }); | ||
// const z4 = validateObject(o, { a: 'boolean', z: 'bug' }); |
@@ -19,2 +19,3 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
clearCofactor: 'function', | ||
allowInfinityPoint: 'boolean', | ||
}); | ||
@@ -158,2 +159,4 @@ const { endo, Fp, a } = opts; | ||
} | ||
// Does not validate if the point is on-curve. | ||
// Use fromHex instead, or call assertValidity() later. | ||
static fromAffine(p) { | ||
@@ -160,0 +163,0 @@ const { x, y } = p || {}; |
{ | ||
"name": "@noble/curves", | ||
"version": "0.6.1", | ||
"version": "0.6.2", | ||
"description": "Minimal, auditable JS implementation of elliptic curve cryptography", | ||
@@ -5,0 +5,0 @@ "files": [ |
@@ -332,43 +332,50 @@ # noble-curves | ||
```ts | ||
function expand_message_xmd( | ||
msg: Uint8Array, DST: Uint8Array, lenInBytes: number, H: CHash | ||
): Uint8Array; | ||
function expand_message_xof( | ||
msg: Uint8Array, DST: Uint8Array, lenInBytes: number, k: number, H: CHash | ||
): Uint8Array; | ||
``` | ||
```ts | ||
function expand_message_xmd( | ||
msg: Uint8Array, | ||
DST: Uint8Array, | ||
lenInBytes: number, | ||
H: CHash | ||
): Uint8Array; | ||
function expand_message_xof( | ||
msg: Uint8Array, | ||
DST: Uint8Array, | ||
lenInBytes: number, | ||
k: number, | ||
H: CHash | ||
): Uint8Array; | ||
``` | ||
- `hash_to_field(msg, count, options)` [(spec)](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3) | ||
hashes arbitrary-length byte strings to a list of one or more elements of a finite field F. | ||
* `msg` a byte string containing the message to hash | ||
* `count` the number of elements of F to output | ||
* `options` `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}` | ||
* Returns `[u_0, ..., u_(count - 1)]`, a list of field elements. | ||
hashes arbitrary-length byte strings to a list of one or more elements of a finite field F. | ||
_ `msg` a byte string containing the message to hash | ||
_ `count` the number of elements of F to output | ||
_ `options` `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}` | ||
_ Returns `[u_0, ..., u_(count - 1)]`, a list of field elements. | ||
```ts | ||
function hash_to_field(msg: Uint8Array, count: number, options: htfOpts): bigint[][]; | ||
type htfOpts = { | ||
// DST: a domain separation tag | ||
// defined in section 2.2.5 | ||
DST: string; | ||
// p: the characteristic of F | ||
// where F is a finite field of characteristic p and order q = p^m | ||
p: bigint; | ||
// m: the extension degree of F, m >= 1 | ||
// where F is a finite field of characteristic p and order q = p^m | ||
m: number; | ||
// k: the target security level for the suite in bits | ||
// defined in section 5.1 | ||
k: number; | ||
// option to use a message that has already been processed by | ||
// expand_message_xmd | ||
expand?: 'xmd' | 'xof'; | ||
// Hash functions for: expand_message_xmd is appropriate for use with a | ||
// wide range of hash functions, including SHA-2, SHA-3, BLAKE2, and others. | ||
// BBS+ uses blake2: https://github.com/hyperledger/aries-framework-go/issues/2247 | ||
// TODO: verify that hash is shake if expand==='xof' via types | ||
hash: CHash; | ||
}; | ||
``` | ||
```ts | ||
function hash_to_field(msg: Uint8Array, count: number, options: htfOpts): bigint[][]; | ||
type htfOpts = { | ||
// DST: a domain separation tag | ||
// defined in section 2.2.5 | ||
DST: string; | ||
// p: the characteristic of F | ||
// where F is a finite field of characteristic p and order q = p^m | ||
p: bigint; | ||
// m: the extension degree of F, m >= 1 | ||
// where F is a finite field of characteristic p and order q = p^m | ||
m: number; | ||
// k: the target security level for the suite in bits | ||
// defined in section 5.1 | ||
k: number; | ||
// option to use a message that has already been processed by | ||
// expand_message_xmd | ||
expand?: 'xmd' | 'xof'; | ||
// Hash functions for: expand_message_xmd is appropriate for use with a | ||
// wide range of hash functions, including SHA-2, SHA-3, BLAKE2, and others. | ||
// BBS+ uses blake2: https://github.com/hyperledger/aries-framework-go/issues/2247 | ||
// TODO: verify that hash is shake if expand==='xof' via types | ||
hash: CHash; | ||
}; | ||
``` | ||
@@ -520,7 +527,7 @@ ### abstract/poseidon: Poseidon hash | ||
- `sign()` | ||
- `der`, `recovered` options were removed | ||
- `canonical` was renamed to `lowS` | ||
- Return type is now `{ r: bigint, s: bigint, recovery: number }` instance of `Signature` | ||
- `der`, `recovered` options were removed | ||
- `canonical` was renamed to `lowS` | ||
- Return type is now `{ r: bigint, s: bigint, recovery: number }` instance of `Signature` | ||
- `verify()` | ||
- `strict` was renamed to `lowS` | ||
- `strict` was renamed to `lowS` | ||
- `recoverPublicKey()`: moved to sig instance `Signature#recoverPublicKey(msgHash)` | ||
@@ -537,2 +544,3 @@ - `Point` was removed: use `ProjectivePoint` in xyz coordinates | ||
- `getSharedSecret` was removed: use separate x25519 sub-module | ||
- `bigint` is no longer allowed in `getPublicKey`, `sign`, `verify`. Reason: ed25519 is LE, can lead to bugs | ||
@@ -539,0 +547,0 @@ ## Contributing & testing |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
586659
12303
558