Refactor textsecure.protos -> textsecure.protobuf

DRY up protobuf declarations and move to a slightly briefer naming
convention.

Also dropped some ArrayBuffer -> string conversions as
ProtoBuf.js handles ArrayBuffers just fine, and in fact, more
efficiently than strings.

Finally, dropped the btoa() wrappers, because that incurs an extra
string -> string conversion before the protobuf's internal string ->
array buffer conversion. In lieu of btoa, we can simply pass in the
optional string encoding argument to the protobuf's decode method,
which in these cases should be 'binary'.

Related: #17
This commit is contained in:
lilia 2014-10-20 01:21:23 -07:00
parent 03cc667e48
commit 1023ea1732
9 changed files with 79 additions and 96 deletions

View file

@ -27,6 +27,7 @@
<script type="text/javascript" src="js-deps/backbone.localStorage.js"></script>
<script type="text/javascript" src="js-deps/libphonenumber_api-compiled.js"></script>
<script type="text/javascript" src="js/protobufs.js"></script>
<script type="text/javascript" src="js/helpers.js"></script>
<script type="text/javascript" src="js/libphonenumber-util.js"></script>
<script type="text/javascript" src="js/webcrypto.js"></script>

View file

@ -129,6 +129,7 @@
<script type="text/javascript" src="js-deps/mustache.js"></script>
<script type="text/javascript" src="js-deps/libphonenumber_api-compiled.js"></script>
<script type="text/javascript" src="js/protobufs.js"></script>
<script type="text/javascript" src="js/helpers.js"></script>
<script type="text/javascript" src="js/libphonenumber-util.js"></script>
<script type="text/javascript" src="js/webcrypto.js"></script>

View file

@ -536,7 +536,7 @@ window.textsecure.crypto = function() {
var initSessionFromPreKeyWhisperMessage;
var decryptWhisperMessage;
var handlePreKeyWhisperMessage = function(from, encodedMessage) {
var preKeyProto = textsecure.protos.decodePreKeyWhisperMessageProtobuf(encodedMessage);
var preKeyProto = textsecure.protobuf.PreKeyWhisperMessage.decode(encodedMessage, 'binary');
return initSessionFromPreKeyWhisperMessage(from, preKeyProto).then(function(sessions) {
return decryptWhisperMessage(from, getString(preKeyProto.message), sessions[0], preKeyProto.registrationId).then(function(result) {
if (sessions[1] !== undefined)
@ -662,7 +662,7 @@ window.textsecure.crypto = function() {
var messageProto = messageBytes.substring(1, messageBytes.length - 8);
var mac = messageBytes.substring(messageBytes.length - 8, messageBytes.length);
var message = textsecure.protos.decodeWhisperMessageProtobuf(messageProto);
var message = textsecure.protobuf.WhisperMessage.decode(messageProto, 'binary');
var remoteEphemeralKey = toArrayBuffer(message.ephemeralKey);
if (session === undefined) {
@ -704,10 +704,10 @@ window.textsecure.crypto = function() {
delete session['pendingPreKey'];
var finalMessage = textsecure.protos.decodePushMessageContentProtobuf(getString(plaintext));
var finalMessage = textsecure.protobuf.PushMessageContent.decode(plaintext);
if ((finalMessage.flags & textsecure.protos.PushMessageContentProtobuf.Flags.END_SESSION)
== textsecure.protos.PushMessageContentProtobuf.Flags.END_SESSION)
if ((finalMessage.flags & textsecure.protobuf.PushMessageContent.Flags.END_SESSION)
== textsecure.protobuf.PushMessageContent.Flags.END_SESSION)
closeSession(session, true);
removeOldChains(session);
@ -778,17 +778,17 @@ window.textsecure.crypto = function() {
self.handleIncomingPushMessageProto = function(proto) {
switch(proto.type) {
case textsecure.protos.IncomingPushMessageProtobuf.Type.PLAINTEXT:
return Promise.resolve(textsecure.protos.decodePushMessageContentProtobuf(getString(proto.message)));
case textsecure.protos.IncomingPushMessageProtobuf.Type.CIPHERTEXT:
case textsecure.protobuf.IncomingPushMessageSignal.Type.PLAINTEXT:
return Promise.resolve(textsecure.protobuf.PushMessageContent.decode(proto.message));
case textsecure.protobuf.IncomingPushMessageSignal.Type.CIPHERTEXT:
var from = proto.source + "." + (proto.sourceDevice == null ? 0 : proto.sourceDevice);
return decryptWhisperMessage(from, getString(proto.message));
case textsecure.protos.IncomingPushMessageProtobuf.Type.PREKEY_BUNDLE:
case textsecure.protobuf.IncomingPushMessageSignal.Type.PREKEY_BUNDLE:
if (proto.message.readUint8() != ((3 << 4) | 3))
throw new Error("Bad version byte");
var from = proto.source + "." + (proto.sourceDevice == null ? 0 : proto.sourceDevice);
return handlePreKeyWhisperMessage(from, getString(proto.message));
case textsecure.protos.IncomingPushMessageProtobuf.Type.RECEIPT:
case textsecure.protobuf.IncomingPushMessageSignal.Type.RECEIPT:
return Promise.resolve(null);
default:
return new Promise(function(resolve, reject) { reject(new Error("Unknown message type")); });
@ -800,7 +800,7 @@ window.textsecure.crypto = function() {
var session = crypto_storage.getOpenSession(deviceObject.encodedNumber);
var doEncryptPushMessageContent = function() {
var msg = new textsecure.protos.WhisperMessageProtobuf();
var msg = new textsecure.protobuf.WhisperMessage();
var plaintext = toArrayBuffer(pushMessageContent.encode());
var paddedPlaintext = new Uint8Array(Math.ceil((plaintext.byteLength + 1) / 160.0) * 160);
@ -850,7 +850,7 @@ window.textsecure.crypto = function() {
});
}
var preKeyMsg = new textsecure.protos.PreKeyWhisperMessageProtobuf();
var preKeyMsg = new textsecure.protobuf.PreKeyWhisperMessage();
preKeyMsg.identityKey = toArrayBuffer(crypto_storage.getIdentityKey().pubKey);
preKeyMsg.registrationId = textsecure.storage.getUnencrypted("registrationId");
@ -957,7 +957,7 @@ window.textsecure.crypto = function() {
return verifyMAC(ivAndCiphertext, ecRes[1], mac).then(function() {
window.crypto.subtle.decrypt({name: "AES-CBC", iv: iv}, ecRes[0], ciphertext).then(function(plaintext) {
var identityKeyMsg = textsecure.protos.decodeIdentityKeyProtobuf(getString(plaintext));
var identityKeyMsg = textsecure.protobuf.IdentityKey.decode(plaintext);
privToPub(toArrayBuffer(identityKeyMsg.identityKey)).then(function(identityKeyPair) {
crypto_storage.putKeyPair("identityKey", identityKeyPair);

View file

@ -167,49 +167,6 @@ function base64ToArrayBuffer(string) {
return base64DecToArr(string);
}
// Protobuf decoding
//TODO: throw on missing fields everywhere
window.textsecure.protos = function() {
var self = {};
self.IncomingPushMessageProtobuf = dcodeIO.ProtoBuf.loadProtoFile("protos/IncomingPushMessageSignal.proto").build("textsecure.IncomingPushMessageSignal");
self.decodeIncomingPushMessageProtobuf = function(string) {
return self.IncomingPushMessageProtobuf.decode(btoa(string));
}
self.PushMessageContentProtobuf = dcodeIO.ProtoBuf.loadProtoFile("protos/IncomingPushMessageSignal.proto").build("textsecure.PushMessageContent");
self.decodePushMessageContentProtobuf = function(string) {
return self.PushMessageContentProtobuf.decode(btoa(string));
}
self.WhisperMessageProtobuf = dcodeIO.ProtoBuf.loadProtoFile("protos/WhisperTextProtocol.proto").build("textsecure.WhisperMessage");
self.decodeWhisperMessageProtobuf = function(string) {
return self.WhisperMessageProtobuf.decode(btoa(string));
}
self.PreKeyWhisperMessageProtobuf = dcodeIO.ProtoBuf.loadProtoFile("protos/WhisperTextProtocol.proto").build("textsecure.PreKeyWhisperMessage");
self.decodePreKeyWhisperMessageProtobuf = function(string) {
return self.PreKeyWhisperMessageProtobuf.decode(btoa(string));
}
self.DeviceInitProtobuf = dcodeIO.ProtoBuf.loadProtoFile("protos/DeviceMessages.proto").build("textsecure.DeviceInit");
self.decodeDeviceInitProtobuf = function(string) {
return self.DeviceInitProtobuf.decode(btoa(string));
}
self.IdentityKeyProtobuf = dcodeIO.ProtoBuf.loadProtoFile("protos/DeviceMessages.proto").build("textsecure.IdentityKey");
self.decodeIdentityKeyProtobuf = function(string) {
return self.IdentityKeyProtobuf.decode(btoa(string));
}
self.DeviceControlProtobuf = dcodeIO.ProtoBuf.loadProtoFile("protos/DeviceMessages.proto").build("textsecure.DeviceControl");
self.decodeDeviceControlProtobuf = function(string) {
return self.DeviceControlProtobuf.decode(btoa(string));
}
return self;
}();
// Number formatting utils
window.textsecure.utils = function() {
var self = {};
@ -611,7 +568,7 @@ window.textsecure.subscribeToPush = function(message_callback) {
socket.onmessage = function(message) {
textsecure.crypto.decryptWebsocketMessage(message.message).then(function(plaintext) {
var proto = textsecure.protos.decodeIncomingPushMessageProtobuf(getString(plaintext));
var proto = textsecure.protobuf.IncomingPushMessageSignal.decode(plaintext);
// 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
console.log("Successfully decoded message with id: " + message.id);
@ -629,8 +586,8 @@ window.textsecure.subscribeToPush = function(message_callback) {
if (decrypted.flags == null)
decrypted.flags = 0;
if ((decrypted.flags & textsecure.protos.PushMessageContentProtobuf.Flags.END_SESSION)
== textsecure.protos.PushMessageContentProtobuf.Flags.END_SESSION)
if ((decrypted.flags & textsecure.protobuf.PushMessageContent.Flags.END_SESSION)
== textsecure.protobuf.PushMessageContent.Flags.END_SESSION)
return;
if (decrypted.flags != 0)
throw new Error("Unknown flags in message");
@ -649,7 +606,7 @@ window.textsecure.subscribeToPush = function(message_callback) {
decrypted.group.id = getString(decrypted.group.id);
var existingGroup = textsecure.storage.groups.getNumbers(decrypted.group.id);
if (existingGroup === undefined) {
if (decrypted.group.type != textsecure.protos.PushMessageContentProtobuf.GroupContext.Type.UPDATE)
if (decrypted.group.type != textsecure.protobuf.PushMessageContent.GroupContext.Type.UPDATE)
throw new Error("Got message for unknown group");
textsecure.storage.groups.createNewGroup(decrypted.group.members, decrypted.group.id);
} else {
@ -659,7 +616,7 @@ window.textsecure.subscribeToPush = function(message_callback) {
throw new Error("Sender was not a member of the group they were sending from");
switch(decrypted.group.type) {
case textsecure.protos.PushMessageContentProtobuf.GroupContext.Type.UPDATE:
case textsecure.protobuf.PushMessageContent.GroupContext.Type.UPDATE:
if (decrypted.group.avatar !== null)
promises.push(handleAttachment(decrypted.group.avatar));
@ -682,12 +639,12 @@ window.textsecure.subscribeToPush = function(message_callback) {
decrypted.attachments = [];
break;
case textsecure.protos.PushMessageContentProtobuf.GroupContext.Type.QUIT:
case textsecure.protobuf.PushMessageContent.GroupContext.Type.QUIT:
textsecure.storage.groups.removeNumber(decrypted.group.id, proto.source);
decrypted.body = null;
decrypted.attachments = [];
case textsecure.protos.PushMessageContentProtobuf.GroupContext.Type.DELIVER:
case textsecure.protobuf.PushMessageContent.GroupContext.Type.DELIVER:
decrypted.group.name = null;
decrypted.group.members = [];
decrypted.group.avatar = null;
@ -741,7 +698,7 @@ window.textsecure.registerSingleDevice = function(number, verificationCode, step
}
window.textsecure.registerSecondDevice = function(encodedDeviceInit, cryptoInfo, stepDone) {
var deviceInit = textsecure.protos.decodeDeviceInit(encodedDeviceInit);
var deviceInit = textsecure.protobuf.DeviceInit.decode(encodedDeviceInit, 'binary');
return cryptoInfo.decryptAndHandleDeviceInit(deviceInit).then(function(identityKey) {
if (identityKey.server != textsecure.api.relay)
throw new Error("Unknown relay used by master");

21
js/protobufs.js Normal file
View file

@ -0,0 +1,21 @@
;(function() {
function loadProtoBufs(filename) {
return dcodeIO.ProtoBuf.loadProtoFile({root: 'protos', file: filename}).build('textsecure');
};
var pushMessages = loadProtoBufs('IncomingPushMessageSignal.proto');
var protocolMessages = loadProtoBufs('WhisperTextProtocol.proto');
var deviceMessages = loadProtoBufs('DeviceMessages.proto');
window.textsecure = window.textsecure || {};
window.textsecure.protobuf = {
IncomingPushMessageSignal : pushMessages.IncomingPushMessageSignal,
PushMessageContent : pushMessages.PushMessageContent,
WhisperMessage : protocolMessages.WhisperMessage,
PreKeyWhisperMessage : protocolMessages.PreKeyWhisperMessage,
DeviceInit : deviceMessages.DeviceInit,
IdentityKey : deviceMessages.IdentityKey,
DeviceControl : deviceMessages.DeviceControl
};
})();

View file

@ -79,11 +79,11 @@ window.textsecure.messaging = function() {
for (var i in groups) {
var group = textsecure.storage.groups.getGroup(groups[i]);
var proto = new textsecure.protos.PushMessageContentProtobuf();
proto.group = new textsecure.protos.PushMessageContentProtobuf.GroupContext();
var proto = new textsecure.protobuf.PushMessageContent();
proto.group = new textsecure.protobuf.PushMessageContent.GroupContext();
proto.group.id = toArrayBuffer(group.id);
proto.group.type = textsecure.protos.PushMessageContentProtobuf.GroupContext.Type.UPDATE;
proto.group.type = textsecure.protobuf.PushMessageContent.GroupContext.Type.UPDATE;
proto.group.members = group.numbers;
proto.group.name = group.name === undefined ? null : group.name;
@ -102,10 +102,10 @@ window.textsecure.messaging = function() {
var tryMessageAgain = function(number, encodedMessage, callback) {
//TODO: Wipe identity key!
refreshGroups(number).then(function() {
var message = textsecure.protos.decodePushMessageContentProtobuf(encodedMessage);
var message = textsecure.protobuf.PushMessageContent.decode(encodedMessage, 'binary');
textsecure.sendMessage([number], message, callback);
});
}
};
textsecure.replay.registerReplayFunction(tryMessageAgain, textsecure.replay.SEND_MESSAGE);
var sendMessageProto = function(numbers, message, callback) {
@ -194,7 +194,7 @@ window.textsecure.messaging = function() {
}
makeAttachmentPointer = function(attachment) {
var proto = new textsecure.protos.PushMessageContentProtobuf.AttachmentPointer();
var proto = new textsecure.protobuf.PushMessageContent.AttachmentPointer();
proto.key = textsecure.crypto.getRandomBytes(64);
var iv = textsecure.crypto.getRandomBytes(16);
@ -233,7 +233,7 @@ window.textsecure.messaging = function() {
}
self.sendMessageToNumber = function(number, messageText, attachments) {
var proto = new textsecure.protos.PushMessageContentProtobuf();
var proto = new textsecure.protobuf.PushMessageContent();
proto.body = messageText;
var promises = [];
@ -246,9 +246,9 @@ window.textsecure.messaging = function() {
}
self.closeSession = function(number) {
var proto = new textsecure.protos.PushMessageContentProtobuf();
var proto = new textsecure.protobuf.PushMessageContent();
proto.body = "TERMINATE";
proto.flags = textsecure.protos.PushMessageContentProtobuf.Flags.END_SESSION;
proto.flags = textsecure.protobuf.PushMessageContent.Flags.END_SESSION;
return sendIndividualProto(number, proto).then(function(res) {
var devices = textsecure.storage.devices.getDeviceObjectsForNumber(number);
for (var i in devices)
@ -259,11 +259,11 @@ window.textsecure.messaging = function() {
}
self.sendMessageToGroup = function(groupId, messageText, attachments) {
var proto = new textsecure.protos.PushMessageContentProtobuf();
var proto = new textsecure.protobuf.PushMessageContent();
proto.body = messageText;
proto.group = new textsecure.protos.PushMessageContentProtobuf.GroupContext();
proto.group = new textsecure.protobuf.PushMessageContent.GroupContext();
proto.group.id = toArrayBuffer(groupId);
proto.group.type = textsecure.protos.PushMessageContentProtobuf.GroupContext.Type.DELIVER;
proto.group.type = textsecure.protobuf.PushMessageContent.GroupContext.Type.DELIVER;
var numbers = textsecure.storage.groups.getNumbers(groupId);
if (numbers === undefined)
@ -279,14 +279,14 @@ window.textsecure.messaging = function() {
}
self.createGroup = function(numbers, name, avatar) {
var proto = new textsecure.protos.PushMessageContentProtobuf();
proto.group = new textsecure.protos.PushMessageContentProtobuf.GroupContext();
var proto = new textsecure.protobuf.PushMessageContent();
proto.group = new textsecure.protobuf.PushMessageContent.GroupContext();
var group = textsecure.storage.groups.createNewGroup(numbers);
proto.group.id = toArrayBuffer(group.id);
var numbers = group.numbers;
proto.group.type = textsecure.protos.PushMessageContentProtobuf.GroupContext.Type.UPDATE;
proto.group.type = textsecure.protobuf.PushMessageContent.GroupContext.Type.UPDATE;
proto.group.members = numbers;
proto.group.name = name;
@ -305,10 +305,10 @@ window.textsecure.messaging = function() {
}
self.addNumberToGroup = function(groupId, number) {
var proto = new textsecure.protos.PushMessageContentProtobuf();
proto.group = new textsecure.protos.PushMessageContentProtobuf.GroupContext();
var proto = new textsecure.protobuf.PushMessageContent();
proto.group = new textsecure.protobuf.PushMessageContent.GroupContext();
proto.group.id = toArrayBuffer(groupId);
proto.group.type = textsecure.protos.PushMessageContentProtobuf.GroupContext.Type.UPDATE;
proto.group.type = textsecure.protobuf.PushMessageContent.GroupContext.Type.UPDATE;
var numbers = textsecure.storage.groups.addNumbers(groupId, [number]);
if (numbers === undefined)
@ -319,10 +319,10 @@ window.textsecure.messaging = function() {
}
self.setGroupName = function(groupId, name) {
var proto = new textsecure.protos.PushMessageContentProtobuf();
proto.group = new textsecure.protos.PushMessageContentProtobuf.GroupContext();
var proto = new textsecure.protobuf.PushMessageContent();
proto.group = new textsecure.protobuf.PushMessageContent.GroupContext();
proto.group.id = toArrayBuffer(groupId);
proto.group.type = textsecure.protos.PushMessageContentProtobuf.GroupContext.Type.UPDATE;
proto.group.type = textsecure.protobuf.PushMessageContent.GroupContext.Type.UPDATE;
proto.group.name = name;
var numbers = textsecure.storage.groups.getNumbers(groupId);
@ -334,10 +334,10 @@ window.textsecure.messaging = function() {
}
self.setGroupAvatar = function(groupId, avatar) {
var proto = new textsecure.protos.PushMessageContentProtobuf();
proto.group = new textsecure.protos.PushMessageContentProtobuf.GroupContext();
var proto = new textsecure.protobuf.PushMessageContent();
proto.group = new textsecure.protobuf.PushMessageContent.GroupContext();
proto.group.id = toArrayBuffer(groupId);
proto.group.type = textsecure.protos.PushMessageContentProtobuf.GroupContext.Type.UPDATE;
proto.group.type = textsecure.protobuf.PushMessageContent.GroupContext.Type.UPDATE;
var numbers = textsecure.storage.groups.getNumbers(groupId);
if (numbers === undefined)
@ -351,10 +351,10 @@ window.textsecure.messaging = function() {
}
self.leaveGroup = function(groupId) {
var proto = new textsecure.protos.PushMessageContentProtobuf();
proto.group = new textsecure.protos.PushMessageContentProtobuf.GroupContext();
var proto = new textsecure.protobuf.PushMessageContent();
proto.group = new textsecure.protobuf.PushMessageContent.GroupContext();
proto.group.id = toArrayBuffer(groupId);
proto.group.type = textsecure.protos.PushMessageContentProtobuf.GroupContext.Type.QUIT;
proto.group.type = textsecure.protobuf.PushMessageContent.GroupContext.Type.QUIT;
var numbers = textsecure.storage.groups.getNumbers(groupId);
if (numbers === undefined)

View file

@ -291,15 +291,16 @@ describe("Axolotl", function() {
if (data.newEphemeralKey !== undefined)
privKeyQueue.push(data.newEphemeralKey);
var message = new textsecure.protos.IncomingPushMessageProtobuf();
var message = new textsecure.protobuf.IncomingPushMessageSignal();
message.type = data.type;
message.source = "SNOWDEN";
message.message = data.message;
message.sourceDevice = 1;
try {
return textsecure.crypto.handleIncomingPushMessageProto(textsecure.protos.decodeIncomingPushMessageProtobuf(getString(message.encode()))).then(function(res) {
var proto = textsecure.protobuf.IncomingPushMessageSignal.decode(message.encode());
return textsecure.crypto.handleIncomingPushMessageProto(proto).then(function(res) {
if (data.expectTerminateSession)
return res.flags == textsecure.protos.PushMessageContentProtobuf.Flags.END_SESSION;
return res.flags == textsecure.protobuf.PushMessageContent.Flags.END_SESSION;
return res.body == data.expectedSmsText;
}).catch(function(e) {
if (data.expectException)
@ -345,11 +346,11 @@ describe("Axolotl", function() {
//XXX: This should be all we do: isEqual(data.expectedCiphertext, msg.body, false);
if (msg.type == 1) {
var expected = getString(data.expectedCiphertext);
var decoded = textsecure.protos.decodeWhisperMessageProtobuf(expected.substring(1, expected.length - 8));
var decoded = textsecure.protobuf.WhisperMessage.decode(expected.substring(1, expected.length - 8), 'binary');
var result = getString(msg.body);
return getString(decoded.encode()) == result.substring(1, result.length - 8);
} else {
var decoded = textsecure.protos.decodePreKeyWhisperMessageProtobuf(getString(data.expectedCiphertext).substr(1));
var decoded = textsecure.protobuf.PreKeyWhisperMessage.decode(getString(data.expectedCiphertext).substr(1), 'binary');
var result = getString(msg.body).substring(1);
return getString(decoded.encode()) == result;
}

View file

@ -104,6 +104,7 @@
<script type="text/javascript" src="js-deps/libphonenumber_api-compiled.js"></script>
<script type="text/javascript" src="js-deps/qrcode.min.js"></script>
<script type="text/javascript" src="js/protobufs.js"></script>
<script type="text/javascript" src="js/helpers.js"></script>
<script type="text/javascript" src="js/libphonenumber-util.js"></script>
<script type="text/javascript" src="js/webcrypto.js"></script>

View file

@ -45,6 +45,7 @@
<script type="text/javascript" src="js-deps/backbone.localStorage.js"></script>
<script type="text/javascript" src="js-deps/libphonenumber_api-compiled.js"></script>
<script type="text/javascript" src="js/protobufs.js" data-cover></script>
<script type="text/javascript" src="js/helpers.js" data-cover></script>
<script type="text/javascript" src="js/webcrypto.js"></script>
<script type="text/javascript" src="js/crypto.js" data-cover></script>