Skip to content
This repository was archived by the owner on Aug 5, 2021. It is now read-only.

Commit 6154353

Browse files
Merge branch 'trust-store-changes'
Catching master up with the latest updates. the trust-store-changes branch has all recent tags, and the master branch has a few readme and project-level changes.
2 parents dbf8c26 + 3d72df7 commit 6154353

File tree

10 files changed

+689
-348
lines changed

10 files changed

+689
-348
lines changed

dist/libsignal-protocol.js

Lines changed: 153 additions & 111 deletions
Large diffs are not rendered by default.

src/SessionBuilder.js

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ SessionBuilder.prototype = {
77
processPreKey: function(device) {
88
return Internal.SessionLock.queueJobForNumber(this.remoteAddress.toString(), function() {
99
return this.storage.isTrustedIdentity(
10-
this.remoteAddress.getName(), device.identityKey
10+
this.remoteAddress.getName(), device.identityKey, this.storage.Direction.SENDING
1111
).then(function(trusted) {
1212
if (!trusted) {
1313
throw new Error('Identity key changed');
@@ -21,15 +21,20 @@ SessionBuilder.prototype = {
2121
}).then(function() {
2222
return Internal.crypto.createKeyPair();
2323
}).then(function(baseKey) {
24-
var devicePreKey = (device.preKey.publicKey);
24+
var devicePreKey;
25+
if (device.preKey) {
26+
devicePreKey = device.preKey.publicKey;
27+
}
2528
return this.initSession(true, baseKey, undefined, device.identityKey,
26-
devicePreKey, device.signedPreKey.publicKey
29+
devicePreKey, device.signedPreKey.publicKey, device.registrationId
2730
).then(function(session) {
2831
session.pendingPreKey = {
29-
preKeyId : device.preKey.keyId,
3032
signedKeyId : device.signedPreKey.keyId,
3133
baseKey : baseKey.pubKey
3234
};
35+
if (device.preKey) {
36+
session.pendingPreKey.preKeyId = device.preKey.keyId;
37+
}
3338
return session;
3439
});
3540
}.bind(this)).then(function(session) {
@@ -39,14 +44,14 @@ SessionBuilder.prototype = {
3944
if (serialized !== undefined) {
4045
record = Internal.SessionRecord.deserialize(serialized);
4146
} else {
42-
record = new Internal.SessionRecord(device.identityKey, device.registrationId);
47+
record = new Internal.SessionRecord();
4348
}
4449

4550
record.archiveCurrentState();
46-
record.updateSessionState(session, device.registrationId);
51+
record.updateSessionState(session);
4752
return Promise.all([
4853
this.storage.storeSession(address, record.serialize()),
49-
this.storage.saveIdentity(this.remoteAddress.getName(), record.identityKey)
54+
this.storage.saveIdentity(this.remoteAddress.toString(), session.indexInfo.remoteIdentityKey)
5055
]);
5156
}.bind(this));
5257
}.bind(this));
@@ -55,7 +60,7 @@ SessionBuilder.prototype = {
5560
processV3: function(record, message) {
5661
var preKeyPair, signedPreKeyPair, session;
5762
return this.storage.isTrustedIdentity(
58-
this.remoteAddress.getName(), message.identityKey.toArrayBuffer()
63+
this.remoteAddress.getName(), message.identityKey.toArrayBuffer(), this.storage.Direction.RECEIVING
5964
).then(function(trusted) {
6065
if (!trusted) {
6166
var e = new Error('Unknown identity key');
@@ -97,21 +102,21 @@ SessionBuilder.prototype = {
97102
}
98103
return this.initSession(false, preKeyPair, signedPreKeyPair,
99104
message.identityKey.toArrayBuffer(),
100-
message.baseKey.toArrayBuffer(), undefined
105+
message.baseKey.toArrayBuffer(), undefined, message.registrationId
101106
).then(function(new_session) {
102107
// Note that the session is not actually saved until the very
103108
// end of decryptWhisperMessage ... to ensure that the sender
104109
// actually holds the private keys for all reported pubkeys
105-
record.updateSessionState(new_session, message.registrationId);
106-
return this.storage.saveIdentity(this.remoteAddress.getName(), message.identityKey.toArrayBuffer()).then(function() {
110+
record.updateSessionState(new_session);
111+
return this.storage.saveIdentity(this.remoteAddress.toString(), message.identityKey.toArrayBuffer()).then(function() {
107112
return message.preKeyId;
108113
});
109114
}.bind(this));
110115
}.bind(this));
111116
},
112117
initSession: function(isInitiator, ourEphemeralKey, ourSignedKey,
113118
theirIdentityPubKey, theirEphemeralPubKey,
114-
theirSignedPubKey) {
119+
theirSignedPubKey, registrationId) {
115120
return this.storage.getIdentityKeyPair().then(function(ourIdentityKey) {
116121
if (isInitiator) {
117122
if (ourSignedKey !== undefined) {
@@ -161,6 +166,7 @@ SessionBuilder.prototype = {
161166
return Internal.HKDF(sharedSecret.buffer, new ArrayBuffer(32), "WhisperText");
162167
}).then(function(masterKey) {
163168
var session = {
169+
registrationId: registrationId,
164170
currentRatchet: {
165171
rootKey : masterKey[0],
166172
lastRemoteEphemeralKey : theirSignedPubKey,

src/SessionCipher.js

Lines changed: 60 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
function SessionCipher(storage, remoteAddress) {
1+
function SessionCipher(storage, remoteAddress, options) {
2+
options = options || {};
3+
4+
if (typeof options.messageKeysLimit === 'undefined') {
5+
options.messageKeysLimit = 1000;
6+
}
7+
8+
this.messageKeysLimit = options.messageKeysLimit;
29
this.remoteAddress = remoteAddress;
310
this.storage = storage;
411
}
@@ -76,10 +83,20 @@ SessionCipher.prototype = {
7683
result.set(new Uint8Array(encodedMsg), 1);
7784
result.set(new Uint8Array(mac, 0, 8), encodedMsg.byteLength + 1);
7885

79-
record.updateSessionState(session);
80-
return this.storage.storeSession(address, record.serialize()).then(function() {
81-
return result;
82-
});
86+
return this.storage.isTrustedIdentity(
87+
this.remoteAddress.getName(), util.toArrayBuffer(session.indexInfo.remoteIdentityKey), this.storage.Direction.SENDING
88+
).then(function(trusted) {
89+
if (!trusted) {
90+
throw new Error('Identity key changed');
91+
}
92+
}).then(function() {
93+
return this.storage.saveIdentity(this.remoteAddress.toString(), session.indexInfo.remoteIdentityKey);
94+
}.bind(this)).then(function() {
95+
record.updateSessionState(session);
96+
return this.storage.storeSession(address, record.serialize()).then(function() {
97+
return result;
98+
});
99+
}.bind(this));
83100
}.bind(this));
84101
}.bind(this));
85102
}.bind(this)).then(function(message) {
@@ -89,22 +106,24 @@ SessionCipher.prototype = {
89106
preKeyMsg.registrationId = myRegistrationId;
90107

91108
preKeyMsg.baseKey = util.toArrayBuffer(session.pendingPreKey.baseKey);
92-
preKeyMsg.preKeyId = session.pendingPreKey.preKeyId;
109+
if (session.pendingPreKey.preKeyId) {
110+
preKeyMsg.preKeyId = session.pendingPreKey.preKeyId;
111+
}
93112
preKeyMsg.signedPreKeyId = session.pendingPreKey.signedKeyId;
94113

95114
preKeyMsg.message = message;
96115
var result = String.fromCharCode((3 << 4) | 3) + util.toString(preKeyMsg.encode());
97116
return {
98117
type : 3,
99118
body : result,
100-
registrationId : record.registrationId
119+
registrationId : session.registrationId
101120
};
102121

103122
} else {
104123
return {
105124
type : 1,
106125
body : util.toString(message),
107-
registrationId : record.registrationId
126+
registrationId : session.registrationId
108127
};
109128
}
110129
});
@@ -122,6 +141,10 @@ SessionCipher.prototype = {
122141
return this.doDecryptWhisperMessage(buffer, session).then(function(plaintext) {
123142
return { plaintext: plaintext, session: session };
124143
}).catch(function(e) {
144+
if (e.name === 'MessageCounterError') {
145+
return Promise.reject(e);
146+
}
147+
125148
errors.push(e);
126149
return this.decryptWithSessionList(buffer, sessionList, errors);
127150
}.bind(this));
@@ -137,10 +160,25 @@ SessionCipher.prototype = {
137160
var errors = [];
138161
return this.decryptWithSessionList(buffer, record.getSessions(), errors).then(function(result) {
139162
return this.getRecord(address).then(function(record) {
140-
record.updateSessionState(result.session);
141-
return this.storage.storeSession(address, record.serialize()).then(function() {
142-
return result.plaintext;
143-
});
163+
if (result.session.indexInfo.baseKey !== record.getOpenSession().indexInfo.baseKey) {
164+
record.archiveCurrentState();
165+
record.promoteState(result.session);
166+
}
167+
168+
return this.storage.isTrustedIdentity(
169+
this.remoteAddress.getName(), util.toArrayBuffer(result.session.indexInfo.remoteIdentityKey), this.storage.Direction.RECEIVING
170+
).then(function(trusted) {
171+
if (!trusted) {
172+
throw new Error('Identity key changed');
173+
}
174+
}).then(function() {
175+
return this.storage.saveIdentity(this.remoteAddress.toString(), result.session.indexInfo.remoteIdentityKey);
176+
}.bind(this)).then(function() {
177+
record.updateSessionState(result.session);
178+
return this.storage.storeSession(address, record.serialize()).then(function() {
179+
return result.plaintext;
180+
});
181+
}.bind(this));
144182
}.bind(this));
145183
}.bind(this));
146184
}.bind(this));
@@ -161,19 +199,19 @@ SessionCipher.prototype = {
161199
throw new Error("No registrationId");
162200
}
163201
record = new Internal.SessionRecord(
164-
util.toString(preKeyProto.identityKey),
165202
preKeyProto.registrationId
166203
);
167204
}
168205
var builder = new SessionBuilder(this.storage, this.remoteAddress);
206+
// isTrustedIdentity is called within processV3, no need to call it here
169207
return builder.processV3(record, preKeyProto).then(function(preKeyId) {
170208
var session = record.getSessionByBaseKey(preKeyProto.baseKey);
171209
return this.doDecryptWhisperMessage(
172210
preKeyProto.message.toArrayBuffer(), session
173211
).then(function(plaintext) {
174212
record.updateSessionState(session);
175213
return this.storage.storeSession(address, record.serialize()).then(function() {
176-
if (preKeyId !== undefined) {
214+
if (preKeyId !== undefined && preKeyId !== null) {
177215
return this.storage.removePreKey(preKeyId);
178216
}
179217
}.bind(this)).then(function() {
@@ -240,7 +278,7 @@ SessionCipher.prototype = {
240278
});
241279
},
242280
fillMessageKeys: function(chain, counter) {
243-
if (Object.keys(chain.messageKeys).length >= 1000) {
281+
if (this.messageKeysLimit && Object.keys(chain.messageKeys).length >= this.messageKeysLimit) {
244282
console.log("Too many message keys for chain");
245283
return Promise.resolve(); // Stalker, much?
246284
}
@@ -330,7 +368,11 @@ SessionCipher.prototype = {
330368
if (record === undefined) {
331369
return undefined;
332370
}
333-
return record.registrationId;
371+
var openSession = record.getOpenSession();
372+
if (openSession === undefined) {
373+
return null;
374+
}
375+
return openSession.registrationId;
334376
});
335377
}.bind(this));
336378
},
@@ -359,8 +401,8 @@ SessionCipher.prototype = {
359401
}
360402
};
361403

362-
libsignal.SessionCipher = function(storage, remoteAddress) {
363-
var cipher = new SessionCipher(storage, remoteAddress);
404+
libsignal.SessionCipher = function(storage, remoteAddress, options) {
405+
var cipher = new SessionCipher(storage, remoteAddress, options);
364406

365407
// returns a Promise that resolves to a ciphertext object
366408
this.encrypt = cipher.encrypt.bind(cipher);

0 commit comments

Comments
 (0)