FINALLY report crypto, etc errors to console thanks to promises...
This commit is contained in:
parent
a7de5e2159
commit
caa363b929
5 changed files with 112 additions and 111 deletions
62
js/api.js
62
js/api.js
|
@ -44,7 +44,7 @@ var API = new function() {
|
||||||
* do_auth: alternative to user/password where user/password are figured out automagically
|
* do_auth: alternative to user/password where user/password are figured out automagically
|
||||||
* jsonData: JSON data sent in the request body
|
* jsonData: JSON data sent in the request body
|
||||||
*/
|
*/
|
||||||
this.doAjax = function doAjax(param) {
|
var doAjax = function(param) {
|
||||||
if (param.urlParameters === undefined)
|
if (param.urlParameters === undefined)
|
||||||
param.urlParameters = "";
|
param.urlParameters = "";
|
||||||
|
|
||||||
|
@ -53,6 +53,7 @@ var API = new function() {
|
||||||
param.password = storage.getEncrypted("password");
|
param.password = storage.getEncrypted("password");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
$.ajax(URL_BASE + URL_CALLS[param.call] + param.urlParameters, {
|
$.ajax(URL_BASE + URL_CALLS[param.call] + param.urlParameters, {
|
||||||
type : param.httpType,
|
type : param.httpType,
|
||||||
data : param.jsonData && jsonThing(param.jsonData),
|
data : param.jsonData && jsonThing(param.jsonData),
|
||||||
|
@ -66,8 +67,7 @@ var API = new function() {
|
||||||
},
|
},
|
||||||
|
|
||||||
success : function(response, textStatus, jqXHR) {
|
success : function(response, textStatus, jqXHR) {
|
||||||
if (param.success_callback !== undefined)
|
resolve(response);
|
||||||
param.success_callback(response);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
error : function(jqXHR, textStatus, errorThrown) {
|
error : function(jqXHR, textStatus, errorThrown) {
|
||||||
|
@ -75,25 +75,30 @@ var API = new function() {
|
||||||
if (code == 200) {
|
if (code == 200) {
|
||||||
// happens sometimes when we get no response
|
// happens sometimes when we get no response
|
||||||
// (TODO: Fix server to return 204? instead)
|
// (TODO: Fix server to return 204? instead)
|
||||||
if (param.success_callback !== undefined)
|
resolve(null);
|
||||||
param.success_callback(null);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (code > 999 || code < 100)
|
if (code > 999 || code < 100)
|
||||||
code = -1;
|
code = -1;
|
||||||
if (param.error_callback !== undefined)
|
var e = new Error(code);
|
||||||
param.error_callback(code);
|
e.name = "HTTPError";
|
||||||
|
reject(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
this.requestVerificationCode = function(number, success_callback, error_callback) {
|
this.requestVerificationCode = function(number, success_callback, error_callback) {
|
||||||
this.doAjax({
|
doAjax({
|
||||||
call : 'accounts',
|
call : 'accounts',
|
||||||
httpType : 'GET',
|
httpType : 'GET',
|
||||||
urlParameters : '/sms/code/' + number,
|
urlParameters : '/sms/code/' + number,
|
||||||
success_callback : success_callback,
|
}).then(function(response) {
|
||||||
error_callback : error_callback
|
if (success_callback !== undefined)
|
||||||
|
success_callback(response);
|
||||||
|
}).catch(function(code) {
|
||||||
|
if (error_callback !== undefined)
|
||||||
|
error_callback(code);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -103,7 +108,7 @@ var API = new function() {
|
||||||
var call = single_device ? 'accounts' : 'devices';
|
var call = single_device ? 'accounts' : 'devices';
|
||||||
var urlPrefix = single_device ? '/code/' : '/';
|
var urlPrefix = single_device ? '/code/' : '/';
|
||||||
|
|
||||||
API.doAjax({
|
doAjax({
|
||||||
call : call,
|
call : call,
|
||||||
httpType : 'PUT',
|
httpType : 'PUT',
|
||||||
urlParameters : urlPrefix + code,
|
urlParameters : urlPrefix + code,
|
||||||
|
@ -112,8 +117,12 @@ var API = new function() {
|
||||||
jsonData : { signalingKey : btoa(getString(signaling_key)),
|
jsonData : { signalingKey : btoa(getString(signaling_key)),
|
||||||
supportsSms : false,
|
supportsSms : false,
|
||||||
fetchesMessages : true },
|
fetchesMessages : true },
|
||||||
success_callback : success_callback,
|
}).then(function(response) {
|
||||||
error_callback : error_callback
|
if (success_callback !== undefined)
|
||||||
|
success_callback(response);
|
||||||
|
}).catch(function(code) {
|
||||||
|
if (error_callback !== undefined)
|
||||||
|
error_callback(code);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -123,23 +132,27 @@ var API = new function() {
|
||||||
for (var i = 0; i < keys.keys.length; i++)
|
for (var i = 0; i < keys.keys.length; i++)
|
||||||
keys.keys[i] = {keyId: i, publicKey: btoa(getString(keys.keys[i].publicKey)), identityKey: identityKey};
|
keys.keys[i] = {keyId: i, publicKey: btoa(getString(keys.keys[i].publicKey)), identityKey: identityKey};
|
||||||
keys.lastResortKey = {keyId: keys.lastResortKey.keyId, publicKey: btoa(getString(keys.lastResortKey.publicKey)), identityKey: identityKey};
|
keys.lastResortKey = {keyId: keys.lastResortKey.keyId, publicKey: btoa(getString(keys.lastResortKey.publicKey)), identityKey: identityKey};
|
||||||
this.doAjax({
|
doAjax({
|
||||||
call : 'keys',
|
call : 'keys',
|
||||||
httpType : 'PUT',
|
httpType : 'PUT',
|
||||||
do_auth : true,
|
do_auth : true,
|
||||||
jsonData : keys,
|
jsonData : keys,
|
||||||
success_callback : success_callback,
|
}).then(function(response) {
|
||||||
error_callback : error_callback
|
if (success_callback !== undefined)
|
||||||
|
success_callback(response);
|
||||||
|
}).catch(function(code) {
|
||||||
|
if (error_callback !== undefined)
|
||||||
|
error_callback(code);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
this.getKeysForNumber = function(number, success_callback, error_callback) {
|
this.getKeysForNumber = function(number, success_callback, error_callback) {
|
||||||
this.doAjax({
|
doAjax({
|
||||||
call : 'keys',
|
call : 'keys',
|
||||||
httpType : 'GET',
|
httpType : 'GET',
|
||||||
do_auth : true,
|
do_auth : true,
|
||||||
urlParameters : "/" + getNumberFromString(number) + "/*",
|
urlParameters : "/" + getNumberFromString(number) + "/*",
|
||||||
success_callback : function(response) {
|
}).then(function(response) {
|
||||||
//TODO: Do this conversion somewhere else?
|
//TODO: Do this conversion somewhere else?
|
||||||
var res = response.keys;
|
var res = response.keys;
|
||||||
for (var i = 0; i < res.length; i++) {
|
for (var i = 0; i < res.length; i++) {
|
||||||
|
@ -149,12 +162,13 @@ var API = new function() {
|
||||||
res[i].keyId = 0;
|
res[i].keyId = 0;
|
||||||
}
|
}
|
||||||
success_callback(res);
|
success_callback(res);
|
||||||
},
|
}).catch(function(code) {
|
||||||
error_callback : error_callback
|
if (error_callback !== undefined)
|
||||||
|
error_callback(code);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
this.sendMessages = function(destination, messageArray, success_callback, error_callback) {
|
this.sendMessages = function(destination, messageArray) {
|
||||||
//TODO: Do this conversion somewhere else?
|
//TODO: Do this conversion somewhere else?
|
||||||
for (var i = 0; i < messageArray.length; i++)
|
for (var i = 0; i < messageArray.length; i++)
|
||||||
messageArray[i].body = btoa(messageArray[i].body);
|
messageArray[i].body = btoa(messageArray[i].body);
|
||||||
|
@ -162,14 +176,12 @@ var API = new function() {
|
||||||
if (messageArray[0].relay !== undefined)
|
if (messageArray[0].relay !== undefined)
|
||||||
jsonData.relay = messageArray[0].relay;
|
jsonData.relay = messageArray[0].relay;
|
||||||
|
|
||||||
this.doAjax({
|
return doAjax({
|
||||||
call : 'messages',
|
call : 'messages',
|
||||||
httpType : 'PUT',
|
httpType : 'PUT',
|
||||||
urlParameters : '/' + destination,
|
urlParameters : '/' + destination,
|
||||||
do_auth : true,
|
do_auth : true,
|
||||||
jsonData : jsonData,
|
jsonData : jsonData,
|
||||||
success_callback : success_callback,
|
|
||||||
error_callback : error_callback
|
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
}(); // API
|
}(); // API
|
||||||
|
|
40
js/crypto.js
40
js/crypto.js
|
@ -115,16 +115,20 @@ window.crypto = (function() {
|
||||||
if (privKey === undefined || privKey.byteLength != 32)
|
if (privKey === undefined || privKey.byteLength != 32)
|
||||||
throw new Error("Invalid private key");
|
throw new Error("Invalid private key");
|
||||||
|
|
||||||
if (pubKey === undefined || pubKey.byteLength != 33 || new Uint8Array(pubKey)[0] != 5)
|
if (pubKey === undefined || ((pubKey.byteLength != 33 || new Uint8Array(pubKey)[0] != 5) && pubKey.byteLength != 32))
|
||||||
throw new Error("Invalid public key");
|
throw new Error("Invalid public key");
|
||||||
|
if (pubKey.byteLength == 33)
|
||||||
|
pubKey = pubKey.slice(1);
|
||||||
|
else
|
||||||
|
console.error("WARNING: Expected pubkey of length 33, please report the ST and client that generated the pubkey");
|
||||||
|
|
||||||
return new Promise(function(resolve) {
|
return new Promise(function(resolve) {
|
||||||
if (USE_NACL) {
|
if (USE_NACL) {
|
||||||
postNaclMessage({command: "ECDHE", priv: privKey, pub: pubKey.slice(1)}).then(function(message) {
|
postNaclMessage({command: "ECDHE", priv: privKey, pub: pubKey}).then(function(message) {
|
||||||
resolve(message.res);
|
resolve(message.res);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
resolve(toArrayBuffer(curve25519(new Uint16Array(privKey), new Uint16Array(pubKey.slice(1)))));
|
resolve(toArrayBuffer(curve25519(new Uint16Array(privKey), new Uint16Array(pubKey))));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -438,7 +442,9 @@ window.crypto = (function() {
|
||||||
if (session === undefined) {
|
if (session === undefined) {
|
||||||
return createNewKeyPair(false).then(function(baseKey) {
|
return createNewKeyPair(false).then(function(baseKey) {
|
||||||
preKeyMsg.baseKey = toArrayBuffer(baseKey.pubKey);
|
preKeyMsg.baseKey = toArrayBuffer(baseKey.pubKey);
|
||||||
return initSession(true, baseKey, deviceObject.encodedNumber, deviceObject.identityKey, deviceObject.publicKey).then(function() {
|
return initSession(true, baseKey, deviceObject.encodedNumber,
|
||||||
|
toArrayBuffer(deviceObject.identityKey), toArrayBuffer(deviceObject.publicKey))
|
||||||
|
.then(function() {
|
||||||
//TODO: Delete preKey info on first message received back
|
//TODO: Delete preKey info on first message received back
|
||||||
session = crypto_storage.getSession(deviceObject.encodedNumber);
|
session = crypto_storage.getSession(deviceObject.encodedNumber);
|
||||||
session.pendingPreKey = baseKey.pubKey;
|
session.pendingPreKey = baseKey.pubKey;
|
||||||
|
@ -475,25 +481,27 @@ window.crypto = (function() {
|
||||||
|
|
||||||
var keys = {};
|
var keys = {};
|
||||||
keys.keys = [];
|
keys.keys = [];
|
||||||
var keysLeft = GENERATE_KEYS_KEYS_GENERATED;
|
|
||||||
return new Promise(function(resolve) {
|
var generateKey = function(keyId) {
|
||||||
for (var i = firstKeyId; i < firstKeyId + GENERATE_KEYS_KEYS_GENERATED; i++) {
|
return crypto_storage.getNewPubKeySTORINGPrivKey("preKey" + keyId, false).then(function(pubKey) {
|
||||||
crypto_storage.getNewPubKeySTORINGPrivKey("preKey" + i, false).then(function(pubKey) {
|
keys.keys[keyId] = {keyId: keyId, publicKey: pubKey, identityKey: identityKey};
|
||||||
keys.keys[i] = {keyId: i, publicKey: pubKey, identityKey: identityKey};
|
});
|
||||||
keysLeft--;
|
};
|
||||||
if (keysLeft == 0) {
|
|
||||||
|
var promises = [];
|
||||||
|
for (var i = firstKeyId; i < firstKeyId + GENERATE_KEYS_KEYS_GENERATED; i++)
|
||||||
|
promises[i] = generateKey(i);
|
||||||
|
|
||||||
|
return Promise.all(promises).then(function() {
|
||||||
// 0xFFFFFF == 16777215
|
// 0xFFFFFF == 16777215
|
||||||
keys.lastResortKey = {keyId: 16777215, publicKey: crypto_storage.getStoredPubKey("preKey16777215"), identityKey: identityKey};//TODO: Rotate lastResortKey
|
keys.lastResortKey = {keyId: 16777215, publicKey: crypto_storage.getStoredPubKey("preKey16777215"), identityKey: identityKey};//TODO: Rotate lastResortKey
|
||||||
if (keys.lastResortKey.publicKey === undefined) {
|
if (keys.lastResortKey.publicKey === undefined) {
|
||||||
return crypto_storage.getNewPubKeySTORINGPrivKey("preKey16777215", false).then(function(pubKey) {
|
return crypto_storage.getNewPubKeySTORINGPrivKey("preKey16777215", false).then(function(pubKey) {
|
||||||
keys.lastResortKey.publicKey = pubKey;
|
keys.lastResortKey.publicKey = pubKey;
|
||||||
resolve(keys);
|
return keys;
|
||||||
});
|
});
|
||||||
} else
|
} else
|
||||||
resolve(keys);
|
return keys;
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (identityKey === undefined)
|
if (identityKey === undefined)
|
||||||
|
|
|
@ -14,14 +14,14 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var FakeWhisperAPI = function() {
|
var FakeWhisperAPI = function() {
|
||||||
this.doAjax = function(param) {
|
var doAjax = function(param) {
|
||||||
if (param.success_callback) {
|
if (param.success_callback) {
|
||||||
setTimeout(param.success_callback, 100, param.response);
|
setTimeout(param.success_callback, 100, param.response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.getKeysForNumber = function(number, success_callback, error_callback) {
|
this.getKeysForNumber = function(number, success_callback, error_callback) {
|
||||||
this.doAjax({ success_callback : success_callback,
|
doAjax({ success_callback : success_callback,
|
||||||
response : [{ identityKey : 1,
|
response : [{ identityKey : 1,
|
||||||
deviceId : 1,
|
deviceId : 1,
|
||||||
publicKey : 1,
|
publicKey : 1,
|
||||||
|
@ -30,7 +30,7 @@ var FakeWhisperAPI = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.sendMessages = function(jsonData, success_callback, error_callback) {
|
this.sendMessages = function(jsonData, success_callback, error_callback) {
|
||||||
this.doAjax({ success_callback : success_callback,
|
doAjax({ success_callback : success_callback,
|
||||||
response : { missingDeviceIds: [] }
|
response : { missingDeviceIds: [] }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,10 +181,6 @@ function jsonThing(thing) {
|
||||||
return JSON.stringify(ensureStringed(thing));
|
return JSON.stringify(ensureStringed(thing));
|
||||||
}
|
}
|
||||||
|
|
||||||
function getArrayBuffer(string) {
|
|
||||||
return base64DecToArr(btoa(string));
|
|
||||||
}
|
|
||||||
|
|
||||||
function base64ToArrayBuffer(string) {
|
function base64ToArrayBuffer(string) {
|
||||||
return base64DecToArr(string);
|
return base64DecToArr(string);
|
||||||
}
|
}
|
||||||
|
@ -505,20 +501,10 @@ function getKeysForNumber(number, success_callback, error_callback) {
|
||||||
function sendMessageToDevices(number, deviceObjectList, message, success_callback, error_callback) {
|
function sendMessageToDevices(number, deviceObjectList, message, success_callback, error_callback) {
|
||||||
var jsonData = [];
|
var jsonData = [];
|
||||||
var relay = undefined;
|
var relay = undefined;
|
||||||
|
var promises = [];
|
||||||
|
|
||||||
var doSend = function() {
|
var addEncryptionFor = function(i) {
|
||||||
API.sendMessages(number, jsonData,
|
return crypto.encryptMessageFor(deviceObjectList[i], message).then(function(encryptedMsg) {
|
||||||
function(result) {
|
|
||||||
success_callback(result);
|
|
||||||
}, function(code) {
|
|
||||||
error_callback(code);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
var addEncryptionFor;
|
|
||||||
addEncryptionFor = function(i) {
|
|
||||||
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,
|
||||||
|
@ -531,27 +517,21 @@ function sendMessageToDevices(number, deviceObjectList, message, success_callbac
|
||||||
jsonData[i].relay = deviceObjectList[i].relay;
|
jsonData[i].relay = deviceObjectList[i].relay;
|
||||||
if (relay === undefined)
|
if (relay === undefined)
|
||||||
relay = jsonData[i].relay;
|
relay = jsonData[i].relay;
|
||||||
else if (relay != jsonData[i].relay) {
|
else if (relay != jsonData[i].relay)
|
||||||
error_callback("Mismatched relays for number " + number);
|
throw new Error("Mismatched relays for number " + number);
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (relay === undefined)
|
if (relay === undefined)
|
||||||
relay = "";
|
relay = "";
|
||||||
else if (relay != "") {
|
else if (relay != "")
|
||||||
error_callback("Mismatched relays for number " + number);
|
throw new Error("Mismatched relays for number " + number);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (i+1 < deviceObjectList.length)
|
|
||||||
addEncryptionFor(i+1);
|
|
||||||
else
|
|
||||||
doSend();
|
|
||||||
});
|
});
|
||||||
//TODO: need to encrypt with session key?
|
|
||||||
}
|
}
|
||||||
addEncryptionFor(0);
|
for (var i = 0; i < deviceObjectList.length; i++)
|
||||||
|
promises[i] = addEncryptionFor(i);
|
||||||
|
return Promise.all(promises).then(function() {
|
||||||
|
return API.sendMessages(number, jsonData);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// callback(success/failure map, see code)
|
// callback(success/failure map, see code)
|
||||||
|
@ -573,13 +553,14 @@ function sendMessageToNumbers(numbers, message, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var doSendMessage = function(number, devicesForNumber, message) {
|
var doSendMessage = function(number, devicesForNumber, message) {
|
||||||
sendMessageToDevices(number, devicesForNumber, message, function(result) {
|
return sendMessageToDevices(number, devicesForNumber, message).then(function(result) {
|
||||||
successfulNumbers[successfulNumbers.length] = number;
|
successfulNumbers[successfulNumbers.length] = number;
|
||||||
numberCompleted();
|
numberCompleted();
|
||||||
}, function(error_code) {
|
}).catch(function(error) {
|
||||||
|
if (error instanceof Error && error.name == "HTTPError" && (error.message == 410 || error.message == 409)) {
|
||||||
//TODO: Re-request keys for number here
|
//TODO: Re-request keys for number here
|
||||||
if (error_code == 410 || error_code == 409) {}
|
}
|
||||||
registerError(number, message);
|
registerError(number, error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -591,11 +572,11 @@ function sendMessageToNumbers(numbers, message, callback) {
|
||||||
getKeysForNumber(number, function(identity_key) {
|
getKeysForNumber(number, function(identity_key) {
|
||||||
devicesForNumber = getDeviceObjectListFromNumber(number);
|
devicesForNumber = getDeviceObjectListFromNumber(number);
|
||||||
if (devicesForNumber.length == 0)
|
if (devicesForNumber.length == 0)
|
||||||
registerError(number, "Failed to retreive new device keys for number " + number);
|
registerError(number, new Error("Failed to retreive new device keys for number " + number));
|
||||||
else
|
else
|
||||||
doSendMessage(number, devicesForNumber, message);
|
doSendMessage(number, devicesForNumber, message);
|
||||||
}, function(error_msg) {
|
}, function(error_msg) {
|
||||||
registerError(number, "Failed to retreive new device keys for number " + number);
|
registerError(number, new Error("Failed to retreive new device keys for number " + number));
|
||||||
});
|
});
|
||||||
} else
|
} else
|
||||||
doSendMessage(number, devicesForNumber, message);
|
doSendMessage(number, devicesForNumber, message);
|
||||||
|
|
|
@ -85,7 +85,7 @@ $('#init-go').click(function() {
|
||||||
|
|
||||||
var register_keys_func = function() {
|
var register_keys_func = function() {
|
||||||
$('#verify2done').html('done');
|
$('#verify2done').html('done');
|
||||||
crypto.generateKeys(function(keys) {
|
crypto.generateKeys().then(function(keys) {
|
||||||
$('#verify3done').html('done');
|
$('#verify3done').html('done');
|
||||||
API.registerKeys(keys,
|
API.registerKeys(keys,
|
||||||
function(response) {
|
function(response) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue