diff --git a/ts/Crypto.ts b/ts/Crypto.ts index 1e1c1d129fa..3894cce91c6 100644 --- a/ts/Crypto.ts +++ b/ts/Crypto.ts @@ -3,7 +3,7 @@ import { Buffer } from 'buffer'; import Long from 'long'; -import { HKDF } from '@signalapp/libsignal-client'; +import { Aci, HKDF } from '@signalapp/libsignal-client'; import * as Bytes from './Bytes'; import { Crypto } from './context/Crypto'; @@ -13,6 +13,9 @@ import { ProfileDecryptError } from './types/errors'; import { getBytesSubarray } from './util/uuidToBytes'; import { logPadSize } from './util/logPadding'; import { Environment, getEnvironment } from './environment'; +import { toWebSafeBase64 } from './util/webSafeBase64'; + +import type { AciString } from './types/ServiceId'; export { HashType, CipherType }; @@ -70,6 +73,26 @@ export function deriveMasterKeyFromGroupV1(groupV1Id: Uint8Array): Uint8Array { return part1; } +export function hashProfileKey( + profileKey: string | undefined, + aci: AciString +): string { + if (!profileKey) { + return 'none'; + } + + const profileKeyBytes = Bytes.fromBase64(profileKey); + const aciBytes = Aci.parseFromServiceIdString(aci).getRawUuidBytes(); + const hashBytes = hmacSha256( + profileKeyBytes, + Bytes.concatenate([Bytes.fromString('profileKeyHash'), aciBytes]) + ); + + const webSafe = toWebSafeBase64(Bytes.toBase64(hashBytes)); + + return webSafe.slice(-3); +} + export function computeHash(data: Uint8Array): string { return Bytes.toBase64(hash(HashType.size512, data)); } diff --git a/ts/models/conversations.ts b/ts/models/conversations.ts index f06a7eaee50..38b0c114375 100644 --- a/ts/models/conversations.ts +++ b/ts/models/conversations.ts @@ -85,6 +85,7 @@ import { decryptProfile, decryptProfileName, deriveAccessKey, + hashProfileKey, } from '../Crypto'; import { decryptAttachmentV2 } from '../AttachmentCrypto'; import * as Bytes from '../Bytes'; @@ -4918,7 +4919,6 @@ export class ConversationModel extends window.Backbone reason, }: { viaStorageServiceSync?: boolean; reason: string } ): Promise { - const logId = `setProfileKey(${this.idForLogging()}/${reason})`; const oldProfileKey = this.get('profileKey'); // profileKey is a string so we can compare it directly @@ -4926,6 +4926,11 @@ export class ConversationModel extends window.Backbone return false; } + const serviceId = this.get('serviceId'); + const aci = isAciString(serviceId) ? serviceId : undefined; + const profileKeyHash = aci ? hashProfileKey(profileKey, aci) : 'no-aci'; + const logId = `setProfileKey(${this.idForLogging()}/${profileKeyHash}/${reason})`; + log.info(`${logId}: Profile key changed. Setting sealedSender to UNKNOWN`); this.set({ profileKeyCredential: null,