Eslintify all of libtextsecure
This commit is contained in:
parent
4b3f9e969a
commit
0774ba2903
36 changed files with 1960 additions and 2128 deletions
|
@ -1,30 +1,28 @@
|
|||
/* global libsignal, crypto, textsecure, dcodeIO, window */
|
||||
|
||||
/* eslint-disable more/no-then, no-bitwise */
|
||||
|
||||
// eslint-disable-next-line func-names
|
||||
(function() {
|
||||
'use strict';
|
||||
const { encrypt, decrypt, calculateMAC, verifyMAC } = libsignal.crypto;
|
||||
|
||||
var encrypt = libsignal.crypto.encrypt;
|
||||
var decrypt = libsignal.crypto.decrypt;
|
||||
var calculateMAC = libsignal.crypto.calculateMAC;
|
||||
var verifyMAC = libsignal.crypto.verifyMAC;
|
||||
|
||||
var PROFILE_IV_LENGTH = 12; // bytes
|
||||
var PROFILE_KEY_LENGTH = 32; // bytes
|
||||
var PROFILE_TAG_LENGTH = 128; // bits
|
||||
var PROFILE_NAME_PADDED_LENGTH = 26; // bytes
|
||||
const PROFILE_IV_LENGTH = 12; // bytes
|
||||
const PROFILE_KEY_LENGTH = 32; // bytes
|
||||
const PROFILE_TAG_LENGTH = 128; // bits
|
||||
const PROFILE_NAME_PADDED_LENGTH = 26; // bytes
|
||||
|
||||
function verifyDigest(data, theirDigest) {
|
||||
return crypto.subtle
|
||||
.digest({ name: 'SHA-256' }, data)
|
||||
.then(function(ourDigest) {
|
||||
var a = new Uint8Array(ourDigest);
|
||||
var b = new Uint8Array(theirDigest);
|
||||
var result = 0;
|
||||
for (var i = 0; i < theirDigest.byteLength; ++i) {
|
||||
result = result | (a[i] ^ b[i]);
|
||||
}
|
||||
if (result !== 0) {
|
||||
throw new Error('Bad digest');
|
||||
}
|
||||
});
|
||||
return crypto.subtle.digest({ name: 'SHA-256' }, data).then(ourDigest => {
|
||||
const a = new Uint8Array(ourDigest);
|
||||
const b = new Uint8Array(theirDigest);
|
||||
let result = 0;
|
||||
for (let i = 0; i < theirDigest.byteLength; i += 1) {
|
||||
result |= a[i] ^ b[i];
|
||||
}
|
||||
if (result !== 0) {
|
||||
throw new Error('Bad digest');
|
||||
}
|
||||
});
|
||||
}
|
||||
function calculateDigest(data) {
|
||||
return crypto.subtle.digest({ name: 'SHA-256' }, data);
|
||||
|
@ -33,127 +31,127 @@
|
|||
window.textsecure = window.textsecure || {};
|
||||
window.textsecure.crypto = {
|
||||
// Decrypts message into a raw string
|
||||
decryptWebsocketMessage: function(message, signaling_key) {
|
||||
var decodedMessage = message.toArrayBuffer();
|
||||
decryptWebsocketMessage(message, signalingKey) {
|
||||
const decodedMessage = message.toArrayBuffer();
|
||||
|
||||
if (signaling_key.byteLength != 52) {
|
||||
throw new Error('Got invalid length signaling_key');
|
||||
if (signalingKey.byteLength !== 52) {
|
||||
throw new Error('Got invalid length signalingKey');
|
||||
}
|
||||
if (decodedMessage.byteLength < 1 + 16 + 10) {
|
||||
throw new Error('Got invalid length message');
|
||||
}
|
||||
if (new Uint8Array(decodedMessage)[0] != 1) {
|
||||
throw new Error('Got bad version number: ' + decodedMessage[0]);
|
||||
if (new Uint8Array(decodedMessage)[0] !== 1) {
|
||||
throw new Error(`Got bad version number: ${decodedMessage[0]}`);
|
||||
}
|
||||
|
||||
var aes_key = signaling_key.slice(0, 32);
|
||||
var mac_key = signaling_key.slice(32, 32 + 20);
|
||||
const aesKey = signalingKey.slice(0, 32);
|
||||
const macKey = signalingKey.slice(32, 32 + 20);
|
||||
|
||||
var iv = decodedMessage.slice(1, 1 + 16);
|
||||
var ciphertext = decodedMessage.slice(
|
||||
const iv = decodedMessage.slice(1, 1 + 16);
|
||||
const ciphertext = decodedMessage.slice(
|
||||
1 + 16,
|
||||
decodedMessage.byteLength - 10
|
||||
);
|
||||
var ivAndCiphertext = decodedMessage.slice(
|
||||
const ivAndCiphertext = decodedMessage.slice(
|
||||
0,
|
||||
decodedMessage.byteLength - 10
|
||||
);
|
||||
var mac = decodedMessage.slice(
|
||||
const mac = decodedMessage.slice(
|
||||
decodedMessage.byteLength - 10,
|
||||
decodedMessage.byteLength
|
||||
);
|
||||
|
||||
return verifyMAC(ivAndCiphertext, mac_key, mac, 10).then(function() {
|
||||
return decrypt(aes_key, ciphertext, iv);
|
||||
});
|
||||
return verifyMAC(ivAndCiphertext, macKey, mac, 10).then(() =>
|
||||
decrypt(aesKey, ciphertext, iv)
|
||||
);
|
||||
},
|
||||
|
||||
decryptAttachment: function(encryptedBin, keys, theirDigest) {
|
||||
if (keys.byteLength != 64) {
|
||||
decryptAttachment(encryptedBin, keys, theirDigest) {
|
||||
if (keys.byteLength !== 64) {
|
||||
throw new Error('Got invalid length attachment keys');
|
||||
}
|
||||
if (encryptedBin.byteLength < 16 + 32) {
|
||||
throw new Error('Got invalid length attachment');
|
||||
}
|
||||
|
||||
var aes_key = keys.slice(0, 32);
|
||||
var mac_key = keys.slice(32, 64);
|
||||
const aesKey = keys.slice(0, 32);
|
||||
const macKey = keys.slice(32, 64);
|
||||
|
||||
var iv = encryptedBin.slice(0, 16);
|
||||
var ciphertext = encryptedBin.slice(16, encryptedBin.byteLength - 32);
|
||||
var ivAndCiphertext = encryptedBin.slice(0, encryptedBin.byteLength - 32);
|
||||
var mac = encryptedBin.slice(
|
||||
const iv = encryptedBin.slice(0, 16);
|
||||
const ciphertext = encryptedBin.slice(16, encryptedBin.byteLength - 32);
|
||||
const ivAndCiphertext = encryptedBin.slice(
|
||||
0,
|
||||
encryptedBin.byteLength - 32
|
||||
);
|
||||
const mac = encryptedBin.slice(
|
||||
encryptedBin.byteLength - 32,
|
||||
encryptedBin.byteLength
|
||||
);
|
||||
|
||||
return verifyMAC(ivAndCiphertext, mac_key, mac, 32)
|
||||
.then(function() {
|
||||
return verifyMAC(ivAndCiphertext, macKey, mac, 32)
|
||||
.then(() => {
|
||||
if (theirDigest !== null) {
|
||||
return verifyDigest(encryptedBin, theirDigest);
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.then(function() {
|
||||
return decrypt(aes_key, ciphertext, iv);
|
||||
});
|
||||
.then(() => decrypt(aesKey, ciphertext, iv));
|
||||
},
|
||||
|
||||
encryptAttachment: function(plaintext, keys, iv) {
|
||||
encryptAttachment(plaintext, keys, iv) {
|
||||
if (
|
||||
!(plaintext instanceof ArrayBuffer) &&
|
||||
!ArrayBuffer.isView(plaintext)
|
||||
) {
|
||||
throw new TypeError(
|
||||
'`plaintext` must be an `ArrayBuffer` or `ArrayBufferView`; got: ' +
|
||||
typeof plaintext
|
||||
`\`plaintext\` must be an \`ArrayBuffer\` or \`ArrayBufferView\`; got: ${typeof plaintext}`
|
||||
);
|
||||
}
|
||||
|
||||
if (keys.byteLength != 64) {
|
||||
if (keys.byteLength !== 64) {
|
||||
throw new Error('Got invalid length attachment keys');
|
||||
}
|
||||
if (iv.byteLength != 16) {
|
||||
if (iv.byteLength !== 16) {
|
||||
throw new Error('Got invalid length attachment iv');
|
||||
}
|
||||
var aes_key = keys.slice(0, 32);
|
||||
var mac_key = keys.slice(32, 64);
|
||||
const aesKey = keys.slice(0, 32);
|
||||
const macKey = keys.slice(32, 64);
|
||||
|
||||
return encrypt(aes_key, plaintext, iv).then(function(ciphertext) {
|
||||
var ivAndCiphertext = new Uint8Array(16 + ciphertext.byteLength);
|
||||
return encrypt(aesKey, plaintext, iv).then(ciphertext => {
|
||||
const ivAndCiphertext = new Uint8Array(16 + ciphertext.byteLength);
|
||||
ivAndCiphertext.set(new Uint8Array(iv));
|
||||
ivAndCiphertext.set(new Uint8Array(ciphertext), 16);
|
||||
|
||||
return calculateMAC(mac_key, ivAndCiphertext.buffer).then(function(
|
||||
mac
|
||||
) {
|
||||
var encryptedBin = new Uint8Array(16 + ciphertext.byteLength + 32);
|
||||
return calculateMAC(macKey, ivAndCiphertext.buffer).then(mac => {
|
||||
const encryptedBin = new Uint8Array(16 + ciphertext.byteLength + 32);
|
||||
encryptedBin.set(ivAndCiphertext);
|
||||
encryptedBin.set(new Uint8Array(mac), 16 + ciphertext.byteLength);
|
||||
return calculateDigest(encryptedBin.buffer).then(function(digest) {
|
||||
return { ciphertext: encryptedBin.buffer, digest: digest };
|
||||
});
|
||||
return calculateDigest(encryptedBin.buffer).then(digest => ({
|
||||
ciphertext: encryptedBin.buffer,
|
||||
digest,
|
||||
}));
|
||||
});
|
||||
});
|
||||
},
|
||||
encryptProfile: function(data, key) {
|
||||
var iv = libsignal.crypto.getRandomBytes(PROFILE_IV_LENGTH);
|
||||
if (key.byteLength != PROFILE_KEY_LENGTH) {
|
||||
encryptProfile(data, key) {
|
||||
const iv = libsignal.crypto.getRandomBytes(PROFILE_IV_LENGTH);
|
||||
if (key.byteLength !== PROFILE_KEY_LENGTH) {
|
||||
throw new Error('Got invalid length profile key');
|
||||
}
|
||||
if (iv.byteLength != PROFILE_IV_LENGTH) {
|
||||
if (iv.byteLength !== PROFILE_IV_LENGTH) {
|
||||
throw new Error('Got invalid length profile iv');
|
||||
}
|
||||
return crypto.subtle
|
||||
.importKey('raw', key, { name: 'AES-GCM' }, false, ['encrypt'])
|
||||
.then(function(key) {
|
||||
return crypto.subtle
|
||||
.then(keyForEncryption =>
|
||||
crypto.subtle
|
||||
.encrypt(
|
||||
{ name: 'AES-GCM', iv: iv, tagLength: PROFILE_TAG_LENGTH },
|
||||
key,
|
||||
{ name: 'AES-GCM', iv, tagLength: PROFILE_TAG_LENGTH },
|
||||
keyForEncryption,
|
||||
data
|
||||
)
|
||||
.then(function(ciphertext) {
|
||||
var ivAndCiphertext = new Uint8Array(
|
||||
.then(ciphertext => {
|
||||
const ivAndCiphertext = new Uint8Array(
|
||||
PROFILE_IV_LENGTH + ciphertext.byteLength
|
||||
);
|
||||
ivAndCiphertext.set(new Uint8Array(iv));
|
||||
|
@ -162,32 +160,32 @@
|
|||
PROFILE_IV_LENGTH
|
||||
);
|
||||
return ivAndCiphertext.buffer;
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
},
|
||||
decryptProfile: function(data, key) {
|
||||
decryptProfile(data, key) {
|
||||
if (data.byteLength < 12 + 16 + 1) {
|
||||
throw new Error('Got too short input: ' + data.byteLength);
|
||||
throw new Error(`Got too short input: ${data.byteLength}`);
|
||||
}
|
||||
var iv = data.slice(0, PROFILE_IV_LENGTH);
|
||||
var ciphertext = data.slice(PROFILE_IV_LENGTH, data.byteLength);
|
||||
if (key.byteLength != PROFILE_KEY_LENGTH) {
|
||||
const iv = data.slice(0, PROFILE_IV_LENGTH);
|
||||
const ciphertext = data.slice(PROFILE_IV_LENGTH, data.byteLength);
|
||||
if (key.byteLength !== PROFILE_KEY_LENGTH) {
|
||||
throw new Error('Got invalid length profile key');
|
||||
}
|
||||
if (iv.byteLength != PROFILE_IV_LENGTH) {
|
||||
if (iv.byteLength !== PROFILE_IV_LENGTH) {
|
||||
throw new Error('Got invalid length profile iv');
|
||||
}
|
||||
var error = new Error(); // save stack
|
||||
const error = new Error(); // save stack
|
||||
return crypto.subtle
|
||||
.importKey('raw', key, { name: 'AES-GCM' }, false, ['decrypt'])
|
||||
.then(function(key) {
|
||||
return crypto.subtle
|
||||
.then(keyForEncryption =>
|
||||
crypto.subtle
|
||||
.decrypt(
|
||||
{ name: 'AES-GCM', iv: iv, tagLength: PROFILE_TAG_LENGTH },
|
||||
key,
|
||||
{ name: 'AES-GCM', iv, tagLength: PROFILE_TAG_LENGTH },
|
||||
keyForEncryption,
|
||||
ciphertext
|
||||
)
|
||||
.catch(function(e) {
|
||||
.catch(e => {
|
||||
if (e.name === 'OperationError') {
|
||||
// bad mac, basically.
|
||||
error.message =
|
||||
|
@ -195,38 +193,36 @@
|
|||
error.name = 'ProfileDecryptError';
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
},
|
||||
encryptProfileName: function(name, key) {
|
||||
var padded = new Uint8Array(PROFILE_NAME_PADDED_LENGTH);
|
||||
encryptProfileName(name, key) {
|
||||
const padded = new Uint8Array(PROFILE_NAME_PADDED_LENGTH);
|
||||
padded.set(new Uint8Array(name));
|
||||
return textsecure.crypto.encryptProfile(padded.buffer, key);
|
||||
},
|
||||
decryptProfileName: function(encryptedProfileName, key) {
|
||||
var data = dcodeIO.ByteBuffer.wrap(
|
||||
decryptProfileName(encryptedProfileName, key) {
|
||||
const data = dcodeIO.ByteBuffer.wrap(
|
||||
encryptedProfileName,
|
||||
'base64'
|
||||
).toArrayBuffer();
|
||||
return textsecure.crypto
|
||||
.decryptProfile(data, key)
|
||||
.then(function(decrypted) {
|
||||
// unpad
|
||||
var name = '';
|
||||
var padded = new Uint8Array(decrypted);
|
||||
for (var i = padded.length; i > 0; i--) {
|
||||
if (padded[i - 1] !== 0x00) {
|
||||
break;
|
||||
}
|
||||
return textsecure.crypto.decryptProfile(data, key).then(decrypted => {
|
||||
// unpad
|
||||
const padded = new Uint8Array(decrypted);
|
||||
let i;
|
||||
for (i = padded.length; i > 0; i -= 1) {
|
||||
if (padded[i - 1] !== 0x00) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return dcodeIO.ByteBuffer.wrap(padded)
|
||||
.slice(0, i)
|
||||
.toArrayBuffer();
|
||||
});
|
||||
return dcodeIO.ByteBuffer.wrap(padded)
|
||||
.slice(0, i)
|
||||
.toArrayBuffer();
|
||||
});
|
||||
},
|
||||
|
||||
getRandomBytes: function(size) {
|
||||
getRandomBytes(size) {
|
||||
return libsignal.crypto.getRandomBytes(size);
|
||||
},
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue