|
|
|
@ -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();
|
|
|
|
|
|
|
|
|
|
const response = await this.server.confirmCode(
|
|
|
|
|
number,
|
|
|
|
|
verificationCode,
|
|
|
|
|
password,
|
|
|
|
|
signalingKey,
|
|
|
|
|
registrationId,
|
|
|
|
|
encryptedDeviceName,
|
|
|
|
|
{ accessKey }
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
if (previousNumber && previousNumber !== number) {
|
|
|
|
|
window.log.warn(
|
|
|
|
|
'New number is different from old number; deleting all previous data'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return response;
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 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'),
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
// 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(
|
|
|
|
|
'read-receipt-setting',
|
|
|
|
|
Boolean(readReceipts)
|
|
|
|
|
);
|
|
|
|
|
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.user.setNumberAndDeviceId(
|
|
|
|
|
number,
|
|
|
|
|
response.deviceId || 1,
|
|
|
|
|
deviceName
|
|
|
|
|
);
|
|
|
|
|
await textsecure.storage.put(
|
|
|
|
|
'regionCode',
|
|
|
|
|
libphonenumber.util.getRegionCodeForNumber(number)
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
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 =
|
|
|
|
|