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
104
js/api.js
104
js/api.js
|
@ -44,7 +44,7 @@ var API = new function() {
|
|||
* do_auth: alternative to user/password where user/password are figured out automagically
|
||||
* jsonData: JSON data sent in the request body
|
||||
*/
|
||||
this.doAjax = function doAjax(param) {
|
||||
var doAjax = function(param) {
|
||||
if (param.urlParameters === undefined)
|
||||
param.urlParameters = "";
|
||||
|
||||
|
@ -53,47 +53,52 @@ var API = new function() {
|
|||
param.password = storage.getEncrypted("password");
|
||||
}
|
||||
|
||||
$.ajax(URL_BASE + URL_CALLS[param.call] + param.urlParameters, {
|
||||
type : param.httpType,
|
||||
data : param.jsonData && jsonThing(param.jsonData),
|
||||
contentType : 'application/json; charset=utf-8',
|
||||
dataType : 'json',
|
||||
return new Promise(function(resolve, reject) {
|
||||
$.ajax(URL_BASE + URL_CALLS[param.call] + param.urlParameters, {
|
||||
type : param.httpType,
|
||||
data : param.jsonData && jsonThing(param.jsonData),
|
||||
contentType : 'application/json; charset=utf-8',
|
||||
dataType : 'json',
|
||||
|
||||
beforeSend : function(xhr) {
|
||||
if (param.user !== undefined &&
|
||||
param.password !== undefined)
|
||||
xhr.setRequestHeader("Authorization", "Basic " + btoa(getString(param.user) + ":" + getString(param.password)));
|
||||
},
|
||||
beforeSend : function(xhr) {
|
||||
if (param.user !== undefined &&
|
||||
param.password !== undefined)
|
||||
xhr.setRequestHeader("Authorization", "Basic " + btoa(getString(param.user) + ":" + getString(param.password)));
|
||||
},
|
||||
|
||||
success : function(response, textStatus, jqXHR) {
|
||||
if (param.success_callback !== undefined)
|
||||
param.success_callback(response);
|
||||
},
|
||||
success : function(response, textStatus, jqXHR) {
|
||||
resolve(response);
|
||||
},
|
||||
|
||||
error : function(jqXHR, textStatus, errorThrown) {
|
||||
var code = jqXHR.status;
|
||||
if (code == 200) {
|
||||
// happens sometimes when we get no response
|
||||
// (TODO: Fix server to return 204? instead)
|
||||
if (param.success_callback !== undefined)
|
||||
param.success_callback(null);
|
||||
return;
|
||||
error : function(jqXHR, textStatus, errorThrown) {
|
||||
var code = jqXHR.status;
|
||||
if (code == 200) {
|
||||
// happens sometimes when we get no response
|
||||
// (TODO: Fix server to return 204? instead)
|
||||
resolve(null);
|
||||
return;
|
||||
}
|
||||
if (code > 999 || code < 100)
|
||||
code = -1;
|
||||
var e = new Error(code);
|
||||
e.name = "HTTPError";
|
||||
reject(e);
|
||||
}
|
||||
if (code > 999 || code < 100)
|
||||
code = -1;
|
||||
if (param.error_callback !== undefined)
|
||||
param.error_callback(code);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
this.requestVerificationCode = function(number, success_callback, error_callback) {
|
||||
this.doAjax({
|
||||
doAjax({
|
||||
call : 'accounts',
|
||||
httpType : 'GET',
|
||||
urlParameters : '/sms/code/' + number,
|
||||
success_callback : success_callback,
|
||||
error_callback : error_callback
|
||||
}).then(function(response) {
|
||||
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 urlPrefix = single_device ? '/code/' : '/';
|
||||
|
||||
API.doAjax({
|
||||
doAjax({
|
||||
call : call,
|
||||
httpType : 'PUT',
|
||||
urlParameters : urlPrefix + code,
|
||||
|
@ -112,8 +117,12 @@ var API = new function() {
|
|||
jsonData : { signalingKey : btoa(getString(signaling_key)),
|
||||
supportsSms : false,
|
||||
fetchesMessages : true },
|
||||
success_callback : success_callback,
|
||||
error_callback : error_callback
|
||||
}).then(function(response) {
|
||||
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++)
|
||||
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};
|
||||
this.doAjax({
|
||||
doAjax({
|
||||
call : 'keys',
|
||||
httpType : 'PUT',
|
||||
do_auth : true,
|
||||
jsonData : keys,
|
||||
success_callback : success_callback,
|
||||
error_callback : error_callback
|
||||
}).then(function(response) {
|
||||
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.doAjax({
|
||||
doAjax({
|
||||
call : 'keys',
|
||||
httpType : 'GET',
|
||||
do_auth : true,
|
||||
urlParameters : "/" + getNumberFromString(number) + "/*",
|
||||
success_callback : function(response) {
|
||||
}).then(function(response) {
|
||||
//TODO: Do this conversion somewhere else?
|
||||
var res = response.keys;
|
||||
for (var i = 0; i < res.length; i++) {
|
||||
|
@ -149,12 +162,13 @@ var API = new function() {
|
|||
res[i].keyId = 0;
|
||||
}
|
||||
success_callback(res);
|
||||
},
|
||||
error_callback : error_callback
|
||||
}).catch(function(code) {
|
||||
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?
|
||||
for (var i = 0; i < messageArray.length; i++)
|
||||
messageArray[i].body = btoa(messageArray[i].body);
|
||||
|
@ -162,14 +176,12 @@ var API = new function() {
|
|||
if (messageArray[0].relay !== undefined)
|
||||
jsonData.relay = messageArray[0].relay;
|
||||
|
||||
this.doAjax({
|
||||
return doAjax({
|
||||
call : 'messages',
|
||||
httpType : 'PUT',
|
||||
urlParameters : '/' + destination,
|
||||
do_auth : true,
|
||||
jsonData : jsonData,
|
||||
success_callback : success_callback,
|
||||
error_callback : error_callback
|
||||
});
|
||||
};
|
||||
}
|
||||
}(); // API
|
||||
|
|
52
js/crypto.js
52
js/crypto.js
|
@ -115,16 +115,20 @@ window.crypto = (function() {
|
|||
if (privKey === undefined || privKey.byteLength != 32)
|
||||
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");
|
||||
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) {
|
||||
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);
|
||||
});
|
||||
} 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) {
|
||||
return createNewKeyPair(false).then(function(baseKey) {
|
||||
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
|
||||
session = crypto_storage.getSession(deviceObject.encodedNumber);
|
||||
session.pendingPreKey = baseKey.pubKey;
|
||||
|
@ -475,25 +481,27 @@ window.crypto = (function() {
|
|||
|
||||
var keys = {};
|
||||
keys.keys = [];
|
||||
var keysLeft = GENERATE_KEYS_KEYS_GENERATED;
|
||||
return new Promise(function(resolve) {
|
||||
for (var i = firstKeyId; i < firstKeyId + GENERATE_KEYS_KEYS_GENERATED; i++) {
|
||||
crypto_storage.getNewPubKeySTORINGPrivKey("preKey" + i, false).then(function(pubKey) {
|
||||
keys.keys[i] = {keyId: i, publicKey: pubKey, identityKey: identityKey};
|
||||
keysLeft--;
|
||||
if (keysLeft == 0) {
|
||||
// 0xFFFFFF == 16777215
|
||||
keys.lastResortKey = {keyId: 16777215, publicKey: crypto_storage.getStoredPubKey("preKey16777215"), identityKey: identityKey};//TODO: Rotate lastResortKey
|
||||
if (keys.lastResortKey.publicKey === undefined) {
|
||||
return crypto_storage.getNewPubKeySTORINGPrivKey("preKey16777215", false).then(function(pubKey) {
|
||||
keys.lastResortKey.publicKey = pubKey;
|
||||
resolve(keys);
|
||||
});
|
||||
} else
|
||||
resolve(keys);
|
||||
}
|
||||
|
||||
var generateKey = function(keyId) {
|
||||
return crypto_storage.getNewPubKeySTORINGPrivKey("preKey" + keyId, false).then(function(pubKey) {
|
||||
keys.keys[keyId] = {keyId: keyId, publicKey: pubKey, identityKey: identityKey};
|
||||
});
|
||||
};
|
||||
|
||||
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
|
||||
keys.lastResortKey = {keyId: 16777215, publicKey: crypto_storage.getStoredPubKey("preKey16777215"), identityKey: identityKey};//TODO: Rotate lastResortKey
|
||||
if (keys.lastResortKey.publicKey === undefined) {
|
||||
return crypto_storage.getNewPubKeySTORINGPrivKey("preKey16777215", false).then(function(pubKey) {
|
||||
keys.lastResortKey.publicKey = pubKey;
|
||||
return keys;
|
||||
});
|
||||
}
|
||||
} else
|
||||
return keys;
|
||||
});
|
||||
}
|
||||
if (identityKey === undefined)
|
||||
|
|
|
@ -14,14 +14,14 @@
|
|||
*/
|
||||
|
||||
var FakeWhisperAPI = function() {
|
||||
this.doAjax = function(param) {
|
||||
var doAjax = function(param) {
|
||||
if (param.success_callback) {
|
||||
setTimeout(param.success_callback, 100, param.response);
|
||||
}
|
||||
}
|
||||
|
||||
this.getKeysForNumber = function(number, success_callback, error_callback) {
|
||||
this.doAjax({ success_callback : success_callback,
|
||||
doAjax({ success_callback : success_callback,
|
||||
response : [{ identityKey : 1,
|
||||
deviceId : 1,
|
||||
publicKey : 1,
|
||||
|
@ -30,7 +30,7 @@ var FakeWhisperAPI = function() {
|
|||
}
|
||||
|
||||
this.sendMessages = function(jsonData, success_callback, error_callback) {
|
||||
this.doAjax({ success_callback : success_callback,
|
||||
doAjax({ success_callback : success_callback,
|
||||
response : { missingDeviceIds: [] }
|
||||
});
|
||||
}
|
||||
|
|
|
@ -181,10 +181,6 @@ function jsonThing(thing) {
|
|||
return JSON.stringify(ensureStringed(thing));
|
||||
}
|
||||
|
||||
function getArrayBuffer(string) {
|
||||
return base64DecToArr(btoa(string));
|
||||
}
|
||||
|
||||
function base64ToArrayBuffer(string) {
|
||||
return base64DecToArr(string);
|
||||
}
|
||||
|
@ -505,20 +501,10 @@ function getKeysForNumber(number, success_callback, error_callback) {
|
|||
function sendMessageToDevices(number, deviceObjectList, message, success_callback, error_callback) {
|
||||
var jsonData = [];
|
||||
var relay = undefined;
|
||||
var promises = [];
|
||||
|
||||
var doSend = function() {
|
||||
API.sendMessages(number, jsonData,
|
||||
function(result) {
|
||||
success_callback(result);
|
||||
}, function(code) {
|
||||
error_callback(code);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
var addEncryptionFor;
|
||||
addEncryptionFor = function(i) {
|
||||
crypto.encryptMessageFor(deviceObjectList[i], message).then(function(encryptedMsg) {
|
||||
var addEncryptionFor = function(i) {
|
||||
return crypto.encryptMessageFor(deviceObjectList[i], message).then(function(encryptedMsg) {
|
||||
jsonData[i] = {
|
||||
type: encryptedMsg.type,
|
||||
destination: deviceObjectList[i].encodedNumber,
|
||||
|
@ -531,27 +517,21 @@ function sendMessageToDevices(number, deviceObjectList, message, success_callbac
|
|||
jsonData[i].relay = deviceObjectList[i].relay;
|
||||
if (relay === undefined)
|
||||
relay = jsonData[i].relay;
|
||||
else if (relay != jsonData[i].relay) {
|
||||
error_callback("Mismatched relays for number " + number);
|
||||
return;
|
||||
}
|
||||
else if (relay != jsonData[i].relay)
|
||||
throw new Error("Mismatched relays for number " + number);
|
||||
} else {
|
||||
if (relay === undefined)
|
||||
relay = "";
|
||||
else if (relay != "") {
|
||||
error_callback("Mismatched relays for number " + number);
|
||||
return;
|
||||
}
|
||||
else if (relay != "")
|
||||
throw new Error("Mismatched relays for number " + number);
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -573,13 +553,14 @@ function sendMessageToNumbers(numbers, message, callback) {
|
|||
}
|
||||
|
||||
var doSendMessage = function(number, devicesForNumber, message) {
|
||||
sendMessageToDevices(number, devicesForNumber, message, function(result) {
|
||||
return sendMessageToDevices(number, devicesForNumber, message).then(function(result) {
|
||||
successfulNumbers[successfulNumbers.length] = number;
|
||||
numberCompleted();
|
||||
}, function(error_code) {
|
||||
//TODO: Re-request keys for number here
|
||||
if (error_code == 410 || error_code == 409) {}
|
||||
registerError(number, message);
|
||||
}).catch(function(error) {
|
||||
if (error instanceof Error && error.name == "HTTPError" && (error.message == 410 || error.message == 409)) {
|
||||
//TODO: Re-request keys for number here
|
||||
}
|
||||
registerError(number, error);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -591,11 +572,11 @@ function sendMessageToNumbers(numbers, message, callback) {
|
|||
getKeysForNumber(number, function(identity_key) {
|
||||
devicesForNumber = getDeviceObjectListFromNumber(number);
|
||||
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
|
||||
doSendMessage(number, devicesForNumber, message);
|
||||
}, 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
|
||||
doSendMessage(number, devicesForNumber, message);
|
||||
|
|
|
@ -85,7 +85,7 @@ $('#init-go').click(function() {
|
|||
|
||||
var register_keys_func = function() {
|
||||
$('#verify2done').html('done');
|
||||
crypto.generateKeys(function(keys) {
|
||||
crypto.generateKeys().then(function(keys) {
|
||||
$('#verify3done').html('done');
|
||||
API.registerKeys(keys,
|
||||
function(response) {
|
||||
|
|
Loading…
Reference in a new issue