Merge pull request #430 from openpgpjs/aes_gcm
Implement AES-GCM proposal (IETF draft)
This commit is contained in:
commit
10bf9ec41e
|
@ -19,10 +19,10 @@ matrix:
|
||||||
env: OPENPGPJSTEST='end2end-4' BROWSER='chrome 46'
|
env: OPENPGPJSTEST='end2end-4' BROWSER='chrome 46'
|
||||||
- node_js: "4"
|
- node_js: "4"
|
||||||
env: OPENPGPJSTEST='end2end-1' BROWSER='firefox 42'
|
env: OPENPGPJSTEST='end2end-1' BROWSER='firefox 42'
|
||||||
- node_js: "4"
|
|
||||||
env: OPENPGPJSTEST='end2end-6' BROWSER='internet explorer 11'
|
|
||||||
- node_js: "4"
|
- node_js: "4"
|
||||||
env: OPENPGPJSTEST='end2end-9' BROWSER='safari 9'
|
env: OPENPGPJSTEST='end2end-9' BROWSER='safari 9'
|
||||||
|
- node_js: "4"
|
||||||
|
env: OPENPGPJSTEST='end2end-6' BROWSER='internet explorer 11'
|
||||||
- node_js: "4"
|
- node_js: "4"
|
||||||
env: OPENPGPJSTEST='end2end-0' BROWSER='firefox 38'
|
env: OPENPGPJSTEST='end2end-0' BROWSER='firefox 38'
|
||||||
- node_js: "4"
|
- node_js: "4"
|
||||||
|
@ -48,6 +48,7 @@ matrix:
|
||||||
- env: OPENPGPJSTEST='end2end-2' BROWSER='firefox beta'
|
- env: OPENPGPJSTEST='end2end-2' BROWSER='firefox beta'
|
||||||
- env: OPENPGPJSTEST='end2end-3' BROWSER='chrome 38'
|
- env: OPENPGPJSTEST='end2end-3' BROWSER='chrome 38'
|
||||||
- env: OPENPGPJSTEST='end2end-5' BROWSER='chrome beta'
|
- env: OPENPGPJSTEST='end2end-5' BROWSER='chrome beta'
|
||||||
|
- env: OPENPGPJSTEST='end2end-6' BROWSER='internet explorer 11'
|
||||||
- env: OPENPGPJSTEST='end2end-7' BROWSER='microsoft edge 20.10240'
|
- env: OPENPGPJSTEST='end2end-7' BROWSER='microsoft edge 20.10240'
|
||||||
- env: OPENPGPJSTEST='end2end-8' BROWSER='safari 8'
|
- env: OPENPGPJSTEST='end2end-8' BROWSER='safari 8'
|
||||||
- env: OPENPGPJSTEST='end2end-10' BROWSER='android 4.4'
|
- env: OPENPGPJSTEST='end2end-10' BROWSER='android 4.4'
|
||||||
|
|
|
@ -19,9 +19,11 @@ OpenPGP.js [ via the `window.crypto.subtle` api, this will be used. Under node.js the native [crypto module](https://nodejs.org/api/crypto.html#crypto_crypto) is used. This can be deactivated by setting `openpgp.config.useNative = false`.
|
* If the user's browser supports [native WebCrypto](http://caniuse.com/#feat=cryptography) via the `window.crypto.subtle` api, this will be used. Under node.js the native [crypto module](https://nodejs.org/api/crypto.html#crypto_crypto) is used. This can be deactivated by setting `openpgp.config.use_native = false`.
|
||||||
|
|
||||||
* For environments that don't provide native crypto, the library falls back to [asm.js](http://caniuse.com/#feat=asmjs) implementations of AES-CFB, SHA-1, and SHA-256. We use [Rusha](https://github.com/srijs/rusha) and [asmCrypto Lite](https://github.com/openpgpjs/asmcrypto-lite) (a minimal subset of asmCrypto.js built specifically for OpenPGP.js).
|
* The library implements the [IETF proposal](https://tools.ietf.org/html/draft-ford-openpgp-format-00) for authenticated encryption using native AES-GCM. This makes symmetric encryption about 30x faster on supported platforms. Since the specification has not been finalized and other OpenPGP implementations haven't adopted it yet, the feature is currently behind a flag. You can activate it by setting `openpgp.config.aead_protect = true`.
|
||||||
|
|
||||||
|
* For environments that don't provide native crypto, the library falls back to [asm.js](http://caniuse.com/#feat=asmjs) implementations of AES, SHA-1, and SHA-256. We use [Rusha](https://github.com/srijs/rusha) and [asmCrypto Lite](https://github.com/openpgpjs/asmcrypto-lite) (a minimal subset of asmCrypto.js built specifically for OpenPGP.js).
|
||||||
|
|
||||||
|
|
||||||
### Getting started
|
### Getting started
|
||||||
|
@ -47,6 +49,8 @@ Here are some examples of how to use the v2.x api. For more elaborate examples a
|
||||||
var openpgp = require('openpgp'); // use as CommonJS, AMD, ES6 module or via window.openpgp
|
var openpgp = require('openpgp'); // use as CommonJS, AMD, ES6 module or via window.openpgp
|
||||||
|
|
||||||
openpgp.initWorker({ path:'openpgp.worker.js' }) // set the relative web worker path
|
openpgp.initWorker({ path:'openpgp.worker.js' }) // set the relative web worker path
|
||||||
|
|
||||||
|
openpgp.config.aead_protect = true // activate fast AES-GCM mode (experimental)
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Encrypt and decrypt *String* data with a password
|
#### Encrypt and decrypt *String* data with a password
|
||||||
|
|
4
npm-shrinkwrap.json
generated
4
npm-shrinkwrap.json
generated
|
@ -3,9 +3,9 @@
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"asmcrypto-lite": {
|
"asmcrypto-lite": {
|
||||||
"version": "1.0.1",
|
"version": "1.1.0",
|
||||||
"from": "asmcrypto-lite@>=1.0.0 <2.0.0",
|
"from": "asmcrypto-lite@>=1.0.0 <2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/asmcrypto-lite/-/asmcrypto-lite-1.0.1.tgz"
|
"resolved": "https://registry.npmjs.org/asmcrypto-lite/-/asmcrypto-lite-1.1.0.tgz"
|
||||||
},
|
},
|
||||||
"babel-preset-es2015": {
|
"babel-preset-es2015": {
|
||||||
"version": "6.5.0",
|
"version": "6.5.0",
|
||||||
|
|
|
@ -37,11 +37,12 @@ export default {
|
||||||
prefer_hash_algorithm: enums.hash.sha256,
|
prefer_hash_algorithm: enums.hash.sha256,
|
||||||
encryption_cipher: enums.symmetric.aes256,
|
encryption_cipher: enums.symmetric.aes256,
|
||||||
compression: enums.compression.zip,
|
compression: enums.compression.zip,
|
||||||
|
aead_protect: false, // use Authenticated Encryption with Additional Data (AEAD) protection for symmetric encryption
|
||||||
integrity_protect: true, // use integrity protection for symmetric encryption
|
integrity_protect: true, // use integrity protection for symmetric encryption
|
||||||
ignore_mdc_error: false, // fail on decrypt if message is not integrity protected
|
ignore_mdc_error: false, // fail on decrypt if message is not integrity protected
|
||||||
rsa_blinding: true,
|
rsa_blinding: true,
|
||||||
useNative: true, // use native node.js crypto and Web Crypto apis (if available)
|
use_native: true, // use native node.js crypto and Web Crypto apis (if available)
|
||||||
zeroCopy: false, // use transferable objects between the Web Worker and main thread
|
zero_copy: false, // use transferable objects between the Web Worker and main thread
|
||||||
debug: false,
|
debug: false,
|
||||||
show_version: true,
|
show_version: true,
|
||||||
show_comment: true,
|
show_comment: true,
|
||||||
|
|
117
src/crypto/gcm.js
Normal file
117
src/crypto/gcm.js
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
// OpenPGP.js - An OpenPGP implementation in javascript
|
||||||
|
// Copyright (C) 2016 Tankred Hase
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Lesser General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 3.0 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @fileoverview This module wraps native AES-GCM en/decryption for both
|
||||||
|
* the WebCrypto api as well as node.js' crypto api.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import util from '../util.js';
|
||||||
|
import config from '../config';
|
||||||
|
import asmCrypto from 'asmcrypto-lite';
|
||||||
|
const webCrypto = util.getWebCrypto(); // no GCM support in IE11, Safari 9
|
||||||
|
const nodeCrypto = util.getNodeCrypto();
|
||||||
|
const Buffer = util.getNodeBuffer();
|
||||||
|
|
||||||
|
export const ivLength = 12; // size of the IV in bytes
|
||||||
|
const TAG_LEN = 16; // size of the tag in bytes
|
||||||
|
const ALGO = 'AES-GCM';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypt plaintext input.
|
||||||
|
* @param {String} cipher The symmetric cipher algorithm to use e.g. 'aes128'
|
||||||
|
* @param {Uint8Array} plaintext The cleartext input to be encrypted
|
||||||
|
* @param {Uint8Array} key The encryption key
|
||||||
|
* @param {Uint8Array} iv The initialization vector (12 bytes)
|
||||||
|
* @return {Promise<Uint8Array>} The ciphertext output
|
||||||
|
*/
|
||||||
|
export function encrypt(cipher, plaintext, key, iv) {
|
||||||
|
if (cipher.substr(0,3) !== 'aes') {
|
||||||
|
return Promise.reject(new Error('GCM mode supports only AES cipher'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (webCrypto && config.use_native && key.length !== 24) { // WebCrypto (no 192 bit support) see: https://www.chromium.org/blink/webcrypto#TOC-AES-support
|
||||||
|
return webEncrypt(plaintext, key, iv);
|
||||||
|
} else if (nodeCrypto && config.use_native) { // Node crypto library
|
||||||
|
return nodeEncrypt(plaintext, key, iv) ;
|
||||||
|
} else { // asm.js fallback
|
||||||
|
return Promise.resolve(asmCrypto.AES_GCM.encrypt(plaintext, key, iv));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypt ciphertext input.
|
||||||
|
* @param {String} cipher The symmetric cipher algorithm to use e.g. 'aes128'
|
||||||
|
* @param {Uint8Array} ciphertext The ciphertext input to be decrypted
|
||||||
|
* @param {Uint8Array} key The encryption key
|
||||||
|
* @param {Uint8Array} iv The initialization vector (12 bytes)
|
||||||
|
* @return {Promise<Uint8Array>} The plaintext output
|
||||||
|
*/
|
||||||
|
export function decrypt(cipher, ciphertext, key, iv) {
|
||||||
|
if (cipher.substr(0,3) !== 'aes') {
|
||||||
|
return Promise.reject(new Error('GCM mode supports only AES cipher'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (webCrypto && config.use_native && key.length !== 24) { // WebCrypto (no 192 bit support) see: https://www.chromium.org/blink/webcrypto#TOC-AES-support
|
||||||
|
return webDecrypt(ciphertext, key, iv);
|
||||||
|
} else if (nodeCrypto && config.use_native) { // Node crypto library
|
||||||
|
return nodeDecrypt(ciphertext, key, iv);
|
||||||
|
} else { // asm.js fallback
|
||||||
|
return Promise.resolve(asmCrypto.AES_GCM.decrypt(ciphertext, key, iv));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////
|
||||||
|
// //
|
||||||
|
// Helper functions //
|
||||||
|
// //
|
||||||
|
//////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
function webEncrypt(pt, key, iv) {
|
||||||
|
return webCrypto.importKey('raw', key, { name: ALGO }, false, ['encrypt'])
|
||||||
|
.then(keyObj => webCrypto.encrypt({ name: ALGO, iv }, keyObj, pt))
|
||||||
|
.then(ct => new Uint8Array(ct));
|
||||||
|
}
|
||||||
|
|
||||||
|
function webDecrypt(ct, key, iv) {
|
||||||
|
return webCrypto.importKey('raw', key, { name: ALGO }, false, ['decrypt'])
|
||||||
|
.then(keyObj => webCrypto.decrypt({ name: ALGO, iv }, keyObj, ct))
|
||||||
|
.then(pt => new Uint8Array(pt));
|
||||||
|
}
|
||||||
|
|
||||||
|
function nodeEncrypt(pt, key, iv) {
|
||||||
|
pt = new Buffer(pt);
|
||||||
|
key = new Buffer(key);
|
||||||
|
iv = new Buffer(iv);
|
||||||
|
const en = new nodeCrypto.createCipheriv('aes-' + (key.length * 8) + '-gcm', key, iv);
|
||||||
|
const ct = Buffer.concat([en.update(pt), en.final(), en.getAuthTag()]); // append auth tag to ciphertext
|
||||||
|
return Promise.resolve(new Uint8Array(ct));
|
||||||
|
}
|
||||||
|
|
||||||
|
function nodeDecrypt(ct, key, iv) {
|
||||||
|
ct = new Buffer(ct);
|
||||||
|
key = new Buffer(key);
|
||||||
|
iv = new Buffer(iv);
|
||||||
|
const de = new nodeCrypto.createDecipheriv('aes-' + (key.length * 8) + '-gcm', key, iv);
|
||||||
|
de.setAuthTag(ct.slice(ct.length - TAG_LEN, ct.length)); // read auth tag at end of ciphertext
|
||||||
|
const pt = Buffer.concat([de.update(ct.slice(0, ct.length - TAG_LEN)), de.final()]);
|
||||||
|
return Promise.resolve(new Uint8Array(pt));
|
||||||
|
}
|
|
@ -8,6 +8,7 @@
|
||||||
import cipher from './cipher';
|
import cipher from './cipher';
|
||||||
import hash from './hash';
|
import hash from './hash';
|
||||||
import cfb from './cfb';
|
import cfb from './cfb';
|
||||||
|
import * as gcm from './gcm';
|
||||||
import publicKey from './public_key';
|
import publicKey from './public_key';
|
||||||
import signature from './signature';
|
import signature from './signature';
|
||||||
import random from './random';
|
import random from './random';
|
||||||
|
@ -21,6 +22,8 @@ const mod = {
|
||||||
hash: hash,
|
hash: hash,
|
||||||
/** @see module:crypto/cfb */
|
/** @see module:crypto/cfb */
|
||||||
cfb: cfb,
|
cfb: cfb,
|
||||||
|
/** @see module:crypto/gcm */
|
||||||
|
gcm: gcm,
|
||||||
/** @see module:crypto/public_key */
|
/** @see module:crypto/public_key */
|
||||||
publicKey: publicKey,
|
publicKey: publicKey,
|
||||||
/** @see module:crypto/signature */
|
/** @see module:crypto/signature */
|
||||||
|
|
|
@ -136,7 +136,7 @@ export default function RSA() {
|
||||||
// Generate a new random private key B bits long, using public expt E
|
// Generate a new random private key B bits long, using public expt E
|
||||||
|
|
||||||
function generate(B, E) {
|
function generate(B, E) {
|
||||||
var webCrypto = util.getWebCrypto();
|
var webCrypto = util.getWebCryptoAll();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Native RSA keygen using Web Crypto
|
// Native RSA keygen using Web Crypto
|
||||||
|
|
|
@ -91,6 +91,7 @@ export default {
|
||||||
} else {
|
} else {
|
||||||
throw new Error('No secure random number generator available.');
|
throw new Error('No secure random number generator available.');
|
||||||
}
|
}
|
||||||
|
return buf;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -94,7 +94,8 @@ export default {
|
||||||
publicSubkey: 14,
|
publicSubkey: 14,
|
||||||
userAttribute: 17,
|
userAttribute: 17,
|
||||||
symEncryptedIntegrityProtected: 18,
|
symEncryptedIntegrityProtected: 18,
|
||||||
modificationDetectionCode: 19
|
modificationDetectionCode: 19,
|
||||||
|
symEncryptedAEADProtected: 20 // see IETF draft: https://tools.ietf.org/html/draft-ford-openpgp-format-00#section-2.1
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Data types in the literal packet
|
/** Data types in the literal packet
|
||||||
|
|
17
src/key.js
17
src/key.js
|
@ -933,24 +933,21 @@ export function readArmored(armoredText) {
|
||||||
*/
|
*/
|
||||||
export function generate(options) {
|
export function generate(options) {
|
||||||
var packetlist, secretKeyPacket, userIdPacket, dataToSign, signaturePacket, secretSubkeyPacket, subkeySignaturePacket;
|
var packetlist, secretKeyPacket, userIdPacket, dataToSign, signaturePacket, secretSubkeyPacket, subkeySignaturePacket;
|
||||||
|
return Promise.resolve().then(() => {
|
||||||
options.keyType = options.keyType || enums.publicKey.rsa_encrypt_sign;
|
options.keyType = options.keyType || enums.publicKey.rsa_encrypt_sign;
|
||||||
// RSA Encrypt-Only and RSA Sign-Only are deprecated and SHOULD NOT be generated
|
if (options.keyType !== enums.publicKey.rsa_encrypt_sign) { // RSA Encrypt-Only and RSA Sign-Only are deprecated and SHOULD NOT be generated
|
||||||
if (options.keyType !== enums.publicKey.rsa_encrypt_sign) {
|
|
||||||
throw new Error('Only RSA Encrypt or Sign supported');
|
throw new Error('Only RSA Encrypt or Sign supported');
|
||||||
}
|
}
|
||||||
// Key without passphrase is unlocked by definition
|
|
||||||
if (!options.passphrase) {
|
if (!options.passphrase) { // Key without passphrase is unlocked by definition
|
||||||
options.unlocked = true;
|
options.unlocked = true;
|
||||||
}
|
}
|
||||||
if (String.prototype.isPrototypeOf(options.userIds) || typeof options.userIds === 'string') {
|
if (String.prototype.isPrototypeOf(options.userIds) || typeof options.userIds === 'string') {
|
||||||
options.userIds = [options.userIds];
|
options.userIds = [options.userIds];
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate
|
return Promise.all([generateSecretKey(), generateSecretSubkey()]).then(wrapKeyObject);
|
||||||
var genSecretKey = generateSecretKey();
|
});
|
||||||
var genSecretSubkey = generateSecretSubkey();
|
|
||||||
return Promise.all([genSecretKey, genSecretSubkey]).then(wrapKeyObject);
|
|
||||||
|
|
||||||
function generateSecretKey() {
|
function generateSecretKey() {
|
||||||
secretKeyPacket = new packet.SecretKey();
|
secretKeyPacket = new packet.SecretKey();
|
||||||
|
@ -990,8 +987,8 @@ export function generate(options) {
|
||||||
signaturePacket.keyFlags = [enums.keyFlags.certify_keys | enums.keyFlags.sign_data];
|
signaturePacket.keyFlags = [enums.keyFlags.certify_keys | enums.keyFlags.sign_data];
|
||||||
signaturePacket.preferredSymmetricAlgorithms = [];
|
signaturePacket.preferredSymmetricAlgorithms = [];
|
||||||
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.aes256);
|
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.aes256);
|
||||||
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.aes192);
|
|
||||||
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.aes128);
|
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.aes128);
|
||||||
|
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.aes192);
|
||||||
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.cast5);
|
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.cast5);
|
||||||
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.tripledes);
|
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.tripledes);
|
||||||
signaturePacket.preferredHashAlgorithms = [];
|
signaturePacket.preferredHashAlgorithms = [];
|
||||||
|
|
|
@ -92,19 +92,29 @@ Message.prototype.getSigningKeyIds = function() {
|
||||||
* @return {Message} new message with decrypted content
|
* @return {Message} new message with decrypted content
|
||||||
*/
|
*/
|
||||||
Message.prototype.decrypt = function(privateKey, sessionKey, password) {
|
Message.prototype.decrypt = function(privateKey, sessionKey, password) {
|
||||||
var keyObj = sessionKey || this.decryptSessionKey(privateKey, password);
|
return Promise.resolve().then(() => {
|
||||||
|
const keyObj = sessionKey || this.decryptSessionKey(privateKey, password);
|
||||||
if (!keyObj || !util.isUint8Array(keyObj.data) || !util.isString(keyObj.algorithm)) {
|
if (!keyObj || !util.isUint8Array(keyObj.data) || !util.isString(keyObj.algorithm)) {
|
||||||
throw new Error('Invalid session key for decryption.');
|
throw new Error('Invalid session key for decryption.');
|
||||||
}
|
}
|
||||||
var symEncryptedPacketlist = this.packets.filterByTag(enums.packet.symmetricallyEncrypted, enums.packet.symEncryptedIntegrityProtected);
|
|
||||||
if (symEncryptedPacketlist.length !== 0) {
|
const symEncryptedPacketlist = this.packets.filterByTag(
|
||||||
var symEncryptedPacket = symEncryptedPacketlist[0];
|
enums.packet.symmetricallyEncrypted,
|
||||||
symEncryptedPacket.decrypt(keyObj.algorithm, keyObj.data);
|
enums.packet.symEncryptedIntegrityProtected,
|
||||||
var resultMsg = new Message(symEncryptedPacket.packets);
|
enums.packet.symEncryptedAEADProtected
|
||||||
// remove packets after decryption
|
);
|
||||||
symEncryptedPacket.packets = new packet.List();
|
|
||||||
return resultMsg;
|
if (symEncryptedPacketlist.length === 0) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const symEncryptedPacket = symEncryptedPacketlist[0];
|
||||||
|
return symEncryptedPacket.decrypt(keyObj.algorithm, keyObj.data).then(() => {
|
||||||
|
const resultMsg = new Message(symEncryptedPacket.packets);
|
||||||
|
symEncryptedPacket.packets = new packet.List(); // remove packets after decryption
|
||||||
|
return resultMsg;
|
||||||
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -205,7 +215,8 @@ Message.prototype.getText = function() {
|
||||||
* @return {Message} new message with encrypted content
|
* @return {Message} new message with encrypted content
|
||||||
*/
|
*/
|
||||||
Message.prototype.encrypt = function(keys, passwords) {
|
Message.prototype.encrypt = function(keys, passwords) {
|
||||||
var symAlgo;
|
let symAlgo, msg, symEncryptedPacket;
|
||||||
|
return Promise.resolve().then(() => {
|
||||||
if (keys) {
|
if (keys) {
|
||||||
symAlgo = keyModule.getPreferredSymAlgo(keys);
|
symAlgo = keyModule.getPreferredSymAlgo(keys);
|
||||||
} else if (passwords) {
|
} else if (passwords) {
|
||||||
|
@ -214,23 +225,25 @@ Message.prototype.encrypt = function(keys, passwords) {
|
||||||
throw new Error('No keys or passwords');
|
throw new Error('No keys or passwords');
|
||||||
}
|
}
|
||||||
|
|
||||||
var sessionKey = crypto.generateSessionKey(enums.read(enums.symmetric, symAlgo));
|
let sessionKey = crypto.generateSessionKey(enums.read(enums.symmetric, symAlgo));
|
||||||
var msg = encryptSessionKey(sessionKey, enums.read(enums.symmetric, symAlgo), keys, passwords);
|
msg = encryptSessionKey(sessionKey, enums.read(enums.symmetric, symAlgo), keys, passwords);
|
||||||
var packetlist = msg.packets;
|
|
||||||
|
|
||||||
var symEncryptedPacket;
|
if (config.aead_protect) {
|
||||||
if (config.integrity_protect) {
|
symEncryptedPacket = new packet.SymEncryptedAEADProtected();
|
||||||
|
} else if (config.integrity_protect) {
|
||||||
symEncryptedPacket = new packet.SymEncryptedIntegrityProtected();
|
symEncryptedPacket = new packet.SymEncryptedIntegrityProtected();
|
||||||
} else {
|
} else {
|
||||||
symEncryptedPacket = new packet.SymmetricallyEncrypted();
|
symEncryptedPacket = new packet.SymmetricallyEncrypted();
|
||||||
}
|
}
|
||||||
symEncryptedPacket.packets = this.packets;
|
symEncryptedPacket.packets = this.packets;
|
||||||
symEncryptedPacket.encrypt(enums.read(enums.symmetric, symAlgo), sessionKey);
|
|
||||||
packetlist.push(symEncryptedPacket);
|
|
||||||
// remove packets after encryption
|
|
||||||
symEncryptedPacket.packets = new packet.List();
|
|
||||||
|
|
||||||
|
return symEncryptedPacket.encrypt(enums.read(enums.symmetric, symAlgo), sessionKey);
|
||||||
|
|
||||||
|
}).then(() => {
|
||||||
|
msg.packets.push(symEncryptedPacket);
|
||||||
|
symEncryptedPacket.packets = new packet.List(); // remove packets after encryption
|
||||||
return msg;
|
return msg;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -99,7 +99,7 @@ export function destroyWorker() {
|
||||||
export function generateKey({ userIds=[], passphrase, numBits=2048, unlocked=false } = {}) {
|
export function generateKey({ userIds=[], passphrase, numBits=2048, unlocked=false } = {}) {
|
||||||
const options = formatUserIds({ userIds, passphrase, numBits, unlocked });
|
const options = formatUserIds({ userIds, passphrase, numBits, unlocked });
|
||||||
|
|
||||||
if (!util.getWebCrypto() && asyncProxy) { // use web worker if web crypto apis are not supported
|
if (!util.getWebCryptoAll() && asyncProxy) { // use web worker if web crypto apis are not supported
|
||||||
return asyncProxy.delegate('generateKey', options);
|
return asyncProxy.delegate('generateKey', options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,18 +109,7 @@ export function generateKey({ userIds=[], passphrase, numBits=2048, unlocked=fal
|
||||||
privateKeyArmored: newKey.armor(),
|
privateKeyArmored: newKey.armor(),
|
||||||
publicKeyArmored: newKey.toPublic().armor()
|
publicKeyArmored: newKey.toPublic().armor()
|
||||||
|
|
||||||
})).catch(err => {
|
})).catch(onError.bind(null, 'Error generating keypair'));
|
||||||
|
|
||||||
// js fallback already tried
|
|
||||||
if (config.debug) { console.error(err); }
|
|
||||||
if (!util.getWebCrypto()) {
|
|
||||||
throw new Error('Error generating keypair using js fallback');
|
|
||||||
}
|
|
||||||
// fall back to js keygen in a worker
|
|
||||||
if (config.debug) { console.log('Error generating keypair using native WebCrypto... falling back back to js'); }
|
|
||||||
return asyncProxy.delegate('generateKey', options);
|
|
||||||
|
|
||||||
}).catch(onError.bind(null, 'Error generating keypair'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -169,17 +158,19 @@ export function decryptKey({ privateKey, passphrase }) {
|
||||||
export function encrypt({ data, publicKeys, privateKeys, passwords, filename, armor=true }) {
|
export function encrypt({ data, publicKeys, privateKeys, passwords, filename, armor=true }) {
|
||||||
checkData(data); publicKeys = toArray(publicKeys); privateKeys = toArray(privateKeys); passwords = toArray(passwords);
|
checkData(data); publicKeys = toArray(publicKeys); privateKeys = toArray(privateKeys); passwords = toArray(passwords);
|
||||||
|
|
||||||
if (asyncProxy) { // use web worker if available
|
if (!nativeAEAD() && asyncProxy) { // use web worker if web crypto apis are not supported
|
||||||
return asyncProxy.delegate('encrypt', { data, publicKeys, privateKeys, passwords, filename, armor });
|
return asyncProxy.delegate('encrypt', { data, publicKeys, privateKeys, passwords, filename, armor });
|
||||||
}
|
}
|
||||||
|
|
||||||
return execute(() => {
|
return Promise.resolve().then(() => {
|
||||||
|
|
||||||
let message = createMessage(data, filename);
|
let message = createMessage(data, filename);
|
||||||
if (privateKeys) { // sign the message only if private keys are specified
|
if (privateKeys) { // sign the message only if private keys are specified
|
||||||
message = message.sign(privateKeys);
|
message = message.sign(privateKeys);
|
||||||
}
|
}
|
||||||
message = message.encrypt(publicKeys, passwords);
|
return message.encrypt(publicKeys, passwords);
|
||||||
|
|
||||||
|
}).then(message => {
|
||||||
|
|
||||||
if(armor) {
|
if(armor) {
|
||||||
return {
|
return {
|
||||||
|
@ -190,7 +181,7 @@ export function encrypt({ data, publicKeys, privateKeys, passwords, filename, ar
|
||||||
message: message
|
message: message
|
||||||
};
|
};
|
||||||
|
|
||||||
}, 'Error encrypting message');
|
}).catch(onError.bind(null, 'Error encrypting message'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -209,20 +200,19 @@ export function encrypt({ data, publicKeys, privateKeys, passwords, filename, ar
|
||||||
export function decrypt({ message, privateKey, publicKeys, sessionKey, password, format='utf8' }) {
|
export function decrypt({ message, privateKey, publicKeys, sessionKey, password, format='utf8' }) {
|
||||||
checkMessage(message); publicKeys = toArray(publicKeys);
|
checkMessage(message); publicKeys = toArray(publicKeys);
|
||||||
|
|
||||||
if (asyncProxy) { // use web worker if available
|
if (!nativeAEAD() && asyncProxy) { // use web worker if web crypto apis are not supported
|
||||||
return asyncProxy.delegate('decrypt', { message, privateKey, publicKeys, sessionKey, password, format });
|
return asyncProxy.delegate('decrypt', { message, privateKey, publicKeys, sessionKey, password, format });
|
||||||
}
|
}
|
||||||
|
|
||||||
return execute(() => {
|
return message.decrypt(privateKey, sessionKey, password).then(message => {
|
||||||
|
|
||||||
message = message.decrypt(privateKey, sessionKey, password);
|
|
||||||
const result = parseMessage(message, format);
|
const result = parseMessage(message, format);
|
||||||
if (publicKeys && result.data) { // verify only if publicKeys are specified
|
if (publicKeys && result.data) { // verify only if publicKeys are specified
|
||||||
result.signatures = message.verify(publicKeys);
|
result.signatures = message.verify(publicKeys);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
}, 'Error decrypting message');
|
}).catch(onError.bind(null, 'Error decrypting message'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -483,3 +473,12 @@ function onError(message, error) {
|
||||||
// rethrow new high level error for api users
|
// rethrow new high level error for api users
|
||||||
throw new Error(message + ': ' + error.message);
|
throw new Error(message + ': ' + error.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for AES-GCM support and configuration by the user. Only browsers that
|
||||||
|
* implement the current WebCrypto specification support native AES-GCM.
|
||||||
|
* @return {Boolean} If authenticated encryption should be used
|
||||||
|
*/
|
||||||
|
function nativeAEAD() {
|
||||||
|
return util.getWebCrypto() && config.aead_protect;
|
||||||
|
}
|
|
@ -12,6 +12,8 @@ import * as packets from './all_packets.js'; // re-import module to parse packet
|
||||||
export { default as Compressed } from './compressed.js';
|
export { default as Compressed } from './compressed.js';
|
||||||
/** @see module:packet/sym_encrypted_integrity_protected */
|
/** @see module:packet/sym_encrypted_integrity_protected */
|
||||||
export { default as SymEncryptedIntegrityProtected } from './sym_encrypted_integrity_protected.js';
|
export { default as SymEncryptedIntegrityProtected } from './sym_encrypted_integrity_protected.js';
|
||||||
|
/** @see module:packet/sym_encrypted_aead_protected */
|
||||||
|
export { default as SymEncryptedAEADProtected } from './sym_encrypted_aead_protected.js';
|
||||||
/** @see module:packet/public_key_encrypted_session_key */
|
/** @see module:packet/public_key_encrypted_session_key */
|
||||||
export { default as PublicKeyEncryptedSessionKey } from './public_key_encrypted_session_key.js';
|
export { default as PublicKeyEncryptedSessionKey } from './public_key_encrypted_session_key.js';
|
||||||
/** @see module:packet/sym_encrypted_session_key */
|
/** @see module:packet/sym_encrypted_session_key */
|
||||||
|
|
88
src/packet/sym_encrypted_aead_protected.js
Normal file
88
src/packet/sym_encrypted_aead_protected.js
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
// OpenPGP.js - An OpenPGP implementation in javascript
|
||||||
|
// Copyright (C) 2016 Tankred Hase
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Lesser General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 3.0 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of the Symmetrically Encrypted Authenticated Encryption with Additional Data (AEAD) Protected Data Packet
|
||||||
|
* {@link https://tools.ietf.org/html/draft-ford-openpgp-format-00#section-2.1}: AEAD Protected Data Packet
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import util from '../util.js';
|
||||||
|
import crypto from '../crypto';
|
||||||
|
import enums from '../enums.js';
|
||||||
|
|
||||||
|
const VERSION = 1; // A one-octet version number of the data packet.
|
||||||
|
const IV_LEN = crypto.gcm.ivLength; // currently only AES-GCM is supported
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
export default function SymEncryptedAEADProtected() {
|
||||||
|
this.tag = enums.packet.symEncryptedAEADProtected;
|
||||||
|
this.version = VERSION;
|
||||||
|
this.iv = null;
|
||||||
|
this.encrypted = null;
|
||||||
|
this.packets = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse an encrypted payload of bytes in the order: version, IV, ciphertext (see specification)
|
||||||
|
*/
|
||||||
|
SymEncryptedAEADProtected.prototype.read = function (bytes) {
|
||||||
|
let offset = 0;
|
||||||
|
if (bytes[offset] !== VERSION) { // The only currently defined value is 1.
|
||||||
|
throw new Error('Invalid packet version.');
|
||||||
|
}
|
||||||
|
offset++;
|
||||||
|
this.iv = bytes.subarray(offset, IV_LEN + offset);
|
||||||
|
offset += IV_LEN;
|
||||||
|
this.encrypted = bytes.subarray(offset, bytes.length);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the encrypted payload of bytes in the order: version, IV, ciphertext (see specification)
|
||||||
|
* @return {Uint8Array} The encrypted payload
|
||||||
|
*/
|
||||||
|
SymEncryptedAEADProtected.prototype.write = function () {
|
||||||
|
return util.concatUint8Array([new Uint8Array([this.version]), this.iv, this.encrypted]);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypt the encrypted payload.
|
||||||
|
* @param {String} sessionKeyAlgorithm The session key's cipher algorithm e.g. 'aes128'
|
||||||
|
* @param {Uint8Array} key The session key used to encrypt the payload
|
||||||
|
* @return {Promise<undefined>} Nothing is returned
|
||||||
|
*/
|
||||||
|
SymEncryptedAEADProtected.prototype.decrypt = function (sessionKeyAlgorithm, key) {
|
||||||
|
return crypto.gcm.decrypt(sessionKeyAlgorithm, this.encrypted, key, this.iv).then(decrypted => {
|
||||||
|
this.packets.read(decrypted);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypt the packet list payload.
|
||||||
|
* @param {String} sessionKeyAlgorithm The session key's cipher algorithm e.g. 'aes128'
|
||||||
|
* @param {Uint8Array} key The session key used to encrypt the payload
|
||||||
|
* @return {Promise<undefined>} Nothing is returned
|
||||||
|
*/
|
||||||
|
SymEncryptedAEADProtected.prototype.encrypt = function (sessionKeyAlgorithm, key) {
|
||||||
|
this.iv = crypto.random.getRandomValues(new Uint8Array(IV_LEN)); // generate new random IV
|
||||||
|
return crypto.gcm.encrypt(sessionKeyAlgorithm, this.packets.write(), key, this.iv).then(encrypted => {
|
||||||
|
this.encrypted = encrypted;
|
||||||
|
});
|
||||||
|
};
|
|
@ -95,7 +95,7 @@ SymEncryptedIntegrityProtected.prototype.encrypt = function (sessionKeyAlgorithm
|
||||||
if(sessionKeyAlgorithm.substr(0,3) === 'aes') { // AES optimizations. Native code for node, asmCrypto for browser.
|
if(sessionKeyAlgorithm.substr(0,3) === 'aes') { // AES optimizations. Native code for node, asmCrypto for browser.
|
||||||
var blockSize = crypto.cipher[sessionKeyAlgorithm].blockSize;
|
var blockSize = crypto.cipher[sessionKeyAlgorithm].blockSize;
|
||||||
|
|
||||||
if(nodeCrypto) { // Node crypto library. Only loaded if config.useNative === true
|
if(nodeCrypto) { // Node crypto library. Only loaded if config.use_native === true
|
||||||
var cipherObj = new nodeCrypto.createCipheriv('aes-' + sessionKeyAlgorithm.substr(3,3) + '-cfb',
|
var cipherObj = new nodeCrypto.createCipheriv('aes-' + sessionKeyAlgorithm.substr(3,3) + '-cfb',
|
||||||
new Buffer(key), new Buffer(new Uint8Array(blockSize)));
|
new Buffer(key), new Buffer(new Uint8Array(blockSize)));
|
||||||
this.encrypted = new Uint8Array(cipherObj.update(new Buffer(util.concatUint8Array([prefix, tohash]))));
|
this.encrypted = new Uint8Array(cipherObj.update(new Buffer(util.concatUint8Array([prefix, tohash]))));
|
||||||
|
@ -108,6 +108,8 @@ SymEncryptedIntegrityProtected.prototype.encrypt = function (sessionKeyAlgorithm
|
||||||
this.encrypted = crypto.cfb.encrypt(prefixrandom, sessionKeyAlgorithm, tohash, key, false)
|
this.encrypted = crypto.cfb.encrypt(prefixrandom, sessionKeyAlgorithm, tohash, key, false)
|
||||||
.subarray(0, prefix.length + tohash.length);
|
.subarray(0, prefix.length + tohash.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -125,7 +127,7 @@ SymEncryptedIntegrityProtected.prototype.decrypt = function (sessionKeyAlgorithm
|
||||||
if(sessionKeyAlgorithm.substr(0,3) === 'aes') { // AES optimizations. Native code for node, asmCrypto for browser.
|
if(sessionKeyAlgorithm.substr(0,3) === 'aes') { // AES optimizations. Native code for node, asmCrypto for browser.
|
||||||
var blockSize = crypto.cipher[sessionKeyAlgorithm].blockSize;
|
var blockSize = crypto.cipher[sessionKeyAlgorithm].blockSize;
|
||||||
|
|
||||||
if(nodeCrypto) { // Node crypto library. Only loaded if config.useNative === true
|
if(nodeCrypto) { // Node crypto library. Only loaded if config.use_native === true
|
||||||
var decipherObj = new nodeCrypto.createDecipheriv('aes-' + sessionKeyAlgorithm.substr(3,3) + '-cfb',
|
var decipherObj = new nodeCrypto.createDecipheriv('aes-' + sessionKeyAlgorithm.substr(3,3) + '-cfb',
|
||||||
new Buffer(key), new Buffer(new Uint8Array(blockSize)));
|
new Buffer(key), new Buffer(new Uint8Array(blockSize)));
|
||||||
decrypted = new Uint8Array(decipherObj.update(new Buffer(this.encrypted)));
|
decrypted = new Uint8Array(decipherObj.update(new Buffer(this.encrypted)));
|
||||||
|
@ -153,4 +155,6 @@ SymEncryptedIntegrityProtected.prototype.decrypt = function (sessionKeyAlgorithm
|
||||||
} else {
|
} else {
|
||||||
this.packets.read(decrypted.subarray(0, decrypted.length - 22));
|
this.packets.read(decrypted.subarray(0, decrypted.length - 22));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
};
|
};
|
||||||
|
|
|
@ -73,11 +73,14 @@ SymmetricallyEncrypted.prototype.decrypt = function (sessionKeyAlgorithm, key) {
|
||||||
throw new Error('Decryption failed due to missing MDC in combination with modern cipher.');
|
throw new Error('Decryption failed due to missing MDC in combination with modern cipher.');
|
||||||
}
|
}
|
||||||
this.packets.read(decrypted);
|
this.packets.read(decrypted);
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
};
|
};
|
||||||
|
|
||||||
SymmetricallyEncrypted.prototype.encrypt = function (algo, key) {
|
SymmetricallyEncrypted.prototype.encrypt = function (algo, key) {
|
||||||
var data = this.packets.write();
|
var data = this.packets.write();
|
||||||
|
|
||||||
this.encrypted = crypto.cfb.encrypt(
|
this.encrypted = crypto.cfb.encrypt(crypto.getPrefixRandom(algo), algo, data, key, true);
|
||||||
crypto.getPrefixRandom(algo), algo, data, key, true);
|
|
||||||
|
return Promise.resolve();
|
||||||
};
|
};
|
||||||
|
|
28
src/util.js
28
src/util.js
|
@ -61,7 +61,7 @@ export default {
|
||||||
* @return {Array<ArrayBuffer>} an array of binary data to be passed
|
* @return {Array<ArrayBuffer>} an array of binary data to be passed
|
||||||
*/
|
*/
|
||||||
getTransferables: function(obj) {
|
getTransferables: function(obj) {
|
||||||
if (config.zeroCopy && Object.prototype.isPrototypeOf(obj)) {
|
if (config.zero_copy && Object.prototype.isPrototypeOf(obj)) {
|
||||||
const transferables = [];
|
const transferables = [];
|
||||||
this.collectBuffers(obj, transferables);
|
this.collectBuffers(obj, transferables);
|
||||||
return transferables.length ? transferables : undefined;
|
return transferables.length ? transferables : undefined;
|
||||||
|
@ -450,12 +450,28 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get native Web Cryptography api. The default configuration is to use
|
* Get native Web Cryptography api, only the current version of the spec.
|
||||||
* the api when available. But it can also be deactivated with config.useNative
|
* The default configuration is to use the api when available. But it can
|
||||||
|
* be deactivated with config.use_native
|
||||||
* @return {Object} The SubtleCrypto api or 'undefined'
|
* @return {Object} The SubtleCrypto api or 'undefined'
|
||||||
*/
|
*/
|
||||||
getWebCrypto: function() {
|
getWebCrypto: function() {
|
||||||
if (!config.useNative) {
|
if (!config.use_native) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return typeof window !== 'undefined' && window.crypto && window.crypto.subtle;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get native Web Cryptography api for all browsers, including legacy
|
||||||
|
* implementations of the spec e.g IE11 and Safari 8/9. The default
|
||||||
|
* configuration is to use the api when available. But it can be deactivated
|
||||||
|
* with config.use_native
|
||||||
|
* @return {Object} The SubtleCrypto api or 'undefined'
|
||||||
|
*/
|
||||||
|
getWebCryptoAll: function() {
|
||||||
|
if (!config.use_native) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -512,11 +528,11 @@ export default {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get native Node.js crypto api. The default configuration is to use
|
* Get native Node.js crypto api. The default configuration is to use
|
||||||
* the api when available. But it can also be deactivated with config.useNative
|
* the api when available. But it can also be deactivated with config.use_native
|
||||||
* @return {Object} The crypto module or 'undefined'
|
* @return {Object} The crypto module or 'undefined'
|
||||||
*/
|
*/
|
||||||
getNodeCrypto: function() {
|
getNodeCrypto: function() {
|
||||||
if (!this.detectNode() || !config.useNative) {
|
if (!this.detectNode() || !config.use_native) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var openpgp = typeof window != 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp');
|
var openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp');
|
||||||
|
|
||||||
var chai = require('chai'),
|
var chai = require('chai'),
|
||||||
expect = chai.expect;
|
expect = chai.expect;
|
||||||
|
@ -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 () {
|
it("Symmetric with OpenPGP CFB resync", function () {
|
||||||
testCFB("hello", true);
|
testCFB("hello", true);
|
||||||
testCFB("1234567", true);
|
testCFB("1234567", true);
|
||||||
|
@ -301,11 +320,37 @@ describe('API functional testing', function() {
|
||||||
testCFB("12345678901234567890123456789012345678901234567890", false);
|
testCFB("12345678901234567890123456789012345678901234567890", false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("asmCrypto AES without OpenPGP CFB resync", function () {
|
it.skip("asmCrypto AES without OpenPGP CFB resync", function () {
|
||||||
testCFB("hello");
|
testAESCFB("hello");
|
||||||
testCFB("1234567");
|
testAESCFB("1234567");
|
||||||
testCFB("foobarfoobar1234567890");
|
testAESCFB("foobarfoobar1234567890");
|
||||||
testCFB("12345678901234567890123456789012345678901234567890");
|
testAESCFB("12345678901234567890123456789012345678901234567890");
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Symmetric AES-GCM (native)', function() {
|
||||||
|
var use_nativeVal;
|
||||||
|
beforeEach(function() {
|
||||||
|
use_nativeVal = openpgp.config.use_native;
|
||||||
|
openpgp.config.use_native = true;
|
||||||
|
});
|
||||||
|
afterEach(function() {
|
||||||
|
openpgp.config.use_native = use_nativeVal;
|
||||||
|
});
|
||||||
|
|
||||||
|
testAESGCM("12345678901234567890123456789012345678901234567890");
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Symmetric AES-GCM (asm.js fallback)', function() {
|
||||||
|
var use_nativeVal;
|
||||||
|
beforeEach(function() {
|
||||||
|
use_nativeVal = openpgp.config.use_native;
|
||||||
|
openpgp.config.use_native = false;
|
||||||
|
});
|
||||||
|
afterEach(function() {
|
||||||
|
openpgp.config.use_native = use_nativeVal;
|
||||||
|
});
|
||||||
|
|
||||||
|
testAESGCM("12345678901234567890123456789012345678901234567890");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Asymmetric using RSA with eme_pkcs1 padding', function (done) {
|
it('Asymmetric using RSA with eme_pkcs1 padding', function (done) {
|
||||||
|
|
|
@ -599,13 +599,13 @@ var pgp_desktop_priv =
|
||||||
expect(prefAlgo).to.equal(openpgp.enums.symmetric.aes256);
|
expect(prefAlgo).to.equal(openpgp.enums.symmetric.aes256);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getPreferredSymAlgo() - two key - AES192', function() {
|
it('getPreferredSymAlgo() - two key - AES128', function() {
|
||||||
var keys = openpgp.key.readArmored(twoKeys).keys;
|
var keys = openpgp.key.readArmored(twoKeys).keys;
|
||||||
var key1 = keys[0];
|
var key1 = keys[0];
|
||||||
var key2 = keys[1];
|
var key2 = keys[1];
|
||||||
key2.getPrimaryUser().selfCertificate.preferredSymmetricAlgorithms = [6,8,3];
|
key2.getPrimaryUser().selfCertificate.preferredSymmetricAlgorithms = [6,7,3];
|
||||||
var prefAlgo = openpgp.key.getPreferredSymAlgo([key1, key2]);
|
var prefAlgo = openpgp.key.getPreferredSymAlgo([key1, key2]);
|
||||||
expect(prefAlgo).to.equal(openpgp.enums.symmetric.aes192);
|
expect(prefAlgo).to.equal(openpgp.enums.symmetric.aes128);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getPreferredSymAlgo() - two key - one without pref', function() {
|
it('getPreferredSymAlgo() - two key - one without pref', function() {
|
||||||
|
@ -626,7 +626,7 @@ var pgp_desktop_priv =
|
||||||
expect(key.subKeys[0].bindingSignature.keyFlags[0] & keyFlags.encrypt_communication).to.equal(keyFlags.encrypt_communication);
|
expect(key.subKeys[0].bindingSignature.keyFlags[0] & keyFlags.encrypt_communication).to.equal(keyFlags.encrypt_communication);
|
||||||
expect(key.subKeys[0].bindingSignature.keyFlags[0] & keyFlags.encrypt_storage).to.equal(keyFlags.encrypt_storage);
|
expect(key.subKeys[0].bindingSignature.keyFlags[0] & keyFlags.encrypt_storage).to.equal(keyFlags.encrypt_storage);
|
||||||
var sym = openpgp.enums.symmetric;
|
var sym = openpgp.enums.symmetric;
|
||||||
expect(key.users[0].selfCertifications[0].preferredSymmetricAlgorithms).to.eql([sym.aes256, sym.aes192, sym.aes128, sym.cast5, sym.tripledes]);
|
expect(key.users[0].selfCertifications[0].preferredSymmetricAlgorithms).to.eql([sym.aes256, sym.aes128, sym.aes192, sym.cast5, sym.tripledes]);
|
||||||
var hash = openpgp.enums.hash;
|
var hash = openpgp.enums.hash;
|
||||||
expect(key.users[0].selfCertifications[0].preferredHashAlgorithms).to.eql([hash.sha256, hash.sha1, hash.sha512]);
|
expect(key.users[0].selfCertifications[0].preferredHashAlgorithms).to.eql([hash.sha256, hash.sha1, hash.sha512]);
|
||||||
var compr = openpgp.enums.compression;
|
var compr = openpgp.enums.compression;
|
||||||
|
@ -634,7 +634,7 @@ var pgp_desktop_priv =
|
||||||
expect(key.users[0].selfCertifications[0].features).to.eql(openpgp.config.integrity_protect ? [1] : null); // modification detection
|
expect(key.users[0].selfCertifications[0].features).to.eql(openpgp.config.integrity_protect ? [1] : null); // modification detection
|
||||||
};
|
};
|
||||||
var opt = {numBits: 512, userIds: 'test <a@b.com>', passphrase: 'hello'};
|
var opt = {numBits: 512, userIds: 'test <a@b.com>', passphrase: 'hello'};
|
||||||
if (openpgp.util.getWebCrypto()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys
|
if (openpgp.util.getWebCryptoAll()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys
|
||||||
openpgp.generateKey(opt).then(function(key) {
|
openpgp.generateKey(opt).then(function(key) {
|
||||||
testPref(key.key);
|
testPref(key.key);
|
||||||
testPref(openpgp.key.readArmored(key.publicKeyArmored).keys[0]);
|
testPref(openpgp.key.readArmored(key.publicKeyArmored).keys[0]);
|
||||||
|
@ -658,11 +658,15 @@ var pgp_desktop_priv =
|
||||||
|
|
||||||
it('Generated key is not unlocked by default', function(done) {
|
it('Generated key is not unlocked by default', function(done) {
|
||||||
var opt = {numBits: 512, userIds: 'test <a@b.com>', passphrase: '123'};
|
var opt = {numBits: 512, userIds: 'test <a@b.com>', passphrase: '123'};
|
||||||
if (openpgp.util.getWebCrypto()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys
|
if (openpgp.util.getWebCryptoAll()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys
|
||||||
openpgp.generateKey(opt).then(function(key) {
|
var key;
|
||||||
var msg = openpgp.message.fromText('hello').encrypt([key.key]);
|
openpgp.generateKey(opt).then(function(newKey) {
|
||||||
msg = msg.decrypt.bind(msg, key.key);
|
key = newKey;
|
||||||
expect(msg).to.throw('Private key is not decrypted.');
|
return openpgp.message.fromText('hello').encrypt([key.key]);
|
||||||
|
}).then(function(msg) {
|
||||||
|
return msg.decrypt(key.key);
|
||||||
|
}).catch(function(err) {
|
||||||
|
expect(err.message).to.equal('Private key is not decrypted.');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -670,7 +674,7 @@ var pgp_desktop_priv =
|
||||||
it('Generate key - single userid', function(done) {
|
it('Generate key - single userid', function(done) {
|
||||||
var userId = 'test <a@b.com>';
|
var userId = 'test <a@b.com>';
|
||||||
var opt = {numBits: 512, userIds: userId, passphrase: '123'};
|
var opt = {numBits: 512, userIds: userId, passphrase: '123'};
|
||||||
if (openpgp.util.getWebCrypto()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys
|
if (openpgp.util.getWebCryptoAll()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys
|
||||||
openpgp.generateKey(opt).then(function(key) {
|
openpgp.generateKey(opt).then(function(key) {
|
||||||
key = key.key;
|
key = key.key;
|
||||||
expect(key.users.length).to.equal(1);
|
expect(key.users.length).to.equal(1);
|
||||||
|
@ -683,7 +687,7 @@ var pgp_desktop_priv =
|
||||||
var userId1 = 'test <a@b.com>';
|
var userId1 = 'test <a@b.com>';
|
||||||
var userId2 = 'test <b@c.com>';
|
var userId2 = 'test <b@c.com>';
|
||||||
var opt = {numBits: 512, userIds: [userId1, userId2], passphrase: '123'};
|
var opt = {numBits: 512, userIds: [userId1, userId2], passphrase: '123'};
|
||||||
if (openpgp.util.getWebCrypto()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys
|
if (openpgp.util.getWebCryptoAll()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys
|
||||||
openpgp.generateKey(opt).then(function(key) {
|
openpgp.generateKey(opt).then(function(key) {
|
||||||
key = key.key;
|
key = key.key;
|
||||||
expect(key.users.length).to.equal(2);
|
expect(key.users.length).to.equal(2);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* globals tryWorker: true */
|
/* globals tryTests: true */
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
@ -184,7 +184,7 @@ describe('OpenPGP.js public api tests', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('generateKey - unit tests', function() {
|
describe('generateKey - unit tests', function() {
|
||||||
var keyGenStub, keyObjStub, getWebCryptoStub;
|
var keyGenStub, keyObjStub, getWebCryptoAllStub;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
keyObjStub = {
|
keyObjStub = {
|
||||||
|
@ -201,13 +201,13 @@ describe('OpenPGP.js public api tests', function() {
|
||||||
};
|
};
|
||||||
keyGenStub = sinon.stub(openpgp.key, 'generate');
|
keyGenStub = sinon.stub(openpgp.key, 'generate');
|
||||||
keyGenStub.returns(resolves(keyObjStub));
|
keyGenStub.returns(resolves(keyObjStub));
|
||||||
getWebCryptoStub = sinon.stub(openpgp.util, 'getWebCrypto');
|
getWebCryptoAllStub = sinon.stub(openpgp.util, 'getWebCryptoAll');
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function() {
|
||||||
keyGenStub.restore();
|
keyGenStub.restore();
|
||||||
openpgp.destroyWorker();
|
openpgp.destroyWorker();
|
||||||
getWebCryptoStub.restore();
|
getWebCryptoAllStub.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail for invalid user name', function() {
|
it('should fail for invalid user name', function() {
|
||||||
|
@ -333,47 +333,28 @@ describe('OpenPGP.js public api tests', function() {
|
||||||
worker: workerStub
|
worker: workerStub
|
||||||
});
|
});
|
||||||
var proxyGenStub = sinon.stub(openpgp.getWorker(), 'delegate');
|
var proxyGenStub = sinon.stub(openpgp.getWorker(), 'delegate');
|
||||||
getWebCryptoStub.returns();
|
getWebCryptoAllStub.returns();
|
||||||
|
|
||||||
openpgp.generateKey();
|
openpgp.generateKey();
|
||||||
expect(proxyGenStub.calledOnce).to.be.true;
|
expect(proxyGenStub.calledOnce).to.be.true;
|
||||||
expect(keyGenStub.calledOnce).to.be.false;
|
expect(keyGenStub.calledOnce).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should delegate to async proxy after web crypto failure', function(done) {
|
|
||||||
var workerStub = {
|
|
||||||
postMessage: function() {}
|
|
||||||
};
|
|
||||||
openpgp.initWorker({
|
|
||||||
worker: workerStub
|
|
||||||
});
|
|
||||||
var proxyGenStub = sinon.stub(openpgp.getWorker(), 'delegate').returns(resolves('proxy_key'));
|
|
||||||
getWebCryptoStub.returns({});
|
|
||||||
keyGenStub.returns(rejects(new Error('Native webcrypto keygen failed on purpose :)')));
|
|
||||||
|
|
||||||
openpgp.generateKey().then(function(newKey) {
|
|
||||||
expect(keyGenStub.calledOnce).to.be.true;
|
|
||||||
expect(proxyGenStub.calledOnce).to.be.true;
|
|
||||||
expect(newKey).to.equal('proxy_key');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('generateKey - integration tests', function() {
|
describe('generateKey - integration tests', function() {
|
||||||
var useNativeVal;
|
var use_nativeVal;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
useNativeVal = openpgp.config.useNative;
|
use_nativeVal = openpgp.config.use_native;
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function() {
|
||||||
openpgp.config.useNative = useNativeVal;
|
openpgp.config.use_native = use_nativeVal;
|
||||||
openpgp.destroyWorker();
|
openpgp.destroyWorker();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work in JS (without worker)', function(done) {
|
it('should work in JS (without worker)', function(done) {
|
||||||
openpgp.config.useNative = false;
|
openpgp.config.use_native = false;
|
||||||
openpgp.destroyWorker();
|
openpgp.destroyWorker();
|
||||||
var opt = {
|
var opt = {
|
||||||
userIds: [{ name: 'Test User', email: 'text@example.com' }],
|
userIds: [{ name: 'Test User', email: 'text@example.com' }],
|
||||||
|
@ -389,7 +370,7 @@ describe('OpenPGP.js public api tests', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work in JS (with worker)', function(done) {
|
it('should work in JS (with worker)', function(done) {
|
||||||
openpgp.config.useNative = false;
|
openpgp.config.use_native = false;
|
||||||
openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
|
openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
|
||||||
var opt = {
|
var opt = {
|
||||||
userIds: [{ name: 'Test User', email: 'text@example.com' }],
|
userIds: [{ name: 'Test User', email: 'text@example.com' }],
|
||||||
|
@ -405,12 +386,12 @@ describe('OpenPGP.js public api tests', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work in with native crypto', function(done) {
|
it('should work in with native crypto', function(done) {
|
||||||
openpgp.config.useNative = true;
|
openpgp.config.use_native = true;
|
||||||
var opt = {
|
var opt = {
|
||||||
userIds: [{ name: 'Test User', email: 'text@example.com' }],
|
userIds: [{ name: 'Test User', email: 'text@example.com' }],
|
||||||
numBits: 512
|
numBits: 512
|
||||||
};
|
};
|
||||||
if (openpgp.util.getWebCrypto()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys
|
if (openpgp.util.getWebCryptoAll()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys
|
||||||
|
|
||||||
openpgp.generateKey(opt).then(function(newKey) {
|
openpgp.generateKey(opt).then(function(newKey) {
|
||||||
expect(newKey.key.getUserIds()[0]).to.equal('Test User <text@example.com>');
|
expect(newKey.key.getUserIds()[0]).to.equal('Test User <text@example.com>');
|
||||||
|
@ -422,7 +403,7 @@ describe('OpenPGP.js public api tests', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('encrypt, decrypt, sign, verify - integration tests', function() {
|
describe('encrypt, decrypt, sign, verify - integration tests', function() {
|
||||||
var privateKey, publicKey, zeroCopyVal;
|
var privateKey, publicKey, zero_copyVal, use_nativeVal, aead_protectVal;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
publicKey = openpgp.key.readArmored(pub_key);
|
publicKey = openpgp.key.readArmored(pub_key);
|
||||||
|
@ -431,11 +412,15 @@ describe('OpenPGP.js public api tests', function() {
|
||||||
privateKey = openpgp.key.readArmored(priv_key);
|
privateKey = openpgp.key.readArmored(priv_key);
|
||||||
expect(privateKey.keys).to.have.length(1);
|
expect(privateKey.keys).to.have.length(1);
|
||||||
expect(privateKey.err).to.not.exist;
|
expect(privateKey.err).to.not.exist;
|
||||||
zeroCopyVal = openpgp.config.zeroCopy;
|
zero_copyVal = openpgp.config.zero_copy;
|
||||||
|
use_nativeVal = openpgp.config.use_native;
|
||||||
|
aead_protectVal = openpgp.config.aead_protect;
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function() {
|
||||||
openpgp.config.zeroCopy = zeroCopyVal;
|
openpgp.config.zero_copy = zero_copyVal;
|
||||||
|
openpgp.config.use_native = use_nativeVal;
|
||||||
|
openpgp.config.aead_protect = aead_protectVal;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Decrypting key with wrong passphrase returns false', function () {
|
it('Decrypting key with wrong passphrase returns false', function () {
|
||||||
|
@ -446,18 +431,43 @@ describe('OpenPGP.js public api tests', function() {
|
||||||
expect(privateKey.keys[0].decrypt(passphrase)).to.be.true;
|
expect(privateKey.keys[0].decrypt(passphrase)).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('without Worker', tests);
|
tryTests('CFB mode (asm.js)', tests, {
|
||||||
|
if: true,
|
||||||
|
beforeEach: function() {
|
||||||
|
openpgp.config.use_native = true;
|
||||||
|
openpgp.config.aead_protect = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
tryWorker('with Worker', tests, function() {
|
tryTests('CFB mode (asm.js, worker)', tests, {
|
||||||
|
if: typeof window !== 'undefined' && window.Worker,
|
||||||
|
before: function() {
|
||||||
openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
|
openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
|
||||||
}, function() {
|
},
|
||||||
openpgp.destroyWorker(); // cleanup worker in case of failure
|
beforeEach: function() {
|
||||||
|
openpgp.config.use_native = true;
|
||||||
|
openpgp.config.aead_protect = false;
|
||||||
|
},
|
||||||
|
after: function() {
|
||||||
|
openpgp.destroyWorker();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
tryTests('GCM mode (native)', tests, {
|
||||||
|
if: openpgp.util.getWebCrypto() || openpgp.util.getNodeCrypto(),
|
||||||
|
beforeEach: function() {
|
||||||
|
openpgp.config.use_native = true;
|
||||||
|
openpgp.config.aead_protect = true;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function tests() {
|
function tests() {
|
||||||
it('Configuration', function(done){
|
it('Configuration', function(done){
|
||||||
openpgp.config.show_version = false;
|
openpgp.config.show_version = false;
|
||||||
openpgp.config.commentstring = 'different';
|
openpgp.config.commentstring = 'different';
|
||||||
|
if (openpgp.getWorker()) { // init again to trigger config event
|
||||||
|
openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
|
||||||
|
}
|
||||||
openpgp.encrypt({ publicKeys:publicKey.keys, data:plaintext }).then(function(encrypted) {
|
openpgp.encrypt({ publicKeys:publicKey.keys, data:plaintext }).then(function(encrypted) {
|
||||||
expect(encrypted.data).to.exist;
|
expect(encrypted.data).to.exist;
|
||||||
expect(encrypted.data).not.to.match(/^Version:/);
|
expect(encrypted.data).not.to.match(/^Version:/);
|
||||||
|
@ -875,7 +885,7 @@ describe('OpenPGP.js public api tests', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should encrypt and decrypt with binary data and transferable objects', function(done) {
|
it('should encrypt and decrypt with binary data and transferable objects', function(done) {
|
||||||
openpgp.config.zeroCopy = true; // activate transferable objects
|
openpgp.config.zero_copy = true; // activate transferable objects
|
||||||
var encOpt = {
|
var encOpt = {
|
||||||
data: new Uint8Array([0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01]),
|
data: new Uint8Array([0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01]),
|
||||||
passwords: password1,
|
passwords: password1,
|
||||||
|
|
|
@ -121,6 +121,29 @@ describe("Packet", function() {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Sym. encrypted AEAD protected packet', function(done) {
|
||||||
|
var key = new Uint8Array([1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2]),
|
||||||
|
algo = 'aes256';
|
||||||
|
|
||||||
|
var literal = new openpgp.packet.Literal(),
|
||||||
|
enc = new openpgp.packet.SymEncryptedAEADProtected(),
|
||||||
|
msg = new openpgp.packet.List();
|
||||||
|
|
||||||
|
msg.push(enc);
|
||||||
|
literal.setText('Hello world!');
|
||||||
|
enc.packets.push(literal);
|
||||||
|
|
||||||
|
var msg2 = new openpgp.packet.List();
|
||||||
|
|
||||||
|
enc.encrypt(algo, key).then(function() {
|
||||||
|
msg2.read(msg.write());
|
||||||
|
return msg2[0].decrypt(algo, key);
|
||||||
|
}).then(function() {
|
||||||
|
expect(msg2[0].packets[0].data).to.deep.equal(literal.data);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Sym encrypted session key with a compressed packet', function(done) {
|
it('Sym encrypted session key with a compressed packet', function(done) {
|
||||||
var msg =
|
var msg =
|
||||||
'-----BEGIN PGP MESSAGE-----\n' +
|
'-----BEGIN PGP MESSAGE-----\n' +
|
||||||
|
@ -150,7 +173,7 @@ describe("Packet", function() {
|
||||||
|
|
||||||
it('Public key encrypted symmetric key packet', function(done) {
|
it('Public key encrypted symmetric key packet', function(done) {
|
||||||
var rsa = new openpgp.crypto.publicKey.rsa();
|
var rsa = new openpgp.crypto.publicKey.rsa();
|
||||||
var keySize = openpgp.util.getWebCrypto() ? 2048 : 512; // webkit webcrypto accepts minimum 2048 bit keys
|
var keySize = openpgp.util.getWebCryptoAll() ? 2048 : 512; // webkit webcrypto accepts minimum 2048 bit keys
|
||||||
|
|
||||||
rsa.generate(keySize, "10001").then(function(mpiGen) {
|
rsa.generate(keySize, "10001").then(function(mpiGen) {
|
||||||
|
|
||||||
|
@ -414,7 +437,7 @@ describe("Packet", function() {
|
||||||
key.push(new openpgp.packet.SecretKey());
|
key.push(new openpgp.packet.SecretKey());
|
||||||
|
|
||||||
var rsa = new openpgp.crypto.publicKey.rsa();
|
var rsa = new openpgp.crypto.publicKey.rsa();
|
||||||
var keySize = openpgp.util.getWebCrypto() ? 2048 : 512; // webkit webcrypto accepts minimum 2048 bit keys
|
var keySize = openpgp.util.getWebCryptoAll() ? 2048 : 512; // webkit webcrypto accepts minimum 2048 bit keys
|
||||||
|
|
||||||
rsa.generate(keySize, "10001").then(function(mipGen) {
|
rsa.generate(keySize, "10001").then(function(mipGen) {
|
||||||
var mpi = [mipGen.n, mipGen.ee, mipGen.d, mipGen.p, mipGen.q, mipGen.u];
|
var mpi = [mipGen.n, mipGen.ee, mipGen.d, mipGen.p, mipGen.q, mipGen.u];
|
||||||
|
@ -443,7 +466,7 @@ describe("Packet", function() {
|
||||||
var key = new openpgp.packet.SecretKey();
|
var key = new openpgp.packet.SecretKey();
|
||||||
|
|
||||||
var rsa = new openpgp.crypto.publicKey.rsa();
|
var rsa = new openpgp.crypto.publicKey.rsa();
|
||||||
var keySize = openpgp.util.getWebCrypto() ? 2048 : 512; // webkit webcrypto accepts minimum 2048 bit keys
|
var keySize = openpgp.util.getWebCryptoAll() ? 2048 : 512; // webkit webcrypto accepts minimum 2048 bit keys
|
||||||
|
|
||||||
rsa.generate(keySize, "10001").then(function(mpiGen) {
|
rsa.generate(keySize, "10001").then(function(mpiGen) {
|
||||||
var mpi = [mpiGen.n, mpiGen.ee, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u];
|
var mpi = [mpiGen.n, mpiGen.ee, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u];
|
||||||
|
|
|
@ -304,13 +304,14 @@ describe("Signature", function() {
|
||||||
var msg = openpgp.message.readArmored(msg_arm1);
|
var msg = openpgp.message.readArmored(msg_arm1);
|
||||||
|
|
||||||
priv_key_gnupg_ext.subKeys[0].subKey.decrypt("abcd");
|
priv_key_gnupg_ext.subKeys[0].subKey.decrypt("abcd");
|
||||||
msg = msg.decrypt(priv_key_gnupg_ext);
|
msg.decrypt(priv_key_gnupg_ext).then(function(msg) {
|
||||||
var verified = msg.verify([pub_key]);
|
var verified = msg.verify([pub_key]);
|
||||||
expect(verified).to.exist;
|
expect(verified).to.exist;
|
||||||
expect(verified).to.have.length(1);
|
expect(verified).to.have.length(1);
|
||||||
expect(verified[0].valid).to.be.true;
|
expect(verified[0].valid).to.be.true;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Verify V4 signature. Hash: SHA1. PK: RSA. Signature Type: 0x00 (binary document)', function(done) {
|
it('Verify V4 signature. Hash: SHA1. PK: RSA. Signature Type: 0x00 (binary document)', function(done) {
|
||||||
var signedArmor =
|
var signedArmor =
|
||||||
|
@ -642,7 +643,7 @@ describe("Signature", function() {
|
||||||
|
|
||||||
it('Sign message with key without password', function(done) {
|
it('Sign message with key without password', function(done) {
|
||||||
var opt = {numBits: 512, userIds: { name:'test', email:'a@b.com' }, passphrase: null};
|
var opt = {numBits: 512, userIds: { name:'test', email:'a@b.com' }, passphrase: null};
|
||||||
if (openpgp.util.getWebCrypto()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys
|
if (openpgp.util.getWebCryptoAll()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys
|
||||||
openpgp.generateKey(opt).then(function(gen) {
|
openpgp.generateKey(opt).then(function(gen) {
|
||||||
var key = gen.key;
|
var key = gen.key;
|
||||||
|
|
||||||
|
|
|
@ -145,7 +145,7 @@ describe('Util unit tests', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getTransferables', function() {
|
describe('getTransferables', function() {
|
||||||
var zeroCopyVal,
|
var zero_copyVal,
|
||||||
buf1 = new Uint8Array(1),
|
buf1 = new Uint8Array(1),
|
||||||
buf2 = new Uint8Array(1),
|
buf2 = new Uint8Array(1),
|
||||||
obj = {
|
obj = {
|
||||||
|
@ -157,16 +157,16 @@ describe('Util unit tests', function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
zeroCopyVal = openpgp.config.zeroCopy;
|
zero_copyVal = openpgp.config.zero_copy;
|
||||||
openpgp.config.zeroCopy = true;
|
openpgp.config.zero_copy = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function() {
|
||||||
openpgp.config.zeroCopy = zeroCopyVal;
|
openpgp.config.zero_copy = zero_copyVal;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return undefined when zeroCopy is false', function() {
|
it('should return undefined when zero_copy is false', function() {
|
||||||
openpgp.config.zeroCopy = false;
|
openpgp.config.zero_copy = false;
|
||||||
expect(openpgp.util.getTransferables(obj)).to.be.undefined;
|
expect(openpgp.util.getTransferables(obj)).to.be.undefined;
|
||||||
});
|
});
|
||||||
it('should return undefined for no input', function() {
|
it('should return undefined for no input', function() {
|
||||||
|
|
|
@ -6,17 +6,19 @@
|
||||||
return new Promise(function(res, rej) { rej(val); });
|
return new Promise(function(res, rej) { rej(val); });
|
||||||
};
|
};
|
||||||
|
|
||||||
(typeof window !== 'undefined' ? window : global).tryWorker = function(name, tests, beforeFn, afterFn) {
|
(typeof window !== 'undefined' ? window : global).tryTests = function(name, tests, options) {
|
||||||
if (typeof window !== 'undefined' && window.Worker) {
|
if (options.if) {
|
||||||
describe(name, function() {
|
describe(name, function() {
|
||||||
before(beforeFn);
|
if (options.before) { before(options.before); }
|
||||||
|
if (options.beforeEach) { beforeEach(options.beforeEach); }
|
||||||
|
|
||||||
tests();
|
tests();
|
||||||
|
|
||||||
after(afterFn);
|
if (options.afterEach) { afterEach(options.afterEach); }
|
||||||
|
if (options.after) { after(options.after); }
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
describe.skip(name + ' (No Web Worker support --> skipping tests)', tests);
|
describe.skip(name + ' (no support --> skipping tests)', tests);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* globals tryWorker: true */
|
/* globals tryTests: true */
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
@ -35,11 +35,15 @@ var pub_key =
|
||||||
var plaintext = 'short message\nnext line\n한국어/조선말';
|
var plaintext = 'short message\nnext line\n한국어/조선말';
|
||||||
var pubKey;
|
var pubKey;
|
||||||
|
|
||||||
tryWorker('Async Proxy', tests, function() {
|
tryTests('Async Proxy', tests, {
|
||||||
|
if: typeof window !== 'undefined' && window.Worker,
|
||||||
|
before: function() {
|
||||||
openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
|
openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
|
||||||
pubKey = openpgp.key.readArmored(pub_key).keys[0];
|
pubKey = openpgp.key.readArmored(pub_key).keys[0];
|
||||||
}, function() {
|
},
|
||||||
|
after: function() {
|
||||||
openpgp.destroyWorker();
|
openpgp.destroyWorker();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function tests() {
|
function tests() {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user