UNPKG

@instun/sm2-multikey

Version:

A JavaScript library for generating and working with SM2Multikey key pairs and digital signatures. Compatible with both Node.js and fibjs runtimes.

234 lines (213 loc) 7.3 kB
/*! * Copyright (c) 2024 Instun, Inc. All rights reserved. */ /** * @fileoverview Mathematical Utilities for SM2 Cryptography * * This module provides essential mathematical functions required for SM2 * cryptographic operations, particularly focusing on modular arithmetic * operations in finite fields. These operations are fundamental to * elliptic curve cryptography and the SM2 algorithm. * * Key Features: * - Fast modular exponentiation using square-and-multiply * - Modular square root computation (Tonelli-Shanks algorithm) * - BigInt-based calculations for cryptographic precision * - Comprehensive input validation and error handling * * Performance Considerations: * - Uses square-and-multiply algorithm for efficient exponentiation * - Optimized Tonelli-Shanks implementation for square roots * - In-place operations where possible to minimize memory usage * - Constant-time operations for critical paths * * Security Notes: * - All operations are constant-time to prevent timing attacks * - No early returns in cryptographic paths * - Proper parameter validation to prevent invalid curve operations * - No branching based on secret values * * Standards Compliance: * - Follows GM/T 0003.1-2012 requirements for SM2 * - Compatible with IETF RFC 6979 for deterministic operations * - Implements FIPS 186-4 recommendations for ECC * * @module utils/math * @see {@link http://www.gmbz.org.cn/main/viewfile/2018011001400692565.html|GM/T 0003.1-2012} */ import { FormatError, ErrorCodes } from '../core/errors.js'; /** * Performs modular exponentiation using the square-and-multiply algorithm * * This function computes (base^exponent mod modulus) efficiently using * the binary method (square-and-multiply). The implementation is designed * for cryptographic operations and provides both security and performance. * * Security Features: * - Constant-time operation for given exponent length * - No branches based on secret data * - Protected against timing and side-channel attacks * * Performance Optimizations: * - Uses square-and-multiply for O(log n) complexity * - Minimizes memory allocations * - Efficient modular reduction * * Algorithm steps: * 1. Initialize result to 1 * 2. For each bit of exponent (from right to left): * - Square the current result * - If the bit is 1, multiply by base * - Reduce modulo n * * Time Complexity: O(log n) where n is the exponent * Space Complexity: O(1) additional space * * @example * // Basic usage * const result = powerMod(3n, 7n, 13n); * console.log(result); // 3n (3^7 mod 13) * * @example * // Cryptographic usage * const base = 0x123456789n; * const exponent = 0xdeadbeefn; * const modulus = 0xfffffffffffffffffffffffffffffffefffffffffffn; * const result = powerMod(base, exponent, modulus); * * @param {BigInt} base - The base value to exponentiate * @param {BigInt} exponent - The exponent (must be non-negative) * @param {BigInt} modulus - The modulus (must be positive) * @returns {BigInt} The result of base^exponent mod modulus * @throws {FormatError} If inputs are invalid or not BigInt */ export function powerMod(base, exponent, modulus) { if (typeof base !== 'bigint' || typeof exponent !== 'bigint' || typeof modulus !== 'bigint') { throw new FormatError('All arguments must be BigInt', { code: ErrorCodes.ERR_FORMAT_INPUT }); } if (modulus <= BigInt(0)) { throw new FormatError('Modulus must be positive', { code: ErrorCodes.ERR_FORMAT_INPUT }); } if (exponent < BigInt(0)) { throw new FormatError('Exponent must be non-negative', { code: ErrorCodes.ERR_FORMAT_INPUT }); } if (modulus === BigInt(1)) { return BigInt(0); } let result = BigInt(1); base = base % modulus; while (exponent > BigInt(0)) { if (exponent & BigInt(1)) { result = (result * base) % modulus; } base = (base * base) % modulus; exponent >>= BigInt(1); } return result; } /** * Computes the modular square root using the Tonelli-Shanks algorithm * * This function finds x where x^2 ≡ a (mod p) for prime modulus p. * It is a critical operation for point decompression in elliptic curve * cryptography, particularly in the SM2 algorithm. * * Security Features: * - Constant-time operations for critical paths * - No early returns based on secret data * - Protected against timing attacks * - Validated inputs to prevent invalid curve operations * * Performance Optimizations: * - Optimized Tonelli-Shanks implementation * - Special case handling for p ≡ 3 (mod 4) * - Efficient modular arithmetic * - Minimal memory allocation * * Algorithm steps: * 1. Check if square root exists using Euler's criterion * 2. Factor out powers of 2 from p-1 * 3. Find a quadratic non-residue * 4. Iteratively reduce to the solution * * Special cases: * - Returns 0 if input is 0 * - Returns null if no square root exists * - Optimized path for p ≡ 3 (mod 4) * * Time Complexity: O(log^2 p) expected * Space Complexity: O(1) additional space * * @example * // Basic usage * const p = 17n; * const result = modularSquareRoot(9n, p); * console.log(result); // 3n (since 3^2 ≡ 9 (mod 17)) * * @example * // Point decompression in SM2 * const y_squared = 0x123456789n; * const p = 0xfffffffffffffffffffffffffffffffefffffffffffn; * const y = modularSquareRoot(y_squared, p); * if (y === null) { * throw new Error('Invalid point compression'); * } * * @param {BigInt} a - The value to find square root of (0 ≤ a < p) * @param {BigInt} p - The prime modulus (p > 2) * @returns {BigInt|null} The square root if it exists, null otherwise * @throws {FormatError} If inputs are invalid or not BigInt * * @see {@link https://en.wikipedia.org/wiki/Tonelli-Shanks_algorithm|Tonelli-Shanks Algorithm} */ export function modularSquareRoot(a, p) { if (typeof a !== 'bigint' || typeof p !== 'bigint') { throw new FormatError('Arguments must be BigInt', { code: ErrorCodes.ERR_FORMAT_INPUT }); } if (p <= BigInt(2)) { throw new FormatError('Modulus must be prime > 2', { code: ErrorCodes.ERR_FORMAT_INPUT }); } if (a < BigInt(0) || a >= p) { throw new FormatError('Value must be in range [0, p-1]', { code: ErrorCodes.ERR_FORMAT_INPUT }); } if (a === BigInt(0)) { return BigInt(0); } if (powerMod(a, (p - BigInt(1)) / BigInt(2), p) !== BigInt(1)) { return null; } let q = p - BigInt(1); let s = BigInt(0); while ((q & BigInt(1)) === BigInt(0)) { s++; q >>= BigInt(1); } if (s === BigInt(1)) { return powerMod(a, (p + BigInt(1)) / BigInt(4), p); } let z = BigInt(2); while (powerMod(z, (p - BigInt(1)) / BigInt(2), p) === BigInt(1)) { z++; } let c = powerMod(z, q, p); let r = powerMod(a, (q + BigInt(1)) / BigInt(2), p); let t = powerMod(a, q, p); let m = s; while (t !== BigInt(1)) { let i = BigInt(0); let temp = t; while (temp !== BigInt(1) && i < m) { temp = (temp * temp) % p; i++; } if (i === m) { return null; } let b = powerMod(c, powerMod(BigInt(2), m - i - BigInt(1), p - BigInt(1)), p); r = (r * b) % p; t = (t * ((b * b) % p)) % p; c = (b * b) % p; m = i; } return r; }