You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
160 lines
6.4 KiB
160 lines
6.4 KiB
"use strict"; |
|
Object.defineProperty(exports, "__esModule", { value: true }); |
|
exports.montgomery = montgomery; |
|
/** |
|
* Montgomery curve methods. It's not really whole montgomery curve, |
|
* just bunch of very specific methods for X25519 / X448 from |
|
* [RFC 7748](https://www.rfc-editor.org/rfc/rfc7748) |
|
* @module |
|
*/ |
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ |
|
const utils_ts_1 = require("../utils.js"); |
|
const modular_ts_1 = require("./modular.js"); |
|
const _0n = BigInt(0); |
|
const _1n = BigInt(1); |
|
const _2n = BigInt(2); |
|
function validateOpts(curve) { |
|
(0, utils_ts_1._validateObject)(curve, { |
|
adjustScalarBytes: 'function', |
|
powPminus2: 'function', |
|
}); |
|
return Object.freeze({ ...curve }); |
|
} |
|
function montgomery(curveDef) { |
|
const CURVE = validateOpts(curveDef); |
|
const { P, type, adjustScalarBytes, powPminus2, randomBytes: rand } = CURVE; |
|
const is25519 = type === 'x25519'; |
|
if (!is25519 && type !== 'x448') |
|
throw new Error('invalid type'); |
|
const randomBytes_ = rand || utils_ts_1.randomBytes; |
|
const montgomeryBits = is25519 ? 255 : 448; |
|
const fieldLen = is25519 ? 32 : 56; |
|
const Gu = is25519 ? BigInt(9) : BigInt(5); |
|
// RFC 7748 #5: |
|
// The constant a24 is (486662 - 2) / 4 = 121665 for curve25519/X25519 and |
|
// (156326 - 2) / 4 = 39081 for curve448/X448 |
|
// const a = is25519 ? 156326n : 486662n; |
|
const a24 = is25519 ? BigInt(121665) : BigInt(39081); |
|
// RFC: x25519 "the resulting integer is of the form 2^254 plus |
|
// eight times a value between 0 and 2^251 - 1 (inclusive)" |
|
// x448: "2^447 plus four times a value between 0 and 2^445 - 1 (inclusive)" |
|
const minScalar = is25519 ? _2n ** BigInt(254) : _2n ** BigInt(447); |
|
const maxAdded = is25519 |
|
? BigInt(8) * _2n ** BigInt(251) - _1n |
|
: BigInt(4) * _2n ** BigInt(445) - _1n; |
|
const maxScalar = minScalar + maxAdded + _1n; // (inclusive) |
|
const modP = (n) => (0, modular_ts_1.mod)(n, P); |
|
const GuBytes = encodeU(Gu); |
|
function encodeU(u) { |
|
return (0, utils_ts_1.numberToBytesLE)(modP(u), fieldLen); |
|
} |
|
function decodeU(u) { |
|
const _u = (0, utils_ts_1.ensureBytes)('u coordinate', u, fieldLen); |
|
// RFC: When receiving such an array, implementations of X25519 |
|
// (but not X448) MUST mask the most significant bit in the final byte. |
|
if (is25519) |
|
_u[31] &= 127; // 0b0111_1111 |
|
// RFC: Implementations MUST accept non-canonical values and process them as |
|
// if they had been reduced modulo the field prime. The non-canonical |
|
// values are 2^255 - 19 through 2^255 - 1 for X25519 and 2^448 - 2^224 |
|
// - 1 through 2^448 - 1 for X448. |
|
return modP((0, utils_ts_1.bytesToNumberLE)(_u)); |
|
} |
|
function decodeScalar(scalar) { |
|
return (0, utils_ts_1.bytesToNumberLE)(adjustScalarBytes((0, utils_ts_1.ensureBytes)('scalar', scalar, fieldLen))); |
|
} |
|
function scalarMult(scalar, u) { |
|
const pu = montgomeryLadder(decodeU(u), decodeScalar(scalar)); |
|
// Some public keys are useless, of low-order. Curve author doesn't think |
|
// it needs to be validated, but we do it nonetheless. |
|
// https://cr.yp.to/ecdh.html#validate |
|
if (pu === _0n) |
|
throw new Error('invalid private or public key received'); |
|
return encodeU(pu); |
|
} |
|
// Computes public key from private. By doing scalar multiplication of base point. |
|
function scalarMultBase(scalar) { |
|
return scalarMult(scalar, GuBytes); |
|
} |
|
// cswap from RFC7748 "example code" |
|
function cswap(swap, x_2, x_3) { |
|
// dummy = mask(swap) AND (x_2 XOR 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. |
|
const dummy = modP(swap * (x_2 - x_3)); |
|
x_2 = modP(x_2 - dummy); // x_2 = x_2 XOR dummy |
|
x_3 = modP(x_3 + dummy); // x_3 = x_3 XOR dummy |
|
return { x_2, x_3 }; |
|
} |
|
/** |
|
* Montgomery x-only multiplication ladder. |
|
* @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(u, scalar) { |
|
(0, utils_ts_1.aInRange)('u', u, _0n, P); |
|
(0, utils_ts_1.aInRange)('scalar', scalar, minScalar, maxScalar); |
|
const k = scalar; |
|
const x_1 = u; |
|
let x_2 = _1n; |
|
let z_2 = _0n; |
|
let x_3 = u; |
|
let z_3 = _1n; |
|
let swap = _0n; |
|
for (let t = BigInt(montgomeryBits - 1); t >= _0n; t--) { |
|
const k_t = (k >> t) & _1n; |
|
swap ^= k_t; |
|
({ x_2, x_3 } = cswap(swap, x_2, x_3)); |
|
({ x_2: z_2, x_3: z_3 } = cswap(swap, z_2, z_3)); |
|
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)); |
|
({ x_2: z_2, x_3: z_3 } = cswap(swap, z_2, z_3)); |
|
const z2 = powPminus2(z_2); // `Fp.pow(x, P - _2n)` is much slower equivalent |
|
return modP(x_2 * z2); // Return x_2 * (z_2^(p - 2)) |
|
} |
|
const lengths = { |
|
secretKey: fieldLen, |
|
publicKey: fieldLen, |
|
seed: fieldLen, |
|
}; |
|
const randomSecretKey = (seed = randomBytes_(fieldLen)) => { |
|
(0, utils_ts_1.abytes)(seed, lengths.seed); |
|
return seed; |
|
}; |
|
function keygen(seed) { |
|
const secretKey = randomSecretKey(seed); |
|
return { secretKey, publicKey: scalarMultBase(secretKey) }; |
|
} |
|
const utils = { |
|
randomSecretKey, |
|
randomPrivateKey: randomSecretKey, |
|
}; |
|
return { |
|
keygen, |
|
getSharedSecret: (secretKey, publicKey) => scalarMult(secretKey, publicKey), |
|
getPublicKey: (secretKey) => scalarMultBase(secretKey), |
|
scalarMult, |
|
scalarMultBase, |
|
utils, |
|
GuBytes: GuBytes.slice(), |
|
lengths, |
|
}; |
|
} |
|
//# sourceMappingURL=montgomery.js.map
|