Move protocol protobufs to libaxolotl/, handling DeviceControl
This commit is contained in:
parent
66cf5b08db
commit
184b1ec89c
8 changed files with 102 additions and 67 deletions
|
@ -81,6 +81,7 @@ module.exports = function(grunt) {
|
||||||
'libaxolotl/groups_storage.js',
|
'libaxolotl/groups_storage.js',
|
||||||
'libaxolotl/crypto.js',
|
'libaxolotl/crypto.js',
|
||||||
'libaxolotl/protocol.js',
|
'libaxolotl/protocol.js',
|
||||||
|
'libaxolotl/protobufs.js',
|
||||||
],
|
],
|
||||||
dest: 'libtextsecure/libaxolotl_concat.js',
|
dest: 'libtextsecure/libaxolotl_concat.js',
|
||||||
},
|
},
|
||||||
|
|
|
@ -87,7 +87,7 @@
|
||||||
conversation.save().then(function() {
|
conversation.save().then(function() {
|
||||||
message.save().then(function() {
|
message.save().then(function() {
|
||||||
return new Promise(function(resolve) {
|
return new Promise(function(resolve) {
|
||||||
resolve(textsecure.protocol.handleIncomingPushMessageProto(pushMessage).then(
|
resolve(textsecure.protocol_wrapper.handleIncomingPushMessageProto(pushMessage).then(
|
||||||
function(pushMessageContent) {
|
function(pushMessageContent) {
|
||||||
handlePushMessageContent(pushMessageContent, message);
|
handlePushMessageContent(pushMessageContent, message);
|
||||||
}
|
}
|
||||||
|
|
17
libaxolotl/protobufs.js
Normal file
17
libaxolotl/protobufs.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
;(function() {
|
||||||
|
function loadProtoBufs(filename) {
|
||||||
|
return dcodeIO.ProtoBuf.loadProtoFile({root: 'protos', file: filename}).build('textsecure');
|
||||||
|
};
|
||||||
|
|
||||||
|
var protocolMessages = loadProtoBufs('WhisperTextProtocol.proto');
|
||||||
|
var deviceMessages = loadProtoBufs('DeviceMessages.proto');
|
||||||
|
|
||||||
|
window.axolotl = window.axolotl || {};
|
||||||
|
window.axolotl.protobuf = {
|
||||||
|
WhisperMessage : protocolMessages.WhisperMessage,
|
||||||
|
PreKeyWhisperMessage : protocolMessages.PreKeyWhisperMessage,
|
||||||
|
DeviceInit : deviceMessages.DeviceInit,
|
||||||
|
IdentityKey : deviceMessages.IdentityKey,
|
||||||
|
DeviceControl : deviceMessages.DeviceControl,
|
||||||
|
};
|
||||||
|
})();
|
|
@ -349,20 +349,8 @@ window.textsecure.protocol = function() {
|
||||||
crypto_storage.saveSession(encodedNumber, session);
|
crypto_storage.saveSession(encodedNumber, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
var initSessionFromPreKeyWhisperMessage;
|
//TODO: Rewrite this!
|
||||||
var decryptWhisperMessage;
|
/*var wipeIdentityAndTryMessageAgain = function(from, encodedMessage, message_id) {
|
||||||
var handlePreKeyWhisperMessage = function(from, 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)
|
|
||||||
sessions[1]();
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var wipeIdentityAndTryMessageAgain = function(from, encodedMessage, message_id) {
|
|
||||||
// Wipe identity key!
|
// Wipe identity key!
|
||||||
textsecure.storage.removeEncrypted("devices" + from.split('.')[0]);
|
textsecure.storage.removeEncrypted("devices" + from.split('.')[0]);
|
||||||
return handlePreKeyWhisperMessage(from, encodedMessage).then(
|
return handlePreKeyWhisperMessage(from, encodedMessage).then(
|
||||||
|
@ -374,9 +362,9 @@ window.textsecure.protocol = function() {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
textsecure.replay.registerFunction(wipeIdentityAndTryMessageAgain, textsecure.replay.Type.INIT_SESSION);
|
textsecure.replay.registerFunction(wipeIdentityAndTryMessageAgain, textsecure.replay.Type.INIT_SESSION);*/
|
||||||
|
|
||||||
initSessionFromPreKeyWhisperMessage = function(encodedNumber, message) {
|
var initSessionFromPreKeyWhisperMessage = function(encodedNumber, message) {
|
||||||
var preKeyPair = crypto_storage.getStoredKeyPair("preKey" + message.preKeyId);
|
var preKeyPair = crypto_storage.getStoredKeyPair("preKey" + message.preKeyId);
|
||||||
var signedPreKeyPair = crypto_storage.getStoredKeyPair("signedKey" + message.signedPreKeyId);
|
var signedPreKeyPair = crypto_storage.getStoredKeyPair("signedKey" + message.signedPreKeyId);
|
||||||
|
|
||||||
|
@ -478,15 +466,18 @@ window.textsecure.protocol = function() {
|
||||||
return finish();
|
return finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns decrypted protobuf
|
/*************************
|
||||||
decryptWhisperMessage = function(encodedNumber, messageBytes, session, registrationId) {
|
*** Public crypto API ***
|
||||||
|
*************************/
|
||||||
|
// returns decrypted plaintext
|
||||||
|
self.decryptWhisperMessage = function(encodedNumber, messageBytes, session, registrationId) {
|
||||||
if (messageBytes[0] != String.fromCharCode((3 << 4) | 3))
|
if (messageBytes[0] != String.fromCharCode((3 << 4) | 3))
|
||||||
throw new Error("Bad version number on WhisperMessage");
|
throw new Error("Bad version number on WhisperMessage");
|
||||||
|
|
||||||
var messageProto = messageBytes.substring(1, messageBytes.length - 8);
|
var messageProto = messageBytes.substring(1, messageBytes.length - 8);
|
||||||
var mac = messageBytes.substring(messageBytes.length - 8, messageBytes.length);
|
var mac = messageBytes.substring(messageBytes.length - 8, messageBytes.length);
|
||||||
|
|
||||||
var message = textsecure.protobuf.WhisperMessage.decode(messageProto, 'binary');
|
var message = axolotl.protobuf.WhisperMessage.decode(messageProto, 'binary');
|
||||||
var remoteEphemeralKey = toArrayBuffer(message.ephemeralKey);
|
var remoteEphemeralKey = toArrayBuffer(message.ephemeralKey);
|
||||||
|
|
||||||
if (session === undefined) {
|
if (session === undefined) {
|
||||||
|
@ -526,17 +517,9 @@ window.textsecure.protocol = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
delete session['pendingPreKey'];
|
delete session['pendingPreKey'];
|
||||||
|
|
||||||
var finalMessage = textsecure.protobuf.PushMessageContent.decode(plaintext);
|
|
||||||
|
|
||||||
if ((finalMessage.flags & textsecure.protobuf.PushMessageContent.Flags.END_SESSION)
|
|
||||||
== textsecure.protobuf.PushMessageContent.Flags.END_SESSION)
|
|
||||||
closeSession(session, true);
|
|
||||||
|
|
||||||
removeOldChains(session);
|
removeOldChains(session);
|
||||||
|
|
||||||
crypto_storage.saveSession(encodedNumber, session, registrationId);
|
crypto_storage.saveSession(encodedNumber, session, registrationId);
|
||||||
return finalMessage;
|
return [plaintext, session];
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -544,9 +527,18 @@ window.textsecure.protocol = function() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/*************************
|
// Inits a session (maybe) and then decrypts the message
|
||||||
*** Public crypto API ***
|
self.handlePreKeyWhisperMessage = function(from, encodedMessage) {
|
||||||
*************************/
|
var preKeyProto = axolotl.protobuf.PreKeyWhisperMessage.decode(encodedMessage, 'binary');
|
||||||
|
return initSessionFromPreKeyWhisperMessage(from, preKeyProto).then(function(sessions) {
|
||||||
|
return self.decryptWhisperMessage(from, getString(preKeyProto.message), sessions[0], preKeyProto.registrationId).then(function(result) {
|
||||||
|
if (sessions[1] !== undefined)
|
||||||
|
sessions[1]();
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Decrypts message into a raw string
|
// Decrypts message into a raw string
|
||||||
self.decryptWebsocketMessage = function(message) {
|
self.decryptWebsocketMessage = function(message) {
|
||||||
var signaling_key = textsecure.storage.getEncrypted("signaling_key"); //TODO: in crypto_storage
|
var signaling_key = textsecure.storage.getEncrypted("signaling_key"); //TODO: in crypto_storage
|
||||||
|
@ -599,31 +591,12 @@ window.textsecure.protocol = function() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
self.handleIncomingPushMessageProto = function(proto) {
|
|
||||||
switch(proto.type) {
|
|
||||||
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.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.protobuf.IncomingPushMessageSignal.Type.RECEIPT:
|
|
||||||
return Promise.resolve(null);
|
|
||||||
default:
|
|
||||||
return new Promise(function(resolve, reject) { reject(new Error("Unknown message type")); });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// return Promise(encoded [PreKey]WhisperMessage)
|
// return Promise(encoded [PreKey]WhisperMessage)
|
||||||
self.encryptMessageFor = function(deviceObject, pushMessageContent) {
|
self.encryptMessageFor = function(deviceObject, pushMessageContent) {
|
||||||
var session = crypto_storage.getOpenSession(deviceObject.encodedNumber);
|
var session = crypto_storage.getOpenSession(deviceObject.encodedNumber);
|
||||||
|
|
||||||
var doEncryptPushMessageContent = function() {
|
var doEncryptPushMessageContent = function() {
|
||||||
var msg = new textsecure.protobuf.WhisperMessage();
|
var msg = new axolotl.protobuf.WhisperMessage();
|
||||||
var plaintext = toArrayBuffer(pushMessageContent.encode());
|
var plaintext = toArrayBuffer(pushMessageContent.encode());
|
||||||
|
|
||||||
var paddedPlaintext = new Uint8Array(Math.ceil((plaintext.byteLength + 1) / 160.0) * 160 - 1);
|
var paddedPlaintext = new Uint8Array(Math.ceil((plaintext.byteLength + 1) / 160.0) * 160 - 1);
|
||||||
|
@ -672,7 +645,7 @@ window.textsecure.protocol = function() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var preKeyMsg = new textsecure.protobuf.PreKeyWhisperMessage();
|
var preKeyMsg = new axolotl.protobuf.PreKeyWhisperMessage();
|
||||||
preKeyMsg.identityKey = toArrayBuffer(crypto_storage.getIdentityKey().pubKey);
|
preKeyMsg.identityKey = toArrayBuffer(crypto_storage.getIdentityKey().pubKey);
|
||||||
preKeyMsg.registrationId = textsecure.storage.getUnencrypted("registrationId");
|
preKeyMsg.registrationId = textsecure.storage.getUnencrypted("registrationId");
|
||||||
|
|
||||||
|
|
|
@ -23,4 +23,49 @@
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
window.textsecure = window.textsecure || {};
|
||||||
|
window.textsecure.protocol_wrapper = {
|
||||||
|
handleIncomingPushMessageProto: function(proto) {
|
||||||
|
var decodeContents = function(res) {
|
||||||
|
var finalMessage = textsecure.protobuf.PushMessageContent.decode(res[0]);
|
||||||
|
|
||||||
|
//TODO
|
||||||
|
/*if ((finalMessage.flags & textsecure.protobuf.PushMessageContent.Flags.END_SESSION)
|
||||||
|
== textsecure.protobuf.PushMessageContent.Flags.END_SESSION)
|
||||||
|
textsecure.protocol.closeSession(res[1], true);*/
|
||||||
|
|
||||||
|
return finalMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(proto.type) {
|
||||||
|
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 textsecure.protocol.decryptWhisperMessage(from, getString(proto.message)).then(decodeContents);
|
||||||
|
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 textsecure.protocol.handlePreKeyWhisperMessage(from, getString(proto.message)).then(decodeContents);
|
||||||
|
case textsecure.protobuf.IncomingPushMessageSignal.Type.RECEIPT:
|
||||||
|
return Promise.resolve(null);
|
||||||
|
case textsecure.protobuf.IncomingPushMessageSignal.Type.PREKEY_BUNDLE_DEVICE_CONTROL:
|
||||||
|
if (proto.message.readUint8() != ((3 << 4) | 3))
|
||||||
|
throw new Error("Bad version byte");
|
||||||
|
var from = proto.source + "." + (proto.sourceDevice == null ? 0 : proto.sourceDevice);
|
||||||
|
return textsecure.protocol.handlePreKeyWhisperMessage(from, getString(proto.message)).then(function(res) {
|
||||||
|
return textsecure.protobuf.DeviceControl.decode(res[0]);
|
||||||
|
});
|
||||||
|
case textsecure.protobuf.IncomingPushMessageSignal.Type.DEVICE_CONTROL:
|
||||||
|
var from = proto.source + "." + (proto.sourceDevice == null ? 0 : proto.sourceDevice);
|
||||||
|
return textsecure.protocol.decryptWhisperMessage(from, getString(proto.message)).then(function(res) {
|
||||||
|
return textsecure.protobuf.DeviceControl.decode(res[0]);
|
||||||
|
});
|
||||||
|
default:
|
||||||
|
return new Promise(function(resolve, reject) { reject(new Error("Unknown message type")); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
var pushMessages = loadProtoBufs('IncomingPushMessageSignal.proto');
|
var pushMessages = loadProtoBufs('IncomingPushMessageSignal.proto');
|
||||||
var protocolMessages = loadProtoBufs('WhisperTextProtocol.proto');
|
|
||||||
var subProtocolMessages = loadProtoBufs('SubProtocol.proto');
|
var subProtocolMessages = loadProtoBufs('SubProtocol.proto');
|
||||||
var deviceMessages = loadProtoBufs('DeviceMessages.proto');
|
var deviceMessages = loadProtoBufs('DeviceMessages.proto');
|
||||||
|
|
||||||
|
@ -13,8 +12,6 @@
|
||||||
window.textsecure.protobuf = {
|
window.textsecure.protobuf = {
|
||||||
IncomingPushMessageSignal : pushMessages.IncomingPushMessageSignal,
|
IncomingPushMessageSignal : pushMessages.IncomingPushMessageSignal,
|
||||||
PushMessageContent : pushMessages.PushMessageContent,
|
PushMessageContent : pushMessages.PushMessageContent,
|
||||||
WhisperMessage : protocolMessages.WhisperMessage,
|
|
||||||
PreKeyWhisperMessage : protocolMessages.PreKeyWhisperMessage,
|
|
||||||
ProvisioningUuid : deviceMessages.ProvisioningUuid,
|
ProvisioningUuid : deviceMessages.ProvisioningUuid,
|
||||||
ProvisionEnvelope : deviceMessages.ProvisionEnvelope,
|
ProvisionEnvelope : deviceMessages.ProvisionEnvelope,
|
||||||
ProvisionMessage : deviceMessages.ProvisionMessage,
|
ProvisionMessage : deviceMessages.ProvisionMessage,
|
||||||
|
|
|
@ -31,7 +31,7 @@ describe('Protocol', function() {
|
||||||
message: text_message.encode()
|
message: text_message.encode()
|
||||||
};
|
};
|
||||||
|
|
||||||
return textsecure.protocol.handleIncomingPushMessageProto(server_message).
|
return textsecure.protocol_wrapper.handleIncomingPushMessageProto(server_message).
|
||||||
then(function(message) {
|
then(function(message) {
|
||||||
assert.equal(message.body, text_message.body);
|
assert.equal(message.body, text_message.body);
|
||||||
assert.equal(message.attachments.length, text_message.attachments.length);
|
assert.equal(message.attachments.length, text_message.attachments.length);
|
||||||
|
@ -134,7 +134,7 @@ describe('Protocol', function() {
|
||||||
message.sourceDevice = 1;
|
message.sourceDevice = 1;
|
||||||
try {
|
try {
|
||||||
var proto = textsecure.protobuf.IncomingPushMessageSignal.decode(message.encode());
|
var proto = textsecure.protobuf.IncomingPushMessageSignal.decode(message.encode());
|
||||||
return textsecure.protocol.handleIncomingPushMessageProto(proto).then(function(res) {
|
return textsecure.protocol_wrapper.handleIncomingPushMessageProto(proto).then(function(res) {
|
||||||
if (data.expectTerminateSession)
|
if (data.expectTerminateSession)
|
||||||
return res.flags == textsecure.protobuf.PushMessageContent.Flags.END_SESSION;
|
return res.flags == textsecure.protobuf.PushMessageContent.Flags.END_SESSION;
|
||||||
return res.body == data.expectedSmsText;
|
return res.body == data.expectedSmsText;
|
||||||
|
@ -182,11 +182,11 @@ describe('Protocol', function() {
|
||||||
//XXX: This should be all we do: isEqual(data.expectedCiphertext, msg.body, false);
|
//XXX: This should be all we do: isEqual(data.expectedCiphertext, msg.body, false);
|
||||||
if (msg.type == 1) {
|
if (msg.type == 1) {
|
||||||
var expected = getString(data.expectedCiphertext);
|
var expected = getString(data.expectedCiphertext);
|
||||||
var decoded = textsecure.protobuf.WhisperMessage.decode(expected.substring(1, expected.length - 8), 'binary');
|
var decoded = axolotl.protobuf.WhisperMessage.decode(expected.substring(1, expected.length - 8), 'binary');
|
||||||
var result = getString(msg.body);
|
var result = getString(msg.body);
|
||||||
return getString(decoded.encode()) == result.substring(1, result.length - 8);
|
return getString(decoded.encode()) == result.substring(1, result.length - 8);
|
||||||
} else {
|
} else {
|
||||||
var decoded = textsecure.protobuf.PreKeyWhisperMessage.decode(getString(data.expectedCiphertext).substr(1), 'binary');
|
var decoded = axolotl.protobuf.PreKeyWhisperMessage.decode(getString(data.expectedCiphertext).substr(1), 'binary');
|
||||||
var result = getString(msg.body).substring(1);
|
var result = getString(msg.body).substring(1);
|
||||||
return getString(decoded.encode()) == result;
|
return getString(decoded.encode()) == result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,14 @@ option java_outer_classname = "PushMessageProtos";
|
||||||
|
|
||||||
message IncomingPushMessageSignal {
|
message IncomingPushMessageSignal {
|
||||||
enum Type {
|
enum Type {
|
||||||
UNKNOWN = 0;
|
UNKNOWN = 0;
|
||||||
CIPHERTEXT = 1;
|
CIPHERTEXT = 1;
|
||||||
KEY_EXCHANGE = 2;
|
KEY_EXCHANGE = 2;
|
||||||
PREKEY_BUNDLE = 3;
|
PREKEY_BUNDLE = 3;
|
||||||
PLAINTEXT = 4;
|
PLAINTEXT = 4;
|
||||||
RECEIPT = 5;
|
RECEIPT = 5;
|
||||||
|
PREKEY_BUNDLE_DEVICE_CONTROL = 6;
|
||||||
|
DEVICE_CONTROL = 7;
|
||||||
}
|
}
|
||||||
optional Type type = 1;
|
optional Type type = 1;
|
||||||
optional string source = 2;
|
optional string source = 2;
|
||||||
|
@ -50,4 +52,4 @@ message PushMessageContent {
|
||||||
repeated AttachmentPointer attachments = 2;
|
repeated AttachmentPointer attachments = 2;
|
||||||
optional GroupContext group = 3;
|
optional GroupContext group = 3;
|
||||||
optional uint32 flags = 4;
|
optional uint32 flags = 4;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue