UNPKG

paillier-bignum

Version:

An implementation of the Paillier cryptosystem relying on bignum

195 lines (173 loc) 5.66 kB
const bignum = require('bignum') /** * @typedef {Object} KeyPair * @property {PaillierPublicKey} publicKey - a Paillier's public key * @property {PaillierPrivateKey} privateKey - the associated Paillier's private key */ /** * Generates a pair private, public key for the Paillier cryptosystem in synchronous mode * * @param {number} bitLength - the bit lenght of the public modulo * @param {boolean} simplevariant - use the simple variant to compute the generator * * @returns {KeyPair} - a pair of public, private keys */ const generateRandomKeys = function (bitLength = 4096, simplevariant = false) { let p, q, n // if p and q are bitLength/2 long -> 2**(bitLength - 2) <= n < 2**(bitLenght) do { p = bignum.prime(Math.floor(bitLength / 2)) q = bignum.prime(Math.floor(bitLength / 2)) n = p.mul(q) } while (n.bitLength() !== bitLength) const phi = p.sub(1).mul(q.sub(1)) const n2 = n.pow(2) let g, lambda, mu if (simplevariant === true) { // If using p,q of equivalent length, a simpler variant of the key // generation steps would be to set // g=n+1, lambda=(p-1)(q-1), mu=lambda.invertm(n) g = n.add(1) lambda = phi mu = lambda.invertm(n) } else { g = getGenerator(n, n2) lambda = lcm(p.sub(1), q.sub(1)) mu = L(g.powm(lambda, n2), n).invertm(n) } const publicKey = new PaillierPublicKey(n, g) const privateKey = new PaillierPrivateKey(lambda, mu, publicKey, p, q) return { publicKey: publicKey, privateKey: privateKey } } /** * Generates a pair private, public key for the Paillier cryptosystem in asynchronous mode * * @param {number} bitLength - the bit lenght of the public modulo * @param {boolean} simplevariant - use the simple variant to compute the generator * * @returns {Promise<KeyPair>} - a promise that returns a {@link KeyPair} if resolve */ const generateRandomKeysAsync = async function (bitLength = 4096, simplevariant = false) { return generateRandomKeys(bitLength, simplevariant) } /** * Class for a Paillier public key */ const PaillierPublicKey = class PaillierPublicKey { /** * Creates an instance of class PaillierPublicKey * @param {bignum | string | number} n - the public modulo * @param {bignum | string | number} g - the public generator */ constructor (n, g) { this.n = bignum(n) this._n2 = this.n.pow(2) // cache n^2 this.g = bignum(g) } /** * Get the bit length of the public modulo * @return {number} - bit length of the public modulo */ get bitLength () { return this.n.bitLength() } /** * Paillier public-key encryption * * @param {bignum | string | number} m - a cleartext number * * @returns {bignum} - the encryption of m with this public key */ encrypt (m) { let r do { r = this.n.rand() } while (r.le(1)) return this.g.powm(bignum(m), this._n2).mul(r.powm(this.n, this._n2)).mod(this._n2) } /** * Homomorphic addition * * @param {...bignums} - 2 or more (big) numbers (m_1,..., m_n) encrypted with this public key * * @returns {bignum} - the encryption of (m_1 + ... + m_2) with this public key */ addition (...ciphertexts) { // ciphertexts of numbers return ciphertexts.reduce((sum, next) => sum.mul(bignum(next)).mod(this._n2), bignum(1)) } /** * Pseudo-homomorphic paillier multiplication * * @param {bignum} c - a number m encrypted with this public key * @param {bignum | string | number} k - either a cleartext message (number) or a scalar * * @returns {bignum} - the ecnryption of k·m with this public key */ multiply (c, k) { // c is ciphertext. k is either a cleartext message (number) or a scalar if (typeof k === 'string') { k = bignum(k) } return bignum(c).powm(k, this._n2) } } /** * Class for Paillier private keys. */ const PaillierPrivateKey = class PaillierPrivateKey { /** * Creates an instance of class PaillierPrivateKey * * @param {bignum | string | number} lambda * @param {bignum | string | number} mu * @param {PaillierPublicKey} publicKey * @param {bignum | string | number} [p = null] - a big prime * @param {bignum | string | number} [q = null] - a big prime */ constructor (lambda, mu, publicKey, p = null, q = null) { this.lambda = bignum(lambda) this.mu = bignum(mu) this.publicKey = publicKey this._p = bignum(p) || null this._q = bignum(q) || null } /** * Get the bit length of the public modulo * @return {number} - bit length of the public modulo */ get bitLength () { return this.publicKey.n.bitLength() } /** * Get the public modulo n=p·q * @returns {bignum} - the public modulo n=p·q */ get n () { return this.publicKey.n } /** * Paillier private-key decryption * * @param {bignum | string} c - a (big) number encrypted with the public key * * @returns {bignum} - the decryption of c with this private key */ decrypt (c) { const l = L(bignum(c).powm(this.lambda, this.publicKey._n2), this.publicKey.n) return l.mul(this.mu).mod(this.publicKey.n) } } function lcm (a, b) { return a.mul(b).div(a.gcd(b)) } function L (a, n) { return a.sub(1).div(n) } function getGenerator (n, n2 = n.pow(2)) { const alpha = n.rand() const beta = n.rand() return alpha.mul(n).add(1).mul(beta.powm(n, n2)).mod(n2) } module.exports = { generateRandomKeys: generateRandomKeys, generateRandomKeysAsync: generateRandomKeysAsync, PrivateKey: PaillierPrivateKey, PublicKey: PaillierPublicKey }