From 628cb0ddd944b9fa4472a715d5ae6f087579c24c Mon Sep 17 00:00:00 2001
From: Tankred Hase <tankred@whiteout.io>
Date: Fri, 30 Aug 2013 16:05:33 +0200
Subject: [PATCH] crypto error handling cleanup

---
 src/js/crypto/aes-cbc.js             | 134 ++++---
 src/js/crypto/crypto-batch-worker.js |  59 ++-
 src/js/crypto/crypto-batch.js        |   4 +-
 src/js/crypto/crypto.js              | 529 ++++++++++++++-------------
 src/js/dao/email-dao.js              |  11 +-
 test/new-unit/email-dao-test.js      |   2 +-
 6 files changed, 399 insertions(+), 340 deletions(-)

diff --git a/src/js/crypto/aes-cbc.js b/src/js/crypto/aes-cbc.js
index 91e317a..6f5ad6f 100644
--- a/src/js/crypto/aes-cbc.js
+++ b/src/js/crypto/aes-cbc.js
@@ -1,69 +1,91 @@
 (function() {
-	'use strict';
+    'use strict';
 
-	/**
-	 * A Wrapper for Forge's AES-CBC encryption
-	 */
-	var AesCBC = function(forge) {
+    /**
+     * A Wrapper for Forge's AES-CBC encryption
+     */
+    var AesCBC = function(forge) {
+        this._forge = forge;
+    };
 
-		var utl = forge.util;
+    /**
+     * Encrypt a String using AES-CBC-Pkcs7 using the provided keysize (e.g. 128, 256)
+     * @param plaintext [String] The input string in UTF-16
+     * @param key [String] The base64 encoded key
+     * @param iv [String] The base64 encoded IV
+     * @return [String] The base64 encoded ciphertext
+     */
+    AesCBC.prototype.encrypt = function(plaintext, key, iv) {
+        // validate args
+        if (!plaintext || !key || !iv) {
+            throw new Error("Missing args for encryption!");
+        }
 
-		/**
-		 * Encrypt a String using AES-CBC-Pkcs7 using the provided keysize (e.g. 128, 256)
-		 * @param plaintext [String] The input string in UTF-16
-		 * @param key [String] The base64 encoded key
-		 * @param iv [String] The base64 encoded IV
-		 * @return [String] The base64 encoded ciphertext
-		 */
-		this.encrypt = function(plaintext, key, iv) {
-			// validate args
-			if (!plaintext || !key || !iv) {
-				throw new Error("Missing args for encryption!");
-			}
+        // decode args to utf8 and encrypt
+        var cipher = this._forge.aes.createEncryptionCipher(this._forge.util.decode64(key));
+        cipher.start(this._forge.util.decode64(iv));
+        cipher.update(this._forge.util.createBuffer(this._forge.util.encodeUtf8(plaintext)));
+        cipher.finish();
 
-			// decode args to utf8 and encrypt
-			var cipher = forge.aes.createEncryptionCipher(utl.decode64(key));
-			cipher.start(utl.decode64(iv));
-			cipher.update(utl.createBuffer(utl.encodeUtf8(plaintext)));
-			cipher.finish();
+        // encode to base64
+        return this._forge.util.encode64(cipher.output.getBytes());
+    };
 
-			// encode to base64
-			return utl.encode64(cipher.output.getBytes());
-		};
+    /**
+     * Decrypt a String using AES-CBC-Pkcs7 using the provided keysize (e.g. 128, 256)
+     * @param ciphertext [String] The base64 encoded ciphertext
+     * @param key [String] The base64 encoded key
+     * @param iv [String] The base64 encoded IV
+     * @return [String] The decrypted plaintext in UTF-16
+     */
+    AesCBC.prototype.decrypt = function(ciphertext, key, iv) {
+        // validate args
+        if (!ciphertext || !key || !iv) {
+            throw new Error("Missing args for decryption!");
+        }
 
-		/**
-		 * Decrypt a String using AES-CBC-Pkcs7 using the provided keysize (e.g. 128, 256)
-		 * @param ciphertext [String] The base64 encoded ciphertext
-		 * @param key [String] The base64 encoded key
-		 * @param iv [String] The base64 encoded IV
-		 * @return [String] The decrypted plaintext in UTF-16
-		 */
-		this.decrypt = function(ciphertext, key, iv) {
-			// validate args
-			if (!ciphertext || !key || !iv) {
-				throw new Error("Missing args for decryption!");
-			}
+        // decode args input to utf8 decrypt
+        var cipher = this._forge.aes.createDecryptionCipher(this._forge.util.decode64(key));
+        cipher.start(this._forge.util.decode64(iv));
+        cipher.update(this._forge.util.createBuffer(this._forge.util.decode64(ciphertext)));
+        cipher.finish();
 
-			// decode args input to utf8 decrypt
-			var cipher = forge.aes.createDecryptionCipher(utl.decode64(key));
-			cipher.start(utl.decode64(iv));
-			cipher.update(utl.createBuffer(utl.decode64(ciphertext)));
-			cipher.finish();
+        // decode to utf16
+        return this._forge.util.decodeUtf8(cipher.output.getBytes());
+    };
 
-			// decode to utf16
-			return utl.decodeUtf8(cipher.output.getBytes());
-		};
+    /**
+     * Calculate a hmac using SHA-256 for a given input
+     * @param parts [Array] Array of Base64 encoded parts
+     * @param key [String] The base64 encoded key
+     * @return [String] The Base64 encoded hmac
+     */
+    AesCBC.prototype.hmac = function(parts, key) {
+        var self = this;
 
-	};
+        // validate args
+        if (!parts || !key) {
+            throw new Error("Missing args for hmac processing!");
+        }
 
-	if (typeof define !== 'undefined' && define.amd) {
-		// AMD
-		define(['forge'], function(forge) {
-			return new AesCBC(forge);
-		});
-	} else if (typeof module !== 'undefined' && module.exports) {
-		// node.js
-		module.exports = new AesCBC(require('node-forge'));
-	}
+        var hmac = self._forge.hmac.create();
+        hmac.start('sha256', self._forge.util.decode64(key));
+        parts.forEach(function(i) {
+            // decode base64 part and append to hmac msg
+            hmac.update(self._forge.util.decode64(i));
+        });
+
+        return self._forge.util.encode64(hmac.digest().getBytes());
+    };
+
+    if (typeof define !== 'undefined' && define.amd) {
+        // AMD
+        define(['forge'], function(forge) {
+            return new AesCBC(forge);
+        });
+    } else if (typeof module !== 'undefined' && module.exports) {
+        // node.js
+        module.exports = new AesCBC(require('node-forge'));
+    }
 
 })();
\ No newline at end of file
diff --git a/src/js/crypto/crypto-batch-worker.js b/src/js/crypto/crypto-batch-worker.js
index e476624..05a06cd 100644
--- a/src/js/crypto/crypto-batch-worker.js
+++ b/src/js/crypto/crypto-batch-worker.js
@@ -17,27 +17,16 @@
 
 			require(['cryptoLib/crypto-batch'], function(batch) {
 
-				var i = e.data,
-					output = null;
+				var output;
 
-				if (i.type === 'encrypt' && i.receiverPubkeys && i.senderPrivkey && i.list) {
-					// start encryption
-					output = batch.encryptListForUser(i.list, i.receiverPubkeys, i.senderPrivkey);
-
-				} else if (i.type === 'decrypt' && i.senderPubkeys && i.receiverPrivkey && i.list) {
-					// start decryption
-					output = batch.decryptListForUser(i.list, i.senderPubkeys, i.receiverPrivkey);
-
-				} else if (i.type === 'reencrypt' && i.senderPubkeys && i.receiverPrivkey && i.list && i.symKey) {
-					// start validation and re-encryption
-					output = batch.reencryptListKeysForUser(i.list, i.senderPubkeys, i.receiverPrivkey, i.symKey);
-
-				} else if (i.type === 'decryptItems' && i.symKey && i.list) {
-					// start decryption
-					output = batch.decryptKeysAndList(i.list, i.symKey);
-
-				} else {
-					throw 'Not all arguments for web worker crypto are defined!';
+				try {
+					output = doOperation(batch, e.data);
+				} catch (e) {
+					output = {
+						err: {
+							errMsg: (e.message) ? e.message : e
+						}
+					};
 				}
 
 				// pass output back to main thread
@@ -47,4 +36,34 @@
 		});
 	};
 
+	function doOperation(batch, i) {
+		var output;
+
+		if (i.type === 'encrypt' && i.receiverPubkeys && i.senderPrivkey && i.list) {
+			// start encryption
+			output = batch.encryptListForUser(i.list, i.receiverPubkeys, i.senderPrivkey);
+
+		} else if (i.type === 'decrypt' && i.senderPubkeys && i.receiverPrivkey && i.list) {
+			// start decryption
+			output = batch.decryptListForUser(i.list, i.senderPubkeys, i.receiverPrivkey);
+
+		} else if (i.type === 'reencrypt' && i.senderPubkeys && i.receiverPrivkey && i.list && i.symKey) {
+			// start validation and re-encryption
+			output = batch.reencryptListKeysForUser(i.list, i.senderPubkeys, i.receiverPrivkey, i.symKey);
+
+		} else if (i.type === 'decryptItems' && i.symKey && i.list) {
+			// start decryption
+			output = batch.decryptKeysAndList(i.list, i.symKey);
+
+		} else {
+			output = {
+				err: {
+					errMsg: 'Not all arguments for web worker crypto are defined!'
+				}
+			};
+		}
+
+		return output;
+	}
+
 }());
\ No newline at end of file
diff --git a/src/js/crypto/crypto-batch.js b/src/js/crypto/crypto-batch.js
index 76ca295..66a865a 100644
--- a/src/js/crypto/crypto-batch.js
+++ b/src/js/crypto/crypto-batch.js
@@ -28,7 +28,7 @@
 			// set sender's keypair id for later verification
 			i.senderPk = senderKeyId;
 			// sign the bundle
-			i.signature = rsa.sign([i.iv, util.str2Base64(i.id), util.str2Base64(i.senderPk), i.encryptedKey, i.ciphertext]);
+			i.signature = rsa.sign([i.iv, util.str2Base64(i.senderPk), i.encryptedKey, i.ciphertext]);
 
 			// delete plaintext values
 			delete i.plaintext;
@@ -79,7 +79,7 @@
 			rsa.init(senderPubkey);
 
 			// verify signature
-			if (!rsa.verify([i.iv, util.str2Base64(i.id), util.str2Base64(i.senderPk), i.encryptedKey, i.ciphertext], i.signature)) {
+			if (!rsa.verify([i.iv, util.str2Base64(i.senderPk), i.encryptedKey, i.ciphertext], i.signature)) {
 				throw new Error('Verifying RSA signature failed!');
 			}
 			// decrypt symmetric item key for user
diff --git a/src/js/crypto/crypto.js b/src/js/crypto/crypto.js
index f5da833..499df2c 100644
--- a/src/js/crypto/crypto.js
+++ b/src/js/crypto/crypto.js
@@ -2,305 +2,322 @@
  * High level crypto api that invokes native crypto (if available) and
  * gracefully degrades to JS crypto (if unavailable)
  */
-define(['cryptoLib/util', 'cryptoLib/aes-cbc', 'cryptoLib/rsa', 'cryptoLib/crypto-batch',
-		'js/crypto/pbkdf2', 'js/app-config'
-], function(util, aes, rsa, cryptoBatch, pbkdf2, app) {
-	'use strict';
+define(function(require) {
+    'use strict';
 
-	var self = {};
+    var util = require('cryptoLib/util'),
+        aes = require('cryptoLib/aes-cbc'),
+        rsa = require('cryptoLib/rsa'),
+        cryptoBatch = require('cryptoLib/crypto-batch'),
+        pbkdf2 = require('js/crypto/pbkdf2'),
+        app = require('js/app-config');
 
-	var passBasedKey;
+    var self = {},
+        passBasedKey;
 
-	/**
-	 * Initializes the crypto modules by fetching the user's
-	 * encrypted secret key from storage and storing it in memory.
-	 */
-	self.init = function(args, callback) {
-		// valdiate input
-		if (!args.emailAddress || !args.keySize || !args.rsaKeySize) {
-			callback({
-				errMsg: 'Crypto init failed. Not all args set!'
-			});
-			return;
-		}
+    /**
+     * Initializes the crypto modules by fetching the user's
+     * encrypted secret key from storage and storing it in memory.
+     */
+    self.init = function(args, callback) {
+        // valdiate input
+        if (!args.emailAddress || !args.keySize || !args.rsaKeySize) {
+            callback({
+                errMsg: 'Crypto init failed. Not all args set!'
+            });
+            return;
+        }
 
-		self.emailAddress = args.emailAddress;
-		self.keySize = args.keySize;
-		self.ivSize = args.keySize;
-		self.rsaKeySize = args.rsaKeySize;
+        self.emailAddress = args.emailAddress;
+        self.keySize = args.keySize;
+        self.ivSize = args.keySize;
+        self.rsaKeySize = args.rsaKeySize;
 
-		// derive PBKDF2 from password in web worker thread
-		self.deriveKey(args.password, self.keySize, function(err, derivedKey) {
-			if (err) {
-				callback(err);
-				return;
-			}
+        // derive PBKDF2 from password in web worker thread
+        self.deriveKey(args.password, self.keySize, function(err, derivedKey) {
+            if (err) {
+                callback(err);
+                return;
+            }
 
-			// remember pbkdf2 for later use
-			passBasedKey = derivedKey;
+            // remember pbkdf2 for later use
+            passBasedKey = derivedKey;
 
-			// check if key exists
-			if (!args.storedKeypair) {
-				// generate keys, encrypt and persist if none exists
-				generateKeypair(derivedKey);
-			} else {
-				// decrypt key
-				decryptKeypair(args.storedKeypair, derivedKey);
-			}
+            // check if key exists
+            if (!args.storedKeypair) {
+                // generate keys, encrypt and persist if none exists
+                generateKeypair(derivedKey);
+            } else {
+                // decrypt key
+                decryptKeypair(args.storedKeypair, derivedKey);
+            }
 
-		});
+        });
 
-		function generateKeypair(derivedKey) {
-			// generate RSA keypair in web worker
-			rsa.generateKeypair(self.rsaKeySize, function(err, generatedKeypair) {
-				if (err) {
-					callback(err);
-					return;
-				}
+        function generateKeypair(derivedKey) {
+            // generate RSA keypair in web worker
+            rsa.generateKeypair(self.rsaKeySize, function(err, generatedKeypair) {
+                if (err) {
+                    callback(err);
+                    return;
+                }
 
-				// encrypt keypair
-				var iv = util.random(self.ivSize);
-				var encryptedPrivateKey = aes.encrypt(generatedKeypair.privkeyPem, derivedKey, iv);
+                // encrypt keypair
+                var iv = util.random(self.ivSize);
+                var encryptedPrivateKey = aes.encrypt(generatedKeypair.privkeyPem, derivedKey, iv);
 
-				// new encrypted keypair object
-				var newKeypair = {
-					publicKey: {
-						_id: generatedKeypair._id,
-						userId: self.emailAddress,
-						publicKey: generatedKeypair.pubkeyPem
-					},
-					privateKey: {
-						_id: generatedKeypair._id,
-						userId: self.emailAddress,
-						encryptedKey: encryptedPrivateKey,
-						iv: iv
-					}
-				};
+                // new encrypted keypair object
+                var newKeypair = {
+                    publicKey: {
+                        _id: generatedKeypair._id,
+                        userId: self.emailAddress,
+                        publicKey: generatedKeypair.pubkeyPem
+                    },
+                    privateKey: {
+                        _id: generatedKeypair._id,
+                        userId: self.emailAddress,
+                        encryptedKey: encryptedPrivateKey,
+                        iv: iv
+                    }
+                };
 
-				// return generated keypair for storage in keychain dao
-				callback(null, newKeypair);
-			});
-		}
+                // return generated keypair for storage in keychain dao
+                callback(null, newKeypair);
+            });
+        }
 
-		function decryptKeypair(storedKeypair, derivedKey) {
-			var decryptedPrivateKey;
+        function decryptKeypair(storedKeypair, derivedKey) {
+            var decryptedPrivateKey;
 
-			// validate input
-			if (!storedKeypair || !storedKeypair.privateKey || !storedKeypair.privateKey.encryptedKey || !storedKeypair.privateKey.iv) {
-				callback({
-					errMsg: 'Incomplete arguments for private key decryption!'
-				});
-				return;
-			}
+            // validate input
+            if (!storedKeypair || !storedKeypair.privateKey || !storedKeypair.privateKey.encryptedKey || !storedKeypair.privateKey.iv) {
+                callback({
+                    errMsg: 'Incomplete arguments for private key decryption!'
+                });
+                return;
+            }
 
-			// try to decrypt with derivedKey
-			try {
-				var prK = storedKeypair.privateKey;
-				decryptedPrivateKey = aes.decrypt(prK.encryptedKey, derivedKey, prK.iv);
-			} catch (ex) {
-				callback({
-					errMsg: 'Wrong password!'
-				});
-				return;
-			}
-			// set rsa keys
-			rsa.init(storedKeypair.publicKey.publicKey, decryptedPrivateKey, storedKeypair.publicKey._id);
+            // try to decrypt with derivedKey
+            try {
+                var prK = storedKeypair.privateKey;
+                decryptedPrivateKey = aes.decrypt(prK.encryptedKey, derivedKey, prK.iv);
+            } catch (ex) {
+                callback({
+                    errMsg: 'Wrong password!'
+                });
+                return;
+            }
+            // set rsa keys
+            rsa.init(storedKeypair.publicKey.publicKey, decryptedPrivateKey, storedKeypair.publicKey._id);
 
-			callback();
-		}
-	};
+            callback();
+        }
+    };
 
-	/**
-	 * Do PBKDF2 key derivation in a WebWorker thread
-	 */
-	self.deriveKey = function(password, keySize, callback) {
-		startWorker('/crypto/pbkdf2-worker.js', {
-			password: password,
-			keySize: keySize
-		}, callback, function() {
-			return pbkdf2.getKey(password, keySize);
-		});
-	};
+    /**
+     * Do PBKDF2 key derivation in a WebWorker thread
+     */
+    self.deriveKey = function(password, keySize, callback) {
+        startWorker('/crypto/pbkdf2-worker.js', {
+            password: password,
+            keySize: keySize
+        }, callback, function() {
+            return pbkdf2.getKey(password, keySize);
+        });
+    };
 
-	//
-	// En/Decrypts single item
-	//
+    //
+    // En/Decrypts single item
+    //
 
-	self.aesEncrypt = function(plaintext, key, iv, callback) {
-		startWorker('/crypto/aes-worker.js', {
-			type: 'encrypt',
-			plaintext: plaintext,
-			key: key,
-			iv: iv
-		}, callback, function() {
-			return self.aesEncryptSync(plaintext, key, iv);
-		});
-	};
+    self.aesEncrypt = function(plaintext, key, iv, callback) {
+        startWorker('/crypto/aes-worker.js', {
+            type: 'encrypt',
+            plaintext: plaintext,
+            key: key,
+            iv: iv
+        }, callback, function() {
+            return self.aesEncryptSync(plaintext, key, iv);
+        });
+    };
 
-	self.aesDecrypt = function(ciphertext, key, iv, callback) {
-		startWorker('/crypto/aes-worker.js', {
-			type: 'decrypt',
-			ciphertext: ciphertext,
-			key: key,
-			iv: iv
-		}, callback, function() {
-			return self.aesDecryptSync(ciphertext, key, iv);
-		});
-	};
+    self.aesDecrypt = function(ciphertext, key, iv, callback) {
+        startWorker('/crypto/aes-worker.js', {
+            type: 'decrypt',
+            ciphertext: ciphertext,
+            key: key,
+            iv: iv
+        }, callback, function() {
+            return self.aesDecryptSync(ciphertext, key, iv);
+        });
+    };
 
-	self.aesEncryptSync = function(plaintext, key, iv) {
-		return aes.encrypt(plaintext, key, iv);
-	};
+    self.aesEncryptSync = function(plaintext, key, iv) {
+        return aes.encrypt(plaintext, key, iv);
+    };
 
-	self.aesDecryptSync = function(ciphertext, key, iv) {
-		return aes.decrypt(ciphertext, key, iv);
-	};
+    self.aesDecryptSync = function(ciphertext, key, iv) {
+        return aes.decrypt(ciphertext, key, iv);
+    };
 
-	//
-	// En/Decrypt a list of items with AES in a WebWorker thread
-	//
+    //
+    // En/Decrypt a list of items with AES in a WebWorker thread
+    //
 
-	self.aesEncryptList = function(list, callback) {
-		startWorker('/crypto/aes-batch-worker.js', {
-			type: 'encrypt',
-			list: list
-		}, callback, function() {
-			return cryptoBatch.encryptList(list);
-		});
-	};
+    self.aesEncryptList = function(list, callback) {
+        startWorker('/crypto/aes-batch-worker.js', {
+            type: 'encrypt',
+            list: list
+        }, callback, function() {
+            return cryptoBatch.encryptList(list);
+        });
+    };
 
-	self.aesDecryptList = function(list, callback) {
-		startWorker('/crypto/aes-batch-worker.js', {
-			type: 'decrypt',
-			list: list
-		}, callback, function() {
-			return cryptoBatch.decryptList(list);
-		});
-	};
+    self.aesDecryptList = function(list, callback) {
+        startWorker('/crypto/aes-batch-worker.js', {
+            type: 'decrypt',
+            list: list
+        }, callback, function() {
+            return cryptoBatch.decryptList(list);
+        });
+    };
 
-	//
-	// En/Decrypt something speficially using the user's secret key
-	//
+    //
+    // En/Decrypt something speficially using the user's secret key
+    //
 
-	self.encryptListForUser = function(list, receiverPubkeys, callback) {
-		var envelope, envelopes = [];
+    self.encryptListForUser = function(list, receiverPubkeys, callback) {
+        var envelope, envelopes = [];
 
-		if (!receiverPubkeys || receiverPubkeys.length !== 1) {
-			callback({
-				errMsg: 'Encryption is currently implemented for only one receiver!'
-			});
-			return;
-		}
+        if (!receiverPubkeys || receiverPubkeys.length !== 1) {
+            callback({
+                errMsg: 'Encryption is currently implemented for only one receiver!'
+            });
+            return;
+        }
 
-		var keypair = rsa.exportKeys();
-		var senderPrivkey = {
-			_id: keypair._id,
-			privateKey: keypair.privkeyPem
-		};
+        var keypair = rsa.exportKeys();
+        var senderPrivkey = {
+            _id: keypair._id,
+            privateKey: keypair.privkeyPem
+        };
 
-		// package objects into batchable envelope format
-		list.forEach(function(i) {
-			envelope = {
-				id: i.id,
-				plaintext: i,
-				key: util.random(self.keySize),
-				iv: util.random(self.ivSize),
-				receiverPk: receiverPubkeys[0]._id
-			};
-			envelopes.push(envelope);
-		});
+        // package objects into batchable envelope format
+        list.forEach(function(i) {
+            envelope = {
+                id: i.id,
+                plaintext: i,
+                key: util.random(self.keySize),
+                iv: util.random(self.ivSize),
+                receiverPk: receiverPubkeys[0]._id
+            };
+            envelopes.push(envelope);
+        });
 
-		startWorker('/crypto/crypto-batch-worker.js', {
-			type: 'encrypt',
-			list: envelopes,
-			senderPrivkey: senderPrivkey,
-			receiverPubkeys: receiverPubkeys
-		}, callback, function() {
-			return cryptoBatch.encryptListForUser(envelopes, receiverPubkeys, senderPrivkey);
-		});
-	};
+        startWorker('/crypto/crypto-batch-worker.js', {
+            type: 'encrypt',
+            list: envelopes,
+            senderPrivkey: senderPrivkey,
+            receiverPubkeys: receiverPubkeys
+        }, callback, function() {
+            return cryptoBatch.encryptListForUser(envelopes, receiverPubkeys, senderPrivkey);
+        });
+    };
 
-	self.decryptListForUser = function(list, senderPubkeys, callback) {
-		if (!senderPubkeys || senderPubkeys < 1) {
-			callback({
-				errMsg: 'Sender public keys must be set!'
-			});
-			return;
-		}
+    self.decryptListForUser = function(list, senderPubkeys, callback) {
+        if (!senderPubkeys || senderPubkeys < 1) {
+            callback({
+                errMsg: 'Sender public keys must be set!'
+            });
+            return;
+        }
 
-		var keypair = rsa.exportKeys();
-		var receiverPrivkey = {
-			_id: keypair._id,
-			privateKey: keypair.privkeyPem
-		};
+        var keypair = rsa.exportKeys();
+        var receiverPrivkey = {
+            _id: keypair._id,
+            privateKey: keypair.privkeyPem
+        };
 
-		startWorker('/crypto/crypto-batch-worker.js', {
-			type: 'decrypt',
-			list: list,
-			receiverPrivkey: receiverPrivkey,
-			senderPubkeys: senderPubkeys
-		}, callback, function() {
-			return cryptoBatch.decryptListForUser(list, senderPubkeys, receiverPrivkey);
-		});
-	};
+        startWorker('/crypto/crypto-batch-worker.js', {
+            type: 'decrypt',
+            list: list,
+            receiverPrivkey: receiverPrivkey,
+            senderPubkeys: senderPubkeys
+        }, callback, function() {
+            return cryptoBatch.decryptListForUser(list, senderPubkeys, receiverPrivkey);
+        });
+    };
 
-	//
-	// Re-encrypt keys item and items seperately
-	//
+    //
+    // Re-encrypt keys item and items seperately
+    //
 
-	self.reencryptListKeysForUser = function(list, senderPubkeys, callback) {
-		var keypair = rsa.exportKeys();
-		var receiverPrivkey = {
-			_id: keypair._id,
-			privateKey: keypair.privkeyPem
-		};
+    self.reencryptListKeysForUser = function(list, senderPubkeys, callback) {
+        var keypair = rsa.exportKeys();
+        var receiverPrivkey = {
+            _id: keypair._id,
+            privateKey: keypair.privkeyPem
+        };
 
-		startWorker('/crypto/crypto-batch-worker.js', {
-			type: 'reencrypt',
-			list: list,
-			receiverPrivkey: receiverPrivkey,
-			senderPubkeys: senderPubkeys,
-			symKey: passBasedKey
-		}, callback, function() {
-			return cryptoBatch.reencryptListKeysForUser(list, senderPubkeys, receiverPrivkey, passBasedKey);
-		});
-	};
+        startWorker('/crypto/crypto-batch-worker.js', {
+            type: 'reencrypt',
+            list: list,
+            receiverPrivkey: receiverPrivkey,
+            senderPubkeys: senderPubkeys,
+            symKey: passBasedKey
+        }, callback, function() {
+            return cryptoBatch.reencryptListKeysForUser(list, senderPubkeys, receiverPrivkey, passBasedKey);
+        });
+    };
 
-	self.decryptKeysAndList = function(list, callback) {
-		startWorker('/crypto/crypto-batch-worker.js', {
-			type: 'decryptItems',
-			list: list,
-			symKey: passBasedKey
-		}, callback, function() {
-			return cryptoBatch.decryptKeysAndList(list, passBasedKey);
-		});
-	};
+    self.decryptKeysAndList = function(list, callback) {
+        startWorker('/crypto/crypto-batch-worker.js', {
+            type: 'decryptItems',
+            list: list,
+            symKey: passBasedKey
+        }, callback, function() {
+            return cryptoBatch.decryptKeysAndList(list, passBasedKey);
+        });
+    };
 
-	//
-	// helper functions
-	//
+    //
+    // helper functions
+    //
 
-	function startWorker(script, args, callback, noWorker) {
-		// check for WebWorker support
-		if (window.Worker) {
+    function startWorker(script, args, callback, noWorker) {
+        // check for WebWorker support
+        if (window.Worker) {
 
-			// init webworker thread
-			var worker = new Worker(app.config.workerPath + script);
+            // 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);
-			};
+            worker.onmessage = function(e) {
+                if (e.data.err) {
+                    callback(e.data.err);
+                    return;
+                }
+                // return result from the worker
+                callback(null, e.data);
+            };
 
-			// send data to the worker
-			worker.postMessage(args);
+            // send data to the worker
+            worker.postMessage(args);
 
-		} else {
-			// no WebWorker support... do synchronous call
-			var result = noWorker();
-			callback(null, result);
-		}
-	}
+        } else {
+            // no WebWorker support... do synchronous call
+            var result;
+            try {
+                result = noWorker();
+            } catch (e) {
+                callback({
+                    errMsg: (e.message) ? e.message : e
+                });
+                return;
+            }
 
-	return self;
+            callback(null, result);
+        }
+    }
+
+    return self;
 });
\ No newline at end of file
diff --git a/src/js/dao/email-dao.js b/src/js/dao/email-dao.js
index b03beaf..579c1a7 100644
--- a/src/js/dao/email-dao.js
+++ b/src/js/dao/email-dao.js
@@ -298,7 +298,7 @@ define(function(require) {
 
         /* message was not found in cache... fetch from imap server */
 
-        function messageReady(err, gottenMessage) {
+        function bodyReady(err, gottenMessage) {
             message = gottenMessage;
             itemCounter++;
             // remember how many items should be fetched before the callback fires
@@ -311,10 +311,10 @@ define(function(require) {
 
             // decrypt Message body
             if (message.body.indexOf(PREFIX) !== -1 && message.body.indexOf(SUFFIX) !== -1) {
-                decryptMessageBody(message, function(err, ptMessage) {
+                decryptBody(message, function(err, ptMessage) {
                     message = ptMessage;
                     // return decrypted message
-                    callback(null, message);
+                    callback(err, message);
                 });
                 return;
             }
@@ -325,7 +325,7 @@ define(function(require) {
             //check();
         }
 
-        function decryptMessageBody(email, callback) {
+        function decryptBody(email, callback) {
             var ctMessageBase64, ctMessage, pubkeyIds;
 
             // parse email body for encrypted message block
@@ -390,7 +390,8 @@ define(function(require) {
         self._imapClient.getMessage({
             path: options.folder,
             uid: options.uid,
-            onMessage: messageReady,
+            onBody: bodyReady,
+            onEnd: bodyReady
             /*onAttachment: attachmentReady*/
         });
     };
diff --git a/test/new-unit/email-dao-test.js b/test/new-unit/email-dao-test.js
index 77199c8..1f2d944 100644
--- a/test/new-unit/email-dao-test.js
+++ b/test/new-unit/email-dao-test.js
@@ -272,7 +272,7 @@ define(function(require) {
                 it('should parse message body without attachement', function(done) {
                     var uid = 415;
 
-                    imapClientStub.getMessage.yieldsTo('onMessage', null, {
+                    imapClientStub.getMessage.yieldsTo('onBody', null, {
                         uid: uid,
                         body: ''
                     });