Skip to content

Commit

Permalink
Add replayable error for signed key failure
Browse files Browse the repository at this point in the history
Disable message sending if signed key updates fail too many times, but
allow the user to retry sending.

// FREEBIE
  • Loading branch information
liliakai committed Feb 17, 2017
1 parent e0fd188 commit cd0fe70
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 7 deletions.
31 changes: 31 additions & 0 deletions js/libtextsecure.js
Expand Up @@ -11,6 +11,7 @@
INIT_SESSION: 2,
TRANSMIT_MESSAGE: 3,
REBUILD_MESSAGE: 4,
RETRY_SEND_MESSAGE_PROTO: 5
};
window.textsecure = window.textsecure || {};
window.textsecure.replay = {
Expand Down Expand Up @@ -89,6 +90,17 @@
SendMessageNetworkError.prototype = new ReplayableError();
SendMessageNetworkError.prototype.constructor = SendMessageNetworkError;

function SignedPreKeyRotationError(numbers, message, timestamp) {
ReplayableError.call(this, {
functionCode : Type.RETRY_SEND_MESSAGE_PROTO,
args : [numbers, message, timestamp]
});
this.name = 'SignedPreKeyRotationError';
this.message = "Too many signed prekey rotation failures";
}
SignedPreKeyRotationError.prototype = new ReplayableError();
SignedPreKeyRotationError.prototype.constructor = SignedPreKeyRotationError;

function MessageError(message, httpError) {
ReplayableError.call(this, {
functionCode : Type.REBUILD_MESSAGE,
Expand Down Expand Up @@ -119,6 +131,7 @@
window.textsecure.ReplayableError = ReplayableError;
window.textsecure.OutgoingMessageError = OutgoingMessageError;
window.textsecure.MessageError = MessageError;
window.textsecure.SignedPreKeyRotationError = SignedPreKeyRotationError;

})();

Expand Down Expand Up @@ -39014,6 +39027,11 @@ MessageSender.prototype = {
}.bind(this));
},
sendMessageProto: function(timestamp, numbers, message, callback) {
var rejections = textsecure.storage.get('signedKeyRotationRejected', 0);
if (rejections > 5) {
throw new textsecure.SignedPreKeyRotationError(numbers, message.toArrayBuffer(), timestamp);
}

var outgoing = new OutgoingMessage(this.server, timestamp, numbers, message, callback);

numbers.forEach(function(number) {
Expand All @@ -39023,6 +39041,18 @@ MessageSender.prototype = {
}.bind(this));
},

retrySendMessageProto: function(numbers, encodedMessage, timestamp) {
var proto = textsecure.protobuf.DataMessage.decode(encodedMessage);
return new Promise(function(resolve, reject) {
this.sendMessageProto(timestamp, numbers, proto, function(res) {
if (res.errors.length > 0)
reject(res);
else
resolve(res);
});
}.bind(this));
},

sendIndividualProto: function(number, proto, timestamp) {
return new Promise(function(resolve, reject) {
this.sendMessageProto(timestamp, [number], proto, function(res) {
Expand Down Expand Up @@ -39330,6 +39360,7 @@ textsecure.MessageSender = function(url, ports, username, password, attachment_s
textsecure.replay.registerFunction(sender.tryMessageAgain.bind(sender), textsecure.replay.Type.ENCRYPT_MESSAGE);
textsecure.replay.registerFunction(sender.retransmitMessage.bind(sender), textsecure.replay.Type.TRANSMIT_MESSAGE);
textsecure.replay.registerFunction(sender.sendMessage.bind(sender), textsecure.replay.Type.REBUILD_MESSAGE);
textsecure.replay.registerFunction(sender.retrySendMessageProto.bind(sender), textsecure.replay.Type.RETRY_SEND_MESSAGE_PROTO);

this.sendExpirationTimerUpdateToNumber = sender.sendExpirationTimerUpdateToNumber.bind(sender);
this.sendExpirationTimerUpdateToGroup = sender.sendExpirationTimerUpdateToGroup .bind(sender);
Expand Down
15 changes: 13 additions & 2 deletions js/models/messages.js
Expand Up @@ -211,6 +211,9 @@
if (result instanceof Error) {
errors = [result];
this.saveErrors(errors);
if (result.name === 'SignedPreKeyRotationError') {
getAccountManager().rotateSignedPreKey();
}
} else {
errors = result.errors;
this.saveErrors(errors);
Expand Down Expand Up @@ -283,7 +286,8 @@
var error = _.find(this.get('errors'), function(e) {
return (e.name === 'MessageError' ||
e.name === 'OutgoingMessageError' ||
e.name === 'SendMessageNetworkError');
e.name === 'SendMessageNetworkError' ||
e.name === 'SignedPreKeyRotationError');
});
return !!error;
},
Expand All @@ -292,11 +296,18 @@
return e.number === number &&
(e.name === 'MessageError' ||
e.name === 'OutgoingMessageError' ||
e.name === 'SendMessageNetworkError');
e.name === 'SendMessageNetworkError' ||
e.name === 'SignedPreKeyRotationError');
});
this.set({errors: errors[1]});
return errors[0][0];
},
isReplayableError: function(e) {
return (e.name === 'MessageError' ||
e.name === 'OutgoingMessageError' ||
e.name === 'SendMessageNetworkError' ||
e.name === 'SignedPreKeyRotationError');
},

resend: function(number) {
var error = this.removeOutgoingErrors(number);
Expand Down
7 changes: 2 additions & 5 deletions js/views/message_view.js
Expand Up @@ -131,11 +131,8 @@
'click .error-message': 'select'
},
retryMessage: function() {
var retrys = _.filter(this.model.get('errors'), function(e) {
return (e.name === 'MessageError' ||
e.name === 'OutgoingMessageError' ||
e.name === 'SendMessageNetworkError');
});
var retrys = _.filter(this.model.get('errors'),
this.model.isReplayableError.bind(this.model));
_.map(retrys, 'number').forEach(function(number) {
this.model.resend(number);
}.bind(this));
Expand Down
13 changes: 13 additions & 0 deletions libtextsecure/errors.js
Expand Up @@ -10,6 +10,7 @@
INIT_SESSION: 2,
TRANSMIT_MESSAGE: 3,
REBUILD_MESSAGE: 4,
RETRY_SEND_MESSAGE_PROTO: 5
};
window.textsecure = window.textsecure || {};
window.textsecure.replay = {
Expand Down Expand Up @@ -88,6 +89,17 @@
SendMessageNetworkError.prototype = new ReplayableError();
SendMessageNetworkError.prototype.constructor = SendMessageNetworkError;

function SignedPreKeyRotationError(numbers, message, timestamp) {
ReplayableError.call(this, {
functionCode : Type.RETRY_SEND_MESSAGE_PROTO,
args : [numbers, message, timestamp]
});
this.name = 'SignedPreKeyRotationError';
this.message = "Too many signed prekey rotation failures";
}
SignedPreKeyRotationError.prototype = new ReplayableError();
SignedPreKeyRotationError.prototype.constructor = SignedPreKeyRotationError;

function MessageError(message, httpError) {
ReplayableError.call(this, {
functionCode : Type.REBUILD_MESSAGE,
Expand Down Expand Up @@ -118,5 +130,6 @@
window.textsecure.ReplayableError = ReplayableError;
window.textsecure.OutgoingMessageError = OutgoingMessageError;
window.textsecure.MessageError = MessageError;
window.textsecure.SignedPreKeyRotationError = SignedPreKeyRotationError;

})();
18 changes: 18 additions & 0 deletions libtextsecure/sendmessage.js
Expand Up @@ -183,6 +183,11 @@ MessageSender.prototype = {
}.bind(this));
},
sendMessageProto: function(timestamp, numbers, message, callback) {
var rejections = textsecure.storage.get('signedKeyRotationRejected', 0);
if (rejections > 5) {
throw new textsecure.SignedPreKeyRotationError(numbers, message.toArrayBuffer(), timestamp);
}

var outgoing = new OutgoingMessage(this.server, timestamp, numbers, message, callback);

numbers.forEach(function(number) {
Expand All @@ -192,6 +197,18 @@ MessageSender.prototype = {
}.bind(this));
},

retrySendMessageProto: function(numbers, encodedMessage, timestamp) {
var proto = textsecure.protobuf.DataMessage.decode(encodedMessage);
return new Promise(function(resolve, reject) {
this.sendMessageProto(timestamp, numbers, proto, function(res) {
if (res.errors.length > 0)
reject(res);
else
resolve(res);
});
}.bind(this));
},

sendIndividualProto: function(number, proto, timestamp) {
return new Promise(function(resolve, reject) {
this.sendMessageProto(timestamp, [number], proto, function(res) {
Expand Down Expand Up @@ -499,6 +516,7 @@ textsecure.MessageSender = function(url, ports, username, password, attachment_s
textsecure.replay.registerFunction(sender.tryMessageAgain.bind(sender), textsecure.replay.Type.ENCRYPT_MESSAGE);
textsecure.replay.registerFunction(sender.retransmitMessage.bind(sender), textsecure.replay.Type.TRANSMIT_MESSAGE);
textsecure.replay.registerFunction(sender.sendMessage.bind(sender), textsecure.replay.Type.REBUILD_MESSAGE);
textsecure.replay.registerFunction(sender.retrySendMessageProto.bind(sender), textsecure.replay.Type.RETRY_SEND_MESSAGE_PROTO);

this.sendExpirationTimerUpdateToNumber = sender.sendExpirationTimerUpdateToNumber.bind(sender);
this.sendExpirationTimerUpdateToGroup = sender.sendExpirationTimerUpdateToGroup .bind(sender);
Expand Down

0 comments on commit cd0fe70

Please sign in to comment.