diff --git a/Gruntfile.js b/Gruntfile.js index 1a463742..0aa4f52e 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -92,6 +92,7 @@ module.exports = function(grunt) { 'libtextsecure/axolotl_wrapper.js', 'libtextsecure/libaxolotl_concat.js', + 'libtextsecure/crypto.js', 'libtextsecure/storage.js', 'libtextsecure/storage/devices.js', 'libtextsecure/protobufs.js', diff --git a/js/background.js b/js/background.js index a9534c01..edee0468 100644 --- a/js/background.js +++ b/js/background.js @@ -37,7 +37,7 @@ new WebSocketResource(socket, function(request) { // TODO: handle different types of requests. for now we only expect // PUT /messages - textsecure.protocol.decryptWebsocketMessage(request.body).then(function(plaintext) { + textsecure.crypto.decryptWebsocketMessage(request.body).then(function(plaintext) { var proto = textsecure.protobuf.IncomingPushMessageSignal.decode(plaintext); // After this point, decoding errors are not the server's // fault, and we should handle them gracefully and tell the diff --git a/libaxolotl/crypto.js b/libaxolotl/crypto.js index 38695eb4..e73db018 100644 --- a/libaxolotl/crypto.js +++ b/libaxolotl/crypto.js @@ -17,7 +17,7 @@ window.axolotl = window.axolotl || {}; /* - * textsecure.crypto + * axolotl.crypto * glues together various implementations into a single interface * for all low-level crypto operations, */ diff --git a/libaxolotl/protocol.js b/libaxolotl/protocol.js index a0fe7fe9..69491d9b 100644 --- a/libaxolotl/protocol.js +++ b/libaxolotl/protocol.js @@ -527,58 +527,6 @@ window.textsecure.protocol = function() { }); } - // Decrypts message into a raw string - self.decryptWebsocketMessage = function(message) { - var signaling_key = textsecure.storage.getEncrypted("signaling_key"); //TODO: in crypto_storage - var aes_key = toArrayBuffer(signaling_key.substring(0, 32)); - var mac_key = toArrayBuffer(signaling_key.substring(32, 32 + 20)); - - var decodedMessage = message.toArrayBuffer(); - if (new Uint8Array(decodedMessage)[0] != 1) - throw new Error("Got bad version number: " + decodedMessage[0]); - - var iv = decodedMessage.slice(1, 1 + 16); - var ciphertext = decodedMessage.slice(1 + 16, decodedMessage.byteLength - 10); - var ivAndCiphertext = decodedMessage.slice(0, decodedMessage.byteLength - 10); - var mac = decodedMessage.slice(decodedMessage.byteLength - 10, decodedMessage.byteLength); - - return verifyMAC(ivAndCiphertext, mac_key, mac).then(function() { - return window.axolotl.crypto.decrypt(aes_key, ciphertext, iv); - }); - }; - - self.decryptAttachment = function(encryptedBin, keys) { - var aes_key = keys.slice(0, 32); - var mac_key = keys.slice(32, 64); - - var iv = encryptedBin.slice(0, 16); - var ciphertext = encryptedBin.slice(16, encryptedBin.byteLength - 32); - var ivAndCiphertext = encryptedBin.slice(0, encryptedBin.byteLength - 32); - var mac = encryptedBin.slice(encryptedBin.byteLength - 32, encryptedBin.byteLength); - - return verifyMAC(ivAndCiphertext, mac_key, mac).then(function() { - return window.axolotl.crypto.decrypt(aes_key, ciphertext, iv); - }); - }; - - self.encryptAttachment = function(plaintext, keys, iv) { - var aes_key = keys.slice(0, 32); - var mac_key = keys.slice(32, 64); - - return window.axolotl.crypto.encrypt(aes_key, plaintext, iv).then(function(ciphertext) { - var ivAndCiphertext = new Uint8Array(16 + ciphertext.byteLength); - ivAndCiphertext.set(new Uint8Array(iv)); - ivAndCiphertext.set(new Uint8Array(ciphertext), 16); - - return axolotl.crypto.sign(mac_key, ivAndCiphertext.buffer).then(function(mac) { - var encryptedBin = new Uint8Array(16 + ciphertext.byteLength + 32); - encryptedBin.set(ivAndCiphertext); - encryptedBin.set(new Uint8Array(mac), 16 + ciphertext.byteLength); - return encryptedBin.buffer; - }); - }); - }; - // return Promise(encoded [PreKey]WhisperMessage) self.encryptMessageFor = function(deviceObject, pushMessageContent) { var session = crypto_storage.getOpenSession(deviceObject.encodedNumber); diff --git a/libtextsecure/crypto.js b/libtextsecure/crypto.js new file mode 100644 index 00000000..e735fcb1 --- /dev/null +++ b/libtextsecure/crypto.js @@ -0,0 +1,83 @@ +/* vim: ts=4:sw=4:expandtab + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +;(function(){ + 'use strict'; + + // Various wrappers around low-level crypto operation for specific functions + + var verifyMAC = function(data, key, mac) { + return axolotl.crypto.sign(key, data).then(function(calculated_mac) { + if (!isEqual(calculated_mac, mac, true)) + throw new Error("Bad MAC"); + }); + } + + window.textsecure = window.textsecure || {}; + window.textsecure.crypto = { + // Decrypts message into a raw string + decryptWebsocketMessage: function(message) { + var signaling_key = axolotl.api.storage.get("signaling_key"); //TODO: in crypto_storage + var aes_key = toArrayBuffer(signaling_key.substring(0, 32)); + var mac_key = toArrayBuffer(signaling_key.substring(32, 32 + 20)); + + var decodedMessage = message.toArrayBuffer(); + if (new Uint8Array(decodedMessage)[0] != 1) + throw new Error("Got bad version number: " + decodedMessage[0]); + + var iv = decodedMessage.slice(1, 1 + 16); + var ciphertext = decodedMessage.slice(1 + 16, decodedMessage.byteLength - 10); + var ivAndCiphertext = decodedMessage.slice(0, decodedMessage.byteLength - 10); + var mac = decodedMessage.slice(decodedMessage.byteLength - 10, decodedMessage.byteLength); + + return verifyMAC(ivAndCiphertext, mac_key, mac).then(function() { + return window.axolotl.crypto.decrypt(aes_key, ciphertext, iv); + }); + }, + + decryptAttachment: function(encryptedBin, keys) { + var aes_key = keys.slice(0, 32); + var mac_key = keys.slice(32, 64); + + var iv = encryptedBin.slice(0, 16); + var ciphertext = encryptedBin.slice(16, encryptedBin.byteLength - 32); + var ivAndCiphertext = encryptedBin.slice(0, encryptedBin.byteLength - 32); + var mac = encryptedBin.slice(encryptedBin.byteLength - 32, encryptedBin.byteLength); + + return verifyMAC(ivAndCiphertext, mac_key, mac).then(function() { + return window.axolotl.crypto.decrypt(aes_key, ciphertext, iv); + }); + }, + + encryptAttachment: function(plaintext, keys, iv) { + var aes_key = keys.slice(0, 32); + var mac_key = keys.slice(32, 64); + + return window.axolotl.crypto.encrypt(aes_key, plaintext, iv).then(function(ciphertext) { + var ivAndCiphertext = new Uint8Array(16 + ciphertext.byteLength); + ivAndCiphertext.set(new Uint8Array(iv)); + ivAndCiphertext.set(new Uint8Array(ciphertext), 16); + + return axolotl.crypto.sign(mac_key, ivAndCiphertext.buffer).then(function(mac) { + var encryptedBin = new Uint8Array(16 + ciphertext.byteLength + 32); + encryptedBin.set(ivAndCiphertext); + encryptedBin.set(new Uint8Array(mac), 16 + ciphertext.byteLength); + return encryptedBin.buffer; + }); + }); + } + }; +})(); diff --git a/libtextsecure/helpers.js b/libtextsecure/helpers.js index 4f0ac21e..196daa9e 100644 --- a/libtextsecure/helpers.js +++ b/libtextsecure/helpers.js @@ -137,7 +137,7 @@ var handleAttachment = function(attachment) { } function decryptAttachment(encrypted) { - return textsecure.protocol.decryptAttachment( + return textsecure.crypto.decryptAttachment( encrypted, attachment.key.toArrayBuffer() ); diff --git a/libtextsecure/sendmessage.js b/libtextsecure/sendmessage.js index f5277037..df7dccab 100644 --- a/libtextsecure/sendmessage.js +++ b/libtextsecure/sendmessage.js @@ -234,7 +234,7 @@ window.textsecure.messaging = function() { proto.key = axolotl.crypto.getRandomBytes(64); var iv = axolotl.crypto.getRandomBytes(16); - return textsecure.protocol.encryptAttachment(attachment.data, proto.key, iv).then(function(encryptedBin) { + return textsecure.crypto.encryptAttachment(attachment.data, proto.key, iv).then(function(encryptedBin) { return textsecure.api.putAttachment(encryptedBin).then(function(id) { proto.id = id; proto.contentType = attachment.contentType;