Cleanup and unit test gcm.js

This commit is contained in:
Tankred Hase 2016-03-22 16:25:24 +08:00
parent 49faca83c5
commit 8aa15b66a9
2 changed files with 75 additions and 44 deletions

View File

@ -23,12 +23,14 @@
'use strict';
import util from '../util.js';
import config from '../config';
import asmCrypto from 'asmcrypto-lite';
const webCrypto = util.getWebCrypto();
const nodeCrypto = util.getNodeCrypto();
const Buffer = util.getNodeBuffer();
export const ivLength = 12;
const ALGO = 'AES-GCM';
/**
* Encrypt plaintext input.
@ -40,26 +42,18 @@ export const ivLength = 12;
*/
export function encrypt(cipher, plaintext, key, iv) {
if (cipher.substr(0,3) !== 'aes') {
return Promise.reject(new Error('Invalid cipher for GCM mode'));
return Promise.reject(new Error('GCM mode supports only AES cipher'));
}
if (webCrypto) { // native WebCrypto api
const keyOptions = {
name: 'AES-GCM'
},
encryptOptions = {
name: 'AES-GCM',
iv: iv
};
return webCrypto.importKey('raw', key, keyOptions, false, ['encrypt']).then(keyObj => {
return webCrypto.encrypt(encryptOptions, keyObj, plaintext);
}).then(ciphertext => {
return new Uint8Array(ciphertext);
});
const keySize = cipher.substr(3,3);
if (webCrypto && config.useNative && keySize !== '192') { // WebCrypto (no 192 bit support) see: https://www.chromium.org/blink/webcrypto#TOC-AES-support
return webCrypto.importKey('raw', key, { name: ALGO }, false, ['encrypt'])
.then(keyObj => webCrypto.encrypt({ name: ALGO, iv }, keyObj, plaintext))
.then(ciphertext => new Uint8Array(ciphertext));
} else if(nodeCrypto) { // native node crypto library
let cipherObj = new nodeCrypto.createCipheriv('aes-' + cipher.substr(3,3) + '-gcm', new Buffer(key), new Buffer(iv));
let encrypted = Buffer.concat([cipherObj.update(new Buffer(plaintext)), cipherObj.final()]);
} else if (nodeCrypto && config.useNative) { // Node crypto library
const en = new nodeCrypto.createCipheriv('aes-' + keySize + '-gcm', new Buffer(key), new Buffer(iv));
const encrypted = Buffer.concat([en.update(new Buffer(plaintext)), en.final()]);
return Promise.resolve(new Uint8Array(encrypted));
} else { // asm.js fallback
@ -77,26 +71,18 @@ export function encrypt(cipher, plaintext, key, iv) {
*/
export function decrypt(cipher, ciphertext, key, iv) {
if (cipher.substr(0,3) !== 'aes') {
return Promise.reject(new Error('Invalid cipher for GCM mode'));
return Promise.reject(new Error('GCM mode supports only AES cipher'));
}
if (webCrypto) { // native WebCrypto api
const keyOptions = {
name: 'AES-GCM'
},
decryptOptions = {
name: 'AES-GCM',
iv: iv
};
return webCrypto.importKey('raw', key, keyOptions, false, ['decrypt']).then(keyObj => {
return webCrypto.decrypt(decryptOptions, keyObj, ciphertext);
}).then(plaintext => {
return new Uint8Array(plaintext);
});
const keySize = cipher.substr(3,3);
if (webCrypto && config.useNative && keySize !== '192') { // WebCrypto (no 192 bit support) see: https://www.chromium.org/blink/webcrypto#TOC-AES-support
return webCrypto.importKey('raw', key, { name: ALGO }, false, ['decrypt'])
.then(keyObj => webCrypto.decrypt({ name: ALGO, iv }, keyObj, ciphertext))
.then(plaintext => new Uint8Array(plaintext));
} else if(nodeCrypto) { // native node crypto library
let decipherObj = new nodeCrypto.createDecipheriv('aes-' + cipher.substr(3,3) + '-gcm', new Buffer(key), new Buffer(iv));
let decrypted = Buffer.concat([decipherObj.update(new Buffer(ciphertext)), decipherObj.final()]);
} else if (nodeCrypto && config.useNative) { // Node crypto library
let de = new nodeCrypto.createDecipheriv('aes-' + keySize + '-gcm', new Buffer(key), new Buffer(iv));
let decrypted = Buffer.concat([de.update(new Buffer(ciphertext)), de.final()]);
return Promise.resolve(new Uint8Array(decrypted));
} else { // asm.js fallback

View File

@ -287,6 +287,25 @@ describe('API functional testing', function() {
});
}
function testAESGCM(plaintext) {
symmAlgos.forEach(function(algo) {
if(algo.substr(0,3) === 'aes') {
it(algo, function(done) {
var key = openpgp.crypto.generateSessionKey(algo);
var iv = openpgp.crypto.random.getRandomValues(new Uint8Array(openpgp.crypto.gcm.ivLength));
openpgp.crypto.gcm.encrypt(algo, util.str2Uint8Array(plaintext), key, iv).then(function(ciphertext) {
return openpgp.crypto.gcm.decrypt(algo, ciphertext, key, iv);
}).then(function(decrypted) {
var decryptedStr = util.Uint8Array2str(decrypted);
expect(decryptedStr).to.equal(plaintext);
done();
});
});
}
});
}
it("Symmetric with OpenPGP CFB resync", function () {
testCFB("hello", true);
testCFB("1234567", true);
@ -301,11 +320,37 @@ describe('API functional testing', function() {
testCFB("12345678901234567890123456789012345678901234567890", false);
});
it("asmCrypto AES without OpenPGP CFB resync", function () {
testCFB("hello");
testCFB("1234567");
testCFB("foobarfoobar1234567890");
testCFB("12345678901234567890123456789012345678901234567890");
it.skip("asmCrypto AES without OpenPGP CFB resync", function () {
testAESCFB("hello");
testAESCFB("1234567");
testAESCFB("foobarfoobar1234567890");
testAESCFB("12345678901234567890123456789012345678901234567890");
});
describe('Symmetric AES-GCM (native)', function() {
var useNativeVal;
beforeEach(function() {
useNativeVal = openpgp.config.useNative;
openpgp.config.useNative = true;
});
afterEach(function() {
openpgp.config.useNative = useNativeVal;
});
testAESGCM("12345678901234567890123456789012345678901234567890");
});
describe('Symmetric AES-GCM (asm.js fallback)', function() {
var useNativeVal;
beforeEach(function() {
useNativeVal = openpgp.config.useNative;
openpgp.config.useNative = false;
});
afterEach(function() {
openpgp.config.useNative = useNativeVal;
});
testAESGCM("12345678901234567890123456789012345678901234567890");
});
it('Asymmetric using RSA with eme_pkcs1 padding', function (done) {