diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index 54fc6ceb..408b4dfe 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -32,7 +32,6 @@ */ import BN from 'bn.js'; -import { RSA_RAW } from 'asmcrypto.js'; import publicKey from './public_key'; import cipher from './cipher'; import random from './random'; @@ -68,18 +67,19 @@ export default { switch (algo) { case 'rsa_encrypt': case 'rsa_encrypt_sign': { + const m = data.toUint8Array(); const n = publicParams[0].toUint8Array(); const e = publicParams[1].toUint8Array(); - const m = data.toUint8Array(); - return constructParams(types, [new BN(RSA_RAW.encrypt(m, [n, e]))]); + const res = await publicKey.rsa.encrypt(m, n, e); + return constructParams(types, [new BN(res)]); } case 'elgamal': { - const elgamal = new publicKey.elgamal(); - const p = publicParams[0].toBigInteger(); - const g = publicParams[1].toBigInteger(); - const y = publicParams[2].toBigInteger(); - const m = data.toBigInteger(); - return constructParams(types, elgamal.encrypt(m, g, p, y)); + const m = data.toBN(); + const p = publicParams[0].toBN(); + const g = publicParams[1].toBN(); + const y = publicParams[2].toBN(); + const res = await publicKey.elgamal.encrypt(m, p, g, y); + return constructParams(types, [res.c1, res.c2]); } case 'ecdh': { const oid = publicParams[0]; @@ -117,18 +117,14 @@ export default { const p = keyIntegers[3].toUint8Array(); const q = keyIntegers[4].toUint8Array(); const u = keyIntegers[5].toUint8Array(); // q^-1 mod p - const dd = new BN(d); - const dp = dd.mod(new BN(p).subn(1)).toArrayLike(Uint8Array); // d mod (p-1) - const dq = dd.mod(new BN(q).subn(1)).toArrayLike(Uint8Array); // d mod (q-1) - return new BN(RSA_RAW.decrypt(c, [n, e, d, q, p, dq, dp, u]).slice(1)); // FIXME remove slice + return publicKey.rsa.decrypt(c, n, e, d, p, q, u); } case 'elgamal': { - const elgamal = new publicKey.elgamal(); - const x = keyIntegers[3].toBigInteger(); - const c1 = dataIntegers[0].toBigInteger(); - const c2 = dataIntegers[1].toBigInteger(); - const p = keyIntegers[0].toBigInteger(); - return elgamal.decrypt(c1, c2, p, x); + const c1 = dataIntegers[0].toBN(); + const c2 = dataIntegers[1].toBN(); + const p = keyIntegers[0].toBN(); + const x = keyIntegers[3].toBN(); + return publicKey.elgamal.decrypt(c1, c2, p, x); } case 'ecdh': { const oid = keyIntegers[0]; @@ -263,8 +259,7 @@ export default { case 'rsa_encrypt': case 'rsa_encrypt_sign': case 'rsa_sign': { - const rsa = new publicKey.rsa(); - return rsa.generate(bits, "10001").then(function(keyObject) { + return publicKey.rsa.generate(bits, "10001").then(function(keyObject) { return constructParams( types, [keyObject.n, keyObject.ee, keyObject.d, keyObject.p, keyObject.q, keyObject.u] ); diff --git a/src/crypto/public_key/dsa.js b/src/crypto/public_key/dsa.js index 8d488e38..f432681d 100644 --- a/src/crypto/public_key/dsa.js +++ b/src/crypto/public_key/dsa.js @@ -86,7 +86,7 @@ export default { } break; } - return {r: r.fromRed(), s: s.fromRed()}; + return { r: r.fromRed(), s: s.fromRed() }; }, /* diff --git a/src/crypto/public_key/elgamal.js b/src/crypto/public_key/elgamal.js index 465dc16a..b81ee2b6 100644 --- a/src/crypto/public_key/elgamal.js +++ b/src/crypto/public_key/elgamal.js @@ -18,39 +18,41 @@ // ElGamal implementation /** - * @requires crypto/public_key/jsbn + * @requires bn.js * @requires crypto/random - * @requires util * @module crypto/public_key/elgamal */ -import BigInteger from './jsbn.js'; -import random from '../random.js'; -import util from '../../util.js'; +import BN from 'bn.js'; +import random from '../random'; -export default function Elgamal() { - function encrypt(m, g, p, y) { - // choose k in {2,...,p-2} - const pMinus2 = p.subtract(BigInteger.TWO); - let k = random.getRandomBigIntegerInRange(BigInteger.ONE, pMinus2); - k = k.mod(pMinus2).add(BigInteger.ONE); - const c = []; - c[0] = g.modPow(k, p); - c[1] = y.modPow(k, p).multiply(m).mod(p); - return c; +const one = new BN(1); + +export default { + /* + * m, p, g, y are all BN + * returns { c1: BN, c2: BN } + */ + encrypt: 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); + const k = random.getRandomBN(one, p); + return { + c1: gred.redPow(k).fromRed(), + c2: yred.redPow(k).redMul(mred).fromRed() + }; + }, + + /* + * c1, c2, p, x are all BN + * returns BN + */ + decrypt: 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(); } - - function decrypt(c1, c2, p, x) { - util.print_debug("Elgamal Decrypt:\nc1:" + util.hexstrdump(c1.toMPI()) + "\n" + - "c2:" + util.hexstrdump(c2.toMPI()) + "\n" + - "p:" + util.hexstrdump(p.toMPI()) + "\n" + - "x:" + util.hexstrdump(x.toMPI())); - return (c1.modPow(x, p).modInverse(p)).multiply(c2).mod(p); - //var c = c1.pow(x).modInverse(p); // c0^-a mod p - //return c.multiply(c2).mod(p); - } - - // signing and signature verification using Elgamal is not required by OpenPGP. - this.encrypt = encrypt; - this.decrypt = decrypt; -} +}; diff --git a/src/crypto/public_key/elliptic/ecdh.js b/src/crypto/public_key/elliptic/ecdh.js index 2bcaeb1b..8bdd1ba5 100644 --- a/src/crypto/public_key/elliptic/ecdh.js +++ b/src/crypto/public_key/elliptic/ecdh.js @@ -67,7 +67,7 @@ function kdf(hash_algo, X, length, param) { * @param {module:type/oid} oid Elliptic curve object identifier * @param {Enums} cipher_algo Symmetric cipher to use * @param {Enums} hash_algo Hash algorithm to use - * @param {Uint8Array} m Value derived from session key (RFC 6637) + * @param {module:type/mpi} m Value derived from session key (RFC 6637) * @param {Uint8Array} Q Recipient public key * @param {String} fingerprint Recipient fingerprint * @return {{V: BN, C: BN}} Returns ephemeral key and encoded session key @@ -81,7 +81,7 @@ async function encrypt(oid, cipher_algo, hash_algo, m, Q, fingerprint) { Q = curve.keyFromPublic(Q); const S = v.derive(Q); const Z = kdf(hash_algo, S, cipher[cipher_algo].keySize, param); - const C = aes_kw.wrap(Z, m.toBytes()); + const C = aes_kw.wrap(Z, m.toString()); return { V: new BN(v.getPublic()), C: C diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js index 635b00f6..f7effda3 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -18,6 +18,7 @@ // RSA implementation /** + * @requires bn.js * @requires asmcrypto.js * @requires crypto/public_key/jsbn * @requires crypto/random @@ -27,6 +28,7 @@ */ +import BN from 'bn.js'; import { RSA_RAW } from 'asmcrypto.js'; import BigInteger from './jsbn'; import random from '../random'; @@ -59,7 +61,7 @@ function unblind(t, n) { return t.multiply(unblinder).mod(n); } -export default function RSA() { +export default { /** * This function uses jsbn Big Num library to decrypt RSA * @param m message @@ -71,7 +73,12 @@ export default function RSA() { * @param u RSA u as BigInteger * @return {BigInteger} The decrypted value of the message */ - function decrypt(m, n, e, d, p, q, u) { + decrypt: function(m, n, e, d, p, q, u) { + const dd = new BN(d); + const dp = dd.mod(new BN(p).subn(1)).toArrayLike(Uint8Array); // d mod (p-1) + const dq = dd.mod(new BN(q).subn(1)).toArrayLike(Uint8Array); // d mod (q-1) + return new BN(RSA_RAW.decrypt(m, [n, e, d, q, p, dq, dp, u]).slice(1)); // FIXME remove slice +/* if (config.rsa_blinding) { m = blind(m, n, e); } @@ -89,53 +96,39 @@ export default function RSA() { } t = t.multiply(p).add(xp); -// var t = RSA.decrypt(m, [n, e, d, q, p, dq, dp, u]).slice(1) - if (config.rsa_blinding) { t = unblind(t, n); } return t; - } +*/ + }, /** * encrypt message * @param m message as BigInteger - * @param e public MPI part as BigInteger * @param n public MPI part as BigInteger + * @param e public MPI part as BigInteger * @return BigInteger */ - function encrypt(m, e, n) { - return m.modPowInt(e, n); - } + encrypt: function(m, n, e) { +// return m.modPowInt(e, n); + return RSA_RAW.encrypt(m, [n, e]); + }, /* Sign and Verify */ - function sign(m, d, n) { - return m.modPow(d, n); -// return RSA_RAW.sign(m, [n, e, d]); - } + sign: function(m, n, e, d) { +// return m.modPow(d, n); + return RSA_RAW.sign(m, [n, e, d]); + }, - function verify(x, e, n) { - return x.modPowInt(e, n); -// return RSA_RAW.verify(x, [n, e]); - } - - // "empty" RSA key constructor - - function KeyObject() { - this.n = null; - this.e = 0; - this.ee = null; - this.d = null; - this.p = null; - this.q = null; - this.dmp1 = null; - this.dmq1 = null; - this.u = null; - } + verify: function(x, n, e) { +// return x.modPowInt(e, n); + return RSA_RAW.verify(x, [n, e]); + }, // Generate a new random private key B bits long, using public expt E - function generate(B, E) { + generate: function(B, E) { const webCrypto = util.getWebCryptoAll(); // @@ -189,6 +182,20 @@ export default function RSA() { }); } + // "empty" RSA key constructor + + function KeyObject() { + this.n = null; + this.e = 0; + this.ee = null; + this.d = null; + this.p = null; + this.q = null; + this.dmp1 = null; + this.dmq1 = null; + this.u = null; + } + function exportKey(keypair) { // export the generated keys as JsonWebKey (JWK) // https://tools.ietf.org/html/draft-ietf-jose-json-web-key-33 @@ -263,11 +270,4 @@ export default function RSA() { resolve(key); }); } - - this.encrypt = encrypt; - this.decrypt = decrypt; - this.verify = verify; - this.sign = sign; - this.generate = generate; - this.keyObject = KeyObject; -} +}; diff --git a/src/crypto/signature.js b/src/crypto/signature.js index bb4a10b3..6b7bc82a 100644 --- a/src/crypto/signature.js +++ b/src/crypto/signature.js @@ -6,7 +6,7 @@ * @module crypto/signature */ -import { RSA_RAW } from 'asmcrypto.js'; +// FIXME wrap rsa.js around this import publicKey from './public_key'; import pkcs1 from './pkcs1'; import util from '../util'; @@ -32,7 +32,7 @@ export default { const m = msg_MPIs[0].toUint8Array(); const n = publickey_MPIs[0].toUint8Array(); const e = publickey_MPIs[1].toUint8Array(); - const EM = RSA_RAW.verify(m, [n, e]); + const EM = publicKey.rsa.verify(m, n, e); const EM2 = pkcs1.emsa.encode(hash_algo, util.Uint8Array2str(data), n.length); return util.hexidump(EM) === EM2; } @@ -93,7 +93,8 @@ export default { const m = util.hex2Uint8Array( '00'+pkcs1.emsa.encode(hash_algo, data, n.length) // FIXME remove '00' ); - return util.Uint8Array2MPI(RSA_RAW.sign(m, [n, e, d])); + const signature = publicKey.rsa.sign(m, n, e, d); + return util.Uint8Array2MPI(signature); } case 17: { // DSA (Digital Signature Algorithm) [FIPS186] [HAC] diff --git a/src/packet/public_key.js b/src/packet/public_key.js index 58f037d9..87d89720 100644 --- a/src/packet/public_key.js +++ b/src/packet/public_key.js @@ -182,7 +182,7 @@ PublicKey.prototype.getFingerprint = function () { } else if (this.version === 3) { const paramCount = crypto.getPubKeyParamTypes(this.algorithm).length; for (let i = 0; i < paramCount; i++) { - toHash += this.params[i].toBytes(); + toHash += this.params[i].toString(); } this.fingerprint = util.Uint8Array2str(crypto.hash.md5(util.str2Uint8Array(toHash))); } diff --git a/src/packet/public_key_encrypted_session_key.js b/src/packet/public_key_encrypted_session_key.js index 2a109974..06f55a17 100644 --- a/src/packet/public_key_encrypted_session_key.js +++ b/src/packet/public_key_encrypted_session_key.js @@ -134,7 +134,7 @@ PublicKeyEncryptedSessionKey.prototype.decrypt = async function (key) { key.params, this.encrypted, key.fingerprint - )).toBytes(); + )).toString(); let checksum; let decoded; diff --git a/src/type/mpi.js b/src/type/mpi.js index 496e5a5c..1620cba0 100644 --- a/src/type/mpi.js +++ b/src/type/mpi.js @@ -44,12 +44,14 @@ import util from '../util'; */ export default function MPI(data) { /** An implementation dependent integer */ - if (data instanceof BigInteger) { + if (data instanceof BN) { + this.fromBN(data); + } else if (data instanceof BigInteger) { this.fromBigInteger(data); - } else if (data instanceof BN) { - this.fromBytes(util.Uint8Array2str(data.toArrayLike(Uint8Array))); + } else if (util.isUint8Array(data)) { + this.fromUint8Array(data); } else if (util.isString(data)) { - this.fromBytes(data); + this.fromString(data); } else { this.data = null; } @@ -57,8 +59,8 @@ export default function MPI(data) { /** * Parsing function for a mpi ({@link https://tools.ietf.org/html/rfc4880#section3.2|RFC 4880 3.2}). - * @param {String} input Payload of mpi data - * @param {String} endian Endianness of the payload; 'be' for big-endian and 'le' for little-endian + * @param {Uint8Array} input Payload of mpi data + * @param {String} endian Endianness of the payload; 'be' for big-endian or 'le' for little-endian * @return {Integer} Length of data read */ MPI.prototype.read = function (bytes, endian='be') { @@ -81,7 +83,7 @@ MPI.prototype.read = function (bytes, endian='be') { const bytelen = Math.ceil(bits / 8); let payload = bytes.subarray(2, 2 + bytelen); if (endian === 'le') { - payload = new Uint8Array(payload).reverse(); + payload = Uint8Array.from(payload).reverse(); } const raw = util.Uint8Array2str(payload); this.fromBytes(raw); @@ -89,47 +91,58 @@ MPI.prototype.read = function (bytes, endian='be') { return 2 + bytelen; }; -MPI.prototype.fromBytes = function (bytes) { - this.data = new BigInteger(util.hexstrdump(bytes), 16); +/** + * Converts the mpi object to a bytes as specified in + * {@link https://tools.ietf.org/html/rfc4880#section-3.2|RFC4880 3.2} + * @param {String} endian Endianness of the payload; 'be' for big-endian or 'le' for little-endian + * @param {Integer} length Length of the data part of the MPI + * @return {Uint8Aray} mpi Byte representation + */ +MPI.prototype.write = function (endian, length) { + return util.Uint8Array2MPI(this.data.toArrayLike(Uint8Array, endian, length)); }; -MPI.prototype.toBytes = function () { - return util.Uint8Array2str(this.toUint8Array()); +MPI.prototype.byteLength = function () { + return this.write().length - 2; }; MPI.prototype.toUint8Array = function () { return this.write().slice(2); }; -MPI.prototype.byteLength = function () { - return this.toBytes().length; +MPI.prototype.fromUint8Array = function (bytes) { + this.data = new BN(bytes); }; -/** - * Converts the mpi object to a bytes as specified in {@link https://tools.ietf.org/html/rfc4880#section-3.2|RFC4880 3.2} - * @return {Uint8Aray} mpi Byte representation - */ -MPI.prototype.write = function () { - return util.str2Uint8Array(this.data.toMPI()); +MPI.prototype.toString = function () { + return util.Uint8Array2str(this.toUint8Array()); }; -MPI.prototype.toBigInteger = function () { +MPI.prototype.fromString = function (str) { + this.data = new BN(util.str2Uint8Array(str)); +}; + +// TODO remove this +MPI.prototype.fromBytes = MPI.prototype.fromString; + +MPI.prototype.toBN = function () { return this.data.clone(); }; -MPI.prototype.toBN = function (bn) { - return new BN(this.write().slice(2)); -}; - -MPI.prototype.fromBigInteger = function (bn) { +MPI.prototype.fromBN = function (bn) { this.data = bn.clone(); }; -MPI.fromClone = function (clone) { - clone.data.copyTo = BigInteger.prototype.copyTo; - const bn = new BigInteger(); - clone.data.copyTo(bn); - const mpi = new MPI(); - mpi.data = bn; - return mpi; +MPI.prototype.toBigInteger = function () { + return new BigInteger(util.hexidump(this.write()), 16); +}; + +MPI.prototype.fromBigInteger = function (bn) { + this.data = new BN(bn.toByteArray()); +}; + +MPI.fromClone = function (clone) { + const bn = new BN(); + clone.data.copy(bn); + return new MPI(bn); }; diff --git a/test/general/packet.js b/test/general/packet.js index 35276a4f..d4a54464 100644 --- a/test/general/packet.js +++ b/test/general/packet.js @@ -171,7 +171,7 @@ describe("Packet", function() { }); it('Public key encrypted symmetric key packet', function() { - const rsa = new openpgp.crypto.publicKey.rsa(); + const rsa = openpgp.crypto.publicKey.rsa; const keySize = openpgp.util.getWebCryptoAll() ? 2048 : 512; // webkit webcrypto accepts minimum 2048 bit keys return rsa.generate(keySize, "10001").then(function(mpiGen) { @@ -435,7 +435,7 @@ describe("Packet", function() { const key = new openpgp.packet.List(); key.push(new openpgp.packet.SecretKey()); - const rsa = new openpgp.crypto.publicKey.rsa(); + const rsa = openpgp.crypto.publicKey.rsa; const keySize = openpgp.util.getWebCryptoAll() ? 2048 : 512; // webkit webcrypto accepts minimum 2048 bit keys return rsa.generate(keySize, "10001").then(function(mpiGen) { @@ -463,7 +463,7 @@ describe("Packet", function() { it('Writing and verification of a signature packet.', function() { const key = new openpgp.packet.SecretKey(); - const rsa = new openpgp.crypto.publicKey.rsa(); + const rsa = openpgp.crypto.publicKey.rsa; const keySize = openpgp.util.getWebCryptoAll() ? 2048 : 512; // webkit webcrypto accepts minimum 2048 bit keys return rsa.generate(keySize, "10001").then(function(mpiGen) {