From eb8e7ec964de09c9d58c806296892b072c2c337c Mon Sep 17 00:00:00 2001 From: Paul Miller Date: Wed, 23 Aug 2023 17:43:14 +0000 Subject: [PATCH] hash-to-curve, weierstrass, bls, ed: upgrade h2c comments to rfc 9380 --- README.md | 33 ++++++++------------------------- src/abstract/hash-to-curve.ts | 20 +++++++++++++------- src/abstract/weierstrass.ts | 3 ++- src/bls12-381.ts | 13 ++++++------- src/ed25519.ts | 3 +-- src/ed448.ts | 5 ++--- 6 files changed, 32 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 9bdadcf..3bc793d 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,7 @@ Audited & minimal JS implementation of elliptic curve cryptography. - ➰ Short Weierstrass, Edwards, Montgomery curves - ✍️ ECDSA, EdDSA, Schnorr, BLS signature schemes, ECDH key agreement - 🔖 SUF-CMA and SBS (non-repudiation) for ed25519, ed448 and others -- #️⃣ Hash-to-curve - for encoding or hashing an arbitrary string to an elliptic curve point +- #️⃣ hash-to-curve for encoding or hashing an arbitrary string to an elliptic curve point - 🧜‍♂️ Poseidon ZK-friendly hash ### This library belongs to _noble_ crypto @@ -676,7 +675,7 @@ utils: { ### 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: @@ -692,19 +691,17 @@ bls12_381.G1.hashToCurve(randomBytes(), { DST: 'another' }); bls12_381.G2.hashToCurve(randomBytes(), { DST: 'custom' }); ``` -If you need low-level methods from 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)). +Low-level methods from the spec: ```ts +// produces a uniformly random byte string using a cryptographic hash function H that outputs b bits. function expand_message_xmd( msg: Uint8Array, DST: Uint8Array, lenInBytes: number, - H: CHash + H: CHash // For CHash see abstract/weierstrass docs section ): Uint8Array; +// produces a uniformly random byte string using an extendable-output function (XOF) H. function expand_message_xof( msg: Uint8Array, DST: Uint8Array, @@ -712,13 +709,9 @@ function expand_message_xof( k: number, H: CHash ): 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 * * `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'; 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 diff --git a/src/abstract/hash-to-curve.ts b/src/abstract/hash-to-curve.ts index 24d472b..64d7db4 100644 --- a/src/abstract/hash-to-curve.ts +++ b/src/abstract/hash-to-curve.ts @@ -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 -// 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( msg: Uint8Array, DST: Uint8Array, @@ -69,7 +69,7 @@ export function expand_message_xmd( isBytes(msg); isBytes(DST); 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)); const { outputLen: b_in_bytes, blockLen: r_in_bytes } = H; const ell = Math.ceil(lenInBytes / b_in_bytes); @@ -88,6 +88,11 @@ export function expand_message_xmd( 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( msg: Uint8Array, DST: Uint8Array, @@ -98,7 +103,7 @@ export function expand_message_xof( isBytes(msg); isBytes(DST); 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)); if (DST.length > 255) { 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 - * 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 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 @@ -201,8 +206,8 @@ export function createHasher( ) { if (typeof mapToCurve !== 'function') throw new Error('mapToCurve() must be defined'); return { - // Encodes byte string to elliptic curve - // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3 + // Encodes byte string to elliptic curve. + // hash_to_curve from https://www.rfc-editor.org/rfc/rfc9380#section-3 hashToCurve(msg: Uint8Array, options?: htfBasicOpts) { const u = hash_to_field(msg, 2, { ...def, DST: def.DST, ...options } as Opts); const u0 = Point.fromAffine(mapToCurve(u[0])); @@ -212,7 +217,8 @@ export function createHasher( 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) { const u = hash_to_field(msg, 1, { ...def, DST: def.encodeDST, ...options } as Opts); const P = Point.fromAffine(mapToCurve(u[0])).clearCofactor(); diff --git a/src/abstract/weierstrass.ts b/src/abstract/weierstrass.ts index 2f8cd24..82e7d42 100644 --- a/src/abstract/weierstrass.ts +++ b/src/abstract/weierstrass.ts @@ -1171,7 +1171,8 @@ export function SWUFpSqrtRatio(Fp: mod.IField, Z: T) { 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( Fp: mod.IField, diff --git a/src/bls12-381.ts b/src/bls12-381.ts index d4c7250..a98d72b 100644 --- a/src/bls12-381.ts +++ b/src/bls12-381.ts @@ -8,8 +8,8 @@ // 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 // [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), -// [hash-to-curve-12](https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-12). +// [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--curve-12). // // ### Summary // 1. BLS Relies on Bilinear Pairing (expensive) @@ -177,7 +177,7 @@ const Fp2: mod.IField & Fp2Utils = { if (im1 > im2 || (im1 === im2 && re1 > re2)) return x1; 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) => { const { re: x0, im: x1 } = Fp2.reim(x); const sign_0 = x0 % _2n; @@ -780,8 +780,7 @@ const FP12_FROBENIUS_COEFFICIENTS = [ // HashToCurve -// 3-isogeny map from E' to E -// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-E.3 +// 3-isogeny map from E' to E https://www.rfc-editor.org/rfc/rfc9380#appendix-E.3 const isogenyMapG2 = isogenyMap( Fp2, [ @@ -985,7 +984,7 @@ function G2psi2(c: ProjConstructor, P: ProjPointType) { // // Parameter definitions are in section 5.3 of the spec unless otherwise noted. // 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) // p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab @@ -1040,7 +1039,7 @@ function signatureG2ToRawBytes(point: ProjPointType) { } // 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. // More complicated math is done over polynominal extension fields. // To simplify calculations in Fp12, we construct extension tower: diff --git a/src/ed25519.ts b/src/ed25519.ts index 47809a3..896f4ee 100644 --- a/src/ed25519.ts +++ b/src/ed25519.ts @@ -478,8 +478,7 @@ export const RistrettoPoint = /* @__PURE__ */ (() => { return RistPoint; })(); -// https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/14/ -// Appendix B. Hashing to ristretto255 +// Hashing to ristretto255. https://www.rfc-editor.org/rfc/rfc9380#appendix-B export const hashToRistretto255 = (msg: Uint8Array, options: htfBasicOpts) => { const d = options.DST; const DST = typeof d === 'string' ? utf8ToBytes(d) : d; diff --git a/src/ed448.ts b/src/ed448.ts index 553de7b..0e5477a 100644 --- a/src/ed448.ts +++ b/src/ed448.ts @@ -69,7 +69,7 @@ function adjustScalarBytes(bytes: Uint8Array): Uint8Array { // Uses algo from RFC8032 5.1.3. function uvRatio(u: bigint, v: bigint): { isValid: boolean; value: bigint } { 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 // 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 @@ -454,8 +454,7 @@ export const DecafPoint = /* @__PURE__ */ (() => { return DcfPoint; })(); -// https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/16/ -// Appendix C. Hashing to decaf448 +// Hashing to decaf448. https://www.rfc-editor.org/rfc/rfc9380#appendix-C export const hashToDecaf448 = (msg: Uint8Array, options: htfBasicOpts) => { const d = options.DST; const DST = typeof d === 'string' ? utf8ToBytes(d) : d;