Update libaxolotl
// FREEBIE
This commit is contained in:
parent
0f4f00ff4e
commit
2d6d2a92b9
2 changed files with 302 additions and 248 deletions
|
@ -34672,55 +34672,66 @@ window.axolotl.protocol = function(storage_interface) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var initSessionFromPreKeyWhisperMessage = function(encodedNumber, message) {
|
var initSessionFromPreKeyWhisperMessage = function(encodedNumber, message) {
|
||||||
return storage_interface.getPreKey(message.preKeyId).then(function(preKeyPair) {
|
var preKeyPair, signedPreKeyPair, session;
|
||||||
return storage_interface.getSignedPreKey(message.signedPreKeyId).then(function(signedPreKeyPair) {
|
return Promise.all([
|
||||||
return crypto_storage.getSessionOrIdentityKeyByBaseKey(encodedNumber, toArrayBuffer(message.baseKey)).then(function(session) {
|
storage_interface.getPreKey(message.preKeyId),
|
||||||
return crypto_storage.getOpenSession(encodedNumber).then(function(open_session) {
|
storage_interface.getSignedPreKey(message.signedPreKeyId),
|
||||||
if (signedPreKeyPair === undefined) {
|
crypto_storage.getSessionOrIdentityKeyByBaseKey(encodedNumber, toArrayBuffer(message.baseKey))
|
||||||
// Session may or may not be the right one, but if its not, we can't do anything about it
|
]).then(function(results) {
|
||||||
// ...fall through and let decryptWhisperMessage handle that case
|
preKeyPair = results[0];
|
||||||
if (session !== undefined && session.currentRatchet !== undefined)
|
signedPreKeyPair = results[1];
|
||||||
return Promise.resolve([session, undefined]);
|
session = results[2];
|
||||||
else
|
return crypto_storage.getOpenSession(encodedNumber);
|
||||||
throw new Error("Missing Signed PreKey for PreKeyWhisperMessage");
|
}).then(function(open_session) {
|
||||||
}
|
if (signedPreKeyPair === undefined) {
|
||||||
if (session !== undefined) {
|
// Session may or may not be the right one, but if its not, we
|
||||||
// Duplicate PreKeyMessage for session:
|
// can't do anything about it ...fall through and let
|
||||||
if (util.isEqual(session.indexInfo.baseKey, message.baseKey))
|
// decryptWhisperMessage handle that case
|
||||||
return Promise.resolve([session, undefined]);
|
if (session !== undefined && session.currentRatchet !== undefined)
|
||||||
|
return Promise.resolve([session, undefined]);
|
||||||
|
else
|
||||||
|
throw new Error("Missing Signed PreKey for PreKeyWhisperMessage");
|
||||||
|
}
|
||||||
|
if (session !== undefined) {
|
||||||
|
// Duplicate PreKeyMessage for session:
|
||||||
|
if (util.isEqual(session.indexInfo.baseKey, message.baseKey)) {
|
||||||
|
return Promise.resolve([session, undefined]);
|
||||||
|
}
|
||||||
|
|
||||||
// We already had a session/known identity key:
|
// We already had a session/known identity key:
|
||||||
if (util.isEqual(session.indexInfo.remoteIdentityKey, message.identityKey)) {
|
if (util.isEqual(session.indexInfo.remoteIdentityKey, message.identityKey)) {
|
||||||
// If the identity key matches the previous one, close the previous one and use the new one
|
// If the identity key matches the previous one, close the
|
||||||
if (open_session !== undefined)
|
// previous one and use the new one
|
||||||
closeSession(open_session); // To be returned and saved later
|
if (open_session !== undefined) {
|
||||||
} else {
|
// To be returned and saved later
|
||||||
// ...otherwise create an error that the UI will pick up and ask the user if they want to re-negotiate
|
closeSession(open_session);
|
||||||
var e = new Error('Unknown identity key');
|
}
|
||||||
e.identityKey = message.identityKey.toArrayBuffer();
|
} else {
|
||||||
throw e;
|
// ...otherwise create an error that the UI will pick up
|
||||||
}
|
// and ask the user if they want to re-negotiate
|
||||||
}
|
var e = new Error('Unknown identity key');
|
||||||
if (message.preKeyId && !preKeyPair) {
|
e.identityKey = message.identityKey.toArrayBuffer();
|
||||||
console.log('Invalid prekey id');
|
throw e;
|
||||||
}
|
}
|
||||||
return initSession(false, preKeyPair, signedPreKeyPair,
|
}
|
||||||
encodedNumber,
|
if (message.preKeyId && !preKeyPair) {
|
||||||
message.identityKey.toArrayBuffer(),
|
console.log('Invalid prekey id');
|
||||||
message.baseKey.toArrayBuffer(),
|
}
|
||||||
undefined
|
return initSession(false, preKeyPair, signedPreKeyPair,
|
||||||
).then(function(new_session) {
|
encodedNumber,
|
||||||
// Note that the session is not actually saved until the very end of decryptWhisperMessage
|
message.identityKey.toArrayBuffer(),
|
||||||
// ... to ensure that the sender actually holds the private keys for all reported pubkeys
|
message.baseKey.toArrayBuffer(),
|
||||||
return [new_session, function() {
|
undefined
|
||||||
return storage_interface.removePreKey(message.preKeyId).then(function() {
|
).then(function(new_session) {
|
||||||
if (open_session !== undefined)
|
// Note that the session is not actually saved until the very
|
||||||
return crypto_storage.saveSession(encodedNumber, open_session);
|
// end of decryptWhisperMessage ... to ensure that the sender
|
||||||
});
|
// actually holds the private keys for all reported pubkeys
|
||||||
}];
|
return [new_session, function() {
|
||||||
});
|
return storage_interface.removePreKey(message.preKeyId).then(function() {
|
||||||
|
if (open_session !== undefined)
|
||||||
|
return crypto_storage.saveSession(encodedNumber, open_session);
|
||||||
});
|
});
|
||||||
});
|
}];
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -34906,96 +34917,112 @@ window.axolotl.protocol = function(storage_interface) {
|
||||||
if (!(plaintext instanceof ArrayBuffer)) {
|
if (!(plaintext instanceof ArrayBuffer)) {
|
||||||
throw new Error("Expected plaintext to be an ArrayBuffer");
|
throw new Error("Expected plaintext to be an ArrayBuffer");
|
||||||
}
|
}
|
||||||
return storage_interface.getMyIdentityKey().then(function(ourIdentityKey) {
|
|
||||||
return storage_interface.getMyRegistrationId().then(function(myRegistrationId) {
|
|
||||||
return crypto_storage.getOpenSession(deviceObject.encodedNumber).then(function(session) {
|
|
||||||
var hadSession = session !== undefined;
|
|
||||||
|
|
||||||
var doEncryptPushMessageContent = function() {
|
var ourIdentityKey, myRegistrationId, session, hadSession;
|
||||||
var msg = new axolotlInternal.protobuf.WhisperMessage();
|
return Promise.all([
|
||||||
|
storage_interface.getMyIdentityKey(),
|
||||||
|
storage_interface.getMyRegistrationId(),
|
||||||
|
crypto_storage.getOpenSession(deviceObject.encodedNumber)
|
||||||
|
]).then(function(results) {
|
||||||
|
ourIdentityKey = results[0];
|
||||||
|
myRegistrationId = results[1];
|
||||||
|
session = results[2];
|
||||||
|
|
||||||
var paddedPlaintext = new Uint8Array(getPaddedMessageLength(plaintext.byteLength + 1) - 1);
|
hadSession = session !== undefined;
|
||||||
paddedPlaintext.set(new Uint8Array(plaintext));
|
|
||||||
paddedPlaintext[plaintext.byteLength] = 0x80;
|
|
||||||
|
|
||||||
msg.ephemeralKey = toArrayBuffer(session.currentRatchet.ephemeralKeyPair.pubKey);
|
if (session === undefined) {
|
||||||
var chain = session[toString(msg.ephemeralKey)];
|
var deviceIdentityKey = toArrayBuffer(deviceObject.identityKey);
|
||||||
|
var deviceSignedKey = toArrayBuffer(deviceObject.signedKey);
|
||||||
|
return axolotlInternal.crypto.Ed25519Verify(
|
||||||
|
deviceIdentityKey, deviceSignedKey,
|
||||||
|
toArrayBuffer(deviceObject.signedKeySignature)
|
||||||
|
).then(function() {
|
||||||
|
return axolotlInternal.crypto.createKeyPair();
|
||||||
|
}).then(function(baseKey) {
|
||||||
|
return initSession(true, baseKey, undefined,
|
||||||
|
deviceObject.encodedNumber, deviceIdentityKey,
|
||||||
|
toArrayBuffer(deviceObject.preKey), deviceSignedKey
|
||||||
|
).then(function(new_session) {
|
||||||
|
session = new_session;
|
||||||
|
session.pendingPreKey = {
|
||||||
|
preKeyId : deviceObject.preKeyId,
|
||||||
|
signedKeyId : deviceObject.signedKeyId,
|
||||||
|
baseKey : baseKey.pubKey
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).then(function doEncryptPushMessageContent() {
|
||||||
|
var msg = new axolotlInternal.protobuf.WhisperMessage();
|
||||||
|
|
||||||
return fillMessageKeys(chain, chain.chainKey.counter + 1).then(function() {
|
var paddedPlaintext = new Uint8Array(
|
||||||
return HKDF(toArrayBuffer(chain.messageKeys[chain.chainKey.counter]), '', "WhisperMessageKeys").then(function(keys) {
|
getPaddedMessageLength(plaintext.byteLength + 1) - 1
|
||||||
delete chain.messageKeys[chain.chainKey.counter];
|
);
|
||||||
msg.counter = chain.chainKey.counter;
|
paddedPlaintext.set(new Uint8Array(plaintext));
|
||||||
msg.previousCounter = session.currentRatchet.previousCounter;
|
paddedPlaintext[plaintext.byteLength] = 0x80;
|
||||||
|
|
||||||
return axolotlInternal.crypto.encrypt(keys[0], paddedPlaintext.buffer, keys[2].slice(0, 16)).then(function(ciphertext) {
|
msg.ephemeralKey = toArrayBuffer(
|
||||||
msg.ciphertext = ciphertext;
|
session.currentRatchet.ephemeralKeyPair.pubKey
|
||||||
var encodedMsg = toArrayBuffer(msg.encode());
|
);
|
||||||
|
var chain = session[toString(msg.ephemeralKey)];
|
||||||
|
|
||||||
var macInput = new Uint8Array(encodedMsg.byteLength + 33*2 + 1);
|
return fillMessageKeys(chain, chain.chainKey.counter + 1).then(function() {
|
||||||
macInput.set(new Uint8Array(toArrayBuffer(ourIdentityKey.pubKey)));
|
return HKDF(toArrayBuffer(chain.messageKeys[chain.chainKey.counter]),
|
||||||
macInput.set(new Uint8Array(toArrayBuffer(session.indexInfo.remoteIdentityKey)), 33);
|
'', "WhisperMessageKeys"
|
||||||
macInput[33*2] = (3 << 4) | 3;
|
).then(function(keys) {
|
||||||
macInput.set(new Uint8Array(encodedMsg), 33*2 + 1);
|
delete chain.messageKeys[chain.chainKey.counter];
|
||||||
|
msg.counter = chain.chainKey.counter;
|
||||||
|
msg.previousCounter = session.currentRatchet.previousCounter;
|
||||||
|
|
||||||
return axolotlInternal.crypto.sign(keys[1], macInput.buffer).then(function(mac) {
|
return axolotlInternal.crypto.encrypt(
|
||||||
var result = new Uint8Array(encodedMsg.byteLength + 9);
|
keys[0], paddedPlaintext.buffer, keys[2].slice(0, 16)
|
||||||
result[0] = (3 << 4) | 3;
|
).then(function(ciphertext) {
|
||||||
result.set(new Uint8Array(encodedMsg), 1);
|
msg.ciphertext = ciphertext;
|
||||||
result.set(new Uint8Array(mac, 0, 8), encodedMsg.byteLength + 1);
|
var encodedMsg = toArrayBuffer(msg.encode());
|
||||||
|
|
||||||
removeOldChains(session);
|
var macInput = new Uint8Array(encodedMsg.byteLength + 33*2 + 1);
|
||||||
|
macInput.set(new Uint8Array(toArrayBuffer(ourIdentityKey.pubKey)));
|
||||||
|
macInput.set(new Uint8Array(toArrayBuffer(session.indexInfo.remoteIdentityKey)), 33);
|
||||||
|
macInput[33*2] = (3 << 4) | 3;
|
||||||
|
macInput.set(new Uint8Array(encodedMsg), 33*2 + 1);
|
||||||
|
|
||||||
return crypto_storage.saveSession(deviceObject.encodedNumber, session, !hadSession ? deviceObject.registrationId : undefined).then(function() {
|
return axolotlInternal.crypto.sign(
|
||||||
return result;
|
keys[1], macInput.buffer
|
||||||
});
|
).then(function(mac) {
|
||||||
});
|
var result = new Uint8Array(encodedMsg.byteLength + 9);
|
||||||
});
|
result[0] = (3 << 4) | 3;
|
||||||
|
result.set(new Uint8Array(encodedMsg), 1);
|
||||||
|
result.set(new Uint8Array(mac, 0, 8), encodedMsg.byteLength + 1);
|
||||||
|
|
||||||
|
removeOldChains(session);
|
||||||
|
|
||||||
|
return crypto_storage.saveSession(
|
||||||
|
deviceObject.encodedNumber,
|
||||||
|
session,
|
||||||
|
!hadSession ? deviceObject.registrationId : undefined
|
||||||
|
).then(function() {
|
||||||
|
return result;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
|
||||||
var preKeyMsg = new axolotlInternal.protobuf.PreKeyWhisperMessage();
|
|
||||||
preKeyMsg.identityKey = toArrayBuffer(ourIdentityKey.pubKey);
|
|
||||||
preKeyMsg.registrationId = myRegistrationId;
|
|
||||||
|
|
||||||
if (session === undefined) {
|
|
||||||
var deviceIdentityKey = toArrayBuffer(deviceObject.identityKey);
|
|
||||||
var deviceSignedKey = toArrayBuffer(deviceObject.signedKey);
|
|
||||||
return axolotlInternal.crypto.Ed25519Verify(deviceIdentityKey, deviceSignedKey, toArrayBuffer(deviceObject.signedKeySignature)).then(function() {
|
|
||||||
return axolotlInternal.crypto.createKeyPair().then(function(baseKey) {
|
|
||||||
preKeyMsg.preKeyId = deviceObject.preKeyId;
|
|
||||||
preKeyMsg.signedPreKeyId = deviceObject.signedKeyId;
|
|
||||||
preKeyMsg.baseKey = toArrayBuffer(baseKey.pubKey);
|
|
||||||
return initSession(true, baseKey, undefined,
|
|
||||||
deviceObject.encodedNumber, deviceIdentityKey,
|
|
||||||
toArrayBuffer(deviceObject.preKey),
|
|
||||||
deviceSignedKey
|
|
||||||
).then(function(new_session) {
|
|
||||||
session = new_session;
|
|
||||||
session.pendingPreKey = { preKeyId: deviceObject.preKeyId, signedKeyId: deviceObject.signedKeyId, baseKey: baseKey.pubKey };
|
|
||||||
return doEncryptPushMessageContent().then(function(message) {
|
|
||||||
preKeyMsg.message = message;
|
|
||||||
var result = String.fromCharCode((3 << 4) | 3) + toString(preKeyMsg.encode());
|
|
||||||
return {type: 3, body: result};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else
|
|
||||||
return doEncryptPushMessageContent().then(function(message) {
|
|
||||||
if (session.pendingPreKey !== undefined) {
|
|
||||||
preKeyMsg.baseKey = toArrayBuffer(session.pendingPreKey.baseKey);
|
|
||||||
preKeyMsg.preKeyId = session.pendingPreKey.preKeyId;
|
|
||||||
preKeyMsg.signedPreKeyId = session.pendingPreKey.signedKeyId;
|
|
||||||
preKeyMsg.message = message;
|
|
||||||
|
|
||||||
var result = String.fromCharCode((3 << 4) | 3) + toString(preKeyMsg.encode());
|
|
||||||
return {type: 3, body: result};
|
|
||||||
} else
|
|
||||||
return {type: 1, body: toString(message)};
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}).then(function(message) {
|
||||||
|
if (session.pendingPreKey !== undefined) {
|
||||||
|
var preKeyMsg = new axolotlInternal.protobuf.PreKeyWhisperMessage();
|
||||||
|
preKeyMsg.identityKey = toArrayBuffer(ourIdentityKey.pubKey);
|
||||||
|
preKeyMsg.registrationId = myRegistrationId;
|
||||||
|
|
||||||
|
preKeyMsg.baseKey = toArrayBuffer(session.pendingPreKey.baseKey);
|
||||||
|
preKeyMsg.preKeyId = session.pendingPreKey.preKeyId;
|
||||||
|
preKeyMsg.signedPreKeyId = session.pendingPreKey.signedKeyId;
|
||||||
|
|
||||||
|
preKeyMsg.message = message;
|
||||||
|
var result = String.fromCharCode((3 << 4) | 3) + toString(preKeyMsg.encode());
|
||||||
|
return {type: 3, body: result};
|
||||||
|
} else {
|
||||||
|
return {type: 1, body: toString(message)};
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34558,55 +34558,66 @@ window.axolotl.protocol = function(storage_interface) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var initSessionFromPreKeyWhisperMessage = function(encodedNumber, message) {
|
var initSessionFromPreKeyWhisperMessage = function(encodedNumber, message) {
|
||||||
return storage_interface.getPreKey(message.preKeyId).then(function(preKeyPair) {
|
var preKeyPair, signedPreKeyPair, session;
|
||||||
return storage_interface.getSignedPreKey(message.signedPreKeyId).then(function(signedPreKeyPair) {
|
return Promise.all([
|
||||||
return crypto_storage.getSessionOrIdentityKeyByBaseKey(encodedNumber, toArrayBuffer(message.baseKey)).then(function(session) {
|
storage_interface.getPreKey(message.preKeyId),
|
||||||
return crypto_storage.getOpenSession(encodedNumber).then(function(open_session) {
|
storage_interface.getSignedPreKey(message.signedPreKeyId),
|
||||||
if (signedPreKeyPair === undefined) {
|
crypto_storage.getSessionOrIdentityKeyByBaseKey(encodedNumber, toArrayBuffer(message.baseKey))
|
||||||
// Session may or may not be the right one, but if its not, we can't do anything about it
|
]).then(function(results) {
|
||||||
// ...fall through and let decryptWhisperMessage handle that case
|
preKeyPair = results[0];
|
||||||
if (session !== undefined && session.currentRatchet !== undefined)
|
signedPreKeyPair = results[1];
|
||||||
return Promise.resolve([session, undefined]);
|
session = results[2];
|
||||||
else
|
return crypto_storage.getOpenSession(encodedNumber);
|
||||||
throw new Error("Missing Signed PreKey for PreKeyWhisperMessage");
|
}).then(function(open_session) {
|
||||||
}
|
if (signedPreKeyPair === undefined) {
|
||||||
if (session !== undefined) {
|
// Session may or may not be the right one, but if its not, we
|
||||||
// Duplicate PreKeyMessage for session:
|
// can't do anything about it ...fall through and let
|
||||||
if (util.isEqual(session.indexInfo.baseKey, message.baseKey))
|
// decryptWhisperMessage handle that case
|
||||||
return Promise.resolve([session, undefined]);
|
if (session !== undefined && session.currentRatchet !== undefined)
|
||||||
|
return Promise.resolve([session, undefined]);
|
||||||
|
else
|
||||||
|
throw new Error("Missing Signed PreKey for PreKeyWhisperMessage");
|
||||||
|
}
|
||||||
|
if (session !== undefined) {
|
||||||
|
// Duplicate PreKeyMessage for session:
|
||||||
|
if (util.isEqual(session.indexInfo.baseKey, message.baseKey)) {
|
||||||
|
return Promise.resolve([session, undefined]);
|
||||||
|
}
|
||||||
|
|
||||||
// We already had a session/known identity key:
|
// We already had a session/known identity key:
|
||||||
if (util.isEqual(session.indexInfo.remoteIdentityKey, message.identityKey)) {
|
if (util.isEqual(session.indexInfo.remoteIdentityKey, message.identityKey)) {
|
||||||
// If the identity key matches the previous one, close the previous one and use the new one
|
// If the identity key matches the previous one, close the
|
||||||
if (open_session !== undefined)
|
// previous one and use the new one
|
||||||
closeSession(open_session); // To be returned and saved later
|
if (open_session !== undefined) {
|
||||||
} else {
|
// To be returned and saved later
|
||||||
// ...otherwise create an error that the UI will pick up and ask the user if they want to re-negotiate
|
closeSession(open_session);
|
||||||
var e = new Error('Unknown identity key');
|
}
|
||||||
e.identityKey = message.identityKey.toArrayBuffer();
|
} else {
|
||||||
throw e;
|
// ...otherwise create an error that the UI will pick up
|
||||||
}
|
// and ask the user if they want to re-negotiate
|
||||||
}
|
var e = new Error('Unknown identity key');
|
||||||
if (message.preKeyId && !preKeyPair) {
|
e.identityKey = message.identityKey.toArrayBuffer();
|
||||||
console.log('Invalid prekey id');
|
throw e;
|
||||||
}
|
}
|
||||||
return initSession(false, preKeyPair, signedPreKeyPair,
|
}
|
||||||
encodedNumber,
|
if (message.preKeyId && !preKeyPair) {
|
||||||
message.identityKey.toArrayBuffer(),
|
console.log('Invalid prekey id');
|
||||||
message.baseKey.toArrayBuffer(),
|
}
|
||||||
undefined
|
return initSession(false, preKeyPair, signedPreKeyPair,
|
||||||
).then(function(new_session) {
|
encodedNumber,
|
||||||
// Note that the session is not actually saved until the very end of decryptWhisperMessage
|
message.identityKey.toArrayBuffer(),
|
||||||
// ... to ensure that the sender actually holds the private keys for all reported pubkeys
|
message.baseKey.toArrayBuffer(),
|
||||||
return [new_session, function() {
|
undefined
|
||||||
return storage_interface.removePreKey(message.preKeyId).then(function() {
|
).then(function(new_session) {
|
||||||
if (open_session !== undefined)
|
// Note that the session is not actually saved until the very
|
||||||
return crypto_storage.saveSession(encodedNumber, open_session);
|
// end of decryptWhisperMessage ... to ensure that the sender
|
||||||
});
|
// actually holds the private keys for all reported pubkeys
|
||||||
}];
|
return [new_session, function() {
|
||||||
});
|
return storage_interface.removePreKey(message.preKeyId).then(function() {
|
||||||
|
if (open_session !== undefined)
|
||||||
|
return crypto_storage.saveSession(encodedNumber, open_session);
|
||||||
});
|
});
|
||||||
});
|
}];
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -34792,96 +34803,112 @@ window.axolotl.protocol = function(storage_interface) {
|
||||||
if (!(plaintext instanceof ArrayBuffer)) {
|
if (!(plaintext instanceof ArrayBuffer)) {
|
||||||
throw new Error("Expected plaintext to be an ArrayBuffer");
|
throw new Error("Expected plaintext to be an ArrayBuffer");
|
||||||
}
|
}
|
||||||
return storage_interface.getMyIdentityKey().then(function(ourIdentityKey) {
|
|
||||||
return storage_interface.getMyRegistrationId().then(function(myRegistrationId) {
|
|
||||||
return crypto_storage.getOpenSession(deviceObject.encodedNumber).then(function(session) {
|
|
||||||
var hadSession = session !== undefined;
|
|
||||||
|
|
||||||
var doEncryptPushMessageContent = function() {
|
var ourIdentityKey, myRegistrationId, session, hadSession;
|
||||||
var msg = new axolotlInternal.protobuf.WhisperMessage();
|
return Promise.all([
|
||||||
|
storage_interface.getMyIdentityKey(),
|
||||||
|
storage_interface.getMyRegistrationId(),
|
||||||
|
crypto_storage.getOpenSession(deviceObject.encodedNumber)
|
||||||
|
]).then(function(results) {
|
||||||
|
ourIdentityKey = results[0];
|
||||||
|
myRegistrationId = results[1];
|
||||||
|
session = results[2];
|
||||||
|
|
||||||
var paddedPlaintext = new Uint8Array(getPaddedMessageLength(plaintext.byteLength + 1) - 1);
|
hadSession = session !== undefined;
|
||||||
paddedPlaintext.set(new Uint8Array(plaintext));
|
|
||||||
paddedPlaintext[plaintext.byteLength] = 0x80;
|
|
||||||
|
|
||||||
msg.ephemeralKey = toArrayBuffer(session.currentRatchet.ephemeralKeyPair.pubKey);
|
if (session === undefined) {
|
||||||
var chain = session[toString(msg.ephemeralKey)];
|
var deviceIdentityKey = toArrayBuffer(deviceObject.identityKey);
|
||||||
|
var deviceSignedKey = toArrayBuffer(deviceObject.signedKey);
|
||||||
|
return axolotlInternal.crypto.Ed25519Verify(
|
||||||
|
deviceIdentityKey, deviceSignedKey,
|
||||||
|
toArrayBuffer(deviceObject.signedKeySignature)
|
||||||
|
).then(function() {
|
||||||
|
return axolotlInternal.crypto.createKeyPair();
|
||||||
|
}).then(function(baseKey) {
|
||||||
|
return initSession(true, baseKey, undefined,
|
||||||
|
deviceObject.encodedNumber, deviceIdentityKey,
|
||||||
|
toArrayBuffer(deviceObject.preKey), deviceSignedKey
|
||||||
|
).then(function(new_session) {
|
||||||
|
session = new_session;
|
||||||
|
session.pendingPreKey = {
|
||||||
|
preKeyId : deviceObject.preKeyId,
|
||||||
|
signedKeyId : deviceObject.signedKeyId,
|
||||||
|
baseKey : baseKey.pubKey
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).then(function doEncryptPushMessageContent() {
|
||||||
|
var msg = new axolotlInternal.protobuf.WhisperMessage();
|
||||||
|
|
||||||
return fillMessageKeys(chain, chain.chainKey.counter + 1).then(function() {
|
var paddedPlaintext = new Uint8Array(
|
||||||
return HKDF(toArrayBuffer(chain.messageKeys[chain.chainKey.counter]), '', "WhisperMessageKeys").then(function(keys) {
|
getPaddedMessageLength(plaintext.byteLength + 1) - 1
|
||||||
delete chain.messageKeys[chain.chainKey.counter];
|
);
|
||||||
msg.counter = chain.chainKey.counter;
|
paddedPlaintext.set(new Uint8Array(plaintext));
|
||||||
msg.previousCounter = session.currentRatchet.previousCounter;
|
paddedPlaintext[plaintext.byteLength] = 0x80;
|
||||||
|
|
||||||
return axolotlInternal.crypto.encrypt(keys[0], paddedPlaintext.buffer, keys[2].slice(0, 16)).then(function(ciphertext) {
|
msg.ephemeralKey = toArrayBuffer(
|
||||||
msg.ciphertext = ciphertext;
|
session.currentRatchet.ephemeralKeyPair.pubKey
|
||||||
var encodedMsg = toArrayBuffer(msg.encode());
|
);
|
||||||
|
var chain = session[toString(msg.ephemeralKey)];
|
||||||
|
|
||||||
var macInput = new Uint8Array(encodedMsg.byteLength + 33*2 + 1);
|
return fillMessageKeys(chain, chain.chainKey.counter + 1).then(function() {
|
||||||
macInput.set(new Uint8Array(toArrayBuffer(ourIdentityKey.pubKey)));
|
return HKDF(toArrayBuffer(chain.messageKeys[chain.chainKey.counter]),
|
||||||
macInput.set(new Uint8Array(toArrayBuffer(session.indexInfo.remoteIdentityKey)), 33);
|
'', "WhisperMessageKeys"
|
||||||
macInput[33*2] = (3 << 4) | 3;
|
).then(function(keys) {
|
||||||
macInput.set(new Uint8Array(encodedMsg), 33*2 + 1);
|
delete chain.messageKeys[chain.chainKey.counter];
|
||||||
|
msg.counter = chain.chainKey.counter;
|
||||||
|
msg.previousCounter = session.currentRatchet.previousCounter;
|
||||||
|
|
||||||
return axolotlInternal.crypto.sign(keys[1], macInput.buffer).then(function(mac) {
|
return axolotlInternal.crypto.encrypt(
|
||||||
var result = new Uint8Array(encodedMsg.byteLength + 9);
|
keys[0], paddedPlaintext.buffer, keys[2].slice(0, 16)
|
||||||
result[0] = (3 << 4) | 3;
|
).then(function(ciphertext) {
|
||||||
result.set(new Uint8Array(encodedMsg), 1);
|
msg.ciphertext = ciphertext;
|
||||||
result.set(new Uint8Array(mac, 0, 8), encodedMsg.byteLength + 1);
|
var encodedMsg = toArrayBuffer(msg.encode());
|
||||||
|
|
||||||
removeOldChains(session);
|
var macInput = new Uint8Array(encodedMsg.byteLength + 33*2 + 1);
|
||||||
|
macInput.set(new Uint8Array(toArrayBuffer(ourIdentityKey.pubKey)));
|
||||||
|
macInput.set(new Uint8Array(toArrayBuffer(session.indexInfo.remoteIdentityKey)), 33);
|
||||||
|
macInput[33*2] = (3 << 4) | 3;
|
||||||
|
macInput.set(new Uint8Array(encodedMsg), 33*2 + 1);
|
||||||
|
|
||||||
return crypto_storage.saveSession(deviceObject.encodedNumber, session, !hadSession ? deviceObject.registrationId : undefined).then(function() {
|
return axolotlInternal.crypto.sign(
|
||||||
return result;
|
keys[1], macInput.buffer
|
||||||
});
|
).then(function(mac) {
|
||||||
});
|
var result = new Uint8Array(encodedMsg.byteLength + 9);
|
||||||
});
|
result[0] = (3 << 4) | 3;
|
||||||
|
result.set(new Uint8Array(encodedMsg), 1);
|
||||||
|
result.set(new Uint8Array(mac, 0, 8), encodedMsg.byteLength + 1);
|
||||||
|
|
||||||
|
removeOldChains(session);
|
||||||
|
|
||||||
|
return crypto_storage.saveSession(
|
||||||
|
deviceObject.encodedNumber,
|
||||||
|
session,
|
||||||
|
!hadSession ? deviceObject.registrationId : undefined
|
||||||
|
).then(function() {
|
||||||
|
return result;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
|
||||||
var preKeyMsg = new axolotlInternal.protobuf.PreKeyWhisperMessage();
|
|
||||||
preKeyMsg.identityKey = toArrayBuffer(ourIdentityKey.pubKey);
|
|
||||||
preKeyMsg.registrationId = myRegistrationId;
|
|
||||||
|
|
||||||
if (session === undefined) {
|
|
||||||
var deviceIdentityKey = toArrayBuffer(deviceObject.identityKey);
|
|
||||||
var deviceSignedKey = toArrayBuffer(deviceObject.signedKey);
|
|
||||||
return axolotlInternal.crypto.Ed25519Verify(deviceIdentityKey, deviceSignedKey, toArrayBuffer(deviceObject.signedKeySignature)).then(function() {
|
|
||||||
return axolotlInternal.crypto.createKeyPair().then(function(baseKey) {
|
|
||||||
preKeyMsg.preKeyId = deviceObject.preKeyId;
|
|
||||||
preKeyMsg.signedPreKeyId = deviceObject.signedKeyId;
|
|
||||||
preKeyMsg.baseKey = toArrayBuffer(baseKey.pubKey);
|
|
||||||
return initSession(true, baseKey, undefined,
|
|
||||||
deviceObject.encodedNumber, deviceIdentityKey,
|
|
||||||
toArrayBuffer(deviceObject.preKey),
|
|
||||||
deviceSignedKey
|
|
||||||
).then(function(new_session) {
|
|
||||||
session = new_session;
|
|
||||||
session.pendingPreKey = { preKeyId: deviceObject.preKeyId, signedKeyId: deviceObject.signedKeyId, baseKey: baseKey.pubKey };
|
|
||||||
return doEncryptPushMessageContent().then(function(message) {
|
|
||||||
preKeyMsg.message = message;
|
|
||||||
var result = String.fromCharCode((3 << 4) | 3) + toString(preKeyMsg.encode());
|
|
||||||
return {type: 3, body: result};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else
|
|
||||||
return doEncryptPushMessageContent().then(function(message) {
|
|
||||||
if (session.pendingPreKey !== undefined) {
|
|
||||||
preKeyMsg.baseKey = toArrayBuffer(session.pendingPreKey.baseKey);
|
|
||||||
preKeyMsg.preKeyId = session.pendingPreKey.preKeyId;
|
|
||||||
preKeyMsg.signedPreKeyId = session.pendingPreKey.signedKeyId;
|
|
||||||
preKeyMsg.message = message;
|
|
||||||
|
|
||||||
var result = String.fromCharCode((3 << 4) | 3) + toString(preKeyMsg.encode());
|
|
||||||
return {type: 3, body: result};
|
|
||||||
} else
|
|
||||||
return {type: 1, body: toString(message)};
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}).then(function(message) {
|
||||||
|
if (session.pendingPreKey !== undefined) {
|
||||||
|
var preKeyMsg = new axolotlInternal.protobuf.PreKeyWhisperMessage();
|
||||||
|
preKeyMsg.identityKey = toArrayBuffer(ourIdentityKey.pubKey);
|
||||||
|
preKeyMsg.registrationId = myRegistrationId;
|
||||||
|
|
||||||
|
preKeyMsg.baseKey = toArrayBuffer(session.pendingPreKey.baseKey);
|
||||||
|
preKeyMsg.preKeyId = session.pendingPreKey.preKeyId;
|
||||||
|
preKeyMsg.signedPreKeyId = session.pendingPreKey.signedKeyId;
|
||||||
|
|
||||||
|
preKeyMsg.message = message;
|
||||||
|
var result = String.fromCharCode((3 << 4) | 3) + toString(preKeyMsg.encode());
|
||||||
|
return {type: 3, body: result};
|
||||||
|
} else {
|
||||||
|
return {type: 1, body: toString(message)};
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue