mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2025-12-20 02:08:57 +00:00
Improve profile key validation
This commit is contained in:
@@ -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 {
|
||||
const plaintext = getZeroes(32);
|
||||
|
||||
|
||||
@@ -107,7 +107,6 @@ import {
|
||||
constantTimeEqual,
|
||||
decryptProfile,
|
||||
decryptProfileName,
|
||||
deriveAccessKey,
|
||||
hashProfileKey,
|
||||
} from '../Crypto.js';
|
||||
import { decryptAttachmentV2 } from '../AttachmentCrypto.js';
|
||||
@@ -194,7 +193,10 @@ import {
|
||||
import { imageToBlurHash } from '../util/imageToBlurHash.js';
|
||||
import { ReceiptType } from '../types/Receipt.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 { generateMessageId } from '../util/generateMessageId.js';
|
||||
import { getMessageAuthorText } from '../util/getMessageAuthorText.js';
|
||||
@@ -5035,6 +5037,19 @@ export class ConversationModel {
|
||||
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');
|
||||
|
||||
// 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
|
||||
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.
|
||||
// We want linked devices to tell us what it should be, instead of telling them to
|
||||
@@ -5132,10 +5150,11 @@ export class ConversationModel {
|
||||
return;
|
||||
}
|
||||
|
||||
const profileKeyBuffer = Bytes.fromBase64(profileKey);
|
||||
const accessKeyBuffer = deriveAccessKey(profileKeyBuffer);
|
||||
const accessKey = Bytes.toBase64(accessKeyBuffer);
|
||||
this.set({ accessKey });
|
||||
this.set({
|
||||
accessKey: Bytes.toBase64(
|
||||
deriveAccessKeyFromProfileKey(Bytes.fromBase64(profileKey))
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
deriveProfileKeyVersion(): string | undefined {
|
||||
|
||||
@@ -74,6 +74,7 @@ import {
|
||||
deriveGroupID,
|
||||
deriveGroupSecretParams,
|
||||
deriveGroupPublicParams,
|
||||
deriveAccessKeyFromProfileKey,
|
||||
} from '../../util/zkgroup.js';
|
||||
import { incrementMessageCounter } from '../../util/incrementMessageCounter.js';
|
||||
import { generateMessageId } from '../../util/generateMessageId.js';
|
||||
@@ -86,7 +87,7 @@ import { ReadStatus } from '../../messages/MessageReadStatus.js';
|
||||
import { SendStatus } from '../../messages/MessageSendState.js';
|
||||
import type { SendStateByConversationId } from '../../messages/MessageSendState.js';
|
||||
import { SeenStatus } from '../../MessageSeenStatus.js';
|
||||
import { constantTimeEqual, deriveAccessKey } from '../../Crypto.js';
|
||||
import { constantTimeEqual } from '../../Crypto.js';
|
||||
import { signalProtocolStore } from '../../SignalProtocolStore.js';
|
||||
import * as Bytes from '../../Bytes.js';
|
||||
import { BACKUP_VERSION, WALLPAPER_TO_BUBBLE_COLOR } from './constants.js';
|
||||
@@ -989,7 +990,7 @@ export class BackupImportStream extends Writable {
|
||||
? Bytes.toBase64(contact.profileKey)
|
||||
: undefined,
|
||||
accessKey: contact.profileKey
|
||||
? Bytes.toBase64(deriveAccessKey(contact.profileKey))
|
||||
? Bytes.toBase64(deriveAccessKeyFromProfileKey(contact.profileKey))
|
||||
: undefined,
|
||||
sealedSender: SEALED_SENDER.UNKNOWN,
|
||||
profileSharing: contact.profileSharing === true,
|
||||
|
||||
@@ -25,7 +25,6 @@ import {
|
||||
deriveSecrets,
|
||||
encryptDeviceName,
|
||||
decryptDeviceName,
|
||||
deriveAccessKey,
|
||||
getAccessKeyVerifier,
|
||||
verifyAccessKey,
|
||||
deriveMasterKeyFromGroupV1,
|
||||
@@ -59,6 +58,7 @@ import {
|
||||
} from '../util/AttachmentCrypto.js';
|
||||
import { getPath } from '../windows/main/attachments.js';
|
||||
import { MediaTier } from '../types/AttachmentDownload.js';
|
||||
import { deriveAccessKeyFromProfileKey } from '../util/zkgroup.js';
|
||||
|
||||
const { emptyDir } = fsExtra;
|
||||
|
||||
@@ -238,7 +238,7 @@ describe('Crypto', () => {
|
||||
describe('accessKey/profileKey', () => {
|
||||
it('verification roundtrips', () => {
|
||||
const profileKey = getRandomBytes(32);
|
||||
const accessKey = deriveAccessKey(profileKey);
|
||||
const accessKey = deriveAccessKeyFromProfileKey(profileKey);
|
||||
|
||||
const verifier = getAccessKeyVerifier(accessKey);
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@ import { isMockEnvironment } from '../environment.js';
|
||||
import { senderCertificateService } from '../services/senderCertificate.js';
|
||||
import {
|
||||
decryptDeviceName,
|
||||
deriveAccessKey,
|
||||
deriveStorageServiceKey,
|
||||
deriveMasterKey,
|
||||
encryptDeviceName,
|
||||
@@ -79,6 +78,7 @@ import { getMessageQueueTime } from '../util/getMessageQueueTime.js';
|
||||
import { canAttemptRemoteBackupDownload } from '../util/isBackupEnabled.js';
|
||||
import { signalProtocolStore } from '../SignalProtocolStore.js';
|
||||
import { itemStorage } from './Storage.js';
|
||||
import { deriveAccessKeyFromProfileKey } from '../util/zkgroup.js';
|
||||
|
||||
const { isNumber, omit, orderBy } = lodash;
|
||||
|
||||
@@ -345,7 +345,7 @@ export default class AccountManager extends EventTarget {
|
||||
const aciKeyPair = generateKeyPair();
|
||||
const pniKeyPair = generateKeyPair();
|
||||
const profileKey = getRandomBytes(PROFILE_KEY_LENGTH);
|
||||
const accessKey = deriveAccessKey(profileKey);
|
||||
const accessKey = deriveAccessKeyFromProfileKey(profileKey);
|
||||
const masterKey = getRandomBytes(MASTER_KEY_LENGTH);
|
||||
const accountEntropyPool = AccountEntropyPool.generate();
|
||||
const mediaRootBackupKey = BackupKey.generateRandom().serialize();
|
||||
|
||||
@@ -125,6 +125,13 @@ export function deriveProfileKeyVersion(
|
||||
return profileKeyVersion.toString();
|
||||
}
|
||||
|
||||
export function deriveAccessKeyFromProfileKey(
|
||||
profileKeyBytes: Uint8Array
|
||||
): Uint8Array {
|
||||
const profileKey = new ProfileKey(profileKeyBytes);
|
||||
return profileKey.deriveAccessKey();
|
||||
}
|
||||
|
||||
export function deriveGroupPublicParams(
|
||||
groupSecretParamsBuffer: Uint8Array
|
||||
): Uint8Array {
|
||||
|
||||
Reference in New Issue
Block a user