Files
libsignal/node/ts/SealedSenderMultiRecipientMessage.ts
Jordan Rose 85686caa01 node: Combine Native.js and .d.ts into Native.ts
This allows the file to be checked by tsc, which would have caught
some of the missing type aliases sooner (now added to Native.ts.in).
Strictly speaking the behavior is slightly different: we have returned
to exporting many items individually instead of collecting them on a
single object.

Co-authored-by: Alex Bakon <akonradi@signal.org>
2025-10-15 17:50:37 -07:00

88 lines
3.0 KiB
TypeScript

//
// Copyright 2024 Signal Messenger, LLC.
// SPDX-License-Identifier: AGPL-3.0-only
//
import { Buffer } from 'node:buffer';
import * as Native from './Native.js';
/**
* A single recipient parsed from a {@link SealedSenderMultiRecipientMessage}.
*
* The `deviceIds` and `registrationIds` arrays are parallel (so the first entry of each belongs to
* one device, the second to another, and so on).
*/
export interface Recipient {
deviceIds: number[];
registrationIds: number[];
}
/**
* A parsed Sealed Sender v2 "SentMessage", ready to be fanned out to multiple recipients.
*
* The implementation assumes that every device for a particular recipient should use the same key
* material.
*/
export default class SealedSenderMultiRecipientMessage {
readonly _buffer: Uint8Array;
readonly _recipientMap: {
[serviceId: string]: Native.SealedSenderMultiRecipientMessageRecipient;
};
readonly _excludedRecipients: string[];
readonly _offsetOfSharedData: number;
constructor(buffer: Uint8Array) {
const { recipientMap, excludedRecipients, offsetOfSharedData } =
Native.SealedSenderMultiRecipientMessage_Parse(buffer);
this._buffer = buffer;
this._recipientMap = recipientMap;
this._excludedRecipients = excludedRecipients;
this._offsetOfSharedData = offsetOfSharedData;
}
/**
* Returns the recipients parsed from the message, keyed by service ID string.
*
* The result has no keys other than the service IDs of the recipients.
*/
recipientsByServiceIdString(): Readonly<{ [serviceId: string]: Recipient }> {
return this._recipientMap;
}
/**
* Returns the service IDs of recipients excluded from receiving the message.
*
* This is enforced to be disjoint from the recipients in {@link #recipientsByServiceIdString}; it
* may be used for authorization purposes or just to check that certain recipients were
* deliberately excluded rather than accidentally.
*/
excludedRecipientServiceIdStrings(): ReadonlyArray<string> {
return this._excludedRecipients;
}
/**
* Returns the Sealed Sender V2 "ReceivedMessage" payload for delivery to a particular recipient.
*
* `recipient` must be one of the recipients in the map returned by
* {@link #recipientsByServiceIdString}. The same payload should be sent to all of the recipient's
* devices.
*/
messageForRecipient(recipient: Recipient): Uint8Array {
const nativeRecipient =
recipient as Native.SealedSenderMultiRecipientMessageRecipient;
// Use Buffer.concat for convenience, but return a proper Uint8Array, both for the correct type
// and to make an independent copy of a possibly-reused buffer.
return new Uint8Array(
Buffer.concat([
Buffer.of(0x22), // The "original" Sealed Sender V2 version
this._buffer.subarray(
nativeRecipient.rangeOffset,
nativeRecipient.rangeOffset + nativeRecipient.rangeLen
),
this._buffer.subarray(this._offsetOfSharedData),
])
);
}
}