mirror of
https://github.com/moparisthebest/mail
synced 2024-11-22 17:02:17 -05:00
refactoring of crypto worker code and lots of cleanup
This commit is contained in:
parent
622e787ba7
commit
f2a14ad65b
@ -4,7 +4,6 @@
|
|||||||
"jquery": true,
|
"jquery": true,
|
||||||
"node": true,
|
"node": true,
|
||||||
"browser": true,
|
"browser": true,
|
||||||
"camelcase": true,
|
|
||||||
"nonew": true,
|
"nonew": true,
|
||||||
"curly": true,
|
"curly": true,
|
||||||
"eqeqeq": true,
|
"eqeqeq": true,
|
||||||
@ -32,10 +31,12 @@
|
|||||||
"describe",
|
"describe",
|
||||||
"it",
|
"it",
|
||||||
"chai",
|
"chai",
|
||||||
|
"asyncTest",
|
||||||
"ok",
|
"ok",
|
||||||
"equal",
|
"equal",
|
||||||
"deepEqual",
|
"deepEqual",
|
||||||
"start",
|
"start",
|
||||||
|
"TestData",
|
||||||
"chrome"
|
"chrome"
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
var CryptoBatch = function(aes, rsa, util, _) {
|
var CryptoBatch = function(aes, rsa, util, _) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encrypt and sign a an item using AES and RSA
|
* Encrypt and sign an item using AES and RSA
|
||||||
* @param i [Object] The item to encrypt
|
* @param i [Object] The item to encrypt
|
||||||
* @param receiverPubkey [String] The public key used to encrypt
|
* @param receiverPubkey [String] The public key used to encrypt
|
||||||
* @param senderKeyId [String] The sender's private key ID used to sign
|
* @param senderKeyId [String] The sender's private key ID used to sign
|
||||||
|
@ -29,7 +29,11 @@ app.crypto.Crypto = function(window, util) {
|
|||||||
self.rsaKeySize = args.rsaKeySize;
|
self.rsaKeySize = args.rsaKeySize;
|
||||||
|
|
||||||
// derive PBKDF2 from password in web worker thread
|
// derive PBKDF2 from password in web worker thread
|
||||||
self.deriveKey(args.password, self.keySize, function(pbkdf2) {
|
self.deriveKey(args.password, self.keySize, function(err, pbkdf2) {
|
||||||
|
if (err) {
|
||||||
|
callback(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// check if key exists
|
// check if key exists
|
||||||
if (!args.storedKeypair) {
|
if (!args.storedKeypair) {
|
||||||
@ -106,29 +110,13 @@ app.crypto.Crypto = function(window, util) {
|
|||||||
* Do PBKDF2 key derivation in a WebWorker thread
|
* Do PBKDF2 key derivation in a WebWorker thread
|
||||||
*/
|
*/
|
||||||
this.deriveKey = function(password, keySize, callback) {
|
this.deriveKey = function(password, keySize, callback) {
|
||||||
// check for WebWorker support
|
startWorker('/crypto/pbkdf2-worker.js', {
|
||||||
if (window.Worker) {
|
|
||||||
|
|
||||||
// init webworker thread
|
|
||||||
var worker = new Worker(app.config.workerPath + '/crypto/pbkdf2-worker.js');
|
|
||||||
|
|
||||||
worker.onmessage = function(e) {
|
|
||||||
// return derived key from the worker
|
|
||||||
callback(e.data);
|
|
||||||
};
|
|
||||||
|
|
||||||
// send plaintext data to the worker
|
|
||||||
worker.postMessage({
|
|
||||||
password: password,
|
password: password,
|
||||||
keySize: keySize
|
keySize: keySize
|
||||||
});
|
}, callback, function() {
|
||||||
|
|
||||||
} else {
|
|
||||||
// no WebWorker support... do synchronous call
|
|
||||||
var pbkdf2 = new app.crypto.PBKDF2();
|
var pbkdf2 = new app.crypto.PBKDF2();
|
||||||
var key = pbkdf2.getKey(password, keySize);
|
return pbkdf2.getKey(password, keySize);
|
||||||
callback(key);
|
});
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -136,43 +124,29 @@ app.crypto.Crypto = function(window, util) {
|
|||||||
//
|
//
|
||||||
|
|
||||||
this.aesEncrypt = function(plaintext, key, iv, callback) {
|
this.aesEncrypt = function(plaintext, key, iv, callback) {
|
||||||
if (window.Worker) {
|
var self = this;
|
||||||
|
|
||||||
var worker = new Worker(app.config.workerPath + '/crypto/aes-worker.js');
|
startWorker('/crypto/aes-worker.js', {
|
||||||
worker.onmessage = function(e) {
|
|
||||||
callback(e.data);
|
|
||||||
};
|
|
||||||
worker.postMessage({
|
|
||||||
type: 'encrypt',
|
type: 'encrypt',
|
||||||
plaintext: plaintext,
|
plaintext: plaintext,
|
||||||
key: key,
|
key: key,
|
||||||
iv: iv
|
iv: iv
|
||||||
|
}, callback, function() {
|
||||||
|
return self.aesEncryptSync(plaintext, key, iv);
|
||||||
});
|
});
|
||||||
|
|
||||||
} else {
|
|
||||||
var ct = this.aesEncryptSync(plaintext, key, iv);
|
|
||||||
callback(ct);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.aesDecrypt = function(ciphertext, key, iv, callback) {
|
this.aesDecrypt = function(ciphertext, key, iv, callback) {
|
||||||
if (window.Worker) {
|
var self = this;
|
||||||
|
|
||||||
var worker = new Worker(app.config.workerPath + '/crypto/aes-worker.js');
|
startWorker('/crypto/aes-worker.js', {
|
||||||
worker.onmessage = function(e) {
|
|
||||||
callback(e.data);
|
|
||||||
};
|
|
||||||
worker.postMessage({
|
|
||||||
type: 'decrypt',
|
type: 'decrypt',
|
||||||
ciphertext: ciphertext,
|
ciphertext: ciphertext,
|
||||||
key: key,
|
key: key,
|
||||||
iv: iv
|
iv: iv
|
||||||
|
}, callback, function() {
|
||||||
|
return self.aesDecryptSync(ciphertext, key, iv);
|
||||||
});
|
});
|
||||||
|
|
||||||
} else {
|
|
||||||
var pt = this.aesDecryptSync(ciphertext, key, iv);
|
|
||||||
callback(pt);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.aesEncryptSync = function(plaintext, key, iv) {
|
this.aesEncryptSync = function(plaintext, key, iv) {
|
||||||
@ -188,41 +162,23 @@ app.crypto.Crypto = function(window, util) {
|
|||||||
//
|
//
|
||||||
|
|
||||||
this.aesEncryptList = function(list, callback) {
|
this.aesEncryptList = function(list, callback) {
|
||||||
if (window.Worker) {
|
startWorker('/crypto/aes-batch-worker.js', {
|
||||||
|
|
||||||
var worker = new Worker(app.config.workerPath + '/crypto/aes-batch-worker.js');
|
|
||||||
worker.onmessage = function(e) {
|
|
||||||
callback(e.data);
|
|
||||||
};
|
|
||||||
worker.postMessage({
|
|
||||||
type: 'encrypt',
|
type: 'encrypt',
|
||||||
list: list
|
list: list
|
||||||
});
|
}, callback, function() {
|
||||||
|
|
||||||
} else {
|
|
||||||
var batch = new cryptoLib.CryptoBatch(aes);
|
var batch = new cryptoLib.CryptoBatch(aes);
|
||||||
var encryptedList = batch.encryptList(list);
|
return batch.encryptList(list);
|
||||||
callback(encryptedList);
|
});
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.aesDecryptList = function(list, callback) {
|
this.aesDecryptList = function(list, callback) {
|
||||||
if (window.Worker) {
|
startWorker('/crypto/aes-batch-worker.js', {
|
||||||
|
|
||||||
var worker = new Worker(app.config.workerPath + '/crypto/aes-batch-worker.js');
|
|
||||||
worker.onmessage = function(e) {
|
|
||||||
callback(e.data);
|
|
||||||
};
|
|
||||||
worker.postMessage({
|
|
||||||
type: 'decrypt',
|
type: 'decrypt',
|
||||||
list: list
|
list: list
|
||||||
});
|
}, callback, function() {
|
||||||
|
|
||||||
} else {
|
|
||||||
var batch = new cryptoLib.CryptoBatch(aes);
|
var batch = new cryptoLib.CryptoBatch(aes);
|
||||||
var decryptedList = batch.decryptList(list);
|
return batch.decryptList(list);
|
||||||
callback(decryptedList);
|
});
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -258,24 +214,15 @@ app.crypto.Crypto = function(window, util) {
|
|||||||
envelopes.push(envelope);
|
envelopes.push(envelope);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (window.Worker) {
|
startWorker('/crypto/crypto-batch-worker.js', {
|
||||||
|
|
||||||
var worker = new Worker(app.config.workerPath + '/crypto/crypto-batch-worker.js');
|
|
||||||
worker.onmessage = function(e) {
|
|
||||||
callback(null, e.data);
|
|
||||||
};
|
|
||||||
worker.postMessage({
|
|
||||||
type: 'encrypt',
|
type: 'encrypt',
|
||||||
list: envelopes,
|
list: envelopes,
|
||||||
senderPrivkey: senderPrivkey,
|
senderPrivkey: senderPrivkey,
|
||||||
receiverPubkeys: receiverPubkeys
|
receiverPubkeys: receiverPubkeys
|
||||||
});
|
}, callback, function() {
|
||||||
|
|
||||||
} else {
|
|
||||||
var batch = new cryptoLib.CryptoBatch(aes, rsa, util, _);
|
var batch = new cryptoLib.CryptoBatch(aes, rsa, util, _);
|
||||||
var encryptedList = batch.encryptListForUser(envelopes, receiverPubkeys, senderPrivkey);
|
return batch.encryptListForUser(envelopes, receiverPubkeys, senderPrivkey);
|
||||||
callback(null, encryptedList);
|
});
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.decryptListForUser = function(list, senderPubkeys, callback) {
|
this.decryptListForUser = function(list, senderPubkeys, callback) {
|
||||||
@ -292,24 +239,37 @@ app.crypto.Crypto = function(window, util) {
|
|||||||
privateKey: keypair.privkeyPem
|
privateKey: keypair.privkeyPem
|
||||||
};
|
};
|
||||||
|
|
||||||
if (window.Worker) {
|
startWorker('/crypto/crypto-batch-worker.js', {
|
||||||
|
|
||||||
var worker = new Worker(app.config.workerPath + '/crypto/crypto-batch-worker.js');
|
|
||||||
worker.onmessage = function(e) {
|
|
||||||
callback(null, e.data);
|
|
||||||
};
|
|
||||||
worker.postMessage({
|
|
||||||
type: 'decrypt',
|
type: 'decrypt',
|
||||||
list: list,
|
list: list,
|
||||||
receiverPrivkey: receiverPrivkey,
|
receiverPrivkey: receiverPrivkey,
|
||||||
senderPubkeys: senderPubkeys
|
senderPubkeys: senderPubkeys
|
||||||
|
}, callback, function() {
|
||||||
|
var batch = new cryptoLib.CryptoBatch(aes, rsa, util, _);
|
||||||
|
return batch.decryptListForUser(list, senderPubkeys, receiverPrivkey);
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function startWorker(script, args, callback, noWorker) {
|
||||||
|
// check for WebWorker support
|
||||||
|
if (window.Worker) {
|
||||||
|
|
||||||
|
// init webworker thread
|
||||||
|
var worker = new Worker(app.config.workerPath + script);
|
||||||
|
|
||||||
|
worker.onmessage = function(e) {
|
||||||
|
// return derived key from the worker
|
||||||
|
callback(null, e.data);
|
||||||
|
};
|
||||||
|
|
||||||
|
// send data to the worker
|
||||||
|
worker.postMessage(args);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
var batch = new cryptoLib.CryptoBatch(aes, rsa, util, _);
|
// no WebWorker support... do synchronous call
|
||||||
var decryptedList = batch.decryptListForUser(list, senderPubkeys, receiverPrivkey);
|
var result = noWorker();
|
||||||
callback(null, decryptedList);
|
callback(null, result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
};
|
};
|
@ -1,341 +0,0 @@
|
|||||||
/**
|
|
||||||
* A wrapper for asymmetric OpenPGP encryption logic
|
|
||||||
*/
|
|
||||||
app.crypto.PGP = function(window, openpgp, util, server) {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var self = this,
|
|
||||||
privateKey, // user's private key
|
|
||||||
publicKey, // user's public key
|
|
||||||
passphrase; // user's passphrase used for decryption
|
|
||||||
|
|
||||||
openpgp.init(); // initialize OpenPGP.js
|
|
||||||
|
|
||||||
//
|
|
||||||
// Key management
|
|
||||||
//
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if user already has a public key on the server and if not,
|
|
||||||
* generate a new keypait for the user
|
|
||||||
*/
|
|
||||||
self.initKeyPair = function(loginInfo, callback, displayCallback, finishCallback) {
|
|
||||||
// check if user already has a keypair in local storage
|
|
||||||
if (loginInfo.publicKeyId) {
|
|
||||||
// decode base 64 key ID
|
|
||||||
var keyId = window.atob(loginInfo.publicKeyId);
|
|
||||||
// read the user's keys from local storage
|
|
||||||
callback(keyId);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// user has no key pair yet
|
|
||||||
displayCallback(function() {
|
|
||||||
// generate new key pair with 2048 bit RSA keys
|
|
||||||
var keys = self.generateKeys(2048);
|
|
||||||
var keyId = keys.privateKey.getKeyId();
|
|
||||||
|
|
||||||
// display finish
|
|
||||||
finishCallback(keyId);
|
|
||||||
// read the user's keys from local storage
|
|
||||||
callback(keyId);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a key pair for the user
|
|
||||||
* @param numBits [int] number of bits for the key creation. (should be 1024+, generally)
|
|
||||||
* @email [string] user's email address
|
|
||||||
* @pass [string] a passphrase used to protect the private key
|
|
||||||
*/
|
|
||||||
self.generateKeys = function(numBits) {
|
|
||||||
// check passphrase
|
|
||||||
if (!passphrase && passphrase !== '') {
|
|
||||||
throw 'No passphrase set!';
|
|
||||||
}
|
|
||||||
|
|
||||||
var userId = 'SafeWith.me User <anonymous@dunno.com>';
|
|
||||||
var keys = openpgp.generate_key_pair(1, numBits, userId, passphrase); // keytype 1=RSA
|
|
||||||
|
|
||||||
self.importKeys(keys.publicKeyArmored, keys.privateKeyArmored);
|
|
||||||
|
|
||||||
return keys;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Import the users key into the HTML5 local storage
|
|
||||||
*/
|
|
||||||
self.importKeys = function(publicKeyArmored, privateKeyArmored) {
|
|
||||||
// check passphrase
|
|
||||||
if (!passphrase && passphrase !== '') {
|
|
||||||
throw 'No passphrase set!';
|
|
||||||
}
|
|
||||||
|
|
||||||
// store keys in html5 local storage
|
|
||||||
openpgp.keyring.importPrivateKey(privateKeyArmored, passphrase);
|
|
||||||
openpgp.keyring.importPublicKey(publicKeyArmored);
|
|
||||||
openpgp.keyring.store();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Export the keys by using the HTML5 FileWriter
|
|
||||||
*/
|
|
||||||
self.exportKeys = function(callback) {
|
|
||||||
// build blob
|
|
||||||
var buf = util.binStr2ArrBuf(publicKey.armored + privateKey.armored);
|
|
||||||
var blob = util.arrBuf2Blob(buf, 'text/plain');
|
|
||||||
// create url
|
|
||||||
util.createUrl(undefined, blob, callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read the users keys from the browser's HTML5 local storage
|
|
||||||
* @email [string] user's email address
|
|
||||||
* @keyId [string] the public key ID in unicode (not base 64)
|
|
||||||
*/
|
|
||||||
self.readKeys = function(keyId, callback, errorCallback) {
|
|
||||||
// read keys from keyring (local storage)
|
|
||||||
var privKeyQuery = openpgp.keyring.getPrivateKeyForKeyId(keyId)[0];
|
|
||||||
if (privKeyQuery) {
|
|
||||||
privateKey = privKeyQuery.key;
|
|
||||||
}
|
|
||||||
publicKey = openpgp.keyring.getPublicKeysForKeyId(keyId)[0];
|
|
||||||
|
|
||||||
// check keys
|
|
||||||
if (!publicKey || !privateKey || (publicKey.keyId !== privateKey.keyId)) {
|
|
||||||
// no amtching keys found in the key store
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// read passphrase from local storage if no passphrase is specified
|
|
||||||
if (!passphrase && passphrase !== '') {
|
|
||||||
passphrase = window.sessionStorage.getItem(window.btoa(keyId) + 'Passphrase');
|
|
||||||
}
|
|
||||||
|
|
||||||
// check passphrase
|
|
||||||
if (!passphrase && passphrase !== '') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// do test encrypt/decrypt to verify passphrase
|
|
||||||
try {
|
|
||||||
var testCt = self.asymmetricEncrypt('test');
|
|
||||||
self.asymmetricDecrypt(testCt);
|
|
||||||
} catch (e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a new key pair for the user and persist the public key on the server
|
|
||||||
*/
|
|
||||||
self.syncKeysToServer = function(email, callback) {
|
|
||||||
// base64 encode key ID
|
|
||||||
var keyId = publicKey.keyId;
|
|
||||||
var encodedKeyId = window.btoa(keyId);
|
|
||||||
var pubKey = {
|
|
||||||
keyId: encodedKeyId,
|
|
||||||
ownerEmail: email,
|
|
||||||
asciiArmored: publicKey.armored
|
|
||||||
};
|
|
||||||
var privKey = {
|
|
||||||
keyId: encodedKeyId,
|
|
||||||
ownerEmail: email,
|
|
||||||
asciiArmored: privateKey.armored
|
|
||||||
};
|
|
||||||
|
|
||||||
var jsonPublicKey = JSON.stringify(pubKey);
|
|
||||||
var jsonPrivateKey = JSON.stringify(privKey);
|
|
||||||
|
|
||||||
// first upload public key
|
|
||||||
server.xhr({
|
|
||||||
type: 'POST',
|
|
||||||
uri: '/ws/publicKeys',
|
|
||||||
contentType: 'application/json',
|
|
||||||
expected: 201,
|
|
||||||
body: jsonPublicKey,
|
|
||||||
success: function(resp) {
|
|
||||||
uploadPrivateKeys();
|
|
||||||
},
|
|
||||||
error: function(e) {
|
|
||||||
// if server is not available, just continue
|
|
||||||
// and read the user's keys from local storage
|
|
||||||
console.log('Server unavailable: keys were not synced to server!');
|
|
||||||
callback(keyId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// then upload private key
|
|
||||||
|
|
||||||
function uploadPrivateKeys() {
|
|
||||||
server.xhr({
|
|
||||||
type: 'POST',
|
|
||||||
uri: '/ws/privateKeys',
|
|
||||||
contentType: 'application/json',
|
|
||||||
expected: 201,
|
|
||||||
body: jsonPrivateKey,
|
|
||||||
success: function(resp) {
|
|
||||||
// read the user's keys from local storage
|
|
||||||
callback(keyId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the keypair from the server and import them into localstorage
|
|
||||||
*/
|
|
||||||
self.fetchKeys = function(email, keyId, callback, errCallback) {
|
|
||||||
var base64Key = window.btoa(keyId);
|
|
||||||
var encodedKeyId = encodeURIComponent(base64Key);
|
|
||||||
|
|
||||||
// get public key
|
|
||||||
server.xhr({
|
|
||||||
type: 'GET',
|
|
||||||
uri: '/ws/publicKeys?keyId=' + encodedKeyId,
|
|
||||||
expected: 200,
|
|
||||||
success: function(pubKey) {
|
|
||||||
getPrivateKey(pubKey);
|
|
||||||
},
|
|
||||||
error: function(e) {
|
|
||||||
// if server is not available, just continue
|
|
||||||
console.log('Server unavailable: keys could not be fetched from server!');
|
|
||||||
errCallback(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// get private key
|
|
||||||
|
|
||||||
function getPrivateKey(pubKey) {
|
|
||||||
server.xhr({
|
|
||||||
type: 'GET',
|
|
||||||
uri: '/ws/privateKeys?keyId=' + encodedKeyId,
|
|
||||||
expected: 200,
|
|
||||||
success: function(privKey) {
|
|
||||||
// import keys
|
|
||||||
self.importKeys(pubKey.asciiArmored, privKey.asciiArmored, email);
|
|
||||||
callback({
|
|
||||||
privateKey: privKey,
|
|
||||||
publicKey: pubKey
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current user's private key
|
|
||||||
*/
|
|
||||||
self.getPrivateKey = function() {
|
|
||||||
if (!privateKey) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
return privateKey.armored;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current user's public key
|
|
||||||
*/
|
|
||||||
self.getPublicKey = function() {
|
|
||||||
if (!publicKey) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
return publicKey.armored;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current user's base64 encoded public key ID
|
|
||||||
*/
|
|
||||||
self.getPublicKeyIdBase64 = function() {
|
|
||||||
return window.btoa(publicKey.keyId);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the user's passphrase for decrypting their private key
|
|
||||||
*/
|
|
||||||
self.setPassphrase = function(pass) {
|
|
||||||
passphrase = pass;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store the passphrase for the current session
|
|
||||||
*/
|
|
||||||
self.rememberPassphrase = function(keyId) {
|
|
||||||
var base64KeyId = window.btoa(keyId);
|
|
||||||
window.sessionStorage.setItem(base64KeyId + 'Passphrase', passphrase);
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Asymmetric crypto
|
|
||||||
//
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypt a string
|
|
||||||
* @param customPubKey [PublicKey] (optional) another user's public key for sharing
|
|
||||||
*/
|
|
||||||
self.asymmetricEncrypt = function(plaintext, customPubKey) {
|
|
||||||
var pub_key = null;
|
|
||||||
if (customPubKey) {
|
|
||||||
// use a custom set public for e.g. or sharing
|
|
||||||
pub_key = openpgp.read_publicKey(customPubKey);
|
|
||||||
} else {
|
|
||||||
// use the user's local public key
|
|
||||||
pub_key = openpgp.read_publicKey(publicKey.armored);
|
|
||||||
}
|
|
||||||
|
|
||||||
var ciphertext = openpgp.write_encrypted_message(pub_key, window.btoa(plaintext));
|
|
||||||
return ciphertext;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypt a string
|
|
||||||
*/
|
|
||||||
self.asymmetricDecrypt = function(ciphertext) {
|
|
||||||
var priv_key = openpgp.read_privateKey(privateKey.armored);
|
|
||||||
|
|
||||||
var msg = openpgp.read_message(ciphertext);
|
|
||||||
var keymat = null;
|
|
||||||
var sesskey = null;
|
|
||||||
|
|
||||||
// Find the private (sub)key for the session key of the message
|
|
||||||
for (var i = 0; i < msg[0].sessionKeys.length; i++) {
|
|
||||||
if (priv_key[0].privateKeyPacket.publicKey.getKeyId() == msg[0].sessionKeys[i].keyId.bytes) {
|
|
||||||
keymat = {
|
|
||||||
key: priv_key[0],
|
|
||||||
keymaterial: priv_key[0].privateKeyPacket
|
|
||||||
};
|
|
||||||
sesskey = msg[0].sessionKeys[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
for (var j = 0; j < priv_key[0].subKeys.length; j++) {
|
|
||||||
if (priv_key[0].subKeys[j].publicKey.getKeyId() == msg[0].sessionKeys[i].keyId.bytes) {
|
|
||||||
keymat = {
|
|
||||||
key: priv_key[0],
|
|
||||||
keymaterial: priv_key[0].subKeys[j]
|
|
||||||
};
|
|
||||||
sesskey = msg[0].sessionKeys[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (keymat !== null) {
|
|
||||||
if (!keymat.keymaterial.decryptSecretMPIs(passphrase)) {
|
|
||||||
throw "Passphrase for secrect key was incorrect!";
|
|
||||||
}
|
|
||||||
|
|
||||||
var decrypted = msg[0].decrypt(keymat, sesskey);
|
|
||||||
return window.atob(decrypted);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
throw "No private key found!";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function needs to be implemented, since it is used by the openpgp utils
|
|
||||||
*/
|
|
||||||
|
|
||||||
function showMessages(str) {}
|
|
@ -54,23 +54,23 @@
|
|||||||
this.formatDate = function(date) {
|
this.formatDate = function(date) {
|
||||||
var year = "" + date.getFullYear();
|
var year = "" + date.getFullYear();
|
||||||
var month = "" + (date.getMonth() + 1);
|
var month = "" + (date.getMonth() + 1);
|
||||||
if (month.length == 1) {
|
if (month.length === 1) {
|
||||||
month = "0" + month;
|
month = "0" + month;
|
||||||
}
|
}
|
||||||
var day = "" + date.getDate();
|
var day = "" + date.getDate();
|
||||||
if (day.length == 1) {
|
if (day.length === 1) {
|
||||||
day = "0" + day;
|
day = "0" + day;
|
||||||
}
|
}
|
||||||
var hour = "" + date.getHours();
|
var hour = "" + date.getHours();
|
||||||
if (hour.length == 1) {
|
if (hour.length === 1) {
|
||||||
hour = "0" + hour;
|
hour = "0" + hour;
|
||||||
}
|
}
|
||||||
var minute = "" + date.getMinutes();
|
var minute = "" + date.getMinutes();
|
||||||
if (minute.length == 1) {
|
if (minute.length === 1) {
|
||||||
minute = "0" + minute;
|
minute = "0" + minute;
|
||||||
}
|
}
|
||||||
var second = "" + date.getSeconds();
|
var second = "" + date.getSeconds();
|
||||||
if (second.length == 1) {
|
if (second.length === 1) {
|
||||||
second = "0" + second;
|
second = "0" + second;
|
||||||
}
|
}
|
||||||
return year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second;
|
return year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second;
|
||||||
|
@ -1,70 +0,0 @@
|
|||||||
module("PGP Crypto");
|
|
||||||
|
|
||||||
var pgp_test = {
|
|
||||||
keyID: null,
|
|
||||||
keySize: 1024
|
|
||||||
};
|
|
||||||
|
|
||||||
asyncTest("Init", 1, function() {
|
|
||||||
// init dependencies
|
|
||||||
pgp_test.util = new app.crypto.Util(window);
|
|
||||||
pgp_test.crypto = new app.crypto.PGP(window, openpgp, util, null);
|
|
||||||
pgp_test.crypto.setPassphrase('asdf');
|
|
||||||
ok(pgp_test.crypto, 'PGP crypto');
|
|
||||||
|
|
||||||
pgp_test.helperEncrDecr = function(crypto, keyId, plaintext) {
|
|
||||||
if (!crypto.getPublicKey()) {
|
|
||||||
crypto.readKeys(keyId);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('plaintext size [bytes]: ' + plaintext.length);
|
|
||||||
|
|
||||||
var startTime = (new Date).getTime();
|
|
||||||
var ct = crypto.asymmetricEncrypt(plaintext);
|
|
||||||
var diff = (new Date).getTime() - startTime;
|
|
||||||
|
|
||||||
console.log('Time taken for encryption [ms]: ' + diff);
|
|
||||||
ok(ct, "ciphertext: see console output for benchmark");
|
|
||||||
console.log('ciphertext size [bytes]: ' + ct.length);
|
|
||||||
|
|
||||||
var decrStart = (new Date).getTime();
|
|
||||||
var pt = crypto.asymmetricDecrypt(ct);
|
|
||||||
var decrDiff = (new Date).getTime() - decrStart;
|
|
||||||
|
|
||||||
console.log('Time taken for decryption [ms]: ' + decrDiff);
|
|
||||||
ok(pt, "decrypted: see console output for benchmark");
|
|
||||||
equal(pt, plaintext, "Decrypted should be the same as the plaintext");
|
|
||||||
};
|
|
||||||
|
|
||||||
start();
|
|
||||||
});
|
|
||||||
|
|
||||||
asyncTest("Generate keypair, De/Encrypt", 7, function() {
|
|
||||||
var startTime = (new Date).getTime();
|
|
||||||
var keys = pgp_test.crypto.generateKeys(pgp_test.keySize);
|
|
||||||
var diff = (new Date).getTime() - startTime;
|
|
||||||
|
|
||||||
pgp_test.keyID = keys.privateKey.getKeyId();
|
|
||||||
pgp_test.crypto.readKeys(pgp_test.keyID);
|
|
||||||
|
|
||||||
console.log('Time taken for key generation [ms]: ' + diff + ' (' + pgp_test.keySize + ' bit RSA keypair)');
|
|
||||||
ok(pgp_test.crypto.getPrivateKey());
|
|
||||||
ok(pgp_test.crypto.getPrivateKey().indexOf('-----BEGIN PGP PRIVATE KEY BLOCK-----') === 0);
|
|
||||||
ok(pgp_test.crypto.getPublicKey());
|
|
||||||
ok(pgp_test.crypto.getPublicKey().indexOf('-----BEGIN PGP PUBLIC KEY BLOCK-----') === 0);
|
|
||||||
|
|
||||||
pgp_test.helperEncrDecr(pgp_test.crypto, pgp_test.keyID, '06a9214036b8a15b512e03d534120006');
|
|
||||||
|
|
||||||
start();
|
|
||||||
|
|
||||||
// pgp_test.crypto.exportKeys(function(url) {
|
|
||||||
// ok(url, 'export url');
|
|
||||||
//
|
|
||||||
// $.get(url, function(data) {
|
|
||||||
// ok(data.indexOf('-----BEGIN PGP PUBLIC KEY BLOCK-----') !== -1, 'exportd public key');
|
|
||||||
// ok(data.indexOf('-----END PGP PRIVATE KEY BLOCK-----') !== -1, 'export private key');
|
|
||||||
//
|
|
||||||
// start();
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
});
|
|
@ -1,3 +1,5 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
module("Crypto Api");
|
module("Crypto Api");
|
||||||
|
|
||||||
var crypto_test = {
|
var crypto_test = {
|
||||||
@ -40,30 +42,33 @@ asyncTest("Init with keypair", 1, function() {
|
|||||||
rsaKeySize: crypto_test.rsaKeySize,
|
rsaKeySize: crypto_test.rsaKeySize,
|
||||||
storedKeypair: crypto_test.generatedKeypair
|
storedKeypair: crypto_test.generatedKeypair
|
||||||
}, function(err, generatedKeypair) {
|
}, function(err, generatedKeypair) {
|
||||||
ok(!err, 'Init crypto with keypair input');
|
ok(!err && !generatedKeypair, 'Init crypto with keypair input');
|
||||||
|
|
||||||
start();
|
start();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
asyncTest("PBKDF2 (Async/Worker)", 1, function() {
|
asyncTest("PBKDF2 (Async/Worker)", 2, function() {
|
||||||
crypto_test.crypto.deriveKey(crypto_test.password, crypto_test.keySize, function(key) {
|
crypto_test.crypto.deriveKey(crypto_test.password, crypto_test.keySize, function(err, key) {
|
||||||
|
ok(!err);
|
||||||
equal(crypto_test.util.base642Str(key).length * 8, crypto_test.keySize, 'Keysize ' + crypto_test.keySize);
|
equal(crypto_test.util.base642Str(key).length * 8, crypto_test.keySize, 'Keysize ' + crypto_test.keySize);
|
||||||
|
|
||||||
start();
|
start();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
asyncTest("AES en/decrypt (Async/Worker)", 2, function() {
|
asyncTest("AES en/decrypt (Async/Worker)", 4, function() {
|
||||||
var secret = 'Big secret';
|
var secret = 'Big secret';
|
||||||
|
|
||||||
var key = crypto_test.util.random(crypto_test.keySize);
|
var key = crypto_test.util.random(crypto_test.keySize);
|
||||||
var iv = crypto_test.util.random(crypto_test.ivSize);
|
var iv = crypto_test.util.random(crypto_test.ivSize);
|
||||||
|
|
||||||
crypto_test.crypto.aesEncrypt(secret, key, iv, function(ciphertext) {
|
crypto_test.crypto.aesEncrypt(secret, key, iv, function(err, ciphertext) {
|
||||||
|
ok(!err);
|
||||||
ok(ciphertext, 'Encrypt item');
|
ok(ciphertext, 'Encrypt item');
|
||||||
|
|
||||||
crypto_test.crypto.aesDecrypt(ciphertext, key, iv, function(decrypted) {
|
crypto_test.crypto.aesDecrypt(ciphertext, key, iv, function(err, decrypted) {
|
||||||
|
ok(!err);
|
||||||
equal(decrypted, secret, 'Decrypt item');
|
equal(decrypted, secret, 'Decrypt item');
|
||||||
|
|
||||||
start();
|
start();
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
<script>
|
<script>
|
||||||
// clear session storage of failed tests, so async order is correct after fail & refresh
|
// clear session storage of failed tests, so async order is correct after fail & refresh
|
||||||
window.sessionStorage.clear();
|
window.sessionStorage.clear();
|
||||||
|
//window.Worker = undefined;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- dependencies -->
|
<!-- dependencies -->
|
||||||
|
Loading…
Reference in New Issue
Block a user