Add config parameter to top-level functions (#1241)

Refactor functions to take the configuration as a parameter.

This allows setting a config option for a single function call, whereas
setting `openpgp.config` could lead to concurrency-related issues when
multiple async function calls are made at the same time.

`openpgp.config` is used as default for unset config values in top-level
functions.
`openpgp.config` is used as default config object in low-level functions
(i.e., when calling a low-level function, it may be required to pass
`{ ...openpgp.config, modifiedConfig: modifiedValue }`).

Also,

- remove `config.rsaBlinding`: blinding is now always applied to RSA decryption
- remove `config.debug`: debugging mode can be enabled by setting
  `process.env.NODE_ENV = 'development'`
- remove `config.useNative`: native crypto is always used when available
This commit is contained in:
larabr
2021-02-26 20:04:54 +01:00
committed by GitHub
parent 15ee659c9c
commit 7f37a8aaca
51 changed files with 1361 additions and 1038 deletions

View File

@@ -242,33 +242,11 @@ module.exports = () => describe('API functional testing', function() {
await Promise.all(symmAlgos.map(async function(algo) {
const symmKey = await crypto.generateSessionKey(algo);
const IV = new Uint8Array(crypto.cipher[algo].blockSize);
const symmencData = await crypto.cfb.encrypt(algo, symmKey, util.strToUint8Array(plaintext), IV);
const symmencData = await crypto.cfb.encrypt(algo, symmKey, util.strToUint8Array(plaintext), IV, openpgp.config);
const text = util.uint8ArrayToStr(await crypto.cfb.decrypt(algo, symmKey, symmencData, new Uint8Array(crypto.cipher[algo].blockSize)));
expect(text).to.equal(plaintext);
}));
}
function testAESGCM(plaintext, nativeDecrypt) {
symmAlgos.forEach(function(algo) {
if (algo.substr(0,3) === 'aes') {
it(algo, async function() {
const key = await crypto.generateSessionKey(algo);
const iv = await crypto.random.getRandomBytes(crypto.gcm.ivLength);
let modeInstance = await crypto.gcm(algo, key);
const ciphertext = await modeInstance.encrypt(util.strToUint8Array(plaintext), iv);
openpgp.config.useNative = nativeDecrypt;
modeInstance = await crypto.gcm(algo, key);
const decrypted = await modeInstance.decrypt(util.strToUint8Array(util.uint8ArrayToStr(ciphertext)), iv);
const decryptedStr = util.uint8ArrayToStr(decrypted);
expect(decryptedStr).to.equal(plaintext);
});
}
});
}
it("Symmetric with OpenPGP CFB", async function () {
await testCFB("hello");
await testCFB("1234567");
@@ -276,45 +254,6 @@ module.exports = () => describe('API functional testing', function() {
await testCFB("12345678901234567890123456789012345678901234567890");
});
describe('Symmetric AES-GCM (native)', function() {
let useNativeVal;
beforeEach(function() {
useNativeVal = openpgp.config.useNative;
openpgp.config.useNative = true;
});
afterEach(function() {
openpgp.config.useNative = useNativeVal;
});
testAESGCM("12345678901234567890123456789012345678901234567890", true);
});
describe('Symmetric AES-GCM (asm.js fallback)', function() {
let useNativeVal;
beforeEach(function() {
useNativeVal = openpgp.config.useNative;
openpgp.config.useNative = false;
});
afterEach(function() {
openpgp.config.useNative = useNativeVal;
});
testAESGCM("12345678901234567890123456789012345678901234567890", false);
});
describe('Symmetric AES-GCM (native encrypt, asm.js decrypt)', function() {
let useNativeVal;
beforeEach(function() {
useNativeVal = openpgp.config.useNative;
openpgp.config.useNative = true;
});
afterEach(function() {
openpgp.config.useNative = useNativeVal;
});
testAESGCM("12345678901234567890123456789012345678901234567890", false);
});
it('Asymmetric using RSA with eme_pkcs1 padding', async function () {
const symmKey = await crypto.generateSessionKey('aes256');
return crypto.publicKeyEncrypt(algoRSA, RSAPublicParams, symmKey).then(RSAEncryptedData => {

View File

@@ -2,10 +2,10 @@
// Adapted from https://github.com/artjomb/cryptojs-extension/blob/8c61d159/test/eax.js
const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../..');
const EAX = require('../../src/crypto/eax');
const util = require('../../src/util');
const sandbox = require('sinon/lib/sinon/sandbox');
const chai = require('chai');
chai.use(require('chai-as-promised'));
@@ -125,30 +125,45 @@ function testAESEAX() {
});
}
module.exports = () => {
module.exports = () => describe('Symmetric AES-EAX', function() {
let sinonSandbox;
let getWebCryptoStub;
let getNodeCryptoStub;
const disableNative = () => {
enableNative();
// stubbed functions return undefined
getWebCryptoStub = sinonSandbox.stub(util, "getWebCrypto");
getNodeCryptoStub = sinonSandbox.stub(util, "getNodeCrypto");
};
const enableNative = () => {
getWebCryptoStub && getWebCryptoStub.restore();
getNodeCryptoStub && getNodeCryptoStub.restore();
};
describe('Symmetric AES-EAX (native)', function() {
let useNativeVal;
beforeEach(function() {
useNativeVal = openpgp.config.useNative;
openpgp.config.useNative = true;
beforeEach(function () {
sinonSandbox = sandbox.create();
enableNative();
});
afterEach(function() {
openpgp.config.useNative = useNativeVal;
afterEach(function () {
sinonSandbox.restore();
});
testAESEAX();
});
describe('Symmetric AES-EAX (asm.js fallback)', function() {
let useNativeVal;
beforeEach(function() {
useNativeVal = openpgp.config.useNative;
openpgp.config.useNative = false;
beforeEach(function () {
sinonSandbox = sandbox.create();
disableNative();
});
afterEach(function() {
openpgp.config.useNative = useNativeVal;
afterEach(function () {
sinonSandbox.restore();
});
testAESEAX();
});
};
});

View File

@@ -4,6 +4,7 @@ const KDFParams = require('../../src/type/kdf_params');
const elliptic_curves = require('../../src/crypto/public_key/elliptic');
const util = require('../../src/util');
const sandbox = require('sinon/lib/sinon/sandbox');
const chai = require('chai');
const elliptic_data = require('./elliptic_data');
@@ -133,133 +134,111 @@ module.exports = () => describe('ECDH key exchange @lightweight', function () {
]);
describe('ECDHE key generation', function () {
const ecdh = elliptic_curves.ecdh;
it('Invalid curve', async function () {
if (!openpgp.config.useIndutnyElliptic && !util.getNodeCrypto()) {
this.skip();
}
const { key: publicKey } = await openpgp.generateKey({ curve: "secp256k1", userIds: [{ name: 'Test' }] });
publicKey.subKeys[0].keyPacket.publicParams.Q = Q1;
publicKey.subKeys[0].keyPacket.fingerprint = fingerprint1;
await expect(
openpgp.encrypt({ publicKeys: [publicKey], message: openpgp.Message.fromText('test') })
const curve = new elliptic_curves.Curve('secp256k1');
const oid = new OID(curve.oid);
const kdfParams = new KDFParams({ hash: curve.hash, cipher: curve.cipher });
const data = util.strToUint8Array('test');
expect(
ecdh.encrypt(oid, kdfParams, data, Q1, fingerprint1)
).to.be.rejectedWith(Error, /Public key is not valid for specified curve|Failed to translate Buffer to a EC_POINT|Unknown point format/);
});
it('Invalid public part of ephemeral key and private key', async function () {
const { key: publicKey } = await openpgp.generateKey({ curve: "curve25519", userIds: [{ name: 'Test' }] });
publicKey.subKeys[0].keyPacket.publicParams.Q = Q1;
publicKey.subKeys[0].keyPacket.fingerprint = fingerprint1;
const { key: privateKey } = await openpgp.generateKey({ curve: "curve25519", userIds: [{ name: 'Test' }] });
privateKey.subKeys[0].keyPacket.publicParams.Q = Q2;
privateKey.subKeys[0].keyPacket.privateParams.d = d2;
privateKey.subKeys[0].keyPacket.fingerprint = fingerprint1;
const armoredMessage = await openpgp.encrypt({ publicKeys: [publicKey], message: openpgp.Message.fromText('test') });
it('Different keys', async function () {
const curve = new elliptic_curves.Curve('curve25519');
const oid = new OID(curve.oid);
const kdfParams = new KDFParams({ hash: curve.hash, cipher: curve.cipher });
const data = util.strToUint8Array('test');
const { publicKey: V, wrappedKey: C } = await ecdh.encrypt(oid, kdfParams, data, Q1, fingerprint1);
await expect(
openpgp.decrypt({ privateKeys: [privateKey], message: await openpgp.readMessage({ armoredMessage }) })
).to.be.rejectedWith('Error decrypting message: Key Data Integrity failed');
ecdh.decrypt(oid, kdfParams, V, C, Q2, d2, fingerprint1)
).to.be.rejectedWith(/Key Data Integrity failed/);
});
it('Invalid fingerprint', async function () {
const { key: publicKey } = await openpgp.generateKey({ curve: "curve25519", userIds: [{ name: 'Test' }] });
publicKey.subKeys[0].keyPacket.publicParams.Q = Q1;
publicKey.subKeys[0].keyPacket.fingerprint = fingerprint1;
const { key: privateKey } = await openpgp.generateKey({ curve: "curve25519", userIds: [{ name: 'Test' }] });
privateKey.subKeys[0].keyPacket.publicParams.Q = Q2;
privateKey.subKeys[0].keyPacket.privateParams.d = d2;
privateKey.subKeys[0].keyPacket.fingerprint = fingerprint2;
const armoredMessage = await openpgp.encrypt({ publicKeys: [publicKey], message: openpgp.Message.fromText('test') });
const curve = new elliptic_curves.Curve('curve25519');
const oid = new OID(curve.oid);
const kdfParams = new KDFParams({ hash: curve.hash, cipher: curve.cipher });
const data = util.strToUint8Array('test');
const { publicKey: V, wrappedKey: C } = await ecdh.encrypt(oid, kdfParams, data, Q2, fingerprint1);
await expect(
openpgp.decrypt({ privateKeys: [privateKey], message: await openpgp.readMessage({ armoredMessage }) })
).to.be.rejectedWith('Error decrypting message: Session key decryption failed');
});
it('Different keys', async function () {
const { key: publicKey } = await openpgp.generateKey({ curve: "curve25519", userIds: [{ name: 'Test' }] });
publicKey.subKeys[0].keyPacket.publicParams.Q = Q2;
publicKey.subKeys[0].keyPacket.fingerprint = fingerprint1;
const { key: privateKey } = await openpgp.generateKey({ curve: "curve25519", userIds: [{ name: 'Test' }] });
privateKey.subKeys[0].keyPacket.publicParams.Q = Q1;
privateKey.subKeys[0].keyPacket.privateParams.d = d1;
privateKey.subKeys[0].keyPacket.fingerprint = fingerprint1;
const armoredMessage = await openpgp.encrypt({ publicKeys: [publicKey], message: openpgp.Message.fromText('test') });
await expect(
openpgp.decrypt({ privateKeys: [privateKey], message: await openpgp.readMessage({ armoredMessage }) })
).to.be.rejectedWith('Error decrypting message: Key Data Integrity failed');
ecdh.decrypt(oid, kdfParams, V, C, Q2, d2, fingerprint2)
).to.be.rejectedWith(/Key Data Integrity failed/);
});
it('Successful exchange curve25519', async function () {
const { key: publicKey } = await openpgp.generateKey({ curve: "curve25519", userIds: [{ name: 'Test' }] });
publicKey.subKeys[0].keyPacket.publicParams.Q = Q1;
publicKey.subKeys[0].keyPacket.fingerprint = fingerprint1;
const { key: privateKey } = await openpgp.generateKey({ curve: "curve25519", userIds: [{ name: 'Test' }] });
privateKey.subKeys[0].keyPacket.publicParams.Q = Q1;
privateKey.subKeys[0].keyPacket.privateParams.d = d1;
privateKey.subKeys[0].keyPacket.fingerprint = fingerprint1;
const armoredMessage = await openpgp.encrypt({ publicKeys: [publicKey], message: openpgp.Message.fromText('test') });
expect((
await openpgp.decrypt({ privateKeys: [privateKey], message: await openpgp.readMessage({ armoredMessage }) })
).data).to.equal('test');
});
it('Successful exchange NIST P256', async function () {
const { key: publicKey } = await openpgp.generateKey({ curve: "p256", userIds: [{ name: 'Test' }] });
publicKey.subKeys[0].keyPacket.publicParams.Q = key_data.p256.pub;
publicKey.subKeys[0].keyPacket.fingerprint = fingerprint1;
const { key: privateKey } = await openpgp.generateKey({ curve: "p256", userIds: [{ name: 'Test' }] });
privateKey.subKeys[0].keyPacket.publicParams.Q = key_data.p256.pub;
privateKey.subKeys[0].keyPacket.privateParams.d = key_data.p256.priv;
privateKey.subKeys[0].keyPacket.fingerprint = fingerprint1;
const armoredMessage = await openpgp.encrypt({ publicKeys: [publicKey], message: openpgp.Message.fromText('test') });
expect((
await openpgp.decrypt({ privateKeys: [privateKey], message: await openpgp.readMessage({ armoredMessage }) })
).data).to.equal('test');
});
it('Successful exchange NIST P384', async function () {
const { key: publicKey } = await openpgp.generateKey({ curve: "p384", userIds: [{ name: 'Test' }] });
publicKey.subKeys[0].keyPacket.publicParams.Q = key_data.p384.pub;
publicKey.subKeys[0].keyPacket.fingerprint = fingerprint1;
const { key: privateKey } = await openpgp.generateKey({ curve: "p384", userIds: [{ name: 'Test' }] });
privateKey.subKeys[0].keyPacket.publicParams.Q = key_data.p384.pub;
privateKey.subKeys[0].keyPacket.privateParams.d = key_data.p384.priv;
privateKey.subKeys[0].keyPacket.fingerprint = fingerprint1;
const armoredMessage = await openpgp.encrypt({ publicKeys: [publicKey], message: openpgp.Message.fromText('test') });
expect((
await openpgp.decrypt({ privateKeys: [privateKey], message: await openpgp.readMessage({ armoredMessage }) })
).data).to.equal('test');
});
it('Successful exchange NIST P521', async function () {
const { key: publicKey } = await openpgp.generateKey({ curve: "p521", userIds: [{ name: 'Test' }] });
publicKey.subKeys[0].keyPacket.publicParams.Q = key_data.p521.pub;
publicKey.subKeys[0].keyPacket.fingerprint = fingerprint1;
const { key: privateKey } = await openpgp.generateKey({ curve: "p521", userIds: [{ name: 'Test' }] });
privateKey.subKeys[0].keyPacket.publicParams.Q = key_data.p521.pub;
privateKey.subKeys[0].keyPacket.privateParams.d = key_data.p521.priv;
privateKey.subKeys[0].keyPacket.fingerprint = fingerprint1;
const armoredMessage = await openpgp.encrypt({ publicKeys: [publicKey], message: openpgp.Message.fromText('test') });
expect((
await openpgp.decrypt({ privateKeys: [privateKey], message: await openpgp.readMessage({ armoredMessage }) })
).data).to.equal('test');
const curve = new elliptic_curves.Curve('curve25519');
const oid = new OID(curve.oid);
const kdfParams = new KDFParams({ hash: curve.hash, cipher: curve.cipher });
const data = util.strToUint8Array('test');
const { publicKey: V, wrappedKey: C } = await ecdh.encrypt(oid, kdfParams, data, Q1, fingerprint1);
expect(await ecdh.decrypt(oid, kdfParams, V, C, Q1, d1, fingerprint1)).to.deep.equal(data);
});
it('Comparing decrypting with useNative = true and false', async function () {
const names = ["p256", "p384", "p521"];
return Promise.all(names.map(async function (name) {
const { key: publicKey } = await openpgp.generateKey({ curve: name, userIds: [{ name: 'Test' }] });
publicKey.subKeys[0].keyPacket.publicParams.Q = key_data[name].pub;
publicKey.subKeys[0].keyPacket.fingerprint = fingerprint1;
const { key: privateKey } = await openpgp.generateKey({ curve: name, userIds: [{ name: 'Test' }] });
privateKey.subKeys[0].keyPacket.publicParams.Q = key_data[name].pub;
privateKey.subKeys[0].keyPacket.privateParams.d = key_data[name].priv;
privateKey.subKeys[0].keyPacket.fingerprint = fingerprint1;
const armoredMessage = await openpgp.encrypt({ publicKeys: [publicKey], message: openpgp.Message.fromText('test') });
expect((
await openpgp.decrypt({ privateKeys: [privateKey], message: await openpgp.readMessage({ armoredMessage }) })
).data).to.equal('test');
const useNative = openpgp.config.useNative;
openpgp.config.useNative = !useNative;
try {
expect((
await openpgp.decrypt({ privateKeys: [privateKey], message: await openpgp.readMessage({ armoredMessage }) })
).data).to.equal('test');
} finally {
openpgp.config.useNative = useNative;
}
}));
['p256', 'p384', 'p521'].forEach(curveName => {
it(`NIST ${curveName} - Successful exchange`, async function () {
const curve = new elliptic_curves.Curve(curveName);
const oid = new OID(curve.oid);
const kdfParams = new KDFParams({ hash: curve.hash, cipher: curve.cipher });
const data = util.strToUint8Array('test');
const Q = key_data[curveName].pub;
const d = key_data[curveName].priv;
const { publicKey: V, wrappedKey: C } = await ecdh.encrypt(oid, kdfParams, data, Q, fingerprint1);
expect(await ecdh.decrypt(oid, kdfParams, V, C, Q, d, fingerprint1)).to.deep.equal(data);
});
});
describe('Comparing decrypting with and without native crypto', () => {
let sinonSandbox;
let getWebCryptoStub;
let getNodeCryptoStub;
beforeEach(function () {
sinonSandbox = sandbox.create();
});
afterEach(function () {
sinonSandbox.restore();
});
const disableNative = () => {
enableNative();
// stubbed functions return undefined
getWebCryptoStub = sinonSandbox.stub(util, "getWebCrypto");
getNodeCryptoStub = sinonSandbox.stub(util, "getNodeCrypto");
};
const enableNative = () => {
getWebCryptoStub && getWebCryptoStub.restore();
getNodeCryptoStub && getNodeCryptoStub.restore();
};
['p256', 'p384', 'p521'].forEach(curveName => {
it(`NIST ${curveName}`, async function () {
const nodeCrypto = util.getNodeCrypto();
const webCrypto = util.getWebCrypto();
if (!nodeCrypto && !webCrypto) {
this.skip();
}
const curve = new elliptic_curves.Curve(curveName);
const oid = new OID(curve.oid);
const kdfParams = new KDFParams({ hash: curve.hash, cipher: curve.cipher });
const data = util.strToUint8Array('test');
const Q = key_data[curveName].pub;
const d = key_data[curveName].priv;
const { publicKey: V, wrappedKey: C } = await ecdh.encrypt(oid, kdfParams, data, Q, fingerprint1);
const nativeDecryptSpy = webCrypto ? sinonSandbox.spy(webCrypto, 'deriveBits') : sinonSandbox.spy(nodeCrypto, 'createECDH');
expect(await ecdh.decrypt(oid, kdfParams, V, C, Q, d, fingerprint1)).to.deep.equal(data);
disableNative();
expect(await ecdh.decrypt(oid, kdfParams, V, C, Q, d, fingerprint1)).to.deep.equal(data);
if (curveName !== 'p521') { // safari does not implement p521 in webcrypto
expect(nativeDecryptSpy.calledOnce).to.be.true;
}
});
});
});
});
});

View File

@@ -4,6 +4,7 @@ const hashMod = require('../../src/crypto/hash');
const config = require('../../src/config');
const util = require('../../src/util');
const sandbox = require('sinon/lib/sinon/sandbox');
const chai = require('chai');
const elliptic_data = require('./elliptic_data');
@@ -99,6 +100,29 @@ module.exports = () => describe('Elliptic Curve Cryptography @lightweight', func
});
});
describe('ECDSA signature', function () {
let sinonSandbox;
let getWebCryptoStub;
let getNodeCryptoStub;
beforeEach(function () {
sinonSandbox = sandbox.create();
});
afterEach(function () {
sinonSandbox.restore();
});
const disableNative = () => {
enableNative();
// stubbed functions return undefined
getWebCryptoStub = sinonSandbox.stub(util, "getWebCrypto");
getNodeCryptoStub = sinonSandbox.stub(util, "getNodeCrypto");
};
const enableNative = () => {
getWebCryptoStub && getWebCryptoStub.restore();
getNodeCryptoStub && getNodeCryptoStub.restore();
};
const verify_signature = async function (oid, hash, r, s, message, pub) {
if (util.isString(message)) {
message = util.strToUint8Array(message);
@@ -151,7 +175,7 @@ module.exports = () => describe('Elliptic Curve Cryptography @lightweight', func
});
it('Invalid public key', async function () {
if (!config.useIndutnyElliptic && !util.getNodeCrypto()) {
this.skip();
this.skip(); // webcrypto does not implement secp256k1
}
if (util.getNodeCrypto()) {
await expect(verify_signature(
@@ -162,20 +186,13 @@ module.exports = () => describe('Elliptic Curve Cryptography @lightweight', func
)).to.eventually.be.false;
}
if (config.useIndutnyElliptic) {
const useNative = config.useNative;
config.useNative = false;
try {
await Promise.all([
expect(verify_signature(
'secp256k1', 8, [], [], [], []
)).to.be.rejectedWith(Error, /Unknown point format/),
expect(verify_signature(
'secp256k1', 8, [], [], [], secp256k1_invalid_point_format
)).to.be.rejectedWith(Error, /Unknown point format/)
]);
} finally {
config.useNative = useNative;
}
disableNative();
await expect(verify_signature(
'secp256k1', 8, [], [], [], []
)).to.be.rejectedWith(Error, /Unknown point format/);
await expect(verify_signature(
'secp256k1', 8, [], [], [], secp256k1_invalid_point_format
)).to.be.rejectedWith(Error, /Unknown point format/);
}
});
it('Invalid point', async function () {
@@ -188,15 +205,10 @@ module.exports = () => describe('Elliptic Curve Cryptography @lightweight', func
)).to.eventually.be.false;
}
if (config.useIndutnyElliptic) {
const useNative = config.useNative;
config.useNative = false;
try {
await expect(verify_signature(
'secp256k1', 8, [], [], [], secp256k1_invalid_point
)).to.be.rejectedWith(Error, /Invalid elliptic public key/);
} finally {
config.useNative = useNative;
}
disableNative();
await expect(verify_signature(
'secp256k1', 8, [], [], [], secp256k1_invalid_point
)).to.be.rejectedWith(Error, /Invalid elliptic public key/);
}
});
it('Invalid signature', function (done) {

86
test/crypto/gcm.js Normal file
View File

@@ -0,0 +1,86 @@
const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../..');
const crypto = require('../../src/crypto');
const util = require('../../src/util');
const sandbox = require('sinon/lib/sinon/sandbox');
const chai = require('chai');
chai.use(require('chai-as-promised'));
const expect = chai.expect;
module.exports = () => describe('Symmetric AES-GCM (experimental)', function() {
let sinonSandbox;
let getWebCryptoStub;
let getNodeCryptoStub;
beforeEach(function () {
sinonSandbox = sandbox.create();
enableNative();
});
afterEach(function () {
sinonSandbox.restore();
});
const disableNative = () => {
enableNative();
// stubbed functions return undefined
getWebCryptoStub = sinonSandbox.stub(util, "getWebCrypto");
getNodeCryptoStub = sinonSandbox.stub(util, "getNodeCrypto");
};
const enableNative = () => {
getWebCryptoStub && getWebCryptoStub.restore();
getNodeCryptoStub && getNodeCryptoStub.restore();
};
function testAESGCM(plaintext, nativeEncrypt, nativeDecrypt) {
const aesAlgos = Object.keys(openpgp.enums.symmetric).filter(
algo => algo.substr(0,3) === 'aes'
);
aesAlgos.forEach(function(algo) {
it(algo, async function() {
const nodeCrypto = util.getNodeCrypto();
const webCrypto = util.getWebCrypto();
if (!nodeCrypto && !webCrypto) {
this.skip(); // eslint-disable-line no-invalid-this
}
const key = await crypto.generateSessionKey(algo);
const iv = await crypto.random.getRandomBytes(crypto.gcm.ivLength);
const nativeEncryptSpy = webCrypto ? sinonSandbox.spy(webCrypto, 'encrypt') : sinonSandbox.spy(nodeCrypto, 'createCipheriv');
const nativeDecryptSpy = webCrypto ? sinonSandbox.spy(webCrypto, 'decrypt') : sinonSandbox.spy(nodeCrypto, 'createDecipheriv');
nativeEncrypt || disableNative();
let modeInstance = await crypto.gcm(algo, key);
const ciphertext = await modeInstance.encrypt(util.strToUint8Array(plaintext), iv);
enableNative();
nativeDecrypt || disableNative();
modeInstance = await crypto.gcm(algo, key);
const decrypted = await modeInstance.decrypt(util.strToUint8Array(util.uint8ArrayToStr(ciphertext)), iv);
enableNative();
const decryptedStr = util.uint8ArrayToStr(decrypted);
expect(decryptedStr).to.equal(plaintext);
if (algo !== 'aes192') { // not implemented by webcrypto
// sanity check: native crypto was indeed on/off
expect(nativeEncryptSpy.called).to.equal(nativeEncrypt);
expect(nativeDecryptSpy.called).to.equal(nativeDecrypt);
}
});
});
}
describe('Symmetric AES-GCM (native)', function() {
testAESGCM("12345678901234567890123456789012345678901234567890", true, true);
});
describe('Symmetric AES-GCM (asm.js fallback)', function() {
testAESGCM("12345678901234567890123456789012345678901234567890", false, false);
});
describe('Symmetric AES-GCM (native encrypt, asm.js decrypt)', function() {
testAESGCM("12345678901234567890123456789012345678901234567890", true, false);
});
});

View File

@@ -7,6 +7,7 @@ module.exports = () => describe('Crypto', function () {
require('./ecdh.js')();
require('./pkcs5.js')();
require('./aes_kw.js')();
require('./gcm.js')();
require('./eax.js')();
require('./ocb.js')();
require('./rsa.js')();

View File

@@ -3,6 +3,7 @@ const crypto = require('../../src/crypto');
const random = require('../../src/crypto/random');
const util = require('../../src/util');
const sandbox = require('sinon/lib/sinon/sandbox');
const chai = require('chai');
chai.use(require('chai-as-promised'));
@@ -13,6 +14,30 @@ const expect = chai.expect;
/* eslint-disable no-invalid-this */
const native = util.getWebCrypto() || util.getNodeCrypto();
module.exports = () => (!native ? describe.skip : describe)('basic RSA cryptography with native crypto', function () {
let sinonSandbox;
let getWebCryptoStub;
let getNodeCryptoStub;
beforeEach(function () {
sinonSandbox = sandbox.create();
enableNative();
});
afterEach(function () {
sinonSandbox.restore();
});
const disableNative = () => {
enableNative();
// stubbed functions return undefined
getWebCryptoStub = sinonSandbox.stub(util, "getWebCrypto");
getNodeCryptoStub = sinonSandbox.stub(util, "getNodeCrypto");
};
const enableNative = () => {
getWebCryptoStub && getWebCryptoStub.restore();
getNodeCryptoStub && getNodeCryptoStub.restore();
};
it('generate rsa key', async function() {
const bits = 1024;
const keyObject = await crypto.publicKey.rsa.generate(bits, 65537);
@@ -49,29 +74,24 @@ module.exports = () => (!native ? describe.skip : describe)('basic RSA cryptogra
it('decrypt nodeCrypto by bnCrypto and vice versa', async function() {
if (!util.getNodeCrypto()) {
this.skip();
this.skip(); // webcrypto does not implement RSA PKCS#1 v.15 decryption
}
const bits = 1024;
const { publicParams, privateParams } = await crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits);
const { n, e, d, p, q, u } = { ...publicParams, ...privateParams };
const message = await crypto.generateSessionKey('aes256');
const useNative = openpgp.config.useNative;
try {
openpgp.config.useNative = false;
const encryptedBn = await crypto.publicKey.rsa.encrypt(message, n, e);
openpgp.config.useNative = true;
const decrypted1 = await crypto.publicKey.rsa.decrypt(encryptedBn, n, e, d, p, q, u);
expect(decrypted1).to.deep.equal(message);
const encryptedNode = await crypto.publicKey.rsa.encrypt(message, n, e);
openpgp.config.useNative = false;
const decrypted2 = await crypto.publicKey.rsa.decrypt(encryptedNode, n, e, d, p, q, u);
expect(decrypted2).to.deep.equal(message);
} finally {
openpgp.config.useNative = useNative;
}
disableNative();
const encryptedBn = await crypto.publicKey.rsa.encrypt(message, n, e);
enableNative();
const decrypted1 = await crypto.publicKey.rsa.decrypt(encryptedBn, n, e, d, p, q, u);
expect(decrypted1).to.deep.equal(message);
const encryptedNode = await crypto.publicKey.rsa.encrypt(message, n, e);
disableNative();
const decrypted2 = await crypto.publicKey.rsa.decrypt(encryptedNode, n, e, d, p, q, u);
expect(decrypted2).to.deep.equal(message);
});
it('compare native crypto and bn math sign', async function() {
it('compare native crypto and bnSign', async function() {
const bits = 1024;
const { publicParams, privateParams } = await crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits);
const { n, e, d, p, q, u } = { ...publicParams, ...privateParams };
@@ -79,25 +99,14 @@ module.exports = () => (!native ? describe.skip : describe)('basic RSA cryptogra
const hashName = 'sha256';
const hash_algo = openpgp.enums.write(openpgp.enums.hash, hashName);
const hashed = await crypto.hash.digest(hash_algo, message);
const useNative = openpgp.config.useNative;
try {
openpgp.config.useNative = true;
let signatureWeb;
try {
signatureWeb = await crypto.publicKey.rsa.sign(hash_algo, message, n, e, d, p, q, u, hashed);
} catch (error) {
util.printDebugError('web crypto error');
this.skip();
}
openpgp.config.useNative = false;
const signatureBN = await crypto.publicKey.rsa.sign(hash_algo, message, n, e, d, p, q, u, hashed);
expect(util.uint8ArrayToHex(signatureWeb)).to.be.equal(util.uint8ArrayToHex(signatureBN));
} finally {
openpgp.config.useNative = useNative;
}
enableNative();
const signatureNative = await crypto.publicKey.rsa.sign(hash_algo, message, n, e, d, p, q, u, hashed);
disableNative();
const signatureBN = await crypto.publicKey.rsa.sign(hash_algo, message, n, e, d, p, q, u, hashed);
expect(util.uint8ArrayToHex(signatureNative)).to.be.equal(util.uint8ArrayToHex(signatureBN));
});
it('compare native crypto and bn math verify', async function() {
it('compare native crypto and bnVerify', async function() {
const bits = 1024;
const { publicParams, privateParams } = await crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits);
const { n, e, d, p, q, u } = { ...publicParams, ...privateParams };
@@ -105,24 +114,12 @@ module.exports = () => (!native ? describe.skip : describe)('basic RSA cryptogra
const hashName = 'sha256';
const hash_algo = openpgp.enums.write(openpgp.enums.hash, hashName);
const hashed = await crypto.hash.digest(hash_algo, message);
let verifyWeb;
let signature;
const useNative = openpgp.config.useNative;
try {
openpgp.config.useNative = true;
try {
signature = await crypto.publicKey.rsa.sign(hash_algo, message, n, e, d, p, q, u, hashed);
verifyWeb = await crypto.publicKey.rsa.verify(hash_algo, message, signature, n, e);
} catch (error) {
util.printDebugError('web crypto error');
this.skip();
}
openpgp.config.useNative = false;
const verifyBN = await crypto.publicKey.rsa.verify(hash_algo, message, signature, n, e, hashed);
expect(verifyWeb).to.be.true;
expect(verifyBN).to.be.true;
} finally {
openpgp.config.useNative = useNative;
}
enableNative();
const signatureNative = await crypto.publicKey.rsa.sign(hash_algo, message, n, e, d, p, q, u, hashed);
const verifyNative = await crypto.publicKey.rsa.verify(hash_algo, message, signatureNative, n, e);
disableNative();
const verifyBN = await crypto.publicKey.rsa.verify(hash_algo, message, signatureNative, n, e, hashed);
expect(verifyNative).to.be.true;
expect(verifyBN).to.be.true;
});
});