Support for loading, storing, and using kyber keys in decryption

This commit is contained in:
Scott Nonnenberg
2023-07-14 09:53:20 -07:00
committed by Fedor Indutnyy
parent c1580a5eb3
commit b6445a6af0
49 changed files with 2260 additions and 806 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -251,15 +251,6 @@ export class SendMessageProtoError extends Error implements CallbackResultType {
}
}
export class SignedPreKeyRotationError extends ReplayableError {
constructor() {
super({
name: 'SignedPreKeyRotationError',
message: 'Too many signed prekey rotation failures',
});
}
}
export class MessageError extends ReplayableError {
readonly httpError: HTTPError;

View File

@@ -32,6 +32,7 @@ import {
import {
IdentityKeys,
KyberPreKeys,
PreKeys,
SenderKeys,
Sessions,
@@ -1758,6 +1759,7 @@ export default class MessageReceiver
const preKeyStore = new PreKeys({ ourUuid: destinationUuid });
const signedPreKeyStore = new SignedPreKeys({ ourUuid: destinationUuid });
const kyberPreKeyStore = new KyberPreKeys({ ourUuid: destinationUuid });
const sealedSenderIdentifier = envelope.sourceUuid;
strictAssert(
@@ -1786,7 +1788,8 @@ export default class MessageReceiver
sessionStore,
identityKeyStore,
preKeyStore,
signedPreKeyStore
signedPreKeyStore,
kyberPreKeyStore
),
zone
);
@@ -1811,6 +1814,7 @@ export default class MessageReceiver
const { destinationUuid } = envelope;
const preKeyStore = new PreKeys({ ourUuid: destinationUuid });
const signedPreKeyStore = new SignedPreKeys({ ourUuid: destinationUuid });
const kyberPreKeyStore = new KyberPreKeys({ ourUuid: destinationUuid });
strictAssert(identifier !== undefined, 'Empty identifier');
strictAssert(sourceDevice !== undefined, 'Empty source device');
@@ -1903,7 +1907,8 @@ export default class MessageReceiver
sessionStore,
identityKeyStore,
preKeyStore,
signedPreKeyStore
signedPreKeyStore,
kyberPreKeyStore
)
),
zone
@@ -2105,17 +2110,18 @@ export default class MessageReceiver
msg: Proto.IStoryMessage,
sentMessage?: ProcessedSent
): Promise<void> {
const logId = getEnvelopeId(envelope);
const envelopeId = getEnvelopeId(envelope);
const logId = `MessageReceiver.handleStoryMessage(${envelopeId})`;
logUnexpectedUrgentValue(envelope, 'story');
if (getStoriesBlocked()) {
log.info('MessageReceiver.handleStoryMessage: dropping', logId);
log.info(`${logId}: dropping`);
this.removeFromCache(envelope);
return;
}
log.info('MessageReceiver.handleStoryMessage', logId);
log.info(`${logId} starting`);
const attachments: Array<ProcessedAttachment> = [];
let preview: ReadonlyArray<ProcessedPreview> | undefined;
@@ -2150,11 +2156,7 @@ export default class MessageReceiver
const groupV2 = msg.group ? processGroupV2Context(msg.group) : undefined;
if (groupV2 && this.isGroupBlocked(groupV2.id)) {
log.warn(
`MessageReceiver.handleStoryMessage: envelope ${getEnvelopeId(
envelope
)} ignored; destined for blocked group`
);
log.warn(`${logId}: ignored; destined for blocked group`);
this.removeFromCache(envelope);
return;
}
@@ -2165,10 +2167,7 @@ export default class MessageReceiver
);
if (timeRemaining <= 0) {
log.info(
'MessageReceiver.handleStoryMessage: story already expired',
logId
);
log.info(`${logId}: story already expired`);
this.removeFromCache(envelope);
return;
}
@@ -2188,6 +2187,7 @@ export default class MessageReceiver
};
if (sentMessage && message.groupV2) {
log.warn(`${logId}: envelope is a sent group story`);
const ev = new SentEvent(
{
destinationUuid: {
@@ -2220,6 +2220,7 @@ export default class MessageReceiver
}
if (sentMessage) {
log.warn(`${logId}: envelope is a sent distribution list story`);
const { storyMessageRecipients } = sentMessage;
const recipients = storyMessageRecipients ?? [];
@@ -2248,8 +2249,7 @@ export default class MessageReceiver
} else {
assertDev(
false,
`MessageReceiver.handleStoryMessage(${logId}): missing ` +
`distribution list id for: ${destinationUuid}`
`${logId}: missing distribution list id for: ${destinationUuid}`
);
}
@@ -2296,6 +2296,7 @@ export default class MessageReceiver
return;
}
log.warn(`${logId}: envelope is a received story`);
const ev = new MessageEvent(
{
source: envelope.source,
@@ -3241,6 +3242,7 @@ export default class MessageReceiver
{
identityKeyPair,
signedPreKey,
lastResortKyberPreKey,
registrationId,
newE164,
}: Proto.SyncMessage.IPniChangeNumber
@@ -3255,6 +3257,7 @@ export default class MessageReceiver
return;
}
// TDOO: DESKTOP-5652
if (
!Bytes.isNotEmpty(identityKeyPair) ||
!Bytes.isNotEmpty(signedPreKey) ||
@@ -3268,6 +3271,7 @@ export default class MessageReceiver
const manager = window.getAccountManager();
await manager.setPni(updatedPni.toString(), {
identityKeyPair,
lastResortKyberPreKey: dropNull(lastResortKyberPreKey),
signedPreKey,
registrationId,
});

View File

@@ -25,7 +25,7 @@ import type {
TextAttachmentType,
UploadedAttachmentType,
} from '../types/Attachment';
import type { UUID, TaggedUUIDStringType } from '../types/UUID';
import { type UUID, type TaggedUUIDStringType, UUIDKind } from '../types/UUID';
import type {
ChallengeType,
GetGroupLogOptionsType,
@@ -53,7 +53,6 @@ import * as Bytes from '../Bytes';
import { getRandomBytes } from '../Crypto';
import {
MessageError,
SignedPreKeyRotationError,
SendMessageProtoError,
HTTPError,
NoSenderKeyError,
@@ -79,6 +78,7 @@ import {
numberToAddressType,
} from '../types/EmbeddedContact';
import { missingCaseError } from '../util/missingCaseError';
import { drop } from '../util/drop';
export type SendMetadataType = {
[identifier: string]: {
@@ -956,27 +956,31 @@ export default class MessageSender {
});
return new Promise((resolve, reject) => {
this.sendMessageProto({
callback: (res: CallbackResultType) => {
if (res.errors && res.errors.length > 0) {
reject(new SendMessageProtoError(res));
} else {
resolve(res);
}
},
contentHint,
groupId,
options,
proto,
recipients: messageOptions.recipients || [],
timestamp: messageOptions.timestamp,
urgent,
story,
});
drop(
this.sendMessageProto({
callback: (res: CallbackResultType) => {
if (res.errors && res.errors.length > 0) {
reject(new SendMessageProtoError(res));
} else {
resolve(res);
}
},
contentHint,
groupId,
options,
proto,
recipients: messageOptions.recipients || [],
timestamp: messageOptions.timestamp,
urgent,
story,
})
);
});
}
sendMessageProto({
// Note: all the other low-level sends call this, so it is a chokepoint for 1:1 sends
// The chokepoint for group sends is sendContentMessageToGroup
async sendMessageProto({
callback,
contentHint,
groupId,
@@ -998,13 +1002,26 @@ export default class MessageSender {
story?: boolean;
timestamp: number;
urgent: boolean;
}>): void {
const rejections = window.textsecure.storage.get(
'signedKeyRotationRejected',
0
);
if (rejections > 5) {
throw new SignedPreKeyRotationError();
}>): Promise<void> {
const accountManager = window.getAccountManager();
try {
if (accountManager.areKeysOutOfDate(UUIDKind.ACI)) {
log.warn(
`sendMessageProto/${timestamp}: Keys are out of date; updating before send`
);
await accountManager.maybeUpdateKeys(UUIDKind.ACI);
if (accountManager.areKeysOutOfDate(UUIDKind.ACI)) {
throw new Error('Keys still out of date after update');
}
}
} catch (error) {
// TODO: DESKTOP-5642
callback({
dataMessage: undefined,
editMessage: undefined,
errors: [error],
});
return;
}
const outgoing = new OutgoingMessage({
@@ -1022,8 +1039,10 @@ export default class MessageSender {
});
recipients.forEach(identifier => {
void this.queueJobForIdentifier(identifier, async () =>
outgoing.sendToIdentifier(identifier)
drop(
this.queueJobForIdentifier(identifier, async () =>
outgoing.sendToIdentifier(identifier)
)
);
});
}
@@ -1056,17 +1075,19 @@ export default class MessageSender {
resolve(result);
};
this.sendMessageProto({
callback,
contentHint,
groupId,
options,
proto,
recipients,
timestamp,
urgent,
story,
});
drop(
this.sendMessageProto({
callback,
contentHint,
groupId,
options,
proto,
recipients,
timestamp,
urgent,
story,
})
);
});
}
@@ -1096,16 +1117,18 @@ export default class MessageSender {
resolve(res);
}
};
this.sendMessageProto({
callback,
contentHint,
groupId,
options,
proto,
recipients: [identifier],
timestamp,
urgent,
});
drop(
this.sendMessageProto({
callback,
contentHint,
groupId,
options,
proto,
recipients: [identifier],
timestamp,
urgent,
})
);
});
}
@@ -2036,18 +2059,20 @@ export default class MessageSender {
}
};
this.sendMessageProto({
callback,
contentHint,
groupId,
options,
proto,
recipients: identifiers,
sendLogCallback,
story,
timestamp,
urgent,
});
drop(
this.sendMessageProto({
callback,
contentHint,
groupId,
options,
proto,
recipients: identifiers,
sendLogCallback,
story,
timestamp,
urgent,
})
);
});
}

View File

@@ -14,6 +14,7 @@ import type { RawBodyRange } from '../types/BodyRange';
export {
IdentityKeyType,
IdentityKeyIdType,
KyberPreKeyType,
PreKeyIdType,
PreKeyType,
SenderKeyIdType,
@@ -286,6 +287,7 @@ export type IRequestHandler = {
export type PniKeyMaterialType = Readonly<{
identityKeyPair: Uint8Array;
signedPreKey: Uint8Array;
lastResortKyberPreKey?: Uint8Array;
registrationId: number;
}>;

View File

@@ -1,13 +1,17 @@
// Copyright 2017 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { isNumber } from 'lodash';
import * as durations from '../util/durations';
import { clearTimeoutIfNecessary } from '../util/clearTimeoutIfNecessary';
import * as Registration from '../util/registration';
import { UUIDKind } from '../types/UUID';
import * as log from '../logging/log';
import * as Errors from '../types/errors';
const ROTATION_INTERVAL = 2 * durations.DAY;
const UPDATE_INTERVAL = 2 * durations.DAY;
const UPDATE_TIME_STORAGE_KEY = 'nextScheduledUpdateKeyTime';
export type MinimalEventsType = {
on(event: 'timetravel', callback: () => void): void;
@@ -15,23 +19,20 @@ export type MinimalEventsType = {
let initComplete = false;
export class RotateSignedPreKeyListener {
export class UpdateKeysListener {
public timeout: NodeJS.Timeout | undefined;
protected scheduleRotationForNow(): void {
protected scheduleUpdateForNow(): void {
const now = Date.now();
void window.textsecure.storage.put('nextSignedKeyRotationTime', now);
void window.textsecure.storage.put(UPDATE_TIME_STORAGE_KEY, now);
}
protected setTimeoutForNextRun(): void {
const now = Date.now();
const time = window.textsecure.storage.get(
'nextSignedKeyRotationTime',
now
);
const time = window.textsecure.storage.get(UPDATE_TIME_STORAGE_KEY, now);
log.info(
'Next signed key rotation scheduled for',
'UpdateKeysListener: Next update scheduled for',
new Date(time).toISOString()
);
@@ -44,31 +45,29 @@ export class RotateSignedPreKeyListener {
this.timeout = setTimeout(() => this.runWhenOnline(), waitTime);
}
private scheduleNextRotation(): void {
private scheduleNextUpdate(): void {
const now = Date.now();
const nextTime = now + ROTATION_INTERVAL;
void window.textsecure.storage.put('nextSignedKeyRotationTime', nextTime);
const nextTime = now + UPDATE_INTERVAL;
void window.textsecure.storage.put(UPDATE_TIME_STORAGE_KEY, nextTime);
}
private async run(): Promise<void> {
log.info('Rotating signed prekey...');
log.info('UpdateKeysListener: Updating keys...');
try {
const accountManager = window.getAccountManager();
await Promise.all([
accountManager.rotateSignedPreKey(UUIDKind.ACI),
accountManager.rotateSignedPreKey(UUIDKind.PNI),
]);
// We try to update this whenever we remove a preKey; this is a fail-safe to ensure
// we're always in good shape
await Promise.all([
accountManager.refreshPreKeys(UUIDKind.ACI),
accountManager.refreshPreKeys(UUIDKind.PNI),
]);
this.scheduleNextRotation();
await accountManager.maybeUpdateKeys(UUIDKind.ACI);
await accountManager.maybeUpdateKeys(UUIDKind.PNI);
this.scheduleNextUpdate();
this.setTimeoutForNextRun();
} catch (error) {
log.error('rotateSignedPrekey() failed. Trying again in five minutes');
const errorString = isNumber(error.code)
? error.code.toString()
: Errors.toLogFormat(error);
log.error(
`UpdateKeysListener.run failure - trying again in five minutes ${errorString}`
);
setTimeout(() => this.setTimeoutForNextRun(), 5 * durations.MINUTE);
}
}
@@ -77,7 +76,9 @@ export class RotateSignedPreKeyListener {
if (window.navigator.onLine) {
void this.run();
} else {
log.info('We are offline; keys will be rotated when we are next online');
log.info(
'UpdateKeysListener: We are offline; will update keys when we are next online'
);
const listener = () => {
window.removeEventListener('online', listener);
this.setTimeoutForNextRun();
@@ -88,17 +89,15 @@ export class RotateSignedPreKeyListener {
public static init(events: MinimalEventsType, newVersion: boolean): void {
if (initComplete) {
window.SignalContext.log.info(
'Rotate signed prekey listener: Already initialized'
);
window.SignalContext.log.info('UpdateKeysListener: Already initialized');
return;
}
initComplete = true;
const listener = new RotateSignedPreKeyListener();
const listener = new UpdateKeysListener();
if (newVersion) {
listener.scheduleRotationForNow();
listener.scheduleUpdateForNow();
}
listener.setTimeoutForNextRun();

View File

@@ -870,6 +870,11 @@ const attachmentV3Response = z.object({
export type AttachmentV3ResponseType = z.infer<typeof attachmentV3Response>;
export type ServerKeyCountType = {
count: number;
pqCount: number;
};
export type WebAPIType = {
startRegistration(): unknown;
finishRegistration(baton: unknown): void;
@@ -925,7 +930,7 @@ export type WebAPIType = {
deviceId?: number,
options?: { accessKey?: string }
) => Promise<ServerKeysType>;
getMyKeys: (uuidKind: UUIDKind) => Promise<number>;
getMyKeyCounts: (uuidKind: UUIDKind) => Promise<ServerKeyCountType>;
getOnboardingStoryManifest: () => Promise<{
version: string;
languages: Record<string, Array<string>>;
@@ -998,7 +1003,7 @@ export type WebAPIType = {
) => Promise<ReserveUsernameResultType>;
confirmUsername(options: ConfirmUsernameOptionsType): Promise<void>;
registerCapabilities: (capabilities: CapabilitiesUploadType) => Promise<void>;
registerKeys: (genKeys: KeysType, uuidKind: UUIDKind) => Promise<void>;
registerKeys: (genKeys: UploadKeysType, uuidKind: UUIDKind) => Promise<void>;
registerSupportForUnauthenticatedDelivery: () => Promise<void>;
reportMessage: (options: ReportMessageOptionsType) => Promise<void>;
requestVerificationSMS: (number: string, token: string) => Promise<void>;
@@ -1032,10 +1037,6 @@ export type WebAPIType = {
}
) => Promise<MultiRecipient200ResponseType>;
setPhoneNumberDiscoverability: (newValue: boolean) => Promise<void>;
setSignedPreKey: (
signedPreKey: SignedPreKeyType,
uuidKind: UUIDKind
) => Promise<void>;
updateDeviceName: (deviceName: string) => Promise<void>;
uploadAvatar: (
uploadAvatarRequestHeaders: UploadAvatarHeadersType,
@@ -1060,33 +1061,46 @@ export type WebAPIType = {
reconnect: () => Promise<void>;
};
export type SignedPreKeyType = {
export type UploadSignedPreKeyType = {
keyId: number;
publicKey: Uint8Array;
signature: Uint8Array;
};
export type UploadPreKeyType = {
keyId: number;
publicKey: Uint8Array;
};
export type UploadKyberPreKeyType = UploadSignedPreKeyType;
export type KeysType = {
export type UploadKeysType = {
identityKey: Uint8Array;
signedPreKey: SignedPreKeyType;
preKeys: Array<{
keyId: number;
publicKey: Uint8Array;
}>;
// If a field is not provided, the server won't update its data.
preKeys?: Array<UploadPreKeyType>;
pqPreKeys?: Array<UploadSignedPreKeyType>;
pqLastResortPreKey?: UploadSignedPreKeyType;
signedPreKey?: UploadSignedPreKeyType;
};
export type ServerKeysType = {
devices: Array<{
deviceId: number;
registrationId: number;
signedPreKey: {
// We'll get a 404 if none of these keys are provided; we'll have at least one
preKey?: {
keyId: number;
publicKey: Uint8Array;
};
signedPreKey?: {
keyId: number;
publicKey: Uint8Array;
signature: Uint8Array;
};
preKey?: {
pqPreKey?: {
keyId: number;
publicKey: Uint8Array;
signature: Uint8Array;
};
}>;
identityKey: Uint8Array;
@@ -1293,7 +1307,7 @@ export function initialize({
getIceServers,
getKeysForIdentifier,
getKeysForIdentifierUnauth,
getMyKeys,
getMyKeyCounts,
getOnboardingStoryManifest,
getProfile,
getProfileUnauth,
@@ -1331,7 +1345,6 @@ export function initialize({
sendMessagesUnauth,
sendWithSenderKey,
setPhoneNumberDiscoverability,
setSignedPreKey,
startRegistration,
unregisterRequestHandler,
updateDeviceName,
@@ -2052,30 +2065,74 @@ export function initialize({
publicKey: string;
signature: string;
};
type JSONPreKeyType = {
keyId: number;
publicKey: string;
};
type JSONKyberPreKeyType = {
keyId: number;
publicKey: string;
signature: string;
};
type JSONKeysType = {
identityKey: string;
signedPreKey: JSONSignedPreKeyType;
preKeys: Array<{
keyId: number;
publicKey: string;
}>;
preKeys?: Array<JSONPreKeyType>;
pqPreKeys?: Array<JSONKyberPreKeyType>;
pqLastResortPreKey?: JSONKyberPreKeyType;
signedPreKey?: JSONSignedPreKeyType;
};
async function registerKeys(genKeys: KeysType, uuidKind: UUIDKind) {
const preKeys = genKeys.preKeys.map(key => ({
async function registerKeys(genKeys: UploadKeysType, uuidKind: UUIDKind) {
const preKeys = genKeys.preKeys?.map(key => ({
keyId: key.keyId,
publicKey: Bytes.toBase64(key.publicKey),
}));
const pqPreKeys = genKeys.pqPreKeys?.map(key => ({
keyId: key.keyId,
publicKey: Bytes.toBase64(key.publicKey),
signature: Bytes.toBase64(key.signature),
}));
if (
!preKeys?.length &&
!pqPreKeys?.length &&
!genKeys.pqLastResortPreKey &&
!genKeys.signedPreKey
) {
throw new Error(
'registerKeys: None of the four potential key types were provided!'
);
}
if (preKeys && preKeys.length === 0) {
throw new Error('registerKeys: Attempting to upload zero preKeys!');
}
if (pqPreKeys && pqPreKeys.length === 0) {
throw new Error('registerKeys: Attempting to upload zero pqPreKeys!');
}
const keys: JSONKeysType = {
identityKey: Bytes.toBase64(genKeys.identityKey),
signedPreKey: {
keyId: genKeys.signedPreKey.keyId,
publicKey: Bytes.toBase64(genKeys.signedPreKey.publicKey),
signature: Bytes.toBase64(genKeys.signedPreKey.signature),
},
preKeys,
pqPreKeys,
...(genKeys.pqLastResortPreKey
? {
pqLastResortPreKey: {
keyId: genKeys.pqLastResortPreKey.keyId,
publicKey: Bytes.toBase64(genKeys.pqLastResortPreKey.publicKey),
signature: Bytes.toBase64(genKeys.pqLastResortPreKey.signature),
},
}
: null),
...(genKeys.signedPreKey
? {
signedPreKey: {
keyId: genKeys.signedPreKey.keyId,
publicKey: Bytes.toBase64(genKeys.signedPreKey.publicKey),
signature: Bytes.toBase64(genKeys.signedPreKey.signature),
},
}
: null),
};
await _ajax({
@@ -2097,50 +2154,39 @@ export function initialize({
});
}
async function setSignedPreKey(
signedPreKey: SignedPreKeyType,
async function getMyKeyCounts(
uuidKind: UUIDKind
) {
await _ajax({
call: 'signed',
urlParameters: `?${uuidKindToQuery(uuidKind)}`,
httpType: 'PUT',
jsonData: {
keyId: signedPreKey.keyId,
publicKey: Bytes.toBase64(signedPreKey.publicKey),
signature: Bytes.toBase64(signedPreKey.signature),
},
});
}
type ServerKeyCountType = {
count: number;
};
async function getMyKeys(uuidKind: UUIDKind): Promise<number> {
): Promise<ServerKeyCountType> {
const result = (await _ajax({
call: 'keys',
urlParameters: `?${uuidKindToQuery(uuidKind)}`,
httpType: 'GET',
responseType: 'json',
validateResponse: { count: 'number' },
validateResponse: { count: 'number', pqCount: 'number' },
})) as ServerKeyCountType;
return result.count;
return result;
}
type ServerKeyResponseType = {
devices: Array<{
deviceId: number;
registrationId: number;
signedPreKey: {
// We'll get a 404 if none of these keys are provided; we'll have at least one
preKey?: {
keyId: number;
publicKey: string;
};
signedPreKey?: {
keyId: number;
publicKey: string;
signature: string;
};
preKey?: {
pqPreKey?: {
keyId: number;
publicKey: string;
signature: string;
};
}>;
identityKey: string;
@@ -2180,12 +2226,25 @@ export function initialize({
return {
deviceId: device.deviceId,
registrationId: device.registrationId,
preKey,
signedPreKey: {
keyId: device.signedPreKey.keyId,
publicKey: Bytes.fromBase64(device.signedPreKey.publicKey),
signature: Bytes.fromBase64(device.signedPreKey.signature),
},
...(preKey ? { preKey } : null),
...(device.signedPreKey
? {
signedPreKey: {
keyId: device.signedPreKey.keyId,
publicKey: Bytes.fromBase64(device.signedPreKey.publicKey),
signature: Bytes.fromBase64(device.signedPreKey.signature),
},
}
: null),
...(device.pqPreKey
? {
pqPreKey: {
keyId: device.pqPreKey.keyId,
publicKey: Bytes.fromBase64(device.pqPreKey.publicKey),
signature: Bytes.fromBase64(device.pqPreKey.signature),
},
}
: null),
};
});
@@ -2199,7 +2258,7 @@ export function initialize({
const keys = (await _ajax({
call: 'keys',
httpType: 'GET',
urlParameters: `/${identifier}/${deviceId || '*'}`,
urlParameters: `/${identifier}/${deviceId || '*'}?pq=true`,
responseType: 'json',
validateResponse: { identityKey: 'string', devices: 'object' },
})) as ServerKeyResponseType;
@@ -2214,7 +2273,7 @@ export function initialize({
const keys = (await _ajax({
call: 'keys',
httpType: 'GET',
urlParameters: `/${identifier}/${deviceId || '*'}`,
urlParameters: `/${identifier}/${deviceId || '*'}?pq=true`,
responseType: 'json',
validateResponse: { identityKey: 'string', devices: 'object' },
unauthenticated: true,

View File

@@ -3,6 +3,7 @@
import {
ErrorCode,
KEMPublicKey,
LibSignalErrorBase,
PreKeyBundle,
processPreKeyBundle,
@@ -101,7 +102,8 @@ async function handleServerKeys(
await Promise.all(
response.devices.map(async device => {
const { deviceId, registrationId, preKey, signedPreKey } = device;
const { deviceId, registrationId, pqPreKey, preKey, signedPreKey } =
device;
if (
devicesToUpdate !== undefined &&
!devicesToUpdate.includes(deviceId)
@@ -135,6 +137,14 @@ async function handleServerKeys(
Buffer.from(response.identityKey)
);
const pqPreKeyId = pqPreKey?.keyId || null;
const pqPreKeyPublic = pqPreKey
? KEMPublicKey.deserialize(Buffer.from(pqPreKey.publicKey))
: null;
const pqPreKeySignature = pqPreKey
? Buffer.from(pqPreKey.signature)
: null;
const preKeyBundle = PreKeyBundle.new(
registrationId,
deviceId,
@@ -143,7 +153,10 @@ async function handleServerKeys(
signedPreKey.keyId,
signedPreKeyObject,
Buffer.from(signedPreKey.signature),
identityKey
identityKey,
pqPreKeyId,
pqPreKeyPublic,
pqPreKeySignature
);
const address = new QualifiedAddress(