hash-to-curve, weierstrass, bls, ed: upgrade h2c comments to rfc 9380

This commit is contained in:
Paul Miller 2023-08-23 17:43:14 +00:00
parent e7ac5e85d3
commit eb8e7ec964
No known key found for this signature in database
GPG Key ID: 697079DA6878B89B
6 changed files with 32 additions and 45 deletions

@ -9,8 +9,7 @@ Audited & minimal JS implementation of elliptic curve cryptography.
- ➰ Short Weierstrass, Edwards, Montgomery curves - ➰ Short Weierstrass, Edwards, Montgomery curves
- ✍️ ECDSA, EdDSA, Schnorr, BLS signature schemes, ECDH key agreement - ✍️ ECDSA, EdDSA, Schnorr, BLS signature schemes, ECDH key agreement
- 🔖 SUF-CMA and SBS (non-repudiation) for ed25519, ed448 and others - 🔖 SUF-CMA and SBS (non-repudiation) for ed25519, ed448 and others
- #⃣ Hash-to-curve - #⃣ hash-to-curve 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
### This library belongs to _noble_ crypto ### This library belongs to _noble_ crypto
@ -676,7 +675,7 @@ utils: {
### abstract/hash-to-curve: Hashing strings to curve points ### abstract/hash-to-curve: Hashing strings to curve points
The module allows to hash arbitrary strings to elliptic curve points. Implements [hash-to-curve v16](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16). The module allows to hash arbitrary strings to elliptic curve points. Implements [RFC 9380](https://www.rfc-editor.org/rfc/rfc9380).
Every curve has exported `hashToCurve` and `encodeToCurve` methods. You should always prefer `hashToCurve` for security: Every curve has exported `hashToCurve` and `encodeToCurve` methods. You should always prefer `hashToCurve` for security:
@ -692,19 +691,17 @@ bls12_381.G1.hashToCurve(randomBytes(), { DST: 'another' });
bls12_381.G2.hashToCurve(randomBytes(), { DST: 'custom' }); bls12_381.G2.hashToCurve(randomBytes(), { DST: 'custom' });
``` ```
If you need low-level methods from spec: Low-level methods from the spec:
`expand_message_xmd` [(spec)](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.4.1) produces a uniformly random byte string using a cryptographic hash function H that outputs b bits.
Hash must conform to `CHash` interface (see [weierstrass section](#abstractweierstrass-short-weierstrass-curve)).
```ts ```ts
// produces a uniformly random byte string using a cryptographic hash function H that outputs b bits.
function expand_message_xmd( function expand_message_xmd(
msg: Uint8Array, msg: Uint8Array,
DST: Uint8Array, DST: Uint8Array,
lenInBytes: number, lenInBytes: number,
H: CHash H: CHash // For CHash see abstract/weierstrass docs section
): Uint8Array; ): Uint8Array;
// produces a uniformly random byte string using an extendable-output function (XOF) H.
function expand_message_xof( function expand_message_xof(
msg: Uint8Array, msg: Uint8Array,
DST: Uint8Array, DST: Uint8Array,
@ -712,13 +709,9 @@ function expand_message_xof(
k: number, k: number,
H: CHash H: CHash
): Uint8Array; ): Uint8Array;
``` // Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F
function hash_to_field(msg: Uint8Array, count: number, options: Opts): bigint[][];
`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.
```ts
/** /**
* * `DST` is a domain separation tag, defined in section 2.2.5 * * `DST` is a domain separation tag, defined in section 2.2.5
* * `p` characteristic of F, where F is a finite field of characteristic p and order q = p^m * * `p` characteristic of F, where F is a finite field of characteristic p and order q = p^m
@ -736,16 +729,6 @@ type Opts = {
expand?: 'xmd' | 'xof'; expand?: 'xmd' | 'xof';
hash: CHash; hash: CHash;
}; };
/**
* Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
* @param msg a byte string containing the message to hash
* @param count the number of elements of F to output
* @param options `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`, see above
* @returns [u_0, ..., u_(count - 1)], a list of field elements.
*/
function hash_to_field(msg: Uint8Array, count: number, options: Opts): bigint[][];
``` ```
### abstract/poseidon: Poseidon hash ### abstract/poseidon: Poseidon hash

@ -59,7 +59,7 @@ function isNum(item: unknown): void {
} }
// Produces a uniformly random byte string using a cryptographic hash function H that outputs b bits // Produces a uniformly random byte string using a cryptographic hash function H that outputs b bits
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.4.1 // https://www.rfc-editor.org/rfc/rfc9380#section-5.3.1
export function expand_message_xmd( export function expand_message_xmd(
msg: Uint8Array, msg: Uint8Array,
DST: Uint8Array, DST: Uint8Array,
@ -69,7 +69,7 @@ export function expand_message_xmd(
isBytes(msg); isBytes(msg);
isBytes(DST); isBytes(DST);
isNum(lenInBytes); isNum(lenInBytes);
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-5.3.3 // https://www.rfc-editor.org/rfc/rfc9380#section-5.3.3
if (DST.length > 255) DST = H(concatBytes(utf8ToBytes('H2C-OVERSIZE-DST-'), DST)); if (DST.length > 255) DST = H(concatBytes(utf8ToBytes('H2C-OVERSIZE-DST-'), DST));
const { outputLen: b_in_bytes, blockLen: r_in_bytes } = H; const { outputLen: b_in_bytes, blockLen: r_in_bytes } = H;
const ell = Math.ceil(lenInBytes / b_in_bytes); const ell = Math.ceil(lenInBytes / b_in_bytes);
@ -88,6 +88,11 @@ export function expand_message_xmd(
return pseudo_random_bytes.slice(0, lenInBytes); return pseudo_random_bytes.slice(0, lenInBytes);
} }
// Produces a uniformly random byte string using an extendable-output function (XOF) H.
// 1. The collision resistance of H MUST be at least k bits.
// 2. H MUST be an XOF that has been proved indifferentiable from
// a random oracle under a reasonable cryptographic assumption.
// https://www.rfc-editor.org/rfc/rfc9380#section-5.3.2
export function expand_message_xof( export function expand_message_xof(
msg: Uint8Array, msg: Uint8Array,
DST: Uint8Array, DST: Uint8Array,
@ -98,7 +103,7 @@ export function expand_message_xof(
isBytes(msg); isBytes(msg);
isBytes(DST); isBytes(DST);
isNum(lenInBytes); isNum(lenInBytes);
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-5.3.3 // https://www.rfc-editor.org/rfc/rfc9380#section-5.3.3
// DST = H('H2C-OVERSIZE-DST-' || a_very_long_DST, Math.ceil((lenInBytes * k) / 8)); // DST = H('H2C-OVERSIZE-DST-' || a_very_long_DST, Math.ceil((lenInBytes * k) / 8));
if (DST.length > 255) { if (DST.length > 255) {
const dkLen = Math.ceil((2 * k) / 8); const dkLen = Math.ceil((2 * k) / 8);
@ -119,7 +124,7 @@ export function expand_message_xof(
/** /**
* Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F * Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3 * https://www.rfc-editor.org/rfc/rfc9380#section-5.2
* @param msg a byte string containing the message to hash * @param msg a byte string containing the message to hash
* @param count the number of elements of F to output * @param count the number of elements of F to output
* @param options `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`, see above * @param options `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`, see above
@ -201,8 +206,8 @@ export function createHasher<T>(
) { ) {
if (typeof mapToCurve !== 'function') throw new Error('mapToCurve() must be defined'); if (typeof mapToCurve !== 'function') throw new Error('mapToCurve() must be defined');
return { return {
// Encodes byte string to elliptic curve // Encodes byte string to elliptic curve.
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3 // hash_to_curve from https://www.rfc-editor.org/rfc/rfc9380#section-3
hashToCurve(msg: Uint8Array, options?: htfBasicOpts) { hashToCurve(msg: Uint8Array, options?: htfBasicOpts) {
const u = hash_to_field(msg, 2, { ...def, DST: def.DST, ...options } as Opts); const u = hash_to_field(msg, 2, { ...def, DST: def.DST, ...options } as Opts);
const u0 = Point.fromAffine(mapToCurve(u[0])); const u0 = Point.fromAffine(mapToCurve(u[0]));
@ -212,7 +217,8 @@ export function createHasher<T>(
return P; return P;
}, },
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3 // Encodes byte string to elliptic curve.
// encode_to_curve from https://www.rfc-editor.org/rfc/rfc9380#section-3
encodeToCurve(msg: Uint8Array, options?: htfBasicOpts) { encodeToCurve(msg: Uint8Array, options?: htfBasicOpts) {
const u = hash_to_field(msg, 1, { ...def, DST: def.encodeDST, ...options } as Opts); const u = hash_to_field(msg, 1, { ...def, DST: def.encodeDST, ...options } as Opts);
const P = Point.fromAffine(mapToCurve(u[0])).clearCofactor(); const P = Point.fromAffine(mapToCurve(u[0])).clearCofactor();

@ -1171,7 +1171,8 @@ export function SWUFpSqrtRatio<T>(Fp: mod.IField<T>, Z: T) {
return sqrtRatio; return sqrtRatio;
} }
/** /**
* From draft-irtf-cfrg-hash-to-curve-16 * Simplified Shallue-van de Woestijne-Ulas Method
* https://www.rfc-editor.org/rfc/rfc9380#section-6.6.2
*/ */
export function mapToCurveSimpleSWU<T>( export function mapToCurveSimpleSWU<T>(
Fp: mod.IField<T>, Fp: mod.IField<T>,

@ -8,8 +8,8 @@
// The library uses G1 for public keys and G2 for signatures. Support for G1 signatures is planned. // The library uses G1 for public keys and G2 for signatures. Support for G1 signatures is planned.
// Compatible with Algorand, Chia, Dfinity, Ethereum, FIL, Zcash. Matches specs // Compatible with Algorand, Chia, Dfinity, Ethereum, FIL, Zcash. Matches specs
// [pairing-curves-11](https://tools.ietf.org/html/draft-irtf-cfrg-pairing-friendly-curves-11), // [pairing-curves-11](https://tools.ietf.org/html/draft-irtf-cfrg-pairing-friendly-curves-11),
// [bls-sigs-04](https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04), // [bls-sigs-04](https:/cfrg-hash-to/tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04),
// [hash-to-curve-12](https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-12). // [hash-to-curve-12](https://tools.ietf.org/html/draft-irtf--curve-12).
// //
// ### Summary // ### Summary
// 1. BLS Relies on Bilinear Pairing (expensive) // 1. BLS Relies on Bilinear Pairing (expensive)
@ -177,7 +177,7 @@ const Fp2: mod.IField<Fp2> & Fp2Utils = {
if (im1 > im2 || (im1 === im2 && re1 > re2)) return x1; if (im1 > im2 || (im1 === im2 && re1 > re2)) return x1;
return x2; return x2;
}, },
// Same as sgn0_fp2 in draft-irtf-cfrg-hash-to-curve-16 // Same as sgn0_m_eq_2 in RFC 9380
isOdd: (x: Fp2) => { isOdd: (x: Fp2) => {
const { re: x0, im: x1 } = Fp2.reim(x); const { re: x0, im: x1 } = Fp2.reim(x);
const sign_0 = x0 % _2n; const sign_0 = x0 % _2n;
@ -780,8 +780,7 @@ const FP12_FROBENIUS_COEFFICIENTS = [
// HashToCurve // HashToCurve
// 3-isogeny map from E' to E // 3-isogeny map from E' to E https://www.rfc-editor.org/rfc/rfc9380#appendix-E.3
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-E.3
const isogenyMapG2 = isogenyMap( const isogenyMapG2 = isogenyMap(
Fp2, Fp2,
[ [
@ -985,7 +984,7 @@ function G2psi2(c: ProjConstructor<Fp2>, P: ProjPointType<Fp2>) {
// //
// Parameter definitions are in section 5.3 of the spec unless otherwise noted. // Parameter definitions are in section 5.3 of the spec unless otherwise noted.
// Parameter values come from section 8.8.2 of the spec. // Parameter values come from section 8.8.2 of the spec.
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-8.8.2 // https://www.rfc-editor.org/rfc/rfc9380#section-8.8.2
// //
// Base field F is GF(p^m) // Base field F is GF(p^m)
// p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab // p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
@ -1040,7 +1039,7 @@ function signatureG2ToRawBytes(point: ProjPointType<Fp2>) {
} }
// To verify curve parameters, see pairing-friendly-curves spec: // To verify curve parameters, see pairing-friendly-curves spec:
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-09 // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-11
// Basic math is done over finite fields over p. // Basic math is done over finite fields over p.
// More complicated math is done over polynominal extension fields. // More complicated math is done over polynominal extension fields.
// To simplify calculations in Fp12, we construct extension tower: // To simplify calculations in Fp12, we construct extension tower:

@ -478,8 +478,7 @@ export const RistrettoPoint = /* @__PURE__ */ (() => {
return RistPoint; return RistPoint;
})(); })();
// https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/14/ // Hashing to ristretto255. https://www.rfc-editor.org/rfc/rfc9380#appendix-B
// Appendix B. Hashing to ristretto255
export const hashToRistretto255 = (msg: Uint8Array, options: htfBasicOpts) => { export const hashToRistretto255 = (msg: Uint8Array, options: htfBasicOpts) => {
const d = options.DST; const d = options.DST;
const DST = typeof d === 'string' ? utf8ToBytes(d) : d; const DST = typeof d === 'string' ? utf8ToBytes(d) : d;

@ -69,7 +69,7 @@ function adjustScalarBytes(bytes: Uint8Array): Uint8Array {
// Uses algo from RFC8032 5.1.3. // Uses algo from RFC8032 5.1.3.
function uvRatio(u: bigint, v: bigint): { isValid: boolean; value: bigint } { function uvRatio(u: bigint, v: bigint): { isValid: boolean; value: bigint } {
const P = ed448P; const P = ed448P;
// https://datatracker.ietf.org/doc/html/rfc8032#section-5.2.3 // https://www.rfc-editor.org/rfc/rfc8032#section-5.2.3
// To compute the square root of (u/v), the first step is to compute the // To compute the square root of (u/v), the first step is to compute the
// candidate root x = (u/v)^((p+1)/4). This can be done using the // candidate root x = (u/v)^((p+1)/4). This can be done using the
// following trick, to use a single modular powering for both the // following trick, to use a single modular powering for both the
@ -454,8 +454,7 @@ export const DecafPoint = /* @__PURE__ */ (() => {
return DcfPoint; return DcfPoint;
})(); })();
// https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/16/ // Hashing to decaf448. https://www.rfc-editor.org/rfc/rfc9380#appendix-C
// Appendix C. Hashing to decaf448
export const hashToDecaf448 = (msg: Uint8Array, options: htfBasicOpts) => { export const hashToDecaf448 = (msg: Uint8Array, options: htfBasicOpts) => {
const d = options.DST; const d = options.DST;
const DST = typeof d === 'string' ? utf8ToBytes(d) : d; const DST = typeof d === 'string' ? utf8ToBytes(d) : d;