mail/src/js/crypto/pgp.js

352 lines
10 KiB
JavaScript
Raw Normal View History

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'),
util = require('openpgp').util,
config = require('js/app-config').config;
2013-10-11 15:30:03 -04:00
var PGP = function() {
2014-02-17 10:05:58 -05:00
openpgp.config.prefer_hash_algorithm = openpgp.enums.hash.sha256;
2014-03-31 08:06:56 -04:00
openpgp.initWorker(config.workerPath + '/../lib/openpgp/openpgp.worker.min.js');
};
2013-10-11 15:30:03 -04:00
/**
* Generate a key pair for the user
*/
PGP.prototype.generateKeys = function(options, callback) {
var userId, passphrase;
2013-10-11 15:30:03 -04:00
if (!util.emailRegEx.test(options.emailAddress) || !options.keySize) {
2013-10-11 15:30:03 -04:00
callback({
errMsg: 'Crypto init failed. Not all options set!'
});
return;
}
// generate keypair (keytype 1=RSA)
userId = 'Whiteout User <' + options.emailAddress + '>';
passphrase = (options.passphrase) ? options.passphrase : undefined;
openpgp.generateKeyPair(1, options.keySize, userId, passphrase, onGenerated);
function onGenerated(err, keys) {
if (err) {
callback({
errMsg: 'Keygeneration failed!',
err: err
});
return;
}
callback(null, {
keyId: keys.key.getKeyPacket().getKeyId().toHex().toUpperCase(),
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-03-31 08:06:56 -04:00
return key.getKeyPacket().getFingerprint().toUpperCase();
}
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
};
/**
* Show a user's key id.
2013-11-06 10:20:49 -05:00
*/
PGP.prototype.getKeyId = function(keyArmored) {
var key, pubKeyId, privKeyId;
// process armored key input
if (keyArmored) {
key = openpgp.key.readArmored(keyArmored).keys[0];
return key.getKeyPacket().getKeyId().toHex().toUpperCase();
}
2014-01-24 07:26:29 -05:00
// check already imported keys
2014-01-24 07:26:29 -05:00
if (!this._privateKey || !this._publicKey) {
throw new Error('Cannot read key IDs... keys not set!');
2013-11-06 10:20:49 -05:00
}
2014-01-24 07:26:29 -05:00
pubKeyId = this._publicKey.getKeyPacket().getKeyId().toHex().toUpperCase();
privKeyId = this._privateKey.getKeyPacket().getKeyId().toHex().toUpperCase();
if (!pubKeyId || !privKeyId || pubKeyId !== privKeyId) {
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
};
/**
* Read all relevant params of an armored key.
*/
PGP.prototype.getKeyParams = function(keyArmored) {
var key, packet;
// 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!');
}
packet = key.getKeyPacket();
return {
_id: packet.getKeyId().toHex().toUpperCase(),
userId: key.getUserIds()[0].split('<')[1].split('>')[0],
2014-03-31 08:06:56 -04:00
fingerprint: packet.getFingerprint().toUpperCase(),
algorithm: packet.algorithm,
bitSize: packet.getBitSize(),
created: packet.created,
};
};
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
if (!options.privateKeyArmored || !options.publicKeyArmored) {
2013-10-11 15:30:03 -04:00
callback({
errMsg: 'Importing keys failed. Not all options set!'
});
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();
callback({
errMsg: 'Importing keys failed. Parsing error!'
});
return;
}
// decrypt private key with passphrase
if (!this._privateKey.decrypt(options.passphrase)) {
resetKeys();
2013-10-11 15:30:03 -04:00
callback({
errMsg: 'Incorrect passphrase!'
});
return;
}
2013-10-11 15:54:43 -04:00
// check if keys have the same id
2014-01-24 07:26:29 -05:00
pubKeyId = this._publicKey.getKeyPacket().getKeyId().toHex();
privKeyId = this._privateKey.getKeyPacket().getKeyId().toHex();
if (!pubKeyId || !privKeyId || pubKeyId !== privKeyId) {
resetKeys();
2013-10-11 15:54:43 -04:00
callback({
errMsg: 'Key IDs dont match!'
});
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) {
2013-10-11 16:10:50 -04:00
callback({
errMsg: 'Could not export keys!'
2013-10-11 15:30:03 -04:00
});
return;
}
2013-10-11 16:10:50 -04:00
callback(null, {
2014-01-24 07:26:29 -05:00
keyId: this._publicKey.getKeyPacket().getKeyId().toHex().toUpperCase(),
privateKeyArmored: this._privateKey.armor(),
publicKeyArmored: this._publicKey.armor()
2013-10-11 15:30:03 -04:00
});
};
/**
* Change the passphrase of an ascii armored private key.
*/
PGP.prototype.changePassphrase = function(options, callback) {
var privKey, packets, newPassphrase, newKeyArmored;
// set undefined instead of empty string as passphrase
newPassphrase = (options.newPassphrase) ? options.newPassphrase : undefined;
if (!options.privateKeyArmored) {
callback({
errMsg: 'Private key must be specified to change passphrase!'
});
return;
}
if (options.oldPassphrase === newPassphrase ||
(!options.oldPassphrase && !newPassphrase)) {
callback(new Error('New and old passphrase are the same!'));
return;
}
// read armored key
try {
privKey = openpgp.key.readArmored(options.privateKeyArmored).keys[0];
} catch (e) {
callback({
errMsg: 'Importing key failed. Parsing error!'
});
return;
}
// decrypt private key with passphrase
if (!privKey.decrypt(options.oldPassphrase)) {
callback({
errMsg: 'Old passphrase incorrect!'
});
return;
}
// encrypt key with new passphrase
try {
packets = privKey.getAllKeyPackets();
for (var i = 0; i < packets.length; i++) {
packets[i].encrypt(newPassphrase);
}
newKeyArmored = privKey.armor();
} catch (e) {
callback({
errMsg: 'Setting new passphrase failed!'
});
return;
}
// check if new passphrase really works
if (!privKey.decrypt(newPassphrase)) {
callback({
errMsg: 'Decrypting key with new passphrase failed!'
});
return;
}
callback(null, newKeyArmored);
};
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) {
var publicKeys = [];
2013-10-11 15:30:03 -04:00
2014-01-24 07:26:29 -05:00
// check keys
if (!this._privateKey || publicKeysArmored.length < 1) {
callback({
errMsg: 'Error encrypting. Keys must be set!'
});
return;
2013-10-11 15:30:03 -04:00
}
// parse armored public keys
try {
2014-01-24 07:26:29 -05:00
publicKeysArmored.forEach(function(pubkeyArmored) {
publicKeys.push(openpgp.key.readArmored(pubkeyArmored).keys[0]);
});
} catch (err) {
callback({
errMsg: 'Error encrypting plaintext!',
err: err
});
return;
}
2013-10-11 15:30:03 -04:00
// encrypt and sign the plaintext
openpgp.signAndEncryptMessage(publicKeys, this._privateKey, plaintext, callback);
2013-10-11 15:30:03 -04:00
};
/**
* Decrypt and verify a pgp message for a single sender
*/
2014-01-24 07:26:29 -05:00
PGP.prototype.decrypt = function(ciphertext, publicKeyArmored, callback) {
var publicKey, message, signaturesValid;
2013-10-11 15:30:03 -04:00
2014-01-24 07:26:29 -05:00
// check keys
if (!this._privateKey || !publicKeyArmored) {
2013-10-11 15:30:03 -04:00
callback({
2014-01-24 07:26:29 -05:00
errMsg: 'Error decrypting. Keys must be set!'
2013-10-11 15:30:03 -04:00
});
return;
2013-10-11 15:30:03 -04:00
}
// read keys and ciphertext message
try {
2014-01-24 07:26:29 -05:00
publicKey = openpgp.key.readArmored(publicKeyArmored).keys[0];
message = openpgp.message.readArmored(ciphertext);
} catch (err) {
callback({
errMsg: 'Error decrypting PGP message!',
err: err
});
return;
}
// decrypt and verify pgp message
openpgp.decryptAndVerifyMessage(this._privateKey, [publicKey], message, onDecrypted);
function onDecrypted(err, decrypted) {
2014-02-28 11:51:08 -05:00
if (err) {
callback({
errMsg: 'Error decrypting PGP message!',
err: err
});
return;
}
// check if signatures are valid
signaturesValid = true;
decrypted.signatures.forEach(function(sig) {
if (!sig.valid) {
signaturesValid = false;
}
2014-01-24 07:26:29 -05:00
});
if (!signaturesValid) {
callback({
errMsg: 'Verifying PGP signature failed!'
});
return;
}
// return decrypted plaintext
callback(null, decrypted.text);
}
2013-10-11 15:30:03 -04:00
};
return PGP;
});