Use UUID-only sender certificate when applicable

This commit is contained in:
Evan Hahn 2021-04-05 15:38:36 -05:00 committed by Josh Perez
parent cfd77bf968
commit 18ccda83ba
3 changed files with 70 additions and 25 deletions

View file

@ -17,7 +17,12 @@ import {
} from '../components/conversation/conversation-details/PendingInvites'; } from '../components/conversation/conversation-details/PendingInvites';
import { GroupV2Membership } from '../components/conversation/conversation-details/ConversationDetailsMembershipList'; import { GroupV2Membership } from '../components/conversation/conversation-details/ConversationDetailsMembershipList';
import { CallMode, CallHistoryDetailsType } from '../types/Calling'; import { CallMode, CallHistoryDetailsType } from '../types/Calling';
import { CallbackResultType, GroupV2InfoType } from '../textsecure/SendMessage'; import {
CallbackResultType,
GroupV2InfoType,
SendMetadataType,
SendOptionsType,
} from '../textsecure/SendMessage';
import { import {
ConversationType, ConversationType,
ConversationTypeType, ConversationTypeType,
@ -26,6 +31,7 @@ import { ColorType } from '../types/Colors';
import { MessageModel } from './messages'; import { MessageModel } from './messages';
import { isMuted } from '../util/isMuted'; import { isMuted } from '../util/isMuted';
import { isConversationUnregistered } from '../util/isConversationUnregistered'; import { isConversationUnregistered } from '../util/isConversationUnregistered';
import { assert } from '../util/assert';
import { missingCaseError } from '../util/missingCaseError'; import { missingCaseError } from '../util/missingCaseError';
import { sniffImageMimeType } from '../util/sniffImageMimeType'; import { sniffImageMimeType } from '../util/sniffImageMimeType';
import { MIMEType, IMAGE_WEBP } from '../types/MIME'; import { MIMEType, IMAGE_WEBP } from '../types/MIME';
@ -44,6 +50,11 @@ import { BodyRangesType } from '../types/Util';
import { getTextWithMentions } from '../util'; import { getTextWithMentions } from '../util';
import { migrateColor } from '../util/migrateColor'; import { migrateColor } from '../util/migrateColor';
import { isNotNil } from '../util/isNotNil'; import { isNotNil } from '../util/isNotNil';
import {
PhoneNumberSharingMode,
parsePhoneNumberSharingMode,
} from '../util/phoneNumberSharingMode';
import { SerializedCertificateType } from '../metadata/SecretSessionCipher';
/* eslint-disable more/no-then */ /* eslint-disable more/no-then */
window.Whisper = window.Whisper || {}; window.Whisper = window.Whisper || {};
@ -3544,19 +3555,17 @@ export class ConversationModel extends window.Backbone.Model<
); );
} }
getSendOptions(options = {}): WhatIsThis { getSendOptions(options = {}): SendOptionsType {
const senderCertificate = window.storage.get('senderCertificate');
const sendMetadata = this.getSendMetadata(options); const sendMetadata = this.getSendMetadata(options);
return { return {
senderCertificate,
sendMetadata, sendMetadata,
}; };
} }
getSendMetadata( getSendMetadata(
options: { syncMessage?: string; disableMeCheck?: boolean } = {} options: { syncMessage?: string; disableMeCheck?: boolean } = {}
): WhatIsThis | null { ): SendMetadataType | undefined {
const { syncMessage, disableMeCheck } = options; const { syncMessage, disableMeCheck } = options;
// START: this code has an Expiration date of ~2018/11/21 // START: this code has an Expiration date of ~2018/11/21
@ -3566,7 +3575,7 @@ export class ConversationModel extends window.Backbone.Model<
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const me = window.ConversationController.get(myId)!; const me = window.ConversationController.get(myId)!;
if (!disableMeCheck && me.get('sealedSender') === SEALED_SENDER.DISABLED) { if (!disableMeCheck && me.get('sealedSender') === SEALED_SENDER.DISABLED) {
return null; return undefined;
} }
// END // END
@ -3583,16 +3592,19 @@ export class ConversationModel extends window.Backbone.Model<
// We never send sync messages as sealed sender // We never send sync messages as sealed sender
if (syncMessage && this.isMe()) { if (syncMessage && this.isMe()) {
return null; return undefined;
} }
const e164 = this.get('e164'); const e164 = this.get('e164');
const uuid = this.get('uuid'); const uuid = this.get('uuid');
const senderCertificate = this.getSenderCertificateForDirectConversation();
// If we've never fetched user's profile, we default to what we have // If we've never fetched user's profile, we default to what we have
if (sealedSender === SEALED_SENDER.UNKNOWN) { if (sealedSender === SEALED_SENDER.UNKNOWN) {
const info = { const info = {
accessKey: accessKey || arrayBufferToBase64(getRandomBytes(16)), accessKey: accessKey || arrayBufferToBase64(getRandomBytes(16)),
senderCertificate,
}; };
return { return {
...(e164 ? { [e164]: info } : {}), ...(e164 ? { [e164]: info } : {}),
@ -3601,7 +3613,7 @@ export class ConversationModel extends window.Backbone.Model<
} }
if (sealedSender === SEALED_SENDER.DISABLED) { if (sealedSender === SEALED_SENDER.DISABLED) {
return null; return undefined;
} }
const info = { const info = {
@ -3609,6 +3621,7 @@ export class ConversationModel extends window.Backbone.Model<
accessKey && sealedSender === SEALED_SENDER.ENABLED accessKey && sealedSender === SEALED_SENDER.ENABLED
? accessKey ? accessKey
: arrayBufferToBase64(getRandomBytes(16)), : arrayBufferToBase64(getRandomBytes(16)),
senderCertificate,
}; };
return { return {
@ -3617,6 +3630,48 @@ export class ConversationModel extends window.Backbone.Model<
}; };
} }
private getSenderCertificateForDirectConversation():
| undefined
| SerializedCertificateType {
if (!this.isPrivate()) {
throw new Error(
'getSenderCertificateForDirectConversation should only be called for direct conversations'
);
}
const phoneNumberSharingMode = parsePhoneNumberSharingMode(
window.storage.get('phoneNumberSharingMode')
);
let storageKey: 'senderCertificate' | 'senderCertificateNoE164';
switch (phoneNumberSharingMode) {
case PhoneNumberSharingMode.Everybody:
storageKey = 'senderCertificate';
break;
case PhoneNumberSharingMode.ContactsOnly: {
const isInSystemContacts = Boolean(this.get('name'));
storageKey = isInSystemContacts
? 'senderCertificate'
: 'senderCertificateNoE164';
break;
}
case PhoneNumberSharingMode.Nobody:
storageKey = 'senderCertificateNoE164';
break;
default:
throw missingCaseError(phoneNumberSharingMode);
}
const result = window.storage.get<SerializedCertificateType>(storageKey);
assert(
result,
`getSenderCertificateForDirectConversation: couldn't find a certificate stored in ${JSON.stringify(
storageKey
)}. Returning undefined`
);
return result;
}
// Is this someone who is a contact, or are we sharing our profile with them? // Is this someone who is a contact, or are we sharing our profile with them?
// Or is the person who added us to this group a contact or are we sharing profile // Or is the person who added us to this group a contact or are we sharing profile
// with them? // with them?

View file

@ -26,10 +26,7 @@ import {
UnregisteredUserError, UnregisteredUserError,
} from './Errors'; } from './Errors';
import { isValidNumber } from '../types/PhoneNumber'; import { isValidNumber } from '../types/PhoneNumber';
import { import { SecretSessionCipher } from '../metadata/SecretSessionCipher';
SecretSessionCipher,
SerializedCertificateType,
} from '../metadata/SecretSessionCipher';
type OutgoingMessageOptionsType = SendOptionsType & { type OutgoingMessageOptionsType = SendOptionsType & {
online?: boolean; online?: boolean;
@ -62,8 +59,6 @@ export default class OutgoingMessage {
sendMetadata?: SendMetadataType; sendMetadata?: SendMetadataType;
senderCertificate?: SerializedCertificateType;
online?: boolean; online?: boolean;
constructor( constructor(
@ -96,9 +91,8 @@ export default class OutgoingMessage {
this.failoverIdentifiers = []; this.failoverIdentifiers = [];
this.unidentifiedDeliveries = []; this.unidentifiedDeliveries = [];
const { sendMetadata, senderCertificate, online } = options; const { sendMetadata, online } = options;
this.sendMetadata = sendMetadata; this.sendMetadata = sendMetadata;
this.senderCertificate = senderCertificate;
this.online = online; this.online = online;
} }
@ -344,12 +338,8 @@ export default class OutgoingMessage {
} = {}; } = {};
const plaintext = this.getPlaintext(); const plaintext = this.getPlaintext();
const { sendMetadata, senderCertificate } = this; const { sendMetadata } = this;
const info = const { accessKey, senderCertificate } = sendMetadata?.[identifier] || {};
sendMetadata && sendMetadata[identifier]
? sendMetadata[identifier]
: { accessKey: undefined };
const { accessKey } = info;
if (accessKey && !senderCertificate) { if (accessKey && !senderCertificate) {
window.log.warn( window.log.warn(
@ -442,8 +432,8 @@ export default class OutgoingMessage {
} }
// This ensures that we don't hit this codepath the next time through // This ensures that we don't hit this codepath the next time through
if (info) { if (sendMetadata) {
info.accessKey = undefined; delete sendMetadata[identifier];
} }
return this.doSendMessage(identifier, deviceIds, recurse); return this.doSendMessage(identifier, deviceIds, recurse);

View file

@ -63,11 +63,11 @@ function stringToArrayBuffer(str: string): ArrayBuffer {
export type SendMetadataType = { export type SendMetadataType = {
[identifier: string]: { [identifier: string]: {
accessKey: string; accessKey: string;
senderCertificate?: SerializedCertificateType;
}; };
}; };
export type SendOptionsType = { export type SendOptionsType = {
senderCertificate?: SerializedCertificateType;
sendMetadata?: SendMetadataType; sendMetadata?: SendMetadataType;
online?: boolean; online?: boolean;
}; };