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

View file

@ -52,6 +52,8 @@ import type {
SignedPreKeyIdType,
SignedPreKeyType,
StoredSignedPreKeyType,
KyberPreKeyType,
StoredKyberPreKeyType,
} from './Interface';
import { MINUTE } from '../util/durations';
import { getMessageIdForLogging } from '../util/idForLogging';
@ -73,6 +75,11 @@ const exclusiveInterface: ClientExclusiveInterface = {
bulkAddIdentityKeys,
getAllIdentityKeys,
createOrUpdateKyberPreKey,
getKyberPreKeyById,
bulkAddKyberPreKeys,
getAllKyberPreKeys,
createOrUpdatePreKey,
getPreKeyById,
bulkAddPreKeys,
@ -248,6 +255,37 @@ async function getAllIdentityKeys(): Promise<Array<IdentityKeyType>> {
return keys.map(key => specToBytes(IDENTITY_KEY_SPEC, key));
}
// Kyber Pre Keys
const KYBER_PRE_KEY_SPEC = ['data'];
async function createOrUpdateKyberPreKey(data: KyberPreKeyType): Promise<void> {
const updated: StoredKyberPreKeyType = specFromBytes(
KYBER_PRE_KEY_SPEC,
data
);
await channels.createOrUpdateKyberPreKey(updated);
}
async function getKyberPreKeyById(
id: PreKeyIdType
): Promise<KyberPreKeyType | undefined> {
const data = await channels.getPreKeyById(id);
return specToBytes(KYBER_PRE_KEY_SPEC, data);
}
async function bulkAddKyberPreKeys(
array: Array<KyberPreKeyType>
): Promise<void> {
const updated: Array<StoredKyberPreKeyType> = map(array, data =>
specFromBytes(KYBER_PRE_KEY_SPEC, data)
);
await channels.bulkAddKyberPreKeys(updated);
}
async function getAllKyberPreKeys(): Promise<Array<KyberPreKeyType>> {
const keys = await channels.getAllPreKeys();
return keys.map(key => specToBytes(KYBER_PRE_KEY_SPEC, key));
}
// Pre Keys
async function createOrUpdatePreKey(data: PreKeyType): Promise<void> {

View file

@ -109,21 +109,35 @@ export type MessageType = MessageAttributesType;
export type MessageTypeUnhydrated = {
json: string;
};
export type PreKeyIdType = `${UUIDStringType}:${number}`;
export type KyberPreKeyType = {
id: PreKeyIdType;
createdAt: number;
data: Uint8Array;
isConfirmed: boolean;
isLastResort: boolean;
keyId: number;
ourUuid: UUIDStringType;
};
export type StoredKyberPreKeyType = KyberPreKeyType & {
data: string;
};
export type PreKeyType = {
id: `${UUIDStringType}:${number}`;
id: PreKeyIdType;
createdAt: number;
keyId: number;
ourUuid: UUIDStringType;
privateKey: Uint8Array;
publicKey: Uint8Array;
};
export type StoredPreKeyType = {
id: `${UUIDStringType}:${number}`;
keyId: number;
ourUuid: UUIDStringType;
export type StoredPreKeyType = PreKeyType & {
privateKey: string;
publicKey: string;
};
export type PreKeyIdType = PreKeyType['id'];
export type ServerSearchResultMessageType = {
json: string;
@ -410,16 +424,24 @@ export type DataInterface = {
removeIdentityKeyById: (id: IdentityKeyIdType) => Promise<void>;
removeAllIdentityKeys: () => Promise<void>;
removePreKeyById: (id: PreKeyIdType) => Promise<void>;
removeKyberPreKeyById: (
id: PreKeyIdType | Array<PreKeyIdType>
) => Promise<void>;
removeKyberPreKeysByUuid: (uuid: UUIDStringType) => Promise<void>;
removeAllKyberPreKeys: () => Promise<void>;
removePreKeyById: (id: PreKeyIdType | Array<PreKeyIdType>) => Promise<void>;
removePreKeysByUuid: (uuid: UUIDStringType) => Promise<void>;
removeAllPreKeys: () => Promise<void>;
removeSignedPreKeyById: (id: SignedPreKeyIdType) => Promise<void>;
removeSignedPreKeyById: (
id: SignedPreKeyIdType | Array<SignedPreKeyIdType>
) => Promise<void>;
removeSignedPreKeysByUuid: (uuid: UUIDStringType) => Promise<void>;
removeAllSignedPreKeys: () => Promise<void>;
removeAllItems: () => Promise<void>;
removeItemById: (id: ItemKeyType) => Promise<void>;
removeItemById: (id: ItemKeyType | Array<ItemKeyType>) => Promise<void>;
createOrUpdateSenderKey: (key: SenderKeyType) => Promise<void>;
getSenderKeyById: (id: SenderKeyIdType) => Promise<SenderKeyType | undefined>;
@ -822,6 +844,13 @@ export type ServerInterface = DataInterface & {
bulkAddIdentityKeys: (array: Array<StoredIdentityKeyType>) => Promise<void>;
getAllIdentityKeys: () => Promise<Array<StoredIdentityKeyType>>;
createOrUpdateKyberPreKey: (data: StoredKyberPreKeyType) => Promise<void>;
getKyberPreKeyById: (
id: PreKeyIdType
) => Promise<StoredKyberPreKeyType | undefined>;
bulkAddKyberPreKeys: (array: Array<StoredKyberPreKeyType>) => Promise<void>;
getAllKyberPreKeys: () => Promise<Array<StoredKyberPreKeyType>>;
createOrUpdatePreKey: (data: StoredPreKeyType) => Promise<void>;
getPreKeyById: (id: PreKeyIdType) => Promise<StoredPreKeyType | undefined>;
bulkAddPreKeys: (array: Array<StoredPreKeyType>) => Promise<void>;
@ -901,6 +930,13 @@ export type ClientExclusiveInterface = {
bulkAddIdentityKeys: (array: Array<IdentityKeyType>) => Promise<void>;
getAllIdentityKeys: () => Promise<Array<IdentityKeyType>>;
createOrUpdateKyberPreKey: (data: KyberPreKeyType) => Promise<void>;
getKyberPreKeyById: (
id: PreKeyIdType
) => Promise<KyberPreKeyType | undefined>;
bulkAddKyberPreKeys: (array: Array<KyberPreKeyType>) => Promise<void>;
getAllKyberPreKeys: () => Promise<Array<KyberPreKeyType>>;
createOrUpdatePreKey: (data: PreKeyType) => Promise<void>;
getPreKeyById: (id: PreKeyIdType) => Promise<PreKeyType | undefined>;
bulkAddPreKeys: (array: Array<PreKeyType>) => Promise<void>;

View file

@ -133,6 +133,7 @@ import type {
UnprocessedType,
UnprocessedUpdateType,
GetNearbyMessageFromDeletedSetOptionsType,
StoredKyberPreKeyType,
} from './Interface';
import { SeenStatus } from '../MessageSeenStatus';
import {
@ -173,6 +174,14 @@ const dataInterface: ServerInterface = {
removeAllIdentityKeys,
getAllIdentityKeys,
createOrUpdateKyberPreKey,
getKyberPreKeyById,
bulkAddKyberPreKeys,
removeKyberPreKeyById,
removeKyberPreKeysByUuid,
removeAllKyberPreKeys,
getAllKyberPreKeys,
createOrUpdatePreKey,
getPreKeyById,
bulkAddPreKeys,
@ -655,6 +664,40 @@ async function getAllIdentityKeys(): Promise<Array<StoredIdentityKeyType>> {
return getAllFromTable(getInstance(), IDENTITY_KEYS_TABLE);
}
const KYBER_PRE_KEYS_TABLE = 'kyberPreKeys';
async function createOrUpdateKyberPreKey(
data: StoredKyberPreKeyType
): Promise<void> {
return createOrUpdate(getInstance(), KYBER_PRE_KEYS_TABLE, data);
}
async function getKyberPreKeyById(
id: PreKeyIdType
): Promise<StoredKyberPreKeyType | undefined> {
return getById(getInstance(), KYBER_PRE_KEYS_TABLE, id);
}
async function bulkAddKyberPreKeys(
array: Array<StoredKyberPreKeyType>
): Promise<void> {
return bulkAdd(getInstance(), KYBER_PRE_KEYS_TABLE, array);
}
async function removeKyberPreKeyById(
id: PreKeyIdType | Array<PreKeyIdType>
): Promise<void> {
return removeById(getInstance(), KYBER_PRE_KEYS_TABLE, id);
}
async function removeKyberPreKeysByUuid(uuid: UUIDStringType): Promise<void> {
const db = getInstance();
db.prepare<Query>('DELETE FROM kyberPreKeys WHERE ourUuid IS $uuid;').run({
uuid,
});
}
async function removeAllKyberPreKeys(): Promise<void> {
return removeAllFromTable(getInstance(), KYBER_PRE_KEYS_TABLE);
}
async function getAllKyberPreKeys(): Promise<Array<StoredKyberPreKeyType>> {
return getAllFromTable(getInstance(), KYBER_PRE_KEYS_TABLE);
}
const PRE_KEYS_TABLE = 'preKeys';
async function createOrUpdatePreKey(data: StoredPreKeyType): Promise<void> {
return createOrUpdate(getInstance(), PRE_KEYS_TABLE, data);
@ -667,7 +710,9 @@ async function getPreKeyById(
async function bulkAddPreKeys(array: Array<StoredPreKeyType>): Promise<void> {
return bulkAdd(getInstance(), PRE_KEYS_TABLE, array);
}
async function removePreKeyById(id: PreKeyIdType): Promise<void> {
async function removePreKeyById(
id: PreKeyIdType | Array<PreKeyIdType>
): Promise<void> {
return removeById(getInstance(), PRE_KEYS_TABLE, id);
}
async function removePreKeysByUuid(uuid: UUIDStringType): Promise<void> {
@ -699,7 +744,9 @@ async function bulkAddSignedPreKeys(
): Promise<void> {
return bulkAdd(getInstance(), SIGNED_PRE_KEYS_TABLE, array);
}
async function removeSignedPreKeyById(id: SignedPreKeyIdType): Promise<void> {
async function removeSignedPreKeyById(
id: SignedPreKeyIdType | Array<SignedPreKeyIdType>
): Promise<void> {
return removeById(getInstance(), SIGNED_PRE_KEYS_TABLE, id);
}
async function removeSignedPreKeysByUuid(uuid: UUIDStringType): Promise<void> {
@ -755,7 +802,9 @@ async function getAllItems(): Promise<StoredAllItemsType> {
return result as unknown as StoredAllItemsType;
}
async function removeItemById(id: ItemKeyType): Promise<void> {
async function removeItemById(
id: ItemKeyType | Array<ItemKeyType>
): Promise<void> {
return removeById(getInstance(), ITEMS_TABLE, id);
}
async function removeAllItems(): Promise<void> {
@ -4989,6 +5038,7 @@ async function removeAll(): Promise<void> {
DELETE FROM identityKeys;
DELETE FROM items;
DELETE FROM jobs;
DELETE FROM kyberPreKeys;
DELETE FROM messages_fts;
DELETE FROM messages;
DELETE FROM preKeys;
@ -5024,6 +5074,7 @@ async function removeAllConfiguration(
`
DELETE FROM identityKeys;
DELETE FROM jobs;
DELETE FROM kyberPreKeys;
DELETE FROM preKeys;
DELETE FROM senderKeys;
DELETE FROM sendLogMessageIds;

View file

@ -0,0 +1,42 @@
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { Database } from '@signalapp/better-sqlite3';
import type { LoggerType } from '../../types/Logging';
export default function updateToSchemaVersion85(
currentVersion: number,
db: Database,
logger: LoggerType
): void {
if (currentVersion >= 85) {
return;
}
db.transaction(() => {
db.exec(
`CREATE TABLE kyberPreKeys(
id STRING PRIMARY KEY NOT NULL,
json TEXT NOT NULL,
ourUuid STRING
GENERATED ALWAYS AS (json_extract(json, '$.ourUuid'))
);`
);
// To manage our ACI or PNI keys quickly
db.exec('CREATE INDEX kyberPreKeys_ourUuid ON kyberPreKeys (ourUuid);');
// Add time to all existing preKeys to allow us to expire them
const now = Date.now();
db.exec(
`UPDATE preKeys SET
json = json_set(json, '$.createdAt', ${now});
`
);
db.pragma('user_version = 85');
})();
logger.info('updateToSchemaVersion85: success!');
}

View file

@ -60,6 +60,7 @@ import updateToSchemaVersion81 from './81-contact-removed-notification';
import updateToSchemaVersion82 from './82-edited-messages-read-index';
import updateToSchemaVersion83 from './83-mentions';
import updateToSchemaVersion84 from './84-all-mentions';
import updateToSchemaVersion85 from './85-add-kyber-keys';
function updateToSchemaVersion1(
currentVersion: number,
@ -1984,11 +1985,13 @@ export const SCHEMA_VERSIONS = [
updateToSchemaVersion77,
updateToSchemaVersion78,
updateToSchemaVersion79,
updateToSchemaVersion80,
updateToSchemaVersion81,
updateToSchemaVersion82,
updateToSchemaVersion83,
updateToSchemaVersion84,
updateToSchemaVersion85,
];
export function updateSchema(db: Database, logger: LoggerType): void {

View file

@ -16,6 +16,7 @@ export type TableType =
| 'conversations'
| 'identityKeys'
| 'items'
| 'kyberPreKeys'
| 'messages'
| 'preKeys'
| 'senderKeys'