UNPKG

js-ecutils

Version:

JavaScript Library for Elliptic Curve Cryptography: key exchanges (Diffie-Hellman, Massey-Omura), ECDSA signatures, and Koblitz encoding. Suitable for crypto education and secure systems.

213 lines (203 loc) 18.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.extendedGcd = extendedGcd; exports.isQuadraticResidue = isQuadraticResidue; exports.modInverse = modInverse; exports.modPow = modPow; exports.modularSqrt = modularSqrt; exports.modulus = modulus; function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } function _arrayWithHoles(r) { if (Array.isArray(r)) return r; } /** * Modular arithmetic utilities for elliptic curve operations. * * Provides quadratic residue testing and modular square root computation, * essential building blocks for point decompression and Koblitz encoding. */ /** * Positive modulo helper. * * JavaScript's `%` operator can return negative results for negative * dividends. This function ensures the result is always in [0, b). * * @param {BigInt} a - Dividend. * @param {BigInt} b - Divisor (must be positive). * @returns {BigInt} a mod b, always non-negative. */ function modulus(a, b) { return (a % b + b) % b; } /** * Extended Euclidean algorithm. * * Computes integers x, y such that: * * a·x + b·y = gcd(a, b) * * Used internally by {@link modInverse} to find multiplicative inverses. * * @param {BigInt} a * @param {BigInt} b * @returns {[BigInt, BigInt, BigInt]} [gcd(a,b), x, y] */ function extendedGcd(a, b) { var old_r = a, r = b; var old_s = 1n, s = 0n; var old_t = 0n, t = 1n; while (r !== 0n) { var q = old_r / r; var _ref = [r, old_r - q * r]; old_r = _ref[0]; r = _ref[1]; var _ref2 = [s, old_s - q * s]; old_s = _ref2[0]; s = _ref2[1]; var _ref3 = [t, old_t - q * t]; old_t = _ref3[0]; t = _ref3[1]; } return [old_r, old_s, old_t]; } /** * Modular multiplicative inverse of a modulo p. * * Returns x such that: * * a · x ≡ 1 (mod p) * * Computed via the extended Euclidean algorithm. Throws if gcd(a, p) ≠ 1 * (i.e., the inverse does not exist). * * @param {BigInt} a * @param {BigInt} p * @returns {BigInt} a⁻¹ mod p */ function modInverse(a, p) { a = modulus(a, p); var _extendedGcd = extendedGcd(a, p), _extendedGcd2 = _slicedToArray(_extendedGcd, 2), g = _extendedGcd2[0], x = _extendedGcd2[1]; if (g !== 1n) { throw new Error('Modular inverse does not exist.'); } return modulus(x, p); } /** * Modular exponentiation via binary method (square-and-multiply). * * Computes: * * base^exp mod m * * Runs in O(log exp) multiplications. * * @param {BigInt} base * @param {BigInt} exp - Non-negative exponent. * @param {BigInt} m - Modulus. * @returns {BigInt} */ function modPow(base, exp, m) { if (m === 1n) return 0n; base = modulus(base, m); var result = 1n; while (exp > 0n) { if (exp & 1n) { result = result * base % m; } exp >>= 1n; base = base * base % m; } return result; } /** * Check whether a is a quadratic residue modulo p using Euler's criterion. * * A non-zero integer a is a quadratic residue mod p (an odd prime) iff: * * a^((p-1)/2) ≡ 1 (mod p) * * Returns false when a ≡ 0 (mod p). * * @param {BigInt} a - The integer to test (will be reduced mod p). * @param {BigInt} p - An odd prime. * @returns {boolean} */ function isQuadraticResidue(a, p) { a = modulus(a, p); if (a === 0n) return false; return modPow(a, (p - 1n) / 2n, p) === 1n; } /** * Compute a square root of a modulo p, or null if none exists. * * Uses the direct formula when p ≡ 3 (mod 4): * * r = a^((p+1)/4) mod p * * Falls back to the Tonelli-Shanks algorithm for the general case * (p ≡ 1 mod 4): * * 1. Factor out powers of 2: p - 1 = Q · 2^S * 2. Find a quadratic non-residue z * 3. Initialize: M = S, c = z^Q, t = a^Q, r = a^((Q+1)/2) * 4. Loop until t ≡ 1 (mod p): * - Find least i such that t^(2^i) ≡ 1 (mod p) * - b = c^(2^(M-i-1)), M = i, c = b², t = t·c, r = r·b * * @param {BigInt} a - The value whose square root is sought (reduced mod p). * @param {BigInt} p - An odd prime. * @returns {BigInt|null} r such that r² ≡ a (mod p), or null if a is not a QR. */ function modularSqrt(a, p) { a = modulus(a, p); if (a === 0n) return 0n; if (!isQuadraticResidue(a, p)) return null; // Shortcut for p ≡ 3 (mod 4) if (p % 4n === 3n) { return modPow(a, (p + 1n) / 4n, p); } // Tonelli-Shanks algorithm for the general case (p ≡ 1 mod 4) // Factor out powers of 2: p - 1 = Q · 2^S var s = 0n; var q = p - 1n; while (q % 2n === 0n) { q /= 2n; s += 1n; } // Find a quadratic non-residue var z = 2n; while (isQuadraticResidue(z, p)) { z += 1n; } var m = s; var c = modPow(z, q, p); var t = modPow(a, q, p); var r = modPow(a, (q + 1n) / 2n, p); while (true) { if (t === 1n) return r; // Find the least i such that t^(2^i) ≡ 1 (mod p) var i = 1n; var temp = t * t % p; while (temp !== 1n) { temp = temp * temp % p; i += 1n; } // Update var b = modPow(c, 1n << m - i - 1n, p); m = i; c = b * b % p; t = t * c % p; r = r * b % p; } } //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJtb2R1bHVzIiwiYSIsImIiLCJleHRlbmRlZEdjZCIsIm9sZF9yIiwiciIsIm9sZF9zIiwicyIsIm9sZF90IiwidCIsInEiLCJfcmVmIiwiX3JlZjIiLCJfcmVmMyIsIm1vZEludmVyc2UiLCJwIiwiX2V4dGVuZGVkR2NkIiwiX2V4dGVuZGVkR2NkMiIsIl9zbGljZWRUb0FycmF5IiwiZyIsIngiLCJFcnJvciIsIm1vZFBvdyIsImJhc2UiLCJleHAiLCJtIiwicmVzdWx0IiwiaXNRdWFkcmF0aWNSZXNpZHVlIiwibW9kdWxhclNxcnQiLCJ6IiwiYyIsImkiLCJ0ZW1wIl0sInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3V0aWxzL21hdGguanMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBNb2R1bGFyIGFyaXRobWV0aWMgdXRpbGl0aWVzIGZvciBlbGxpcHRpYyBjdXJ2ZSBvcGVyYXRpb25zLlxuICpcbiAqIFByb3ZpZGVzIHF1YWRyYXRpYyByZXNpZHVlIHRlc3RpbmcgYW5kIG1vZHVsYXIgc3F1YXJlIHJvb3QgY29tcHV0YXRpb24sXG4gKiBlc3NlbnRpYWwgYnVpbGRpbmcgYmxvY2tzIGZvciBwb2ludCBkZWNvbXByZXNzaW9uIGFuZCBLb2JsaXR6IGVuY29kaW5nLlxuICovXG5cbi8qKlxuICogUG9zaXRpdmUgbW9kdWxvIGhlbHBlci5cbiAqXG4gKiBKYXZhU2NyaXB0J3MgYCVgIG9wZXJhdG9yIGNhbiByZXR1cm4gbmVnYXRpdmUgcmVzdWx0cyBmb3IgbmVnYXRpdmVcbiAqIGRpdmlkZW5kcy4gIFRoaXMgZnVuY3Rpb24gZW5zdXJlcyB0aGUgcmVzdWx0IGlzIGFsd2F5cyBpbiBbMCwgYikuXG4gKlxuICogQHBhcmFtIHtCaWdJbnR9IGEgLSBEaXZpZGVuZC5cbiAqIEBwYXJhbSB7QmlnSW50fSBiIC0gRGl2aXNvciAobXVzdCBiZSBwb3NpdGl2ZSkuXG4gKiBAcmV0dXJucyB7QmlnSW50fSBhIG1vZCBiLCBhbHdheXMgbm9uLW5lZ2F0aXZlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gbW9kdWx1cyhhLCBiKSB7XG4gIHJldHVybiAoKGEgJSBiKSArIGIpICUgYlxufVxuXG4vKipcbiAqIEV4dGVuZGVkIEV1Y2xpZGVhbiBhbGdvcml0aG0uXG4gKlxuICogQ29tcHV0ZXMgaW50ZWdlcnMgeCwgeSBzdWNoIHRoYXQ6XG4gKlxuICogICAgIGHCt3ggKyBiwrd5ID0gZ2NkKGEsIGIpXG4gKlxuICogVXNlZCBpbnRlcm5hbGx5IGJ5IHtAbGluayBtb2RJbnZlcnNlfSB0byBmaW5kIG11bHRpcGxpY2F0aXZlIGludmVyc2VzLlxuICpcbiAqIEBwYXJhbSB7QmlnSW50fSBhXG4gKiBAcGFyYW0ge0JpZ0ludH0gYlxuICogQHJldHVybnMge1tCaWdJbnQsIEJpZ0ludCwgQmlnSW50XX0gW2djZChhLGIpLCB4LCB5XVxuICovXG5leHBvcnQgZnVuY3Rpb24gZXh0ZW5kZWRHY2QoYSwgYikge1xuICBsZXQgb2xkX3IgPSBhLFxuICAgIHIgPSBiXG4gIGxldCBvbGRfcyA9IDFuLFxuICAgIHMgPSAwblxuICBsZXQgb2xkX3QgPSAwbixcbiAgICB0ID0gMW5cbiAgd2hpbGUgKHIgIT09IDBuKSB7XG4gICAgY29uc3QgcSA9IG9sZF9yIC8gclxuICAgIDtbb2xkX3IsIHJdID0gW3IsIG9sZF9yIC0gcSAqIHJdXG4gICAgO1tvbGRfcywgc10gPSBbcywgb2xkX3MgLSBxICogc11cbiAgICA7W29sZF90LCB0XSA9IFt0LCBvbGRfdCAtIHEgKiB0XVxuICB9XG4gIHJldHVybiBbb2xkX3IsIG9sZF9zLCBvbGRfdF1cbn1cblxuLyoqXG4gKiBNb2R1bGFyIG11bHRpcGxpY2F0aXZlIGludmVyc2Ugb2YgYSBtb2R1bG8gcC5cbiAqXG4gKiBSZXR1cm5zIHggc3VjaCB0aGF0OlxuICpcbiAqICAgICBhIMK3IHgg4omhIDEgIChtb2QgcClcbiAqXG4gKiBDb21wdXRlZCB2aWEgdGhlIGV4dGVuZGVkIEV1Y2xpZGVhbiBhbGdvcml0aG0uICBUaHJvd3MgaWYgZ2NkKGEsIHApIOKJoCAxXG4gKiAoaS5lLiwgdGhlIGludmVyc2UgZG9lcyBub3QgZXhpc3QpLlxuICpcbiAqIEBwYXJhbSB7QmlnSW50fSBhXG4gKiBAcGFyYW0ge0JpZ0ludH0gcFxuICogQHJldHVybnMge0JpZ0ludH0gYeKBu8K5IG1vZCBwXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBtb2RJbnZlcnNlKGEsIHApIHtcbiAgYSA9IG1vZHVsdXMoYSwgcClcbiAgY29uc3QgW2csIHhdID0gZXh0ZW5kZWRHY2QoYSwgcClcbiAgaWYgKGcgIT09IDFuKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdNb2R1bGFyIGludmVyc2UgZG9lcyBub3QgZXhpc3QuJylcbiAgfVxuICByZXR1cm4gbW9kdWx1cyh4LCBwKVxufVxuXG4vKipcbiAqIE1vZHVsYXIgZXhwb25lbnRpYXRpb24gdmlhIGJpbmFyeSBtZXRob2QgKHNxdWFyZS1hbmQtbXVsdGlwbHkpLlxuICpcbiAqIENvbXB1dGVzOlxuICpcbiAqICAgICBiYXNlXmV4cCAgbW9kIG1cbiAqXG4gKiBSdW5zIGluIE8obG9nIGV4cCkgbXVsdGlwbGljYXRpb25zLlxuICpcbiAqIEBwYXJhbSB7QmlnSW50fSBiYXNlXG4gKiBAcGFyYW0ge0JpZ0ludH0gZXhwIC0gTm9uLW5lZ2F0aXZlIGV4cG9uZW50LlxuICogQHBhcmFtIHtCaWdJbnR9IG0gICAtIE1vZHVsdXMuXG4gKiBAcmV0dXJucyB7QmlnSW50fVxuICovXG5leHBvcnQgZnVuY3Rpb24gbW9kUG93KGJhc2UsIGV4cCwgbSkge1xuICBpZiAobSA9PT0gMW4pIHJldHVybiAwblxuICBiYXNlID0gbW9kdWx1cyhiYXNlLCBtKVxuICBsZXQgcmVzdWx0ID0gMW5cbiAgd2hpbGUgKGV4cCA+IDBuKSB7XG4gICAgaWYgKGV4cCAmIDFuKSB7XG4gICAgICByZXN1bHQgPSAocmVzdWx0ICogYmFzZSkgJSBtXG4gICAgfVxuICAgIGV4cCA+Pj0gMW5cbiAgICBiYXNlID0gKGJhc2UgKiBiYXNlKSAlIG1cbiAgfVxuICByZXR1cm4gcmVzdWx0XG59XG5cbi8qKlxuICogQ2hlY2sgd2hldGhlciBhIGlzIGEgcXVhZHJhdGljIHJlc2lkdWUgbW9kdWxvIHAgdXNpbmcgRXVsZXIncyBjcml0ZXJpb24uXG4gKlxuICogQSBub24temVybyBpbnRlZ2VyIGEgaXMgYSBxdWFkcmF0aWMgcmVzaWR1ZSBtb2QgcCAoYW4gb2RkIHByaW1lKSBpZmY6XG4gKlxuICogICAgIGFeKChwLTEpLzIpIOKJoSAxICAobW9kIHApXG4gKlxuICogUmV0dXJucyBmYWxzZSB3aGVuIGEg4omhIDAgKG1vZCBwKS5cbiAqXG4gKiBAcGFyYW0ge0JpZ0ludH0gYSAtIFRoZSBpbnRlZ2VyIHRvIHRlc3QgKHdpbGwgYmUgcmVkdWNlZCBtb2QgcCkuXG4gKiBAcGFyYW0ge0JpZ0ludH0gcCAtIEFuIG9kZCBwcmltZS5cbiAqIEByZXR1cm5zIHtib29sZWFufVxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNRdWFkcmF0aWNSZXNpZHVlKGEsIHApIHtcbiAgYSA9IG1vZHVsdXMoYSwgcClcbiAgaWYgKGEgPT09IDBuKSByZXR1cm4gZmFsc2VcbiAgcmV0dXJuIG1vZFBvdyhhLCAocCAtIDFuKSAvIDJuLCBwKSA9PT0gMW5cbn1cblxuLyoqXG4gKiBDb21wdXRlIGEgc3F1YXJlIHJvb3Qgb2YgYSBtb2R1bG8gcCwgb3IgbnVsbCBpZiBub25lIGV4aXN0cy5cbiAqXG4gKiBVc2VzIHRoZSBkaXJlY3QgZm9ybXVsYSB3aGVuIHAg4omhIDMgKG1vZCA0KTpcbiAqXG4gKiAgICAgciA9IGFeKChwKzEpLzQpICBtb2QgcFxuICpcbiAqIEZhbGxzIGJhY2sgdG8gdGhlIFRvbmVsbGktU2hhbmtzIGFsZ29yaXRobSBmb3IgdGhlIGdlbmVyYWwgY2FzZVxuICogKHAg4omhIDEgbW9kIDQpOlxuICpcbiAqICAgMS4gRmFjdG9yIG91dCBwb3dlcnMgb2YgMjogIHAgLSAxID0gUSDCtyAyXlNcbiAqICAgMi4gRmluZCBhIHF1YWRyYXRpYyBub24tcmVzaWR1ZSB6XG4gKiAgIDMuIEluaXRpYWxpemU6ICBNID0gUywgIGMgPSB6XlEsICB0ID0gYV5RLCAgciA9IGFeKChRKzEpLzIpXG4gKiAgIDQuIExvb3AgdW50aWwgdCDiiaEgMSAobW9kIHApOlxuICogICAgICAgIC0gRmluZCBsZWFzdCBpIHN1Y2ggdGhhdCB0XigyXmkpIOKJoSAxIChtb2QgcClcbiAqICAgICAgICAtIGIgPSBjXigyXihNLWktMSkpLCAgTSA9IGksICBjID0gYsKyLCAgdCA9IHTCt2MsICByID0gcsK3YlxuICpcbiAqIEBwYXJhbSB7QmlnSW50fSBhIC0gVGhlIHZhbHVlIHdob3NlIHNxdWFyZSByb290IGlzIHNvdWdodCAocmVkdWNlZCBtb2QgcCkuXG4gKiBAcGFyYW0ge0JpZ0ludH0gcCAtIEFuIG9kZCBwcmltZS5cbiAqIEByZXR1cm5zIHtCaWdJbnR8bnVsbH0gciBzdWNoIHRoYXQgcsKyIOKJoSBhIChtb2QgcCksIG9yIG51bGwgaWYgYSBpcyBub3QgYSBRUi5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIG1vZHVsYXJTcXJ0KGEsIHApIHtcbiAgYSA9IG1vZHVsdXMoYSwgcClcbiAgaWYgKGEgPT09IDBuKSByZXR1cm4gMG5cblxuICBpZiAoIWlzUXVhZHJhdGljUmVzaWR1ZShhLCBwKSkgcmV0dXJuIG51bGxcblxuICAvLyBTaG9ydGN1dCBmb3IgcCDiiaEgMyAobW9kIDQpXG4gIGlmIChwICUgNG4gPT09IDNuKSB7XG4gICAgcmV0dXJuIG1vZFBvdyhhLCAocCArIDFuKSAvIDRuLCBwKVxuICB9XG5cbiAgLy8gVG9uZWxsaS1TaGFua3MgYWxnb3JpdGhtIGZvciB0aGUgZ2VuZXJhbCBjYXNlIChwIOKJoSAxIG1vZCA0KVxuICAvLyBGYWN0b3Igb3V0IHBvd2VycyBvZiAyOiAgcCAtIDEgPSBRIMK3IDJeU1xuICBsZXQgcyA9IDBuXG4gIGxldCBxID0gcCAtIDFuXG4gIHdoaWxlIChxICUgMm4gPT09IDBuKSB7XG4gICAgcSAvPSAyblxuICAgIHMgKz0gMW5cbiAgfVxuXG4gIC8vIEZpbmQgYSBxdWFkcmF0aWMgbm9uLXJlc2lkdWVcbiAgbGV0IHogPSAyblxuICB3aGlsZSAoaXNRdWFkcmF0aWNSZXNpZHVlKHosIHApKSB7XG4gICAgeiArPSAxblxuICB9XG5cbiAgbGV0IG0gPSBzXG4gIGxldCBjID0gbW9kUG93KHosIHEsIHApXG4gIGxldCB0ID0gbW9kUG93KGEsIHEsIHApXG4gIGxldCByID0gbW9kUG93KGEsIChxICsgMW4pIC8gMm4sIHApXG5cbiAgd2hpbGUgKHRydWUpIHtcbiAgICBpZiAodCA9PT0gMW4pIHJldHVybiByXG4gICAgLy8gRmluZCB0aGUgbGVhc3QgaSBzdWNoIHRoYXQgdF4oMl5pKSDiiaEgMSAobW9kIHApXG4gICAgbGV0IGkgPSAxblxuICAgIGxldCB0ZW1wID0gKHQgKiB0KSAlIHBcbiAgICB3aGlsZSAodGVtcCAhPT0gMW4pIHtcbiAgICAgIHRlbXAgPSAodGVtcCAqIHRlbXApICUgcFxuICAgICAgaSArPSAxblxuICAgIH1cbiAgICAvLyBVcGRhdGVcbiAgICBjb25zdCBiID0gbW9kUG93KGMsIDFuIDw8IChtIC0gaSAtIDFuKSwgcClcbiAgICBtID0gaVxuICAgIGMgPSAoYiAqIGIpICUgcFxuICAgIHQgPSAodCAqIGMpICUgcFxuICAgIHIgPSAociAqIGIpICUgcFxuICB9XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ08sU0FBU0EsT0FBT0EsQ0FBQ0MsQ0FBQyxFQUFFQyxDQUFDLEVBQUU7RUFDNUIsT0FBTyxDQUFFRCxDQUFDLEdBQUdDLENBQUMsR0FBSUEsQ0FBQyxJQUFJQSxDQUFDO0FBQzFCOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ08sU0FBU0MsV0FBV0EsQ0FBQ0YsQ0FBQyxFQUFFQyxDQUFDLEVBQUU7RUFDaEMs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