From 1812166a539cac85fbe017a86dc61032e8b23bca Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 21 Feb 2018 14:37:57 -0800 Subject: [PATCH] RSA using asmcrypto with asmcrypto bignum --- src/crypto/crypto.js | 104 ++++---- src/crypto/public_key/dsa.js | 5 +- src/crypto/public_key/elgamal.js | 5 +- src/crypto/public_key/elliptic/curves.js | 17 +- src/crypto/public_key/rsa.js | 322 +++++++++-------------- src/crypto/signature.js | 20 +- src/key.js | 3 + src/packet/signature.js | 2 +- test/general/packet.js | 18 +- 9 files changed, 212 insertions(+), 284 deletions(-) diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index cfe4ae95..d4526020 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -18,7 +18,6 @@ // The GPG4Browsers crypto interface /** - * @requires bn.js * @requires crypto/public_key * @requires crypto/cipher * @requires crypto/random @@ -31,7 +30,6 @@ * @module crypto/crypto */ -import BN from 'bn.js'; import publicKey from './public_key'; import cipher from './cipher'; import random from './random'; @@ -70,11 +68,11 @@ export default { switch (algo) { case enums.publicKey.rsa_encrypt: case enums.publicKey.rsa_encrypt_sign: { - const m = data.toUint8Array(); - const n = pub_params[0].toUint8Array(); - const e = pub_params[1].toUint8Array(); + const m = data.toBN(); + const n = pub_params[0].toBN(); + const e = pub_params[1].toBN(); const res = await publicKey.rsa.encrypt(m, n, e); - return constructParams(types, [new BN(res)]); + return constructParams(types, [res]); } case enums.publicKey.elgamal: { const m = data.toBN(); @@ -116,13 +114,13 @@ export default { switch (algo) { case enums.publicKey.rsa_encrypt_sign: case enums.publicKey.rsa_encrypt: { - const c = data_params[0].toUint8Array(); - const n = key_params[0].toUint8Array(); // pq - const e = key_params[1].toUint8Array(); - const d = key_params[2].toUint8Array(); // de = 1 mod (p-1)(q-1) - const p = key_params[3].toUint8Array(); - const q = key_params[4].toUint8Array(); - const u = key_params[5].toUint8Array(); // q^-1 mod p + const c = data_params[0].toBN(); + const n = key_params[0].toBN(); // n = pq + const e = key_params[1].toBN(); + const d = key_params[2].toBN(); // de = 1 mod (p-1)(q-1) + const p = key_params[3].toBN(); + const q = key_params[4].toBN(); + const u = key_params[5].toBN(); // q^-1 mod p return publicKey.rsa.decrypt(c, n, e, d, p, q, u); } case enums.publicKey.elgamal: { @@ -153,28 +151,28 @@ export default { */ getPrivKeyParamTypes: function(algo) { switch (algo) { + // Algorithm-Specific Fields for RSA secret keys: + // - multiprecision integer (MPI) of RSA secret exponent d. + // - MPI of RSA secret prime value p. + // - MPI of RSA secret prime value q (p < q). + // - MPI of u, the multiplicative inverse of p, mod q. case enums.publicKey.rsa_encrypt: case enums.publicKey.rsa_encrypt_sign: case enums.publicKey.rsa_sign: - // Algorithm-Specific Fields for RSA secret keys: - // - multiprecision integer (MPI) of RSA secret exponent d. - // - MPI of RSA secret prime value p. - // - MPI of RSA secret prime value q (p < q). - // - MPI of u, the multiplicative inverse of p, mod q. return [type_mpi, type_mpi, type_mpi, type_mpi]; + // Algorithm-Specific Fields for Elgamal secret keys: + // - MPI of Elgamal secret exponent x. case enums.publicKey.elgamal: - // Algorithm-Specific Fields for Elgamal secret keys: - // - MPI of Elgamal secret exponent x. return [type_mpi]; + // Algorithm-Specific Fields for DSA secret keys: + // - MPI of DSA secret exponent x. case enums.publicKey.dsa: - // Algorithm-Specific Fields for DSA secret keys: - // - MPI of DSA secret exponent x. return [type_mpi]; + // Algorithm-Specific Fields for ECDSA or ECDH secret keys: + // - MPI of an integer representing the secret key. case enums.publicKey.ecdh: case enums.publicKey.ecdsa: case enums.publicKey.eddsa: - // Algorithm-Specific Fields for ECDSA or ECDH secret keys: - // - MPI of an integer representing the secret key. return [type_mpi]; default: throw new Error('Invalid public key encryption algorithm.'); @@ -186,37 +184,37 @@ export default { * @return {Array} The array of types */ getPubKeyParamTypes: function(algo) { - // Algorithm-Specific Fields for RSA public keys: - // - a multiprecision integer (MPI) of RSA public modulus n; - // - an MPI of RSA public encryption exponent e. switch (algo) { + // Algorithm-Specific Fields for RSA public keys: + // - a multiprecision integer (MPI) of RSA public modulus n; + // - an MPI of RSA public encryption exponent e. case enums.publicKey.rsa_encrypt: case enums.publicKey.rsa_encrypt_sign: case enums.publicKey.rsa_sign: return [type_mpi, type_mpi]; - // Algorithm-Specific Fields for Elgamal public keys: - // - MPI of Elgamal prime p; - // - MPI of Elgamal group generator g; - // - MPI of Elgamal public key value y (= g**x mod p where x is secret). + // Algorithm-Specific Fields for Elgamal public keys: + // - MPI of Elgamal prime p; + // - MPI of Elgamal group generator g; + // - MPI of Elgamal public key value y (= g**x mod p where x is secret). case enums.publicKey.elgamal: return [type_mpi, type_mpi, type_mpi]; - // Algorithm-Specific Fields for DSA public keys: - // - MPI of DSA prime p; - // - MPI of DSA group order q (q is a prime divisor of p-1); - // - MPI of DSA group generator g; - // - MPI of DSA public-key value y (= g**x mod p where x is secret). + // Algorithm-Specific Fields for DSA public keys: + // - MPI of DSA prime p; + // - MPI of DSA group order q (q is a prime divisor of p-1); + // - MPI of DSA group generator g; + // - MPI of DSA public-key value y (= g**x mod p where x is secret). case enums.publicKey.dsa: return [type_mpi, type_mpi, type_mpi, type_mpi]; - // Algorithm-Specific Fields for ECDSA/EdDSA public keys: - // - OID of curve; - // - MPI of EC point representing public key. + // Algorithm-Specific Fields for ECDSA/EdDSA public keys: + // - OID of curve; + // - MPI of EC point representing public key. case enums.publicKey.ecdsa: case enums.publicKey.eddsa: return [type_oid, type_mpi]; - // Algorithm-Specific Fields for ECDH public keys: - // - OID of curve; - // - MPI of EC point representing public key. - // - KDF: variable-length field containing KDF parameters. + // Algorithm-Specific Fields for ECDH public keys: + // - OID of curve; + // - MPI of EC point representing public key. + // - KDF: variable-length field containing KDF parameters. case enums.publicKey.ecdh: return [type_oid, type_mpi, type_kdf_params]; default: @@ -230,24 +228,22 @@ export default { */ getEncSessionKeyParamTypes: function(algo) { switch (algo) { - // Algorithm-Specific Fields for RSA encrypted session keys: - // - MPI of RSA encrypted value m**e mod n. + // Algorithm-Specific Fields for RSA encrypted session keys: + // - MPI of RSA encrypted value m**e mod n. case enums.publicKey.rsa_encrypt: case enums.publicKey.rsa_encrypt_sign: return [type_mpi]; - // Algorithm-Specific Fields for Elgamal encrypted session keys: - // - MPI of Elgamal value g**k mod p - // - MPI of Elgamal value m * y**k mod p + // Algorithm-Specific Fields for Elgamal encrypted session keys: + // - MPI of Elgamal value g**k mod p + // - MPI of Elgamal value m * y**k mod p case enums.publicKey.elgamal: return [type_mpi, type_mpi]; - - // Algorithm-Specific Fields for ECDH encrypted session keys: - // - MPI containing the ephemeral key used to establish the shared secret - // - ECDH Symmetric Key + // Algorithm-Specific Fields for ECDH encrypted session keys: + // - MPI containing the ephemeral key used to establish the shared secret + // - ECDH Symmetric Key case enums.publicKey.ecdh: return [type_mpi, type_ecdh_symkey]; - default: throw new Error('Invalid public key encryption algorithm.'); } @@ -267,7 +263,7 @@ export default { case enums.publicKey.rsa_sign: { return publicKey.rsa.generate(bits, "10001").then(function(keyObject) { return constructParams( - types, [keyObject.n, keyObject.ee, keyObject.d, keyObject.p, keyObject.q, keyObject.u] + types, [keyObject.n, keyObject.e, 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 b12f263e..77b770db 100644 --- a/src/crypto/public_key/dsa.js +++ b/src/crypto/public_key/dsa.js @@ -32,7 +32,7 @@ import random from '../random.js'; import config from '../../config'; import util from '../../util'; -const one = new BN(1); +const two = new BN(2); const zero = new BN(0); /* @@ -73,7 +73,8 @@ export default { // signature shall be recalculated. It is extremely unlikely that r = 0 // or s = 0 if signatures are generated properly. while (true) { - k = random.getRandomBN(one, q); + // TODO confirm this range + k = random.getRandomBN(two, q.subn(1)); // returns in [2, q-2] r = gred.redPow(k).fromRed().toRed(redq); // (g**k mod p) mod q if (zero.cmp(r) === 0) { continue; diff --git a/src/crypto/public_key/elgamal.js b/src/crypto/public_key/elgamal.js index b81ee2b6..2279620f 100644 --- a/src/crypto/public_key/elgamal.js +++ b/src/crypto/public_key/elgamal.js @@ -26,7 +26,7 @@ import BN from 'bn.js'; import random from '../random'; -const one = new BN(1); +const two = new BN(2); export default { /* @@ -38,7 +38,8 @@ export default { const mred = m.toRed(redp); const gred = g.toRed(redp); const yred = y.toRed(redp); - const k = random.getRandomBN(one, p); + // TODO confirm this range + const k = random.getRandomBN(two, p.subn(1)); // returns in [2, p-2] return { c1: gred.redPow(k).fromRed(), c2: yred.redPow(k).redMul(mred).fromRed() diff --git a/src/crypto/public_key/elliptic/curves.js b/src/crypto/public_key/elliptic/curves.js index cdaaed70..58569ffb 100644 --- a/src/crypto/public_key/elliptic/curves.js +++ b/src/crypto/public_key/elliptic/curves.js @@ -37,22 +37,19 @@ import base64 from '../../../encoding/base64'; const webCrypto = util.getWebCrypto(); const nodeCrypto = util.getNodeCrypto(); -let webCurves = {}; -let nodeCurves = {}; -webCurves = { +const nodeCurves = {}; +const webCurves = { 'p256': 'P-256', 'p384': 'P-384', 'p521': 'P-521' }; if (nodeCrypto) { const knownCurves = nodeCrypto.getCurves(); - nodeCurves = { - 'secp256k1': knownCurves.includes('secp256k1') ? 'secp256k1' : undefined, - 'p256': knownCurves.includes('prime256v1') ? 'prime256v1' : undefined, - 'p384': knownCurves.includes('secp384r1') ? 'secp384r1' : undefined, - 'p521': knownCurves.includes('secp521r1') ? 'secp521r1' : undefined - // TODO add more here - }; + nodeCurves.secp256k1 = knownCurves.includes('secp256k1') ? 'secp256k1' : undefined; + nodeCurves.p256 = knownCurves.includes('prime256v1') ? 'prime256v1' : undefined; + nodeCurves.p384 = knownCurves.includes('secp384r1') ? 'secp384r1' : undefined; + nodeCurves.p521 = knownCurves.includes('secp521r1') ? 'secp521r1' : undefined; + // TODO add more here } const curves = { diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js index f7effda3..e6c943a3 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -29,245 +29,181 @@ import BN from 'bn.js'; -import { RSA_RAW } from 'asmcrypto.js'; -import BigInteger from './jsbn'; +import { random as asmcrypto_random, RSA, RSA_RAW } from 'asmcrypto.js'; import random from '../random'; import config from '../../config'; import util from '../../util'; -function SecureRandom() { - function nextBytes(byteArray) { - for (let n = 0; n < byteArray.length; n++) { - byteArray[n] = random.getSecureRandomOctet(); - } - } - this.nextBytes = nextBytes; -} - -let blinder = BigInteger.ZERO; -let unblinder = BigInteger.ZERO; - -function blind(m, n, e) { - if (unblinder.bitLength() === n.bitLength()) { - unblinder = unblinder.square().mod(n); - } else { - unblinder = random.getRandomBigIntegerInRange(BigInteger.TWO, n); - } - blinder = unblinder.modInverse(n).modPow(e, n); - return m.multiply(blinder).mod(n); -} - -function unblind(t, n) { - return t.multiply(unblinder).mod(n); -} +const two = new BN(2); +const zero = new BN(0); export default { - /** - * This function uses jsbn Big Num library to decrypt RSA - * @param m message - * @param n RSA public modulus n as BigInteger - * @param e RSA public exponent as BigInteger - * @param d RSA d as BigInteger - * @param p RSA p as BigInteger - * @param q RSA q as BigInteger - * @param u RSA u as BigInteger - * @return {BigInteger} The decrypted value of the message + /** Create signature + * @param m message as BN + * @param n public MPI part as BN + * @param e public MPI part as BN + * @param d private MPI part as BN + * @return BN */ - 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); - } - const xp = m.mod(p).modPow(d.mod(p.subtract(BigInteger.ONE)), p); - const xq = m.mod(q).modPow(d.mod(q.subtract(BigInteger.ONE)), q); - util.print_debug("rsa.js decrypt\nxpn:" + util.hexstrdump(xp.toMPI()) + "\nxqn:" + util.hexstrdump(xq.toMPI())); - - let t = xq.subtract(xp); - if (t[0] === 0) { - t = xp.subtract(xq); - t = t.multiply(u).mod(q); - t = q.subtract(t); - } else { - t = t.multiply(u).mod(q); - } - t = t.multiply(p).add(xp); - - if (config.rsa_blinding) { - t = unblind(t, n); - } - return t; -*/ - }, - - /** - * encrypt message - * @param m message as BigInteger - * @param n public MPI part as BigInteger - * @param e public MPI part as BigInteger - * @return BigInteger - */ - encrypt: function(m, n, e) { -// return m.modPowInt(e, n); - return RSA_RAW.encrypt(m, [n, e]); - }, - - /* Sign and Verify */ sign: function(m, n, e, d) { -// return m.modPow(d, n); + m = m.toArrayLike(Uint8Array); + n = n.toArrayLike(Uint8Array); + e = e.toArrayLike(Uint8Array); + d = d.toArrayLike(Uint8Array); return RSA_RAW.sign(m, [n, e, d]); }, - verify: function(x, n, e) { -// return x.modPowInt(e, n); - return RSA_RAW.verify(x, [n, e]); + /** + * Verify signature + * @param s signature as BN + * @param n public MPI part as BN + * @param e public MPI part as BN + * @return BN + */ + verify: function(s, n, e) { + s = s.toArrayLike(Uint8Array); + n = n.toArrayLike(Uint8Array); + e = e.toArrayLike(Uint8Array); + return RSA_RAW.verify(s, [n, e]); }, - // Generate a new random private key B bits long, using public expt E + /** + * Encrypt message + * @param m message as BN + * @param n public MPI part as BN + * @param e public MPI part as BN + * @return BN + */ + encrypt: function(m, n, e) { + m = m.toArrayLike(Uint8Array); + n = n.toArrayLike(Uint8Array); + e = e.toArrayLike(Uint8Array); + return RSA_RAW.encrypt(m, [n, e]); + }, - generate: function(B, E) { + /** + * Decrypt RSA message + * @param m message as BN + * @param n RSA public modulus n as BN + * @param e RSA public exponent as BN + * @param d RSA d as BN + * @param p RSA p as BN + * @param q RSA q as BN + * @param u RSA u as BN + * @return {BN} The decrypted value of the message + */ + decrypt: function(m, n, e, d, p, q, u) { + let blinder = zero; + let unblinder = zero; + const nred = new BN.red(n); + + config.rsa_blinding = false; // FIXME + if (config.rsa_blinding) { + if (unblinder.bitLength() === n.bitLength()) { + unblinder = unblinder.sqr().mod(n); + } else { + unblinder = random.getRandomBN(two, n); + } + blinder = unblinder.toRed(nred).redInvm().redPow(e).fromRed(); + m = m.mul(blinder).mod(n); + } + + const dq = d.mod(q.subn(1)).toArrayLike(Uint8Array); // d mod (q-1) + const dp = d.mod(p.subn(1)).toArrayLike(Uint8Array); // d mod (p-1) + const nn = n.toArrayLike(Uint8Array); + m = m.toArrayLike(Uint8Array); + e = e.toArrayLike(Uint8Array); + d = d.toArrayLike(Uint8Array); + q = q.toArrayLike(Uint8Array); + p = p.toArrayLike(Uint8Array); + u = u.toArrayLike(Uint8Array); + let result = new BN(RSA_RAW.decrypt(m, [nn, e, d, q, p, dq, dp, u]).slice(1)); // FIXME remove slice + + if (config.rsa_blinding) { + result = result.mul(unblinder).mod(n); + } + + return result; + }, + + /** + * Generate a new random private key B bits long with public exponent E + */ + generate: async function(B, E) { + let key; + E = new BN(E, 16); const webCrypto = util.getWebCryptoAll(); - // // Native RSA keygen using Web Crypto - // - if (webCrypto) { - const Euint32 = new Uint32Array([parseInt(E, 16)]); // get integer of exponent - const Euint8 = new Uint8Array(Euint32.buffer); // get bytes of exponent + let keyPair; let keyGenOpt; - - let keys; + const Euint8 = E.toArrayLike(Uint8Array); // get bytes of exponent if ((window.crypto && window.crypto.subtle) || window.msCrypto) { // current standard spec keyGenOpt = { name: 'RSASSA-PKCS1-v1_5', modulusLength: B, // the specified keysize in bits - publicExponent: Euint8.subarray(0, 3), // take three bytes (max 65537) + publicExponent: Euint8, // take three bytes (max 65537) hash: { name: 'SHA-1' // not required for actual RSA keys, but for crypto api 'sign' and 'verify' } }; - - keys = webCrypto.generateKey(keyGenOpt, true, ['sign', 'verify']); - if (typeof keys.then !== 'function') { // IE11 KeyOperation - keys = util.promisifyIE11Op(keys, 'Error generating RSA key pair.'); - } - } - else if (window.crypto && window.crypto.webkitSubtle) { + keyPair = await webCrypto.generateKey(keyGenOpt, true, ['sign', 'verify']); + } else if (window.crypto && window.crypto.webkitSubtle) { // outdated spec implemented by old Webkit keyGenOpt = { name: 'RSA-OAEP', modulusLength: B, // the specified keysize in bits - publicExponent: Euint8.subarray(0, 3), // take three bytes (max 65537) + publicExponent: Euint8, // take three bytes (max 65537) hash: { name: 'SHA-1' // not required for actual RSA keys, but for crypto api 'sign' and 'verify' } }; - keys = webCrypto.generateKey(keyGenOpt, true, ['encrypt', 'decrypt']); - } - else { + keyPair = await webCrypto.generateKey(keyGenOpt, true, ['encrypt', 'decrypt']); + } else { throw new Error('Unknown WebCrypto implementation'); } - return keys.then(exportKey).then(function(key) { - if (key instanceof ArrayBuffer) { - // parse raw ArrayBuffer bytes to jwk/json (WebKit/Safari/IE11 quirk) - return decodeKey(JSON.parse(String.fromCharCode.apply(null, new Uint8Array(key)))); - } - return decodeKey(key); - }); - } - - // "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 - let key = webCrypto.exportKey('jwk', keypair.privateKey); - if (typeof key.then !== 'function') { // IE11 KeyOperation - key = util.promisifyIE11Op(key, 'Error exporting RSA key pair.'); - } - return key; - } + let jwk = await webCrypto.exportKey('jwk', keyPair.privateKey); - function decodeKey(jwk) { - // map JWK parameters to local BigInteger type system - const key = new KeyObject(); - key.n = toBigInteger(jwk.n); - key.ee = new BigInteger(E, 16); - key.d = toBigInteger(jwk.d); - key.p = toBigInteger(jwk.p); - key.q = toBigInteger(jwk.q); + // parse raw ArrayBuffer bytes to jwk/json (WebKit/Safari/IE11 quirk) + if (jwk instanceof ArrayBuffer) { + jwk = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(key))); + } + + // map JWK parameters to BN + key = {}; + key.n = b64toBN(jwk.n); + key.e = E; + key.d = b64toBN(jwk.d); + key.p = b64toBN(jwk.p); + key.q = b64toBN(jwk.q); key.u = key.p.modInverse(key.q); - - function toBigInteger(base64url) { - const base64 = base64url.replace(/\-/g, '+').replace(/_/g, '/'); - const hex = util.hexstrdump(atob(base64)); - return new BigInteger(hex, 16); - } - return key; } - // - // JS code - // + // TODO use this is ../../encoding/base64.js and ./elliptic/{key,curve}.js + function b64toBN(base64url) { + const base64 = base64url.replace(/\-/g, '+').replace(/_/g, '/'); + const hex = util.hexstrdump(atob(base64)); + return new BN(hex, 16); + } - return new Promise(function(resolve) { - const key = new KeyObject(); - const rng = new SecureRandom(); - const qs = B >> 1; - key.e = parseInt(E, 16); - key.ee = new BigInteger(E, 16); - - for (;;) { - for (;;) { - key.p = new BigInteger(B - qs, 1, rng); - if (key.p.subtract(BigInteger.ONE).gcd(key.ee).compareTo(BigInteger.ONE) === 0 && key.p.isProbablePrime(10)) { - break; - } - } - for (;;) { - key.q = new BigInteger(qs, 1, rng); - if (key.q.subtract(BigInteger.ONE).gcd(key.ee).compareTo(BigInteger.ONE) === 0 && key.q.isProbablePrime(10)) { - break; - } - } - if (key.p.compareTo(key.q) <= 0) { - const t = key.p; - key.p = key.q; - key.q = t; - } - const p1 = key.p.subtract(BigInteger.ONE); - const q1 = key.q.subtract(BigInteger.ONE); - const phi = p1.multiply(q1); - if (phi.gcd(key.ee).compareTo(BigInteger.ONE) === 0) { - key.n = key.p.multiply(key.q); - key.d = key.ee.modInverse(phi); - key.dmp1 = key.d.mod(p1); - key.dmq1 = key.d.mod(q1); - key.u = key.p.modInverse(key.q); - break; - } - } - - resolve(key); - }); + // asmcrypto fallback + await asmcrypto_random.seed(await random.getRandomBytes(1024)); // FIXME how much randomness? + key = await RSA.generateKey(B, E.toArrayLike(Uint8Array)); + return { + n: key[0], + e: key[1], + d: key[2], + q: key[3], + p: key[4], + // dq: key[5], + // dp: key[6], + u: key[7] + }; } }; diff --git a/src/crypto/signature.js b/src/crypto/signature.js index 3fd018f6..32319a9b 100644 --- a/src/crypto/signature.js +++ b/src/crypto/signature.js @@ -1,4 +1,5 @@ /** + * @requires bn.js * @requires crypto/public_key * @requires crypto/pkcs1 * @requires enums @@ -6,6 +7,7 @@ * @module crypto/signature */ +import BN from 'bn.js'; import publicKey from './public_key'; import pkcs1 from './pkcs1'; import enums from '../enums'; @@ -29,11 +31,11 @@ export default { case enums.publicKey.rsa_encrypt_sign: case enums.publicKey.rsa_encrypt: case enums.publicKey.rsa_sign: { - const m = msg_MPIs[0].toUint8Array(); - const n = pub_MPIs[0].toUint8Array(); - const e = pub_MPIs[1].toUint8Array(); + const m = msg_MPIs[0].toBN(); + const n = pub_MPIs[0].toBN(); + const e = pub_MPIs[1].toBN(); const EM = publicKey.rsa.verify(m, n, e); - const EM2 = pkcs1.emsa.encode(hash_algo, util.Uint8Array2str(data), n.length); + const EM2 = pkcs1.emsa.encode(hash_algo, util.Uint8Array2str(data), n.byteLength()); return util.hexidump(EM) === EM2; } case enums.publicKey.dsa: { @@ -78,13 +80,11 @@ export default { case enums.publicKey.rsa_encrypt_sign: case enums.publicKey.rsa_encrypt: case enums.publicKey.rsa_sign: { - const n = key_params[0].toUint8Array(); - const e = key_params[1].toUint8Array(); - const d = key_params[2].toUint8Array(); + const n = key_params[0].toBN(); + const e = key_params[1].toBN(); + const d = key_params[2].toBN(); data = util.Uint8Array2str(data); - const m = util.hex2Uint8Array( - '00'+pkcs1.emsa.encode(hash_algo, data, n.length) // FIXME remove '00' - ); + const m = new BN(pkcs1.emsa.encode(hash_algo, data, n.byteLength()), 16); const signature = publicKey.rsa.sign(m, n, e, d); return util.Uint8Array2MPI(signature); } diff --git a/src/key.js b/src/key.js index 93e5f2d9..5bf3de9a 100644 --- a/src/key.js +++ b/src/key.js @@ -696,6 +696,7 @@ Key.prototype.verifyPrimaryUser = async function(keys) { return; } const dataToVerify = { userid: user.userId || user.userAttribute, key: primaryKey }; + // TODO: fix the race condition, this should be a forEach await Promise.all(user.selfCertifications.map(async function(selfCertification) { // skip if certificate is not the most recent if ((selfCertification.isPrimaryUserID && @@ -703,6 +704,7 @@ Key.prototype.verifyPrimaryUser = async function(keys) { (!lastPrimaryUserID && selfCertification.created < lastCreated)) { return; } + // TODO break apart the .verify/isRevoked/isExpired checks // skip if certificates is not valid if (!(selfCertification.verified || await selfCertification.verify(primaryKey, dataToVerify)) || (selfCertification.revoked || await user.isRevoked(primaryKey, selfCertification)) || @@ -781,6 +783,7 @@ User.prototype.toPacketlist = function() { * @return {Boolean} True if the certificate is revoked */ User.prototype.isRevoked = async function(primaryKey, certificate, key) { + certificate.revoked = null; if (this.revocationCertifications) { const dataToVerify = { userid: this.userId || this.userAttribute, key: primaryKey }; // TODO clarify OpenPGP's behavior given an expired revocation signature diff --git a/src/packet/signature.js b/src/packet/signature.js index 015672f0..1baf0a7f 100644 --- a/src/packet/signature.js +++ b/src/packet/signature.js @@ -85,7 +85,7 @@ export default function Signature(date=new Date()) { this.signatureTargetHash = null; this.embeddedSignature = null; - this.verified = false; + this.verified = null; } /** diff --git a/test/general/packet.js b/test/general/packet.js index d4a54464..c614a633 100644 --- a/test/general/packet.js +++ b/test/general/packet.js @@ -176,11 +176,9 @@ describe("Packet", function() { return rsa.generate(keySize, "10001").then(function(mpiGen) { - let mpi = [mpiGen.n, mpiGen.ee, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u]; + let mpi = [mpiGen.n, mpiGen.e, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u]; mpi = mpi.map(function(k) { - const mpi = new openpgp.MPI(); - mpi.fromBigInteger(k); - return mpi; + return new openpgp.MPI(k); }); const enc = new openpgp.packet.PublicKeyEncryptedSessionKey(); @@ -439,11 +437,9 @@ describe("Packet", function() { const keySize = openpgp.util.getWebCryptoAll() ? 2048 : 512; // webkit webcrypto accepts minimum 2048 bit keys return rsa.generate(keySize, "10001").then(function(mpiGen) { - let mpi = [mpiGen.n, mpiGen.ee, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u]; + let mpi = [mpiGen.n, mpiGen.e, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u]; mpi = mpi.map(function(k) { - const mpi = new openpgp.MPI(); - mpi.fromBigInteger(k); - return mpi; + return new openpgp.MPI(k); }); key[0].params = mpi; @@ -467,11 +463,9 @@ describe("Packet", function() { const keySize = openpgp.util.getWebCryptoAll() ? 2048 : 512; // webkit webcrypto accepts minimum 2048 bit keys return rsa.generate(keySize, "10001").then(function(mpiGen) { - let mpi = [mpiGen.n, mpiGen.ee, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u]; + let mpi = [mpiGen.n, mpiGen.e, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u]; mpi = mpi.map(function(k) { - const mpi = new openpgp.MPI(); - mpi.fromBigInteger(k); - return mpi; + return new openpgp.MPI(k); }); key.params = mpi;