Update libaxolotl
// FREEBIE
This commit is contained in:
parent
7e2c6fd6bc
commit
af392c077d
2 changed files with 284 additions and 304 deletions
|
@ -34277,15 +34277,6 @@ window.axolotl.protocol = function(storage_interface) {
|
||||||
// (also the time between signedPreKey regenerations)
|
// (also the time between signedPreKey regenerations)
|
||||||
var MESSAGE_LOST_THRESHOLD_MS = 1000*60*60*24*7;
|
var MESSAGE_LOST_THRESHOLD_MS = 1000*60*60*24*7;
|
||||||
|
|
||||||
function objectContainsKeys(object) {
|
|
||||||
var count = 0;
|
|
||||||
for (var key in object) {
|
|
||||||
count++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return count != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function toString(thing) {
|
function toString(thing) {
|
||||||
if (typeof thing == 'string') {
|
if (typeof thing == 'string') {
|
||||||
return thing;
|
return thing;
|
||||||
|
@ -34549,100 +34540,103 @@ window.axolotl.protocol = function(storage_interface) {
|
||||||
var initSession = function(isInitiator, ourEphemeralKey, ourSignedKey, encodedNumber, theirIdentityPubKey, theirEphemeralPubKey, theirSignedPubKey) {
|
var initSession = function(isInitiator, ourEphemeralKey, ourSignedKey, encodedNumber, theirIdentityPubKey, theirEphemeralPubKey, theirSignedPubKey) {
|
||||||
return storage_interface.getMyIdentityKey().then(function(ourIdentityKey) {
|
return storage_interface.getMyIdentityKey().then(function(ourIdentityKey) {
|
||||||
if (isInitiator) {
|
if (isInitiator) {
|
||||||
if (ourSignedKey !== undefined)
|
if (ourSignedKey !== undefined) {
|
||||||
throw new Error("Invalid call to initSession");
|
throw new Error("Invalid call to initSession");
|
||||||
|
}
|
||||||
ourSignedKey = ourEphemeralKey;
|
ourSignedKey = ourEphemeralKey;
|
||||||
} else {
|
} else {
|
||||||
if (theirSignedPubKey !== undefined)
|
if (theirSignedPubKey !== undefined) {
|
||||||
throw new Error("Invalid call to initSession");
|
throw new Error("Invalid call to initSession");
|
||||||
|
}
|
||||||
theirSignedPubKey = theirEphemeralPubKey;
|
theirSignedPubKey = theirEphemeralPubKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
var sharedSecret;
|
var sharedSecret;
|
||||||
if (ourEphemeralKey === undefined || theirEphemeralPubKey === undefined)
|
if (ourEphemeralKey === undefined || theirEphemeralPubKey === undefined) {
|
||||||
sharedSecret = new Uint8Array(32 * 4);
|
sharedSecret = new Uint8Array(32 * 4);
|
||||||
else
|
} else {
|
||||||
sharedSecret = new Uint8Array(32 * 5);
|
sharedSecret = new Uint8Array(32 * 5);
|
||||||
|
}
|
||||||
|
|
||||||
for (var i = 0; i < 32; i++)
|
for (var i = 0; i < 32; i++) {
|
||||||
sharedSecret[i] = 0xff;
|
sharedSecret[i] = 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
return axolotlInternal.crypto.ECDHE(theirSignedPubKey, ourIdentityKey.privKey).then(function(ecRes1) {
|
return Promise.all([
|
||||||
function finishInit() {
|
axolotlInternal.crypto.ECDHE(theirSignedPubKey, ourIdentityKey.privKey),
|
||||||
return axolotlInternal.crypto.ECDHE(theirSignedPubKey, ourSignedKey.privKey).then(function(ecRes) {
|
axolotlInternal.crypto.ECDHE(theirIdentityPubKey, ourSignedKey.privKey),
|
||||||
sharedSecret.set(new Uint8Array(ecRes), 32 * 3);
|
axolotlInternal.crypto.ECDHE(theirSignedPubKey, ourSignedKey.privKey)
|
||||||
|
]).then(function(ecRes) {
|
||||||
|
if (isInitiator) {
|
||||||
|
sharedSecret.set(new Uint8Array(ecRes[0]), 32);
|
||||||
|
sharedSecret.set(new Uint8Array(ecRes[1]), 32 * 2);
|
||||||
|
} else {
|
||||||
|
sharedSecret.set(new Uint8Array(ecRes[0]), 32 * 2);
|
||||||
|
sharedSecret.set(new Uint8Array(ecRes[1]), 32)
|
||||||
|
}
|
||||||
|
sharedSecret.set(new Uint8Array(ecRes[2]), 32 * 3);
|
||||||
|
|
||||||
return HKDF(sharedSecret.buffer, '', "WhisperText").then(function(masterKey) {
|
if (ourEphemeralKey !== undefined && theirEphemeralPubKey !== undefined) {
|
||||||
var session = {currentRatchet: { rootKey: masterKey[0], lastRemoteEphemeralKey: theirSignedPubKey, previousCounter: 0 },
|
return axolotlInternal.crypto.ECDHE(
|
||||||
indexInfo: { remoteIdentityKey: theirIdentityPubKey, closed: -1 },
|
theirEphemeralPubKey, ourEphemeralKey.privKey
|
||||||
oldRatchetList: []
|
).then(function(ecRes4) {
|
||||||
};
|
sharedSecret.set(new Uint8Array(ecRes4), 32 * 4);
|
||||||
if (!isInitiator)
|
|
||||||
session.indexInfo.baseKey = theirEphemeralPubKey;
|
|
||||||
else
|
|
||||||
session.indexInfo.baseKey = ourEphemeralKey.pubKey;
|
|
||||||
|
|
||||||
// If we're initiating we go ahead and set our first sending ephemeral key now,
|
|
||||||
// otherwise we figure it out when we first maybeStepRatchet with the remote's ephemeral key
|
|
||||||
if (isInitiator) {
|
|
||||||
return axolotlInternal.crypto.createKeyPair().then(function(ourSendingEphemeralKey) {
|
|
||||||
session.currentRatchet.ephemeralKeyPair = ourSendingEphemeralKey;
|
|
||||||
return calculateRatchet(session, theirSignedPubKey, true).then(function() {
|
|
||||||
return session;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
session.currentRatchet.ephemeralKeyPair = ourSignedKey;
|
|
||||||
return session;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}).then(function() {
|
||||||
|
return HKDF(sharedSecret.buffer, '', "WhisperText");
|
||||||
|
}).then(function(masterKey) {
|
||||||
|
var session = {
|
||||||
|
currentRatchet: {
|
||||||
|
rootKey : masterKey[0],
|
||||||
|
lastRemoteEphemeralKey : theirSignedPubKey,
|
||||||
|
previousCounter : 0
|
||||||
|
},
|
||||||
|
indexInfo: {
|
||||||
|
remoteIdentityKey : theirIdentityPubKey,
|
||||||
|
closed : -1
|
||||||
|
},
|
||||||
|
oldRatchetList: []
|
||||||
|
};
|
||||||
|
|
||||||
var promise;
|
// If we're initiating we go ahead and set our first sending ephemeral key now,
|
||||||
if (ourEphemeralKey === undefined || theirEphemeralPubKey === undefined)
|
// otherwise we figure it out when we first maybeStepRatchet with the remote's ephemeral key
|
||||||
promise = Promise.resolve(new ArrayBuffer(0));
|
if (isInitiator) {
|
||||||
else
|
session.indexInfo.baseKey = ourEphemeralKey.pubKey;
|
||||||
promise = axolotlInternal.crypto.ECDHE(theirEphemeralPubKey, ourEphemeralKey.privKey);
|
return axolotlInternal.crypto.createKeyPair().then(function(ourSendingEphemeralKey) {
|
||||||
return promise.then(function(ecRes4) {
|
session.currentRatchet.ephemeralKeyPair = ourSendingEphemeralKey;
|
||||||
sharedSecret.set(new Uint8Array(ecRes4), 32 * 4);
|
return calculateRatchet(session, theirSignedPubKey, true).then(function() {
|
||||||
|
return session;
|
||||||
if (isInitiator)
|
});
|
||||||
return axolotlInternal.crypto.ECDHE(theirIdentityPubKey, ourSignedKey.privKey).then(function(ecRes2) {
|
});
|
||||||
sharedSecret.set(new Uint8Array(ecRes1), 32);
|
} else {
|
||||||
sharedSecret.set(new Uint8Array(ecRes2), 32 * 2);
|
session.indexInfo.baseKey = theirEphemeralPubKey;
|
||||||
}).then(finishInit);
|
session.currentRatchet.ephemeralKeyPair = ourSignedKey;
|
||||||
else
|
return session;
|
||||||
return axolotlInternal.crypto.ECDHE(theirIdentityPubKey, ourSignedKey.privKey).then(function(ecRes2) {
|
}
|
||||||
sharedSecret.set(new Uint8Array(ecRes1), 32 * 2);
|
|
||||||
sharedSecret.set(new Uint8Array(ecRes2), 32)
|
|
||||||
}).then(finishInit);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var removeOldChains = function(session) {
|
var removeOldChains = function(session) {
|
||||||
// Sending ratchets are always removed when we step because we never need them again
|
// Sending ratchets are always removed when we step because we never need them again
|
||||||
// Receiving ratchets are either removed if we step with all keys used up to previousCounter
|
// Receiving ratchets are added to the oldRatchetList, which we parse
|
||||||
// and are otherwise added to the oldRatchetList, which we parse here and remove ratchets
|
// here and remove all but the last four.
|
||||||
// older than a week (we assume the message was lost and move on with our lives at that point)
|
while (session.oldRatchetList.length > 4) {
|
||||||
var newList = [];
|
var index = 0;
|
||||||
for (var i = 0; i < session.oldRatchetList.length; i++) {
|
var oldest = session.oldRatchetList[0];
|
||||||
var entry = session.oldRatchetList[i];
|
for (var i = 0; i < session.oldRatchetList.length; i++) {
|
||||||
var ratchet = toString(entry.ephemeralKey);
|
if (session.oldRatchetList[i].added < oldest.added) {
|
||||||
console.log("Checking old chain with added time " + (entry.added/1000));
|
oldest = session.oldRatchetList[i];
|
||||||
if ((!objectContainsKeys(session[ratchet].messageKeys) && (session[ratchet].chainKey === undefined || session[ratchet].chainKey.key === undefined))
|
index = i;
|
||||||
|| entry.added < Date.now() - MESSAGE_LOST_THRESHOLD_MS) {
|
}
|
||||||
delete session[ratchet];
|
}
|
||||||
console.log("...deleted");
|
delete session[toString(oldest.ephemeralKey)];
|
||||||
} else
|
session.oldRatchetList.splice(index, 1);
|
||||||
newList[newList.length] = entry;
|
|
||||||
}
|
}
|
||||||
session.oldRatchetList = newList;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var closeSession = function(session, sessionClosedByRemote) {
|
var closeSession = function(session) {
|
||||||
if (session.indexInfo.closed > -1)
|
if (session.indexInfo.closed > -1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -34655,10 +34649,9 @@ window.axolotl.protocol = function(storage_interface) {
|
||||||
// Move all receive ratchets to the oldRatchetList to mark them for deletion
|
// Move all receive ratchets to the oldRatchetList to mark them for deletion
|
||||||
for (var i in session) {
|
for (var i in session) {
|
||||||
if (session[i].chainKey !== undefined && session[i].chainKey.key !== undefined) {
|
if (session[i].chainKey !== undefined && session[i].chainKey.key !== undefined) {
|
||||||
if (!sessionClosedByRemote)
|
session.oldRatchetList[session.oldRatchetList.length] = {
|
||||||
session.oldRatchetList[session.oldRatchetList.length] = { added: Date.now(), ephemeralKey: i };
|
added: Date.now(), ephemeralKey: i
|
||||||
else
|
};
|
||||||
delete session[i].chainKey.key;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Delete current root key and our ephemeral key pair to disallow ratchet stepping
|
// Delete current root key and our ephemeral key pair to disallow ratchet stepping
|
||||||
|
@ -34709,10 +34702,14 @@ window.axolotl.protocol = function(storage_interface) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (message.preKeyId && !preKeyPair) {
|
if (message.preKeyId && !preKeyPair) {
|
||||||
console.log('Invalid prekey id');
|
console.log('Invalid prekey id');
|
||||||
}
|
}
|
||||||
return initSession(false, preKeyPair, signedPreKeyPair, encodedNumber, toArrayBuffer(message.identityKey), toArrayBuffer(message.baseKey), undefined)
|
return initSession(false, preKeyPair, signedPreKeyPair,
|
||||||
.then(function(new_session) {
|
encodedNumber,
|
||||||
|
message.identityKey.toArrayBuffer(),
|
||||||
|
message.baseKey.toArrayBuffer(),
|
||||||
|
undefined
|
||||||
|
).then(function(new_session) {
|
||||||
// Note that the session is not actually saved until the very end of decryptWhisperMessage
|
// Note that the session is not actually saved until the very end of decryptWhisperMessage
|
||||||
// ... to ensure that the sender actually holds the private keys for all reported pubkeys
|
// ... to ensure that the sender actually holds the private keys for all reported pubkeys
|
||||||
return [new_session, function() {
|
return [new_session, function() {
|
||||||
|
@ -34752,7 +34749,7 @@ window.axolotl.protocol = function(storage_interface) {
|
||||||
return fillMessageKeys(chain, counter);
|
return fillMessageKeys(chain, counter);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
var maybeStepRatchet = function(session, remoteKey, previousCounter) {
|
var maybeStepRatchet = function(session, remoteKey, previousCounter) {
|
||||||
if (session[toString(remoteKey)] !== undefined)
|
if (session[toString(remoteKey)] !== undefined)
|
||||||
|
@ -34760,7 +34757,18 @@ window.axolotl.protocol = function(storage_interface) {
|
||||||
|
|
||||||
var ratchet = session.currentRatchet;
|
var ratchet = session.currentRatchet;
|
||||||
|
|
||||||
var finish = function() {
|
return Promise.resolve().then(function() {
|
||||||
|
var previousRatchet = session[toString(ratchet.lastRemoteEphemeralKey)];
|
||||||
|
if (previousRatchet !== undefined) {
|
||||||
|
return fillMessageKeys(previousRatchet, previousCounter).then(function() {
|
||||||
|
delete previousRatchet.chainKey.key;
|
||||||
|
session.oldRatchetList[session.oldRatchetList.length] = {
|
||||||
|
added : Date.now(),
|
||||||
|
ephemeralKey : ratchet.lastRemoteEphemeralKey
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).then(function() {
|
||||||
return calculateRatchet(session, remoteKey, false).then(function() {
|
return calculateRatchet(session, remoteKey, false).then(function() {
|
||||||
// Now swap the ephemeral key and calculate the new sending chain
|
// Now swap the ephemeral key and calculate the new sending chain
|
||||||
var previousRatchet = toString(ratchet.ephemeralKeyPair.pubKey);
|
var previousRatchet = toString(ratchet.ephemeralKeyPair.pubKey);
|
||||||
|
@ -34776,20 +34784,8 @@ window.axolotl.protocol = function(storage_interface) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
};
|
||||||
var previousRatchet = session[toString(ratchet.lastRemoteEphemeralKey)];
|
|
||||||
if (previousRatchet !== undefined) {
|
|
||||||
return fillMessageKeys(previousRatchet, previousCounter).then(function() {
|
|
||||||
delete previousRatchet.chainKey.key;
|
|
||||||
if (!objectContainsKeys(previousRatchet.messageKeys))
|
|
||||||
delete session[toString(ratchet.lastRemoteEphemeralKey)];
|
|
||||||
else
|
|
||||||
session.oldRatchetList[session.oldRatchetList.length] = { added: Date.now(), ephemeralKey: ratchet.lastRemoteEphemeralKey };
|
|
||||||
}).then(finish);
|
|
||||||
} else
|
|
||||||
return finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
var doDecryptWhisperMessage = function(encodedNumber, messageBytes, session, registrationId) {
|
var doDecryptWhisperMessage = function(encodedNumber, messageBytes, session, registrationId) {
|
||||||
if (!messageBytes instanceof ArrayBuffer) {
|
if (!messageBytes instanceof ArrayBuffer) {
|
||||||
|
@ -34805,18 +34801,19 @@ window.axolotl.protocol = function(storage_interface) {
|
||||||
var message = axolotlInternal.protobuf.WhisperMessage.decode(messageProto);
|
var message = axolotlInternal.protobuf.WhisperMessage.decode(messageProto);
|
||||||
var remoteEphemeralKey = message.ephemeralKey.toArrayBuffer();
|
var remoteEphemeralKey = message.ephemeralKey.toArrayBuffer();
|
||||||
|
|
||||||
var promise;
|
return Promise.resolve().then(function() {
|
||||||
if (session === undefined) {
|
if (session === undefined) {
|
||||||
promise = crypto_storage.getSessionByRemoteEphemeralKey(encodedNumber, remoteEphemeralKey).then(function(session) {
|
return crypto_storage.getSessionByRemoteEphemeralKey(encodedNumber, remoteEphemeralKey);
|
||||||
if (session === undefined)
|
} else {
|
||||||
throw new Error("No session found to decrypt message from " + encodedNumber);
|
|
||||||
return session;
|
return session;
|
||||||
});
|
}
|
||||||
} else {
|
}).then(function(session) {
|
||||||
promise = Promise.resolve(session);
|
if (session === undefined) {
|
||||||
}
|
throw new Error("No session found to decrypt message from " + encodedNumber);
|
||||||
|
}
|
||||||
return promise.then(function(session) {
|
if (session.indexInfo.closed != -1) {
|
||||||
|
console.log('decrypting message for closed session');
|
||||||
|
}
|
||||||
return maybeStepRatchet(session, remoteEphemeralKey, message.previousCounter).then(function() {
|
return maybeStepRatchet(session, remoteEphemeralKey, message.previousCounter).then(function() {
|
||||||
var chain = session[toString(message.ephemeralKey)];
|
var chain = session[toString(message.ephemeralKey)];
|
||||||
|
|
||||||
|
@ -34827,47 +34824,40 @@ window.axolotl.protocol = function(storage_interface) {
|
||||||
e.name = 'MessageCounterError';
|
e.name = 'MessageCounterError';
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
return HKDF(toArrayBuffer(messageKey), '', "WhisperMessageKeys").then(function(keys) {
|
delete chain.messageKeys[message.counter];
|
||||||
return storage_interface.getMyIdentityKey().then(function(ourIdentityKey) {
|
return HKDF(toArrayBuffer(messageKey), '', "WhisperMessageKeys");
|
||||||
delete chain.messageKeys[message.counter];
|
});
|
||||||
|
}).then(function(keys) {
|
||||||
|
return storage_interface.getMyIdentityKey().then(function(ourIdentityKey) {
|
||||||
|
|
||||||
var macInput = new Uint8Array(messageProto.byteLength + 33*2 + 1);
|
var macInput = new Uint8Array(messageProto.byteLength + 33*2 + 1);
|
||||||
macInput.set(new Uint8Array(toArrayBuffer(session.indexInfo.remoteIdentityKey)));
|
macInput.set(new Uint8Array(toArrayBuffer(session.indexInfo.remoteIdentityKey)));
|
||||||
macInput.set(new Uint8Array(toArrayBuffer(ourIdentityKey.pubKey)), 33);
|
macInput.set(new Uint8Array(toArrayBuffer(ourIdentityKey.pubKey)), 33);
|
||||||
macInput[33*2] = (3 << 4) | 3;
|
macInput[33*2] = (3 << 4) | 3;
|
||||||
macInput.set(new Uint8Array(messageProto), 33*2 + 1);
|
macInput.set(new Uint8Array(messageProto), 33*2 + 1);
|
||||||
|
|
||||||
return verifyMAC(macInput.buffer, keys[1], mac, 8).then(function() {
|
return verifyMAC(macInput.buffer, keys[1], mac, 8);
|
||||||
return axolotlInternal.crypto.decrypt(
|
}).then(function() {
|
||||||
keys[0],
|
return axolotlInternal.crypto.decrypt(keys[0], message.ciphertext.toArrayBuffer(), keys[2].slice(0, 16));
|
||||||
message.ciphertext.toArrayBuffer(),
|
});
|
||||||
keys[2].slice(0, 16)
|
}).then(function(paddedPlaintext) {
|
||||||
).then(function(paddedPlaintext) {
|
paddedPlaintext = new Uint8Array(paddedPlaintext);
|
||||||
paddedPlaintext = new Uint8Array(paddedPlaintext);
|
var plaintext;
|
||||||
var plaintext;
|
for (var i = paddedPlaintext.length - 1; i >= 0; i--) {
|
||||||
for (var i = paddedPlaintext.length - 1; i >= 0; i--) {
|
if (paddedPlaintext[i] == 0x80) {
|
||||||
if (paddedPlaintext[i] == 0x80) {
|
plaintext = new Uint8Array(i);
|
||||||
plaintext = new Uint8Array(i);
|
plaintext.set(paddedPlaintext.subarray(0, i));
|
||||||
plaintext.set(paddedPlaintext.subarray(0, i));
|
plaintext = plaintext.buffer;
|
||||||
plaintext = plaintext.buffer;
|
break;
|
||||||
break;
|
} else if (paddedPlaintext[i] != 0x00) {
|
||||||
} else if (paddedPlaintext[i] != 0x00)
|
throw new Error('Invalid padding');
|
||||||
throw new Error('Invalid padding');
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete session['pendingPreKey'];
|
delete session['pendingPreKey'];
|
||||||
removeOldChains(session);
|
removeOldChains(session);
|
||||||
return crypto_storage.saveSession(encodedNumber, session, registrationId).then(function() {
|
return crypto_storage.saveSession(encodedNumber, session, registrationId).then(function() {
|
||||||
return [plaintext, function() {
|
return [plaintext];
|
||||||
closeSession(session, true);
|
|
||||||
removeOldChains(session);
|
|
||||||
return crypto_storage.saveSession(encodedNumber, session);
|
|
||||||
}];
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -34979,7 +34969,8 @@ window.axolotl.protocol = function(storage_interface) {
|
||||||
return initSession(true, baseKey, undefined,
|
return initSession(true, baseKey, undefined,
|
||||||
deviceObject.encodedNumber, deviceIdentityKey,
|
deviceObject.encodedNumber, deviceIdentityKey,
|
||||||
toArrayBuffer(deviceObject.preKey),
|
toArrayBuffer(deviceObject.preKey),
|
||||||
deviceSignedKey).then(function(new_session) {
|
deviceSignedKey
|
||||||
|
).then(function(new_session) {
|
||||||
session = new_session;
|
session = new_session;
|
||||||
session.pendingPreKey = { preKeyId: deviceObject.preKeyId, signedKeyId: deviceObject.signedKeyId, baseKey: baseKey.pubKey };
|
session.pendingPreKey = { preKeyId: deviceObject.preKeyId, signedKeyId: deviceObject.signedKeyId, baseKey: baseKey.pubKey };
|
||||||
return doEncryptPushMessageContent().then(function(message) {
|
return doEncryptPushMessageContent().then(function(message) {
|
||||||
|
@ -37051,20 +37042,18 @@ MessageReceiver.prototype.extend({
|
||||||
handleLegacyMessage: function (envelope) {
|
handleLegacyMessage: function (envelope) {
|
||||||
return this.decrypt(envelope, envelope.legacyMessage).then(function(result) {
|
return this.decrypt(envelope, envelope.legacyMessage).then(function(result) {
|
||||||
var plaintext = result[0]; // array buffer
|
var plaintext = result[0]; // array buffer
|
||||||
var close_session = result[1]; // function
|
|
||||||
var message = textsecure.protobuf.DataMessage.decode(plaintext);
|
var message = textsecure.protobuf.DataMessage.decode(plaintext);
|
||||||
return this.handleDataMessage(envelope, message, close_session);
|
return this.handleDataMessage(envelope, message);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
handleContentMessage: function (envelope) {
|
handleContentMessage: function (envelope) {
|
||||||
return this.decrypt(envelope, envelope.content).then(function(result) {
|
return this.decrypt(envelope, envelope.content).then(function(result) {
|
||||||
var plaintext = result[0]; // array buffer
|
var plaintext = result[0]; // array buffer
|
||||||
var close_session = result[1]; // function
|
|
||||||
var content = textsecure.protobuf.Content.decode(plaintext);
|
var content = textsecure.protobuf.Content.decode(plaintext);
|
||||||
if (content.syncMessage) {
|
if (content.syncMessage) {
|
||||||
return this.handleSyncMessage(envelope, content.syncMessage);
|
return this.handleSyncMessage(envelope, content.syncMessage);
|
||||||
} else if (content.dataMessage) {
|
} else if (content.dataMessage) {
|
||||||
return this.handleDataMessage(envelope, content.dataMessage, close_session);
|
return this.handleDataMessage(envelope, content.dataMessage);
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Got Content message with no dataMessage and no syncMessage');
|
throw new Error('Got Content message with no dataMessage and no syncMessage');
|
||||||
}
|
}
|
||||||
|
|
|
@ -34163,15 +34163,6 @@ window.axolotl.protocol = function(storage_interface) {
|
||||||
// (also the time between signedPreKey regenerations)
|
// (also the time between signedPreKey regenerations)
|
||||||
var MESSAGE_LOST_THRESHOLD_MS = 1000*60*60*24*7;
|
var MESSAGE_LOST_THRESHOLD_MS = 1000*60*60*24*7;
|
||||||
|
|
||||||
function objectContainsKeys(object) {
|
|
||||||
var count = 0;
|
|
||||||
for (var key in object) {
|
|
||||||
count++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return count != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function toString(thing) {
|
function toString(thing) {
|
||||||
if (typeof thing == 'string') {
|
if (typeof thing == 'string') {
|
||||||
return thing;
|
return thing;
|
||||||
|
@ -34435,100 +34426,103 @@ window.axolotl.protocol = function(storage_interface) {
|
||||||
var initSession = function(isInitiator, ourEphemeralKey, ourSignedKey, encodedNumber, theirIdentityPubKey, theirEphemeralPubKey, theirSignedPubKey) {
|
var initSession = function(isInitiator, ourEphemeralKey, ourSignedKey, encodedNumber, theirIdentityPubKey, theirEphemeralPubKey, theirSignedPubKey) {
|
||||||
return storage_interface.getMyIdentityKey().then(function(ourIdentityKey) {
|
return storage_interface.getMyIdentityKey().then(function(ourIdentityKey) {
|
||||||
if (isInitiator) {
|
if (isInitiator) {
|
||||||
if (ourSignedKey !== undefined)
|
if (ourSignedKey !== undefined) {
|
||||||
throw new Error("Invalid call to initSession");
|
throw new Error("Invalid call to initSession");
|
||||||
|
}
|
||||||
ourSignedKey = ourEphemeralKey;
|
ourSignedKey = ourEphemeralKey;
|
||||||
} else {
|
} else {
|
||||||
if (theirSignedPubKey !== undefined)
|
if (theirSignedPubKey !== undefined) {
|
||||||
throw new Error("Invalid call to initSession");
|
throw new Error("Invalid call to initSession");
|
||||||
|
}
|
||||||
theirSignedPubKey = theirEphemeralPubKey;
|
theirSignedPubKey = theirEphemeralPubKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
var sharedSecret;
|
var sharedSecret;
|
||||||
if (ourEphemeralKey === undefined || theirEphemeralPubKey === undefined)
|
if (ourEphemeralKey === undefined || theirEphemeralPubKey === undefined) {
|
||||||
sharedSecret = new Uint8Array(32 * 4);
|
sharedSecret = new Uint8Array(32 * 4);
|
||||||
else
|
} else {
|
||||||
sharedSecret = new Uint8Array(32 * 5);
|
sharedSecret = new Uint8Array(32 * 5);
|
||||||
|
}
|
||||||
|
|
||||||
for (var i = 0; i < 32; i++)
|
for (var i = 0; i < 32; i++) {
|
||||||
sharedSecret[i] = 0xff;
|
sharedSecret[i] = 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
return axolotlInternal.crypto.ECDHE(theirSignedPubKey, ourIdentityKey.privKey).then(function(ecRes1) {
|
return Promise.all([
|
||||||
function finishInit() {
|
axolotlInternal.crypto.ECDHE(theirSignedPubKey, ourIdentityKey.privKey),
|
||||||
return axolotlInternal.crypto.ECDHE(theirSignedPubKey, ourSignedKey.privKey).then(function(ecRes) {
|
axolotlInternal.crypto.ECDHE(theirIdentityPubKey, ourSignedKey.privKey),
|
||||||
sharedSecret.set(new Uint8Array(ecRes), 32 * 3);
|
axolotlInternal.crypto.ECDHE(theirSignedPubKey, ourSignedKey.privKey)
|
||||||
|
]).then(function(ecRes) {
|
||||||
|
if (isInitiator) {
|
||||||
|
sharedSecret.set(new Uint8Array(ecRes[0]), 32);
|
||||||
|
sharedSecret.set(new Uint8Array(ecRes[1]), 32 * 2);
|
||||||
|
} else {
|
||||||
|
sharedSecret.set(new Uint8Array(ecRes[0]), 32 * 2);
|
||||||
|
sharedSecret.set(new Uint8Array(ecRes[1]), 32)
|
||||||
|
}
|
||||||
|
sharedSecret.set(new Uint8Array(ecRes[2]), 32 * 3);
|
||||||
|
|
||||||
return HKDF(sharedSecret.buffer, '', "WhisperText").then(function(masterKey) {
|
if (ourEphemeralKey !== undefined && theirEphemeralPubKey !== undefined) {
|
||||||
var session = {currentRatchet: { rootKey: masterKey[0], lastRemoteEphemeralKey: theirSignedPubKey, previousCounter: 0 },
|
return axolotlInternal.crypto.ECDHE(
|
||||||
indexInfo: { remoteIdentityKey: theirIdentityPubKey, closed: -1 },
|
theirEphemeralPubKey, ourEphemeralKey.privKey
|
||||||
oldRatchetList: []
|
).then(function(ecRes4) {
|
||||||
};
|
sharedSecret.set(new Uint8Array(ecRes4), 32 * 4);
|
||||||
if (!isInitiator)
|
|
||||||
session.indexInfo.baseKey = theirEphemeralPubKey;
|
|
||||||
else
|
|
||||||
session.indexInfo.baseKey = ourEphemeralKey.pubKey;
|
|
||||||
|
|
||||||
// If we're initiating we go ahead and set our first sending ephemeral key now,
|
|
||||||
// otherwise we figure it out when we first maybeStepRatchet with the remote's ephemeral key
|
|
||||||
if (isInitiator) {
|
|
||||||
return axolotlInternal.crypto.createKeyPair().then(function(ourSendingEphemeralKey) {
|
|
||||||
session.currentRatchet.ephemeralKeyPair = ourSendingEphemeralKey;
|
|
||||||
return calculateRatchet(session, theirSignedPubKey, true).then(function() {
|
|
||||||
return session;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
session.currentRatchet.ephemeralKeyPair = ourSignedKey;
|
|
||||||
return session;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}).then(function() {
|
||||||
|
return HKDF(sharedSecret.buffer, '', "WhisperText");
|
||||||
|
}).then(function(masterKey) {
|
||||||
|
var session = {
|
||||||
|
currentRatchet: {
|
||||||
|
rootKey : masterKey[0],
|
||||||
|
lastRemoteEphemeralKey : theirSignedPubKey,
|
||||||
|
previousCounter : 0
|
||||||
|
},
|
||||||
|
indexInfo: {
|
||||||
|
remoteIdentityKey : theirIdentityPubKey,
|
||||||
|
closed : -1
|
||||||
|
},
|
||||||
|
oldRatchetList: []
|
||||||
|
};
|
||||||
|
|
||||||
var promise;
|
// If we're initiating we go ahead and set our first sending ephemeral key now,
|
||||||
if (ourEphemeralKey === undefined || theirEphemeralPubKey === undefined)
|
// otherwise we figure it out when we first maybeStepRatchet with the remote's ephemeral key
|
||||||
promise = Promise.resolve(new ArrayBuffer(0));
|
if (isInitiator) {
|
||||||
else
|
session.indexInfo.baseKey = ourEphemeralKey.pubKey;
|
||||||
promise = axolotlInternal.crypto.ECDHE(theirEphemeralPubKey, ourEphemeralKey.privKey);
|
return axolotlInternal.crypto.createKeyPair().then(function(ourSendingEphemeralKey) {
|
||||||
return promise.then(function(ecRes4) {
|
session.currentRatchet.ephemeralKeyPair = ourSendingEphemeralKey;
|
||||||
sharedSecret.set(new Uint8Array(ecRes4), 32 * 4);
|
return calculateRatchet(session, theirSignedPubKey, true).then(function() {
|
||||||
|
return session;
|
||||||
if (isInitiator)
|
});
|
||||||
return axolotlInternal.crypto.ECDHE(theirIdentityPubKey, ourSignedKey.privKey).then(function(ecRes2) {
|
});
|
||||||
sharedSecret.set(new Uint8Array(ecRes1), 32);
|
} else {
|
||||||
sharedSecret.set(new Uint8Array(ecRes2), 32 * 2);
|
session.indexInfo.baseKey = theirEphemeralPubKey;
|
||||||
}).then(finishInit);
|
session.currentRatchet.ephemeralKeyPair = ourSignedKey;
|
||||||
else
|
return session;
|
||||||
return axolotlInternal.crypto.ECDHE(theirIdentityPubKey, ourSignedKey.privKey).then(function(ecRes2) {
|
}
|
||||||
sharedSecret.set(new Uint8Array(ecRes1), 32 * 2);
|
|
||||||
sharedSecret.set(new Uint8Array(ecRes2), 32)
|
|
||||||
}).then(finishInit);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var removeOldChains = function(session) {
|
var removeOldChains = function(session) {
|
||||||
// Sending ratchets are always removed when we step because we never need them again
|
// Sending ratchets are always removed when we step because we never need them again
|
||||||
// Receiving ratchets are either removed if we step with all keys used up to previousCounter
|
// Receiving ratchets are added to the oldRatchetList, which we parse
|
||||||
// and are otherwise added to the oldRatchetList, which we parse here and remove ratchets
|
// here and remove all but the last four.
|
||||||
// older than a week (we assume the message was lost and move on with our lives at that point)
|
while (session.oldRatchetList.length > 4) {
|
||||||
var newList = [];
|
var index = 0;
|
||||||
for (var i = 0; i < session.oldRatchetList.length; i++) {
|
var oldest = session.oldRatchetList[0];
|
||||||
var entry = session.oldRatchetList[i];
|
for (var i = 0; i < session.oldRatchetList.length; i++) {
|
||||||
var ratchet = toString(entry.ephemeralKey);
|
if (session.oldRatchetList[i].added < oldest.added) {
|
||||||
console.log("Checking old chain with added time " + (entry.added/1000));
|
oldest = session.oldRatchetList[i];
|
||||||
if ((!objectContainsKeys(session[ratchet].messageKeys) && (session[ratchet].chainKey === undefined || session[ratchet].chainKey.key === undefined))
|
index = i;
|
||||||
|| entry.added < Date.now() - MESSAGE_LOST_THRESHOLD_MS) {
|
}
|
||||||
delete session[ratchet];
|
}
|
||||||
console.log("...deleted");
|
delete session[toString(oldest.ephemeralKey)];
|
||||||
} else
|
session.oldRatchetList.splice(index, 1);
|
||||||
newList[newList.length] = entry;
|
|
||||||
}
|
}
|
||||||
session.oldRatchetList = newList;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var closeSession = function(session, sessionClosedByRemote) {
|
var closeSession = function(session) {
|
||||||
if (session.indexInfo.closed > -1)
|
if (session.indexInfo.closed > -1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -34541,10 +34535,9 @@ window.axolotl.protocol = function(storage_interface) {
|
||||||
// Move all receive ratchets to the oldRatchetList to mark them for deletion
|
// Move all receive ratchets to the oldRatchetList to mark them for deletion
|
||||||
for (var i in session) {
|
for (var i in session) {
|
||||||
if (session[i].chainKey !== undefined && session[i].chainKey.key !== undefined) {
|
if (session[i].chainKey !== undefined && session[i].chainKey.key !== undefined) {
|
||||||
if (!sessionClosedByRemote)
|
session.oldRatchetList[session.oldRatchetList.length] = {
|
||||||
session.oldRatchetList[session.oldRatchetList.length] = { added: Date.now(), ephemeralKey: i };
|
added: Date.now(), ephemeralKey: i
|
||||||
else
|
};
|
||||||
delete session[i].chainKey.key;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Delete current root key and our ephemeral key pair to disallow ratchet stepping
|
// Delete current root key and our ephemeral key pair to disallow ratchet stepping
|
||||||
|
@ -34595,10 +34588,14 @@ window.axolotl.protocol = function(storage_interface) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (message.preKeyId && !preKeyPair) {
|
if (message.preKeyId && !preKeyPair) {
|
||||||
console.log('Invalid prekey id');
|
console.log('Invalid prekey id');
|
||||||
}
|
}
|
||||||
return initSession(false, preKeyPair, signedPreKeyPair, encodedNumber, toArrayBuffer(message.identityKey), toArrayBuffer(message.baseKey), undefined)
|
return initSession(false, preKeyPair, signedPreKeyPair,
|
||||||
.then(function(new_session) {
|
encodedNumber,
|
||||||
|
message.identityKey.toArrayBuffer(),
|
||||||
|
message.baseKey.toArrayBuffer(),
|
||||||
|
undefined
|
||||||
|
).then(function(new_session) {
|
||||||
// Note that the session is not actually saved until the very end of decryptWhisperMessage
|
// Note that the session is not actually saved until the very end of decryptWhisperMessage
|
||||||
// ... to ensure that the sender actually holds the private keys for all reported pubkeys
|
// ... to ensure that the sender actually holds the private keys for all reported pubkeys
|
||||||
return [new_session, function() {
|
return [new_session, function() {
|
||||||
|
@ -34638,7 +34635,7 @@ window.axolotl.protocol = function(storage_interface) {
|
||||||
return fillMessageKeys(chain, counter);
|
return fillMessageKeys(chain, counter);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
var maybeStepRatchet = function(session, remoteKey, previousCounter) {
|
var maybeStepRatchet = function(session, remoteKey, previousCounter) {
|
||||||
if (session[toString(remoteKey)] !== undefined)
|
if (session[toString(remoteKey)] !== undefined)
|
||||||
|
@ -34646,7 +34643,18 @@ window.axolotl.protocol = function(storage_interface) {
|
||||||
|
|
||||||
var ratchet = session.currentRatchet;
|
var ratchet = session.currentRatchet;
|
||||||
|
|
||||||
var finish = function() {
|
return Promise.resolve().then(function() {
|
||||||
|
var previousRatchet = session[toString(ratchet.lastRemoteEphemeralKey)];
|
||||||
|
if (previousRatchet !== undefined) {
|
||||||
|
return fillMessageKeys(previousRatchet, previousCounter).then(function() {
|
||||||
|
delete previousRatchet.chainKey.key;
|
||||||
|
session.oldRatchetList[session.oldRatchetList.length] = {
|
||||||
|
added : Date.now(),
|
||||||
|
ephemeralKey : ratchet.lastRemoteEphemeralKey
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).then(function() {
|
||||||
return calculateRatchet(session, remoteKey, false).then(function() {
|
return calculateRatchet(session, remoteKey, false).then(function() {
|
||||||
// Now swap the ephemeral key and calculate the new sending chain
|
// Now swap the ephemeral key and calculate the new sending chain
|
||||||
var previousRatchet = toString(ratchet.ephemeralKeyPair.pubKey);
|
var previousRatchet = toString(ratchet.ephemeralKeyPair.pubKey);
|
||||||
|
@ -34662,20 +34670,8 @@ window.axolotl.protocol = function(storage_interface) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
};
|
||||||
var previousRatchet = session[toString(ratchet.lastRemoteEphemeralKey)];
|
|
||||||
if (previousRatchet !== undefined) {
|
|
||||||
return fillMessageKeys(previousRatchet, previousCounter).then(function() {
|
|
||||||
delete previousRatchet.chainKey.key;
|
|
||||||
if (!objectContainsKeys(previousRatchet.messageKeys))
|
|
||||||
delete session[toString(ratchet.lastRemoteEphemeralKey)];
|
|
||||||
else
|
|
||||||
session.oldRatchetList[session.oldRatchetList.length] = { added: Date.now(), ephemeralKey: ratchet.lastRemoteEphemeralKey };
|
|
||||||
}).then(finish);
|
|
||||||
} else
|
|
||||||
return finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
var doDecryptWhisperMessage = function(encodedNumber, messageBytes, session, registrationId) {
|
var doDecryptWhisperMessage = function(encodedNumber, messageBytes, session, registrationId) {
|
||||||
if (!messageBytes instanceof ArrayBuffer) {
|
if (!messageBytes instanceof ArrayBuffer) {
|
||||||
|
@ -34691,18 +34687,19 @@ window.axolotl.protocol = function(storage_interface) {
|
||||||
var message = axolotlInternal.protobuf.WhisperMessage.decode(messageProto);
|
var message = axolotlInternal.protobuf.WhisperMessage.decode(messageProto);
|
||||||
var remoteEphemeralKey = message.ephemeralKey.toArrayBuffer();
|
var remoteEphemeralKey = message.ephemeralKey.toArrayBuffer();
|
||||||
|
|
||||||
var promise;
|
return Promise.resolve().then(function() {
|
||||||
if (session === undefined) {
|
if (session === undefined) {
|
||||||
promise = crypto_storage.getSessionByRemoteEphemeralKey(encodedNumber, remoteEphemeralKey).then(function(session) {
|
return crypto_storage.getSessionByRemoteEphemeralKey(encodedNumber, remoteEphemeralKey);
|
||||||
if (session === undefined)
|
} else {
|
||||||
throw new Error("No session found to decrypt message from " + encodedNumber);
|
|
||||||
return session;
|
return session;
|
||||||
});
|
}
|
||||||
} else {
|
}).then(function(session) {
|
||||||
promise = Promise.resolve(session);
|
if (session === undefined) {
|
||||||
}
|
throw new Error("No session found to decrypt message from " + encodedNumber);
|
||||||
|
}
|
||||||
return promise.then(function(session) {
|
if (session.indexInfo.closed != -1) {
|
||||||
|
console.log('decrypting message for closed session');
|
||||||
|
}
|
||||||
return maybeStepRatchet(session, remoteEphemeralKey, message.previousCounter).then(function() {
|
return maybeStepRatchet(session, remoteEphemeralKey, message.previousCounter).then(function() {
|
||||||
var chain = session[toString(message.ephemeralKey)];
|
var chain = session[toString(message.ephemeralKey)];
|
||||||
|
|
||||||
|
@ -34713,47 +34710,40 @@ window.axolotl.protocol = function(storage_interface) {
|
||||||
e.name = 'MessageCounterError';
|
e.name = 'MessageCounterError';
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
return HKDF(toArrayBuffer(messageKey), '', "WhisperMessageKeys").then(function(keys) {
|
delete chain.messageKeys[message.counter];
|
||||||
return storage_interface.getMyIdentityKey().then(function(ourIdentityKey) {
|
return HKDF(toArrayBuffer(messageKey), '', "WhisperMessageKeys");
|
||||||
delete chain.messageKeys[message.counter];
|
});
|
||||||
|
}).then(function(keys) {
|
||||||
|
return storage_interface.getMyIdentityKey().then(function(ourIdentityKey) {
|
||||||
|
|
||||||
var macInput = new Uint8Array(messageProto.byteLength + 33*2 + 1);
|
var macInput = new Uint8Array(messageProto.byteLength + 33*2 + 1);
|
||||||
macInput.set(new Uint8Array(toArrayBuffer(session.indexInfo.remoteIdentityKey)));
|
macInput.set(new Uint8Array(toArrayBuffer(session.indexInfo.remoteIdentityKey)));
|
||||||
macInput.set(new Uint8Array(toArrayBuffer(ourIdentityKey.pubKey)), 33);
|
macInput.set(new Uint8Array(toArrayBuffer(ourIdentityKey.pubKey)), 33);
|
||||||
macInput[33*2] = (3 << 4) | 3;
|
macInput[33*2] = (3 << 4) | 3;
|
||||||
macInput.set(new Uint8Array(messageProto), 33*2 + 1);
|
macInput.set(new Uint8Array(messageProto), 33*2 + 1);
|
||||||
|
|
||||||
return verifyMAC(macInput.buffer, keys[1], mac, 8).then(function() {
|
return verifyMAC(macInput.buffer, keys[1], mac, 8);
|
||||||
return axolotlInternal.crypto.decrypt(
|
}).then(function() {
|
||||||
keys[0],
|
return axolotlInternal.crypto.decrypt(keys[0], message.ciphertext.toArrayBuffer(), keys[2].slice(0, 16));
|
||||||
message.ciphertext.toArrayBuffer(),
|
});
|
||||||
keys[2].slice(0, 16)
|
}).then(function(paddedPlaintext) {
|
||||||
).then(function(paddedPlaintext) {
|
paddedPlaintext = new Uint8Array(paddedPlaintext);
|
||||||
paddedPlaintext = new Uint8Array(paddedPlaintext);
|
var plaintext;
|
||||||
var plaintext;
|
for (var i = paddedPlaintext.length - 1; i >= 0; i--) {
|
||||||
for (var i = paddedPlaintext.length - 1; i >= 0; i--) {
|
if (paddedPlaintext[i] == 0x80) {
|
||||||
if (paddedPlaintext[i] == 0x80) {
|
plaintext = new Uint8Array(i);
|
||||||
plaintext = new Uint8Array(i);
|
plaintext.set(paddedPlaintext.subarray(0, i));
|
||||||
plaintext.set(paddedPlaintext.subarray(0, i));
|
plaintext = plaintext.buffer;
|
||||||
plaintext = plaintext.buffer;
|
break;
|
||||||
break;
|
} else if (paddedPlaintext[i] != 0x00) {
|
||||||
} else if (paddedPlaintext[i] != 0x00)
|
throw new Error('Invalid padding');
|
||||||
throw new Error('Invalid padding');
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete session['pendingPreKey'];
|
delete session['pendingPreKey'];
|
||||||
removeOldChains(session);
|
removeOldChains(session);
|
||||||
return crypto_storage.saveSession(encodedNumber, session, registrationId).then(function() {
|
return crypto_storage.saveSession(encodedNumber, session, registrationId).then(function() {
|
||||||
return [plaintext, function() {
|
return [plaintext];
|
||||||
closeSession(session, true);
|
|
||||||
removeOldChains(session);
|
|
||||||
return crypto_storage.saveSession(encodedNumber, session);
|
|
||||||
}];
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -34865,7 +34855,8 @@ window.axolotl.protocol = function(storage_interface) {
|
||||||
return initSession(true, baseKey, undefined,
|
return initSession(true, baseKey, undefined,
|
||||||
deviceObject.encodedNumber, deviceIdentityKey,
|
deviceObject.encodedNumber, deviceIdentityKey,
|
||||||
toArrayBuffer(deviceObject.preKey),
|
toArrayBuffer(deviceObject.preKey),
|
||||||
deviceSignedKey).then(function(new_session) {
|
deviceSignedKey
|
||||||
|
).then(function(new_session) {
|
||||||
session = new_session;
|
session = new_session;
|
||||||
session.pendingPreKey = { preKeyId: deviceObject.preKeyId, signedKeyId: deviceObject.signedKeyId, baseKey: baseKey.pubKey };
|
session.pendingPreKey = { preKeyId: deviceObject.preKeyId, signedKeyId: deviceObject.signedKeyId, baseKey: baseKey.pubKey };
|
||||||
return doEncryptPushMessageContent().then(function(message) {
|
return doEncryptPushMessageContent().then(function(message) {
|
||||||
|
|
Loading…
Reference in a new issue