Improve profile key validation

This commit is contained in:
trevor-signal
2025-10-16 12:07:11 -04:00
committed by GitHub
parent 69aa0b3e30
commit df27e4c4e8
6 changed files with 40 additions and 21 deletions

View File

@@ -204,14 +204,6 @@ export function deriveStorageItemKey({
); );
} }
export function deriveAccessKey(profileKey: Uint8Array): Uint8Array {
const iv = getZeroes(12);
const plaintext = getZeroes(16);
const accessKey = encryptAesGcm(profileKey, iv, plaintext);
return getFirstBytes(accessKey, 16);
}
export function getAccessKeyVerifier(accessKey: Uint8Array): Uint8Array { export function getAccessKeyVerifier(accessKey: Uint8Array): Uint8Array {
const plaintext = getZeroes(32); const plaintext = getZeroes(32);

View File

@@ -107,7 +107,6 @@ import {
constantTimeEqual, constantTimeEqual,
decryptProfile, decryptProfile,
decryptProfileName, decryptProfileName,
deriveAccessKey,
hashProfileKey, hashProfileKey,
} from '../Crypto.js'; } from '../Crypto.js';
import { decryptAttachmentV2 } from '../AttachmentCrypto.js'; import { decryptAttachmentV2 } from '../AttachmentCrypto.js';
@@ -194,7 +193,10 @@ import {
import { imageToBlurHash } from '../util/imageToBlurHash.js'; import { imageToBlurHash } from '../util/imageToBlurHash.js';
import { ReceiptType } from '../types/Receipt.js'; import { ReceiptType } from '../types/Receipt.js';
import { getQuoteAttachment } from '../util/makeQuote.js'; import { getQuoteAttachment } from '../util/makeQuote.js';
import { deriveProfileKeyVersion } from '../util/zkgroup.js'; import {
deriveAccessKeyFromProfileKey,
deriveProfileKeyVersion,
} from '../util/zkgroup.js';
import { incrementMessageCounter } from '../util/incrementMessageCounter.js'; import { incrementMessageCounter } from '../util/incrementMessageCounter.js';
import { generateMessageId } from '../util/generateMessageId.js'; import { generateMessageId } from '../util/generateMessageId.js';
import { getMessageAuthorText } from '../util/getMessageAuthorText.js'; import { getMessageAuthorText } from '../util/getMessageAuthorText.js';
@@ -5035,6 +5037,19 @@ export class ConversationModel {
return false; return false;
} }
let derivedAccessKey: Uint8Array;
try {
derivedAccessKey = deriveAccessKeyFromProfileKey(
Bytes.fromBase64(profileKey)
);
} catch (error) {
log.warn(
'setProfileKey: refusing to set invalid profile key',
Errors.toLogFormat(error)
);
return false;
}
const oldProfileKey = this.get('profileKey'); const oldProfileKey = this.get('profileKey');
// profileKey is a string so we can compare it directly // profileKey is a string so we can compare it directly
@@ -5078,7 +5093,10 @@ export class ConversationModel {
} }
// Don't trigger immediate profile fetches when syncing to remote storage // Don't trigger immediate profile fetches when syncing to remote storage
this.set({ profileKey }, { noTrigger: viaStorageServiceSync }); this.set(
{ profileKey, accessKey: Bytes.toBase64(derivedAccessKey) },
{ noTrigger: viaStorageServiceSync }
);
// If our profile key was cleared above, we don't tell our linked devices about it. // If our profile key was cleared above, we don't tell our linked devices about it.
// We want linked devices to tell us what it should be, instead of telling them to // We want linked devices to tell us what it should be, instead of telling them to
@@ -5132,10 +5150,11 @@ export class ConversationModel {
return; return;
} }
const profileKeyBuffer = Bytes.fromBase64(profileKey); this.set({
const accessKeyBuffer = deriveAccessKey(profileKeyBuffer); accessKey: Bytes.toBase64(
const accessKey = Bytes.toBase64(accessKeyBuffer); deriveAccessKeyFromProfileKey(Bytes.fromBase64(profileKey))
this.set({ accessKey }); ),
});
} }
deriveProfileKeyVersion(): string | undefined { deriveProfileKeyVersion(): string | undefined {

View File

@@ -74,6 +74,7 @@ import {
deriveGroupID, deriveGroupID,
deriveGroupSecretParams, deriveGroupSecretParams,
deriveGroupPublicParams, deriveGroupPublicParams,
deriveAccessKeyFromProfileKey,
} from '../../util/zkgroup.js'; } from '../../util/zkgroup.js';
import { incrementMessageCounter } from '../../util/incrementMessageCounter.js'; import { incrementMessageCounter } from '../../util/incrementMessageCounter.js';
import { generateMessageId } from '../../util/generateMessageId.js'; import { generateMessageId } from '../../util/generateMessageId.js';
@@ -86,7 +87,7 @@ import { ReadStatus } from '../../messages/MessageReadStatus.js';
import { SendStatus } from '../../messages/MessageSendState.js'; import { SendStatus } from '../../messages/MessageSendState.js';
import type { SendStateByConversationId } from '../../messages/MessageSendState.js'; import type { SendStateByConversationId } from '../../messages/MessageSendState.js';
import { SeenStatus } from '../../MessageSeenStatus.js'; import { SeenStatus } from '../../MessageSeenStatus.js';
import { constantTimeEqual, deriveAccessKey } from '../../Crypto.js'; import { constantTimeEqual } from '../../Crypto.js';
import { signalProtocolStore } from '../../SignalProtocolStore.js'; import { signalProtocolStore } from '../../SignalProtocolStore.js';
import * as Bytes from '../../Bytes.js'; import * as Bytes from '../../Bytes.js';
import { BACKUP_VERSION, WALLPAPER_TO_BUBBLE_COLOR } from './constants.js'; import { BACKUP_VERSION, WALLPAPER_TO_BUBBLE_COLOR } from './constants.js';
@@ -989,7 +990,7 @@ export class BackupImportStream extends Writable {
? Bytes.toBase64(contact.profileKey) ? Bytes.toBase64(contact.profileKey)
: undefined, : undefined,
accessKey: contact.profileKey accessKey: contact.profileKey
? Bytes.toBase64(deriveAccessKey(contact.profileKey)) ? Bytes.toBase64(deriveAccessKeyFromProfileKey(contact.profileKey))
: undefined, : undefined,
sealedSender: SEALED_SENDER.UNKNOWN, sealedSender: SEALED_SENDER.UNKNOWN,
profileSharing: contact.profileSharing === true, profileSharing: contact.profileSharing === true,

View File

@@ -25,7 +25,6 @@ import {
deriveSecrets, deriveSecrets,
encryptDeviceName, encryptDeviceName,
decryptDeviceName, decryptDeviceName,
deriveAccessKey,
getAccessKeyVerifier, getAccessKeyVerifier,
verifyAccessKey, verifyAccessKey,
deriveMasterKeyFromGroupV1, deriveMasterKeyFromGroupV1,
@@ -59,6 +58,7 @@ import {
} from '../util/AttachmentCrypto.js'; } from '../util/AttachmentCrypto.js';
import { getPath } from '../windows/main/attachments.js'; import { getPath } from '../windows/main/attachments.js';
import { MediaTier } from '../types/AttachmentDownload.js'; import { MediaTier } from '../types/AttachmentDownload.js';
import { deriveAccessKeyFromProfileKey } from '../util/zkgroup.js';
const { emptyDir } = fsExtra; const { emptyDir } = fsExtra;
@@ -238,7 +238,7 @@ describe('Crypto', () => {
describe('accessKey/profileKey', () => { describe('accessKey/profileKey', () => {
it('verification roundtrips', () => { it('verification roundtrips', () => {
const profileKey = getRandomBytes(32); const profileKey = getRandomBytes(32);
const accessKey = deriveAccessKey(profileKey); const accessKey = deriveAccessKeyFromProfileKey(profileKey);
const verifier = getAccessKeyVerifier(accessKey); const verifier = getAccessKeyVerifier(accessKey);

View File

@@ -38,7 +38,6 @@ import { isMockEnvironment } from '../environment.js';
import { senderCertificateService } from '../services/senderCertificate.js'; import { senderCertificateService } from '../services/senderCertificate.js';
import { import {
decryptDeviceName, decryptDeviceName,
deriveAccessKey,
deriveStorageServiceKey, deriveStorageServiceKey,
deriveMasterKey, deriveMasterKey,
encryptDeviceName, encryptDeviceName,
@@ -79,6 +78,7 @@ import { getMessageQueueTime } from '../util/getMessageQueueTime.js';
import { canAttemptRemoteBackupDownload } from '../util/isBackupEnabled.js'; import { canAttemptRemoteBackupDownload } from '../util/isBackupEnabled.js';
import { signalProtocolStore } from '../SignalProtocolStore.js'; import { signalProtocolStore } from '../SignalProtocolStore.js';
import { itemStorage } from './Storage.js'; import { itemStorage } from './Storage.js';
import { deriveAccessKeyFromProfileKey } from '../util/zkgroup.js';
const { isNumber, omit, orderBy } = lodash; const { isNumber, omit, orderBy } = lodash;
@@ -345,7 +345,7 @@ export default class AccountManager extends EventTarget {
const aciKeyPair = generateKeyPair(); const aciKeyPair = generateKeyPair();
const pniKeyPair = generateKeyPair(); const pniKeyPair = generateKeyPair();
const profileKey = getRandomBytes(PROFILE_KEY_LENGTH); const profileKey = getRandomBytes(PROFILE_KEY_LENGTH);
const accessKey = deriveAccessKey(profileKey); const accessKey = deriveAccessKeyFromProfileKey(profileKey);
const masterKey = getRandomBytes(MASTER_KEY_LENGTH); const masterKey = getRandomBytes(MASTER_KEY_LENGTH);
const accountEntropyPool = AccountEntropyPool.generate(); const accountEntropyPool = AccountEntropyPool.generate();
const mediaRootBackupKey = BackupKey.generateRandom().serialize(); const mediaRootBackupKey = BackupKey.generateRandom().serialize();

View File

@@ -125,6 +125,13 @@ export function deriveProfileKeyVersion(
return profileKeyVersion.toString(); return profileKeyVersion.toString();
} }
export function deriveAccessKeyFromProfileKey(
profileKeyBytes: Uint8Array
): Uint8Array {
const profileKey = new ProfileKey(profileKeyBytes);
return profileKey.deriveAccessKey();
}
export function deriveGroupPublicParams( export function deriveGroupPublicParams(
groupSecretParamsBuffer: Uint8Array groupSecretParamsBuffer: Uint8Array
): Uint8Array { ): Uint8Array {