| 
									
										
										
										
											2023-01-03 11:55:46 -08:00
										 |  |  | // Copyright 2020 Signal Messenger, LLC
 | 
					
						
							| 
									
										
										
										
											2020-10-30 15:34:04 -05:00
										 |  |  | // SPDX-License-Identifier: AGPL-3.0-only
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 14:53:21 -07:00
										 |  |  | /* eslint-disable @typescript-eslint/no-explicit-any */ | 
					
						
							|  |  |  | /* eslint-disable more/no-then */ | 
					
						
							|  |  |  | /* eslint-disable no-param-reassign */ | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | import { reject } from 'lodash'; | 
					
						
							| 
									
										
										
										
											2021-04-16 16:13:13 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-25 15:40:04 -07:00
										 |  |  | import { z } from 'zod'; | 
					
						
							| 
									
										
										
										
											2021-10-26 14:15:33 -05:00
										 |  |  | import type { | 
					
						
							| 
									
										
										
										
											2021-05-28 12:11:19 -07:00
										 |  |  |   CiphertextMessage, | 
					
						
							|  |  |  |   PlaintextContent, | 
					
						
							| 
									
										
										
										
											2022-03-24 14:47:21 -07:00
										 |  |  | } from '@signalapp/libsignal-client'; | 
					
						
							| 
									
										
										
										
											2021-10-26 14:15:33 -05:00
										 |  |  | import { | 
					
						
							| 
									
										
										
										
											2022-11-22 10:43:43 -08:00
										 |  |  |   ErrorCode, | 
					
						
							|  |  |  |   LibSignalErrorBase, | 
					
						
							| 
									
										
										
										
											2021-10-26 14:15:33 -05:00
										 |  |  |   CiphertextMessageType, | 
					
						
							| 
									
										
										
										
											2021-04-16 16:13:13 -07:00
										 |  |  |   ProtocolAddress, | 
					
						
							| 
									
										
										
										
											2021-05-28 12:11:19 -07:00
										 |  |  |   sealedSenderEncrypt, | 
					
						
							| 
									
										
										
										
											2021-04-16 16:13:13 -07:00
										 |  |  |   SenderCertificate, | 
					
						
							|  |  |  |   signalEncrypt, | 
					
						
							| 
									
										
										
										
											2021-05-28 12:11:19 -07:00
										 |  |  |   UnidentifiedSenderMessageContent, | 
					
						
							| 
									
										
										
										
											2022-03-24 14:47:21 -07:00
										 |  |  | } from '@signalapp/libsignal-client'; | 
					
						
							| 
									
										
										
										
											2021-04-16 16:13:13 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-28 16:38:55 -07:00
										 |  |  | import type { WebAPIType, MessageType } from './WebAPI'; | 
					
						
							| 
									
										
										
										
											2021-10-26 14:15:33 -05:00
										 |  |  | import type { SendMetadataType, SendOptionsType } from './SendMessage'; | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  | import { | 
					
						
							|  |  |  |   OutgoingIdentityKeyError, | 
					
						
							|  |  |  |   OutgoingMessageError, | 
					
						
							|  |  |  |   SendMessageNetworkError, | 
					
						
							| 
									
										
										
										
											2021-05-05 17:09:29 -07:00
										 |  |  |   SendMessageChallengeError, | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |   UnregisteredUserError, | 
					
						
							| 
									
										
										
										
											2021-09-21 17:58:03 -07:00
										 |  |  |   HTTPError, | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  | } from './Errors'; | 
					
						
							| 
									
										
										
										
											2021-10-26 14:15:33 -05:00
										 |  |  | import type { CallbackResultType, CustomError } from './Types.d'; | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  | import { Address } from '../types/Address'; | 
					
						
							| 
									
										
										
										
											2022-11-22 10:43:43 -08:00
										 |  |  | import * as Errors from '../types/errors'; | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  | import { QualifiedAddress } from '../types/QualifiedAddress'; | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  | import type { ServiceIdString } from '../types/ServiceId'; | 
					
						
							| 
									
										
										
										
											2021-04-16 16:13:13 -07:00
										 |  |  | import { Sessions, IdentityKeys } from '../LibSignalStores'; | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  | import { getKeysForServiceId } from './getKeysForServiceId'; | 
					
						
							| 
									
										
										
										
											2021-07-09 12:36:10 -07:00
										 |  |  | import { SignalService as Proto } from '../protobuf'; | 
					
						
							| 
									
										
										
										
											2021-09-17 14:27:53 -04:00
										 |  |  | import * as log from '../logging/log'; | 
					
						
							| 
									
										
										
										
											2021-04-16 16:13:13 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | export const enum SenderCertificateMode { | 
					
						
							|  |  |  |   WithE164, | 
					
						
							|  |  |  |   WithoutE164, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  | export type SendLogCallbackType = (options: { | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |   serviceId: ServiceIdString; | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |   deviceIds: Array<number>; | 
					
						
							|  |  |  | }) => Promise<void>; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-14 13:34:52 -08:00
										 |  |  | export const serializedCertificateSchema = z.object({ | 
					
						
							|  |  |  |   expires: z.number().optional(), | 
					
						
							|  |  |  |   serialized: z.instanceof(Uint8Array), | 
					
						
							|  |  |  | }); | 
					
						
							| 
									
										
										
										
											2021-04-16 16:13:13 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | export type SerializedCertificateType = z.infer< | 
					
						
							|  |  |  |   typeof serializedCertificateSchema | 
					
						
							|  |  |  | >; | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | type OutgoingMessageOptionsType = SendOptionsType & { | 
					
						
							|  |  |  |   online?: boolean; | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2015-10-20 16:30:39 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-16 16:13:13 -07:00
										 |  |  | function ciphertextMessageTypeToEnvelopeType(type: number) { | 
					
						
							|  |  |  |   if (type === CiphertextMessageType.PreKey) { | 
					
						
							| 
									
										
										
										
											2021-07-09 12:36:10 -07:00
										 |  |  |     return Proto.Envelope.Type.PREKEY_BUNDLE; | 
					
						
							| 
									
										
										
										
											2021-04-16 16:13:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  |   if (type === CiphertextMessageType.Whisper) { | 
					
						
							| 
									
										
										
										
											2021-07-09 12:36:10 -07:00
										 |  |  |     return Proto.Envelope.Type.CIPHERTEXT; | 
					
						
							| 
									
										
										
										
											2021-04-16 16:13:13 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-05-28 12:11:19 -07:00
										 |  |  |   if (type === CiphertextMessageType.Plaintext) { | 
					
						
							| 
									
										
										
										
											2021-07-09 12:36:10 -07:00
										 |  |  |     return Proto.Envelope.Type.PLAINTEXT_CONTENT; | 
					
						
							| 
									
										
										
										
											2021-05-28 12:11:19 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-04-16 16:13:13 -07:00
										 |  |  |   throw new Error( | 
					
						
							|  |  |  |     `ciphertextMessageTypeToEnvelopeType: Unrecognized type ${type}` | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-25 15:40:04 -07:00
										 |  |  | function getPaddedMessageLength(messageLength: number): number { | 
					
						
							|  |  |  |   const messageLengthWithTerminator = messageLength + 1; | 
					
						
							|  |  |  |   let messagePartCount = Math.floor(messageLengthWithTerminator / 160); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (messageLengthWithTerminator % 160 !== 0) { | 
					
						
							|  |  |  |     messagePartCount += 1; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return messagePartCount * 160; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-09 12:36:10 -07:00
										 |  |  | export function padMessage(messageBuffer: Uint8Array): Uint8Array { | 
					
						
							| 
									
										
										
										
											2021-05-25 15:40:04 -07:00
										 |  |  |   const plaintext = new Uint8Array( | 
					
						
							|  |  |  |     getPaddedMessageLength(messageBuffer.byteLength + 1) - 1 | 
					
						
							|  |  |  |   ); | 
					
						
							| 
									
										
										
										
											2021-07-09 12:36:10 -07:00
										 |  |  |   plaintext.set(messageBuffer); | 
					
						
							| 
									
										
										
										
											2021-05-25 15:40:04 -07:00
										 |  |  |   plaintext[messageBuffer.byteLength] = 0x80; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return plaintext; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  | export default class OutgoingMessage { | 
					
						
							|  |  |  |   server: WebAPIType; | 
					
						
							| 
									
										
										
										
											2020-09-24 14:53:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |   timestamp: number; | 
					
						
							| 
									
										
										
										
											2020-09-24 14:53:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |   serviceIds: ReadonlyArray<ServiceIdString>; | 
					
						
							| 
									
										
										
										
											2020-09-24 14:53:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-09 12:36:10 -07:00
										 |  |  |   message: Proto.Content | PlaintextContent; | 
					
						
							| 
									
										
										
										
											2020-09-24 14:53:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |   callback: (result: CallbackResultType) => void; | 
					
						
							| 
									
										
										
										
											2020-09-24 14:53:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |   plaintext?: Uint8Array; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |   serviceIdsCompleted: number; | 
					
						
							| 
									
										
										
										
											2020-09-24 14:53:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-24 08:11:18 -07:00
										 |  |  |   errors: Array<CustomError>; | 
					
						
							| 
									
										
										
										
											2020-09-24 14:53:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |   successfulServiceIds: Array<ServiceIdString>; | 
					
						
							| 
									
										
										
										
											2020-09-24 14:53:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |   failoverServiceIds: Array<ServiceIdString>; | 
					
						
							| 
									
										
										
										
											2020-09-24 14:53:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |   unidentifiedDeliveries: Array<ServiceIdString>; | 
					
						
							| 
									
										
										
										
											2020-09-24 14:53:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |   sendMetadata?: SendMetadataType; | 
					
						
							| 
									
										
										
										
											2020-09-24 14:53:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |   online?: boolean; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-28 12:11:19 -07:00
										 |  |  |   groupId?: string; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   contentHint: number; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-01 09:55:13 -07:00
										 |  |  |   urgent: boolean; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-30 09:59:36 -07:00
										 |  |  |   story?: boolean; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |   recipients: Record<string, Array<number>>; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   sendLogCallback?: SendLogCallbackType; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   constructor({ | 
					
						
							|  |  |  |     callback, | 
					
						
							|  |  |  |     contentHint, | 
					
						
							|  |  |  |     groupId, | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |     serviceIds, | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |     message, | 
					
						
							|  |  |  |     options, | 
					
						
							|  |  |  |     sendLogCallback, | 
					
						
							|  |  |  |     server, | 
					
						
							| 
									
										
										
										
											2022-09-30 09:59:36 -07:00
										 |  |  |     story, | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |     timestamp, | 
					
						
							| 
									
										
										
										
											2022-07-01 09:55:13 -07:00
										 |  |  |     urgent, | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |   }: { | 
					
						
							|  |  |  |     callback: (result: CallbackResultType) => void; | 
					
						
							|  |  |  |     contentHint: number; | 
					
						
							|  |  |  |     groupId: string | undefined; | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |     serviceIds: ReadonlyArray<ServiceIdString>; | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |     message: Proto.Content | Proto.DataMessage | PlaintextContent; | 
					
						
							|  |  |  |     options?: OutgoingMessageOptionsType; | 
					
						
							|  |  |  |     sendLogCallback?: SendLogCallbackType; | 
					
						
							|  |  |  |     server: WebAPIType; | 
					
						
							| 
									
										
										
										
											2022-09-30 09:59:36 -07:00
										 |  |  |     story?: boolean; | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |     timestamp: number; | 
					
						
							| 
									
										
										
										
											2022-07-01 09:55:13 -07:00
										 |  |  |     urgent: boolean; | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |   }) { | 
					
						
							| 
									
										
										
										
											2021-07-09 12:36:10 -07:00
										 |  |  |     if (message instanceof Proto.DataMessage) { | 
					
						
							|  |  |  |       const content = new Proto.Content(); | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |       content.dataMessage = message; | 
					
						
							|  |  |  |       this.message = content; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       this.message = message; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     this.server = server; | 
					
						
							|  |  |  |     this.timestamp = timestamp; | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |     this.serviceIds = serviceIds; | 
					
						
							| 
									
										
										
										
											2021-05-28 12:11:19 -07:00
										 |  |  |     this.contentHint = contentHint; | 
					
						
							|  |  |  |     this.groupId = groupId; | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |     this.callback = callback; | 
					
						
							| 
									
										
										
										
											2022-09-30 09:59:36 -07:00
										 |  |  |     this.story = story; | 
					
						
							| 
									
										
										
										
											2022-07-01 09:55:13 -07:00
										 |  |  |     this.urgent = urgent; | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |     this.serviceIdsCompleted = 0; | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |     this.errors = []; | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |     this.successfulServiceIds = []; | 
					
						
							|  |  |  |     this.failoverServiceIds = []; | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |     this.unidentifiedDeliveries = []; | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |     this.recipients = {}; | 
					
						
							|  |  |  |     this.sendLogCallback = sendLogCallback; | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |     this.sendMetadata = options?.sendMetadata; | 
					
						
							|  |  |  |     this.online = options?.online; | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-09-24 14:53:21 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   numberCompleted(): void { | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |     this.serviceIdsCompleted += 1; | 
					
						
							|  |  |  |     if (this.serviceIdsCompleted >= this.serviceIds.length) { | 
					
						
							| 
									
										
										
										
											2022-03-04 17:39:37 -08:00
										 |  |  |       const proto = this.message; | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |       const contentProto = this.getContentProtoBytes(); | 
					
						
							| 
									
										
										
										
											2022-07-01 09:55:13 -07:00
										 |  |  |       const { timestamp, contentHint, recipients, urgent } = this; | 
					
						
							| 
									
										
										
										
											2022-03-04 17:39:37 -08:00
										 |  |  |       let dataMessage: Uint8Array | undefined; | 
					
						
							| 
									
										
										
										
											2023-04-20 12:31:59 -04:00
										 |  |  |       let editMessage: Uint8Array | undefined; | 
					
						
							| 
									
										
										
										
											2022-08-15 14:53:33 -07:00
										 |  |  |       let hasPniSignatureMessage = false; | 
					
						
							| 
									
										
										
										
											2022-03-04 17:39:37 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-15 14:53:33 -07:00
										 |  |  |       if (proto instanceof Proto.Content) { | 
					
						
							|  |  |  |         if (proto.dataMessage) { | 
					
						
							|  |  |  |           dataMessage = Proto.DataMessage.encode(proto.dataMessage).finish(); | 
					
						
							| 
									
										
										
										
											2023-04-20 12:31:59 -04:00
										 |  |  |         } else if (proto.editMessage) { | 
					
						
							|  |  |  |           editMessage = Proto.EditMessage.encode(proto.editMessage).finish(); | 
					
						
							| 
									
										
										
										
											2022-08-15 14:53:33 -07:00
										 |  |  |         } | 
					
						
							|  |  |  |         hasPniSignatureMessage = Boolean(proto.pniSignatureMessage); | 
					
						
							| 
									
										
										
										
											2022-03-04 17:39:37 -08:00
										 |  |  |       } else if (proto instanceof Proto.DataMessage) { | 
					
						
							|  |  |  |         dataMessage = Proto.DataMessage.encode(proto).finish(); | 
					
						
							| 
									
										
										
										
											2023-04-20 12:31:59 -04:00
										 |  |  |       } else if (proto instanceof Proto.EditMessage) { | 
					
						
							|  |  |  |         editMessage = Proto.EditMessage.encode(proto).finish(); | 
					
						
							| 
									
										
										
										
											2022-03-04 17:39:37 -08:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-02 09:51:22 -07:00
										 |  |  |       this.callback({ | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |         successfulServiceIds: this.successfulServiceIds, | 
					
						
							|  |  |  |         failoverServiceIds: this.failoverServiceIds, | 
					
						
							| 
									
										
										
										
											2018-05-02 09:51:22 -07:00
										 |  |  |         errors: this.errors, | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |         unidentifiedDeliveries: this.unidentifiedDeliveries, | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         contentHint, | 
					
						
							| 
									
										
										
										
											2022-03-04 17:39:37 -08:00
										 |  |  |         dataMessage, | 
					
						
							| 
									
										
										
										
											2023-04-20 12:31:59 -04:00
										 |  |  |         editMessage, | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |         recipients, | 
					
						
							|  |  |  |         contentProto, | 
					
						
							|  |  |  |         timestamp, | 
					
						
							| 
									
										
										
										
											2022-07-01 09:55:13 -07:00
										 |  |  |         urgent, | 
					
						
							| 
									
										
										
										
											2022-08-15 14:53:33 -07:00
										 |  |  |         hasPniSignatureMessage, | 
					
						
							| 
									
										
										
										
											2018-05-02 09:51:22 -07:00
										 |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-09-24 14:53:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-22 15:30:28 -07:00
										 |  |  |   registerError( | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |     serviceId: ServiceIdString, | 
					
						
							| 
									
										
										
										
											2020-10-22 15:30:28 -07:00
										 |  |  |     reason: string, | 
					
						
							|  |  |  |     providedError?: Error | 
					
						
							|  |  |  |   ): void { | 
					
						
							|  |  |  |     let error = providedError; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-21 17:58:03 -07:00
										 |  |  |     if (!error || (error instanceof HTTPError && error.code !== 404)) { | 
					
						
							| 
									
										
										
										
											2021-05-05 17:09:29 -07:00
										 |  |  |       if (error && error.code === 428) { | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |         error = new SendMessageChallengeError(serviceId, error); | 
					
						
							| 
									
										
										
										
											2021-05-05 17:09:29 -07:00
										 |  |  |       } else { | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |         error = new OutgoingMessageError(serviceId, null, null, error); | 
					
						
							| 
									
										
										
										
											2021-05-05 17:09:29 -07:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2018-05-02 09:51:22 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-10-20 16:30:39 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-22 10:43:43 -08:00
										 |  |  |     error.cause = reason; | 
					
						
							| 
									
										
										
										
											2020-10-22 15:30:28 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-02 09:51:22 -07:00
										 |  |  |     this.errors[this.errors.length] = error; | 
					
						
							|  |  |  |     this.numberCompleted(); | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-09-24 14:53:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |   reloadDevicesAndSend( | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |     serviceId: ServiceIdString, | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |     recurse?: boolean | 
					
						
							|  |  |  |   ): () => Promise<void> { | 
					
						
							| 
									
										
										
										
											2021-05-25 15:40:04 -07:00
										 |  |  |     return async () => { | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |       const ourAci = window.textsecure.storage.user.getCheckedAci(); | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  |       const deviceIds = await window.textsecure.storage.protocol.getDeviceIds({ | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |         ourServiceId: ourAci, | 
					
						
							|  |  |  |         serviceId, | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  |       }); | 
					
						
							| 
									
										
										
										
											2021-05-25 15:40:04 -07:00
										 |  |  |       if (deviceIds.length === 0) { | 
					
						
							|  |  |  |         this.registerError( | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |           serviceId, | 
					
						
							| 
									
										
										
										
											2021-05-25 15:40:04 -07:00
										 |  |  |           'reloadDevicesAndSend: Got empty device list when loading device keys', | 
					
						
							|  |  |  |           undefined | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |         return undefined; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |       return this.doSendMessage(serviceId, deviceIds, recurse); | 
					
						
							| 
									
										
										
										
											2021-05-25 15:40:04 -07:00
										 |  |  |     }; | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-21 14:51:20 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |   async getKeysForServiceId( | 
					
						
							|  |  |  |     serviceId: ServiceIdString, | 
					
						
							| 
									
										
										
										
											2021-05-25 15:40:04 -07:00
										 |  |  |     updateDevices?: Array<number> | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |   ): Promise<void> { | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |     const { sendMetadata } = this; | 
					
						
							|  |  |  |     const info = | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |       sendMetadata && sendMetadata[serviceId] | 
					
						
							|  |  |  |         ? sendMetadata[serviceId] | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |         : { accessKey: undefined }; | 
					
						
							|  |  |  |     const { accessKey } = info; | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |     const { accessKeyFailed } = await getKeysForServiceId( | 
					
						
							|  |  |  |       serviceId, | 
					
						
							| 
									
										
										
										
											2022-11-22 10:43:43 -08:00
										 |  |  |       this.server, | 
					
						
							|  |  |  |       updateDevices, | 
					
						
							|  |  |  |       accessKey | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |     if (accessKeyFailed && !this.failoverServiceIds.includes(serviceId)) { | 
					
						
							|  |  |  |       this.failoverServiceIds.push(serviceId); | 
					
						
							| 
									
										
										
										
											2018-07-21 14:51:20 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-06-14 19:30:18 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |   async transmitMessage( | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |     serviceId: ServiceIdString, | 
					
						
							| 
									
										
										
										
											2021-09-28 16:38:55 -07:00
										 |  |  |     jsonData: ReadonlyArray<MessageType>, | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |     timestamp: number, | 
					
						
							|  |  |  |     { accessKey }: { accessKey?: string } = {} | 
					
						
							| 
									
										
										
										
											2020-09-24 14:53:21 -07:00
										 |  |  |   ): Promise<void> { | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |     let promise; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (accessKey) { | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |       promise = this.server.sendMessagesUnauth(serviceId, jsonData, timestamp, { | 
					
						
							|  |  |  |         accessKey, | 
					
						
							|  |  |  |         online: this.online, | 
					
						
							|  |  |  |         story: this.story, | 
					
						
							|  |  |  |         urgent: this.urgent, | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |       promise = this.server.sendMessages(serviceId, jsonData, timestamp, { | 
					
						
							| 
									
										
										
										
											2022-07-01 09:55:13 -07:00
										 |  |  |         online: this.online, | 
					
						
							| 
									
										
										
										
											2022-09-30 09:59:36 -07:00
										 |  |  |         story: this.story, | 
					
						
							| 
									
										
										
										
											2022-07-01 09:55:13 -07:00
										 |  |  |         urgent: this.urgent, | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return promise.catch(e => { | 
					
						
							| 
									
										
										
										
											2021-09-21 17:58:03 -07:00
										 |  |  |       if (e instanceof HTTPError && e.code !== 409 && e.code !== 410) { | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |         // 409 and 410 should bubble and be handled by doSendMessage
 | 
					
						
							|  |  |  |         // 404 should throw UnregisteredUserError
 | 
					
						
							| 
									
										
										
										
											2021-05-05 17:09:29 -07:00
										 |  |  |         // 428 should throw SendMessageChallengeError
 | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |         // all other network errors can be retried later.
 | 
					
						
							|  |  |  |         if (e.code === 404) { | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |           throw new UnregisteredUserError(serviceId, e); | 
					
						
							| 
									
										
										
										
											2016-06-14 19:30:18 -07:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-05-05 17:09:29 -07:00
										 |  |  |         if (e.code === 428) { | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |           throw new SendMessageChallengeError(serviceId, e); | 
					
						
							| 
									
										
										
										
											2021-05-05 17:09:29 -07:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |         throw new SendMessageNetworkError(serviceId, jsonData, e); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |       } | 
					
						
							|  |  |  |       throw e; | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-06-14 19:30:18 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-23 17:49:05 -07:00
										 |  |  |   getPlaintext(): Uint8Array { | 
					
						
							| 
									
										
										
										
											2018-05-02 09:51:22 -07:00
										 |  |  |     if (!this.plaintext) { | 
					
						
							| 
									
										
										
										
											2021-05-28 12:11:19 -07:00
										 |  |  |       const { message } = this; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-09 12:36:10 -07:00
										 |  |  |       if (message instanceof Proto.Content) { | 
					
						
							|  |  |  |         this.plaintext = padMessage(Proto.Content.encode(message).finish()); | 
					
						
							| 
									
										
										
										
											2021-05-28 12:11:19 -07:00
										 |  |  |       } else { | 
					
						
							|  |  |  |         this.plaintext = message.serialize(); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2018-05-02 09:51:22 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-09-23 17:49:05 -07:00
										 |  |  |     return this.plaintext; | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-08-04 12:25:30 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |   getContentProtoBytes(): Uint8Array | undefined { | 
					
						
							|  |  |  |     if (this.message instanceof Proto.Content) { | 
					
						
							|  |  |  |       return new Uint8Array(Proto.Content.encode(this.message).finish()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return undefined; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-28 12:11:19 -07:00
										 |  |  |   async getCiphertextMessage({ | 
					
						
							|  |  |  |     identityKeyStore, | 
					
						
							|  |  |  |     protocolAddress, | 
					
						
							|  |  |  |     sessionStore, | 
					
						
							|  |  |  |   }: { | 
					
						
							|  |  |  |     identityKeyStore: IdentityKeys; | 
					
						
							|  |  |  |     protocolAddress: ProtocolAddress; | 
					
						
							|  |  |  |     sessionStore: Sessions; | 
					
						
							|  |  |  |   }): Promise<CiphertextMessage> { | 
					
						
							|  |  |  |     const { message } = this; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-09 12:36:10 -07:00
										 |  |  |     if (message instanceof Proto.Content) { | 
					
						
							| 
									
										
										
										
											2021-05-28 12:11:19 -07:00
										 |  |  |       return signalEncrypt( | 
					
						
							|  |  |  |         Buffer.from(this.getPlaintext()), | 
					
						
							|  |  |  |         protocolAddress, | 
					
						
							|  |  |  |         sessionStore, | 
					
						
							|  |  |  |         identityKeyStore | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return message.asCiphertextMessage(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |   async doSendMessage( | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |     serviceId: ServiceIdString, | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |     deviceIds: Array<number>, | 
					
						
							|  |  |  |     recurse?: boolean | 
					
						
							|  |  |  |   ): Promise<void> { | 
					
						
							| 
									
										
										
										
											2021-04-05 15:38:36 -05:00
										 |  |  |     const { sendMetadata } = this; | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |     const { accessKey, senderCertificate } = sendMetadata?.[serviceId] || {}; | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (accessKey && !senderCertificate) { | 
					
						
							| 
									
										
										
										
											2021-09-17 14:27:53 -04:00
										 |  |  |       log.warn( | 
					
						
							| 
									
										
										
										
											2020-01-16 15:57:37 -08:00
										 |  |  |         'OutgoingMessage.doSendMessage: accessKey was provided, but senderCertificate was not' | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-31 16:58:14 -07:00
										 |  |  |     const sealedSender = Boolean(accessKey && senderCertificate); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-17 10:57:47 -07:00
										 |  |  |     // We don't send to ourselves unless sealedSender is enabled
 | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |     const ourNumber = window.textsecure.storage.user.getNumber(); | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |     const ourAci = window.textsecure.storage.user.getCheckedAci(); | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |     const ourDeviceId = window.textsecure.storage.user.getDeviceId(); | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |     if ((serviceId === ourNumber || serviceId === ourAci) && !sealedSender) { | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |       deviceIds = reject( | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |         deviceIds, | 
					
						
							|  |  |  |         deviceId => | 
					
						
							|  |  |  |           // because we store our own device ID as a string at least sometimes
 | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |           deviceId === ourDeviceId || | 
					
						
							|  |  |  |           (typeof ourDeviceId === 'string' && | 
					
						
							|  |  |  |             deviceId === parseInt(ourDeviceId, 10)) | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |     const sessionStore = new Sessions({ ourServiceId: ourAci }); | 
					
						
							|  |  |  |     const identityKeyStore = new IdentityKeys({ ourServiceId: ourAci }); | 
					
						
							| 
									
										
										
										
											2021-04-16 16:13:13 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-02 09:51:22 -07:00
										 |  |  |     return Promise.all( | 
					
						
							| 
									
										
										
										
											2021-04-16 16:13:13 -07:00
										 |  |  |       deviceIds.map(async destinationDeviceId => { | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  |         const address = new QualifiedAddress( | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |           ourAci, | 
					
						
							|  |  |  |           new Address(serviceId, destinationDeviceId) | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  |         ); | 
					
						
							| 
									
										
										
										
											2017-08-04 12:25:30 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-28 16:38:55 -07:00
										 |  |  |         return window.textsecure.storage.protocol.enqueueSessionJob<MessageType>( | 
					
						
							| 
									
										
										
										
											2021-05-04 18:03:03 -07:00
										 |  |  |           address, | 
					
						
							| 
									
										
										
										
											2023-12-19 10:55:09 -08:00
										 |  |  |           `doSendMessage(${address.toString()}, ${this.timestamp})`, | 
					
						
							| 
									
										
										
										
											2021-05-04 18:03:03 -07:00
										 |  |  |           async () => { | 
					
						
							|  |  |  |             const protocolAddress = ProtocolAddress.new( | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |               serviceId, | 
					
						
							| 
									
										
										
										
											2021-05-04 18:03:03 -07:00
										 |  |  |               destinationDeviceId | 
					
						
							|  |  |  |             ); | 
					
						
							| 
									
										
										
										
											2018-05-02 09:51:22 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-04 18:03:03 -07:00
										 |  |  |             const activeSession = await sessionStore.getSession( | 
					
						
							|  |  |  |               protocolAddress | 
					
						
							|  |  |  |             ); | 
					
						
							|  |  |  |             if (!activeSession) { | 
					
						
							|  |  |  |               throw new Error( | 
					
						
							| 
									
										
										
										
											2022-02-09 22:33:19 +02:00
										 |  |  |                 'OutgoingMessage.doSendMessage: No active session!' | 
					
						
							| 
									
										
										
										
											2021-05-04 18:03:03 -07:00
										 |  |  |               ); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-04-16 16:13:13 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-11 16:43:05 -06:00
										 |  |  |             const destinationRegistrationId = | 
					
						
							|  |  |  |               activeSession.remoteRegistrationId(); | 
					
						
							| 
									
										
										
										
											2021-05-04 18:03:03 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |             if (sealedSender && senderCertificate) { | 
					
						
							| 
									
										
										
										
											2021-05-28 12:11:19 -07:00
										 |  |  |               const ciphertextMessage = await this.getCiphertextMessage({ | 
					
						
							|  |  |  |                 identityKeyStore, | 
					
						
							|  |  |  |                 protocolAddress, | 
					
						
							|  |  |  |                 sessionStore, | 
					
						
							|  |  |  |               }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-04 18:03:03 -07:00
										 |  |  |               const certificate = SenderCertificate.deserialize( | 
					
						
							|  |  |  |                 Buffer.from(senderCertificate.serialized) | 
					
						
							|  |  |  |               ); | 
					
						
							| 
									
										
										
										
											2021-05-28 12:11:19 -07:00
										 |  |  |               const groupIdBuffer = this.groupId | 
					
						
							|  |  |  |                 ? Buffer.from(this.groupId, 'base64') | 
					
						
							|  |  |  |                 : null; | 
					
						
							| 
									
										
										
										
											2021-05-04 18:03:03 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-28 12:11:19 -07:00
										 |  |  |               const content = UnidentifiedSenderMessageContent.new( | 
					
						
							|  |  |  |                 ciphertextMessage, | 
					
						
							| 
									
										
										
										
											2021-05-04 18:03:03 -07:00
										 |  |  |                 certificate, | 
					
						
							| 
									
										
										
										
											2021-05-28 12:11:19 -07:00
										 |  |  |                 this.contentHint, | 
					
						
							|  |  |  |                 groupIdBuffer | 
					
						
							|  |  |  |               ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               const buffer = await sealedSenderEncrypt( | 
					
						
							|  |  |  |                 content, | 
					
						
							|  |  |  |                 protocolAddress, | 
					
						
							| 
									
										
										
										
											2021-05-04 18:03:03 -07:00
										 |  |  |                 identityKeyStore | 
					
						
							|  |  |  |               ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               return { | 
					
						
							| 
									
										
										
										
											2021-07-09 12:36:10 -07:00
										 |  |  |                 type: Proto.Envelope.Type.UNIDENTIFIED_SENDER, | 
					
						
							| 
									
										
										
										
											2021-05-04 18:03:03 -07:00
										 |  |  |                 destinationDeviceId, | 
					
						
							|  |  |  |                 destinationRegistrationId, | 
					
						
							|  |  |  |                 content: buffer.toString('base64'), | 
					
						
							|  |  |  |               }; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-28 12:11:19 -07:00
										 |  |  |             const ciphertextMessage = await this.getCiphertextMessage({ | 
					
						
							|  |  |  |               identityKeyStore, | 
					
						
							| 
									
										
										
										
											2021-05-04 18:03:03 -07:00
										 |  |  |               protocolAddress, | 
					
						
							|  |  |  |               sessionStore, | 
					
						
							| 
									
										
										
										
											2021-05-28 12:11:19 -07:00
										 |  |  |             }); | 
					
						
							| 
									
										
										
										
											2021-05-04 18:03:03 -07:00
										 |  |  |             const type = ciphertextMessageTypeToEnvelopeType( | 
					
						
							|  |  |  |               ciphertextMessage.type() | 
					
						
							|  |  |  |             ); | 
					
						
							| 
									
										
										
										
											2020-09-24 14:53:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-02 11:34:17 -07:00
										 |  |  |             const content = ciphertextMessage.serialize().toString('base64'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-04 18:03:03 -07:00
										 |  |  |             return { | 
					
						
							|  |  |  |               type, | 
					
						
							|  |  |  |               destinationDeviceId, | 
					
						
							|  |  |  |               destinationRegistrationId, | 
					
						
							| 
									
										
										
										
											2021-07-02 11:34:17 -07:00
										 |  |  |               content, | 
					
						
							| 
									
										
										
										
											2021-05-04 18:03:03 -07:00
										 |  |  |             }; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         ); | 
					
						
							| 
									
										
										
										
											2018-07-21 14:51:20 -07:00
										 |  |  |       }) | 
					
						
							| 
									
										
										
										
											2018-05-02 09:51:22 -07:00
										 |  |  |     ) | 
					
						
							| 
									
										
										
										
											2021-09-28 16:38:55 -07:00
										 |  |  |       .then(async (jsonData: Array<MessageType>) => { | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |         if (sealedSender) { | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |           return this.transmitMessage(serviceId, jsonData, this.timestamp, { | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |             accessKey, | 
					
						
							|  |  |  |           }).then( | 
					
						
							|  |  |  |             () => { | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |               this.recipients[serviceId] = deviceIds; | 
					
						
							|  |  |  |               this.unidentifiedDeliveries.push(serviceId); | 
					
						
							|  |  |  |               this.successfulServiceIds.push(serviceId); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |               this.numberCompleted(); | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |               if (this.sendLogCallback) { | 
					
						
							| 
									
										
										
										
											2022-12-21 10:41:48 -08:00
										 |  |  |                 void this.sendLogCallback({ | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |                   serviceId, | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |                   deviceIds, | 
					
						
							|  |  |  |                 }); | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |               } else if (this.successfulServiceIds.length > 1) { | 
					
						
							| 
									
										
										
										
											2021-09-17 14:27:53 -04:00
										 |  |  |                 log.warn( | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |                   `OutgoingMessage.doSendMessage: no sendLogCallback provided for message ${this.timestamp}, but multiple recipients` | 
					
						
							|  |  |  |                 ); | 
					
						
							|  |  |  |               } | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |             }, | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |             async (error: Error) => { | 
					
						
							| 
									
										
										
										
											2021-09-21 17:58:03 -07:00
										 |  |  |               if ( | 
					
						
							| 
									
										
										
										
											2023-06-20 17:06:38 -07:00
										 |  |  |                 error instanceof SendMessageNetworkError && | 
					
						
							| 
									
										
										
										
											2021-09-21 17:58:03 -07:00
										 |  |  |                 (error.code === 401 || error.code === 403) | 
					
						
							|  |  |  |               ) { | 
					
						
							| 
									
										
										
										
											2023-06-20 17:06:38 -07:00
										 |  |  |                 log.warn( | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |                   `OutgoingMessage.doSendMessage: Failing over to unsealed send for serviceId ${serviceId}` | 
					
						
							| 
									
										
										
										
											2023-06-20 17:06:38 -07:00
										 |  |  |                 ); | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |                 if (this.failoverServiceIds.indexOf(serviceId) === -1) { | 
					
						
							|  |  |  |                   this.failoverServiceIds.push(serviceId); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 // This ensures that we don't hit this codepath the next time through
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:38:36 -05:00
										 |  |  |                 if (sendMetadata) { | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |                   delete sendMetadata[serviceId]; | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |                 return this.doSendMessage(serviceId, deviceIds, recurse); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |               } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               throw error; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           ); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |         return this.transmitMessage(serviceId, jsonData, this.timestamp).then( | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |           () => { | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |             this.successfulServiceIds.push(serviceId); | 
					
						
							|  |  |  |             this.recipients[serviceId] = deviceIds; | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |             this.numberCompleted(); | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |             if (this.sendLogCallback) { | 
					
						
							| 
									
										
										
										
											2022-12-21 10:41:48 -08:00
										 |  |  |               void this.sendLogCallback({ | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |                 serviceId, | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |                 deviceIds, | 
					
						
							|  |  |  |               }); | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |             } else if (this.successfulServiceIds.length > 1) { | 
					
						
							| 
									
										
										
										
											2021-09-17 14:27:53 -04:00
										 |  |  |               log.warn( | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |                 `OutgoingMessage.doSendMessage: no sendLogCallback provided for message ${this.timestamp}, but multiple recipients` | 
					
						
							|  |  |  |               ); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |           } | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       }) | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |       .catch(async error => { | 
					
						
							| 
									
										
										
										
											2018-07-21 14:51:20 -07:00
										 |  |  |         if ( | 
					
						
							| 
									
										
										
										
											2021-09-21 17:58:03 -07:00
										 |  |  |           error instanceof HTTPError && | 
					
						
							| 
									
										
										
										
											2018-07-21 14:51:20 -07:00
										 |  |  |           (error.code === 410 || error.code === 409) | 
					
						
							|  |  |  |         ) { | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |           if (!recurse) { | 
					
						
							|  |  |  |             this.registerError( | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |               serviceId, | 
					
						
							| 
									
										
										
										
											2018-07-21 14:51:20 -07:00
										 |  |  |               'Hit retry limit attempting to reload device list', | 
					
						
							|  |  |  |               error | 
					
						
							| 
									
										
										
										
											2018-05-02 09:51:22 -07:00
										 |  |  |             ); | 
					
						
							| 
									
										
										
										
											2020-09-24 14:53:21 -07:00
										 |  |  |             return undefined; | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |           } | 
					
						
							| 
									
										
										
										
											2018-07-21 14:51:20 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-21 17:58:03 -07:00
										 |  |  |           const response = error.response as { | 
					
						
							|  |  |  |             extraDevices?: Array<number>; | 
					
						
							|  |  |  |             staleDevices?: Array<number>; | 
					
						
							|  |  |  |             missingDevices?: Array<number>; | 
					
						
							|  |  |  |           }; | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |           let p: Promise<any> = Promise.resolve(); | 
					
						
							| 
									
										
										
										
											2018-07-21 14:51:20 -07:00
										 |  |  |           if (error.code === 409) { | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |             p = this.removeDeviceIdsForServiceId( | 
					
						
							|  |  |  |               serviceId, | 
					
						
							| 
									
										
										
										
											2021-09-21 17:58:03 -07:00
										 |  |  |               response.extraDevices || [] | 
					
						
							| 
									
										
										
										
											2018-05-02 09:51:22 -07:00
										 |  |  |             ); | 
					
						
							|  |  |  |           } else { | 
					
						
							| 
									
										
										
										
											2018-07-21 14:51:20 -07:00
										 |  |  |             p = Promise.all( | 
					
						
							| 
									
										
										
										
											2021-09-21 17:58:03 -07:00
										 |  |  |               (response.staleDevices || []).map(async (deviceId: number) => { | 
					
						
							| 
									
										
										
										
											2021-04-16 16:13:13 -07:00
										 |  |  |                 await window.textsecure.storage.protocol.archiveSession( | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |                   new QualifiedAddress(ourAci, new Address(serviceId, deviceId)) | 
					
						
							| 
									
										
										
										
											2021-04-16 16:13:13 -07:00
										 |  |  |                 ); | 
					
						
							|  |  |  |               }) | 
					
						
							| 
									
										
										
										
											2018-05-02 09:51:22 -07:00
										 |  |  |             ); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |           return p.then(async () => { | 
					
						
							| 
									
										
										
										
											2018-07-21 14:51:20 -07:00
										 |  |  |             const resetDevices = | 
					
						
							|  |  |  |               error.code === 410 | 
					
						
							| 
									
										
										
										
											2021-09-21 17:58:03 -07:00
										 |  |  |                 ? response.staleDevices | 
					
						
							|  |  |  |                 : response.missingDevices; | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |             return this.getKeysForServiceId(serviceId, resetDevices).then( | 
					
						
							| 
									
										
										
										
											2018-10-31 16:58:14 -07:00
										 |  |  |               // We continue to retry as long as the error code was 409; the assumption is
 | 
					
						
							|  |  |  |               //   that we'll request new device info and the next request will succeed.
 | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |               this.reloadDevicesAndSend(serviceId, error.code === 409) | 
					
						
							| 
									
										
										
										
											2018-05-02 09:51:22 -07:00
										 |  |  |             ); | 
					
						
							| 
									
										
										
										
											2018-07-21 14:51:20 -07:00
										 |  |  |           }); | 
					
						
							| 
									
										
										
										
											2020-09-24 14:53:21 -07:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-11-22 10:43:43 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         let newError = error; | 
					
						
							|  |  |  |         if ( | 
					
						
							|  |  |  |           error instanceof LibSignalErrorBase && | 
					
						
							|  |  |  |           error.code === ErrorCode.UntrustedIdentity | 
					
						
							|  |  |  |         ) { | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |           newError = new OutgoingIdentityKeyError(serviceId, error); | 
					
						
							| 
									
										
										
										
											2021-09-17 14:27:53 -04:00
										 |  |  |           log.error( | 
					
						
							| 
									
										
										
										
											2018-07-21 14:51:20 -07:00
										 |  |  |             'Got "key changed" error from encrypt - no identityKey for application layer', | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |             serviceId, | 
					
						
							| 
									
										
										
										
											2018-07-21 14:51:20 -07:00
										 |  |  |             deviceIds | 
					
						
							|  |  |  |           ); | 
					
						
							| 
									
										
										
										
											2020-01-10 08:10:43 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |           log.info('closing all sessions for', serviceId); | 
					
						
							|  |  |  |           window.textsecure.storage.protocol.archiveAllSessions(serviceId).then( | 
					
						
							|  |  |  |             () => { | 
					
						
							|  |  |  |               throw error; | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             innerError => { | 
					
						
							|  |  |  |               log.error( | 
					
						
							|  |  |  |                 'doSendMessage: Error closing sessions: ' + | 
					
						
							|  |  |  |                   `${Errors.toLogFormat(innerError)}` | 
					
						
							|  |  |  |               ); | 
					
						
							|  |  |  |               throw error; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           ); | 
					
						
							| 
									
										
										
										
											2018-07-21 14:51:20 -07:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |         this.registerError( | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |           serviceId, | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |           'Failed to create or send message', | 
					
						
							| 
									
										
										
										
											2022-11-22 10:43:43 -08:00
										 |  |  |           newError | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |         ); | 
					
						
							| 
									
										
										
										
											2020-09-24 14:53:21 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return undefined; | 
					
						
							| 
									
										
										
										
											2018-05-02 09:51:22 -07:00
										 |  |  |       }); | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-05-02 09:51:22 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |   async removeDeviceIdsForServiceId( | 
					
						
							|  |  |  |     serviceId: ServiceIdString, | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |     deviceIdsToRemove: Array<number> | 
					
						
							| 
									
										
										
										
											2020-09-24 14:53:21 -07:00
										 |  |  |   ): Promise<void> { | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |     const ourAci = window.textsecure.storage.user.getCheckedAci(); | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-09 14:05:11 -08:00
										 |  |  |     await Promise.all( | 
					
						
							|  |  |  |       deviceIdsToRemove.map(async deviceId => { | 
					
						
							| 
									
										
										
										
											2021-04-16 16:13:13 -07:00
										 |  |  |         await window.textsecure.storage.protocol.archiveSession( | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |           new QualifiedAddress(ourAci, new Address(serviceId, deviceId)) | 
					
						
							| 
									
										
										
										
											2020-12-09 14:05:11 -08:00
										 |  |  |         ); | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-05-02 09:51:22 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |   async sendToServiceId(serviceId: ServiceIdString): Promise<void> { | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |     try { | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |       const ourAci = window.textsecure.storage.user.getCheckedAci(); | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  |       const deviceIds = await window.textsecure.storage.protocol.getDeviceIds({ | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |         ourServiceId: ourAci, | 
					
						
							|  |  |  |         serviceId, | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  |       }); | 
					
						
							| 
									
										
										
										
											2021-05-25 15:40:04 -07:00
										 |  |  |       if (deviceIds.length === 0) { | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |         await this.getKeysForServiceId(serviceId); | 
					
						
							| 
									
										
										
										
											2021-05-25 15:40:04 -07:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |       await this.reloadDevicesAndSend(serviceId, true)(); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |     } catch (error) { | 
					
						
							| 
									
										
										
										
											2022-11-22 10:43:43 -08:00
										 |  |  |       if ( | 
					
						
							|  |  |  |         error instanceof LibSignalErrorBase && | 
					
						
							|  |  |  |         error.code === ErrorCode.UntrustedIdentity | 
					
						
							|  |  |  |       ) { | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |         const newError = new OutgoingIdentityKeyError(serviceId, error); | 
					
						
							|  |  |  |         this.registerError(serviceId, 'Untrusted identity', newError); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |       } else { | 
					
						
							|  |  |  |         this.registerError( | 
					
						
							| 
									
										
										
										
											2023-08-10 18:43:33 +02:00
										 |  |  |           serviceId, | 
					
						
							|  |  |  |           `Failed to retrieve new device keys for serviceId ${serviceId}`, | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |           error | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | } |