mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2025-12-24 12:19:41 +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 {
|
export function getAccessKeyVerifier(accessKey: Uint8Array): Uint8Array {
|
||||||
const plaintext = getZeroes(32);
|
const plaintext = getZeroes(32);
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user