tenvoy
Version:
PGP, NaCl, and PBKDF2 in node.js and the browser (hashing, random, encryption, decryption, signatures, conversions), used by TogaTech.org
137 lines (125 loc) • 3.95 kB
JavaScript
// GPG4Browsers - An OpenPGP implementation in javascript
// Copyright (C) 2011 Recurity Labs GmbH
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 3.0 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* @fileoverview ElGamal implementation
* @requires bn.js
* @requires crypto/random
* @module crypto/public_key/elgamal
*/
import BN from 'bn.js';
import random from '../random';
export default {
/**
* ElGamal Encryption function
* @param {BN} m
* @param {BN} p
* @param {BN} g
* @param {BN} y
* @returns {{ c1: BN, c2: BN }}
* @async
*/
encrypt: async function(m, p, g, y) {
const redp = new BN.red(p);
const mred = m.toRed(redp);
const gred = g.toRed(redp);
const yred = y.toRed(redp);
// OpenPGP uses a "special" version of ElGamal where g is generator of the full group Z/pZ*
// hence g has order p-1, and to avoid that k = 0 mod p-1, we need to pick k in [1, p-2]
const k = await random.getRandomBN(new BN(1), p.subn(1));
return {
c1: gred.redPow(k).fromRed(),
c2: yred.redPow(k).redMul(mred).fromRed()
};
},
/**
* ElGamal Encryption function
* @param {BN} c1
* @param {BN} c2
* @param {BN} p
* @param {BN} x
* @returns BN
* @async
*/
decrypt: async function(c1, c2, p, x) {
const redp = new BN.red(p);
const c1red = c1.toRed(redp);
const c2red = c2.toRed(redp);
return c1red.redPow(x).redInvm().redMul(c2red).fromRed();
},
/**
* Validate ElGamal parameters
* @param {Uint8Array} p ElGamal prime
* @param {Uint8Array} g ElGamal group generator
* @param {Uint8Array} y ElGamal public key
* @param {Uint8Array} x ElGamal private exponent
* @returns {Promise<Boolean>} whether params are valid
* @async
*/
validateParams: async function (p, g, y, x) {
p = new BN(p);
g = new BN(g);
y = new BN(y);
const one = new BN(1);
// Check that 1 < g < p
if (g.lte(one) || g.gte(p)) {
return false;
}
// Expect p-1 to be large
const pSize = p.subn(1).bitLength();
if (pSize < 1023) {
return false;
}
const pred = new BN.red(p);
const gModP = g.toRed(pred);
/**
* g should have order p-1
* Check that g ** (p-1) = 1 mod p
*/
if (!gModP.redPow(p.subn(1)).eq(one)) {
return false;
}
/**
* Since p-1 is not prime, g might have a smaller order that divides p-1
* We want to make sure that the order is large enough to hinder a small subgroup attack
*
* We just check g**i != 1 for all i up to a threshold
*/
let res = g;
const i = new BN(1);
const threshold = new BN(2).shln(17); // we want order > threshold
while (i.lt(threshold)) {
res = res.mul(g).mod(p);
if (res.eqn(1)) {
return false;
}
i.iaddn(1);
}
/**
* Re-derive public key y' = g ** x mod p
* Expect y == y'
*
* Blinded exponentiation computes g**{r(p-1) + x} to compare to y
*/
x = new BN(x);
const r = await random.getRandomBN(new BN(2).shln(pSize - 1), new BN(2).shln(pSize)); // draw r of same size as p-1
const rqx = p.subn(1).mul(r).add(x);
if (!y.eq(gModP.redPow(rqx))) {
return false;
}
return true;
}
};