Handle PNI keys from ProvisionMessage
This commit is contained in:
parent
19441cd3f3
commit
2b0c98f943
|
@ -14,9 +14,13 @@ message ProvisionEnvelope {
|
||||||
}
|
}
|
||||||
|
|
||||||
message ProvisionMessage {
|
message ProvisionMessage {
|
||||||
optional bytes identityKeyPrivate = 2;
|
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 number = 3;
|
||||||
optional string uuid = 8;
|
|
||||||
optional string provisioningCode = 4;
|
optional string provisioningCode = 4;
|
||||||
optional string userAgent = 5;
|
optional string userAgent = 5;
|
||||||
optional bytes profileKey = 6;
|
optional bytes profileKey = 6;
|
||||||
|
|
|
@ -21,6 +21,7 @@ import { InstallError } from '../../components/installScreen/InstallScreenErrorS
|
||||||
import { MAX_DEVICE_NAME_LENGTH } from '../../components/installScreen/InstallScreenChoosingDeviceNameStep';
|
import { MAX_DEVICE_NAME_LENGTH } from '../../components/installScreen/InstallScreenChoosingDeviceNameStep';
|
||||||
import { HTTPError } from '../../textsecure/Errors';
|
import { HTTPError } from '../../textsecure/Errors';
|
||||||
import { isRecord } from '../../util/isRecord';
|
import { isRecord } from '../../util/isRecord';
|
||||||
|
import * as Errors from '../../types/errors';
|
||||||
import { normalizeDeviceName } from '../../util/normalizeDeviceName';
|
import { normalizeDeviceName } from '../../util/normalizeDeviceName';
|
||||||
|
|
||||||
type PropsType = ComponentProps<typeof InstallScreen>;
|
type PropsType = ComponentProps<typeof InstallScreen>;
|
||||||
|
@ -183,7 +184,7 @@ export function SmartInstallScreen(): ReactElement {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error(
|
log.error(
|
||||||
'confirmNumber: error clearing database',
|
'confirmNumber: error clearing database',
|
||||||
error && error.stack ? error.stack : error
|
Errors.toLogFormat(error)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -205,13 +206,17 @@ export function SmartInstallScreen(): ReactElement {
|
||||||
);
|
);
|
||||||
|
|
||||||
window.removeSetupMenuItems();
|
window.removeSetupMenuItems();
|
||||||
} catch (err: unknown) {
|
} catch (error) {
|
||||||
|
log.error(
|
||||||
|
'account.registerSecondDevice: got an error',
|
||||||
|
Errors.toLogFormat(error)
|
||||||
|
);
|
||||||
if (hasCleanedUp) {
|
if (hasCleanedUp) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setState({
|
setState({
|
||||||
step: InstallScreenStep.Error,
|
step: InstallScreenStep.Error,
|
||||||
error: getInstallError(err),
|
error: getInstallError(error),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -76,7 +76,7 @@ export type GeneratedKeysType = {
|
||||||
type CreateAccountOptionsType = Readonly<{
|
type CreateAccountOptionsType = Readonly<{
|
||||||
number: string;
|
number: string;
|
||||||
verificationCode: string;
|
verificationCode: string;
|
||||||
identityKeyPair: KeyPairType;
|
aciKeyPair: KeyPairType;
|
||||||
pniKeyPair?: KeyPairType;
|
pniKeyPair?: KeyPairType;
|
||||||
profileKey?: Uint8Array;
|
profileKey?: Uint8Array;
|
||||||
deviceName?: string;
|
deviceName?: string;
|
||||||
|
@ -167,7 +167,7 @@ export default class AccountManager extends EventTarget {
|
||||||
|
|
||||||
async registerSingleDevice(number: string, verificationCode: string) {
|
async registerSingleDevice(number: string, verificationCode: string) {
|
||||||
return this.queueTask(async () => {
|
return this.queueTask(async () => {
|
||||||
const identityKeyPair = generateKeyPair();
|
const aciKeyPair = generateKeyPair();
|
||||||
const pniKeyPair = generateKeyPair();
|
const pniKeyPair = generateKeyPair();
|
||||||
const profileKey = getRandomBytes(PROFILE_KEY_LENGTH);
|
const profileKey = getRandomBytes(PROFILE_KEY_LENGTH);
|
||||||
const accessKey = deriveAccessKey(profileKey);
|
const accessKey = deriveAccessKey(profileKey);
|
||||||
|
@ -177,7 +177,7 @@ export default class AccountManager extends EventTarget {
|
||||||
await this.createAccount({
|
await this.createAccount({
|
||||||
number,
|
number,
|
||||||
verificationCode,
|
verificationCode,
|
||||||
identityKeyPair,
|
aciKeyPair,
|
||||||
pniKeyPair,
|
pniKeyPair,
|
||||||
profileKey,
|
profileKey,
|
||||||
accessKey,
|
accessKey,
|
||||||
|
@ -277,7 +277,7 @@ export default class AccountManager extends EventTarget {
|
||||||
if (
|
if (
|
||||||
!provisionMessage.number ||
|
!provisionMessage.number ||
|
||||||
!provisionMessage.provisioningCode ||
|
!provisionMessage.provisioningCode ||
|
||||||
!provisionMessage.identityKeyPair
|
!provisionMessage.aciKeyPair
|
||||||
) {
|
) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'AccountManager.registerSecondDevice: Provision message was missing key data'
|
'AccountManager.registerSecondDevice: Provision message was missing key data'
|
||||||
|
@ -290,20 +290,31 @@ export default class AccountManager extends EventTarget {
|
||||||
await this.createAccount({
|
await this.createAccount({
|
||||||
number: provisionMessage.number,
|
number: provisionMessage.number,
|
||||||
verificationCode: provisionMessage.provisioningCode,
|
verificationCode: provisionMessage.provisioningCode,
|
||||||
identityKeyPair: provisionMessage.identityKeyPair,
|
aciKeyPair: provisionMessage.aciKeyPair,
|
||||||
|
pniKeyPair: provisionMessage.pniKeyPair,
|
||||||
profileKey: provisionMessage.profileKey,
|
profileKey: provisionMessage.profileKey,
|
||||||
deviceName,
|
deviceName,
|
||||||
userAgent: provisionMessage.userAgent,
|
userAgent: provisionMessage.userAgent,
|
||||||
readReceipts: provisionMessage.readReceipts,
|
readReceipts: provisionMessage.readReceipts,
|
||||||
});
|
});
|
||||||
await clearSessionsAndPreKeys();
|
await clearSessionsAndPreKeys();
|
||||||
// TODO: DESKTOP-2794
|
|
||||||
|
const keyKinds = [UUIDKind.ACI];
|
||||||
|
if (provisionMessage.pniKeyPair) {
|
||||||
|
keyKinds.push(UUIDKind.PNI);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
keyKinds.map(async kind => {
|
||||||
const keys = await this.generateKeys(
|
const keys = await this.generateKeys(
|
||||||
SIGNED_KEY_GEN_BATCH_SIZE,
|
SIGNED_KEY_GEN_BATCH_SIZE,
|
||||||
UUIDKind.ACI
|
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 {
|
} finally {
|
||||||
this.server.finishRegistration(registrationBaton);
|
this.server.finishRegistration(registrationBaton);
|
||||||
}
|
}
|
||||||
|
@ -493,7 +504,7 @@ export default class AccountManager extends EventTarget {
|
||||||
async createAccount({
|
async createAccount({
|
||||||
number,
|
number,
|
||||||
verificationCode,
|
verificationCode,
|
||||||
identityKeyPair,
|
aciKeyPair,
|
||||||
pniKeyPair,
|
pniKeyPair,
|
||||||
profileKey,
|
profileKey,
|
||||||
deviceName,
|
deviceName,
|
||||||
|
@ -511,7 +522,7 @@ export default class AccountManager extends EventTarget {
|
||||||
|
|
||||||
let encryptedDeviceName;
|
let encryptedDeviceName;
|
||||||
if (deviceName) {
|
if (deviceName) {
|
||||||
encryptedDeviceName = this.encryptDeviceName(deviceName, identityKeyPair);
|
encryptedDeviceName = this.encryptDeviceName(deviceName, aciKeyPair);
|
||||||
await this.deviceNameIsEncrypted();
|
await this.deviceNameIsEncrypted();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -623,7 +634,7 @@ export default class AccountManager extends EventTarget {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
storage.protocol.saveIdentityWithAttributes(new UUID(ourUuid), {
|
storage.protocol.saveIdentityWithAttributes(new UUID(ourUuid), {
|
||||||
...identityAttrs,
|
...identityAttrs,
|
||||||
publicKey: identityKeyPair.pubKey,
|
publicKey: aciKeyPair.pubKey,
|
||||||
}),
|
}),
|
||||||
pniKeyPair
|
pniKeyPair
|
||||||
? storage.protocol.saveIdentityWithAttributes(new UUID(ourPni), {
|
? storage.protocol.saveIdentityWithAttributes(new UUID(ourPni), {
|
||||||
|
@ -636,8 +647,8 @@ export default class AccountManager extends EventTarget {
|
||||||
const identityKeyMap = {
|
const identityKeyMap = {
|
||||||
...(storage.get('identityKeyMap') || {}),
|
...(storage.get('identityKeyMap') || {}),
|
||||||
[ourUuid]: {
|
[ourUuid]: {
|
||||||
pubKey: Bytes.toBase64(identityKeyPair.pubKey),
|
pubKey: Bytes.toBase64(aciKeyPair.pubKey),
|
||||||
privKey: Bytes.toBase64(identityKeyPair.privKey),
|
privKey: Bytes.toBase64(aciKeyPair.privKey),
|
||||||
},
|
},
|
||||||
...(pniKeyPair
|
...(pniKeyPair
|
||||||
? {
|
? {
|
||||||
|
|
|
@ -17,9 +17,11 @@ import { strictAssert } from '../util/assert';
|
||||||
import { normalizeUuid } from '../util/normalizeUuid';
|
import { normalizeUuid } from '../util/normalizeUuid';
|
||||||
|
|
||||||
type ProvisionDecryptResult = {
|
type ProvisionDecryptResult = {
|
||||||
identityKeyPair: KeyPairType;
|
aciKeyPair: KeyPairType;
|
||||||
|
pniKeyPair?: KeyPairType;
|
||||||
number?: string;
|
number?: string;
|
||||||
uuid?: string;
|
aci?: string;
|
||||||
|
pni?: string;
|
||||||
provisioningCode?: string;
|
provisioningCode?: string;
|
||||||
userAgent?: string;
|
userAgent?: string;
|
||||||
readReceipts?: boolean;
|
readReceipts?: boolean;
|
||||||
|
@ -61,18 +63,24 @@ class ProvisioningCipherInner {
|
||||||
|
|
||||||
const plaintext = decryptAes256CbcPkcsPadding(keys[0], ciphertext, iv);
|
const plaintext = decryptAes256CbcPkcsPadding(keys[0], ciphertext, iv);
|
||||||
const provisionMessage = Proto.ProvisionMessage.decode(plaintext);
|
const provisionMessage = Proto.ProvisionMessage.decode(plaintext);
|
||||||
const privKey = provisionMessage.identityKeyPrivate;
|
const aciPrivKey = provisionMessage.aciIdentityKeyPrivate;
|
||||||
strictAssert(privKey, 'Missing identityKeyPrivate in ProvisionMessage');
|
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;
|
const { aci, pni } = provisionMessage;
|
||||||
strictAssert(uuid, 'Missing uuid in provisioning message');
|
strictAssert(aci, 'Missing aci in provisioning message');
|
||||||
|
|
||||||
const ret: ProvisionDecryptResult = {
|
const ret: ProvisionDecryptResult = {
|
||||||
identityKeyPair: keyPair,
|
aciKeyPair,
|
||||||
|
pniKeyPair,
|
||||||
number: provisionMessage.number,
|
number: provisionMessage.number,
|
||||||
uuid: normalizeUuid(uuid, 'ProvisionMessage.uuid'),
|
aci: normalizeUuid(aci, 'ProvisionMessage.aci'),
|
||||||
|
pni: pni ? normalizeUuid(pni, 'ProvisionMessage.pni') : undefined,
|
||||||
provisioningCode: provisionMessage.provisioningCode,
|
provisioningCode: provisionMessage.provisioningCode,
|
||||||
userAgent: provisionMessage.userAgent,
|
userAgent: provisionMessage.userAgent,
|
||||||
readReceipts: provisionMessage.readReceipts,
|
readReceipts: provisionMessage.readReceipts,
|
||||||
|
|
Loading…
Reference in New Issue