modular, weierstrass, bls: use new mapHashToField
This commit is contained in:
parent
2ce3b825f8
commit
1545230ee5
@ -13,9 +13,6 @@ Audited & minimal JS implementation of elliptic curve cryptography.
|
|||||||
for encoding or hashing an arbitrary string to an elliptic curve point
|
for encoding or hashing an arbitrary string to an elliptic curve point
|
||||||
- 🧜♂️ Poseidon ZK-friendly hash
|
- 🧜♂️ Poseidon ZK-friendly hash
|
||||||
|
|
||||||
Check out [Upgrading](#upgrading) if you've previously used single-feature noble
|
|
||||||
packages. See [Resources](#resources) for articles and real-world software that uses curves.
|
|
||||||
|
|
||||||
### This library belongs to _noble_ crypto
|
### This library belongs to _noble_ crypto
|
||||||
|
|
||||||
> **noble-crypto** — high-security, easily auditable set of contained cryptographic libraries and tools.
|
> **noble-crypto** — high-security, easily auditable set of contained cryptographic libraries and tools.
|
||||||
@ -40,7 +37,6 @@ For [Deno](https://deno.land), ensure to use [npm specifier](https://deno.land/m
|
|||||||
For React Native, you may need a [polyfill for crypto.getRandomValues](https://github.com/LinusU/react-native-get-random-values).
|
For React Native, you may need a [polyfill for crypto.getRandomValues](https://github.com/LinusU/react-native-get-random-values).
|
||||||
If you don't like NPM, a standalone [noble-curves.js](https://github.com/paulmillr/noble-curves/releases) is also available.
|
If you don't like NPM, a standalone [noble-curves.js](https://github.com/paulmillr/noble-curves/releases) is also available.
|
||||||
|
|
||||||
- [Usage](#usage)
|
|
||||||
- [Implementations](#implementations)
|
- [Implementations](#implementations)
|
||||||
- [ECDSA signature scheme](#ecdsa-signature-scheme)
|
- [ECDSA signature scheme](#ecdsa-signature-scheme)
|
||||||
- [ECDSA public key recovery & extra entropy](#ecdsa-public-key-recovery--extra-entropy)
|
- [ECDSA public key recovery & extra entropy](#ecdsa-public-key-recovery--extra-entropy)
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
* Some projects may prefer to swap this relation, it is not supported for now.
|
* Some projects may prefer to swap this relation, it is not supported for now.
|
||||||
*/
|
*/
|
||||||
import { AffinePoint } from './curve.js';
|
import { AffinePoint } from './curve.js';
|
||||||
import { IField, hashToPrivateScalar } from './modular.js';
|
import { IField, getMinHashLength, mapHashToField } from './modular.js';
|
||||||
import { Hex, PrivKey, CHash, bitLen, bitGet, ensureBytes } from './utils.js';
|
import { Hex, PrivKey, CHash, bitLen, bitGet, ensureBytes } from './utils.js';
|
||||||
import * as htf from './hash-to-curve.js';
|
import * as htf from './hash-to-curve.js';
|
||||||
import {
|
import {
|
||||||
@ -189,10 +189,8 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
|
|
||||||
const utils = {
|
const utils = {
|
||||||
randomPrivateKey: (): Uint8Array => {
|
randomPrivateKey: (): Uint8Array => {
|
||||||
const bytesTaken = groupLen + Math.ceil(groupLen / 2); // e.g. 48b for 32b field
|
const length = getMinHashLength(Fr.ORDER);
|
||||||
const rand = CURVE.randomBytes(bytesTaken);
|
return mapHashToField(CURVE.randomBytes(length), Fr.ORDER);
|
||||||
const num = hashToPrivateScalar(rand, Fr.ORDER);
|
|
||||||
return Fr.toBytes(num);
|
|
||||||
},
|
},
|
||||||
calcPairingPrecomputes,
|
calcPairingPrecomputes,
|
||||||
};
|
};
|
||||||
|
@ -409,16 +409,9 @@ export function FpSqrtEven<T>(Fp: IField<T>, elm: T) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* "Constant-time" private key generation utility.
|
* "Constant-time" private key generation utility.
|
||||||
* Can take (n + 8) or more bytes of uniform input e.g. from CSPRNG or KDF
|
* Same as mapKeyToField, but accepts less bytes (40 instead of 48 for 32-byte field).
|
||||||
* and convert them into private scalar, with the modulo bias being negligible.
|
* Which makes it slightly more biased, less secure.
|
||||||
* Needs at least 40 bytes of input for 32-byte private key.
|
* @deprecated use mapKeyToField instead
|
||||||
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
|
|
||||||
* FIPS 186-5, A.2 https://csrc.nist.gov/publications/detail/fips/186/5/final
|
|
||||||
* hash-to-curve, https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-hashing-to-a-finite-field
|
|
||||||
* @param hash hash output from SHA3 or a similar function
|
|
||||||
* @param groupOrder size of subgroup - (e.g. secp256k1.CURVE.n)
|
|
||||||
* @param isLE interpret hash bytes as LE num
|
|
||||||
* @returns valid private scalar
|
|
||||||
*/
|
*/
|
||||||
export function hashToPrivateScalar(
|
export function hashToPrivateScalar(
|
||||||
hash: string | Uint8Array,
|
hash: string | Uint8Array,
|
||||||
@ -428,11 +421,43 @@ export function hashToPrivateScalar(
|
|||||||
hash = ensureBytes('privateHash', hash);
|
hash = ensureBytes('privateHash', hash);
|
||||||
const hashLen = hash.length;
|
const hashLen = hash.length;
|
||||||
const minLen = nLength(groupOrder).nByteLength + 8; // 8b (64 bits) gives 2^-64 bias
|
const minLen = nLength(groupOrder).nByteLength + 8; // 8b (64 bits) gives 2^-64 bias
|
||||||
// Small numbers aren't supported: need to understand their security / bias story first.
|
|
||||||
// Huge numbers aren't supported for security: it's easier to detect timings of their ops in JS.
|
|
||||||
if (minLen < 24 || hashLen < minLen || hashLen > 1024)
|
if (minLen < 24 || hashLen < minLen || hashLen > 1024)
|
||||||
throw new Error(`expected ${minLen}-1024 bytes of input, got ${hashLen}`);
|
throw new Error(`expected ${minLen}-1024 bytes of input, got ${hashLen}`);
|
||||||
const num = isLE ? bytesToNumberLE(hash) : bytesToNumberBE(hash);
|
const num = isLE ? bytesToNumberLE(hash) : bytesToNumberBE(hash);
|
||||||
// `mod(x, 11)` can sometimes produce 0. `mod(x, 10) + 1` is the same, but no 0
|
|
||||||
return mod(num, groupOrder - _1n) + _1n;
|
return mod(num, groupOrder - _1n) + _1n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getFieldBytesLength(fieldOrder: bigint): number {
|
||||||
|
return nLength(fieldOrder).nByteLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMinHashLength(fieldOrder: bigint): number {
|
||||||
|
const length = getFieldBytesLength(fieldOrder);
|
||||||
|
return length + Math.ceil(length / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "Constant-time" private key generation utility.
|
||||||
|
* Can take (n + n/2) or more bytes of uniform input e.g. from CSPRNG or KDF
|
||||||
|
* and convert them into private scalar, with the modulo bias being negligible.
|
||||||
|
* Needs at least 48 bytes of input for 32-byte private key.
|
||||||
|
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
|
||||||
|
* FIPS 186-5, A.2 https://csrc.nist.gov/publications/detail/fips/186/5/final
|
||||||
|
* hash-to-curve, https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-hashing-to-a-finite-field
|
||||||
|
* @param hash hash output from SHA3 or a similar function
|
||||||
|
* @param groupOrder size of subgroup - (e.g. secp256k1.CURVE.n)
|
||||||
|
* @param isLE interpret hash bytes as LE num
|
||||||
|
* @returns valid private scalar
|
||||||
|
*/
|
||||||
|
export function mapHashToField(key: Uint8Array, fieldOrder: bigint, isLE = false): Uint8Array {
|
||||||
|
const len = key.length;
|
||||||
|
const fieldLen = getFieldBytesLength(fieldOrder);
|
||||||
|
const minLen = getMinHashLength(fieldOrder);
|
||||||
|
// No small numbers: need to understand bias story. No huge numbers: easier to detect JS timings.
|
||||||
|
if (len < 16 || len < minLen || len > 1024)
|
||||||
|
throw new Error(`expected ${minLen}-1024 bytes of input, got ${len}`);
|
||||||
|
const num = isLE ? bytesToNumberBE(key) : bytesToNumberLE(key);
|
||||||
|
// `mod(x, 11)` can sometimes produce 0. `mod(x, 10) + 1` is the same, but no 0
|
||||||
|
const reduced = mod(num, fieldOrder - _1n) + _1n;
|
||||||
|
return isLE ? numberToBytesLE(reduced, fieldLen) : numberToBytesBE(reduced, fieldLen)
|
||||||
|
}
|
||||||
|
@ -849,11 +849,8 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
* (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
|
* (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
|
||||||
*/
|
*/
|
||||||
randomPrivateKey: (): Uint8Array => {
|
randomPrivateKey: (): Uint8Array => {
|
||||||
const groupLen = CURVE.nByteLength;
|
const length = mod.getMinHashLength(CURVE.n);
|
||||||
const bytesTaken = groupLen + Math.ceil(groupLen / 2); // e.g. 48b for 32b field
|
return mod.mapHashToField(CURVE.randomBytes(length), CURVE.n);
|
||||||
const rand = CURVE.randomBytes(bytesTaken);
|
|
||||||
const num = mod.hashToPrivateScalar(rand, CURVE_ORDER);
|
|
||||||
return ut.numberToBytesBE(num, groupLen);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user