diff --git a/protos/DeviceMessages.proto b/protos/DeviceMessages.proto index d926f238a..765b76719 100644 --- a/protos/DeviceMessages.proto +++ b/protos/DeviceMessages.proto @@ -14,13 +14,17 @@ message ProvisionEnvelope { } message ProvisionMessage { - optional bytes identityKeyPrivate = 2; - optional string number = 3; - optional string uuid = 8; - optional string provisioningCode = 4; - optional string userAgent = 5; - optional bytes profileKey = 6; - optional bool readReceipts = 7; + optional bytes aciIdentityKeyPublic = 1; + optional bytes aciIdentityKeyPrivate = 2; + optional bytes pniIdentityKeyPublic = 11; + optional bytes pniIdentityKeyPrivate = 12; + optional string aci = 8; + optional string pni = 10; + optional string number = 3; + optional string provisioningCode = 4; + optional string userAgent = 5; + optional bytes profileKey = 6; + optional bool readReceipts = 7; optional uint32 ProvisioningVersion = 9; } diff --git a/ts/state/smart/InstallScreen.tsx b/ts/state/smart/InstallScreen.tsx index 8ac154d62..1a0fbb9c2 100644 --- a/ts/state/smart/InstallScreen.tsx +++ b/ts/state/smart/InstallScreen.tsx @@ -21,6 +21,7 @@ import { InstallError } from '../../components/installScreen/InstallScreenErrorS import { MAX_DEVICE_NAME_LENGTH } from '../../components/installScreen/InstallScreenChoosingDeviceNameStep'; import { HTTPError } from '../../textsecure/Errors'; import { isRecord } from '../../util/isRecord'; +import * as Errors from '../../types/errors'; import { normalizeDeviceName } from '../../util/normalizeDeviceName'; type PropsType = ComponentProps; @@ -183,7 +184,7 @@ export function SmartInstallScreen(): ReactElement { } catch (error) { log.error( 'confirmNumber: error clearing database', - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } } @@ -205,13 +206,17 @@ export function SmartInstallScreen(): ReactElement { ); window.removeSetupMenuItems(); - } catch (err: unknown) { + } catch (error) { + log.error( + 'account.registerSecondDevice: got an error', + Errors.toLogFormat(error) + ); if (hasCleanedUp) { return; } setState({ step: InstallScreenStep.Error, - error: getInstallError(err), + error: getInstallError(error), }); } })(); diff --git a/ts/textsecure/AccountManager.ts b/ts/textsecure/AccountManager.ts index cc1b85570..ac81a0b94 100644 --- a/ts/textsecure/AccountManager.ts +++ b/ts/textsecure/AccountManager.ts @@ -76,7 +76,7 @@ export type GeneratedKeysType = { type CreateAccountOptionsType = Readonly<{ number: string; verificationCode: string; - identityKeyPair: KeyPairType; + aciKeyPair: KeyPairType; pniKeyPair?: KeyPairType; profileKey?: Uint8Array; deviceName?: string; @@ -167,7 +167,7 @@ export default class AccountManager extends EventTarget { async registerSingleDevice(number: string, verificationCode: string) { return this.queueTask(async () => { - const identityKeyPair = generateKeyPair(); + const aciKeyPair = generateKeyPair(); const pniKeyPair = generateKeyPair(); const profileKey = getRandomBytes(PROFILE_KEY_LENGTH); const accessKey = deriveAccessKey(profileKey); @@ -177,7 +177,7 @@ export default class AccountManager extends EventTarget { await this.createAccount({ number, verificationCode, - identityKeyPair, + aciKeyPair, pniKeyPair, profileKey, accessKey, @@ -277,7 +277,7 @@ export default class AccountManager extends EventTarget { if ( !provisionMessage.number || !provisionMessage.provisioningCode || - !provisionMessage.identityKeyPair + !provisionMessage.aciKeyPair ) { throw new Error( 'AccountManager.registerSecondDevice: Provision message was missing key data' @@ -290,20 +290,31 @@ export default class AccountManager extends EventTarget { await this.createAccount({ number: provisionMessage.number, verificationCode: provisionMessage.provisioningCode, - identityKeyPair: provisionMessage.identityKeyPair, + aciKeyPair: provisionMessage.aciKeyPair, + pniKeyPair: provisionMessage.pniKeyPair, profileKey: provisionMessage.profileKey, deviceName, userAgent: provisionMessage.userAgent, readReceipts: provisionMessage.readReceipts, }); await clearSessionsAndPreKeys(); - // TODO: DESKTOP-2794 - const keys = await this.generateKeys( - SIGNED_KEY_GEN_BATCH_SIZE, - UUIDKind.ACI + + const keyKinds = [UUIDKind.ACI]; + if (provisionMessage.pniKeyPair) { + keyKinds.push(UUIDKind.PNI); + } + + await Promise.all( + keyKinds.map(async kind => { + const keys = await this.generateKeys( + SIGNED_KEY_GEN_BATCH_SIZE, + kind + ); + + await this.server.registerKeys(keys, kind); + await this.confirmKeys(keys, kind); + }) ); - await this.server.registerKeys(keys, UUIDKind.ACI); - await this.confirmKeys(keys, UUIDKind.ACI); } finally { this.server.finishRegistration(registrationBaton); } @@ -493,7 +504,7 @@ export default class AccountManager extends EventTarget { async createAccount({ number, verificationCode, - identityKeyPair, + aciKeyPair, pniKeyPair, profileKey, deviceName, @@ -511,7 +522,7 @@ export default class AccountManager extends EventTarget { let encryptedDeviceName; if (deviceName) { - encryptedDeviceName = this.encryptDeviceName(deviceName, identityKeyPair); + encryptedDeviceName = this.encryptDeviceName(deviceName, aciKeyPair); await this.deviceNameIsEncrypted(); } @@ -623,7 +634,7 @@ export default class AccountManager extends EventTarget { await Promise.all([ storage.protocol.saveIdentityWithAttributes(new UUID(ourUuid), { ...identityAttrs, - publicKey: identityKeyPair.pubKey, + publicKey: aciKeyPair.pubKey, }), pniKeyPair ? storage.protocol.saveIdentityWithAttributes(new UUID(ourPni), { @@ -636,8 +647,8 @@ export default class AccountManager extends EventTarget { const identityKeyMap = { ...(storage.get('identityKeyMap') || {}), [ourUuid]: { - pubKey: Bytes.toBase64(identityKeyPair.pubKey), - privKey: Bytes.toBase64(identityKeyPair.privKey), + pubKey: Bytes.toBase64(aciKeyPair.pubKey), + privKey: Bytes.toBase64(aciKeyPair.privKey), }, ...(pniKeyPair ? { diff --git a/ts/textsecure/ProvisioningCipher.ts b/ts/textsecure/ProvisioningCipher.ts index 841c4e707..8783fcfb4 100644 --- a/ts/textsecure/ProvisioningCipher.ts +++ b/ts/textsecure/ProvisioningCipher.ts @@ -17,9 +17,11 @@ import { strictAssert } from '../util/assert'; import { normalizeUuid } from '../util/normalizeUuid'; type ProvisionDecryptResult = { - identityKeyPair: KeyPairType; + aciKeyPair: KeyPairType; + pniKeyPair?: KeyPairType; number?: string; - uuid?: string; + aci?: string; + pni?: string; provisioningCode?: string; userAgent?: string; readReceipts?: boolean; @@ -61,18 +63,24 @@ class ProvisioningCipherInner { const plaintext = decryptAes256CbcPkcsPadding(keys[0], ciphertext, iv); const provisionMessage = Proto.ProvisionMessage.decode(plaintext); - const privKey = provisionMessage.identityKeyPrivate; - strictAssert(privKey, 'Missing identityKeyPrivate in ProvisionMessage'); + const aciPrivKey = provisionMessage.aciIdentityKeyPrivate; + const pniPrivKey = provisionMessage.pniIdentityKeyPrivate; + strictAssert(aciPrivKey, 'Missing aciKeyPrivate in ProvisionMessage'); - const keyPair = createKeyPair(privKey); + const aciKeyPair = createKeyPair(aciPrivKey); + const pniKeyPair = pniPrivKey?.length + ? createKeyPair(pniPrivKey) + : undefined; - const { uuid } = provisionMessage; - strictAssert(uuid, 'Missing uuid in provisioning message'); + const { aci, pni } = provisionMessage; + strictAssert(aci, 'Missing aci in provisioning message'); const ret: ProvisionDecryptResult = { - identityKeyPair: keyPair, + aciKeyPair, + pniKeyPair, number: provisionMessage.number, - uuid: normalizeUuid(uuid, 'ProvisionMessage.uuid'), + aci: normalizeUuid(aci, 'ProvisionMessage.aci'), + pni: pni ? normalizeUuid(pni, 'ProvisionMessage.pni') : undefined, provisioningCode: provisionMessage.provisioningCode, userAgent: provisionMessage.userAgent, readReceipts: provisionMessage.readReceipts,