Some initial helpers.js namespaceing
This commit is contained in:
parent
07a23f0759
commit
05101b69b0
5 changed files with 154 additions and 137 deletions
64
js/crypto.js
64
js/crypto.js
|
@ -14,14 +14,19 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// functions exposed for replacement and direct calling in test code
|
var textsecure = textsecure || {};
|
||||||
var crypto_tests = {};
|
|
||||||
|
|
||||||
window.crypto = (function() {
|
textsecure.crypto = new function() {
|
||||||
|
// functions exposed for replacement and direct calling in test code
|
||||||
|
var testing_only = {};
|
||||||
|
|
||||||
|
/******************************
|
||||||
|
*** Random constants/utils ***
|
||||||
|
******************************/
|
||||||
// We consider messages lost after a week and might throw away keys at that point
|
// We consider messages lost after a week and might throw away keys at that point
|
||||||
var MESSAGE_LOST_THRESHOLD_MS = 1000*60*60*24*7;
|
var MESSAGE_LOST_THRESHOLD_MS = 1000*60*60*24*7;
|
||||||
|
|
||||||
crypto.getRandomBytes = function(size) {
|
var getRandomBytes = function(size) {
|
||||||
//TODO: Better random (https://www.grc.com/r&d/js.htm?)
|
//TODO: Better random (https://www.grc.com/r&d/js.htm?)
|
||||||
try {
|
try {
|
||||||
var buffer = new ArrayBuffer(size);
|
var buffer = new ArrayBuffer(size);
|
||||||
|
@ -33,13 +38,23 @@ window.crypto = (function() {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.getRandomBytes = getRandomBytes;
|
||||||
|
|
||||||
|
function intToArrayBuffer(nInt) {
|
||||||
|
var res = new ArrayBuffer(16);
|
||||||
|
var thing = new Uint8Array(res);
|
||||||
|
thing[0] = (nInt >> 24) & 0xff;
|
||||||
|
thing[1] = (nInt >> 16) & 0xff;
|
||||||
|
thing[2] = (nInt >> 8 ) & 0xff;
|
||||||
|
thing[3] = (nInt >> 0 ) & 0xff;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
function HmacSHA256(key, input) {
|
function HmacSHA256(key, input) {
|
||||||
return window.crypto.subtle.sign({name: "HMAC", hash: "SHA-256"}, key, input);
|
return window.crypto.subtle.sign({name: "HMAC", hash: "SHA-256"}, key, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testing_only.privToPub = function(privKey, isIdentity) {
|
||||||
crypto_tests.privToPub = function(privKey, isIdentity) {
|
|
||||||
if (privKey.byteLength != 32)
|
if (privKey.byteLength != 32)
|
||||||
throw new Error("Invalid private key");
|
throw new Error("Invalid private key");
|
||||||
|
|
||||||
|
@ -75,13 +90,16 @@ window.crypto = (function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
var privToPub = function(privKey, isIdentity) { return crypto_tests.privToPub(privKey, isIdentity); }
|
var privToPub = function(privKey, isIdentity) { return testing_only.privToPub(privKey, isIdentity); }
|
||||||
|
|
||||||
crypto_tests.createNewKeyPair = function(isIdentity) {
|
testing_only.createNewKeyPair = function(isIdentity) {
|
||||||
return privToPub(crypto.getRandomBytes(32), isIdentity);
|
return privToPub(getRandomBytes(32), isIdentity);
|
||||||
}
|
}
|
||||||
var createNewKeyPair = function(isIdentity) { return crypto_tests.createNewKeyPair(isIdentity); }
|
var createNewKeyPair = function(isIdentity) { return testing_only.createNewKeyPair(isIdentity); }
|
||||||
|
|
||||||
|
/***************************
|
||||||
|
*** Key/session storage ***
|
||||||
|
***************************/
|
||||||
var crypto_storage = {};
|
var crypto_storage = {};
|
||||||
|
|
||||||
crypto_storage.getNewPubKeySTORINGPrivKey = function(keyName, isIdentity) {
|
crypto_storage.getNewPubKeySTORINGPrivKey = function(keyName, isIdentity) {
|
||||||
|
@ -208,9 +226,7 @@ window.crypto = (function() {
|
||||||
/*****************************
|
/*****************************
|
||||||
*** Internal Crypto stuff ***
|
*** Internal Crypto stuff ***
|
||||||
*****************************/
|
*****************************/
|
||||||
//TODO: Think about replacing CryptoJS stuff with optional NaCL-based implementations
|
testing_only.ECDHE = function(pubKey, privKey) {
|
||||||
// Probably means all of the low-level crypto stuff here needs pulled out into its own file
|
|
||||||
crypto_tests.ECDHE = function(pubKey, privKey) {
|
|
||||||
if (privKey === undefined || privKey.byteLength != 32)
|
if (privKey === undefined || privKey.byteLength != 32)
|
||||||
throw new Error("Invalid private key");
|
throw new Error("Invalid private key");
|
||||||
|
|
||||||
|
@ -231,9 +247,9 @@ window.crypto = (function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
var ECDHE = function(pubKey, privKey) { return crypto_tests.ECDHE(pubKey, privKey); }
|
var ECDHE = function(pubKey, privKey) { return testing_only.ECDHE(pubKey, privKey); }
|
||||||
|
|
||||||
crypto_tests.HKDF = function(input, salt, info) {
|
testing_only.HKDF = function(input, salt, info) {
|
||||||
// Specific implementation of RFC 5869 that only returns exactly 64 bytes
|
// Specific implementation of RFC 5869 that only returns exactly 64 bytes
|
||||||
return HmacSHA256(salt, input).then(function(PRK) {
|
return HmacSHA256(salt, input).then(function(PRK) {
|
||||||
var infoBuffer = new ArrayBuffer(info.byteLength + 1 + 32);
|
var infoBuffer = new ArrayBuffer(info.byteLength + 1 + 32);
|
||||||
|
@ -260,7 +276,7 @@ window.crypto = (function() {
|
||||||
|
|
||||||
info = toArrayBuffer(info); // TODO: maybe convert calls?
|
info = toArrayBuffer(info); // TODO: maybe convert calls?
|
||||||
|
|
||||||
return crypto_tests.HKDF(input, salt, info);
|
return testing_only.HKDF(input, salt, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
var calculateMACWithVersionByte = function(data, key, version) {
|
var calculateMACWithVersionByte = function(data, key, version) {
|
||||||
|
@ -363,7 +379,7 @@ window.crypto = (function() {
|
||||||
// Lock down current receive ratchet
|
// Lock down current receive ratchet
|
||||||
// TODO: Some kind of delete chainKey['key']
|
// TODO: Some kind of delete chainKey['key']
|
||||||
// Delete current sending ratchet
|
// Delete current sending ratchet
|
||||||
delete session[getString(ratchet.ephemeralKeyPair.pubKey)];
|
delete session[getString(session.currentRatchet.ephemeralKeyPair.pubKey)];
|
||||||
// Delete current root key and our ephemeral key pair
|
// Delete current root key and our ephemeral key pair
|
||||||
delete session.currentRatchet['rootKey'];
|
delete session.currentRatchet['rootKey'];
|
||||||
delete session.currentRatchet['ephemeralKeyPair'];
|
delete session.currentRatchet['ephemeralKeyPair'];
|
||||||
|
@ -540,7 +556,7 @@ window.crypto = (function() {
|
||||||
*** Public crypto API ***
|
*** Public crypto API ***
|
||||||
*************************/
|
*************************/
|
||||||
// Decrypts message into a raw string
|
// Decrypts message into a raw string
|
||||||
crypto.decryptWebsocketMessage = function(message) {
|
this.decryptWebsocketMessage = function(message) {
|
||||||
var signaling_key = storage.getEncrypted("signaling_key"); //TODO: in crypto_storage
|
var signaling_key = storage.getEncrypted("signaling_key"); //TODO: in crypto_storage
|
||||||
var aes_key = toArrayBuffer(signaling_key.substring(0, 32));
|
var aes_key = toArrayBuffer(signaling_key.substring(0, 32));
|
||||||
var mac_key = toArrayBuffer(signaling_key.substring(32, 32 + 20));
|
var mac_key = toArrayBuffer(signaling_key.substring(32, 32 + 20));
|
||||||
|
@ -559,7 +575,7 @@ window.crypto = (function() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
crypto.decryptAttachment = function(encryptedBin, keys) {
|
this.decryptAttachment = function(encryptedBin, keys) {
|
||||||
var aes_key = keys.slice(0, 32);
|
var aes_key = keys.slice(0, 32);
|
||||||
var mac_key = keys.slice(32, 64);
|
var mac_key = keys.slice(32, 64);
|
||||||
|
|
||||||
|
@ -573,7 +589,7 @@ window.crypto = (function() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
crypto.handleIncomingPushMessageProto = function(proto) {
|
this.handleIncomingPushMessageProto = function(proto) {
|
||||||
switch(proto.type) {
|
switch(proto.type) {
|
||||||
case 0: //TYPE_MESSAGE_PLAINTEXT
|
case 0: //TYPE_MESSAGE_PLAINTEXT
|
||||||
return Promise.resolve({message: decodePushMessageContentProtobuf(getString(proto.message)), pushMessage:proto});
|
return Promise.resolve({message: decodePushMessageContentProtobuf(getString(proto.message)), pushMessage:proto});
|
||||||
|
@ -596,7 +612,7 @@ window.crypto = (function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// return Promise(encoded [PreKey]WhisperMessage)
|
// return Promise(encoded [PreKey]WhisperMessage)
|
||||||
crypto.encryptMessageFor = function(deviceObject, pushMessageContent) {
|
this.encryptMessageFor = function(deviceObject, pushMessageContent) {
|
||||||
var session = crypto_storage.getOpenSession(deviceObject.encodedNumber);
|
var session = crypto_storage.getOpenSession(deviceObject.encodedNumber);
|
||||||
|
|
||||||
var doEncryptPushMessageContent = function() {
|
var doEncryptPushMessageContent = function() {
|
||||||
|
@ -663,7 +679,7 @@ window.crypto = (function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
var GENERATE_KEYS_KEYS_GENERATED = 100;
|
var GENERATE_KEYS_KEYS_GENERATED = 100;
|
||||||
crypto.generateKeys = function() {
|
this.generateKeys = function() {
|
||||||
var identityKey = crypto_storage.getStoredPubKey("identityKey");
|
var identityKey = crypto_storage.getStoredPubKey("identityKey");
|
||||||
var identityKeyCalculated = function(pubKey) {
|
var identityKeyCalculated = function(pubKey) {
|
||||||
identityKey = pubKey;
|
identityKey = pubKey;
|
||||||
|
@ -704,4 +720,6 @@ window.crypto = (function() {
|
||||||
else
|
else
|
||||||
return identityKeyCalculated(identityKey);
|
return identityKeyCalculated(identityKey);
|
||||||
}
|
}
|
||||||
})();
|
|
||||||
|
this.testing_only = testing_only;
|
||||||
|
}();
|
||||||
|
|
181
js/helpers.js
181
js/helpers.js
|
@ -95,16 +95,6 @@ function base64EncArr (aBytes) {
|
||||||
/*********************************
|
/*********************************
|
||||||
*** Type conversion utilities ***
|
*** Type conversion utilities ***
|
||||||
*********************************/
|
*********************************/
|
||||||
function intToArrayBuffer(nInt) {
|
|
||||||
var res = new ArrayBuffer(16);
|
|
||||||
var thing = new Uint8Array(res);
|
|
||||||
thing[0] = (nInt >> 24) & 0xff;
|
|
||||||
thing[1] = (nInt >> 16) & 0xff;
|
|
||||||
thing[2] = (nInt >> 8 ) & 0xff;
|
|
||||||
thing[3] = (nInt >> 0 ) & 0xff;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Strings/arrays
|
// Strings/arrays
|
||||||
//TODO: Throw all this shit in favor of consistent types
|
//TODO: Throw all this shit in favor of consistent types
|
||||||
var StaticByteBufferProto = new dcodeIO.ByteBuffer().__proto__;
|
var StaticByteBufferProto = new dcodeIO.ByteBuffer().__proto__;
|
||||||
|
@ -261,43 +251,95 @@ function objectContainsKeys(object) {
|
||||||
/************************************************
|
/************************************************
|
||||||
*** Utilities to store data in local storage ***
|
*** Utilities to store data in local storage ***
|
||||||
************************************************/
|
************************************************/
|
||||||
var storage = {};
|
var storage = new function() {
|
||||||
|
/*****************************
|
||||||
|
*** Base Storage Routines ***
|
||||||
|
*****************************/
|
||||||
|
this.putEncrypted = function(key, value) {
|
||||||
|
//TODO
|
||||||
|
if (value === undefined)
|
||||||
|
throw new Error("Tried to store undefined");
|
||||||
|
localStorage.setItem("e" + key, jsonThing(value));
|
||||||
|
}
|
||||||
|
|
||||||
storage.putEncrypted = function(key, value) {
|
this.getEncrypted = function(key, defaultValue) {
|
||||||
//TODO
|
//TODO
|
||||||
if (value === undefined)
|
var value = localStorage.getItem("e" + key);
|
||||||
throw new Error("Tried to store undefined");
|
if (value === null)
|
||||||
localStorage.setItem("e" + key, jsonThing(value));
|
return defaultValue;
|
||||||
}
|
return JSON.parse(value);
|
||||||
|
}
|
||||||
|
|
||||||
storage.getEncrypted = function(key, defaultValue) {
|
this.removeEncrypted = function(key) {
|
||||||
//TODO
|
localStorage.removeItem("e" + key);
|
||||||
var value = localStorage.getItem("e" + key);
|
}
|
||||||
if (value === null)
|
|
||||||
return defaultValue;
|
|
||||||
return JSON.parse(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
storage.removeEncrypted = function(key) {
|
this.putUnencrypted = function(key, value) {
|
||||||
localStorage.removeItem("e" + key);
|
if (value === undefined)
|
||||||
}
|
throw new Error("Tried to store undefined");
|
||||||
|
localStorage.setItem("u" + key, jsonThing(value));
|
||||||
|
}
|
||||||
|
|
||||||
storage.putUnencrypted = function(key, value) {
|
this.getUnencrypted = function(key, defaultValue) {
|
||||||
if (value === undefined)
|
var value = localStorage.getItem("u" + key);
|
||||||
throw new Error("Tried to store undefined");
|
if (value === null)
|
||||||
localStorage.setItem("u" + key, jsonThing(value));
|
return defaultValue;
|
||||||
}
|
return JSON.parse(value);
|
||||||
|
}
|
||||||
|
|
||||||
storage.getUnencrypted = function(key, defaultValue) {
|
this.removeUnencrypted = function(key) {
|
||||||
var value = localStorage.getItem("u" + key);
|
localStorage.removeItem("u" + key);
|
||||||
if (value === null)
|
}
|
||||||
return defaultValue;
|
|
||||||
return JSON.parse(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
storage.removeUnencrypted = function(key) {
|
/**********************
|
||||||
localStorage.removeItem("u" + key);
|
*** Device Storage ***
|
||||||
}
|
**********************/
|
||||||
|
this.devices = new function() {
|
||||||
|
this.getDeviceObject = function(encodedNumber) {
|
||||||
|
return storage.getEncrypted("deviceObject" + getEncodedNumber(encodedNumber));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getDeviceIdListFromNumber = function(number) {
|
||||||
|
return storage.getEncrypted("deviceIdList" + getNumberFromString(number), []);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addDeviceIdForNumber = function(number, deviceId) {
|
||||||
|
var deviceIdList = this.getDeviceIdListFromNumber(getNumberFromString(number));
|
||||||
|
for (var i = 0; i < deviceIdList.length; i++) {
|
||||||
|
if (deviceIdList[i] == deviceId)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
deviceIdList[deviceIdList.length] = deviceId;
|
||||||
|
storage.putEncrypted("deviceIdList" + getNumberFromString(number), deviceIdList);
|
||||||
|
}
|
||||||
|
|
||||||
|
// throws "Identity key mismatch"
|
||||||
|
this.saveDeviceObject = function(deviceObject) {
|
||||||
|
var existing = this.getDeviceObject(deviceObject.encodedNumber);
|
||||||
|
if (existing === undefined)
|
||||||
|
existing = {encodedNumber: getEncodedNumber(deviceObject.encodedNumber)};
|
||||||
|
for (key in deviceObject) {
|
||||||
|
if (key == "encodedNumber")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (key == "identityKey" && deviceObject.identityKey != deviceObject.identityKey)
|
||||||
|
throw new Error("Identity key mismatch");
|
||||||
|
|
||||||
|
existing[key] = deviceObject[key];
|
||||||
|
}
|
||||||
|
storage.putEncrypted("deviceObject" + getEncodedNumber(deviceObject.encodedNumber), existing);
|
||||||
|
this.addDeviceIdForNumber(deviceObject.encodedNumber, getDeviceId(deviceObject.encodedNumber));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getDeviceObjectListFromNumber = function(number) {
|
||||||
|
var deviceObjectList = [];
|
||||||
|
var deviceIdList = this.getDeviceIdListFromNumber(number);
|
||||||
|
for (var i = 0; i < deviceIdList.length; i++)
|
||||||
|
deviceObjectList[deviceObjectList.length] = this.getDeviceObject(getNumberFromString(number) + "." + deviceIdList[i]);
|
||||||
|
return deviceObjectList;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
function registrationDone() {
|
function registrationDone() {
|
||||||
storage.putUnencrypted("registration_done", "");
|
storage.putUnencrypted("registration_done", "");
|
||||||
|
@ -328,49 +370,6 @@ function storeMessage(messageObject) {
|
||||||
chrome.runtime.sendMessage(conversation[conversation.length - 1]);
|
chrome.runtime.sendMessage(conversation[conversation.length - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDeviceObject(encodedNumber) {
|
|
||||||
return storage.getEncrypted("deviceObject" + getEncodedNumber(encodedNumber));
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDeviceIdListFromNumber(number) {
|
|
||||||
return storage.getEncrypted("deviceIdList" + getNumberFromString(number), []);
|
|
||||||
}
|
|
||||||
|
|
||||||
function addDeviceIdForNumber(number, deviceId) {
|
|
||||||
var deviceIdList = getDeviceIdListFromNumber(getNumberFromString(number));
|
|
||||||
for (var i = 0; i < deviceIdList.length; i++) {
|
|
||||||
if (deviceIdList[i] == deviceId)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
deviceIdList[deviceIdList.length] = deviceId;
|
|
||||||
storage.putEncrypted("deviceIdList" + getNumberFromString(number), deviceIdList);
|
|
||||||
}
|
|
||||||
|
|
||||||
// throws "Identity key mismatch"
|
|
||||||
function saveDeviceObject(deviceObject) {
|
|
||||||
var existing = getDeviceObject(deviceObject.encodedNumber);
|
|
||||||
if (existing === undefined)
|
|
||||||
existing = {encodedNumber: getEncodedNumber(deviceObject.encodedNumber)};
|
|
||||||
for (key in deviceObject) {
|
|
||||||
if (key == "encodedNumber")
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (key == "identityKey" && deviceObject.identityKey != deviceObject.identityKey)
|
|
||||||
throw new Error("Identity key mismatch");
|
|
||||||
|
|
||||||
existing[key] = deviceObject[key];
|
|
||||||
}
|
|
||||||
storage.putEncrypted("deviceObject" + getEncodedNumber(deviceObject.encodedNumber), existing);
|
|
||||||
addDeviceIdForNumber(deviceObject.encodedNumber, getDeviceId(deviceObject.encodedNumber));
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDeviceObjectListFromNumber(number) {
|
|
||||||
var deviceObjectList = [];
|
|
||||||
var deviceIdList = getDeviceIdListFromNumber(number);
|
|
||||||
for (var i = 0; i < deviceIdList.length; i++)
|
|
||||||
deviceObjectList[deviceObjectList.length] = getDeviceObject(getNumberFromString(number) + "." + deviceIdList[i]);
|
|
||||||
return deviceObjectList;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************
|
/**********************
|
||||||
*** NaCL Interface ***
|
*** NaCL Interface ***
|
||||||
|
@ -456,16 +455,16 @@ function subscribeToPush(message_callback) {
|
||||||
if (message.type == 3) {
|
if (message.type == 3) {
|
||||||
console.log("Got pong message");
|
console.log("Got pong message");
|
||||||
} else if (message.type === undefined && message.id !== undefined) {
|
} else if (message.type === undefined && message.id !== undefined) {
|
||||||
crypto.decryptWebsocketMessage(message.message).then(function(plaintext) {
|
textsecure.crypto.decryptWebsocketMessage(message.message).then(function(plaintext) {
|
||||||
var proto = decodeIncomingPushMessageProtobuf(getString(plaintext));
|
var proto = decodeIncomingPushMessageProtobuf(getString(plaintext));
|
||||||
// After this point, a) decoding errors are not the server's fault, and
|
// After this point, a) decoding errors are not the server's fault, and
|
||||||
// b) we should handle them gracefully and tell the user they received an invalid message
|
// b) we should handle them gracefully and tell the user they received an invalid message
|
||||||
console.log("Successfully decoded message with id: " + message.id);
|
console.log("Successfully decoded message with id: " + message.id);
|
||||||
socket.send(JSON.stringify({type: 1, id: message.id}));
|
socket.send(JSON.stringify({type: 1, id: message.id}));
|
||||||
return crypto.handleIncomingPushMessageProto(proto).then(function(decrypted) {
|
return textsecure.crypto.handleIncomingPushMessageProto(proto).then(function(decrypted) {
|
||||||
var handleAttachment = function(attachment) {
|
var handleAttachment = function(attachment) {
|
||||||
return API.getAttachment(attachment.id).then(function(encryptedBin) {
|
return API.getAttachment(attachment.id).then(function(encryptedBin) {
|
||||||
return crypto.decryptAttachment(encryptedBin, toArrayBuffer(attachment.key)).then(function(decryptedBin) {
|
return textsecure.crypto.decryptAttachment(encryptedBin, toArrayBuffer(attachment.key)).then(function(decryptedBin) {
|
||||||
attachment.decrypted = decryptedBin;
|
attachment.decrypted = decryptedBin;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -492,7 +491,7 @@ function subscribeToPush(message_callback) {
|
||||||
function getKeysForNumber(number) {
|
function getKeysForNumber(number) {
|
||||||
return API.getKeysForNumber(number).then(function(response) {
|
return API.getKeysForNumber(number).then(function(response) {
|
||||||
for (var i = 0; i < response.length; i++) {
|
for (var i = 0; i < response.length; i++) {
|
||||||
saveDeviceObject({
|
storage.devices.saveDeviceObject({
|
||||||
encodedNumber: number + "." + response[i].deviceId,
|
encodedNumber: number + "." + response[i].deviceId,
|
||||||
identityKey: response[i].identityKey,
|
identityKey: response[i].identityKey,
|
||||||
publicKey: response[i].publicKey,
|
publicKey: response[i].publicKey,
|
||||||
|
@ -512,7 +511,7 @@ function sendMessageToDevices(number, deviceObjectList, message, success_callbac
|
||||||
var promises = [];
|
var promises = [];
|
||||||
|
|
||||||
var addEncryptionFor = function(i) {
|
var addEncryptionFor = function(i) {
|
||||||
return crypto.encryptMessageFor(deviceObjectList[i], message).then(function(encryptedMsg) {
|
return textsecure.crypto.encryptMessageFor(deviceObjectList[i], message).then(function(encryptedMsg) {
|
||||||
jsonData[i] = {
|
jsonData[i] = {
|
||||||
type: encryptedMsg.type,
|
type: encryptedMsg.type,
|
||||||
destination: deviceObjectList[i].encodedNumber,
|
destination: deviceObjectList[i].encodedNumber,
|
||||||
|
@ -574,11 +573,11 @@ function sendMessageToNumbers(numbers, message, callback) {
|
||||||
|
|
||||||
for (var i = 0; i < numbers.length; i++) {
|
for (var i = 0; i < numbers.length; i++) {
|
||||||
var number = numbers[i];
|
var number = numbers[i];
|
||||||
var devicesForNumber = getDeviceObjectListFromNumber(number);
|
var devicesForNumber = storage.devices.getDeviceObjectListFromNumber(number);
|
||||||
|
|
||||||
if (devicesForNumber.length == 0) {
|
if (devicesForNumber.length == 0) {
|
||||||
getKeysForNumber(number).then(function(identity_key) {
|
getKeysForNumber(number).then(function(identity_key) {
|
||||||
devicesForNumber = getDeviceObjectListFromNumber(number);
|
devicesForNumber = storage.devices.getDeviceObjectListFromNumber(number);
|
||||||
if (devicesForNumber.length == 0)
|
if (devicesForNumber.length == 0)
|
||||||
registerError(number, "Failed to retreive new device keys for number " + number, null);
|
registerError(number, "Failed to retreive new device keys for number " + number, null);
|
||||||
else
|
else
|
||||||
|
@ -592,7 +591,7 @@ function sendMessageToNumbers(numbers, message, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function requestIdentityPrivKeyFromMasterDevice(number, identityKey) {
|
function requestIdentityPrivKeyFromMasterDevice(number, identityKey) {
|
||||||
sendMessageToDevices([getDeviceObject(getNumberFromString(number)) + ".1"],
|
sendMessageToDevices([storage.devices.getDeviceObject(getNumberFromString(number)) + ".1"],
|
||||||
{message: "Identity Key request"}, function() {}, function() {});//TODO
|
{message: "Identity Key request"}, function() {}, function() {});//TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,10 +39,10 @@ $('#number').on('change', function() {//TODO
|
||||||
});
|
});
|
||||||
|
|
||||||
var single_device = false;
|
var single_device = false;
|
||||||
var signaling_key = window.crypto.getRandomBytes(32 + 20);
|
var signaling_key = textsecure.crypto.getRandomBytes(32 + 20);
|
||||||
var password = btoa(getString(window.crypto.getRandomBytes(16)));
|
var password = btoa(getString(textsecure.crypto.getRandomBytes(16)));
|
||||||
password = password.substring(0, password.length - 2);
|
password = password.substring(0, password.length - 2);
|
||||||
var registrationId = new Uint16Array(window.crypto.getRandomBytes(2))[0];
|
var registrationId = new Uint16Array(textsecure.crypto.getRandomBytes(2))[0];
|
||||||
registrationId = registrationId & 0x3fff;
|
registrationId = registrationId & 0x3fff;
|
||||||
|
|
||||||
$('#init-go-single-client').click(function() {
|
$('#init-go-single-client').click(function() {
|
||||||
|
@ -89,7 +89,7 @@ $('#init-go').click(function() {
|
||||||
|
|
||||||
var register_keys_func = function() {
|
var register_keys_func = function() {
|
||||||
$('#verify2done').html('done');
|
$('#verify2done').html('done');
|
||||||
crypto.generateKeys().then(function(keys) {
|
textsecure.crypto.generateKeys().then(function(keys) {
|
||||||
$('#verify3done').html('done');
|
$('#verify3done').html('done');
|
||||||
API.registerKeys(keys,
|
API.registerKeys(keys,
|
||||||
function(response) {
|
function(response) {
|
||||||
|
|
34
js/test.js
34
js/test.js
|
@ -122,7 +122,7 @@ registerOnLoadFunction(function() {
|
||||||
var server_message = {type: 0, // unencrypted
|
var server_message = {type: 0, // unencrypted
|
||||||
source: "+19999999999", timestamp: 42, message: text_message.encode() };
|
source: "+19999999999", timestamp: 42, message: text_message.encode() };
|
||||||
|
|
||||||
return crypto.handleIncomingPushMessageProto(server_message).then(function(message) {
|
return textsecure.crypto.handleIncomingPushMessageProto(server_message).then(function(message) {
|
||||||
return (message.message.body == text_message.body &&
|
return (message.message.body == text_message.body &&
|
||||||
message.message.attachments.length == text_message.attachments.length &&
|
message.message.attachments.length == text_message.attachments.length &&
|
||||||
text_message.attachments.length == 0);
|
text_message.attachments.length == 0);
|
||||||
|
@ -130,7 +130,7 @@ registerOnLoadFunction(function() {
|
||||||
}, 'Unencrypted PushMessageProto "decrypt"', true);
|
}, 'Unencrypted PushMessageProto "decrypt"', true);
|
||||||
|
|
||||||
TEST(function() {
|
TEST(function() {
|
||||||
return crypto.generateKeys().then(function() {
|
return textsecure.crypto.generateKeys().then(function() {
|
||||||
if (storage.getEncrypted("25519KeyidentityKey") === undefined)
|
if (storage.getEncrypted("25519KeyidentityKey") === undefined)
|
||||||
return false;
|
return false;
|
||||||
if (storage.getEncrypted("25519KeypreKey16777215") === undefined)
|
if (storage.getEncrypted("25519KeypreKey16777215") === undefined)
|
||||||
|
@ -152,7 +152,7 @@ registerOnLoadFunction(function() {
|
||||||
var bob_pub = hexToArrayBuffer("05de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f");
|
var bob_pub = hexToArrayBuffer("05de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f");
|
||||||
var shared_sec = hexToArrayBuffer("4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742");
|
var shared_sec = hexToArrayBuffer("4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742");
|
||||||
|
|
||||||
return crypto_tests.privToPub(alice_priv, true).then(function(aliceKeyPair) {
|
return textsecure.crypto.testing_only.privToPub(alice_priv, true).then(function(aliceKeyPair) {
|
||||||
var target = new Uint8Array(alice_priv.slice(0));
|
var target = new Uint8Array(alice_priv.slice(0));
|
||||||
target[0] &= 248;
|
target[0] &= 248;
|
||||||
target[31] &= 127;
|
target[31] &= 127;
|
||||||
|
@ -160,7 +160,7 @@ registerOnLoadFunction(function() {
|
||||||
if (String.fromCharCode.apply(null, new Uint8Array(aliceKeyPair.privKey)) != String.fromCharCode.apply(null, target))
|
if (String.fromCharCode.apply(null, new Uint8Array(aliceKeyPair.privKey)) != String.fromCharCode.apply(null, target))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return crypto_tests.privToPub(bob_priv, true).then(function(bobKeyPair) {
|
return textsecure.crypto.testing_only.privToPub(bob_priv, true).then(function(bobKeyPair) {
|
||||||
var target = new Uint8Array(bob_priv.slice(0));
|
var target = new Uint8Array(bob_priv.slice(0));
|
||||||
target[0] &= 248;
|
target[0] &= 248;
|
||||||
target[31] &= 127;
|
target[31] &= 127;
|
||||||
|
@ -174,11 +174,11 @@ registerOnLoadFunction(function() {
|
||||||
if (String.fromCharCode.apply(null, new Uint8Array(bobKeyPair.pubKey)) != String.fromCharCode.apply(null, new Uint8Array(bob_pub)))
|
if (String.fromCharCode.apply(null, new Uint8Array(bobKeyPair.pubKey)) != String.fromCharCode.apply(null, new Uint8Array(bob_pub)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return crypto_tests.ECDHE(bobKeyPair.pubKey, aliceKeyPair.privKey).then(function(ss) {
|
return textsecure.crypto.testing_only.ECDHE(bobKeyPair.pubKey, aliceKeyPair.privKey).then(function(ss) {
|
||||||
if (String.fromCharCode.apply(null, new Uint16Array(ss)) != String.fromCharCode.apply(null, new Uint16Array(shared_sec)))
|
if (String.fromCharCode.apply(null, new Uint16Array(ss)) != String.fromCharCode.apply(null, new Uint16Array(shared_sec)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return crypto_tests.ECDHE(aliceKeyPair.pubKey, bobKeyPair.privKey).then(function(ss) {
|
return textsecure.crypto.testing_only.ECDHE(aliceKeyPair.pubKey, bobKeyPair.privKey).then(function(ss) {
|
||||||
if (String.fromCharCode.apply(null, new Uint16Array(ss)) != String.fromCharCode.apply(null, new Uint16Array(shared_sec)))
|
if (String.fromCharCode.apply(null, new Uint16Array(ss)) != String.fromCharCode.apply(null, new Uint16Array(shared_sec)))
|
||||||
return false;
|
return false;
|
||||||
else
|
else
|
||||||
|
@ -204,7 +204,7 @@ registerOnLoadFunction(function() {
|
||||||
for (var i = 0; i < 10; i++)
|
for (var i = 0; i < 10; i++)
|
||||||
info[i] = 240 + i;
|
info[i] = 240 + i;
|
||||||
|
|
||||||
return crypto_tests.HKDF(IKM, salt, info).then(function(OKM){
|
return textsecure.crypto.testing_only.HKDF(IKM, salt, info).then(function(OKM){
|
||||||
var T1 = hexToArrayBuffer("3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf");
|
var T1 = hexToArrayBuffer("3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf");
|
||||||
var T2 = hexToArrayBuffer("34007208d5b887185865");
|
var T2 = hexToArrayBuffer("34007208d5b887185865");
|
||||||
return (getString(OKM[0]) == getString(T1) && getString(OKM[1]).substring(0, 10) == getString(T2));
|
return (getString(OKM[0]) == getString(T1) && getString(OKM[1]).substring(0, 10) == getString(T2));
|
||||||
|
@ -300,28 +300,28 @@ registerOnLoadFunction(function() {
|
||||||
];
|
];
|
||||||
|
|
||||||
var axolotlTestVectors = function(v, remoteDevice) {
|
var axolotlTestVectors = function(v, remoteDevice) {
|
||||||
var origCreateNewKeyPair = crypto_tests.createNewKeyPair;
|
var origCreateNewKeyPair = textsecure.crypto.testing_only.createNewKeyPair;
|
||||||
var doStep;
|
var doStep;
|
||||||
var stepDone;
|
var stepDone;
|
||||||
|
|
||||||
stepDone = function(res) {
|
stepDone = function(res) {
|
||||||
if (!res || privKeyQueue.length != 0) {
|
if (!res || privKeyQueue.length != 0) {
|
||||||
crypto_tests.createNewKeyPair = origCreateNewKeyPair;
|
textsecure.crypto.testing_only.createNewKeyPair = origCreateNewKeyPair;
|
||||||
return false;
|
return false;
|
||||||
} else if (step == v.length) {
|
} else if (step == v.length) {
|
||||||
crypto_tests.createNewKeyPair = origCreateNewKeyPair;
|
textsecure.crypto.testing_only.createNewKeyPair = origCreateNewKeyPair;
|
||||||
return true;
|
return true;
|
||||||
} else
|
} else
|
||||||
return doStep().then(stepDone);
|
return doStep().then(stepDone);
|
||||||
}
|
}
|
||||||
|
|
||||||
var privKeyQueue = [];
|
var privKeyQueue = [];
|
||||||
crypto_tests.createNewKeyPair = function(isIdentity) {
|
textsecure.crypto.testing_only.createNewKeyPair = function(isIdentity) {
|
||||||
if (privKeyQueue.length == 0 || isIdentity)
|
if (privKeyQueue.length == 0 || isIdentity)
|
||||||
throw new Error('Out of private keys');
|
throw new Error('Out of private keys');
|
||||||
else {
|
else {
|
||||||
var privKey = privKeyQueue.shift();
|
var privKey = privKeyQueue.shift();
|
||||||
return crypto_tests.privToPub(privKey, false).then(function(keyPair) {
|
return textsecure.crypto.testing_only.privToPub(privKey, false).then(function(keyPair) {
|
||||||
var a = btoa(getString(keyPair.privKey)); var b = btoa(getString(privKey));
|
var a = btoa(getString(keyPair.privKey)); var b = btoa(getString(privKey));
|
||||||
if (getString(keyPair.privKey) != getString(privKey))
|
if (getString(keyPair.privKey) != getString(privKey))
|
||||||
throw new Error('Failed to rederive private key!');
|
throw new Error('Failed to rederive private key!');
|
||||||
|
@ -345,15 +345,15 @@ registerOnLoadFunction(function() {
|
||||||
message.type = data.type;
|
message.type = data.type;
|
||||||
message.source = remoteDevice.encodedNumber;
|
message.source = remoteDevice.encodedNumber;
|
||||||
message.message = data.message;
|
message.message = data.message;
|
||||||
return crypto.handleIncomingPushMessageProto(decodeIncomingPushMessageProtobuf(getString(message.encode()))).then(function(res) {
|
return textsecure.crypto.handleIncomingPushMessageProto(decodeIncomingPushMessageProtobuf(getString(message.encode()))).then(function(res) {
|
||||||
return res.message.body == data.expectedSmsText;
|
return res.message.body == data.expectedSmsText;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.ourIdentityKey !== undefined)
|
if (data.ourIdentityKey !== undefined)
|
||||||
return crypto_tests.privToPub(data.ourIdentityKey, true).then(function(keyPair) {
|
return textsecure.crypto.testing_only.privToPub(data.ourIdentityKey, true).then(function(keyPair) {
|
||||||
storage.putEncrypted("25519KeyidentityKey", keyPair);
|
storage.putEncrypted("25519KeyidentityKey", keyPair);
|
||||||
return crypto_tests.privToPub(data.ourPreKey, false).then(function(keyPair) {
|
return textsecure.crypto.testing_only.privToPub(data.ourPreKey, false).then(function(keyPair) {
|
||||||
storage.putEncrypted("25519KeypreKey" + data.preKeyId, keyPair);
|
storage.putEncrypted("25519KeypreKey" + data.preKeyId, keyPair);
|
||||||
return postLocalKeySetup();
|
return postLocalKeySetup();
|
||||||
});
|
});
|
||||||
|
@ -374,7 +374,7 @@ registerOnLoadFunction(function() {
|
||||||
var message = new PushMessageContentProtobuf();
|
var message = new PushMessageContentProtobuf();
|
||||||
message.body = data.smsText;
|
message.body = data.smsText;
|
||||||
|
|
||||||
return crypto.encryptMessageFor(remoteDevice, message).then(function(res) {
|
return textsecure.crypto.encryptMessageFor(remoteDevice, message).then(function(res) {
|
||||||
//XXX: This should be all we do: stepDone(getString(data.expectedCiphertext) == getString(res.body));
|
//XXX: This should be all we do: stepDone(getString(data.expectedCiphertext) == getString(res.body));
|
||||||
if (res.type == 1) { //XXX: This should be used for everything...
|
if (res.type == 1) { //XXX: This should be used for everything...
|
||||||
var expectedString = getString(data.expectedCiphertext);
|
var expectedString = getString(data.expectedCiphertext);
|
||||||
|
@ -395,7 +395,7 @@ registerOnLoadFunction(function() {
|
||||||
privKeyQueue.push(data.ourEphemeralKey);
|
privKeyQueue.push(data.ourEphemeralKey);
|
||||||
|
|
||||||
if (data.ourIdentityKey !== undefined)
|
if (data.ourIdentityKey !== undefined)
|
||||||
return crypto_tests.privToPub(data.ourIdentityKey, true).then(function(keyPair) {
|
return textsecure.crypto.testing_only.privToPub(data.ourIdentityKey, true).then(function(keyPair) {
|
||||||
storage.putEncrypted("25519KeyidentityKey", keyPair);
|
storage.putEncrypted("25519KeyidentityKey", keyPair);
|
||||||
return postLocalKeySetup();
|
return postLocalKeySetup();
|
||||||
});
|
});
|
||||||
|
|
|
@ -42,9 +42,9 @@
|
||||||
<script type="text/javascript" src="js/webcrypto.js"></script>
|
<script type="text/javascript" src="js/webcrypto.js"></script>
|
||||||
<script type="text/javascript" src="js/crypto.js"></script>
|
<script type="text/javascript" src="js/crypto.js"></script>
|
||||||
<script type="text/javascript" src="js/helpers.js"></script>
|
<script type="text/javascript" src="js/helpers.js"></script>
|
||||||
<!-- TODO: Tests for api stuff -->
|
|
||||||
<script type="text/javascript" src="js/api.js"></script>
|
<script type="text/javascript" src="js/api.js"></script>
|
||||||
<script type="text/javascript" src="js/fake_api.js"></script>
|
<!-- TODO: Tests for api stuff -->
|
||||||
|
<!--<script type="text/javascript" src="js/fake_api.js"></script>-->
|
||||||
<script type="text/javascript" src="js/test.js"></script>
|
<script type="text/javascript" src="js/test.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue