Encrypt device name on account create, on first launch if needed

This commit is contained in:
Scott Nonnenberg 2018-12-13 11:12:33 -08:00
parent 775e31c854
commit 47f834cf5c
10 changed files with 282 additions and 95 deletions

View file

@ -681,6 +681,7 @@
textsecure.storage.user.getDeviceId() != '1'
) {
window.getSyncRequest();
window.getAccountManager().maybeUpdateDeviceName();
}
const udSupportKey = 'hasRegisterSupportForUnauthenticatedDelivery';

View file

@ -1,5 +1,5 @@
/* eslint-env browser */
/* global dcodeIO */
/* global dcodeIO, libsignal */
/* eslint-disable camelcase, no-bitwise */
@ -10,9 +10,11 @@ module.exports = {
concatenateBytes,
constantTimeEqual,
decryptAesCtr,
decryptDeviceName,
decryptSymmetric,
deriveAccessKey,
encryptAesCtr,
encryptDeviceName,
encryptSymmetric,
fromEncodedBinaryToArrayBuffer,
getAccessKeyVerifier,
@ -30,6 +32,55 @@ module.exports = {
// High-level Operations
async function encryptDeviceName(deviceName, identityPublic) {
const plaintext = bytesFromString(deviceName);
const ephemeralKeyPair = await libsignal.KeyHelper.generateIdentityKeyPair();
const masterSecret = await libsignal.Curve.async.calculateAgreement(
identityPublic,
ephemeralKeyPair.privKey
);
const key1 = await hmacSha256(masterSecret, bytesFromString('auth'));
const syntheticIv = _getFirstBytes(await hmacSha256(key1, plaintext), 16);
const key2 = await hmacSha256(masterSecret, bytesFromString('cipher'));
const cipherKey = await hmacSha256(key2, syntheticIv);
const counter = getZeroes(16);
const ciphertext = await encryptAesCtr(cipherKey, plaintext, counter);
return {
ephemeralPublic: ephemeralKeyPair.pubKey,
syntheticIv,
ciphertext,
};
}
async function decryptDeviceName(
{ ephemeralPublic, syntheticIv, ciphertext } = {},
identityPrivate
) {
const masterSecret = await libsignal.Curve.async.calculateAgreement(
ephemeralPublic,
identityPrivate
);
const key2 = await hmacSha256(masterSecret, bytesFromString('cipher'));
const cipherKey = await hmacSha256(key2, syntheticIv);
const counter = getZeroes(16);
const plaintext = await decryptAesCtr(cipherKey, ciphertext, counter);
const key1 = await hmacSha256(masterSecret, bytesFromString('auth'));
const ourSyntheticIv = _getFirstBytes(await hmacSha256(key1, plaintext), 16);
if (!constantTimeEqual(ourSyntheticIv, syntheticIv)) {
throw new Error('decryptDeviceName: synthetic IV did not match');
}
return stringFromBytes(plaintext);
}
async function deriveAccessKey(profileKey) {
const iv = getZeroes(12);
const plaintext = getZeroes(16);

View file

@ -325,6 +325,7 @@ function HTTPError(message, providedCode, response, stack) {
const URL_CALLS = {
accounts: 'v1/accounts',
updateDeviceName: 'v1/accounts/name',
attachment: 'v1/attachments',
deliveryCert: 'v1/certificate/delivery',
supportUnauthenticatedDelivery: 'v1/devices/unauthenticated_delivery',
@ -386,6 +387,7 @@ function initialize({ url, cdnUrl, certificateAuthority, proxyUrl }) {
sendMessages,
sendMessagesUnauth,
setSignedPreKey,
updateDeviceName,
};
function _ajax(param) {
@ -568,6 +570,16 @@ function initialize({ url, cdnUrl, certificateAuthority, proxyUrl }) {
return response;
}
function updateDeviceName(deviceName) {
return _ajax({
call: 'updateDeviceName',
httpType: 'PUT',
jsonData: {
deviceName,
},
});
}
function getDevices() {
return _ajax({
call: 'devices',