mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2026-04-28 12:23:31 +01:00
Support endorsements for group 1:1 sends
Co-authored-by: trevor-signal <131492920+trevor-signal@users.noreply.github.com>
This commit is contained in:
@@ -42,6 +42,7 @@ import { Sessions, IdentityKeys } from '../LibSignalStores';
|
||||
import { getKeysForServiceId } from './getKeysForServiceId';
|
||||
import { SignalService as Proto } from '../protobuf';
|
||||
import * as log from '../logging/log';
|
||||
import type { GroupSendToken } from '../types/GroupSendEndorsements';
|
||||
|
||||
export const enum SenderCertificateMode {
|
||||
WithE164,
|
||||
@@ -306,13 +307,20 @@ export default class OutgoingMessage {
|
||||
serviceId: ServiceIdString,
|
||||
jsonData: ReadonlyArray<MessageType>,
|
||||
timestamp: number,
|
||||
{ accessKey }: { accessKey?: string } = {}
|
||||
{
|
||||
accessKey,
|
||||
groupSendToken,
|
||||
}: {
|
||||
accessKey: string | null;
|
||||
groupSendToken: GroupSendToken | null;
|
||||
} = { accessKey: null, groupSendToken: null }
|
||||
): Promise<void> {
|
||||
let promise;
|
||||
|
||||
if (accessKey) {
|
||||
if (accessKey != null || groupSendToken != null) {
|
||||
promise = this.server.sendMessagesUnauth(serviceId, jsonData, timestamp, {
|
||||
accessKey,
|
||||
groupSendToken,
|
||||
online: this.online,
|
||||
story: this.story,
|
||||
urgent: this.urgent,
|
||||
@@ -393,7 +401,11 @@ export default class OutgoingMessage {
|
||||
recurse?: boolean
|
||||
): Promise<void> {
|
||||
const { sendMetadata } = this;
|
||||
const { accessKey, senderCertificate } = sendMetadata?.[serviceId] || {};
|
||||
const {
|
||||
accessKey = null,
|
||||
groupSendToken = null,
|
||||
senderCertificate,
|
||||
} = sendMetadata?.[serviceId] || {};
|
||||
|
||||
if (accessKey && !senderCertificate) {
|
||||
log.warn(
|
||||
@@ -401,7 +413,9 @@ export default class OutgoingMessage {
|
||||
);
|
||||
}
|
||||
|
||||
const sealedSender = Boolean(accessKey && senderCertificate);
|
||||
const sealedSender =
|
||||
(accessKey != null || groupSendToken != null) &&
|
||||
senderCertificate != null;
|
||||
|
||||
// We don't send to ourselves unless sealedSender is enabled
|
||||
const ourNumber = window.textsecure.storage.user.getNumber();
|
||||
@@ -508,6 +522,7 @@ export default class OutgoingMessage {
|
||||
if (sealedSender) {
|
||||
return this.transmitMessage(serviceId, jsonData, this.timestamp, {
|
||||
accessKey,
|
||||
groupSendToken,
|
||||
}).then(
|
||||
() => {
|
||||
this.recipients[serviceId] = deviceIds;
|
||||
|
||||
@@ -36,8 +36,6 @@ import {
|
||||
import type {
|
||||
ChallengeType,
|
||||
GetGroupLogOptionsType,
|
||||
GetProfileOptionsType,
|
||||
GetProfileUnauthOptionsType,
|
||||
GroupCredentialsType,
|
||||
GroupLogResponseType,
|
||||
ProxiedRequestOptionsType,
|
||||
@@ -103,12 +101,22 @@ import {
|
||||
getProtoForCallHistory,
|
||||
} from '../util/callDisposition';
|
||||
import { MAX_MESSAGE_COUNT } from '../util/deleteForMe.types';
|
||||
import type { GroupSendToken } from '../types/GroupSendEndorsements';
|
||||
|
||||
export type SendIdentifierData =
|
||||
| {
|
||||
accessKey: string;
|
||||
senderCertificate: SerializedCertificateType | null;
|
||||
groupSendToken: null;
|
||||
}
|
||||
| {
|
||||
accessKey: null;
|
||||
senderCertificate: SerializedCertificateType | null;
|
||||
groupSendToken: GroupSendToken;
|
||||
};
|
||||
|
||||
export type SendMetadataType = {
|
||||
[serviceId: ServiceIdString]: {
|
||||
accessKey: string;
|
||||
senderCertificate?: SerializedCertificateType;
|
||||
};
|
||||
[serviceId: ServiceIdString]: SendIdentifierData;
|
||||
};
|
||||
|
||||
export type SendOptionsType = {
|
||||
@@ -2321,17 +2329,6 @@ export default class MessageSender {
|
||||
// Note: instead of updating these functions, or adding new ones, remove these and go
|
||||
// directly to window.textsecure.messaging.server.<function>
|
||||
|
||||
async getProfile(
|
||||
serviceId: ServiceIdString,
|
||||
options: GetProfileOptionsType | GetProfileUnauthOptionsType
|
||||
): ReturnType<WebAPIType['getProfile']> {
|
||||
if (options.accessKey !== undefined) {
|
||||
return this.server.getProfileUnauth(serviceId, options);
|
||||
}
|
||||
|
||||
return this.server.getProfile(serviceId, options);
|
||||
}
|
||||
|
||||
async getAvatar(path: string): Promise<ReturnType<WebAPIType['getAvatar']>> {
|
||||
return this.server.getAvatar(path);
|
||||
}
|
||||
|
||||
@@ -74,6 +74,10 @@ import { isStagingServer } from '../util/isStagingServer';
|
||||
import type { IWebSocketResource } from './WebsocketResources';
|
||||
import type { GroupSendToken } from '../types/GroupSendEndorsements';
|
||||
import { parseUnknown, safeParseUnknown } from '../util/schemas';
|
||||
import type {
|
||||
ProfileFetchAuthRequestOptions,
|
||||
ProfileFetchUnauthRequestOptions,
|
||||
} from '../services/profiles';
|
||||
|
||||
// Note: this will break some code that expects to be able to use err.response when a
|
||||
// web request fails, because it will force it to text. But it is very useful for
|
||||
@@ -91,7 +95,7 @@ function resolveLibsignalNetEnvironment(url: string): Net.Environment {
|
||||
}
|
||||
|
||||
function _createRedactor(
|
||||
...toReplace: ReadonlyArray<string | undefined>
|
||||
...toReplace: ReadonlyArray<string | undefined | null>
|
||||
): RedactUrl {
|
||||
// NOTE: It would be nice to remove this cast, but TypeScript doesn't support
|
||||
// it. However, there is [an issue][0] that discusses this in more detail.
|
||||
@@ -898,31 +902,6 @@ export type CdsLookupOptionsType = Readonly<{
|
||||
useLibsignal?: boolean;
|
||||
}>;
|
||||
|
||||
type GetProfileCommonOptionsType = Readonly<
|
||||
{
|
||||
userLanguages: ReadonlyArray<string>;
|
||||
} & (
|
||||
| {
|
||||
profileKeyVersion?: undefined;
|
||||
profileKeyCredentialRequest?: undefined;
|
||||
}
|
||||
| {
|
||||
profileKeyVersion: string;
|
||||
profileKeyCredentialRequest?: string;
|
||||
}
|
||||
)
|
||||
>;
|
||||
|
||||
export type GetProfileOptionsType = GetProfileCommonOptionsType &
|
||||
Readonly<{
|
||||
accessKey?: undefined;
|
||||
}>;
|
||||
|
||||
export type GetProfileUnauthOptionsType = GetProfileCommonOptionsType &
|
||||
Readonly<{
|
||||
accessKey: string;
|
||||
}>;
|
||||
|
||||
export type GetGroupCredentialsOptionsType = Readonly<{
|
||||
startDayInMs: number;
|
||||
endDayInMs: number;
|
||||
@@ -1311,14 +1290,14 @@ export type WebAPIType = {
|
||||
}>;
|
||||
getProfile: (
|
||||
serviceId: ServiceIdString,
|
||||
options: GetProfileOptionsType
|
||||
options: ProfileFetchAuthRequestOptions
|
||||
) => Promise<ProfileType>;
|
||||
getAccountForUsername: (
|
||||
options: GetAccountForUsernameOptionsType
|
||||
) => Promise<GetAccountForUsernameResultType>;
|
||||
getProfileUnauth: (
|
||||
serviceId: ServiceIdString,
|
||||
options: GetProfileUnauthOptionsType
|
||||
options: ProfileFetchUnauthRequestOptions
|
||||
) => Promise<ProfileType>;
|
||||
getBadgeImageFile: (imageUrl: string) => Promise<Uint8Array>;
|
||||
getSubscriptionConfiguration: (
|
||||
@@ -1413,7 +1392,8 @@ export type WebAPIType = {
|
||||
messageArray: ReadonlyArray<MessageType>,
|
||||
timestamp: number,
|
||||
options: {
|
||||
accessKey?: string;
|
||||
accessKey: string | null;
|
||||
groupSendToken: GroupSendToken | null;
|
||||
online?: boolean;
|
||||
story?: boolean;
|
||||
urgent?: boolean;
|
||||
@@ -1421,8 +1401,8 @@ export type WebAPIType = {
|
||||
) => Promise<void>;
|
||||
sendWithSenderKey: (
|
||||
payload: Uint8Array,
|
||||
accessKeys: Uint8Array | undefined,
|
||||
groupSendToken: GroupSendToken | undefined,
|
||||
accessKeys: Uint8Array | null,
|
||||
groupSendToken: GroupSendToken | null,
|
||||
timestamp: number,
|
||||
options: {
|
||||
online?: boolean;
|
||||
@@ -2177,19 +2157,19 @@ export function initialize({
|
||||
{
|
||||
profileKeyVersion,
|
||||
profileKeyCredentialRequest,
|
||||
}: GetProfileCommonOptionsType
|
||||
}: ProfileFetchAuthRequestOptions | ProfileFetchUnauthRequestOptions
|
||||
) {
|
||||
let profileUrl = `/${serviceId}`;
|
||||
if (profileKeyVersion !== undefined) {
|
||||
if (profileKeyVersion != null) {
|
||||
profileUrl += `/${profileKeyVersion}`;
|
||||
if (profileKeyCredentialRequest !== undefined) {
|
||||
if (profileKeyCredentialRequest != null) {
|
||||
profileUrl +=
|
||||
`/${profileKeyCredentialRequest}` +
|
||||
'?credentialType=expiringProfileKey';
|
||||
}
|
||||
} else {
|
||||
strictAssert(
|
||||
profileKeyCredentialRequest === undefined,
|
||||
profileKeyCredentialRequest == null,
|
||||
'getProfileUrl called without version, but with request'
|
||||
);
|
||||
}
|
||||
@@ -2199,7 +2179,7 @@ export function initialize({
|
||||
|
||||
async function getProfile(
|
||||
serviceId: ServiceIdString,
|
||||
options: GetProfileOptionsType
|
||||
options: ProfileFetchAuthRequestOptions
|
||||
) {
|
||||
const { profileKeyVersion, profileKeyCredentialRequest, userLanguages } =
|
||||
options;
|
||||
@@ -2258,15 +2238,25 @@ export function initialize({
|
||||
|
||||
async function getProfileUnauth(
|
||||
serviceId: ServiceIdString,
|
||||
options: GetProfileUnauthOptionsType
|
||||
options: ProfileFetchUnauthRequestOptions
|
||||
) {
|
||||
const {
|
||||
accessKey,
|
||||
groupSendToken,
|
||||
profileKeyVersion,
|
||||
profileKeyCredentialRequest,
|
||||
userLanguages,
|
||||
} = options;
|
||||
|
||||
if (profileKeyVersion != null || profileKeyCredentialRequest != null) {
|
||||
// Without an up-to-date profile key, we won't be able to read the
|
||||
// profile anyways so there's no point in falling back to endorsements.
|
||||
strictAssert(
|
||||
groupSendToken == null,
|
||||
'Should not use endorsements for fetching a versioned profile'
|
||||
);
|
||||
}
|
||||
|
||||
return (await _ajax({
|
||||
call: 'profile',
|
||||
httpType: 'GET',
|
||||
@@ -2276,8 +2266,8 @@ export function initialize({
|
||||
},
|
||||
responseType: 'json',
|
||||
unauthenticated: true,
|
||||
accessKey,
|
||||
groupSendToken: undefined,
|
||||
accessKey: accessKey ?? undefined,
|
||||
groupSendToken: groupSendToken ?? undefined,
|
||||
redactUrl: _createRedactor(
|
||||
serviceId,
|
||||
profileKeyVersion,
|
||||
@@ -3273,11 +3263,13 @@ export function initialize({
|
||||
timestamp: number,
|
||||
{
|
||||
accessKey,
|
||||
groupSendToken,
|
||||
online,
|
||||
urgent = true,
|
||||
story = false,
|
||||
}: {
|
||||
accessKey?: string;
|
||||
accessKey: string | null;
|
||||
groupSendToken: GroupSendToken | null;
|
||||
online?: boolean;
|
||||
story?: boolean;
|
||||
urgent?: boolean;
|
||||
@@ -3297,8 +3289,8 @@ export function initialize({
|
||||
jsonData,
|
||||
responseType: 'json',
|
||||
unauthenticated: true,
|
||||
accessKey,
|
||||
groupSendToken: undefined,
|
||||
accessKey: accessKey ?? undefined,
|
||||
groupSendToken: groupSendToken ?? undefined,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3334,8 +3326,8 @@ export function initialize({
|
||||
|
||||
async function sendWithSenderKey(
|
||||
data: Uint8Array,
|
||||
accessKeys: Uint8Array | undefined,
|
||||
groupSendToken: GroupSendToken | undefined,
|
||||
accessKeys: Uint8Array | null,
|
||||
groupSendToken: GroupSendToken | null,
|
||||
timestamp: number,
|
||||
{
|
||||
online,
|
||||
@@ -3360,7 +3352,7 @@ export function initialize({
|
||||
responseType: 'json',
|
||||
unauthenticated: true,
|
||||
accessKey: accessKeys != null ? Bytes.toBase64(accessKeys) : undefined,
|
||||
groupSendToken,
|
||||
groupSendToken: groupSendToken ?? undefined,
|
||||
});
|
||||
const parseResult = safeParseUnknown(
|
||||
multiRecipient200ResponseSchema,
|
||||
|
||||
Reference in New Issue
Block a user