diff --git a/ACKNOWLEDGMENTS.md b/ACKNOWLEDGMENTS.md index f39cb3ef56..14dd777075 100644 --- a/ACKNOWLEDGMENTS.md +++ b/ACKNOWLEDGMENTS.md @@ -4300,7 +4300,7 @@ For more information on this, and how to apply and follow the GNU AGPL, see ``` -## libsignal-account-keys 0.1.0, attest 0.1.0, libsignal-ffi 0.60.1, libsignal-jni 0.60.1, libsignal-jni-testing 0.60.1, libsignal-node 0.60.1, signal-neon-futures 0.1.0, signal-neon-futures-tests 0.1.0, libsignal-bridge 0.1.0, libsignal-bridge-macros 0.1.0, libsignal-bridge-testing 0.1.0, libsignal-bridge-types 0.1.0, libsignal-core 0.1.0, signal-crypto 0.1.0, device-transfer 0.1.0, libsignal-keytrans 0.0.1, signal-media 0.1.0, libsignal-message-backup 0.1.0, libsignal-message-backup-macros 0.1.0, libsignal-net 0.1.0, libsignal-net-infra 0.1.0, poksho 0.7.0, libsignal-protocol 0.1.0, libsignal-svr3 0.1.0, usernames 0.1.0, zkcredential 0.1.0, zkgroup 0.9.0 +## libsignal-account-keys 0.1.0, attest 0.1.0, libsignal-ffi 0.60.2, libsignal-jni 0.60.2, libsignal-jni-testing 0.60.2, libsignal-node 0.60.2, signal-neon-futures 0.1.0, signal-neon-futures-tests 0.1.0, libsignal-bridge 0.1.0, libsignal-bridge-macros 0.1.0, libsignal-bridge-testing 0.1.0, libsignal-bridge-types 0.1.0, libsignal-core 0.1.0, signal-crypto 0.1.0, device-transfer 0.1.0, libsignal-keytrans 0.0.1, signal-media 0.1.0, libsignal-message-backup 0.1.0, libsignal-message-backup-macros 0.1.0, libsignal-net 0.1.0, libsignal-net-infra 0.1.0, poksho 0.7.0, libsignal-protocol 0.1.0, libsignal-svr3 0.1.0, usernames 0.1.0, zkcredential 0.1.0, zkgroup 0.9.0 ``` GNU AFFERO GENERAL PUBLIC LICENSE diff --git a/package-lock.json b/package-lock.json index 3a0ab39289..ea9e3e8c05 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ "@react-aria/utils": "3.16.0", "@react-spring/web": "9.5.5", "@signalapp/better-sqlite3": "9.0.8", - "@signalapp/libsignal-client": "0.60.1", + "@signalapp/libsignal-client": "0.60.2", "@signalapp/ringrtc": "2.48.4", "@types/fabric": "4.5.3", "backbone": "1.4.0", @@ -7274,9 +7274,9 @@ } }, "node_modules/@signalapp/libsignal-client": { - "version": "0.60.1", - "resolved": "https://registry.npmjs.org/@signalapp/libsignal-client/-/libsignal-client-0.60.1.tgz", - "integrity": "sha512-euLw0lFVyqSFeA/hYwr0RHDIsFKNVPTYDMr9JT1hG4oflYdzeesgPxqsJNDMio4esQGUSKcXxtw2gjsl+Qczfg==", + "version": "0.60.2", + "resolved": "https://registry.npmjs.org/@signalapp/libsignal-client/-/libsignal-client-0.60.2.tgz", + "integrity": "sha512-tU4kNP/yCwkFntb2ahXOSQJtzdy+YifAB2yv5hw0qyKSidRHLn6bYiz4Zo2tjxLDRoBLAUxCRsQramStiqNZdA==", "hasInstallScript": true, "license": "AGPL-3.0-only", "dependencies": { diff --git a/package.json b/package.json index 40f6ed001c..18adc9f27b 100644 --- a/package.json +++ b/package.json @@ -108,7 +108,7 @@ "@react-aria/utils": "3.16.0", "@react-spring/web": "9.5.5", "@signalapp/better-sqlite3": "9.0.8", - "@signalapp/libsignal-client": "0.60.1", + "@signalapp/libsignal-client": "0.60.2", "@signalapp/ringrtc": "2.48.4", "@types/fabric": "4.5.3", "backbone": "1.4.0", diff --git a/ts/Crypto.ts b/ts/Crypto.ts index ce4ce2dae7..5654c009ea 100644 --- a/ts/Crypto.ts +++ b/ts/Crypto.ts @@ -155,52 +155,6 @@ export function decryptDeviceName( return Bytes.toString(plaintext); } -const BACKUP_KEY_LEN = 32; -const BACKUP_MEDIA_THUMBNAIL_ENCRYPT_INFO = - '20241030_SIGNAL_BACKUPS_ENCRYPT_THUMBNAIL:'; -const BACKUP_MEDIA_AES_KEY_LEN = 32; -const BACKUP_MEDIA_MAC_KEY_LEN = 32; -const BACKUP_MEDIA_IV_LEN = 16; - -export type BackupMediaKeyMaterialType = Readonly<{ - aesKey: Uint8Array; - macKey: Uint8Array; -}>; - -export function deriveBackupMediaThumbnailInnerEncryptionKeyMaterial( - mediaRootKey: Uint8Array, - mediaId: Uint8Array -): BackupMediaKeyMaterialType { - if (mediaRootKey.byteLength !== BACKUP_KEY_LEN) { - throw new Error( - 'deriveBackupMediaThumbnailKeyMaterial: invalid backup key length' - ); - } - - if (!mediaId.length) { - throw new Error('deriveBackupMediaThumbnailKeyMaterial: mediaId missing'); - } - - const hkdf = HKDF.new(3); - const material = hkdf.deriveSecrets( - BACKUP_MEDIA_MAC_KEY_LEN + BACKUP_MEDIA_AES_KEY_LEN + BACKUP_MEDIA_IV_LEN, - Buffer.from(mediaRootKey), - Buffer.concat([ - Buffer.from(BACKUP_MEDIA_THUMBNAIL_ENCRYPT_INFO), - Buffer.from(mediaId), - ]), - Buffer.alloc(0) - ); - - return { - aesKey: material.subarray(0, BACKUP_MEDIA_AES_KEY_LEN), - macKey: material.subarray( - BACKUP_MEDIA_AES_KEY_LEN, - BACKUP_MEDIA_AES_KEY_LEN + BACKUP_MEDIA_MAC_KEY_LEN - ), - }; -} - export function deriveMasterKey(accountEntropyPool: string): Uint8Array { return AccountEntropyPool.deriveSvrKey(accountEntropyPool); } diff --git a/ts/jobs/AttachmentBackupManager.ts b/ts/jobs/AttachmentBackupManager.ts index 5a4244900c..bf576846f6 100644 --- a/ts/jobs/AttachmentBackupManager.ts +++ b/ts/jobs/AttachmentBackupManager.ts @@ -25,10 +25,10 @@ import { decryptAttachmentV2ToSink, ReencryptedDigestMismatchError, } from '../AttachmentCrypto'; -import { deriveBackupMediaThumbnailInnerEncryptionKeyMaterial } from '../Crypto'; import { getBackupMediaRootKey, deriveBackupMediaKeyMaterial, + deriveBackupThumbnailTransitKeyMaterial, } from '../services/backups/crypto'; import { type AttachmentBackupJobType, @@ -432,11 +432,10 @@ async function backupThumbnailAttachment( return; } - const { aesKey, macKey } = - deriveBackupMediaThumbnailInnerEncryptionKeyMaterial( - getBackupMediaRootKey().serialize(), - mediaId.bytes - ); + const { aesKey, macKey } = deriveBackupThumbnailTransitKeyMaterial( + getBackupMediaRootKey(), + mediaId.bytes + ); log.info(`${logId}: uploading thumbnail to transit tier`); const uploadResult = await uploadThumbnailToTransitTier({ diff --git a/ts/services/backups/crypto.ts b/ts/services/backups/crypto.ts index d23ccab67f..02ccfd059d 100644 --- a/ts/services/backups/crypto.ts +++ b/ts/services/backups/crypto.ts @@ -104,3 +104,24 @@ export function deriveBackupMediaKeyMaterial( ), }; } + +export function deriveBackupThumbnailTransitKeyMaterial( + mediaRootKey: BackupKey, + mediaId: Uint8Array +): BackupMediaKeyMaterialType { + if (!mediaId.length) { + throw new Error('deriveBackupThumbnailTransitKeyMaterial: mediaId missing'); + } + + const material = mediaRootKey.deriveThumbnailTransitEncryptionKey( + Buffer.from(mediaId) + ); + + return { + macKey: material.subarray(0, BACKUP_MEDIA_MAC_KEY_LEN), + aesKey: material.subarray( + BACKUP_MEDIA_MAC_KEY_LEN, + BACKUP_MEDIA_MAC_KEY_LEN + BACKUP_MEDIA_AES_KEY_LEN + ), + }; +}