| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155 | /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */import { mod, pow } from './modular.js';import { bytesToNumberLE, ensureBytes, numberToBytesLE, validateObject } from './utils.js';const _0n = BigInt(0);const _1n = BigInt(1);function validateOpts(curve) {    validateObject(curve, {        a: 'bigint',    }, {        montgomeryBits: 'isSafeInteger',        nByteLength: 'isSafeInteger',        adjustScalarBytes: 'function',        domain: 'function',        powPminus2: 'function',        Gu: 'bigint',    });    // Set defaults    return Object.freeze({ ...curve });}// NOTE: not really montgomery curve, just bunch of very specific methods for X25519/X448 (RFC 7748, https://www.rfc-editor.org/rfc/rfc7748)// Uses only one coordinate instead of twoexport function montgomery(curveDef) {    const CURVE = validateOpts(curveDef);    const { P } = CURVE;    const modP = (n) => mod(n, P);    const montgomeryBits = CURVE.montgomeryBits;    const montgomeryBytes = Math.ceil(montgomeryBits / 8);    const fieldLen = CURVE.nByteLength;    const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes) => bytes);    const powPminus2 = CURVE.powPminus2 || ((x) => pow(x, P - BigInt(2), P));    // cswap from RFC7748. But it is not from RFC7748!    /*      cswap(swap, x_2, x_3):           dummy = mask(swap) AND (x_2 XOR x_3)           x_2 = x_2 XOR dummy           x_3 = x_3 XOR dummy           Return (x_2, x_3)    Where mask(swap) is the all-1 or all-0 word of the same length as x_2     and x_3, computed, e.g., as mask(swap) = 0 - swap.    */    function cswap(swap, x_2, x_3) {        const dummy = modP(swap * (x_2 - x_3));        x_2 = modP(x_2 - dummy);        x_3 = modP(x_3 + dummy);        return [x_2, x_3];    }    // Accepts 0 as well    function assertFieldElement(n) {        if (typeof n === 'bigint' && _0n <= n && n < P)            return n;        throw new Error('Expected valid scalar 0 < scalar < CURVE.P');    }    // x25519 from 4    // The constant a24 is (486662 - 2) / 4 = 121665 for curve25519/X25519    const a24 = (CURVE.a - BigInt(2)) / BigInt(4);    /**     *     * @param pointU u coordinate (x) on Montgomery Curve 25519     * @param scalar by which the point would be multiplied     * @returns new Point on Montgomery curve     */    function montgomeryLadder(pointU, scalar) {        const u = assertFieldElement(pointU);        // Section 5: Implementations MUST accept non-canonical values and process them as        // if they had been reduced modulo the field prime.        const k = assertFieldElement(scalar);        const x_1 = u;        let x_2 = _1n;        let z_2 = _0n;        let x_3 = u;        let z_3 = _1n;        let swap = _0n;        let sw;        for (let t = BigInt(montgomeryBits - 1); t >= _0n; t--) {            const k_t = (k >> t) & _1n;            swap ^= k_t;            sw = cswap(swap, x_2, x_3);            x_2 = sw[0];            x_3 = sw[1];            sw = cswap(swap, z_2, z_3);            z_2 = sw[0];            z_3 = sw[1];            swap = k_t;            const A = x_2 + z_2;            const AA = modP(A * A);            const B = x_2 - z_2;            const BB = modP(B * B);            const E = AA - BB;            const C = x_3 + z_3;            const D = x_3 - z_3;            const DA = modP(D * A);            const CB = modP(C * B);            const dacb = DA + CB;            const da_cb = DA - CB;            x_3 = modP(dacb * dacb);            z_3 = modP(x_1 * modP(da_cb * da_cb));            x_2 = modP(AA * BB);            z_2 = modP(E * (AA + modP(a24 * E)));        }        // (x_2, x_3) = cswap(swap, x_2, x_3)        sw = cswap(swap, x_2, x_3);        x_2 = sw[0];        x_3 = sw[1];        // (z_2, z_3) = cswap(swap, z_2, z_3)        sw = cswap(swap, z_2, z_3);        z_2 = sw[0];        z_3 = sw[1];        // z_2^(p - 2)        const z2 = powPminus2(z_2);        // Return x_2 * (z_2^(p - 2))        return modP(x_2 * z2);    }    function encodeUCoordinate(u) {        return numberToBytesLE(modP(u), montgomeryBytes);    }    function decodeUCoordinate(uEnc) {        // Section 5: When receiving such an array, implementations of X25519        // MUST mask the most significant bit in the final byte.        const u = ensureBytes('u coordinate', uEnc, montgomeryBytes);        if (fieldLen === 32)            u[31] &= 127; // 0b0111_1111        return bytesToNumberLE(u);    }    function decodeScalar(n) {        const bytes = ensureBytes('scalar', n);        const len = bytes.length;        if (len !== montgomeryBytes && len !== fieldLen)            throw new Error(`Expected ${montgomeryBytes} or ${fieldLen} bytes, got ${len}`);        return bytesToNumberLE(adjustScalarBytes(bytes));    }    function scalarMult(scalar, u) {        const pointU = decodeUCoordinate(u);        const _scalar = decodeScalar(scalar);        const pu = montgomeryLadder(pointU, _scalar);        // The result was not contributory        // https://cr.yp.to/ecdh.html#validate        if (pu === _0n)            throw new Error('Invalid private or public key received');        return encodeUCoordinate(pu);    }    // Computes public key from private. By doing scalar multiplication of base point.    const GuBytes = encodeUCoordinate(CURVE.Gu);    function scalarMultBase(scalar) {        return scalarMult(scalar, GuBytes);    }    return {        scalarMult,        scalarMultBase,        getSharedSecret: (privateKey, publicKey) => scalarMult(privateKey, publicKey),        getPublicKey: (privateKey) => scalarMultBase(privateKey),        utils: { randomPrivateKey: () => CURVE.randomBytes(CURVE.nByteLength) },        GuBytes: GuBytes,    };}//# sourceMappingURL=montgomery.js.map
 |