You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
session-desktop/libloki/libloki-protocol.js

93 lines
3.6 KiB
JavaScript

/* global window, libsignal, textsecure */
// eslint-disable-next-line func-names
(function() {
window.libloki = window.libloki || {};
class FallBackDecryptionError extends Error {}
const IV_LENGTH = 16;
FallBackSessionCipher = function (address) {
this.identityKeyString = address.getName();
this.pubKey = StringView.hexToArrayBuffer(address.getName());
this.encrypt = async (plaintext) => {
const myKeyPair = await textsecure.storage.protocol.getIdentityKeyPair();
const myPrivateKey = myKeyPair.privKey;
const symmetricKey = libsignal.Curve.calculateAgreement(this.pubKey, myPrivateKey);
const iv = libsignal.crypto.getRandomBytes(IV_LENGTH);
const ciphertext = await libsignal.crypto.encrypt(symmetricKey, plaintext, iv);
const ivAndCiphertext = new Uint8Array(
iv.byteLength + ciphertext.byteLength
);
ivAndCiphertext.set(new Uint8Array(iv));
ivAndCiphertext.set(
new Uint8Array(ciphertext),
iv.byteLength
);
return {
type : 6, //friend request
body : ivAndCiphertext,
registrationId : null
};
},
this.decrypt = async (ivAndCiphertext) => {
const iv = ivAndCiphertext.slice(0, IV_LENGTH);
const cipherText = ivAndCiphertext.slice(IV_LENGTH);
const myKeyPair = await textsecure.storage.protocol.getIdentityKeyPair();
const myPrivateKey = myKeyPair.privKey;
const symmetricKey = libsignal.Curve.calculateAgreement(this.pubKey, myPrivateKey);
try {
return await libsignal.crypto.decrypt(symmetricKey, cipherText, iv);
} catch(e) {
throw new FallBackDecryptionError('Could not decrypt message from ' + this.identityKeyString + ' using FallBack encryption.')
}
}
}
getPreKeyBundleForNumber = async function(pubKey) {
const myKeyPair = await textsecure.storage.protocol.getIdentityKeyPair();
const identityKey = myKeyPair.pubKey;
// Retrieve ids. The ids stored are always the latest generated + 1
const signedKeyId = textsecure.storage.get('signedKeyId', 1) - 1;
const [signedKey, preKey] = await Promise.all([
textsecure.storage.protocol.loadSignedPreKey(signedKeyId),
new Promise(async (resolve, reject) => {
// retrieve existing prekey if we already generated one for that recipient
const storedPreKey = await textsecure.storage.protocol.loadPreKeyForContactIdentityKeyString(pubKey);
if (storedPreKey) {
resolve({ pubKey: storedPreKey.pubKey, keyId: storedPreKey.keyId });
} else {
// generate and store new prekey
const preKeyId = textsecure.storage.get('maxPreKeyId', 1);
textsecure.storage.put('maxPreKeyId', preKeyId + 1);
const preKey = await libsignal.KeyHelper.generatePreKey(preKeyId);
await textsecure.storage.protocol.storePreKey(preKey.keyId, preKey.keyPair, pubKey);
resolve({ pubKey: preKey.keyPair.pubKey, keyId: preKeyId });
}
})
]);
const preKeyMessage = new textsecure.protobuf.PreKeyBundleMessage({
identityKey: new Uint8Array(identityKey),
deviceId: 1, // TODO: fetch from somewhere
preKeyId: preKey.keyId,
signedKeyId,
preKey: new Uint8Array(preKey.pubKey),
signedKey: new Uint8Array(signedKey.pubKey),
signature: new Uint8Array(signedKey.signature),
});
return preKeyMessage;
}
window.libloki.FallBackSessionCipher = FallBackSessionCipher;
window.libloki.getPreKeyBundleForNumber = getPreKeyBundleForNumber;
window.libloki.FallBackDecryptionError = FallBackDecryptionError;
})();