From d69172b5bf4de89ab168ff75153bee5d67546e9f Mon Sep 17 00:00:00 2001 From: QuantumSoham Date: Mon, 28 Apr 2025 22:42:03 +0530 Subject: [PATCH] feat: add RSA encryption and decryption algorithm --- Ciphers/RSAAlgorithm.js | 106 ++++++++++++++++++++++++++++++ Ciphers/test/RSAAlgorithm.test.js | 14 ++++ 2 files changed, 120 insertions(+) create mode 100644 Ciphers/RSAAlgorithm.js create mode 100644 Ciphers/test/RSAAlgorithm.test.js diff --git a/Ciphers/RSAAlgorithm.js b/Ciphers/RSAAlgorithm.js new file mode 100644 index 0000000000..347e2531a1 --- /dev/null +++ b/Ciphers/RSAAlgorithm.js @@ -0,0 +1,106 @@ +// RSAAlgorithm.js + +/** + * Generates the greatest common divisor of two numbers. + * @param {number} a - First number. + * @param {number} b - Second number. + * @returns {number} - The GCD of a and b. + */ +export function gcd(a, b) { + if (b === 0) { + return a + } + return gcd(b, a % b) +} + +/** + * Calculates modular inverse using Extended Euclidean Algorithm. + * @param {number} e - The number to find inverse for. + * @param {number} phi - The modulus. + * @returns {number} - The modular inverse of e mod phi. + */ +export function modInverse(e, phi) { + let [m0, x0, x1] = [phi, 0, 1] + + if (phi === 1) { + return 0 + } + + while (e > 1) { + let q = Math.floor(e / phi) + ;[e, phi] = [phi, e % phi] + ;[x0, x1] = [x1 - q * x0, x0] + } + + if (x1 < 0) { + x1 += m0 + } + + return x1 +} + +/** + * Performs modular exponentiation. + * @param {number} base - Base number. + * @param {number} exponent - Exponent. + * @param {number} modulus - Modulus. + * @returns {number} - (base^exponent) % modulus. + */ +export function modPow(base, exponent, modulus) { + if (modulus === 1) return 0 + let result = 1 + base = base % modulus + + while (exponent > 0) { + if (exponent % 2 === 1) { + result = (result * base) % modulus + } + exponent = Math.floor(exponent / 2) + base = (base * base) % modulus + } + + return result +} + +/** + * Generates RSA keys. + * @param {number} p - A prime number. + * @param {number} q - A prime number. + * @returns {{publicKey: {e: number, n: number}, privateKey: {d: number, n: number}}} + */ +export function generateKeys(p, q) { + const n = p * q + const phi = (p - 1) * (q - 1) + + let e = 2 + while (e < phi && gcd(e, phi) !== 1) { + e++ + } + + const d = modInverse(e, phi) + + return { + publicKey: { e, n }, + privateKey: { d, n } + } +} + +/** + * Encrypts a message with a public key. + * @param {number} message - The message to encrypt (as a number). + * @param {{e: number, n: number}} publicKey - The public key. + * @returns {number} - The encrypted message. + */ +export function encrypt(message, publicKey) { + return modPow(message, publicKey.e, publicKey.n) +} + +/** + * Decrypts a cipher with a private key. + * @param {number} cipher - The encrypted message (cipher). + * @param {{d: number, n: number}} privateKey - The private key. + * @returns {number} - The decrypted message. + */ +export function decrypt(cipher, privateKey) { + return modPow(cipher, privateKey.d, privateKey.n) +} diff --git a/Ciphers/test/RSAAlgorithm.test.js b/Ciphers/test/RSAAlgorithm.test.js new file mode 100644 index 0000000000..ac1d0e540f --- /dev/null +++ b/Ciphers/test/RSAAlgorithm.test.js @@ -0,0 +1,14 @@ +// RSAAlgorithm.test.js +import { describe, it, expect } from 'vitest' +import { generateKeys, encrypt, decrypt } from '../RSAAlgorithm.js' + +describe('RSA Algorithm', () => { + it('should encrypt and decrypt correctly', () => { + const { publicKey, privateKey } = generateKeys(61, 53) // two primes + const message = 65 + const cipher = encrypt(message, publicKey) + const decrypted = decrypt(cipher, privateKey) + + expect(decrypted).toBe(message) + }) +})