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

Commit fbc6554

Browse files
committed
Decrypt against previous sessions
In congruence with the java implementation, attempt to decrypt against previous session states if decryption fails using the open session.
1 parent bea0c3b commit fbc6554

File tree

3 files changed

+92
-20
lines changed

3 files changed

+92
-20
lines changed

dist/libsignal-protocol.js

+46-10
Original file line numberDiff line numberDiff line change
@@ -35679,6 +35679,26 @@ Internal.SessionRecord = function() {
3567935679
throw new Error("Had open sessions on a record that had no registrationId set");
3568035680
}
3568135681
},
35682+
getSessions: function() {
35683+
// return an array of sessions ordered by time closed,
35684+
// followed by the open session
35685+
var list = [];
35686+
var openSession;
35687+
for (var k in this._sessions) {
35688+
if (this._sessions[k].indexInfo.closed === -1) {
35689+
openSession = this._sessions[k];
35690+
} else {
35691+
list.push(this._sessions[k]);
35692+
}
35693+
}
35694+
list = list.sort(function(s1, s2) {
35695+
return s1.indexInfo.closed - s2.indexInfo.closed;
35696+
});
35697+
if (openSession) {
35698+
list.push(openSession);
35699+
}
35700+
return list;
35701+
},
3568235702
archiveCurrentState: function() {
3568335703
var open_session = this.getOpenSession();
3568435704
if (open_session !== undefined) {
@@ -35690,6 +35710,7 @@ Internal.SessionRecord = function() {
3569035710
if (session.indexInfo.closed > -1) {
3569135711
return;
3569235712
}
35713+
console.log('closing session', session.indexInfo.baseKey);
3569335714

3569435715
// After this has run, we can still receive messages on ratchet chains which
3569535716
// were already open (unless we know we dont need them),
@@ -36110,6 +36131,22 @@ SessionCipher.prototype = {
3611036131
});
3611136132
}.bind(this));
3611236133
},
36134+
decryptWithSessionList: function(buffer, sessionList, errors) {
36135+
// Iterate recursively through the list, attempting to decrypt
36136+
// using each one at a time. Stop and return the result if we get
36137+
// a valid result
36138+
if (sessionList.length === 0) {
36139+
return Promise.reject(errors[0]);
36140+
}
36141+
36142+
var session = sessionList.pop();
36143+
return this.doDecryptWhisperMessage(buffer, session).then(function(plaintext) {
36144+
return { plaintext: plaintext, session: session };
36145+
}).catch(function(e) {
36146+
errors.push(e);
36147+
return this.decryptWithSessionList(buffer, sessionList, errors);
36148+
}.bind(this));
36149+
},
3611336150
decryptWhisperMessage: function(buffer, encoding) {
3611436151
buffer = dcodeIO.ByteBuffer.wrap(buffer, encoding).toArrayBuffer();
3611536152
return Internal.SessionLock.queueJobForNumber(this.remoteAddress.toString(), function() {
@@ -36118,15 +36155,14 @@ SessionCipher.prototype = {
3611836155
if (!record) {
3611936156
throw new Error("No record for device " + address);
3612036157
}
36121-
var messageProto = buffer.slice(1, buffer.byteLength - 8);
36122-
var message = Internal.protobuf.WhisperMessage.decode(messageProto);
36123-
var remoteEphemeralKey = message.ephemeralKey.toArrayBuffer();
36124-
var session = record.getSessionByRemoteEphemeralKey(remoteEphemeralKey);
36125-
return this.doDecryptWhisperMessage(buffer, session).then(function(plaintext) {
36126-
record.updateSessionState(session);
36127-
return this.storage.storeSession(address, record.serialize()).then(function() {
36128-
return plaintext;
36129-
});
36158+
var errors = [];
36159+
return this.decryptWithSessionList(buffer, record.getSessions(), errors).then(function(result) {
36160+
return this.getRecord(address).then(function(record) {
36161+
record.updateSessionState(result.session);
36162+
return this.storage.storeSession(address, record.serialize()).then(function() {
36163+
return result.plaintext;
36164+
});
36165+
}.bind(this));
3613036166
}.bind(this));
3613136167
}.bind(this));
3613236168
}.bind(this));
@@ -36184,7 +36220,7 @@ SessionCipher.prototype = {
3618436220
var remoteEphemeralKey = message.ephemeralKey.toArrayBuffer();
3618536221

3618636222
if (session === undefined) {
36187-
throw new Error("No session found to decrypt message from " + this.remoteAddress.toString());
36223+
return Promise.reject(new Error("No session found to decrypt message from " + this.remoteAddress.toString()));
3618836224
}
3618936225
if (session.indexInfo.closed != -1) {
3619036226
console.log('decrypting message for closed session');

src/SessionCipher.js

+25-10
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,22 @@ SessionCipher.prototype = {
101101
});
102102
}.bind(this));
103103
},
104+
decryptWithSessionList: function(buffer, sessionList, errors) {
105+
// Iterate recursively through the list, attempting to decrypt
106+
// using each one at a time. Stop and return the result if we get
107+
// a valid result
108+
if (sessionList.length === 0) {
109+
return Promise.reject(errors[0]);
110+
}
111+
112+
var session = sessionList.pop();
113+
return this.doDecryptWhisperMessage(buffer, session).then(function(plaintext) {
114+
return { plaintext: plaintext, session: session };
115+
}).catch(function(e) {
116+
errors.push(e);
117+
return this.decryptWithSessionList(buffer, sessionList, errors);
118+
}.bind(this));
119+
},
104120
decryptWhisperMessage: function(buffer, encoding) {
105121
buffer = dcodeIO.ByteBuffer.wrap(buffer, encoding).toArrayBuffer();
106122
return Internal.SessionLock.queueJobForNumber(this.remoteAddress.toString(), function() {
@@ -109,15 +125,14 @@ SessionCipher.prototype = {
109125
if (!record) {
110126
throw new Error("No record for device " + address);
111127
}
112-
var messageProto = buffer.slice(1, buffer.byteLength - 8);
113-
var message = Internal.protobuf.WhisperMessage.decode(messageProto);
114-
var remoteEphemeralKey = message.ephemeralKey.toArrayBuffer();
115-
var session = record.getSessionByRemoteEphemeralKey(remoteEphemeralKey);
116-
return this.doDecryptWhisperMessage(buffer, session).then(function(plaintext) {
117-
record.updateSessionState(session);
118-
return this.storage.storeSession(address, record.serialize()).then(function() {
119-
return plaintext;
120-
});
128+
var errors = [];
129+
return this.decryptWithSessionList(buffer, record.getSessions(), errors).then(function(result) {
130+
return this.getRecord(address).then(function(record) {
131+
record.updateSessionState(result.session);
132+
return this.storage.storeSession(address, record.serialize()).then(function() {
133+
return result.plaintext;
134+
});
135+
}.bind(this));
121136
}.bind(this));
122137
}.bind(this));
123138
}.bind(this));
@@ -175,7 +190,7 @@ SessionCipher.prototype = {
175190
var remoteEphemeralKey = message.ephemeralKey.toArrayBuffer();
176191

177192
if (session === undefined) {
178-
throw new Error("No session found to decrypt message from " + this.remoteAddress.toString());
193+
return Promise.reject(new Error("No session found to decrypt message from " + this.remoteAddress.toString()));
179194
}
180195
if (session.indexInfo.closed != -1) {
181196
console.log('decrypting message for closed session');

src/SessionRecord.js

+21
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,26 @@ Internal.SessionRecord = function() {
184184
throw new Error("Had open sessions on a record that had no registrationId set");
185185
}
186186
},
187+
getSessions: function() {
188+
// return an array of sessions ordered by time closed,
189+
// followed by the open session
190+
var list = [];
191+
var openSession;
192+
for (var k in this._sessions) {
193+
if (this._sessions[k].indexInfo.closed === -1) {
194+
openSession = this._sessions[k];
195+
} else {
196+
list.push(this._sessions[k]);
197+
}
198+
}
199+
list = list.sort(function(s1, s2) {
200+
return s1.indexInfo.closed - s2.indexInfo.closed;
201+
});
202+
if (openSession) {
203+
list.push(openSession);
204+
}
205+
return list;
206+
},
187207
archiveCurrentState: function() {
188208
var open_session = this.getOpenSession();
189209
if (open_session !== undefined) {
@@ -195,6 +215,7 @@ Internal.SessionRecord = function() {
195215
if (session.indexInfo.closed > -1) {
196216
return;
197217
}
218+
console.log('closing session', session.indexInfo.baseKey);
198219

199220
// After this has run, we can still receive messages on ratchet chains which
200221
// were already open (unless we know we dont need them),

0 commit comments

Comments
 (0)