From e943dfd3047b1aed4816ae08bcd016c0e57ad5bc Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Fri, 19 Apr 2013 13:55:21 +0200 Subject: [PATCH] implemented public key cloud storage methods --- src/js/crypto/crypto.js | 11 +++ src/js/dao/cloudstorage-dao.js | 108 ++++++++++++++++++---- src/js/dao/email-dao.js | 8 +- src/js/view/messagelist-view.js | 6 +- test/integration/cloudstorage-dao-test.js | 30 +++++- test/integration/index.html | 3 +- 6 files changed, 136 insertions(+), 30 deletions(-) diff --git a/src/js/crypto/crypto.js b/src/js/crypto/crypto.js index c33fc61..facabec 100644 --- a/src/js/crypto/crypto.js +++ b/src/js/crypto/crypto.js @@ -75,6 +75,17 @@ app.crypto.Crypto = function(window, util) { } }; + /** + * Derive an asymmetric keypait from the user's secret + */ + this.deriveKeyPair = function(naclCrypto, id) { + var keys = naclCrypto.generateKeypair(symmetricUserKey); + if (id) { + keys.id = id; + } + return keys; + }; + // // En/Decrypts single item // diff --git a/src/js/dao/cloudstorage-dao.js b/src/js/dao/cloudstorage-dao.js index 3bb1d22..19d5f19 100644 --- a/src/js/dao/cloudstorage-dao.js +++ b/src/js/dao/cloudstorage-dao.js @@ -5,23 +5,30 @@ app.dao.CloudStorage = function(window, $) { 'use strict'; - /** - * Lists the encrypted items - * @param type [String] The type of item e.g. 'email' - * @param offset [Number] The offset of items to fetch (0 is the last stored item) - * @param num [Number] The number of items to fetch (null means fetch all) - */ - this.listEncryptedItems = function(type, emailAddress, folderName, callback) { - var folder, uri, self = this; + // + // Public Key + // - // fetch encrypted json objects from cloud service - uri = app.config.cloudUrl + '/' + type + '/user/' + emailAddress + '/folder/' + folderName; + /** + * Find the user's corresponding public key + */ + this.getPublicKey = function(emailAddress, callback) { + var uri; + + uri = app.config.cloudUrl + '/publickey/user/' + emailAddress; $.ajax({ url: uri, type: 'GET', dataType: 'json', success: function(list) { - callback(list); + if (!list || list.length === 0) { + callback({ + error: 'No public key for that user!' + }); + return; + } + + callback(null, list[0]); }, error: function(xhr, textStatus, err) { callback({ @@ -32,14 +39,74 @@ app.dao.CloudStorage = function(window, $) { }); }; + /** + * Persist the user's publc key + */ + this.putPublicKey = function(pubkey, callback) { + var uri; + + uri = app.config.cloudUrl + '/publickey/user/' + pubkey.userId + '/key/' + pubkey._id; + $.ajax({ + url: uri, + type: 'PUT', + data: JSON.stringify(pubkey), + contentType: 'application/json', + success: function() { + callback(); + }, + error: function(xhr, textStatus, err) { + callback({ + error: err, + status: textStatus + }); + } + }); + }; + + // + // Encrypted Mails + // + + /** + * Lists the encrypted items + * @param type [String] The type of item e.g. 'email' + * @param offset [Number] The offset of items to fetch (0 is the last stored item) + * @param num [Number] The number of items to fetch (null means fetch all) + */ + this.listEncryptedItems = function(type, emailAddress, folderName, callback) { + var uri; + + // fetch encrypted json objects from cloud service + uri = app.config.cloudUrl + '/' + type + '/user/' + emailAddress + '/folder/' + folderName; + $.ajax({ + url: uri, + type: 'GET', + dataType: 'json', + success: function(list) { + callback(null, list); + }, + error: function(xhr, textStatus, err) { + callback({ + error: err, + status: textStatus + }); + } + }); + }; + + // + // Secret Key + // + /** * Persist encrypted user key to cloud service */ - this.persistUserSecretKey = function(emailAddress, callback) { + this.putUserSecretKey = function(emailAddress, callback) { // fetch user's encrypted secret key from keychain/storage var keyStore = new app.dao.LocalStorageDAO(window), storageId = emailAddress + '_encryptedSymmetricKey', - storedKey = keyStore.read(storageId); + storedKey = keyStore.read(storageId), + uri; if (!storedKey) { callback({ @@ -49,8 +116,9 @@ app.dao.CloudStorage = function(window, $) { return; } + uri = app.config.cloudUrl + '/secretkey/user/' + emailAddress + '/key/' + storedKey._id; $.ajax({ - url: app.config.cloudUrl + '/secretkey/user/' + emailAddress + '/key/' + storedKey._id, + url: uri, type: 'PUT', data: JSON.stringify(storedKey), contentType: 'application/json', @@ -74,10 +142,12 @@ app.dao.CloudStorage = function(window, $) { var self = this, keyStore = new app.dao.LocalStorageDAO(window), storageId = emailAddress + '_encryptedSymmetricKey', - storedKey = keyStore.read(storageId); + storedKey = keyStore.read(storageId), + uri; + uri = app.config.cloudUrl + '/secretkey/user/' + emailAddress; $.ajax({ - url: app.config.cloudUrl + '/secretkey/user/' + emailAddress, + url: uri, type: 'GET', dataType: 'json', success: function(keys) { @@ -107,14 +177,14 @@ app.dao.CloudStorage = function(window, $) { } else if (storedKey && fetchedKey && (storedKey.encryptedKey !== fetchedKey.encryptedKey || storedKey.keyIV !== fetchedKey.keyIV)) { // local and fetched keys are not equal - if (confirm('Swap local key?')) { + if (window.confirm('Swap local key?')) { // replace local key with fetched key keyStore.persist(storageId, fetchedKey); replaceCallback(); } else { - if (confirm('Swap cloud key?')) { + if (window.confirm('Swap cloud key?')) { // upload local key to cloud - self.persistUserSecretKey(emailAddress, callback); + self.putUserSecretKey(emailAddress, callback); } else { callback({ error: 'err', diff --git a/src/js/dao/email-dao.js b/src/js/dao/email-dao.js index a26b0f8..fad3a7e 100644 --- a/src/js/dao/email-dao.js +++ b/src/js/dao/email-dao.js @@ -91,10 +91,12 @@ app.dao.EmailDAO = function(_, crypto, devicestorage, cloudstorage) { this.syncFromCloud = function(folderName, callback) { var folder, self = this; - cloudstorage.listEncryptedItems('email', this.account.get('emailAddress'), folderName, function(res) { + cloudstorage.listEncryptedItems('email', this.account.get('emailAddress'), folderName, function(err, res) { // return if an error occured or if fetched list from cloud storage is empty - if (!res || res.status || res.length === 0) { - callback(res); // error + if (err || !res || res.status || res.length === 0) { + callback({ + error: err + }); // error return; } diff --git a/src/js/view/messagelist-view.js b/src/js/view/messagelist-view.js index a5f7d53..f5ded12 100644 --- a/src/js/view/messagelist-view.js +++ b/src/js/view/messagelist-view.js @@ -33,12 +33,12 @@ textVisible: true }); // sync from cloud - this.dao.syncFromCloud(this.folder, function(res) { + this.dao.syncFromCloud(this.folder, function(err) { $.mobile.loading('hide'); // check for error - if (res && res.status) { - alert('Syncing failed!'); + if (err) { + window.alert('Syncing failed!'); return; } diff --git a/test/integration/cloudstorage-dao-test.js b/test/integration/cloudstorage-dao-test.js index 94c8bff..2168edc 100644 --- a/test/integration/cloudstorage-dao-test.js +++ b/test/integration/cloudstorage-dao-test.js @@ -4,7 +4,13 @@ var cloudstoragedao_test = { user: 'test@atlasdev.onmicrosoft.com', password: 'Xoza76645', keySize: 128, - ivSize: 104 + ivSize: 104, + privateKey: "Bv51afjeuH8CatKo75HOHQRT1B3amvF+DEwijka79nA=", + publicKey: new app.model.PublicKey({ + _id: "da4bfa93-ba87-490e-877c-e4020a6f6729", + userId: 'test@atlasdev.onmicrosoft.com', + publicKey: "yHUhh3Pcbjmh2k367pSXfE8hDwPsAxQs0QETm9mfmz0=" + }) }; asyncTest("Init", 1, function() { @@ -24,6 +30,22 @@ asyncTest("Init", 1, function() { }); }); +asyncTest("Persist public key to cloud", 1, function() { + cloudstoragedao_test.cloudstorage.putPublicKey(cloudstoragedao_test.publicKey.toJSON(), function(err) { + ok(!err, 'Persist key to cloud'); + + start(); + }); +}); + +asyncTest("Get Public key from cloud", 2, function() { + cloudstoragedao_test.cloudstorage.getPublicKey(cloudstoragedao_test.user, function(err, data) { + ok(!err && data && data.publicKey, 'Get public key from cloud'); + deepEqual(data, cloudstoragedao_test.publicKey.toJSON(), 'Public key is equal'); + + start(); + }); +}); asyncTest("Get user secret key from cloud", 1, function() { cloudstoragedao_test.cloudstorage.getUserSecretKey(cloudstoragedao_test.user, function(err) { @@ -40,7 +62,7 @@ asyncTest("Get user secret key from cloud", 1, function() { }); asyncTest("Persist user secret key to cloud", 1, function() { - cloudstoragedao_test.cloudstorage.persistUserSecretKey(cloudstoragedao_test.user, function(err) { + cloudstoragedao_test.cloudstorage.putUserSecretKey(cloudstoragedao_test.user, function(err) { ok(!err, 'Persist key to cloud'); start(); @@ -58,8 +80,8 @@ asyncTest("Sync emails from cloud", 3, function() { cloudstoragedao_test.emailDao.init(account, cloudstoragedao_test.password, function() { ok(true, 'Init complete'); - cloudstoragedao_test.emailDao.syncFromCloud('inbox', function(res) { - ok(!res, 'Synced items'); + cloudstoragedao_test.emailDao.syncFromCloud('inbox', function(err) { + ok(!err, 'Synced items'); cloudstoragedao_test.emailDao.listItems('inbox', 0, null, function(collection) { ok(collection.length > 0, 'Read synced items'); diff --git a/test/integration/index.html b/test/integration/index.html index 9d90095..94c7f27 100644 --- a/test/integration/index.html +++ b/test/integration/index.html @@ -47,12 +47,13 @@ +