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

@ -4,6 +4,7 @@
libsignal,
WebSocketResource,
btoa,
Signal,
getString,
libphonenumber,
Event,
@ -45,6 +46,59 @@
requestSMSVerification(number) {
return this.server.requestVerificationSMS(number);
},
async encryptDeviceName(name, providedIdentityKey) {
const identityKey =
providedIdentityKey ||
(await textsecure.storage.protocol.getIdentityKeyPair());
if (!identityKey) {
throw new Error(
'Identity key was not provided and is not in database!'
);
}
const encrypted = await Signal.Crypto.encryptDeviceName(
name,
identityKey.pubKey
);
const proto = new textsecure.protobuf.DeviceName();
proto.ephemeralPublic = encrypted.ephemeralPublic;
proto.syntheticIv = encrypted.syntheticIv;
proto.ciphertext = encrypted.ciphertext;
const arrayBuffer = proto.encode().toArrayBuffer();
return Signal.Crypto.arrayBufferToBase64(arrayBuffer);
},
async decryptDeviceName(base64) {
const identityKey = await textsecure.storage.protocol.getIdentityKeyPair();
const arrayBuffer = Signal.Crypto.base64ToArrayBuffer(base64);
const proto = textsecure.protobuf.DeviceName.decode(arrayBuffer);
const encrypted = {
ephemeralPublic: proto.ephemeralPublic.toArrayBuffer(),
syntheticIv: proto.syntheticIv.toArrayBuffer(),
ciphertext: proto.ciphertext.toArrayBuffer(),
};
const name = await Signal.Crypto.decryptDeviceName(
encrypted,
identityKey.privKey
);
return name;
},
async maybeUpdateDeviceName() {
const isNameEncrypted = textsecure.storage.user.getDeviceNameEncrypted();
if (isNameEncrypted) {
return;
}
const deviceName = await textsecure.storage.user.getDeviceName();
const base64 = await this.encryptDeviceName(deviceName);
await this.server.updateDeviceName(base64);
},
async deviceNameIsEncrypted() {
await textsecure.storage.user.setDeviceNameEncrypted();
},
registerSingleDevice(number, verificationCode) {
const registerKeys = this.server.registerKeys.bind(this.server);
const createAccount = this.createAccount.bind(this);
@ -335,7 +389,7 @@
});
});
},
createAccount(
async createAccount(
number,
verificationCode,
identityKeyPair,
@ -353,110 +407,106 @@
const previousNumber = getNumber(textsecure.storage.get('number_id'));
return this.server
.confirmCode(
number,
verificationCode,
password,
signalingKey,
registrationId,
deviceName,
{ accessKey }
)
.then(response => {
if (previousNumber && previousNumber !== number) {
window.log.warn(
'New number is different from old number; deleting all previous data'
);
const encryptedDeviceName = await this.encryptDeviceName(
deviceName,
identityKeyPair
);
await this.deviceNameIsEncrypted();
return textsecure.storage.protocol.removeAllData().then(
() => {
window.log.info('Successfully deleted previous data');
return response;
},
error => {
window.log.error(
'Something went wrong deleting data from previous number',
error && error.stack ? error.stack : error
);
const response = await this.server.confirmCode(
number,
verificationCode,
password,
signalingKey,
registrationId,
encryptedDeviceName,
{ accessKey }
);
return response;
}
);
}
if (previousNumber && previousNumber !== number) {
window.log.warn(
'New number is different from old number; deleting all previous data'
);
return response;
})
.then(async response => {
await Promise.all([
textsecure.storage.remove('identityKey'),
textsecure.storage.remove('signaling_key'),
textsecure.storage.remove('password'),
textsecure.storage.remove('registrationId'),
textsecure.storage.remove('number_id'),
textsecure.storage.remove('device_name'),
textsecure.storage.remove('regionCode'),
textsecure.storage.remove('userAgent'),
textsecure.storage.remove('profileKey'),
textsecure.storage.remove('read-receipts-setting'),
]);
// update our own identity key, which may have changed
// if we're relinking after a reinstall on the master device
await textsecure.storage.protocol.saveIdentityWithAttributes(number, {
id: number,
publicKey: identityKeyPair.pubKey,
firstUse: true,
timestamp: Date.now(),
verified: textsecure.storage.protocol.VerifiedStatus.VERIFIED,
nonblockingApproval: true,
});
await textsecure.storage.put('identityKey', identityKeyPair);
await textsecure.storage.put('signaling_key', signalingKey);
await textsecure.storage.put('password', password);
await textsecure.storage.put('registrationId', registrationId);
if (profileKey) {
await textsecure.storage.put('profileKey', profileKey);
}
if (userAgent) {
await textsecure.storage.put('userAgent', userAgent);
}
await textsecure.storage.put(
'read-receipt-setting',
Boolean(readReceipts)
try {
await textsecure.storage.protocol.removeAllData();
window.log.info('Successfully deleted previous data');
} catch (error) {
window.log.error(
'Something went wrong deleting data from previous number',
error && error.stack ? error.stack : error
);
}
}
await textsecure.storage.user.setNumberAndDeviceId(
number,
response.deviceId || 1,
deviceName
);
await textsecure.storage.put(
'regionCode',
libphonenumber.util.getRegionCodeForNumber(number)
);
});
await Promise.all([
textsecure.storage.remove('identityKey'),
textsecure.storage.remove('signaling_key'),
textsecure.storage.remove('password'),
textsecure.storage.remove('registrationId'),
textsecure.storage.remove('number_id'),
textsecure.storage.remove('device_name'),
textsecure.storage.remove('regionCode'),
textsecure.storage.remove('userAgent'),
textsecure.storage.remove('profileKey'),
textsecure.storage.remove('read-receipts-setting'),
]);
// update our own identity key, which may have changed
// if we're relinking after a reinstall on the master device
await textsecure.storage.protocol.saveIdentityWithAttributes(number, {
id: number,
publicKey: identityKeyPair.pubKey,
firstUse: true,
timestamp: Date.now(),
verified: textsecure.storage.protocol.VerifiedStatus.VERIFIED,
nonblockingApproval: true,
});
await textsecure.storage.put('identityKey', identityKeyPair);
await textsecure.storage.put('signaling_key', signalingKey);
await textsecure.storage.put('password', password);
await textsecure.storage.put('registrationId', registrationId);
if (profileKey) {
await textsecure.storage.put('profileKey', profileKey);
}
if (userAgent) {
await textsecure.storage.put('userAgent', userAgent);
}
await textsecure.storage.put(
'read-receipt-setting',
Boolean(readReceipts)
);
await textsecure.storage.user.setNumberAndDeviceId(
number,
response.deviceId || 1,
deviceName
);
await textsecure.storage.put(
'regionCode',
libphonenumber.util.getRegionCodeForNumber(number)
);
},
clearSessionsAndPreKeys() {
async clearSessionsAndPreKeys() {
const store = textsecure.storage.protocol;
window.log.info('clearing all sessions, prekeys, and signed prekeys');
return Promise.all([
await Promise.all([
store.clearPreKeyStore(),
store.clearSignedPreKeysStore(),
store.clearSessionStore(),
]);
},
// Takes the same object returned by generateKeys
confirmKeys(keys) {
async confirmKeys(keys) {
const store = textsecure.storage.protocol;
const key = keys.signedPreKey;
const confirmed = true;
window.log.info('confirmKeys: confirming key', key.keyId);
return store.storeSignedPreKey(key.keyId, key.keyPair, confirmed);
await store.storeSignedPreKey(key.keyId, key.keyPair, confirmed);
},
generateKeys(count, providedProgressCallback) {
const progressCallback =