mirror of
https://github.com/moparisthebest/mail
synced 2024-11-22 08:52:15 -05:00
[WO-184] implement support for importing an existing pgp key
This commit is contained in:
parent
09999f991a
commit
1b94e7b5ce
10
Gruntfile.js
10
Gruntfile.js
@ -220,6 +220,16 @@ module.exports = function(grunt) {
|
||||
cwd: 'dist/',
|
||||
src: ['**/*'],
|
||||
dest: 'release/'
|
||||
},
|
||||
nodeWebkit: {
|
||||
options: {
|
||||
mode: 'zip',
|
||||
archive: 'release/whiteout-mail_' + zipName + '.nw'
|
||||
},
|
||||
expand: true,
|
||||
cwd: 'dist/',
|
||||
src: ['**/*'],
|
||||
dest: '/'
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -25,6 +25,10 @@ define(function(require) {
|
||||
// scope functions
|
||||
//
|
||||
|
||||
$scope.importKey = function() {
|
||||
$location.path('/login-new-device');
|
||||
};
|
||||
|
||||
/*
|
||||
* Taken from jQuery validate.password plug-in 1.0
|
||||
* http://bassistance.de/jquery-plugins/jquery-plugin-validate.password/
|
||||
|
@ -11,7 +11,8 @@ define(function(require) {
|
||||
// attach global error handler
|
||||
errorUtil.attachHandler($scope);
|
||||
|
||||
var emailDao = appController._emailDao;
|
||||
var emailDao = appController._emailDao,
|
||||
pgp = appController._crypto;
|
||||
|
||||
$scope.incorrect = false;
|
||||
|
||||
@ -27,18 +28,32 @@ define(function(require) {
|
||||
|
||||
function unlockCrypto() {
|
||||
var userId = emailDao._account.emailAddress;
|
||||
// check if user already has a public key on the key server
|
||||
emailDao._keychain.getUserKeyPair(userId, function(err, keypair) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
keypair = keypair || {};
|
||||
|
||||
// set parsed private key
|
||||
keypair.privateKey = {
|
||||
_id: keypair.publicKey._id,
|
||||
_id: pgp.getKeyId($scope.key.privateKeyArmored),
|
||||
userId: userId,
|
||||
encryptedKey: $scope.key.privateKeyArmored
|
||||
};
|
||||
|
||||
if (!keypair.publicKey) {
|
||||
// there is no public key on the key server yet... use parsed
|
||||
keypair.publicKey = {
|
||||
_id: pgp.getKeyId($scope.key.publicKeyArmored),
|
||||
userId: userId,
|
||||
publicKey: $scope.key.publicKeyArmored
|
||||
};
|
||||
}
|
||||
|
||||
// import and validate keypair
|
||||
emailDao.unlock({
|
||||
keypair: keypair,
|
||||
passphrase: $scope.passphrase
|
||||
|
@ -68,23 +68,33 @@ define(function(require) {
|
||||
return fingerprint(this._publicKey);
|
||||
};
|
||||
|
||||
PGP.prototype.getUserId = function(keyArmored) {
|
||||
var key = openpgp.key.readArmored(keyArmored).keys[0];
|
||||
return key.getUserIds()[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* Show a user's key id
|
||||
*/
|
||||
PGP.prototype.getKeyId = function() {
|
||||
var pubKeyId, privKeyId;
|
||||
PGP.prototype.getKeyId = function(keyArmored) {
|
||||
var key, pubKeyId, privKeyId;
|
||||
|
||||
// check keys
|
||||
// process armored key input
|
||||
if (keyArmored) {
|
||||
key = openpgp.key.readArmored(keyArmored).keys[0];
|
||||
return key.getKeyPacket().getKeyId().toHex().toUpperCase();
|
||||
}
|
||||
|
||||
// check already imported keys
|
||||
if (!this._privateKey || !this._publicKey) {
|
||||
return;
|
||||
throw new Error('Cannot read key IDs... keys not set!');
|
||||
}
|
||||
|
||||
pubKeyId = this._publicKey.getKeyPacket().getKeyId().toHex().toUpperCase();
|
||||
privKeyId = this._privateKey.getKeyPacket().getKeyId().toHex().toUpperCase();
|
||||
|
||||
if (!pubKeyId || !privKeyId || pubKeyId !== privKeyId) {
|
||||
console.error('Key IDs do not match!');
|
||||
return;
|
||||
throw new Error('Key IDs do not match!');
|
||||
}
|
||||
|
||||
return pubKeyId;
|
||||
|
@ -126,20 +126,7 @@ define(function(require) {
|
||||
|
||||
if (options.keypair) {
|
||||
// import existing key pair into crypto module
|
||||
self._crypto.importKeys({
|
||||
passphrase: options.passphrase,
|
||||
privateKeyArmored: options.keypair.privateKey.encryptedKey,
|
||||
publicKeyArmored: options.keypair.publicKey.publicKey
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// set decrypted privateKey to pgpMailer
|
||||
self._pgpbuilder._privateKey = self._crypto._privateKey;
|
||||
callback();
|
||||
});
|
||||
handleExistingKeypair(options.keypair);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -157,6 +144,44 @@ define(function(require) {
|
||||
handleGenerated(generatedKeypair);
|
||||
});
|
||||
|
||||
function handleExistingKeypair(keypair) {
|
||||
var pubUserID, privUserID;
|
||||
|
||||
// check if key IDs match
|
||||
if (!keypair.privateKey._id || keypair.privateKey._id !== keypair.publicKey._id) {
|
||||
callback({
|
||||
errMsg: 'Key IDs dont match!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// check if the key's user ID matches the current account
|
||||
pubUserID = self._crypto.getUserId(keypair.publicKey.publicKey);
|
||||
privUserID = self._crypto.getUserId(keypair.privateKey.encryptedKey);
|
||||
if (pubUserID.indexOf(self._account.emailAddress) === -1 || privUserID.indexOf(self._account.emailAddress) === -1) {
|
||||
callback({
|
||||
errMsg: 'User IDs dont match!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// import existing key pair into crypto module
|
||||
self._crypto.importKeys({
|
||||
passphrase: options.passphrase,
|
||||
privateKeyArmored: keypair.privateKey.encryptedKey,
|
||||
publicKeyArmored: keypair.publicKey.publicKey
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// set decrypted privateKey to pgpMailer
|
||||
self._pgpbuilder._privateKey = self._crypto._privateKey;
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
function handleGenerated(generatedKeypair) {
|
||||
// import the new key pair into crypto module
|
||||
self._crypto.importKeys({
|
||||
@ -1308,4 +1333,4 @@ define(function(require) {
|
||||
};
|
||||
|
||||
return EmailDAO;
|
||||
});
|
||||
});
|
||||
|
@ -27,7 +27,7 @@
|
||||
p {
|
||||
line-height: 150%;
|
||||
|
||||
b {
|
||||
b, a {
|
||||
color: $color-blue;
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
<div class="content" ng-switch on="state.ui">
|
||||
|
||||
<div ng-switch-when="1">
|
||||
<p><b>Set passphrase.</b> The passphrase protects your private key. If you forget your passphrase you will not be able to recover your messages.</p>
|
||||
<p><b>Generate PGP key.</b> Choose a passphrase to protect your new key. If you forget it at a later time you will not be able to read past messages.</p><p>Alternatively you can also <a href="#" ng-click="$event.preventDefault(); importKey()">import an existing PGP key</a>.</p>
|
||||
<form>
|
||||
<div>
|
||||
<label class="input-error-message" ng-class="{'passphrase-label-ok': passphraseRating >= 2}">{{passphraseMsg}}</label><br>
|
||||
|
@ -4,7 +4,7 @@
|
||||
</div><!--/logo-->
|
||||
|
||||
<div class="content">
|
||||
<p><b>Import keyfile.</b> You are already registered on another device. To access your emails on this device, please import your key file.</p>
|
||||
<p><b>Import keyfile.</b> To access your emails on this device, please import your existing key file.</p>
|
||||
|
||||
<form>
|
||||
<div>
|
||||
|
@ -354,11 +354,16 @@ define(function(require) {
|
||||
_pgpbuilder: {}
|
||||
};
|
||||
|
||||
pgpStub.getUserId.returns('Whiteout User <' + emailAddress + '>');
|
||||
|
||||
pgpStub.importKeys.withArgs({
|
||||
passphrase: passphrase,
|
||||
privateKeyArmored: mockKeyPair.privateKey.encryptedKey,
|
||||
publicKeyArmored: mockKeyPair.publicKey.publicKey
|
||||
}).yields();
|
||||
pgpStub._privateKey = {
|
||||
foo: 'bar'
|
||||
};
|
||||
|
||||
dao.unlock({
|
||||
passphrase: passphrase,
|
||||
@ -367,6 +372,7 @@ define(function(require) {
|
||||
expect(err).to.not.exist;
|
||||
|
||||
expect(pgpStub.importKeys.calledOnce).to.be.true;
|
||||
expect(dao._pgpbuilder._privateKey).to.equal(pgpStub._privateKey);
|
||||
|
||||
done();
|
||||
});
|
||||
|
@ -4,13 +4,14 @@ define(function(require) {
|
||||
var expect = chai.expect,
|
||||
angular = require('angular'),
|
||||
mocks = require('angularMocks'),
|
||||
PGP = require('js/crypto/pgp'),
|
||||
LoginNewDeviceCtrl = require('js/controller/login-new-device'),
|
||||
KeychainDAO = require('js/dao/keychain-dao'),
|
||||
EmailDAO = require('js/dao/email-dao'),
|
||||
appController = require('js/app-controller');
|
||||
|
||||
describe('Login (new device) Controller unit test', function() {
|
||||
var scope, ctrl, origEmailDao, emailDaoMock,
|
||||
var scope, ctrl, origEmailDao, emailDaoMock, pgpMock,
|
||||
emailAddress = 'fred@foo.com',
|
||||
passphrase = 'asd',
|
||||
keyId,
|
||||
@ -24,8 +25,8 @@ define(function(require) {
|
||||
appController._emailDao = emailDaoMock;
|
||||
|
||||
keyId = '9FEB47936E712926';
|
||||
keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
emailDaoMock._keychain = keychainMock;
|
||||
emailDaoMock._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
appController._crypto = pgpMock = sinon.createStubInstance(PGP);
|
||||
|
||||
emailDaoMock._account = {
|
||||
emailAddress: emailAddress,
|
||||
@ -57,11 +58,14 @@ define(function(require) {
|
||||
});
|
||||
|
||||
describe('confirm passphrase', function() {
|
||||
it('should unlock crypto', function() {
|
||||
it('should unlock crypto with a public key on the server', function() {
|
||||
scope.passphrase = passphrase;
|
||||
scope.key = {
|
||||
privateKeyArmored: 'b'
|
||||
};
|
||||
|
||||
pgpMock.getKeyId.returns(keyId);
|
||||
|
||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(null, {
|
||||
_id: keyId,
|
||||
publicKey: 'a'
|
||||
@ -75,6 +79,25 @@ define(function(require) {
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should unlock crypto with no key on the server', function() {
|
||||
scope.passphrase = passphrase;
|
||||
scope.key = {
|
||||
privateKeyArmored: 'b',
|
||||
publicKeyArmored: 'a'
|
||||
};
|
||||
|
||||
pgpMock.getKeyId.returns(keyId);
|
||||
|
||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields();
|
||||
emailDaoMock.unlock.withArgs(sinon.match.any, passphrase).yields();
|
||||
keychainMock.putUserKeyPair.yields();
|
||||
|
||||
scope.confirmPassphrase();
|
||||
|
||||
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should not do anything without passphrase', function() {
|
||||
scope.state.passphrase = '';
|
||||
|
||||
|
@ -139,10 +139,22 @@ define(function(require) {
|
||||
});
|
||||
|
||||
describe('Get KeyId', function() {
|
||||
it('should work', function() {
|
||||
it('should work without param', function() {
|
||||
var keyId = pgp.getKeyId();
|
||||
expect(keyId).to.equal('F6F60E9B42CDFF4C');
|
||||
});
|
||||
|
||||
it('should work with param', function() {
|
||||
var keyId = pgp.getKeyId(pubkey);
|
||||
expect(keyId).to.equal('F6F60E9B42CDFF4C');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Get UserId', function() {
|
||||
it('should work with param', function() {
|
||||
var userId = pgp.getUserId(pubkey);
|
||||
expect(userId).to.contain(user);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Get Fingerprint', function() {
|
||||
|
Loading…
Reference in New Issue
Block a user