Write tests for new api: openpgp.generateKey

This commit is contained in:
Tankred Hase 2016-02-08 19:32:42 +07:00
parent c38d41036e
commit a44e1e5024
8 changed files with 416 additions and 194 deletions

View File

@ -223,11 +223,10 @@ module.exports = function(grunt) {
}
},
},
watch: {
src: {
files: ['src/**/*.js'],
tasks: ['browserify:openpgp']
tasks: ['browserify:openpgp', 'browserify:worker']
},
test: {
files: ['test/*.js', 'test/crypto/**/*.js', 'test/general/**/*.js', 'test/worker/**/*.js'],

View File

@ -924,7 +924,7 @@ export function readArmored(armoredText) {
* @param {module:enums.publicKey} [options.keyType=module:enums.publicKey.rsa_encrypt_sign] to indicate what type of key to make.
* RSA is 1. See {@link http://tools.ietf.org/html/rfc4880#section-9.1}
* @param {Integer} options.numBits number of bits for the key creation.
* @param {String|Array<String>} options.userId assumes already in form of "User Name <username@email.com>"
* @param {String|Array<String>} options.userIds assumes already in form of "User Name <username@email.com>"
If array is used, the first userId is set as primary user Id
* @param {String} options.passphrase The passphrase used to encrypt the resulting private key
* @param {Boolean} [options.unlocked=false] The secret part of the generated key is unlocked
@ -943,8 +943,8 @@ export function generate(options) {
if (!options.passphrase) {
options.unlocked = true;
}
if (String.prototype.isPrototypeOf(options.userId) || typeof options.userId === 'string') {
options.userId = [options.userId];
if (String.prototype.isPrototypeOf(options.userIds) || typeof options.userIds === 'string') {
options.userIds = [options.userIds];
}
// generate
@ -975,7 +975,7 @@ export function generate(options) {
packetlist.push(secretKeyPacket);
options.userId.forEach(function(userId, index) {
options.userIds.forEach(function(userId, index) {
userIdPacket = new packet.Userid();
userIdPacket.read(util.str2Uint8Array(userId));

View File

@ -42,7 +42,7 @@ es6Promise.polyfill(); // load ES6 Promises polyfill
//////////////////////////
let asyncProxy = null; // instance of the asyncproxy
let asyncProxy; // instance of the asyncproxy
/**
* Set the path for the web worker script and create an instance of the async proxy
@ -65,6 +65,13 @@ export function getWorker() {
return asyncProxy;
}
/**
* Cleanup the current instance of the web worker.
*/
export function destroyWorker() {
asyncProxy = undefined;
}
////////////////////////////
// //
@ -84,8 +91,8 @@ export function getWorker() {
* @static
*/
export function generateKey({ userIds=[], passphrase, numBits=2048, unlocked=false } = {}) {
userIds = userIds.map(id => id.name + ' <' + id.email + '>'); // format user ids for internal use
const options = { userIds, passphrase, numBits, unlocked };
formatUserIds(options);
if (!util.getWebCrypto() && asyncProxy) { // use web worker if web crypto apis are not supported
return asyncProxy.generateKey(options);
@ -100,12 +107,12 @@ export function generateKey({ userIds=[], passphrase, numBits=2048, unlocked=fal
})).catch(err => {
// js fallback already tried
console.error(err);
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
console.log('Error generating keypair using native WebCrypto... falling back back to js!');
if (config.debug) { console.log('Error generating keypair using native WebCrypto... falling back back to js!'); }
return asyncProxy.generateKey(options);
}).catch(onError.bind(null, 'Error generating keypair!'));
@ -132,8 +139,8 @@ export function generateKey({ userIds=[], passphrase, numBits=2048, unlocked=fal
* @static
*/
export function encrypt({ data, publicKeys, privateKeys, passwords, filename, packets }) {
publicKeys = publicKeys ? (publicKeys.length ? publicKeys : [publicKeys]) : undefined; // normalize key objects to arrays
privateKeys = privateKeys ? (privateKeys.length ? privateKeys : [privateKeys]) : undefined;
publicKeys = toArray(publicKeys);
privateKeys = toArray(privateKeys);
if (asyncProxy) { // use web worker if available
return asyncProxy.encrypt({ data, publicKeys, privateKeys, passwords, filename, packets });
@ -170,7 +177,7 @@ export function encrypt({ data, publicKeys, privateKeys, passwords, filename, pa
* @static
*/
export function decrypt({ message, privateKey, publickeys, sessionKey, password, format='utf8' }) {
publickeys = publickeys ? (publickeys.length ? publickeys : [publickeys]) : undefined; // normalize key objects to arrays
publickeys = toArray(publickeys);
if (asyncProxy) { // use web worker if available
return asyncProxy.decrypt({ message, privateKey, publickeys, sessionKey, password, format });
@ -204,7 +211,10 @@ export function decrypt({ message, privateKey, publickeys, sessionKey, password,
* @static
*/
export function sign({ data, privateKeys }) {
privateKeys = privateKeys.length ? privateKeys : [privateKeys];
privateKeys = toArray(privateKeys);
if (!util.isString(data)) {
throw new Error('Only cleartext data of type String supported!');
}
if (asyncProxy) { // use web worker if available
return asyncProxy.sign({ data, privateKeys });
@ -230,23 +240,21 @@ export function sign({ data, privateKeys }) {
* @static
*/
export function verify({ message, publicKeys }) {
publicKeys = publicKeys.length ? publicKeys : [publicKeys];
publicKeys = toArray(publicKeys);
if (!(message instanceof cleartext.CleartextMessage)) {
throw new Error('Parameter [message] needs to be of type CleartextMessage.');
}
if (asyncProxy) { // use web worker if available
return asyncProxy.verify({ message, publicKeys });
}
return execute(() => {
return execute(() => ({
if (!(message instanceof cleartext.CleartextMessage)) {
throw new Error('Parameter [message] needs to be of type CleartextMessage.');
}
return {
data: message.getText(),
signatures: message.verify(publicKeys)
};
data: message.getText(),
signatures: message.verify(publicKeys)
}, 'Error verifying cleartext signed message!');
}), 'Error verifying cleartext signed message!');
}
@ -306,6 +314,43 @@ export function decryptSessionKey({ message, privateKey, sessionKey, password })
//////////////////////////
/**
* Format user ids for internal use.
*/
function formatUserIds(options) {
if (!options.userIds) {
return;
}
options.userIds = toArray(options.userIds); // normalize to array
options.userIds = options.userIds.map(id => {
if (util.isString(id) && !util.isUserId(id)) {
throw new Error('Invalid user id format!');
}
if (util.isUserId(id)) {
return id; // user id is already in correct format... no conversion necessary
}
// name and email address can be empty but must be of type the correct type
id.name = id.name || '';
id.email = id.email || '';
if (!util.isString(id.name) || (id.email && !util.isEmailAddress(id.email))) {
throw new Error('Invalid user id format!');
}
return id.name + ' <' + id.email + '>';
});
}
/**
* Normalize parameter to an array if it is not undefined.
* @param {Object} param the parameter to be normalized
* @return {Array<Object>|undefined} the resulting array or undefined
*/
function toArray(param) {
if (param && !util.isArray(param)) {
param = [param];
}
return param;
}
/**
* Creates a message obejct either from a Uint8Array or a string.
* @param {String|Uint8Array} data the payload for the message
@ -314,9 +359,9 @@ export function decryptSessionKey({ message, privateKey, sessionKey, password })
*/
function createMessage(data, filename) {
let msg;
if (data instanceof Uint8Array) {
if (util.isUint8Array(data)) {
msg = messageLib.fromBinary(data, filename);
} else if (typeof data === 'string') {
} else if (util.isString(data)) {
msg = messageLib.fromText(data, filename);
} else {
throw new Error('Data must be of type String or Uint8Array!');
@ -391,9 +436,7 @@ function execute(cmd, message) {
*/
function onError(message, error) {
// log the stack trace
if (config.debug) {
console.error(error.stack);
}
if (config.debug) { console.error(error.stack); }
// rethrow new high level error for api users
throw new Error(message);
}

View File

@ -31,58 +31,49 @@ import packet from '../packet';
import * as key from '../key.js';
import type_keyid from '../type/keyid.js';
var INITIAL_RANDOM_SEED = 50000, // random bytes seeded to worker
const INITIAL_RANDOM_SEED = 50000, // random bytes seeded to worker
RANDOM_SEED_REQUEST = 20000; // random bytes seeded after worker request
/**
* Initializes a new proxy and loads the web worker
* @constructor
* @param {String} path The path to the worker or 'openpgp.worker.js' by default
* @param {Object} [options.config=Object] config The worker configuration
* @param {Object} [options.worker=Object] alternative to path parameter:
* web worker initialized with 'openpgp.worker.js'
* @param {String} path The path to the worker or 'openpgp.worker.js' by default
* @param {Object} config config The worker configuration
* @param {Object} worker alternative to path parameter: web worker initialized with 'openpgp.worker.js'
* @return {Promise}
*/
export default function AsyncProxy(path, options) {
if (options && options.worker) {
this.worker = options.worker;
} else {
this.worker = new Worker(path || 'openpgp.worker.js');
}
export default function AsyncProxy({ path='openpgp.worker.js', worker, config } = {}) {
this.worker = worker || new Worker(path);
this.worker.onmessage = this.onMessage.bind(this);
this.worker.onerror = function(e) {
this.worker.onerror = e => {
throw new Error('Unhandled error in openpgp worker: ' + e.message + ' (' + e.filename + ':' + e.lineno + ')');
};
this.seedRandom(INITIAL_RANDOM_SEED);
// FIFO
this.tasks = [];
if (options && options.config) {
this.worker.postMessage({event: 'configure', config: options.config});
if (config) {
this.worker.postMessage({ event:'configure', config });
}
}
/**
* Command pattern that wraps synchronous code into a promise
* @param {Object} self The current this
* @param {function} cmd The synchronous function with a return value
* to be wrapped in a promise
* @return {Promise} The promise wrapped around cmd
*/
AsyncProxy.prototype.execute = function(cmd) {
var self = this;
var promise = new Promise(function(resolve, reject) {
return new Promise((resolve, reject) => {
cmd();
self.tasks.push({ resolve:resolve, reject:reject });
this.tasks.push({ resolve, reject });
});
return promise;
};
/**
* Message handling
*/
AsyncProxy.prototype.onMessage = function(event) {
var msg = event.data;
const msg = event.data;
switch (msg.event) {
case 'method-return':
if (msg.err) {
@ -106,8 +97,8 @@ AsyncProxy.prototype.onMessage = function(event) {
* @param {Integer} size Number of bytes to send
*/
AsyncProxy.prototype.seedRandom = function(size) {
var buf = this.getRandomBuffer(size);
this.worker.postMessage({event: 'seed-random', buf: buf});
const buf = this.getRandomBuffer(size);
this.worker.postMessage({ event:'seed-random', buf });
};
/**
@ -119,7 +110,7 @@ AsyncProxy.prototype.getRandomBuffer = function(size) {
if (!size) {
return null;
}
var buf = new Uint8Array(size);
const buf = new Uint8Array(size);
crypto.random.getRandomValues(buf);
return buf;
};
@ -131,71 +122,56 @@ AsyncProxy.prototype.terminate = function() {
this.worker.terminate();
};
/**
* Encrypts message text/data with keys or passwords
* @param {(Array<module:key~Key>|module:key~Key)} keys array of keys or single key, used to encrypt the message
* @param {Uint8Array} data message as Uint8Array
* @param {(Array<String>|String)} passwords passwords for the message
* @param {Object} params parameter object with optional properties binary {Boolean},
* filename {String}, and packets {Boolean}
*/
AsyncProxy.prototype.encryptMessage = function(keys, data, passwords, params) {
var self = this;
return self.execute(function() {
if(keys) {
if (!Array.prototype.isPrototypeOf(keys)) {
keys = [keys];
}
keys = keys.map(function(key) {
return key.toPacketlist();
});
//////////////////////////////////////////////////////////////////////////////////////////////////
// //
// Proxy functions. See the corresponding code in the openpgp module for the documentation. //
// //
//////////////////////////////////////////////////////////////////////////////////////////////////
AsyncProxy.prototype.generateKey = function(options) {
return new Promise((resolve, reject) => {
this.worker.postMessage({
event: 'generate-key',
options: options
});
this.tasks.push({ resolve: data => {
const packetlist = packet.List.fromStructuredClone(data.key);
data.key = new key.Key(packetlist);
resolve(data);
}, reject });
});
};
AsyncProxy.prototype.encrypt = function({ data, publicKeys, privateKeys, passwords, filename, packets }) {
return this.execute(() => {
if(publicKeys) {
publicKeys = publicKeys.length ? publicKeys : [publicKeys];
publicKeys = publicKeys.map(key => key.toPacketlist());
}
self.worker.postMessage({
event: 'encrypt-message',
keys: keys,
data: data,
passwords: passwords,
params: params
if(privateKeys) {
privateKeys = privateKeys.length ? privateKeys : [privateKeys];
privateKeys = privateKeys.map(key => key.toPacketlist());
}
this.worker.postMessage({
event:'encrypt',
options: { data, publicKeys, privateKeys, passwords, filename, packets }
});
});
};
/**
* Encrypts session key with keys or passwords
* @param {Uint8Array} sessionKey sessionKey as Uint8Array
* @param {String} algo algorithm of sessionKey
* @param {(Array<module:key~Key>|module:key~Key)} keys array of keys or single key, used to encrypt the key
* @param {(Array<String>|String)} passwords passwords for the message
*/
AsyncProxy.prototype.encryptSessionKey = function(sessionKey, algo, keys, passwords) {
var self = this;
return self.execute(function() {
AsyncProxy.prototype.encryptSessionKey = function({ sessionKey, algo, keys, passwords }) {
return this.execute(() => {
if(keys) {
if (!Array.prototype.isPrototypeOf(keys)) {
keys = [keys];
}
keys = keys.map(function(key) {
return key.toPacketlist();
});
keys = keys.length ? keys : [keys];
keys = keys.map(key => key.toPacketlist());
}
self.worker.postMessage({
event: 'encrypt-session-key',
sessionKey: sessionKey,
algo: algo,
keys: keys,
passwords: passwords
});
this.worker.postMessage({ event:'encrypt-session-key', sessionKey, algo, keys, passwords });
});
};
/**
* Signs message text and encrypts it
* @param {(Array<module:key~Key>|module:key~Key)} publicKeys array of keys or single key, used to encrypt the message
* @param {module:key~Key} privateKey private key with decrypted secret key data for signing
* @param {Uint8Array} text message as Uint8Array
*/
AsyncProxy.prototype.signAndEncryptMessage = function(publicKeys, privateKey, text) {
var self = this;
@ -216,36 +192,16 @@ AsyncProxy.prototype.signAndEncryptMessage = function(publicKeys, privateKey, te
});
};
/**
* Decrypts message
* @param {module:key~Key|String} privateKey private key with decrypted secret key data or string password
* @param {module:message~Message} msg the message object with the encrypted data
* @param {Object} params parameter object with optional properties binary {Boolean}
* and sessionKeyAlgorithm {String} which must only be set when privateKey is a session key
*/
AsyncProxy.prototype.decryptMessage = function(privateKey, message, params) {
var self = this;
return self.execute(function() {
AsyncProxy.prototype.decryptMessage = function({ message, privateKey, format }) {
return this.execute(() => {
if(!(String.prototype.isPrototypeOf(privateKey) || typeof privateKey === 'string' || Uint8Array.prototype.isPrototypeOf(privateKey))) {
privateKey = privateKey.toPacketlist();
}
self.worker.postMessage({
event: 'decrypt-message',
privateKey: privateKey,
message: message,
params: params
});
this.worker.postMessage({ event:'decrypt-message', message, privateKey, format });
});
};
/**
* @param {module:key~Key|String} privateKey private key with decrypted secret key data or string password
* @param {module:message~Message} msg the message object with the encrypted session key packets
* @return {Promise<Object|null>} decrypted session key and algorithm in object form
* or null if no key packets found
*/
AsyncProxy.prototype.decryptSessionKey = function(privateKey, message) {
var self = this;
@ -262,12 +218,6 @@ AsyncProxy.prototype.decryptSessionKey = function(privateKey, message) {
});
};
/**
* Decrypts message and verifies signatures
* @param {module:key~Key} privateKey private key with decrypted secret key data
* @param {(Array<module:key~Key>|module:key~Key)} publicKeys array of keys or single key to verify signatures
* @param {module:message~Message} message the message object with signed and encrypted data
*/
AsyncProxy.prototype.decryptAndVerifyMessage = function(privateKey, publicKeys, message) {
var self = this;
@ -298,11 +248,6 @@ AsyncProxy.prototype.decryptAndVerifyMessage = function(privateKey, publicKeys,
return promise;
};
/**
* Signs a cleartext message
* @param {(Array<module:key~Key>|module:key~Key)} privateKeys array of keys or single key, with decrypted secret key data to sign cleartext
* @param {Uint8Array} text cleartext
*/
AsyncProxy.prototype.signClearMessage = function(privateKeys, text) {
var self = this;
@ -321,11 +266,6 @@ AsyncProxy.prototype.signClearMessage = function(privateKeys, text) {
});
};
/**
* Verifies signatures of cleartext signed message
* @param {(Array<module:key~Key>|module:key~Key)} publicKeys array of keys or single key, to verify signatures
* @param {module:cleartext~CleartextMessage} message cleartext message object with signatures
*/
AsyncProxy.prototype.verifyClearSignedMessage = function(publicKeys, message) {
var self = this;
@ -354,39 +294,6 @@ AsyncProxy.prototype.verifyClearSignedMessage = function(publicKeys, message) {
return promise;
};
/**
* Generates a new OpenPGP key pair. Currently only supports RSA keys.
* Primary and subkey will be of same type.
* @param {module:enums.publicKey} keyType to indicate what type of key to make.
* RSA is 1. See {@link http://tools.ietf.org/html/rfc4880#section-9.1}
* @param {Integer} numBits number of bits for the key creation. (should be 1024+, generally)
* @param {String} userId assumes already in form of "User Name <username@email.com>"
* @param {String} passphrase The passphrase used to encrypt the resulting private key
*/
AsyncProxy.prototype.generateKeyPair = function(options) {
var self = this;
var promise = new Promise(function(resolve, reject) {
self.worker.postMessage({
event: 'generate-key-pair',
options: options
});
self.tasks.push({ resolve:function(data) {
var packetlist = packet.List.fromStructuredClone(data.key);
data.key = new key.Key(packetlist);
resolve(data);
}, reject:reject });
});
return promise;
};
/**
* Decrypts secret part of all secret key packets of key.
* @param {module:key~Key} privateKey private key with encrypted secret key data
* @param {String} password password to unlock the key
*/
AsyncProxy.prototype.decryptKey = function(privateKey, password) {
var self = this;
@ -408,12 +315,6 @@ AsyncProxy.prototype.decryptKey = function(privateKey, password) {
return promise;
};
/**
* Decrypts secret part of key packets matching array of keyids.
* @param {module:key~Key} privateKey private key with encrypted secret key data
* @param {Array<module:type/keyid>} keyIds
* @param {String} password password to unlock the key
*/
AsyncProxy.prototype.decryptKeyPacket = function(privateKey, keyIds, password) {
var self = this;

View File

@ -15,7 +15,9 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
this.window = {}; // to make UMD bundles work
/* globals self: true */
self.window = {}; // to make UMD bundles work
// Mozilla bind polyfill because phantomjs is stupid
if (!Function.prototype.bind) {
@ -46,7 +48,7 @@ var MAX_SIZE_RANDOM_BUFFER = 60000;
window.openpgp.crypto.random.randomBuffer.init(MAX_SIZE_RANDOM_BUFFER);
this.onmessage = function (event) {
self.onmessage = function (event) {
var data = null,
err = null,
msg = event.data,
@ -58,12 +60,14 @@ this.onmessage = function (event) {
window.openpgp.config[i] = msg.config[i];
}
break;
case 'seed-random':
if (!(msg.buf instanceof Uint8Array)) {
msg.buf = new Uint8Array(msg.buf);
}
window.openpgp.crypto.random.randomBuffer.set(msg.buf);
break;
case 'encrypt-message':
if(msg.keys) {
msg.keys = msg.keys.map(packetlistCloneToKey);
@ -74,6 +78,7 @@ this.onmessage = function (event) {
response({event: 'method-return', err: e.message});
});
break;
case 'encrypt-session-key':
if(msg.keys) {
msg.keys = msg.keys.map(packetlistCloneToKey);
@ -84,6 +89,7 @@ this.onmessage = function (event) {
response({event: 'method-return', err: e.message});
});
break;
case 'sign-and-encrypt-message':
if (!msg.publicKeys.length) {
msg.publicKeys = [msg.publicKeys];
@ -96,6 +102,7 @@ this.onmessage = function (event) {
response({event: 'method-return', err: e.message});
});
break;
case 'decrypt-message':
if(!(String.prototype.isPrototypeOf(msg.privateKey) || typeof msg.privateKey === 'string' || Uint8Array.prototype.isPrototypeOf(msg.privateKey))) {
msg.privateKey = packetlistCloneToKey(msg.privateKey);
@ -107,6 +114,7 @@ this.onmessage = function (event) {
response({event: 'method-return', err: e.message});
});
break;
case 'decrypt-session-key':
if(!(String.prototype.isPrototypeOf(msg.privateKey) || typeof msg.privateKey === 'string')) {
msg.privateKey = packetlistCloneToKey(msg.privateKey);
@ -118,6 +126,7 @@ this.onmessage = function (event) {
response({event: 'method-return', err: e.message});
});
break;
case 'decrypt-and-verify-message':
msg.privateKey = packetlistCloneToKey(msg.privateKey);
if (!msg.publicKeys.length) {
@ -131,6 +140,7 @@ this.onmessage = function (event) {
response({event: 'method-return', err: e.message});
});
break;
case 'sign-clear-message':
msg.privateKeys = msg.privateKeys.map(packetlistCloneToKey);
window.openpgp.signClearMessage(msg.privateKeys, msg.text).then(function(data) {
@ -139,6 +149,7 @@ this.onmessage = function (event) {
response({event: 'method-return', err: e.message});
});
break;
case 'verify-clear-signed-message':
if (!msg.publicKeys.length) {
msg.publicKeys = [msg.publicKeys];
@ -152,14 +163,16 @@ this.onmessage = function (event) {
response({event: 'method-return', err: e.message});
});
break;
case 'generate-key-pair':
window.openpgp.generateKeyPair(msg.options).then(function(data) {
case 'generate-key':
window.openpgp.generateKey(msg.options).then(function(data) {
data.key = data.key.toPacketlist();
response({event: 'method-return', data: data});
}).catch(function(e) {
response({event: 'method-return', err: e.message});
});
break;
case 'decrypt-key':
try {
msg.privateKey = packetlistCloneToKey(msg.privateKey);
@ -174,6 +187,7 @@ this.onmessage = function (event) {
}
response({event: 'method-return', data: data, err: err});
break;
case 'decrypt-key-packet':
try {
msg.privateKey = packetlistCloneToKey(msg.privateKey);
@ -189,6 +203,7 @@ this.onmessage = function (event) {
}
response({event: 'method-return', data: data, err: err});
break;
default:
throw new Error('Unknown Worker Event.');
}

View File

@ -1,5 +1,6 @@
describe('General', function () {
require('./util.js');
require('./openpgp.js');
require('./basic.js');
require('./armor.js');
require('./key.js');

267
test/general/openpgp.js Normal file
View File

@ -0,0 +1,267 @@
'use strict';
var openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp');
var sinon = require('sinon'),
chai = require('chai'),
expect = chai.expect;
describe('OpenPGP.js public api tests', function() {
describe('initWorker, getWorker, destroyWorker', function() {
afterEach(function() {
openpgp.destroyWorker(); // cleanup worker in case of failure
});
it('should work', function() {
var workerStub = {
postMessage: function() {}
};
openpgp.initWorker({
worker: workerStub
});
expect(openpgp.getWorker()).to.exist;
openpgp.destroyWorker();
expect(openpgp.getWorker()).to.not.exist;
});
});
describe('generateKey - unit tests', function() {
var keyGenStub, keyObjStub, getWebCryptoStub;
beforeEach(function() {
keyObjStub = {
armor: function() {
return 'priv_key';
},
toPublic: function() {
return {
armor: function() {
return 'pub_key';
}
};
}
};
keyGenStub = sinon.stub(openpgp.key, 'generate');
keyGenStub.returns(resolves(keyObjStub));
getWebCryptoStub = sinon.stub(openpgp.util, 'getWebCrypto');
});
afterEach(function() {
keyGenStub.restore();
openpgp.destroyWorker();
getWebCryptoStub.restore();
});
it('should fail for invalid user name', function() {
var opt = {
userIds: [{ name: {}, email: 'text@example.com' }]
};
var test = openpgp.generateKey.bind(null, opt);
expect(test).to.throw(/Invalid user id format/);
});
it('should fail for invalid user email address', function() {
var opt = {
userIds: [{ name: 'Test User', email: 'textexample.com' }]
};
var test = openpgp.generateKey.bind(null, opt);
expect(test).to.throw(/Invalid user id format/);
});
it('should fail for invalid user email address', function() {
var opt = {
userIds: [{ name: 'Test User', email: 'text@examplecom' }]
};
var test = openpgp.generateKey.bind(null, opt);
expect(test).to.throw(/Invalid user id format/);
});
it('should fail for invalid string user id', function() {
var opt = {
userIds: ['Test User text@example.com>']
};
var test = openpgp.generateKey.bind(null, opt);
expect(test).to.throw(/Invalid user id format/);
});
it('should fail for invalid single string user id', function() {
var opt = {
userIds: 'Test User text@example.com>'
};
var test = openpgp.generateKey.bind(null, opt);
expect(test).to.throw(/Invalid user id format/);
});
it('should work for valid single string user id', function(done) {
var opt = {
userIds: 'Test User <text@example.com>'
};
openpgp.generateKey(opt).then(function() { done(); });
});
it('should work for valid string user id', function(done) {
var opt = {
userIds: ['Test User <text@example.com>']
};
openpgp.generateKey(opt).then(function() { done(); });
});
it('should work for valid single user id hash', function(done) {
var opt = {
userIds: { name: 'Test User', email: 'text@example.com' }
};
openpgp.generateKey(opt).then(function() { done(); });
});
it('should work for valid single user id hash', function(done) {
var opt = {
userIds: [{ name: 'Test User', email: 'text@example.com' }]
};
openpgp.generateKey(opt).then(function() { done(); });
});
it('should work for an empty name', function(done) {
var opt = {
userIds: { email: 'text@example.com' }
};
openpgp.generateKey(opt).then(function() { done(); });
});
it('should work for an empty email address', function(done) {
var opt = {
userIds: { name: 'Test User' }
};
openpgp.generateKey(opt).then(function() { done(); });
});
it('should have default params set', function(done) {
var opt = {
userIds: { name: 'Test User', email: 'text@example.com' },
passphrase: 'secret',
unlocked: true
};
openpgp.generateKey(opt).then(function(newKey) {
expect(keyGenStub.withArgs({
userIds: ['Test User <text@example.com>'],
passphrase: 'secret',
numBits: 2048,
unlocked: true
}).calledOnce).to.be.true;
expect(newKey.key).to.exist;
expect(newKey.privateKeyArmored).to.exist;
expect(newKey.publicKeyArmored).to.exist;
done();
});
});
it('should work for no params', function(done) {
openpgp.generateKey().then(function(newKey) {
expect(keyGenStub.withArgs({
userIds: [],
passphrase: undefined,
numBits: 2048,
unlocked: false
}).calledOnce).to.be.true;
expect(newKey.key).to.exist;
done();
});
});
it('should delegate to async proxy', function() {
var workerStub = {
postMessage: function() {}
};
openpgp.initWorker({
worker: workerStub
});
var proxyGenStub = sinon.stub(openpgp.getWorker(), 'generateKey');
getWebCryptoStub.returns();
openpgp.generateKey();
expect(proxyGenStub.calledOnce).to.be.true;
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(), 'generateKey').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() {
var useNativeVal;
beforeEach(function() {
useNativeVal = openpgp.config.useNative;
});
afterEach(function() {
openpgp.config.useNative = useNativeVal;
openpgp.destroyWorker();
});
it('should work in JS (without worker)', function(done) {
openpgp.config.useNative = false;
openpgp.destroyWorker();
var opt = {
userIds: [{ name: 'Test User', email: 'text@example.com' }],
numBits: 512
};
openpgp.generateKey(opt).then(function(newKey) {
expect(newKey.key.getUserIds()[0]).to.equal('Test User <text@example.com>');
expect(newKey.privateKeyArmored).to.exist;
expect(newKey.publicKeyArmored).to.exist;
done();
});
});
it('should work in JS (with worker)', function(done) {
openpgp.config.useNative = false;
openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
var opt = {
userIds: [{ name: 'Test User', email: 'text@example.com' }],
numBits: 512
};
openpgp.generateKey(opt).then(function(newKey) {
expect(newKey.key.getUserIds()[0]).to.equal('Test User <text@example.com>');
expect(newKey.privateKeyArmored).to.exist;
expect(newKey.publicKeyArmored).to.exist;
done();
});
});
it('should work in JS (use native)', function(done) {
openpgp.config.useNative = true;
var opt = {
userIds: [{ name: 'Test User', email: 'text@example.com' }],
numBits: 512
};
if (openpgp.util.getWebCrypto()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys
openpgp.generateKey(opt).then(function(newKey) {
expect(newKey.key.getUserIds()[0]).to.equal('Test User <text@example.com>');
expect(newKey.privateKeyArmored).to.exist;
expect(newKey.publicKeyArmored).to.exist;
done();
});
});
});
});

View File

@ -7,10 +7,6 @@ var chai = require('chai'),
describe('Util unit tests', function() {
beforeEach(function() {});
afterEach(function() {});
describe('isString', function() {
it('should return true for type "string"', function() {
var data = 'foo';