Reorder getSendTarget logic for clarity

This commit is contained in:
Fedor Indutny 2025-05-20 12:40:16 -07:00 committed by GitHub
parent c10d59458f
commit 49161b7e17
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 78 additions and 59 deletions

View file

@ -47,6 +47,7 @@ import type {
CustomColorType,
CustomColorDataType,
} from '../../types/Colors';
import { SEALED_SENDER } from '../../types/SealedSender';
import type {
ConversationAttributesType,
CustomError,
@ -81,7 +82,7 @@ import { ReadStatus } from '../../messages/MessageReadStatus';
import { SendStatus } from '../../messages/MessageSendState';
import type { SendStateByConversationId } from '../../messages/MessageSendState';
import { SeenStatus } from '../../MessageSeenStatus';
import { constantTimeEqual } from '../../Crypto';
import { constantTimeEqual, deriveAccessKey } from '../../Crypto';
import * as Bytes from '../../Bytes';
import { BACKUP_VERSION, WALLPAPER_TO_BUBBLE_COLOR } from './constants';
import { UnsupportedBackupVersion } from './errors';
@ -945,6 +946,10 @@ export class BackupImportStream extends Writable {
profileKey: contact.profileKey
? Bytes.toBase64(contact.profileKey)
: undefined,
accessKey: contact.profileKey
? Bytes.toBase64(deriveAccessKey(contact.profileKey))
: undefined,
sealedSender: SEALED_SENDER.UNKNOWN,
profileSharing: contact.profileSharing === true,
profileName: dropNull(contact.profileGivenName),
profileFamilyName: dropNull(contact.profileFamilyName),

View file

@ -36,6 +36,7 @@ import { createProxyAgent } from '../util/createProxyAgent';
import type { ProxyAgent } from '../util/createProxyAgent';
import type { FetchFunctionType } from '../util/uploads/tusProtocol';
import { VerificationTransport } from '../types/VerificationTransport';
import { ZERO_ACCESS_KEY } from '../types/SealedSender';
import { toLogFormat } from '../types/errors';
import { isPackIdValid, redactPackId } from '../types/Stickers';
import type {
@ -362,7 +363,20 @@ async function _promiseAjax<Type extends ResponseType, OutputShape>(
const logType = socketManager ? '(WS)' : '(REST)';
const redactedURL = options.redactUrl ? options.redactUrl(url) : url;
const unauthLabel = options.unauthenticated ? ' (unauth)' : '';
const { accessKey, basicAuth, groupSendToken, unauthenticated } = options;
let unauthLabel = '';
if (options.unauthenticated) {
if (groupSendToken != null) {
unauthLabel = ' (unauth+gse)';
} else if (accessKey === ZERO_ACCESS_KEY) {
unauthLabel = ' (unauth+zero-key)';
} else if (accessKey != null) {
unauthLabel = ' (unauth+key)';
} else {
unauthLabel = ' (unauth)';
}
}
const logId = `${options.type} ${logType} ${redactedURL}${unauthLabel}`;
log.info(logId);
@ -375,7 +389,6 @@ async function _promiseAjax<Type extends ResponseType, OutputShape>(
fetchOptions.headers['Content-Length'] = contentLength.toString();
}
const { accessKey, basicAuth, groupSendToken, unauthenticated } = options;
if (basicAuth) {
fetchOptions.headers.Authorization = `Basic ${basicAuth}`;
} else if (unauthenticated) {

View file

@ -1,6 +1,8 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
export const ZERO_ACCESS_KEY = 'AAAAAAAAAAAAAAAAAAAAAA==';
export enum SEALED_SENDER {
UNKNOWN = 0,
ENABLED = 1,

View file

@ -7,23 +7,16 @@ import type {
SendMetadataType,
SendOptionsType,
} from '../textsecure/SendMessage';
import * as Bytes from '../Bytes';
import { getRandomBytes, getZeroes } from '../Crypto';
import { getConversationMembers } from './getConversationMembers';
import { isDirectConversation, isMe } from './whatTypeOfConversation';
import { senderCertificateService } from '../services/senderCertificate';
import { shouldSharePhoneNumberWith } from './phoneNumberSharingMode';
import type { SerializedCertificateType } from '../textsecure/OutgoingMessage';
import { SenderCertificateMode } from '../textsecure/OutgoingMessage';
import { ZERO_ACCESS_KEY, SEALED_SENDER } from '../types/SealedSender';
import { isNotNil } from './isNotNil';
import { maybeCreateGroupSendEndorsementState } from './groupSendEndorsements';
const SEALED_SENDER = {
UNKNOWN: 0,
ENABLED: 1,
DISABLED: 2,
UNRESTRICTED: 3,
};
import { missingCaseError } from './missingCaseError';
export async function getSendOptionsForRecipients(
recipients: ReadonlyArray<string>,
@ -94,61 +87,69 @@ export async function getSendOptions(
};
}
const { accessKey, sealedSender } = conversationAttrs;
const { accessKey } = conversationAttrs;
const { e164, serviceId } = conversationAttrs;
let sealedSender = conversationAttrs.sealedSender as
| SEALED_SENDER
| undefined;
const senderCertificate =
await getSenderCertificateForDirectConversation(conversationAttrs);
let identifierData: SendIdentifierData | null = null;
// If we've never fetched user's profile, we default to what we have
if (sealedSender === SEALED_SENDER.UNKNOWN || story) {
identifierData = {
accessKey:
accessKey ||
(story
? Bytes.toBase64(getZeroes(16))
: Bytes.toBase64(getRandomBytes(16))),
senderCertificate,
groupSendToken: null,
};
if (story) {
// Always send story using zero access key
sealedSender = SEALED_SENDER.UNRESTRICTED;
}
if (sealedSender === SEALED_SENDER.DISABLED) {
if (serviceId != null && groupId != null) {
const { state: groupSendEndorsementState, didRefreshGroupState } =
await maybeCreateGroupSendEndorsementState(
groupId,
alreadyRefreshedGroupState
);
switch (sealedSender) {
case SEALED_SENDER.DISABLED:
// Try to get GSE token
if (serviceId != null && groupId != null) {
const { state: groupSendEndorsementState, didRefreshGroupState } =
await maybeCreateGroupSendEndorsementState(
groupId,
alreadyRefreshedGroupState
);
if (
groupSendEndorsementState != null &&
groupSendEndorsementState.hasMember(serviceId)
) {
const token = groupSendEndorsementState.buildToken(
new Set([serviceId])
);
if (token != null) {
identifierData = {
accessKey: null,
senderCertificate,
groupSendToken: token,
};
if (
groupSendEndorsementState != null &&
groupSendEndorsementState.hasMember(serviceId)
) {
const token = groupSendEndorsementState.buildToken(
new Set([serviceId])
);
if (token != null) {
identifierData = {
accessKey: null,
senderCertificate,
groupSendToken: token,
};
}
} else if (didRefreshGroupState && !alreadyRefreshedGroupState) {
return getSendOptions(conversationAttrs, options, true);
}
} else if (didRefreshGroupState && !alreadyRefreshedGroupState) {
return getSendOptions(conversationAttrs, options, true);
}
}
} else {
identifierData = {
accessKey:
accessKey && sealedSender === SEALED_SENDER.ENABLED
? accessKey
: Bytes.toBase64(getRandomBytes(16)),
senderCertificate,
groupSendToken: null,
};
break;
case SEALED_SENDER.UNRESTRICTED:
identifierData = {
accessKey: ZERO_ACCESS_KEY,
senderCertificate,
groupSendToken: null,
};
break;
case SEALED_SENDER.ENABLED:
case SEALED_SENDER.UNKNOWN:
case undefined:
identifierData = {
accessKey: accessKey || ZERO_ACCESS_KEY,
senderCertificate,
groupSendToken: null,
};
break;
default:
throw missingCaseError(sealedSender);
}
let sendMetadata: SendMetadataType = {};

View file

@ -13,7 +13,6 @@ import {
SenderCertificate,
UnidentifiedSenderMessageContent,
} from '@signalapp/libsignal-client';
import * as Bytes from '../Bytes';
import { senderCertificateService } from '../services/senderCertificate';
import type { SendLogCallbackType } from '../textsecure/OutgoingMessage';
import {
@ -53,7 +52,7 @@ import type {
} from '../model-types.d';
import type { SendTypesType } from './handleMessageSend';
import { handleMessageSend, shouldSaveProto } from './handleMessageSend';
import { SEALED_SENDER } from '../types/SealedSender';
import { SEALED_SENDER, ZERO_ACCESS_KEY } from '../types/SealedSender';
import { parseIntOrThrow } from './parseIntOrThrow';
import {
multiRecipient200ResponseSchema,
@ -87,7 +86,6 @@ const DAY = 24 * HOUR;
const MAX_RECURSION = 10;
const ACCESS_KEY_LENGTH = 16;
const ZERO_ACCESS_KEY = Bytes.toBase64(new Uint8Array(ACCESS_KEY_LENGTH));
// Public API: