diff --git a/README.md b/README.md index 2c21f33..0f1489a 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Audited & minimal JS implementation of elliptic curve cryptography. - **noble** family, zero dependencies - Short Weierstrass, Edwards, Montgomery curves - ECDSA, EdDSA, Schnorr, BLS signature schemes, ECDH key agreement -- #️⃣ [hash to curve](https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/) +- #️⃣ [hash to curve](#abstracthash-to-curve-hashing-strings-to-curve-points) for encoding or hashing an arbitrary string to an elliptic curve point - 🧜‍♂️ [Poseidon](https://www.poseidon-hash.info) ZK-friendly hash - 🏎 [Ultra-fast](#speed), hand-optimized for caveats of JS engines @@ -470,7 +470,7 @@ const x25519 = montgomery({ ### abstract/hash-to-curve: Hashing strings to curve points -The module allows to hash arbitrary strings to elliptic curve points. Implements [hash-to-curve v11](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11). +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). Every curve has exported `hashToCurve` and `encodeToCurve` methods: @@ -481,7 +481,6 @@ hashToCurve('0102abcd'); console.log(hashToCurve(randomBytes())); console.log(encodeToCurve(randomBytes())); - import { bls12_381 } from '@noble/curves/bls12-381'; bls12_381.G1.hashToCurve(randomBytes(), { DST: 'another' }); bls12_381.G2.hashToCurve(randomBytes(), { DST: 'custom' }); @@ -491,6 +490,8 @@ 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)). + ```ts function expand_message_xmd( msg: Uint8Array, @@ -509,13 +510,18 @@ function expand_message_xof( `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. + +- `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}`. + - `p` is field prime, m=field extension (1 for prime fields) + - `k` is security target in bits (e.g. 128). + - `expand` should be `xmd` for SHA2, SHA3, BLAKE; `xof` for SHAKE, BLAKE-XOF + - `hash` conforming to `utils.CHash` interface, with `outputLen` / `blockLen` props +- Returns `[u_0, ..., u_(count - 1)]`, a list of field elements. ```ts -function hash_to_field(msg: Uint8Array, count: number, options: htfOpts): bigint[][]; +function hash_to_field(msg: Uint8Array, count: number, options: Opts): bigint[][]; ``` ### abstract/poseidon: Poseidon hash @@ -586,7 +592,6 @@ const derived = hkdf(sha256, someKey, undefined, 'application', 40); // 40 bytes const validPrivateKey = mod.hashToPrivateScalar(derived, p256.CURVE.n); ``` - ### abstract/utils: General utilities ```ts diff --git a/src/abstract/hash-to-curve.ts b/src/abstract/hash-to-curve.ts index 5bdf20d..6befe77 100644 --- a/src/abstract/hash-to-curve.ts +++ b/src/abstract/hash-to-curve.ts @@ -3,16 +3,20 @@ import type { Group, GroupConstructor, AffinePoint } from './curve.js'; import { mod, Field } from './modular.js'; import { bytesToNumberBE, CHash, concatBytes, utf8ToBytes, validateObject } from './utils.js'; +/** + * * `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 + * * `m` is extension degree (1 for prime fields) + * * `k` is the target security target in bits (e.g. 128), from section 5.1 + * * `expand` is `xmd` (SHA2, SHA3, BLAKE) or `xof` (SHAKE, BLAKE-XOF) + * * `hash` conforming to `utils.CHash` interface, with `outputLen` / `blockLen` props + */ export type Opts = { - DST: string | Uint8Array; // DST: a domain separation tag, defined in section 2.2.5 - p: bigint; // characteristic of F, where F is a finite field of characteristic p and order q = p^m - m: number; // extension degree of F, m >= 1 - k: number; // k: the target security level for the suite in bits, defined in section 5.1 - expand?: 'xmd' | 'xof'; // use a message that has already been processed by expand_message_xmd - // 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 + DST: string | Uint8Array; + p: bigint; + m: number; + k: number; + expand?: 'xmd' | 'xof'; hash: CHash; }; @@ -115,11 +119,6 @@ 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 - * As for options: - * * `p` is field prime, m=field extension (1 for prime fields) - * * `k` is security target in bits (e.g. 128). - * * `expand` should be `xmd` for SHA2, SHA3, BLAKE; `xof` for SHAKE, BLAKE-XOF - * * `hash` conforming to `utils.CHash` interface, with `outputLen` / `blockLen` props * @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