Use Web Crypto & Node crypto for RSA signing and verifying (#999)
Also, when generating RSA keys in JS, generate them with p < q, as per the spec. Also, when generating RSA keys using Web Crypto or Node crypto, swap the generated p and q around, so that will satisfy p < q in most browsers (but not old Microsoft Edge, 50% of the time) and so that we can use the generated u coefficient (p^-1 mod q in OpenPGP, q^-1 mod p in RFC3447). Then, when signing and verifying, swap p and q again, so that the key hopefully satisfies Safari's requirement that p > q, and so that we can keep using u again.
This commit is contained in:
parent
e20d727d76
commit
6e7f399eb3
|
@ -114,7 +114,7 @@ module.exports = {
|
|||
"init-declarations": "off",
|
||||
"jsx-quotes": "error",
|
||||
"key-spacing": "off",
|
||||
"keyword-spacing": "off",
|
||||
"keyword-spacing": "error",
|
||||
"line-comment-position": "off",
|
||||
"linebreak-style": [
|
||||
"error",
|
||||
|
|
|
@ -184,7 +184,7 @@ module.exports = function(grunt) {
|
|||
}
|
||||
},
|
||||
eslint: {
|
||||
target: ['src/**/*.js', './Gruntfile.js'],
|
||||
target: ['src/**/*.js', './Gruntfile.js', 'test/crypto/rsa.js'],
|
||||
options: {
|
||||
configFile: '.eslintrc.js',
|
||||
fix: !!grunt.option('fix')
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
/**
|
||||
* @fileoverview Provides functions for asymmetric encryption and decryption as
|
||||
* well as key generation and parameter handling for all public-key cryptosystems.
|
||||
* @requires bn.js
|
||||
* @requires crypto/public_key
|
||||
* @requires crypto/cipher
|
||||
* @requires crypto/random
|
||||
|
@ -33,7 +32,6 @@
|
|||
* @module crypto/crypto
|
||||
*/
|
||||
|
||||
import BN from 'bn.js';
|
||||
import publicKey from './public_key';
|
||||
import cipher from './cipher';
|
||||
import random from './random';
|
||||
|
@ -92,7 +90,7 @@ export default {
|
|||
const kdf_params = pub_params[2];
|
||||
const { publicKey: V, wrappedKey: C } = await publicKey.elliptic.ecdh.encrypt(
|
||||
oid, kdf_params.cipher, kdf_params.hash, data, Q, fingerprint);
|
||||
return constructParams(types, [new BN(V), C]);
|
||||
return constructParams(types, [V, C]);
|
||||
}
|
||||
default:
|
||||
return [];
|
||||
|
|
|
@ -38,7 +38,7 @@ const tagLength = 16;
|
|||
|
||||
function ntz(n) {
|
||||
let ntz = 0;
|
||||
for(let i = 1; (n & i) === 0; i <<= 1) {
|
||||
for (let i = 1; (n & i) === 0; i <<= 1) {
|
||||
ntz++;
|
||||
}
|
||||
return ntz;
|
||||
|
|
|
@ -98,7 +98,7 @@ export default {
|
|||
* @param {BN} p
|
||||
* @param {BN} q
|
||||
* @param {BN} y
|
||||
* @returns BN
|
||||
* @returns {boolean}
|
||||
* @async
|
||||
*/
|
||||
verify: async function(hash_algo, r, s, hashed, g, p, q, y) {
|
||||
|
|
|
@ -209,7 +209,7 @@ Curve.prototype.genKeyPair = async function () {
|
|||
keyPair = await indutnyCurve.genKeyPair({
|
||||
entropy: util.Uint8Array_to_str(await random.getRandomBytes(32))
|
||||
});
|
||||
return { publicKey: keyPair.getPublic('array', false), privateKey: keyPair.getPrivate().toArray() };
|
||||
return { publicKey: new Uint8Array(keyPair.getPublic('array', false)), privateKey: keyPair.getPrivate().toArrayLike(Uint8Array) };
|
||||
};
|
||||
|
||||
async function generate(curve) {
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
*/
|
||||
|
||||
import BN from 'bn.js';
|
||||
import stream from 'web-stream-tools';
|
||||
import enums from '../../../enums';
|
||||
import util from '../../../util';
|
||||
import Curve, { webCurves, privateToJwk, rawPublicToJwk } from './curves';
|
||||
|
@ -49,8 +48,7 @@ const nodeCrypto = util.getNodeCrypto();
|
|||
*/
|
||||
async function sign(oid, hash_algo, message, publicKey, privateKey, hashed) {
|
||||
const curve = new Curve(oid);
|
||||
if (message && !message.locked) {
|
||||
message = await stream.readToEnd(message);
|
||||
if (message && !util.isStream(message)) {
|
||||
const keyPair = { publicKey, privateKey };
|
||||
switch (curve.type) {
|
||||
case 'web': {
|
||||
|
@ -89,8 +87,7 @@ async function sign(oid, hash_algo, message, publicKey, privateKey, hashed) {
|
|||
*/
|
||||
async function verify(oid, hash_algo, signature, message, publicKey, hashed) {
|
||||
const curve = new Curve(oid);
|
||||
if (message && !message.locked) {
|
||||
message = await stream.readToEnd(message);
|
||||
if (message && !util.isStream(message)) {
|
||||
switch (curve.type) {
|
||||
case 'web':
|
||||
try {
|
||||
|
|
|
@ -30,6 +30,12 @@ import prime from './prime';
|
|||
import random from '../random';
|
||||
import config from '../../config';
|
||||
import util from '../../util';
|
||||
import pkcs1 from '../pkcs1';
|
||||
import enums from '../../enums';
|
||||
|
||||
const webCrypto = util.getWebCrypto();
|
||||
const nodeCrypto = util.getNodeCrypto();
|
||||
const asn1 = nodeCrypto ? require('asn1.js') : undefined;
|
||||
|
||||
// Helper for IE11 KeyOperation objects
|
||||
function promisifyIE11Op(keyObj, err) {
|
||||
|
@ -47,8 +53,8 @@ function promisifyIE11Op(keyObj, err) {
|
|||
}
|
||||
|
||||
/* eslint-disable no-invalid-this */
|
||||
const RSAPrivateKey = util.detectNode() ? require('asn1.js').define('RSAPrivateKey', function () {
|
||||
this.seq().obj( // used for native NodeJS keygen
|
||||
const RSAPrivateKey = util.detectNode() ? asn1.define('RSAPrivateKey', function () {
|
||||
this.seq().obj( // used for native NodeJS crypto
|
||||
this.key('version').int(), // 0
|
||||
this.key('modulus').int(), // n
|
||||
this.key('publicExponent').int(), // e
|
||||
|
@ -60,39 +66,68 @@ const RSAPrivateKey = util.detectNode() ? require('asn1.js').define('RSAPrivateK
|
|||
this.key('coefficient').int() // u
|
||||
);
|
||||
}) : undefined;
|
||||
|
||||
const RSAPublicKey = util.detectNode() ? asn1.define('RSAPubliceKey', function () {
|
||||
this.seq().obj( // used for native NodeJS crypto
|
||||
this.key('modulus').int(), // n
|
||||
this.key('publicExponent').int(), // e
|
||||
);
|
||||
}) : undefined;
|
||||
/* eslint-enable no-invalid-this */
|
||||
|
||||
export default {
|
||||
/** Create signature
|
||||
* @param {BN} m message
|
||||
* @param {BN} n RSA public modulus
|
||||
* @param {BN} e RSA public exponent
|
||||
* @param {BN} d RSA private exponent
|
||||
* @returns {BN} RSA Signature
|
||||
* @param {module:enums.hash} hash_algo Hash algorithm
|
||||
* @param {Uint8Array} data message
|
||||
* @param {Uint8Array} n RSA public modulus
|
||||
* @param {Uint8Array} e RSA public exponent
|
||||
* @param {Uint8Array} d RSA private exponent
|
||||
* @param {Uint8Array} p RSA private prime p
|
||||
* @param {Uint8Array} q RSA private prime q
|
||||
* @param {Uint8Array} u RSA private coefficient
|
||||
* @param {Uint8Array} hashed hashed message
|
||||
* @returns {Uint8Array} RSA Signature
|
||||
* @async
|
||||
*/
|
||||
sign: async function(m, n, e, d) {
|
||||
if (n.cmp(m) <= 0) {
|
||||
throw new Error('Message size cannot exceed modulus size');
|
||||
sign: async function(hash_algo, data, n, e, d, p, q, u, hashed) {
|
||||
if (data && !util.isStream(data)) {
|
||||
if (webCrypto) {
|
||||
try {
|
||||
return await this.webSign(enums.read(enums.webHash, hash_algo), data, n, e, d, p, q, u);
|
||||
} catch (err) {
|
||||
util.print_debug_error(err);
|
||||
}
|
||||
} else if (nodeCrypto) {
|
||||
return this.nodeSign(hash_algo, data, n, e, d, p, q, u);
|
||||
}
|
||||
}
|
||||
const nred = new BN.red(n);
|
||||
return m.toRed(nred).redPow(d).toArrayLike(Uint8Array, 'be', n.byteLength());
|
||||
return this.bnSign(hash_algo, n, d, hashed);
|
||||
},
|
||||
|
||||
/**
|
||||
* Verify signature
|
||||
* @param {BN} s signature
|
||||
* @param {BN} n RSA public modulus
|
||||
* @param {BN} e RSA public exponent
|
||||
* @returns {BN}
|
||||
* @param {module:enums.hash} hash_algo Hash algorithm
|
||||
* @param {Uint8Array} data message
|
||||
* @param {Uint8Array} s signature
|
||||
* @param {Uint8Array} n RSA public modulus
|
||||
* @param {Uint8Array} e RSA public exponent
|
||||
* @param {Uint8Array} hashed hashed message
|
||||
* @returns {Boolean}
|
||||
* @async
|
||||
*/
|
||||
verify: async function(s, n, e) {
|
||||
if (n.cmp(s) <= 0) {
|
||||
throw new Error('Signature size cannot exceed modulus size');
|
||||
verify: async function(hash_algo, data, s, n, e, hashed) {
|
||||
if (data && !util.isStream(data)) {
|
||||
if (webCrypto) {
|
||||
try {
|
||||
return await this.webVerify(enums.read(enums.webHash, hash_algo), data, s, n, e);
|
||||
} catch (err) {
|
||||
util.print_debug_error(err);
|
||||
}
|
||||
} else if (nodeCrypto) {
|
||||
return this.nodeVerify(hash_algo, data, s, n, e);
|
||||
}
|
||||
}
|
||||
const nred = new BN.red(n);
|
||||
return s.toRed(nred).redPow(e).toArrayLike(Uint8Array, 'be', n.byteLength());
|
||||
return this.bnVerify(hash_algo, s, n, e, hashed);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -119,7 +154,7 @@ export default {
|
|||
* @param {BN} d RSA private exponent
|
||||
* @param {BN} p RSA private prime p
|
||||
* @param {BN} q RSA private prime q
|
||||
* @param {BN} u RSA private inverse of prime q
|
||||
* @param {BN} u RSA private coefficient
|
||||
* @returns {BN} RSA Plaintext
|
||||
* @async
|
||||
*/
|
||||
|
@ -171,8 +206,6 @@ export default {
|
|||
generate: async function(B, E) {
|
||||
let key;
|
||||
E = new BN(E, 16);
|
||||
const webCrypto = util.getWebCryptoAll();
|
||||
const nodeCrypto = util.getNodeCrypto();
|
||||
|
||||
// Native RSA keygen using Web Crypto
|
||||
if (webCrypto) {
|
||||
|
@ -214,15 +247,16 @@ export default {
|
|||
if (jwk instanceof ArrayBuffer) {
|
||||
jwk = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(jwk)));
|
||||
}
|
||||
|
||||
// map JWK parameters to BN
|
||||
key = {};
|
||||
key.n = new BN(util.b64_to_Uint8Array(jwk.n));
|
||||
key.e = E;
|
||||
key.d = new BN(util.b64_to_Uint8Array(jwk.d));
|
||||
key.p = new BN(util.b64_to_Uint8Array(jwk.p));
|
||||
key.q = new BN(util.b64_to_Uint8Array(jwk.q));
|
||||
key.u = key.p.invm(key.q);
|
||||
// switch p and q
|
||||
key.p = new BN(util.b64_to_Uint8Array(jwk.q));
|
||||
key.q = new BN(util.b64_to_Uint8Array(jwk.p));
|
||||
// Since p and q are switched in places, we could keep u
|
||||
key.u = new BN(util.b64_to_Uint8Array(jwk.qi));
|
||||
return key;
|
||||
} else if (nodeCrypto && nodeCrypto.generateKeyPair && RSAPrivateKey) {
|
||||
const opts = {
|
||||
|
@ -238,26 +272,29 @@ export default {
|
|||
resolve(RSAPrivateKey.decode(der, 'der'));
|
||||
}
|
||||
}));
|
||||
/** PGP spec differs from DER spec, DER: `(inverse of q) mod p`, PGP: `(inverse of p) mod q`.
|
||||
* @link https://tools.ietf.org/html/rfc3447#section-3.2
|
||||
* @link https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-08#section-5.6.1
|
||||
*/
|
||||
return {
|
||||
n: prv.modulus,
|
||||
e: prv.publicExponent,
|
||||
d: prv.privateExponent,
|
||||
p: prv.prime1,
|
||||
q: prv.prime2,
|
||||
dp: prv.exponent1,
|
||||
dq: prv.exponent2,
|
||||
// re-compute `u` because PGP spec differs from DER spec, DER: `(inverse of q) mod p`, PGP: `(inverse of p) mod q`
|
||||
u: prv.prime1.invm(prv.prime2) // PGP type of u
|
||||
// switch p and q
|
||||
p: prv.prime2,
|
||||
q: prv.prime1,
|
||||
// Since p and q are switched in places, we could keep u
|
||||
u: prv.coefficient // PGP type of u
|
||||
};
|
||||
}
|
||||
|
||||
// RSA keygen fallback using 40 iterations of the Miller-Rabin test
|
||||
// See https://stackoverflow.com/a/6330138 for justification
|
||||
// Also see section C.3 here: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST
|
||||
let p = await prime.randomProbablePrime(B - (B >> 1), E, 40);
|
||||
let q = await prime.randomProbablePrime(B >> 1, E, 40);
|
||||
let q = await prime.randomProbablePrime(B - (B >> 1), E, 40);
|
||||
let p = await prime.randomProbablePrime(B >> 1, E, 40);
|
||||
|
||||
if (p.cmp(q) < 0) {
|
||||
if (q.cmp(p) < 0) {
|
||||
[p, q] = [q, p];
|
||||
}
|
||||
|
||||
|
@ -274,5 +311,161 @@ export default {
|
|||
};
|
||||
},
|
||||
|
||||
bnSign: async function (hash_algo, n, d, hashed) {
|
||||
n = new BN(n);
|
||||
const m = new BN(await pkcs1.emsa.encode(hash_algo, hashed, n.byteLength()), 16);
|
||||
d = new BN(d);
|
||||
if (n.cmp(m) <= 0) {
|
||||
throw new Error('Message size cannot exceed modulus size');
|
||||
}
|
||||
const nred = new BN.red(n);
|
||||
return m.toRed(nred).redPow(d).toArrayLike(Uint8Array, 'be', n.byteLength());
|
||||
},
|
||||
|
||||
webSign: async function (hash_name, data, n, e, d, p, q, u) {
|
||||
// OpenPGP keys require that p < q, and Safari Web Crypto requires that p > q.
|
||||
// We swap them in privateToJwk, so it usually works out, but nevertheless,
|
||||
// not all OpenPGP keys are compatible with this requirement.
|
||||
// OpenPGP.js used to generate RSA keys the wrong way around (p > q), and still
|
||||
// does if the underlying Web Crypto does so (e.g. old MS Edge 50% of the time).
|
||||
const jwk = privateToJwk(n, e, d, p, q, u);
|
||||
const algo = {
|
||||
name: "RSASSA-PKCS1-v1_5",
|
||||
hash: { name: hash_name }
|
||||
};
|
||||
const key = await webCrypto.importKey("jwk", jwk, algo, false, ["sign"]);
|
||||
// add hash field for ms edge support
|
||||
return new Uint8Array(await webCrypto.sign({ "name": "RSASSA-PKCS1-v1_5", "hash": hash_name }, key, data));
|
||||
},
|
||||
|
||||
nodeSign: async function (hash_algo, data, n, e, d, p, q, u) {
|
||||
const pBNum = new BN(p);
|
||||
const qBNum = new BN(q);
|
||||
const dBNum = new BN(d);
|
||||
const dq = dBNum.mod(qBNum.subn(1)); // d mod (q-1)
|
||||
const dp = dBNum.mod(pBNum.subn(1)); // d mod (p-1)
|
||||
const sign = nodeCrypto.createSign(enums.read(enums.hash, hash_algo));
|
||||
sign.write(data);
|
||||
sign.end();
|
||||
const keyObject = {
|
||||
version: 0,
|
||||
modulus: new BN(n),
|
||||
publicExponent: new BN(e),
|
||||
privateExponent: new BN(d),
|
||||
// switch p and q
|
||||
prime1: new BN(q),
|
||||
prime2: new BN(p),
|
||||
// switch dp and dq
|
||||
exponent1: dq,
|
||||
exponent2: dp,
|
||||
coefficient: new BN(u)
|
||||
};
|
||||
if (typeof nodeCrypto.createPrivateKey !== 'undefined') { //from version 11.6.0 Node supports der encoded key objects
|
||||
const der = RSAPrivateKey.encode(keyObject, 'der');
|
||||
return new Uint8Array(sign.sign({ key: der, format: 'der', type: 'pkcs1' }));
|
||||
}
|
||||
const pem = RSAPrivateKey.encode(keyObject, 'pem', {
|
||||
label: 'RSA PRIVATE KEY'
|
||||
});
|
||||
return new Uint8Array(sign.sign(pem));
|
||||
},
|
||||
|
||||
bnVerify: async function (hash_algo, s, n, e, hashed) {
|
||||
n = new BN(n);
|
||||
s = new BN(s);
|
||||
e = new BN(e);
|
||||
if (n.cmp(s) <= 0) {
|
||||
throw new Error('Signature size cannot exceed modulus size');
|
||||
}
|
||||
const nred = new BN.red(n);
|
||||
const EM1 = s.toRed(nred).redPow(e).toArrayLike(Uint8Array, 'be', n.byteLength());
|
||||
const EM2 = await pkcs1.emsa.encode(hash_algo, hashed, n.byteLength());
|
||||
return util.Uint8Array_to_hex(EM1) === EM2;
|
||||
},
|
||||
|
||||
webVerify: async function (hash_name, data, s, n, e) {
|
||||
const jwk = publicToJwk(n, e);
|
||||
const key = await webCrypto.importKey("jwk", jwk, {
|
||||
name: "RSASSA-PKCS1-v1_5",
|
||||
hash: { name: hash_name }
|
||||
}, false, ["verify"]);
|
||||
// add hash field for ms edge support
|
||||
return webCrypto.verify({ "name": "RSASSA-PKCS1-v1_5", "hash": hash_name }, key, s, data);
|
||||
},
|
||||
|
||||
nodeVerify: async function (hash_algo, data, s, n, e) {
|
||||
const verify = nodeCrypto.createVerify(enums.read(enums.hash, hash_algo));
|
||||
verify.write(data);
|
||||
verify.end();
|
||||
const keyObject = {
|
||||
modulus: new BN(n),
|
||||
publicExponent: new BN(e)
|
||||
};
|
||||
let key;
|
||||
if (typeof nodeCrypto.createPrivateKey !== 'undefined') { //from version 11.6.0 Node supports der encoded key objects
|
||||
const der = RSAPublicKey.encode(keyObject, 'der');
|
||||
key = { key: der, format: 'der', type: 'pkcs1' };
|
||||
} else {
|
||||
key = RSAPublicKey.encode(keyObject, 'pem', {
|
||||
label: 'RSA PUBLIC KEY'
|
||||
});
|
||||
}
|
||||
try {
|
||||
return await verify.verify(key, s);
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
prime: prime
|
||||
};
|
||||
|
||||
/** Convert Openpgp private key params to jwk key according to
|
||||
* @link https://tools.ietf.org/html/rfc7517
|
||||
* @param {String} hash_algo
|
||||
* @param {Uint8Array} n
|
||||
* @param {Uint8Array} e
|
||||
* @param {Uint8Array} d
|
||||
* @param {Uint8Array} p
|
||||
* @param {Uint8Array} q
|
||||
* @param {Uint8Array} u
|
||||
*/
|
||||
function privateToJwk(n, e, d, p, q, u) {
|
||||
const pBNum = new BN(p);
|
||||
const qBNum = new BN(q);
|
||||
const dBNum = new BN(d);
|
||||
|
||||
let dq = dBNum.mod(qBNum.subn(1)); // d mod (q-1)
|
||||
let dp = dBNum.mod(pBNum.subn(1)); // d mod (p-1)
|
||||
dp = dp.toArrayLike(Uint8Array);
|
||||
dq = dq.toArrayLike(Uint8Array);
|
||||
return {
|
||||
kty: 'RSA',
|
||||
n: util.Uint8Array_to_b64(n, true),
|
||||
e: util.Uint8Array_to_b64(e, true),
|
||||
d: util.Uint8Array_to_b64(d, true),
|
||||
// switch p and q
|
||||
p: util.Uint8Array_to_b64(q, true),
|
||||
q: util.Uint8Array_to_b64(p, true),
|
||||
// switch dp and dq
|
||||
dp: util.Uint8Array_to_b64(dq, true),
|
||||
dq: util.Uint8Array_to_b64(dp, true),
|
||||
qi: util.Uint8Array_to_b64(u, true),
|
||||
ext: true
|
||||
};
|
||||
}
|
||||
|
||||
/** Convert Openpgp key public params to jwk key according to
|
||||
* @link https://tools.ietf.org/html/rfc7517
|
||||
* @param {String} hash_algo
|
||||
* @param {Uint8Array} n
|
||||
* @param {Uint8Array} e
|
||||
*/
|
||||
function publicToJwk(n, e) {
|
||||
return {
|
||||
kty: 'RSA',
|
||||
n: util.Uint8Array_to_b64(n, true),
|
||||
e: util.Uint8Array_to_b64(e, true),
|
||||
ext: true
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,18 +1,14 @@
|
|||
/**
|
||||
* @fileoverview Provides functions for asymmetric signing and signature verification
|
||||
* @requires bn.js
|
||||
* @requires crypto/crypto
|
||||
* @requires crypto/public_key
|
||||
* @requires crypto/pkcs1
|
||||
* @requires enums
|
||||
* @requires util
|
||||
* @module crypto/signature
|
||||
*/
|
||||
|
||||
import BN from 'bn.js';
|
||||
import crypto from './crypto';
|
||||
import publicKey from './public_key';
|
||||
import pkcs1 from './pkcs1';
|
||||
import enums from '../enums';
|
||||
import util from '../util';
|
||||
|
||||
|
@ -40,12 +36,10 @@ export default {
|
|||
case enums.publicKey.rsa_encrypt_sign:
|
||||
case enums.publicKey.rsa_encrypt:
|
||||
case enums.publicKey.rsa_sign: {
|
||||
const m = msg_MPIs[0].toBN();
|
||||
const n = pub_MPIs[0].toBN();
|
||||
const e = pub_MPIs[1].toBN();
|
||||
const EM = await publicKey.rsa.verify(m, n, e);
|
||||
const EM2 = await pkcs1.emsa.encode(hash_algo, hashed, n.byteLength());
|
||||
return util.Uint8Array_to_hex(EM) === EM2;
|
||||
const m = msg_MPIs[0].toUint8Array();
|
||||
const n = pub_MPIs[0].toUint8Array();
|
||||
const e = pub_MPIs[1].toUint8Array();
|
||||
return publicKey.rsa.verify(hash_algo, data, m, n, e, hashed);
|
||||
}
|
||||
case enums.publicKey.dsa: {
|
||||
const r = msg_MPIs[0].toBN();
|
||||
|
@ -99,11 +93,13 @@ export default {
|
|||
case enums.publicKey.rsa_encrypt_sign:
|
||||
case enums.publicKey.rsa_encrypt:
|
||||
case enums.publicKey.rsa_sign: {
|
||||
const n = key_params[0].toBN();
|
||||
const e = key_params[1].toBN();
|
||||
const d = key_params[2].toBN();
|
||||
const m = new BN(await pkcs1.emsa.encode(hash_algo, hashed, n.byteLength()), 16);
|
||||
const signature = await publicKey.rsa.sign(m, n, e, d);
|
||||
const n = key_params[0].toUint8Array();
|
||||
const e = key_params[1].toUint8Array();
|
||||
const d = key_params[2].toUint8Array();
|
||||
const p = key_params[3].toUint8Array();
|
||||
const q = key_params[4].toUint8Array();
|
||||
const u = key_params[5].toUint8Array();
|
||||
const signature = await publicKey.rsa.sign(hash_algo, data, n, e, d, p, q, u, hashed);
|
||||
return util.Uint8Array_to_MPI(signature);
|
||||
}
|
||||
case enums.publicKey.dsa: {
|
||||
|
|
|
@ -275,7 +275,7 @@ function dearmor(input) {
|
|||
}
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
return;
|
||||
}
|
||||
|
@ -307,7 +307,7 @@ function dearmor(input) {
|
|||
}
|
||||
await writer.ready;
|
||||
await writer.close();
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
await writer.abort(e);
|
||||
}
|
||||
}));
|
||||
|
@ -325,11 +325,11 @@ function dearmor(input) {
|
|||
}
|
||||
await writer.ready;
|
||||
await writer.close();
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
await writer.abort(e);
|
||||
}
|
||||
});
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -598,7 +598,7 @@ Message.prototype.verify = async function(keys, date = new Date(), streaming) {
|
|||
await reader.readToEnd();
|
||||
await writer.ready;
|
||||
await writer.close();
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
onePassSigList.forEach(onePassSig => {
|
||||
onePassSig.correspondingSigReject(e);
|
||||
});
|
||||
|
|
|
@ -432,7 +432,6 @@ export function decrypt({ message, privateKeys, passwords, sessionKeys, publicKe
|
|||
export function sign({ message, privateKeys, armor = true, streaming = message && message.fromStream, detached = false, date = new Date(), fromUserIds = [] }) {
|
||||
checkCleartextOrMessage(message);
|
||||
privateKeys = toArray(privateKeys); fromUserIds = toArray(fromUserIds);
|
||||
|
||||
if (asyncProxy) { // use web worker if available
|
||||
return asyncProxy.delegate('sign', {
|
||||
message, privateKeys, armor, streaming, detached, date, fromUserIds
|
||||
|
@ -678,7 +677,7 @@ async function prepareSignatures(signatures) {
|
|||
signature.signature = await signature.signature;
|
||||
try {
|
||||
signature.valid = await signature.verified;
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
signature.valid = null;
|
||||
signature.error = e;
|
||||
util.print_debug_error(e);
|
||||
|
@ -699,7 +698,7 @@ function onError(message, error) {
|
|||
// update error message
|
||||
try {
|
||||
error.message = message + ': ' + error.message;
|
||||
} catch(e) {}
|
||||
} catch (e) {}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ function verificationObjectToClone(verObject) {
|
|||
try {
|
||||
await verified;
|
||||
delete packets[0].signature;
|
||||
} catch(e) {}
|
||||
} catch (e) {}
|
||||
return packets;
|
||||
});
|
||||
} else {
|
||||
|
|
|
@ -237,7 +237,7 @@ export default {
|
|||
}
|
||||
}
|
||||
}
|
||||
} while(wasPartialLength);
|
||||
} while (wasPartialLength);
|
||||
|
||||
// If this was not a packet that "supports streaming", we peek to check
|
||||
// whether it is the last packet in the message. We peek 2 bytes instead
|
||||
|
@ -283,7 +283,7 @@ export default {
|
|||
await callback({ tag, packet });
|
||||
}
|
||||
return !nextPacket || !nextPacket.length;
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
if (writer) {
|
||||
await writer.abort(e);
|
||||
return true;
|
||||
|
|
|
@ -68,7 +68,7 @@ List.prototype.read = async function (bytes, streaming) {
|
|||
return;
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
await writer.abort(e);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -346,7 +346,7 @@ SecretKey.prototype.decrypt = async function (passphrase) {
|
|||
try {
|
||||
const modeInstance = await mode(this.symmetric, key);
|
||||
cleartext = await modeInstance.decrypt(this.keyMaterial, this.iv.subarray(0, mode.ivLength), new Uint8Array());
|
||||
} catch(err) {
|
||||
} catch (err) {
|
||||
if (err.message === 'Authentication tag mismatch') {
|
||||
throw new Error('Incorrect key passphrase: ' + err.message);
|
||||
}
|
||||
|
|
|
@ -181,7 +181,6 @@ Signature.prototype.sign = async function (key, data, detached = false, streamin
|
|||
const hash = await this.hash(signatureType, data, toHash, detached);
|
||||
|
||||
this.signedHashValue = stream.slice(stream.clone(hash), 0, 2);
|
||||
|
||||
const params = key.params;
|
||||
const signed = async () => crypto.signature.sign(
|
||||
publicKeyAlgorithm, hashAlgorithm, params, toHash, await stream.readToEnd(hash)
|
||||
|
|
|
@ -182,7 +182,7 @@ SymEncryptedAEADProtected.prototype.crypt = async function (fn, key, data, strea
|
|||
break;
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
await writer.abort(e);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -61,7 +61,7 @@ Userid.prototype.read = function (bytes) {
|
|||
Userid.prototype.parse = function (userid) {
|
||||
try {
|
||||
Object.assign(this, util.parseUserId(userid));
|
||||
} catch(e) {}
|
||||
} catch (e) {}
|
||||
this.userid = userid;
|
||||
};
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ if (typeof window !== 'undefined') {
|
|||
if (typeof Object.assign === 'undefined') {
|
||||
require('core-js/fn/object/assign');
|
||||
}
|
||||
} catch(e) {}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
if (typeof TransformStream === 'undefined') {
|
||||
|
|
|
@ -86,7 +86,7 @@ export default {
|
|||
try {
|
||||
const result = await reader.read();
|
||||
port1.postMessage(result, util.getTransferables(result));
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
port1.postMessage({ error: e.message });
|
||||
}
|
||||
} else if (action === 'cancel') {
|
||||
|
@ -679,7 +679,7 @@ export default {
|
|||
try {
|
||||
const { name, address: email, comments } = emailAddresses.parseOneAddress({ input: userid, atInDisplayName: true });
|
||||
return { name, email, comment: comments.replace(/^\(|\)$/g, '') };
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
throw new Error('Invalid user id format');
|
||||
}
|
||||
},
|
||||
|
|
|
@ -156,7 +156,7 @@ AsyncProxy.prototype.delegate = function(method, options) {
|
|||
const requests = this.workers.map(worker => worker.requests);
|
||||
const minRequests = Math.min(...requests);
|
||||
let workerId = 0;
|
||||
for(; workerId < this.workers.length; workerId++) {
|
||||
for (; workerId < this.workers.length; workerId++) {
|
||||
if (this.workers[workerId].requests === minRequests) {
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -8,4 +8,5 @@ describe('Crypto', function () {
|
|||
require('./aes_kw.js');
|
||||
require('./eax.js');
|
||||
require('./ocb.js');
|
||||
require('./rsa.js');
|
||||
});
|
||||
|
|
141
test/crypto/rsa.js
Normal file
141
test/crypto/rsa.js
Normal file
|
@ -0,0 +1,141 @@
|
|||
const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp');
|
||||
const chai = require('chai');
|
||||
|
||||
chai.use(require('chai-as-promised'));
|
||||
|
||||
const expect = chai.expect;
|
||||
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-invalid-this */
|
||||
const native = openpgp.util.getWebCrypto() || openpgp.util.getNodeCrypto();
|
||||
(!native ? describe.skip : describe)('basic RSA cryptography with native crypto', function () {
|
||||
it('generate rsa key', async function() {
|
||||
const bits = openpgp.util.getWebCryptoAll() ? 2048 : 1024;
|
||||
const keyObject = await openpgp.crypto.publicKey.rsa.generate(bits, "10001");
|
||||
expect(keyObject.n).to.exist;
|
||||
expect(keyObject.e).to.exist;
|
||||
expect(keyObject.d).to.exist;
|
||||
expect(keyObject.p).to.exist;
|
||||
expect(keyObject.q).to.exist;
|
||||
expect(keyObject.u).to.exist;
|
||||
});
|
||||
|
||||
it('sign and verify using generated key params', async function() {
|
||||
const bits = openpgp.util.getWebCryptoAll() ? 2048 : 1024;
|
||||
const keyParams = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsa_sign, bits);
|
||||
const message = await openpgp.crypto.random.getRandomBytes(64);
|
||||
const hash_algo = openpgp.enums.write(openpgp.enums.hash, 'sha256');
|
||||
const hashed = await openpgp.crypto.hash.digest(hash_algo, message);
|
||||
const n = keyParams[0].toUint8Array();
|
||||
const e = keyParams[1].toUint8Array();
|
||||
const d = keyParams[2].toUint8Array();
|
||||
const p = keyParams[3].toUint8Array();
|
||||
const q = keyParams[4].toUint8Array();
|
||||
const u = keyParams[5].toUint8Array();
|
||||
const signature = await openpgp.crypto.publicKey.rsa.sign(hash_algo, message, n, e, d, p, q, u, hashed);
|
||||
expect(signature).to.exist;
|
||||
const verify = await openpgp.crypto.publicKey.rsa.verify(hash_algo, message, signature, n, e, hashed);
|
||||
expect(verify).to.be.true;
|
||||
});
|
||||
|
||||
it('compare webCrypto and bn math sign', async function() {
|
||||
if (!openpgp.util.getWebCrypto()) {
|
||||
this.skip();
|
||||
}
|
||||
const bits = openpgp.util.getWebCrypto() ? 2048 : 1024;
|
||||
const keyParams = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsa_sign, bits);
|
||||
const n = keyParams[0].toUint8Array();
|
||||
const e = keyParams[1].toUint8Array();
|
||||
const d = keyParams[2].toUint8Array();
|
||||
const p = keyParams[3].toUint8Array();
|
||||
const q = keyParams[4].toUint8Array();
|
||||
const u = keyParams[5].toUint8Array();
|
||||
const message = await openpgp.crypto.random.getRandomBytes(64);
|
||||
const hashName = 'sha256';
|
||||
const hash_algo = openpgp.enums.write(openpgp.enums.hash, hashName);
|
||||
const hashed = await openpgp.crypto.hash.digest(hash_algo, message);
|
||||
let signatureWeb;
|
||||
try {
|
||||
signatureWeb = await openpgp.crypto.publicKey.rsa.webSign('SHA-256', message, n, e, d, p, q, u, hashed);
|
||||
} catch (error) {
|
||||
openpgp.util.print_debug_error('web crypto error');
|
||||
this.skip();
|
||||
}
|
||||
const signatureBN = await openpgp.crypto.publicKey.rsa.bnSign(hash_algo, n, d, hashed);
|
||||
expect(openpgp.util.Uint8Array_to_hex(signatureWeb)).to.be.equal(openpgp.util.Uint8Array_to_hex(signatureBN));
|
||||
});
|
||||
|
||||
it('compare webCrypto and bn math verify', async function() {
|
||||
if (!openpgp.util.getWebCrypto()) {
|
||||
this.skip();
|
||||
}
|
||||
const bits = openpgp.util.getWebCrypto() ? 2048 : 1024;
|
||||
const keyParams = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsa_sign, bits);
|
||||
const n = keyParams[0].toUint8Array();
|
||||
const e = keyParams[1].toUint8Array();
|
||||
const d = keyParams[2].toUint8Array();
|
||||
const p = keyParams[3].toUint8Array();
|
||||
const q = keyParams[4].toUint8Array();
|
||||
const u = keyParams[5].toUint8Array();
|
||||
const message = await openpgp.crypto.random.getRandomBytes(64);
|
||||
const hashName = 'sha256';
|
||||
const hash_algo = openpgp.enums.write(openpgp.enums.hash, hashName);
|
||||
const hashed = await openpgp.crypto.hash.digest(hash_algo, message);
|
||||
let verifyWeb;
|
||||
let signature;
|
||||
try {
|
||||
signature = await openpgp.crypto.publicKey.rsa.webSign('SHA-256', message, n, e, d, p, q, u, hashed);
|
||||
verifyWeb = await openpgp.crypto.publicKey.rsa.webVerify('SHA-256', message, signature, n, e);
|
||||
} catch (error) {
|
||||
openpgp.util.print_debug_error('web crypto error');
|
||||
this.skip();
|
||||
}
|
||||
const verifyBN = await openpgp.crypto.publicKey.rsa.bnVerify(hash_algo, signature, n, e, hashed);
|
||||
expect(verifyWeb).to.be.true;
|
||||
expect(verifyBN).to.be.true;
|
||||
});
|
||||
|
||||
it('compare nodeCrypto and bn math sign', async function() {
|
||||
if (!openpgp.util.getNodeCrypto()) {
|
||||
this.skip();
|
||||
}
|
||||
const bits = 1024;
|
||||
const keyParams = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsa_sign, bits);
|
||||
const n = keyParams[0].toUint8Array();
|
||||
const e = keyParams[1].toUint8Array();
|
||||
const d = keyParams[2].toUint8Array();
|
||||
const p = keyParams[3].toUint8Array();
|
||||
const q = keyParams[4].toUint8Array();
|
||||
const u = keyParams[5].toUint8Array();
|
||||
const message = await openpgp.crypto.random.getRandomBytes(64);
|
||||
const hashName = 'sha256';
|
||||
const hash_algo = openpgp.enums.write(openpgp.enums.hash, hashName);
|
||||
const hashed = await openpgp.crypto.hash.digest(hash_algo, message);
|
||||
const signatureNode = await openpgp.crypto.publicKey.rsa.nodeSign(hash_algo, message, n, e, d, p, q, u);
|
||||
const signatureBN = await openpgp.crypto.publicKey.rsa.bnSign(hash_algo, n, d, hashed);
|
||||
expect(openpgp.util.Uint8Array_to_hex(signatureNode)).to.be.equal(openpgp.util.Uint8Array_to_hex(signatureBN));
|
||||
});
|
||||
|
||||
it('compare nodeCrypto and bn math verify', async function() {
|
||||
if (!openpgp.util.getNodeCrypto()) {
|
||||
this.skip();
|
||||
}
|
||||
const bits = openpgp.util.getWebCrypto() ? 2048 : 1024;
|
||||
const keyParams = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsa_sign, bits);
|
||||
const n = keyParams[0].toUint8Array();
|
||||
const e = keyParams[1].toUint8Array();
|
||||
const d = keyParams[2].toUint8Array();
|
||||
const p = keyParams[3].toUint8Array();
|
||||
const q = keyParams[4].toUint8Array();
|
||||
const u = keyParams[5].toUint8Array();
|
||||
const message = await openpgp.crypto.random.getRandomBytes(64);
|
||||
const hashName = 'sha256';
|
||||
const hash_algo = openpgp.enums.write(openpgp.enums.hash, hashName);
|
||||
const hashed = await openpgp.crypto.hash.digest(hash_algo, message);
|
||||
const signatureNode = await openpgp.crypto.publicKey.rsa.nodeSign(hash_algo, message, n, e, d, p, q, u);
|
||||
const verifyNode = await openpgp.crypto.publicKey.rsa.nodeVerify(hash_algo, message, signatureNode, n, e);
|
||||
const verifyBN = await openpgp.crypto.publicKey.rsa.bnVerify(hash_algo, signatureNode, n, e, hashed);
|
||||
expect(verifyNode).to.be.true;
|
||||
expect(verifyBN).to.be.true;
|
||||
});
|
||||
});
|
|
@ -248,12 +248,13 @@ EJ4QcD/oQ6x1M/8X/iKQCtxZP8RnlrbH7ExkNON5s5g=
|
|||
});
|
||||
it('Decrypt and verify message with leading zero in hash signed with old elliptic algorithm', async function () {
|
||||
//this test would not work with nodeCrypto, since message is signed with leading zero stripped from the hash
|
||||
const use_native = openpgp.config.use_native;
|
||||
openpgp.config.use_native = false;
|
||||
const juliet = await load_priv_key('juliet');
|
||||
const romeo = await load_pub_key('romeo');
|
||||
const msg = await openpgp.message.readArmored(data.romeo. message_encrypted_with_leading_zero_in_hash_signed_by_elliptic_with_old_implementation);
|
||||
const result = await openpgp.decrypt({privateKeys: juliet, publicKeys: [romeo], message: msg});
|
||||
|
||||
openpgp.config.use_native = use_native;
|
||||
expect(result).to.exist;
|
||||
expect(result.data).to.equal(data.romeo.message_with_leading_zero_in_hash_old_elliptic_implementation);
|
||||
expect(result.signatures).to.have.length(1);
|
||||
|
|
|
@ -76,6 +76,90 @@ const priv_key =
|
|||
|
||||
const passphrase = 'hello world';
|
||||
|
||||
const brainpoolPub = [
|
||||
'-----BEGIN PGP PUBLIC KEY BLOCK-----',
|
||||
'',
|
||||
'mHMEWq8ruRMJKyQDAwIIAQELAwMEhi/66JLo1vMhpytb1bYvBhd/aKHde2Zwke7r',
|
||||
'zWFTYBZQl/DUrpMrVAhkQhk5G3kqFWf98O/DpvVmY6EDr3IjmODWowNvGfC4Avc9',
|
||||
'rYRgV8GbMBUVLIS+ytS1YNpAKW4vtBlidW5ueSA8YnVubnlAYnVubnkuYnVubnk+',
|
||||
'iLAEExMKADgWIQSLliWLcmzBLxv2/X36PWTJvPM4vAUCWq8ruQIbAwULCQgHAwUV',
|
||||
'CgkICwUWAgMBAAIeAQIXgAAKCRD6PWTJvPM4vIcVAYCIO41QylZkb9W4FP+kd3bz',
|
||||
'b73xxwojWpCiw1bWV9Xe/dKA23DtCYhlmhF/Twjh9lkBfihHXs/negGMnqbA8TQF',
|
||||
'U1IvBflDcA7yj677lgLkze/yd5hg/ZVx7M8XyUzcEm9xi7h3BFqvK7kSCSskAwMC',
|
||||
'CAEBCwMDBCkGskA01sBvG/B1bl0EN+yxF6xPn74WQoAMm7K4n1PlZ1u8RWg+BJVG',
|
||||
'Kna/88ZGcT5BZSUvRrYWgqb4/SPAPea5C1p6UYd+C0C0dVf0FaGv5z0gCtc/+kwF',
|
||||
'3sLGLZh3rAMBCQmImAQYEwoAIBYhBIuWJYtybMEvG/b9ffo9ZMm88zi8BQJaryu5',
|
||||
'AhsMAAoJEPo9ZMm88zi8w1QBfR4k1d5ElME3ef7viE+Mud4qGv1ra56pKa86hS9+',
|
||||
'l262twTxe1hk08/FySeJW08P3wF/WrhCrE9UDD6FQiZk1lqekhd9bf84v6i5Smbi',
|
||||
'oml1QWkiI6BtbLD39Su6zQKR7u+Y',
|
||||
'=wB7z',
|
||||
'-----END PGP PUBLIC KEY BLOCK-----'
|
||||
].join('\n');
|
||||
|
||||
const brainpoolPriv = [
|
||||
'-----BEGIN PGP PRIVATE KEY BLOCK-----',
|
||||
'',
|
||||
'lNYEWq8ruRMJKyQDAwIIAQELAwMEhi/66JLo1vMhpytb1bYvBhd/aKHde2Zwke7r',
|
||||
'zWFTYBZQl/DUrpMrVAhkQhk5G3kqFWf98O/DpvVmY6EDr3IjmODWowNvGfC4Avc9',
|
||||
'rYRgV8GbMBUVLIS+ytS1YNpAKW4v/gcDAtyjmSfDquSq5ffphtkwJ56Zz5jc+jSm',
|
||||
'yZaPgmnPOwcgYhWy1g7BcBKYFPNKZlajnV4Rut2VUWkELwWrRmchX4ENJoAKZob0',
|
||||
'l/zjgOPug3FtEGirOPmvi7nOkjDEFNJwtBlidW5ueSA8YnVubnlAYnVubnkuYnVu',
|
||||
'bnk+iLAEExMKADgWIQSLliWLcmzBLxv2/X36PWTJvPM4vAUCWq8ruQIbAwULCQgH',
|
||||
'AwUVCgkICwUWAgMBAAIeAQIXgAAKCRD6PWTJvPM4vIcVAYCIO41QylZkb9W4FP+k',
|
||||
'd3bzb73xxwojWpCiw1bWV9Xe/dKA23DtCYhlmhF/Twjh9lkBfihHXs/negGMnqbA',
|
||||
'8TQFU1IvBflDcA7yj677lgLkze/yd5hg/ZVx7M8XyUzcEm9xi5zaBFqvK7kSCSsk',
|
||||
'AwMCCAEBCwMDBCkGskA01sBvG/B1bl0EN+yxF6xPn74WQoAMm7K4n1PlZ1u8RWg+',
|
||||
'BJVGKna/88ZGcT5BZSUvRrYWgqb4/SPAPea5C1p6UYd+C0C0dVf0FaGv5z0gCtc/',
|
||||
'+kwF3sLGLZh3rAMBCQn+BwMC6RvzFHWyKqPlVqrm6+j797Y9vHdZW1zixtmEK0Wg',
|
||||
'lvQRpZF8AbpSzk/XolsoeQyic1e18C6ubFZFw7cI7ekINiRu/OXOvBnTbc5TdbDi',
|
||||
'kKTuOkL+lEwWrUTEwdshbJ+ImAQYEwoAIBYhBIuWJYtybMEvG/b9ffo9ZMm88zi8',
|
||||
'BQJaryu5AhsMAAoJEPo9ZMm88zi8w1QBfR4k1d5ElME3ef7viE+Mud4qGv1ra56p',
|
||||
'Ka86hS9+l262twTxe1hk08/FySeJW08P3wF/WrhCrE9UDD6FQiZk1lqekhd9bf84',
|
||||
'v6i5Smbioml1QWkiI6BtbLD39Su6zQKR7u+Y',
|
||||
'=uGZP',
|
||||
'-----END PGP PRIVATE KEY BLOCK-----'
|
||||
].join('\n');
|
||||
|
||||
const brainpoolPass = '321';
|
||||
|
||||
const xPub = [
|
||||
'-----BEGIN PGP PUBLIC KEY BLOCK-----',
|
||||
'',
|
||||
'mDMEWkN+5BYJKwYBBAHaRw8BAQdAIGqj23Kp273IPkgjwA7ue5MDIRAfWLYRqnFy',
|
||||
'c2AFMcC0EUxpZ2h0IDxsaWdodEBzdW4+iJAEExYIADgWIQSGS0GuVELT3Rs0woce',
|
||||
'zfAmwCRYMAUCWkN+5AIbAwULCQgHAgYVCAkKCwIEFgIDAQIeAQIXgAAKCRAezfAm',
|
||||
'wCRYMLteAQCFZcl8kBxCH86wmqpc2+KtEA8l/hsfh7jd+JWuyFuuRAD7BOix8Vo1',
|
||||
'P/hv8qUYwSn3IRXPeGXucoWVoKGgxRd+zAO4OARaQ37kEgorBgEEAZdVAQUBAQdA',
|
||||
'L1KkHCFxtK1CgvZlInT/y6OQeCfXiYzd/i452t2ZR2ADAQgHiHgEGBYIACAWIQSG',
|
||||
'S0GuVELT3Rs0wocezfAmwCRYMAUCWkN+5AIbDAAKCRAezfAmwCRYMJ71AQDmoQTg',
|
||||
'36pfjrl82srS6XPRJxl3r/6lpWGaNij0VptB2wEA2V10ifOhnwILCw1qBle6On7a',
|
||||
'Ba257lrFM+cOSMaEsgo=',
|
||||
'=D8HS',
|
||||
'-----END PGP PUBLIC KEY BLOCK-----'
|
||||
].join('\n');
|
||||
|
||||
const xPriv = [
|
||||
'-----BEGIN PGP PRIVATE KEY BLOCK-----',
|
||||
'',
|
||||
'lIYEWkN+5BYJKwYBBAHaRw8BAQdAIGqj23Kp273IPkgjwA7ue5MDIRAfWLYRqnFy',
|
||||
'c2AFMcD+BwMCeaL+cNXzgI7uJQ7HBv53TAXO3y5uyJQMonkFtQtldL8YDbNP3pbd',
|
||||
'3zzo9fxU12bWAJyFwBlBWJqkrxZN+0jt0ElsG3kp+V67MESJkrRhKrQRTGlnaHQg',
|
||||
'PGxpZ2h0QHN1bj6IkAQTFggAOBYhBIZLQa5UQtPdGzTChx7N8CbAJFgwBQJaQ37k',
|
||||
'AhsDBQsJCAcCBhUICQoLAgQWAgMBAh4BAheAAAoJEB7N8CbAJFgwu14BAIVlyXyQ',
|
||||
'HEIfzrCaqlzb4q0QDyX+Gx+HuN34la7IW65EAPsE6LHxWjU/+G/ypRjBKfchFc94',
|
||||
'Ze5yhZWgoaDFF37MA5yLBFpDfuQSCisGAQQBl1UBBQEBB0AvUqQcIXG0rUKC9mUi',
|
||||
'dP/Lo5B4J9eJjN3+Ljna3ZlHYAMBCAf+BwMCvyW2D5Yx6dbujE3yHi1XQ9MbhOY5',
|
||||
'XRFFgYIUYzzi1qmaL+8Gr9zODsUdeO60XHnMXOmqVa6/sdx32TWo5s3sgS19kRUM',
|
||||
'D+pbxS/aZnxvrYh4BBgWCAAgFiEEhktBrlRC090bNMKHHs3wJsAkWDAFAlpDfuQC',
|
||||
'GwwACgkQHs3wJsAkWDCe9QEA5qEE4N+qX465fNrK0ulz0ScZd6/+paVhmjYo9Fab',
|
||||
'QdsBANlddInzoZ8CCwsNagZXujp+2gWtue5axTPnDkjGhLIK',
|
||||
'=wo91',
|
||||
'-----END PGP PRIVATE KEY BLOCK-----'
|
||||
].join('\n');
|
||||
|
||||
const xPass = 'sun';
|
||||
|
||||
|
||||
let privKey, pubKey, plaintext, data, i, canceled, expectedType, dataArrived;
|
||||
|
||||
function tests() {
|
||||
|
@ -223,6 +307,68 @@ function tests() {
|
|||
}
|
||||
});
|
||||
|
||||
it('Encrypt and decrypt larger message roundtrip using curve x25519 (allow_unauthenticated_stream=true)', async function() {
|
||||
let allow_unauthenticated_streamValue = openpgp.config.allow_unauthenticated_stream;
|
||||
openpgp.config.allow_unauthenticated_stream = true;
|
||||
const priv = (await openpgp.key.readArmored(xPriv)).keys[0];
|
||||
const pub = (await openpgp.key.readArmored(xPub)).keys[0];
|
||||
await priv.decrypt(xPass);
|
||||
try {
|
||||
const encrypted = await openpgp.encrypt({
|
||||
message: openpgp.message.fromBinary(data),
|
||||
publicKeys: pub,
|
||||
privateKeys: priv
|
||||
});
|
||||
|
||||
const msgAsciiArmored = encrypted.data;
|
||||
const message = await openpgp.message.readArmored(msgAsciiArmored);
|
||||
const decrypted = await openpgp.decrypt({
|
||||
publicKeys: pub,
|
||||
privateKeys: priv,
|
||||
message,
|
||||
format: 'binary'
|
||||
});
|
||||
expect(util.isStream(decrypted.data)).to.equal(expectedType);
|
||||
const reader = openpgp.stream.getReader(decrypted.data);
|
||||
expect(await reader.peekBytes(1024)).to.deep.equal(plaintext[0]);
|
||||
dataArrived();
|
||||
expect(await reader.readToEnd()).to.deep.equal(util.concatUint8Array(plaintext));
|
||||
} finally {
|
||||
openpgp.config.allow_unauthenticated_stream = allow_unauthenticated_streamValue;
|
||||
}
|
||||
});
|
||||
|
||||
it('Encrypt and decrypt larger message roundtrip using curve brainpool (allow_unauthenticated_stream=true)', async function() {
|
||||
let allow_unauthenticated_streamValue = openpgp.config.allow_unauthenticated_stream;
|
||||
openpgp.config.allow_unauthenticated_stream = true;
|
||||
const priv = (await openpgp.key.readArmored(brainpoolPriv)).keys[0];
|
||||
const pub = (await openpgp.key.readArmored(brainpoolPub)).keys[0];
|
||||
await priv.decrypt(brainpoolPass);
|
||||
try {
|
||||
const encrypted = await openpgp.encrypt({
|
||||
message: openpgp.message.fromBinary(data),
|
||||
publicKeys: pub,
|
||||
privateKeys: priv
|
||||
});
|
||||
|
||||
const msgAsciiArmored = encrypted.data;
|
||||
const message = await openpgp.message.readArmored(msgAsciiArmored);
|
||||
const decrypted = await openpgp.decrypt({
|
||||
publicKeys: pub,
|
||||
privateKeys: priv,
|
||||
message,
|
||||
format: 'binary'
|
||||
});
|
||||
expect(util.isStream(decrypted.data)).to.equal(expectedType);
|
||||
const reader = openpgp.stream.getReader(decrypted.data);
|
||||
expect(await reader.peekBytes(1024)).to.deep.equal(plaintext[0]);
|
||||
dataArrived();
|
||||
expect(await reader.readToEnd()).to.deep.equal(util.concatUint8Array(plaintext));
|
||||
} finally {
|
||||
openpgp.config.allow_unauthenticated_stream = allow_unauthenticated_streamValue;
|
||||
}
|
||||
});
|
||||
|
||||
it('Detect MDC modifications (allow_unauthenticated_stream=true)', async function() {
|
||||
let allow_unauthenticated_streamValue = openpgp.config.allow_unauthenticated_stream;
|
||||
openpgp.config.allow_unauthenticated_stream = true;
|
||||
|
@ -591,6 +737,7 @@ function tests() {
|
|||
message: openpgp.message.fromBinary(data),
|
||||
privateKeys: privKey,
|
||||
detached: true,
|
||||
streaming: expectedType
|
||||
});
|
||||
const sigArmored = await openpgp.stream.readToEnd(signed.signature);
|
||||
const signature = await openpgp.message.readArmored(sigArmored);
|
||||
|
@ -631,6 +778,66 @@ function tests() {
|
|||
expect(verified.signatures[0].valid).to.be.true;
|
||||
});
|
||||
|
||||
it('Detached sign small message using brainpool curve keys', async function() {
|
||||
dataArrived(); // Do not wait until data arrived.
|
||||
const data = new ReadableStream({
|
||||
async start(controller) {
|
||||
controller.enqueue(util.str_to_Uint8Array('hello '));
|
||||
controller.enqueue(util.str_to_Uint8Array('world'));
|
||||
controller.close();
|
||||
}
|
||||
});
|
||||
const priv = (await openpgp.key.readArmored(brainpoolPriv)).keys[0];
|
||||
const pub = (await openpgp.key.readArmored(brainpoolPub)).keys[0];
|
||||
await priv.decrypt(brainpoolPass);
|
||||
const signed = await openpgp.sign({
|
||||
message: openpgp.message.fromBinary(data),
|
||||
privateKeys: priv,
|
||||
detached: true,
|
||||
streaming: expectedType
|
||||
});
|
||||
const sigArmored = await openpgp.stream.readToEnd(signed.signature);
|
||||
const signature = await openpgp.message.readArmored(sigArmored);
|
||||
const verified = await openpgp.verify({
|
||||
signature,
|
||||
publicKeys: pub,
|
||||
message: openpgp.message.fromText('hello world')
|
||||
});
|
||||
expect(openpgp.util.decode_utf8(verified.data)).to.equal('hello world');
|
||||
expect(verified.signatures).to.exist.and.have.length(1);
|
||||
expect(verified.signatures[0].valid).to.be.true;
|
||||
});
|
||||
|
||||
it('Detached sign small message using x25519 curve keys', async function() {
|
||||
dataArrived(); // Do not wait until data arrived.
|
||||
const data = new ReadableStream({
|
||||
async start(controller) {
|
||||
controller.enqueue(util.str_to_Uint8Array('hello '));
|
||||
controller.enqueue(util.str_to_Uint8Array('world'));
|
||||
controller.close();
|
||||
}
|
||||
});
|
||||
const priv = (await openpgp.key.readArmored(xPriv)).keys[0];
|
||||
const pub = (await openpgp.key.readArmored(xPub)).keys[0];
|
||||
await priv.decrypt(xPass);
|
||||
const signed = await openpgp.sign({
|
||||
message: openpgp.message.fromBinary(data),
|
||||
privateKeys: priv,
|
||||
detached: true,
|
||||
streaming: expectedType
|
||||
});
|
||||
const sigArmored = await openpgp.stream.readToEnd(signed.signature);
|
||||
const signature = await openpgp.message.readArmored(sigArmored);
|
||||
const verified = await openpgp.verify({
|
||||
signature,
|
||||
publicKeys: pub,
|
||||
message: openpgp.message.fromText('hello world')
|
||||
});
|
||||
expect(openpgp.util.decode_utf8(verified.data)).to.equal('hello world');
|
||||
expect(verified.signatures).to.exist.and.have.length(1);
|
||||
expect(verified.signatures[0].valid).to.be.true;
|
||||
});
|
||||
|
||||
it("Detached sign is expected to pull entire input stream when we're not pulling signed stream", async function() {
|
||||
const signed = await openpgp.sign({
|
||||
message: openpgp.message.fromBinary(data),
|
||||
|
|
Loading…
Reference in New Issue
Block a user