@@ -20,7 +20,7 @@ import {
2020} from '@signalapp/libsignal-client' ;
2121
2222import { DataReader , DataWriter } from './sql/Client.js' ;
23- import type { ItemType } from './sql/Interface.js' ;
23+ import type { ItemType , KyberPreKeyTripleType } from './sql/Interface.js' ;
2424import * as Bytes from './Bytes.js' ;
2525import { constantTimeEqual , sha256 } from './Crypto.js' ;
2626import { assertDev , strictAssert } from './util/assert.js' ;
@@ -213,6 +213,19 @@ export function hydrateSignedPreKey(
213213 ) ;
214214}
215215
216+ // Format: keyId:signedPreKeyId:baseKey
217+ type KyberTripleCacheKeyPrefixType = `${KyberPreKeyTripleType [ 'id' ] } :`;
218+ type KyberTripleCacheKeyType =
219+ `${KyberTripleCacheKeyPrefixType } ${KyberPreKeyTripleType [ 'signedPreKeyId' ] } :${string } `;
220+
221+ function getKyberTripleCacheKey ( {
222+ id,
223+ signedPreKeyId,
224+ baseKey,
225+ } : KyberPreKeyTripleType ) : KyberTripleCacheKeyType {
226+ return `${ id } :${ signedPreKeyId } :${ Bytes . toHex ( baseKey ) } ` ;
227+ }
228+
216229type SessionCacheEntry = CacheEntryType < SessionType , SessionRecord > ;
217230type SenderKeyCacheEntry = CacheEntryType < SenderKeyType , SenderKeyRecord > ;
218231
@@ -254,6 +267,8 @@ export class SignalProtocolStore extends EventEmitter {
254267 CacheEntryType < SignedPreKeyType , SignedPreKeyRecord >
255268 > ;
256269
270+ readonly #kyberTriples = new Set < KyberTripleCacheKeyType > ( ) ;
271+
257272 senderKeyQueues = new Map < QualifiedAddressStringType , PQueue > ( ) ;
258273
259274 sessionQueues = new Map < SessionIdType , PQueue > ( ) ;
@@ -267,6 +282,10 @@ export class SignalProtocolStore extends EventEmitter {
267282 #pendingSessions = new Map < SessionIdType , SessionCacheEntry > ( ) ;
268283 #pendingSenderKeys = new Map < SenderKeyIdType , SenderKeyCacheEntry > ( ) ;
269284 #pendingUnprocessed = new Map < string , UnprocessedType > ( ) ;
285+ #pendingKyberTriples = new Map <
286+ KyberTripleCacheKeyType ,
287+ KyberPreKeyTripleType
288+ > ( ) ;
270289
271290 async hydrateCaches ( ) : Promise < void > {
272291 await Promise . all ( [
@@ -310,6 +329,15 @@ export class SignalProtocolStore extends EventEmitter {
310329 this . #ourRegistrationIds. set ( serviceId , map . value [ serviceId ] ) ;
311330 }
312331 } ) ( ) ,
332+ ( async ( ) => {
333+ this . #kyberTriples. clear ( ) ;
334+
335+ const triples = await DataReader . getAllKyberTriples ( ) ;
336+
337+ for ( const t of triples ) {
338+ this . #kyberTriples. add ( getKyberTripleCacheKey ( t ) ) ;
339+ }
340+ } ) ( ) ,
313341 _fillCaches < string , IdentityKeyType , PublicKey > (
314342 this ,
315343 'identityKeys' ,
@@ -525,14 +553,30 @@ export class SignalProtocolStore extends EventEmitter {
525553 `maybeRemoveKyberPreKey: Not removing kyber prekey ${ id } ; it's a last resort key`
526554 ) ;
527555
528- const result = await DataWriter . markKyberTripleSeenOrFail ( {
529- id : `${ ourServiceId } :${ keyId } ` ,
530- signedPreKeyId,
531- baseKey : baseKey . serialize ( ) ,
556+ await this . withZone ( zone , 'maybeRemoveKyberPreKey' , async ( ) => {
557+ const triple : KyberPreKeyTripleType = {
558+ id : `${ ourServiceId } :${ keyId } ` ,
559+ signedPreKeyId,
560+ baseKey : baseKey . serialize ( ) ,
561+ } ;
562+
563+ const cacheKey = getKyberTripleCacheKey ( triple ) ;
564+
565+ // Note: we don't have to check for `#pendingKyberPreKeysToRemove` since
566+ // it makes the key in question inaccessible to begin with.
567+ if (
568+ this . #kyberTriples. has ( cacheKey ) ||
569+ this . #pendingKyberTriples. has ( cacheKey )
570+ ) {
571+ throw new Error ( `Duplicate kyber triple ${ keyId } :${ signedPreKeyId } ` ) ;
572+ }
573+
574+ this . #pendingKyberTriples. set ( cacheKey , triple ) ;
575+
576+ if ( ! zone . supportsPendingKyberPreKeysToRemove ( ) ) {
577+ await this . #commitZoneChanges( 'removeKyberPreKeys' ) ;
578+ }
532579 } ) ;
533- if ( result === 'fail' ) {
534- throw new Error ( `Duplicate kyber triple ${ keyId } :${ signedPreKeyId } ` ) ;
535- }
536580 }
537581
538582 async removeKyberPreKeys (
@@ -1169,13 +1213,15 @@ export class SignalProtocolStore extends EventEmitter {
11691213 const pendingSenderKeys = this . #pendingSenderKeys;
11701214 const pendingSessions = this . #pendingSessions;
11711215 const pendingUnprocessed = this . #pendingUnprocessed;
1216+ const pendingKyberTriples = this . #pendingKyberTriples;
11721217
11731218 if (
11741219 pendingKyberPreKeysToRemove . size === 0 &&
11751220 pendingPreKeysToRemove . size === 0 &&
11761221 pendingSenderKeys . size === 0 &&
11771222 pendingSessions . size === 0 &&
1178- pendingUnprocessed . size === 0
1223+ pendingUnprocessed . size === 0 &&
1224+ pendingKyberTriples . size === 0
11791225 ) {
11801226 return ;
11811227 }
@@ -1186,14 +1232,16 @@ export class SignalProtocolStore extends EventEmitter {
11861232 `pending preKeysToRemove ${ pendingKyberPreKeysToRemove . size } , ` +
11871233 `pending senderKeys ${ pendingSenderKeys . size } , ` +
11881234 `pending sessions ${ pendingSessions . size } , ` +
1189- `pending unprocessed ${ pendingUnprocessed . size } `
1235+ `pending unprocessed ${ pendingUnprocessed . size } , ` +
1236+ `pending kyberTriples ${ pendingKyberTriples . size } `
11901237 ) ;
11911238
11921239 this . #pendingKyberPreKeysToRemove = new Set ( ) ;
11931240 this . #pendingPreKeysToRemove = new Set ( ) ;
11941241 this . #pendingSenderKeys = new Map ( ) ;
11951242 this . #pendingSessions = new Map ( ) ;
11961243 this . #pendingUnprocessed = new Map ( ) ;
1244+ this . #pendingKyberTriples = new Map ( ) ;
11971245
11981246 // Commit both sender keys, sessions and unprocessed in the same database transaction
11991247 // to unroll both on error.
@@ -1207,17 +1255,30 @@ export class SignalProtocolStore extends EventEmitter {
12071255 ( { fromDB } ) => fromDB
12081256 ) ,
12091257 unprocessed : Array . from ( pendingUnprocessed . values ( ) ) ,
1258+ kyberTriples : Array . from ( pendingKyberTriples . values ( ) ) ,
12101259 } ) ;
12111260
12121261 // Apply changes to in-memory storage after successful DB write.
12131262
1263+ for ( const cacheKey of pendingKyberTriples . keys ( ) ) {
1264+ this . #kyberTriples. add ( cacheKey ) ;
1265+ }
1266+
12141267 const { kyberPreKeys } = this ;
12151268 assertDev (
12161269 kyberPreKeys !== undefined ,
12171270 "Can't commit unhydrated kyberPreKeys storage"
12181271 ) ;
1219- pendingKyberPreKeysToRemove . forEach ( value => {
1272+ pendingKyberPreKeysToRemove . forEach ( ( value : PreKeyIdType ) => {
12201273 kyberPreKeys . delete ( value ) ;
1274+
1275+ // Remove all cached kyber triples for this key.
1276+ const prefix : KyberTripleCacheKeyPrefixType = `${ value } :` ;
1277+ for ( const key of this . #kyberTriples. keys ( ) ) {
1278+ if ( key . startsWith ( prefix ) ) {
1279+ this . #kyberTriples. delete ( key ) ;
1280+ }
1281+ }
12211282 } ) ;
12221283 if ( kyberPreKeys . size < LOW_KEYS_THRESHOLD ) {
12231284 this . #emitLowKeys( `removeKyberPreKeys@${ kyberPreKeys . size } ` ) ;
0 commit comments