| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 | import { mod } from './modular.js';import { abytes, bytesToNumberBE, concatBytes, utf8ToBytes, validateObject } from './utils.js';// Octet Stream to Integer. "spec" implementation of os2ip is 2.5x slower vs bytesToNumberBE.const os2ip = bytesToNumberBE;// Integer to Octet Stream (numberToBytesBE)function i2osp(value, length) {    if (value < 0 || value >= 1 << (8 * length)) {        throw new Error(`bad I2OSP call: value=${value} length=${length}`);    }    const res = Array.from({ length }).fill(0);    for (let i = length - 1; i >= 0; i--) {        res[i] = value & 0xff;        value >>>= 8;    }    return new Uint8Array(res);}function strxor(a, b) {    const arr = new Uint8Array(a.length);    for (let i = 0; i < a.length; i++) {        arr[i] = a[i] ^ b[i];    }    return arr;}function anum(item) {    if (!Number.isSafeInteger(item))        throw new Error('number expected');}// Produces a uniformly random byte string using a cryptographic hash function H that outputs b bits// https://www.rfc-editor.org/rfc/rfc9380#section-5.3.1export function expand_message_xmd(msg, DST, lenInBytes, H) {    abytes(msg);    abytes(DST);    anum(lenInBytes);    // 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);    if (ell > 255)        throw new Error('Invalid xmd length');    const DST_prime = concatBytes(DST, i2osp(DST.length, 1));    const Z_pad = i2osp(0, r_in_bytes);    const l_i_b_str = i2osp(lenInBytes, 2); // len_in_bytes_str    const b = new Array(ell);    const b_0 = H(concatBytes(Z_pad, msg, l_i_b_str, i2osp(0, 1), DST_prime));    b[0] = H(concatBytes(b_0, i2osp(1, 1), DST_prime));    for (let i = 1; i <= ell; i++) {        const args = [strxor(b_0, b[i - 1]), i2osp(i + 1, 1), DST_prime];        b[i] = H(concatBytes(...args));    }    const pseudo_random_bytes = concatBytes(...b);    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.2export function expand_message_xof(msg, DST, lenInBytes, k, H) {    abytes(msg);    abytes(DST);    anum(lenInBytes);    // 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);        DST = H.create({ dkLen }).update(utf8ToBytes('H2C-OVERSIZE-DST-')).update(DST).digest();    }    if (lenInBytes > 65535 || DST.length > 255)        throw new Error('expand_message_xof: invalid lenInBytes');    return (H.create({ dkLen: lenInBytes })        .update(msg)        .update(i2osp(lenInBytes, 2))        // 2. DST_prime = DST || I2OSP(len(DST), 1)        .update(DST)        .update(i2osp(DST.length, 1))        .digest());}/** * Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F * 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 * @returns [u_0, ..., u_(count - 1)], a list of field elements. */export function hash_to_field(msg, count, options) {    validateObject(options, {        DST: 'stringOrUint8Array',        p: 'bigint',        m: 'isSafeInteger',        k: 'isSafeInteger',        hash: 'hash',    });    const { p, k, m, hash, expand, DST: _DST } = options;    abytes(msg);    anum(count);    const DST = typeof _DST === 'string' ? utf8ToBytes(_DST) : _DST;    const log2p = p.toString(2).length;    const L = Math.ceil((log2p + k) / 8); // section 5.1 of ietf draft link above    const len_in_bytes = count * m * L;    let prb; // pseudo_random_bytes    if (expand === 'xmd') {        prb = expand_message_xmd(msg, DST, len_in_bytes, hash);    }    else if (expand === 'xof') {        prb = expand_message_xof(msg, DST, len_in_bytes, k, hash);    }    else if (expand === '_internal_pass') {        // for internal tests only        prb = msg;    }    else {        throw new Error('expand must be "xmd" or "xof"');    }    const u = new Array(count);    for (let i = 0; i < count; i++) {        const e = new Array(m);        for (let j = 0; j < m; j++) {            const elm_offset = L * (j + i * m);            const tv = prb.subarray(elm_offset, elm_offset + L);            e[j] = mod(os2ip(tv), p);        }        u[i] = e;    }    return u;}export function isogenyMap(field, map) {    // Make same order as in spec    const COEFF = map.map((i) => Array.from(i).reverse());    return (x, y) => {        const [xNum, xDen, yNum, yDen] = COEFF.map((val) => val.reduce((acc, i) => field.add(field.mul(acc, x), i)));        x = field.div(xNum, xDen); // xNum / xDen        y = field.mul(y, field.div(yNum, yDen)); // y * (yNum / yDev)        return { x, y };    };}export function createHasher(Point, mapToCurve, def) {    if (typeof mapToCurve !== 'function')        throw new Error('mapToCurve() must be defined');    return {        // Encodes byte string to elliptic curve.        // hash_to_curve from https://www.rfc-editor.org/rfc/rfc9380#section-3        hashToCurve(msg, options) {            const u = hash_to_field(msg, 2, { ...def, DST: def.DST, ...options });            const u0 = Point.fromAffine(mapToCurve(u[0]));            const u1 = Point.fromAffine(mapToCurve(u[1]));            const P = u0.add(u1).clearCofactor();            P.assertValidity();            return P;        },        // Encodes byte string to elliptic curve.        // encode_to_curve from https://www.rfc-editor.org/rfc/rfc9380#section-3        encodeToCurve(msg, options) {            const u = hash_to_field(msg, 1, { ...def, DST: def.encodeDST, ...options });            const P = Point.fromAffine(mapToCurve(u[0])).clearCofactor();            P.assertValidity();            return P;        },        // Same as encodeToCurve, but without hash        mapToCurve(scalars) {            if (!Array.isArray(scalars))                throw new Error('mapToCurve: expected array of bigints');            for (const i of scalars)                if (typeof i !== 'bigint')                    throw new Error(`mapToCurve: expected array of bigints, got ${i} in array`);            const P = Point.fromAffine(mapToCurve(scalars)).clearCofactor();            P.assertValidity();            return P;        },    };}//# sourceMappingURL=hash-to-curve.js.map
 |