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.
93 lines
3.6 KiB
JavaScript
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;
|
|
|
|
})(); |