2018-07-21 21:51:20 +00:00
|
|
|
/* global libsignal, textsecure */
|
|
|
|
|
|
|
|
/* eslint-disable more/no-then */
|
2016-05-02 05:31:44 +00:00
|
|
|
|
2018-07-21 21:51:20 +00:00
|
|
|
// eslint-disable-next-line func-names
|
|
|
|
(function() {
|
2018-05-02 16:51:22 +00:00
|
|
|
function ProvisioningCipher() {}
|
2016-05-02 05:31:44 +00:00
|
|
|
|
2018-05-02 16:51:22 +00:00
|
|
|
ProvisioningCipher.prototype = {
|
2018-07-21 21:51:20 +00:00
|
|
|
decrypt(provisionEnvelope) {
|
|
|
|
const masterEphemeral = provisionEnvelope.publicKey.toArrayBuffer();
|
|
|
|
const message = provisionEnvelope.body.toArrayBuffer();
|
|
|
|
if (new Uint8Array(message)[0] !== 1) {
|
2018-05-02 16:51:22 +00:00
|
|
|
throw new Error('Bad version number on ProvisioningMessage');
|
|
|
|
}
|
2016-05-02 05:31:44 +00:00
|
|
|
|
2018-07-21 21:51:20 +00:00
|
|
|
const iv = message.slice(1, 16 + 1);
|
|
|
|
const mac = message.slice(message.byteLength - 32, message.byteLength);
|
|
|
|
const ivAndCiphertext = message.slice(0, message.byteLength - 32);
|
|
|
|
const ciphertext = message.slice(16 + 1, message.byteLength - 32);
|
2016-05-02 05:31:44 +00:00
|
|
|
|
2018-05-02 16:51:22 +00:00
|
|
|
return libsignal.Curve.async
|
|
|
|
.calculateAgreement(masterEphemeral, this.keyPair.privKey)
|
2018-07-21 21:51:20 +00:00
|
|
|
.then(ecRes =>
|
|
|
|
libsignal.HKDF.deriveSecrets(
|
2018-05-02 16:51:22 +00:00
|
|
|
ecRes,
|
|
|
|
new ArrayBuffer(32),
|
|
|
|
'TextSecure Provisioning Message'
|
2018-07-21 21:51:20 +00:00
|
|
|
)
|
|
|
|
)
|
|
|
|
.then(keys =>
|
|
|
|
libsignal.crypto
|
2018-05-02 16:51:22 +00:00
|
|
|
.verifyMAC(ivAndCiphertext, keys[1], mac, 32)
|
2018-07-21 21:51:20 +00:00
|
|
|
.then(() => libsignal.crypto.decrypt(keys[0], ciphertext, iv))
|
|
|
|
)
|
|
|
|
.then(plaintext => {
|
|
|
|
const provisionMessage = textsecure.protobuf.ProvisionMessage.decode(
|
2018-05-02 16:51:22 +00:00
|
|
|
plaintext
|
|
|
|
);
|
2018-07-21 21:51:20 +00:00
|
|
|
const privKey = provisionMessage.identityKeyPrivate.toArrayBuffer();
|
2016-05-02 05:31:44 +00:00
|
|
|
|
2018-07-21 21:51:20 +00:00
|
|
|
return libsignal.Curve.async.createKeyPair(privKey).then(keyPair => {
|
|
|
|
const ret = {
|
|
|
|
identityKeyPair: keyPair,
|
|
|
|
number: provisionMessage.number,
|
|
|
|
provisioningCode: provisionMessage.provisioningCode,
|
|
|
|
userAgent: provisionMessage.userAgent,
|
|
|
|
readReceipts: provisionMessage.readReceipts,
|
|
|
|
};
|
|
|
|
if (provisionMessage.profileKey) {
|
|
|
|
ret.profileKey = provisionMessage.profileKey.toArrayBuffer();
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
});
|
2016-05-02 05:31:44 +00:00
|
|
|
});
|
|
|
|
},
|
2018-07-21 21:51:20 +00:00
|
|
|
getPublicKey() {
|
2018-05-02 16:51:22 +00:00
|
|
|
return Promise.resolve()
|
2018-07-21 21:51:20 +00:00
|
|
|
.then(() => {
|
|
|
|
if (!this.keyPair) {
|
|
|
|
return libsignal.Curve.async.generateKeyPair().then(keyPair => {
|
|
|
|
this.keyPair = keyPair;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
})
|
|
|
|
.then(() => this.keyPair.pubKey);
|
2018-05-02 16:51:22 +00:00
|
|
|
},
|
|
|
|
};
|
2016-05-02 05:31:44 +00:00
|
|
|
|
2018-07-21 21:51:20 +00:00
|
|
|
libsignal.ProvisioningCipher = function ProvisioningCipherWrapper() {
|
|
|
|
const cipher = new ProvisioningCipher();
|
2016-05-02 05:31:44 +00:00
|
|
|
|
2018-05-02 16:51:22 +00:00
|
|
|
this.decrypt = cipher.decrypt.bind(cipher);
|
2016-05-02 05:31:44 +00:00
|
|
|
this.getPublicKey = cipher.getPublicKey.bind(cipher);
|
2018-05-02 16:51:22 +00:00
|
|
|
};
|
2016-05-02 05:31:44 +00:00
|
|
|
})();
|