modular, weierstrass, bls: use new mapHashToField

This commit is contained in:
Paul Miller 2023-08-18 21:08:46 +00:00
parent 2ce3b825f8
commit 1545230ee5
No known key found for this signature in database
GPG Key ID: 697079DA6878B89B
4 changed files with 43 additions and 27 deletions

@ -13,9 +13,6 @@ Audited & minimal JS implementation of elliptic curve cryptography.
for encoding or hashing an arbitrary string to an elliptic curve point
- 🧜‍♂️ 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
> **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).
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)
- [ECDSA signature scheme](#ecdsa-signature-scheme)
- [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.
*/
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 * as htf from './hash-to-curve.js';
import {
@ -189,10 +189,8 @@ export function bls<Fp2, Fp6, Fp12>(
const utils = {
randomPrivateKey: (): Uint8Array => {
const bytesTaken = groupLen + Math.ceil(groupLen / 2); // e.g. 48b for 32b field
const rand = CURVE.randomBytes(bytesTaken);
const num = hashToPrivateScalar(rand, Fr.ORDER);
return Fr.toBytes(num);
const length = getMinHashLength(Fr.ORDER);
return mapHashToField(CURVE.randomBytes(length), Fr.ORDER);
},
calcPairingPrecomputes,
};

@ -409,16 +409,9 @@ export function FpSqrtEven<T>(Fp: IField<T>, elm: T) {
/**
* "Constant-time" private key generation utility.
* Can take (n + 8) 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 40 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
* Same as mapKeyToField, but accepts less bytes (40 instead of 48 for 32-byte field).
* Which makes it slightly more biased, less secure.
* @deprecated use mapKeyToField instead
*/
export function hashToPrivateScalar(
hash: string | Uint8Array,
@ -428,11 +421,43 @@ export function hashToPrivateScalar(
hash = ensureBytes('privateHash', hash);
const hashLen = hash.length;
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)
throw new Error(`expected ${minLen}-1024 bytes of input, got ${hashLen}`);
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;
}
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.
*/
randomPrivateKey: (): Uint8Array => {
const groupLen = CURVE.nByteLength;
const bytesTaken = groupLen + Math.ceil(groupLen / 2); // e.g. 48b for 32b field
const rand = CURVE.randomBytes(bytesTaken);
const num = mod.hashToPrivateScalar(rand, CURVE_ORDER);
return ut.numberToBytesBE(num, groupLen);
const length = mod.getMinHashLength(CURVE.n);
return mod.mapHashToField(CURVE.randomBytes(length), CURVE.n);
},
/**