Retry API, standardize <script> list
This commit is contained in:
parent
8f49d201e6
commit
cf35b7056f
7 changed files with 143 additions and 70 deletions
|
@ -14,8 +14,6 @@
|
|||
|
||||
<html>
|
||||
<head>
|
||||
<script type="text/javascript" src="js/webcrypto.js"></script>
|
||||
<script type="text/javascript" src="js/crypto.js"></script>
|
||||
<script type="text/javascript" src="js-deps/nacl-common.js"></script>
|
||||
<script type="text/javascript" src="js-deps/jquery.js"></script>
|
||||
<script type="text/javascript" src="js-deps/core.js"></script>
|
||||
|
@ -33,9 +31,12 @@
|
|||
<script type="text/javascript" src="js-deps/underscore.js"></script>
|
||||
<script type="text/javascript" src="js-deps/backbone.js"></script>
|
||||
<script type="text/javascript" src="js-deps/backbone.localStorage.js"></script>
|
||||
|
||||
<script type="text/javascript" src="js/helpers.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/models/messages.js"></script>
|
||||
<script type="text/javascript" src="js/models/threads.js"></script>
|
||||
<script type="text/javascript" src="js/helpers.js"></script>
|
||||
<script type="text/javascript" src="js/api.js"></script>
|
||||
<script type="text/javascript" src="js/chromium.js"></script>
|
||||
<script type="text/javascript" src="js/background.js"></script>
|
||||
|
|
|
@ -104,6 +104,8 @@ window.textsecure.api = function() {
|
|||
textsecure.throwHumanError(code, "HTTPError", "The server rejected our query, please file a bug report.");
|
||||
}
|
||||
} catch (e) {
|
||||
if (jqXHR.responseJSON)
|
||||
e.response = jqXHR.responseJSON;
|
||||
reject(e);
|
||||
}
|
||||
}
|
||||
|
|
41
js/crypto.js
41
js/crypto.js
|
@ -238,12 +238,6 @@ window.textsecure.crypto = function() {
|
|||
throw new Error("Datastore inconsistency: device was stored without identity key");
|
||||
}
|
||||
|
||||
// Used when device keys change - we assume key compromise so refuse all new messages
|
||||
self.forceRemoveAllSessions = function(encodedNumber) {
|
||||
textsecure.storage.removeEncrypted("session" + encodedNumber);
|
||||
}
|
||||
|
||||
|
||||
/*****************************
|
||||
*** Internal Crypto stuff ***
|
||||
*****************************/
|
||||
|
@ -407,7 +401,26 @@ window.textsecure.crypto = function() {
|
|||
session.indexInfo.closed = new Date().getTime();
|
||||
}
|
||||
|
||||
var initSessionFromPreKeyWhisperMessage = function(encodedNumber, message) {
|
||||
var initSessionFromPreKeyWhisperMessage;
|
||||
var decryptWhisperMessage;
|
||||
var handlePreKeyWhisperMessage = function(from, encodedMessage) {
|
||||
var preKeyProto = textsecure.protos.decodePreKeyWhisperMessageProtobuf(encodedMessage);
|
||||
return initSessionFromPreKeyWhisperMessage(from, preKeyProto).then(function(sessions) {
|
||||
return decryptWhisperMessage(from, getString(preKeyProto.message), sessions[0], preKeyProto.registrationId).then(function(result) {
|
||||
if (sessions[1] !== undefined)
|
||||
crypto_storage.saveSession(from, sessions[1]);
|
||||
return result;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var wipeIdentityAndTryMessageAgain = function(from, encodedMessage) {
|
||||
//TODO: Wipe identity key!
|
||||
return handlePreKeyWhisperMessage(from, encodedMessage);
|
||||
}
|
||||
textsecure.replay.registerReplayFunction(wipeIdentityAndTryMessageAgain, textsecure.replay.REPLAY_FUNCS.INIT_SESSION);
|
||||
|
||||
initSessionFromPreKeyWhisperMessage = function(encodedNumber, message) {
|
||||
var preKeyPair = crypto_storage.getAndRemovePreKeyPair(message.preKeyId);
|
||||
|
||||
var session = crypto_storage.getSessionOrIdentityKeyByBaseKey(encodedNumber, toArrayBuffer(message.baseKey));
|
||||
|
@ -428,8 +441,7 @@ window.textsecure.crypto = function() {
|
|||
closeSession(open_session); // To be returned and saved later
|
||||
} else {
|
||||
// ...otherwise create an error that the UI will pick up and ask the user if they want to re-negotiate
|
||||
// TODO: Save the message for possible later renegotiation
|
||||
textsecure.throwHumanError("Received message with unknown identity key", "WarnTryAgainError", "The identity of the sender has changed. This may be malicious, or the sender may have simply reinstalled TextSecure.");
|
||||
throw textsecure.createTryAgainError("Received message with unknown identity key", "The identity of the sender has changed. This may be malicious, or the sender may have simply reinstalled TextSecure.", textsecure.replay.REPLAY_FUNCS.INIT_SESSION, [encodedNumber, getString(message.encode())]);
|
||||
}
|
||||
}
|
||||
return initSession(false, preKeyPair, encodedNumber, toArrayBuffer(message.identityKey), toArrayBuffer(message.baseKey))
|
||||
|
@ -525,7 +537,7 @@ window.textsecure.crypto = function() {
|
|||
}
|
||||
|
||||
// returns decrypted protobuf
|
||||
var decryptWhisperMessage = function(encodedNumber, messageBytes, session, registrationId) {
|
||||
decryptWhisperMessage = function(encodedNumber, messageBytes, session, registrationId) {
|
||||
if (messageBytes[0] != String.fromCharCode((2 << 4) | 2))
|
||||
throw new Error("Bad version number on WhisperMessage");
|
||||
|
||||
|
@ -618,14 +630,7 @@ window.textsecure.crypto = function() {
|
|||
if (proto.message.readUint8() != (2 << 4 | 2))
|
||||
throw new Error("Bad version byte");
|
||||
var from = proto.source + "." + (proto.sourceDevice == null ? 0 : proto.sourceDevice);
|
||||
var preKeyProto = textsecure.protos.decodePreKeyWhisperMessageProtobuf(getString(proto.message));
|
||||
return initSessionFromPreKeyWhisperMessage(from, preKeyProto).then(function(sessions) {
|
||||
return decryptWhisperMessage(from, getString(preKeyProto.message), sessions[0], preKeyProto.registrationId).then(function(result) {
|
||||
if (sessions[1] !== undefined)
|
||||
crypto_storage.saveSession(proto.source, sessions[1]);
|
||||
return result;
|
||||
});
|
||||
});
|
||||
return handlePreKeyWhisperMessage(from, getString(proto.message));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
115
js/helpers.js
115
js/helpers.js
|
@ -389,8 +389,8 @@ window.textsecure.storage = function() {
|
|||
var devicesRemoved = 0;
|
||||
for (i in map.devices) {
|
||||
var keep = true;
|
||||
for (idToRemove in deviceIdsToRemove)
|
||||
if (map.devices[i].encodedNumber == number + "." + idToRemove)
|
||||
for (j in deviceIdsToRemove)
|
||||
if (map.devices[i].encodedNumber == number + "." + deviceIdsToRemove[j])
|
||||
keep = false;
|
||||
|
||||
if (keep)
|
||||
|
@ -459,6 +459,41 @@ window.textsecure.nacl = function() {
|
|||
//TODO: Some kind of textsecure.init(use_nacl)
|
||||
window.textsecure.registerOnLoadFunction = window.textsecure.nacl.registerOnLoadFunction;
|
||||
|
||||
window.textsecure.replay = function() {
|
||||
var self = {};
|
||||
|
||||
self.REPLAY_FUNCS = {
|
||||
SEND_MESSAGE: 1,
|
||||
INIT_SESSION: 2,
|
||||
}
|
||||
|
||||
var functions = {};
|
||||
|
||||
self.registerReplayFunction = function(func, functionCode) {
|
||||
functions[functionCode] = func;
|
||||
}
|
||||
|
||||
self.replayError = function(replayData) {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
args.shift();
|
||||
args = replayData.args.concat(args);
|
||||
functions[replayData.replayFunction].apply(window, args);
|
||||
}
|
||||
|
||||
self.createReplayableError = function(shortMsg, longMsg, replayFunction, args) {
|
||||
var e = new Error(shortMsg);
|
||||
e.name = "ReplayableError";
|
||||
e.humanError = e.longMessage = longMsg;
|
||||
e.replayData = { replayFunction: replayFunction, args: args };
|
||||
e.replay = function() {
|
||||
self.replayError(e.replayData);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
return self;
|
||||
}();
|
||||
|
||||
// message_callback({message: decryptedMessage, pushMessage: server-providedPushMessage})
|
||||
window.textsecure.subscribeToPush = function() {
|
||||
var subscribeToPushMessageSemaphore = 0;
|
||||
|
@ -539,13 +574,13 @@ window.textsecure.sendMessage = function() {
|
|||
var identityKey = getString(response[0].identityKey);
|
||||
for (i in response)
|
||||
if (getString(response[i].identityKey) != identityKey)
|
||||
throw new Error("Identity key changed");
|
||||
throw new Error("Identity key not consistent");
|
||||
|
||||
for (i in response) {
|
||||
var updateDevice = (updateDevices === undefined);
|
||||
if (!updateDevice)
|
||||
for (deviceId in updateDevices)
|
||||
if (deviceId == response[i].deviceId)
|
||||
for (j in updateDevices)
|
||||
if (updateDevices[j] == response[i].deviceId)
|
||||
updateDevice = true;
|
||||
|
||||
if (updateDevice)
|
||||
|
@ -602,6 +637,13 @@ window.textsecure.sendMessage = function() {
|
|||
});
|
||||
}
|
||||
|
||||
var tryMessageAgain = function(number, encodedMessage, callback) {
|
||||
//TODO: Wipe identity key!
|
||||
var message = textsecure.protos.decodePushMessageContentProtobuf(encodedMessage);
|
||||
textsecure.sendMessage([number], message, callback);
|
||||
}
|
||||
textsecure.replay.registerReplayFunction(tryMessageAgain, textsecure.replay.SEND_MESSAGE);
|
||||
|
||||
return function(numbers, message, callback) {
|
||||
var numbersCompleted = 0;
|
||||
var errors = [];
|
||||
|
@ -620,30 +662,41 @@ window.textsecure.sendMessage = function() {
|
|||
numberCompleted();
|
||||
}
|
||||
|
||||
var doSendMessage = function(number, devicesForNumber, message) {
|
||||
var doSendMessage;
|
||||
var reloadDevicesAndSend = function(number, recurse) {
|
||||
return function() {
|
||||
var devicesForNumber = textsecure.storage.devices.getDeviceObjectsForNumber(number);
|
||||
if (devicesForNumber.length == 0)
|
||||
registerError(number, "Go empty device list when loading device keys", null);
|
||||
else
|
||||
doSendMessage(number, devicesForNumber, recurse);
|
||||
}
|
||||
}
|
||||
|
||||
doSendMessage = function(number, devicesForNumber, recurse) {
|
||||
return sendMessageToDevices(number, devicesForNumber, message).then(function(result) {
|
||||
successfulNumbers[successfulNumbers.length] = number;
|
||||
numberCompleted();
|
||||
}).catch(function(error) {
|
||||
if (error instanceof Error && error.name == "HTTPError" && (error.message == 410 || error.message == 409)) {
|
||||
if (!recurse)
|
||||
return registerError(number, "Hit retry limit attempting to reload device list", error);
|
||||
|
||||
if (error.message == 409)
|
||||
textsecure.storage.devices.removeDeviceIdsForNumber(number, error.response.extraDevices);
|
||||
|
||||
var resetDevices = ((error.message == 410) ? error.response.staleDevices : error.response.missingDevices);
|
||||
getKeysForNumber(number, resetDevices).then(function() {
|
||||
if (error.message == 409)
|
||||
resetDevices = resetDevices.concat(error.response.extraDevices);
|
||||
|
||||
textsecure.storage.devices.removeDeviceIdsForNumber(number, resetDevices);
|
||||
for (i in resetDevices)
|
||||
textsecure.crypto.forceRemoveAllSessions(number + "." + resetDevices[i]);
|
||||
|
||||
//TODO: Try again
|
||||
}).catch(function(error) {
|
||||
if (error.message !== "Identity key changed")
|
||||
registerError(number, "Failed to reload device keys", error);
|
||||
else {
|
||||
// TODO: Identity key changed, check which devices it changed for and get upset
|
||||
registerError(number, "Identity key changed!!!!", error);
|
||||
}
|
||||
});
|
||||
getKeysForNumber(number, resetDevices)
|
||||
.then(reloadDevicesAndSend(number, false))
|
||||
.catch(function(error) {
|
||||
if (error.message !== "Identity key changed")
|
||||
registerError(number, "Failed to reload device keys", error);
|
||||
else {
|
||||
error = textsecure.replay.createReplayableError("The destination's identity key has changed", "The identity of the destination has changed. This may be malicious, or the destination may have simply reinstalled TextSecure.",
|
||||
textsecure.replay.SEND_MESSAGE, [number, getString(message.encode())]);
|
||||
registerError(number, "Identity key changed", error);
|
||||
}
|
||||
});
|
||||
} else
|
||||
registerError(number, "Failed to create or send message", error);
|
||||
});
|
||||
|
@ -654,17 +707,13 @@ window.textsecure.sendMessage = function() {
|
|||
var devicesForNumber = textsecure.storage.devices.getDeviceObjectsForNumber(number);
|
||||
|
||||
if (devicesForNumber.length == 0) {
|
||||
getKeysForNumber(number).then(function() {
|
||||
devicesForNumber = textsecure.storage.devices.getDeviceObjectsForNumber(number);
|
||||
if (devicesForNumber.length == 0)
|
||||
registerError(number, "Failed to retreive new device keys for number " + number, null);
|
||||
else
|
||||
doSendMessage(number, devicesForNumber, message);
|
||||
}).catch(function(error) {
|
||||
registerError(number, "Failed to retreive new device keys for number " + number, error);
|
||||
});
|
||||
getKeysForNumber(number)
|
||||
.then(reloadDevicesAndSend(number, true))
|
||||
.catch(function(error) {
|
||||
registerError(number, "Failed to retreive new device keys for number " + number, error);
|
||||
});
|
||||
} else
|
||||
doSendMessage(number, devicesForNumber, message);
|
||||
doSendMessage(number, devicesForNumber, true);
|
||||
}
|
||||
}
|
||||
}();
|
||||
|
|
12
options.html
12
options.html
|
@ -38,7 +38,6 @@
|
|||
<div id="setup-complete" style="display: none;">
|
||||
<h2>You are now registered on TextSecure with number <span id="complete-number"></span></h2>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="js-deps/nacl-common.js"></script>
|
||||
<script type="text/javascript" src="js-deps/jquery.js"></script>
|
||||
<script type="text/javascript" src="js-deps/core.js"></script>
|
||||
|
@ -53,9 +52,16 @@
|
|||
<script type="text/javascript" src="js-deps/Long.min.js"></script>
|
||||
<script type="text/javascript" src="js-deps/ByteBuffer.min.js"></script>
|
||||
<script type="text/javascript" src="js-deps/ProtoBuf.min.js"></script>
|
||||
<script type="text/javascript" src="js/crypto.js"></script>
|
||||
<script type="text/javascript" src="js/api.js"></script>
|
||||
<script type="text/javascript" src="js-deps/underscore.js"></script>
|
||||
<script type="text/javascript" src="js-deps/backbone.js"></script>
|
||||
<script type="text/javascript" src="js-deps/backbone.localStorage.js"></script>
|
||||
|
||||
<script type="text/javascript" src="js/helpers.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/models/messages.js"></script>
|
||||
<script type="text/javascript" src="js/models/threads.js"></script>
|
||||
<script type="text/javascript" src="js/api.js"></script>
|
||||
<script type="text/javascript" src="js/chromium.js"></script>
|
||||
<script type="text/javascript" src="js/options.js"></script>
|
||||
</body>
|
||||
|
|
25
popup.html
25
popup.html
|
@ -44,19 +44,8 @@
|
|||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="js/webcrypto.js"></script>
|
||||
<script type="text/javascript" src="js/crypto.js"></script>
|
||||
<script type="text/javascript" src="js-deps/nacl-common.js"></script>
|
||||
<script type="text/javascript" src="js-deps/jquery.js"></script>
|
||||
<script type="text/javascript" src="js-deps/underscore.js"></script>
|
||||
<script type="text/javascript" src="js-deps/backbone.js"></script>
|
||||
<script type="text/javascript" src="js-deps/backbone.localStorage.js"></script>
|
||||
<script type="text/javascript" src="js/models/messages.js"></script>
|
||||
<script type="text/javascript" src="js/models/threads.js"></script>
|
||||
<script type="text/javascript" src="js/views/notifications.js"></script>
|
||||
<script type="text/javascript" src="js/views/message.js"></script>
|
||||
<script type="text/javascript" src="js/views/conversation.js"></script>
|
||||
<script type="text/javascript" src="js/views/messages.js"></script>
|
||||
<script type="text/javascript" src="js-deps/core.js"></script>
|
||||
<script type="text/javascript" src="js-deps/enc-base64.js"></script>
|
||||
<script type="text/javascript" src="js-deps/cipher-core.js"></script>
|
||||
|
@ -69,8 +58,22 @@
|
|||
<script type="text/javascript" src="js-deps/Long.min.js"></script>
|
||||
<script type="text/javascript" src="js-deps/ByteBuffer.min.js"></script>
|
||||
<script type="text/javascript" src="js-deps/ProtoBuf.min.js"></script>
|
||||
<script type="text/javascript" src="js-deps/underscore.js"></script>
|
||||
<script type="text/javascript" src="js-deps/backbone.js"></script>
|
||||
<script type="text/javascript" src="js-deps/backbone.localStorage.js"></script>
|
||||
|
||||
<script type="text/javascript" src="js/helpers.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/models/messages.js"></script>
|
||||
<script type="text/javascript" src="js/models/threads.js"></script>
|
||||
<script type="text/javascript" src="js/api.js"></script>
|
||||
<script type="text/javascript" src="js/chromium.js"></script>
|
||||
|
||||
<script type="text/javascript" src="js/views/notifications.js"></script>
|
||||
<script type="text/javascript" src="js/views/message.js"></script>
|
||||
<script type="text/javascript" src="js/views/conversation.js"></script>
|
||||
<script type="text/javascript" src="js/views/messages.js"></script>
|
||||
<script type="text/javascript" src="js/popup.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
11
test.html
11
test.html
|
@ -29,20 +29,27 @@
|
|||
<script type="text/javascript" src="js-deps/core.js"></script>
|
||||
<script type="text/javascript" src="js-deps/enc-base64.js"></script>
|
||||
<script type="text/javascript" src="js-deps/cipher-core.js"></script>
|
||||
<script type="text/javascript" src="js-deps/lib-typedarrays.js"></script>
|
||||
<script type="text/javascript" src="js-deps/aes.js"></script>
|
||||
<script type="text/javascript" src="js-deps/mode-ctr-min.js"></script>
|
||||
<script type="text/javascript" src="js-deps/pad-nopadding.js"></script>
|
||||
<script type="text/javascript" src="js-deps/hmac-sha256.js"></script>
|
||||
<script type="text/javascript" src="js-deps/curve255.js"></script>
|
||||
<script type="text/javascript" src="js-deps/lib-typedarrays.js"></script>
|
||||
<script type="text/javascript" src="js-deps/Long.min.js"></script>
|
||||
<script type="text/javascript" src="js-deps/ByteBuffer.min.js"></script>
|
||||
<script type="text/javascript" src="js-deps/ProtoBuf.min.js"></script>
|
||||
<script type="text/javascript" src="js-deps/underscore.js"></script>
|
||||
<script type="text/javascript" src="js-deps/backbone.js"></script>
|
||||
<script type="text/javascript" src="js-deps/backbone.localStorage.js"></script>
|
||||
|
||||
<script type="text/javascript" src="js/helpers.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/helpers.js"></script>
|
||||
<script type="text/javascript" src="js/models/messages.js"></script>
|
||||
<script type="text/javascript" src="js/models/threads.js"></script>
|
||||
<script type="text/javascript" src="js/api.js"></script>
|
||||
<script type="text/javascript" src="js/chromium.js"></script>
|
||||
|
||||
<script type="text/javascript" src="js/fake_api.js"></script>
|
||||
<script type="text/javascript" src="js/test.js"></script>
|
||||
</body>
|
||||
|
|
Loading…
Reference in a new issue