2013-10-11 15:30:03 -04:00
|
|
|
/**
|
|
|
|
* High level crypto api that handles all calls to OpenPGP.js
|
|
|
|
*/
|
|
|
|
define(function(require) {
|
|
|
|
'use strict';
|
|
|
|
|
2014-01-24 07:26:29 -05:00
|
|
|
var openpgp = require('openpgp'),
|
2014-02-12 13:48:29 -05:00
|
|
|
util = require('openpgp').util,
|
|
|
|
config = require('js/app-config').config;
|
2013-10-11 15:30:03 -04:00
|
|
|
|
2014-02-12 13:48:29 -05:00
|
|
|
var PGP = function() {
|
2014-02-17 10:05:58 -05:00
|
|
|
openpgp.config.prefer_hash_algorithm = openpgp.enums.hash.sha256;
|
2014-06-30 13:59:02 -04:00
|
|
|
openpgp.initWorker(config.workerPath + '/../lib/openpgp/openpgp.worker.js');
|
2014-02-12 13:48:29 -05:00
|
|
|
};
|
2013-10-11 15:30:03 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate a key pair for the user
|
|
|
|
*/
|
|
|
|
PGP.prototype.generateKeys = function(options, callback) {
|
2014-04-11 12:39:13 -04:00
|
|
|
var userId, passphrase;
|
2013-10-11 15:30:03 -04:00
|
|
|
|
2014-03-28 13:08:04 -04:00
|
|
|
if (!util.emailRegEx.test(options.emailAddress) || !options.keySize) {
|
2014-06-13 06:33:30 -04:00
|
|
|
callback(new Error('Crypto init failed. Not all options set!'));
|
2013-10-11 15:30:03 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-06-13 05:48:13 -04:00
|
|
|
// generate keypair
|
2014-02-12 13:48:29 -05:00
|
|
|
userId = 'Whiteout User <' + options.emailAddress + '>';
|
2014-04-11 12:39:13 -04:00
|
|
|
passphrase = (options.passphrase) ? options.passphrase : undefined;
|
2014-06-13 05:48:13 -04:00
|
|
|
openpgp.generateKeyPair({
|
|
|
|
keyType: 1, // (keytype 1=RSA)
|
|
|
|
numBits: options.keySize,
|
|
|
|
userId: userId,
|
|
|
|
passphrase: passphrase
|
|
|
|
}, onGenerated);
|
2014-02-12 13:48:29 -05:00
|
|
|
|
|
|
|
function onGenerated(err, keys) {
|
|
|
|
if (err) {
|
2014-06-13 06:33:30 -04:00
|
|
|
callback(new Error('Keygeneration failed!'));
|
2014-02-12 13:48:29 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
callback(null, {
|
2014-06-30 13:59:02 -04:00
|
|
|
keyId: keys.key.primaryKey.getKeyId().toHex().toUpperCase(),
|
2014-02-12 13:48:29 -05:00
|
|
|
privateKeyArmored: keys.privateKeyArmored,
|
|
|
|
publicKeyArmored: keys.publicKeyArmored
|
2013-10-11 21:19:01 -04:00
|
|
|
});
|
|
|
|
}
|
2013-10-11 15:30:03 -04:00
|
|
|
};
|
|
|
|
|
2013-11-06 10:20:49 -05:00
|
|
|
/**
|
|
|
|
* Show a user's fingerprint
|
|
|
|
*/
|
2014-01-24 07:26:29 -05:00
|
|
|
PGP.prototype.getFingerprint = function(keyArmored) {
|
|
|
|
function fingerprint(key) {
|
2014-06-30 13:59:02 -04:00
|
|
|
return key.primaryKey.getFingerprint().toUpperCase();
|
2013-12-03 07:15:10 -05:00
|
|
|
}
|
|
|
|
|
2014-01-24 07:26:29 -05:00
|
|
|
// process armored key input
|
|
|
|
if (keyArmored) {
|
|
|
|
return fingerprint(openpgp.key.readArmored(keyArmored).keys[0]);
|
2013-11-06 10:20:49 -05:00
|
|
|
}
|
|
|
|
|
2014-01-24 07:26:29 -05:00
|
|
|
if (!this._publicKey) {
|
|
|
|
throw new Error('No public key set for fingerprint generation!');
|
2013-11-06 10:20:49 -05:00
|
|
|
}
|
|
|
|
|
2014-01-24 07:26:29 -05:00
|
|
|
// get local fingerpring
|
|
|
|
return fingerprint(this._publicKey);
|
2013-11-06 10:20:49 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2014-03-06 12:19:51 -05:00
|
|
|
* Show a user's key id.
|
2013-11-06 10:20:49 -05:00
|
|
|
*/
|
2014-03-05 14:14:23 -05:00
|
|
|
PGP.prototype.getKeyId = function(keyArmored) {
|
|
|
|
var key, pubKeyId, privKeyId;
|
|
|
|
|
|
|
|
// process armored key input
|
|
|
|
if (keyArmored) {
|
|
|
|
key = openpgp.key.readArmored(keyArmored).keys[0];
|
2014-06-30 13:59:02 -04:00
|
|
|
return key.primaryKey.getKeyId().toHex().toUpperCase();
|
2014-03-05 14:14:23 -05:00
|
|
|
}
|
2014-01-24 07:26:29 -05:00
|
|
|
|
2014-03-05 14:14:23 -05:00
|
|
|
// check already imported keys
|
2014-01-24 07:26:29 -05:00
|
|
|
if (!this._privateKey || !this._publicKey) {
|
2014-03-05 14:14:23 -05:00
|
|
|
throw new Error('Cannot read key IDs... keys not set!');
|
2013-11-06 10:20:49 -05:00
|
|
|
}
|
|
|
|
|
2014-06-30 13:59:02 -04:00
|
|
|
pubKeyId = this._publicKey.primaryKey.getKeyId().toHex().toUpperCase();
|
|
|
|
privKeyId = this._privateKey.primaryKey.getKeyId().toHex().toUpperCase();
|
2014-01-24 07:26:29 -05:00
|
|
|
|
|
|
|
if (!pubKeyId || !privKeyId || pubKeyId !== privKeyId) {
|
2014-03-05 14:14:23 -05:00
|
|
|
throw new Error('Key IDs do not match!');
|
2014-01-24 07:26:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return pubKeyId;
|
2013-11-06 10:20:49 -05:00
|
|
|
};
|
|
|
|
|
2014-03-06 12:19:51 -05:00
|
|
|
/**
|
|
|
|
* Read all relevant params of an armored key.
|
|
|
|
*/
|
|
|
|
PGP.prototype.getKeyParams = function(keyArmored) {
|
2014-06-18 04:02:33 -04:00
|
|
|
var key, packet, userIds;
|
2014-04-11 12:39:13 -04:00
|
|
|
|
|
|
|
// process armored key input
|
|
|
|
if (keyArmored) {
|
|
|
|
key = openpgp.key.readArmored(keyArmored).keys[0];
|
|
|
|
} else if (this._publicKey) {
|
|
|
|
key = this._publicKey;
|
|
|
|
} else {
|
|
|
|
throw new Error('Cannot read key params... keys not set!');
|
|
|
|
}
|
|
|
|
|
2014-06-30 13:59:02 -04:00
|
|
|
packet = key.primaryKey;
|
2014-03-06 12:19:51 -05:00
|
|
|
|
2014-06-18 04:02:33 -04:00
|
|
|
// read user names and email addresses
|
|
|
|
userIds = [];
|
|
|
|
key.getUserIds().forEach(function(userId) {
|
|
|
|
userIds.push({
|
|
|
|
name: userId.split('<')[0].trim(),
|
|
|
|
emailAddress: userId.split('<')[1].split('>')[0].trim()
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2014-03-06 12:19:51 -05:00
|
|
|
return {
|
|
|
|
_id: packet.getKeyId().toHex().toUpperCase(),
|
2014-06-18 04:02:33 -04:00
|
|
|
userId: userIds[0].emailAddress, // the primary (first) email address of the key
|
|
|
|
userIds: userIds, // a dictonary of all the key's name/address pairs
|
2014-03-31 08:06:56 -04:00
|
|
|
fingerprint: packet.getFingerprint().toUpperCase(),
|
2014-03-06 12:19:51 -05:00
|
|
|
algorithm: packet.algorithm,
|
|
|
|
bitSize: packet.getBitSize(),
|
|
|
|
created: packet.created,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2014-07-01 14:58:34 -04:00
|
|
|
/**
|
|
|
|
* Extract a public key from a private key
|
|
|
|
* @param {String} privateKeyArmored The private PGP key block
|
|
|
|
* @return {String} The publick PGP key block
|
|
|
|
*/
|
|
|
|
PGP.prototype.extractPublicKey = function(privateKeyArmored) {
|
|
|
|
var privkey = openpgp.key.readArmored(privateKeyArmored).keys[0];
|
|
|
|
var pubkey = privkey.toPublic();
|
|
|
|
return pubkey.armor();
|
|
|
|
};
|
|
|
|
|
2013-10-11 15:30:03 -04:00
|
|
|
/**
|
|
|
|
* Import the user's key pair
|
|
|
|
*/
|
|
|
|
PGP.prototype.importKeys = function(options, callback) {
|
2014-01-24 07:26:29 -05:00
|
|
|
var pubKeyId, privKeyId, self = this;
|
2013-10-11 15:54:43 -04:00
|
|
|
|
2014-01-24 07:26:29 -05:00
|
|
|
// check options
|
2014-03-28 12:40:02 -04:00
|
|
|
if (!options.privateKeyArmored || !options.publicKeyArmored) {
|
2014-06-13 06:33:30 -04:00
|
|
|
callback(new Error('Importing keys failed. Not all options set!'));
|
2013-10-11 15:30:03 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-01-24 07:26:29 -05:00
|
|
|
function resetKeys() {
|
|
|
|
self._publicKey = undefined;
|
|
|
|
self._privateKey = undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
// read armored keys
|
|
|
|
try {
|
|
|
|
this._publicKey = openpgp.key.readArmored(options.publicKeyArmored).keys[0];
|
|
|
|
this._privateKey = openpgp.key.readArmored(options.privateKeyArmored).keys[0];
|
|
|
|
} catch (e) {
|
|
|
|
resetKeys();
|
2014-06-13 06:33:30 -04:00
|
|
|
callback(new Error('Importing keys failed. Parsing error!'));
|
2014-01-24 07:26:29 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// decrypt private key with passphrase
|
|
|
|
if (!this._privateKey.decrypt(options.passphrase)) {
|
|
|
|
resetKeys();
|
2014-06-13 06:33:30 -04:00
|
|
|
callback(new Error('Incorrect passphrase!'));
|
2013-10-11 15:30:03 -04:00
|
|
|
return;
|
|
|
|
}
|
2013-10-11 15:54:43 -04:00
|
|
|
|
|
|
|
// check if keys have the same id
|
2014-06-30 13:59:02 -04:00
|
|
|
pubKeyId = this._publicKey.primaryKey.getKeyId().toHex();
|
|
|
|
privKeyId = this._privateKey.primaryKey.getKeyId().toHex();
|
2014-01-24 07:26:29 -05:00
|
|
|
if (!pubKeyId || !privKeyId || pubKeyId !== privKeyId) {
|
|
|
|
resetKeys();
|
2014-06-13 06:33:30 -04:00
|
|
|
callback(new Error('Key IDs dont match!'));
|
2013-10-11 15:54:43 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-10-11 15:30:03 -04:00
|
|
|
callback();
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Export the user's key pair
|
|
|
|
*/
|
|
|
|
PGP.prototype.exportKeys = function(callback) {
|
2014-01-24 07:26:29 -05:00
|
|
|
if (!this._publicKey || !this._privateKey) {
|
2014-06-13 06:33:30 -04:00
|
|
|
callback(new Error('Could not export keys!'));
|
2013-10-11 15:30:03 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-10-11 16:10:50 -04:00
|
|
|
callback(null, {
|
2014-06-30 13:59:02 -04:00
|
|
|
keyId: this._publicKey.primaryKey.getKeyId().toHex().toUpperCase(),
|
2014-01-24 07:26:29 -05:00
|
|
|
privateKeyArmored: this._privateKey.armor(),
|
|
|
|
publicKeyArmored: this._publicKey.armor()
|
2013-10-11 15:30:03 -04:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2014-03-31 14:15:09 -04:00
|
|
|
/**
|
|
|
|
* Change the passphrase of an ascii armored private key.
|
|
|
|
*/
|
|
|
|
PGP.prototype.changePassphrase = function(options, callback) {
|
2014-04-11 12:39:13 -04:00
|
|
|
var privKey, packets, newPassphrase, newKeyArmored;
|
2014-03-31 14:15:09 -04:00
|
|
|
|
2014-04-11 12:39:13 -04:00
|
|
|
// set undefined instead of empty string as passphrase
|
|
|
|
newPassphrase = (options.newPassphrase) ? options.newPassphrase : undefined;
|
|
|
|
|
|
|
|
if (!options.privateKeyArmored) {
|
2014-06-13 06:33:30 -04:00
|
|
|
callback(new Error('Private key must be specified to change passphrase!'));
|
2014-03-31 14:15:09 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-04-11 12:39:13 -04:00
|
|
|
if (options.oldPassphrase === newPassphrase ||
|
|
|
|
(!options.oldPassphrase && !newPassphrase)) {
|
|
|
|
callback(new Error('New and old passphrase are the same!'));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-03-31 14:15:09 -04:00
|
|
|
// read armored key
|
|
|
|
try {
|
|
|
|
privKey = openpgp.key.readArmored(options.privateKeyArmored).keys[0];
|
|
|
|
} catch (e) {
|
2014-06-13 06:33:30 -04:00
|
|
|
callback(new Error('Importing key failed. Parsing error!'));
|
2014-03-31 14:15:09 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// decrypt private key with passphrase
|
|
|
|
if (!privKey.decrypt(options.oldPassphrase)) {
|
2014-06-13 06:33:30 -04:00
|
|
|
callback(new Error('Old passphrase incorrect!'));
|
2014-03-31 14:15:09 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// encrypt key with new passphrase
|
|
|
|
try {
|
|
|
|
packets = privKey.getAllKeyPackets();
|
|
|
|
for (var i = 0; i < packets.length; i++) {
|
2014-04-11 12:39:13 -04:00
|
|
|
packets[i].encrypt(newPassphrase);
|
2014-03-31 14:15:09 -04:00
|
|
|
}
|
2014-04-11 12:39:13 -04:00
|
|
|
newKeyArmored = privKey.armor();
|
2014-03-31 14:15:09 -04:00
|
|
|
} catch (e) {
|
2014-06-13 06:33:30 -04:00
|
|
|
callback(new Error('Setting new passphrase failed!'));
|
2014-03-31 14:15:09 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-04-11 12:39:13 -04:00
|
|
|
// check if new passphrase really works
|
|
|
|
if (!privKey.decrypt(newPassphrase)) {
|
2014-06-13 06:33:30 -04:00
|
|
|
callback(new Error('Decrypting key with new passphrase failed!'));
|
2014-04-11 12:39:13 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
callback(null, newKeyArmored);
|
2014-03-31 14:15:09 -04:00
|
|
|
};
|
|
|
|
|
2013-10-11 15:30:03 -04:00
|
|
|
/**
|
|
|
|
* Encrypt and sign a pgp message for a list of receivers
|
|
|
|
*/
|
2014-01-24 07:26:29 -05:00
|
|
|
PGP.prototype.encrypt = function(plaintext, publicKeysArmored, callback) {
|
2014-07-01 17:28:44 -04:00
|
|
|
var publicKeys;
|
2013-10-11 15:30:03 -04:00
|
|
|
|
2014-01-24 07:26:29 -05:00
|
|
|
// check keys
|
2014-07-01 17:28:44 -04:00
|
|
|
if (!this._privateKey) {
|
2014-06-13 06:33:30 -04:00
|
|
|
callback(new Error('Error encrypting. Keys must be set!'));
|
2014-01-24 07:26:29 -05:00
|
|
|
return;
|
2013-10-11 15:30:03 -04:00
|
|
|
}
|
|
|
|
|
2014-02-12 13:48:29 -05:00
|
|
|
// parse armored public keys
|
2013-11-14 07:57:52 -05:00
|
|
|
try {
|
2014-07-01 17:28:44 -04:00
|
|
|
if (publicKeysArmored && publicKeysArmored.length) {
|
|
|
|
publicKeys = [];
|
|
|
|
publicKeysArmored.forEach(function(pubkeyArmored) {
|
|
|
|
publicKeys = publicKeys.concat(openpgp.key.readArmored(pubkeyArmored).keys);
|
|
|
|
});
|
|
|
|
}
|
2013-11-14 07:57:52 -05:00
|
|
|
} catch (err) {
|
2014-06-13 06:33:30 -04:00
|
|
|
callback(new Error('Error encrypting plaintext!'));
|
2013-11-14 07:57:52 -05:00
|
|
|
return;
|
|
|
|
}
|
2013-10-11 15:30:03 -04:00
|
|
|
|
2014-07-01 17:28:44 -04:00
|
|
|
if (publicKeys) {
|
|
|
|
// encrypt and sign the plaintext
|
|
|
|
openpgp.signAndEncryptMessage(publicKeys, this._privateKey, plaintext, callback);
|
|
|
|
} else {
|
|
|
|
// if no public keys are available encrypt for myself
|
|
|
|
openpgp.signAndEncryptMessage([this._publicKey], this._privateKey, plaintext, callback);
|
|
|
|
}
|
2013-10-11 15:30:03 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2014-07-01 17:28:44 -04:00
|
|
|
* [decrypt description]
|
|
|
|
* @param {String} ciphertext The encrypted PGP message block
|
|
|
|
* @param {String} publicKeyArmored The public key used to sign the message
|
|
|
|
* @param {Function} callback(error, plaintext, signaturesValid) signaturesValid is undefined in case there are no signature, null in case there are signatures but the wrong public key or no key was used to verify, true if the signature was successfully verified, or false if the signataure verification failed.
|
2013-10-11 15:30:03 -04:00
|
|
|
*/
|
2014-01-24 07:26:29 -05:00
|
|
|
PGP.prototype.decrypt = function(ciphertext, publicKeyArmored, callback) {
|
2014-06-30 13:59:02 -04:00
|
|
|
var publicKeys, message, signaturesValid;
|
2013-10-11 15:30:03 -04:00
|
|
|
|
2014-01-24 07:26:29 -05:00
|
|
|
// check keys
|
2014-07-01 17:28:44 -04:00
|
|
|
if (!this._privateKey) {
|
2014-06-13 06:33:30 -04:00
|
|
|
callback(new Error('Error decrypting. Keys must be set!'));
|
2013-11-14 07:57:52 -05:00
|
|
|
return;
|
2013-10-11 15:30:03 -04:00
|
|
|
}
|
2013-11-14 07:57:52 -05:00
|
|
|
|
2014-02-12 13:48:29 -05:00
|
|
|
// read keys and ciphertext message
|
2013-11-14 07:57:52 -05:00
|
|
|
try {
|
2014-07-01 17:28:44 -04:00
|
|
|
if (publicKeyArmored) {
|
|
|
|
// parse public keys if available ...
|
|
|
|
publicKeys = openpgp.key.readArmored(publicKeyArmored).keys;
|
|
|
|
} else {
|
|
|
|
// use own public key to know if signatures are available
|
|
|
|
publicKeys = [this._publicKey];
|
|
|
|
}
|
2014-01-24 07:26:29 -05:00
|
|
|
message = openpgp.message.readArmored(ciphertext);
|
2013-11-14 07:57:52 -05:00
|
|
|
} catch (err) {
|
2014-06-30 13:59:02 -04:00
|
|
|
callback(new Error('Error parsing encrypted PGP message!'));
|
2013-11-14 07:57:52 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-02-12 13:48:29 -05:00
|
|
|
// decrypt and verify pgp message
|
2014-06-18 04:02:33 -04:00
|
|
|
openpgp.decryptAndVerifyMessage(this._privateKey, publicKeys, message, onDecrypted);
|
2014-02-12 13:48:29 -05:00
|
|
|
|
|
|
|
function onDecrypted(err, decrypted) {
|
2014-02-28 11:51:08 -05:00
|
|
|
if (err) {
|
2014-07-01 17:28:44 -04:00
|
|
|
callback(new Error('Error decrypting and verifying PGP message!'));
|
2014-02-28 11:51:08 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-02-12 13:48:29 -05:00
|
|
|
// check if signatures are valid
|
2014-06-30 13:59:02 -04:00
|
|
|
if (decrypted.signatures.length > 0) {
|
2014-07-01 17:28:44 -04:00
|
|
|
signaturesValid = true; // signature is correct
|
2014-06-30 13:59:02 -04:00
|
|
|
for (var i = 0; i < decrypted.signatures.length; i++) {
|
|
|
|
if (decrypted.signatures[i].valid === false) {
|
|
|
|
signaturesValid = false; // signature is wrong ... message was tampered with
|
|
|
|
break;
|
|
|
|
} else if (decrypted.signatures[i].valid === null) {
|
2014-07-01 17:28:44 -04:00
|
|
|
signaturesValid = null; // signature not found for the specified public key
|
2014-06-30 13:59:02 -04:00
|
|
|
break;
|
|
|
|
}
|
2014-02-12 13:48:29 -05:00
|
|
|
}
|
2014-06-30 13:59:02 -04:00
|
|
|
}
|
2013-12-05 07:00:00 -05:00
|
|
|
|
2014-02-12 13:48:29 -05:00
|
|
|
// return decrypted plaintext
|
2014-06-30 13:59:02 -04:00
|
|
|
callback(null, decrypted.text, signaturesValid);
|
2014-02-12 13:48:29 -05:00
|
|
|
}
|
2013-10-11 15:30:03 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
return PGP;
|
|
|
|
});
|