|
| 1 | +/** |
| 2 | + * Baby-step giant-step discrete logarithm modulo a prime. |
| 3 | + * https://en.wikipedia.org/wiki/Baby-step_giant-step |
| 4 | + * |
| 5 | + * Solves base^x ≡ target (mod modulus) for the smallest non-negative x. |
| 6 | + */ |
| 7 | + |
| 8 | +/** |
| 9 | + * @param {number} base |
| 10 | + * @param {number} target |
| 11 | + * @param {number} modulus prime (or modulus where base is invertible) |
| 12 | + * @returns {number} smallest non-negative discrete log |
| 13 | + */ |
| 14 | +export function babyStepGiantStep(base, target, modulus) { |
| 15 | + if ( |
| 16 | + typeof base !== 'number' || |
| 17 | + typeof target !== 'number' || |
| 18 | + typeof modulus !== 'number' || |
| 19 | + !Number.isInteger(base) || |
| 20 | + !Number.isInteger(target) || |
| 21 | + !Number.isInteger(modulus) |
| 22 | + ) { |
| 23 | + throw new TypeError('Arguments must be integers') |
| 24 | + } |
| 25 | + if (modulus <= 1) throw new RangeError('modulus must be > 1') |
| 26 | + |
| 27 | + base = ((base % modulus) + modulus) % modulus |
| 28 | + target = ((target % modulus) + modulus) % modulus |
| 29 | + |
| 30 | + if (target === 1) return 0 |
| 31 | + if (base === 0) { |
| 32 | + if (target === 0) return 1 |
| 33 | + throw new RangeError('no discrete log') |
| 34 | + } |
| 35 | + |
| 36 | + const modPow = (b, e, mod) => { |
| 37 | + let r = 1 |
| 38 | + b = ((b % mod) + mod) % mod |
| 39 | + while (e > 0) { |
| 40 | + if (e % 2 === 1) r = (r * b) % mod |
| 41 | + b = (b * b) % mod |
| 42 | + e = Math.floor(e / 2) |
| 43 | + } |
| 44 | + return r |
| 45 | + } |
| 46 | + |
| 47 | + const m = Math.ceil(Math.sqrt(modulus - 1)) |
| 48 | + const baby = new Map() |
| 49 | + let value = 1 |
| 50 | + for (let j = 0; j < m; j++) { |
| 51 | + if (!baby.has(value)) baby.set(value, j) |
| 52 | + value = (value * base) % modulus |
| 53 | + } |
| 54 | + |
| 55 | + // factor = base^{-m} mod modulus (Fermat inverse assumes prime modulus) |
| 56 | + const invBase = modPow(base, modulus - 2, modulus) |
| 57 | + const factor = modPow(invBase, m, modulus) |
| 58 | + |
| 59 | + let gamma = target |
| 60 | + for (let i = 0; i < m; i++) { |
| 61 | + if (baby.has(gamma)) { |
| 62 | + return i * m + baby.get(gamma) |
| 63 | + } |
| 64 | + gamma = (gamma * factor) % modulus |
| 65 | + } |
| 66 | + |
| 67 | + throw new RangeError('no discrete log') |
| 68 | +} |
0 commit comments