@signalapp/mock-server
Version:
Mock Signal Server for writing tests
305 lines (304 loc) • 14.3 kB
TypeScript
/// <reference types="node" />
/// <reference types="node" />
/// <reference types="node" />
import { SenderCertificate } from '@signalapp/libsignal-client';
import { AuthCredentialPresentation, CreateCallLinkCredentialRequest, CreateCallLinkCredentialResponse, GenericServerSecretParams, ProfileKeyCredentialRequest, ServerSecretParams } from '@signalapp/libsignal-client/zkgroup';
import https from 'https';
import Long from 'long';
import { AddressInfo } from 'net';
import { signalservice as Proto } from '../../protos/compiled';
import { ServerCertificate } from '../crypto';
import { ChangeNumberOptions, Device, DeviceKeys } from '../data/device';
import { BackupHeaders, BackupMediaBatch, CreateCallLink, DeleteCallLink, Message, SetBackupId, SetBackupKey, UpdateCallLink, UsernameConfirmation, UsernameReservation } from '../data/schemas';
import { AciString, AttachmentId, DeviceId, PniString, ProvisionIdString, ProvisioningCode, RegistrationId, ServiceIdKind, ServiceIdString } from '../types';
import { ModifyGroupResult, ServerGroup } from './group';
export declare enum EnvelopeType {
CipherText = "CipherText",
Plaintext = "Plaintext",
PreKey = "PreKey",
SealedSender = "SealedSender",
SenderKey = "SenderKey"
}
export type ProvisioningResponse = Readonly<{
envelope: Buffer;
}>;
export type CredentialsRange = Readonly<{
from: number;
to: number;
}>;
export type StorageCredentials = Readonly<{
username: string;
password: string;
}>;
export type Credentials = Array<{
credential: string;
redemptionTime: number;
}>;
export type BackupCredentials = Readonly<{
messages: Credentials;
media: Credentials;
}>;
export type ChallengeResponse = Readonly<{
code: number;
data: unknown;
}>;
export type PreparedMultiDeviceMessage = ReadonlyArray<[Device, Message]>;
export type ProvisionDeviceOptions = Readonly<{
number: string;
password: string;
provisioningCode: ProvisioningCode;
registrationId: RegistrationId;
pniRegistrationId: RegistrationId;
}>;
export type RegisterDeviceOptions = Readonly<({
primary?: undefined;
provisionId?: ProvisionIdString;
number: string;
password: string;
} | {
primary: Device;
provisionId?: undefined;
number?: undefined;
password?: string;
}) & {
registrationId: RegistrationId;
pniRegistrationId: RegistrationId;
}>;
export type PrepareMultiDeviceMessageResult = Readonly<{
status: 'stale';
staleDevices: ReadonlyArray<number>;
} | {
status: 'incomplete';
missingDevices: ReadonlyArray<number>;
extraDevices: ReadonlyArray<number>;
} | {
status: 'unknown';
} | {
status: 'ok';
targetServiceId: ServiceIdString;
result: PreparedMultiDeviceMessage;
}>;
export type ConfirmUsernameResult = Readonly<{
usernameLinkHandle?: string;
}>;
export type SetUsernameLinkResult = Readonly<{
entropy: Uint8Array;
serverId: string;
}>;
export type StorageWriteResult = Readonly<{
updated: false;
manifest: Proto.IStorageManifest;
error?: void;
} | {
updated: true;
manifest?: void;
error?: void;
} | {
updated?: void;
error: string;
}>;
export type ModifyGroupOptions = Readonly<{
group: ServerGroup;
actions: Proto.GroupChange.IActions;
aciCiphertext: Uint8Array;
pniCiphertext: Uint8Array;
}>;
export type EncryptedStickerPack = Readonly<{
id: Buffer;
manifest: Buffer;
stickers: ReadonlyArray<Buffer>;
}>;
export type IsSendRateLimitedOptions = Readonly<{
source: ServiceIdString;
target: ServiceIdString;
}>;
export { type ModifyGroupResult };
interface WebSocket {
sendMessage(message: Buffer | 'empty'): Promise<void>;
}
export type CallLinkEntry = Readonly<{
adminPasskey: Buffer;
encryptedName: string;
restrictions: 'none' | 'adminApproval';
revoked: boolean;
expiration: number;
}>;
export type BackupInfo = Readonly<{
cdn: 3;
backupDir: string;
mediaDir: string;
backupName: string;
usedSpace?: number;
}>;
export type BackupMediaObject = Readonly<{
cdn: 3;
mediaId: string;
objectLength: number;
}>;
export type BackupMediaList = Readonly<{
storedMediaObjects: ReadonlyArray<BackupMediaObject>;
backupDir: string;
mediaDir: string;
cursor: string | undefined;
}>;
export type BackupMediaCursor = {
readonly backupId: string;
remainingMedia: ReadonlyArray<BackupMediaObject>;
};
export type ListBackupMediaOptions = Readonly<{
cursor: string | undefined;
limit: number;
}>;
export type BackupMediaBatchResponse = Readonly<{
status: number;
failureReason?: string;
cdn: 3;
mediaId: string;
}>;
export type BackupMediaBatchResult = Readonly<{
responses: ReadonlyArray<BackupMediaBatchResponse>;
}>;
export type TransferArchiveResponse = Readonly<{
error: 'RELINK_REQUESTED' | 'CONTINUE_WITHOUT_UPLOAD';
} | {
cdn: 3;
key: string;
}>;
export type AttachmentUploadForm = Readonly<{
cdn: 3;
key: string;
headers: Record<string, string>;
signedUploadLocation: string;
}>;
export type RemoteConfigValueType = {
enabled: boolean;
value?: string;
};
export declare abstract class Server {
private readonly devices;
private readonly devicesByServiceId;
private readonly devicesByAuth;
private readonly usedServiceIds;
private readonly usedProvisionIds;
private readonly storageAuthByUsername;
private readonly storageAuthByDevice;
private readonly storageManifestByAci;
private readonly storageItemsByAci;
private readonly provisioningCodes;
private readonly attachments;
private readonly stickerPacks;
private readonly webSockets;
private readonly messageQueue;
private readonly groups;
private readonly aciByUsername;
private readonly aciByReservedUsername;
private readonly usernameByAci;
private readonly reservedUsernameByAci;
private readonly usernameLinkIdByServiceId;
private readonly usernameLinkById;
private readonly callLinksByRoomId;
private readonly backupAuthReqByAci;
private readonly backupKeyById;
private readonly backupCDNPasswordById;
private readonly backupMediaById;
private readonly backupMediaCursorById;
private readonly remoteConfig;
protected privCertificate: ServerCertificate | undefined;
protected privZKSecret: ServerSecretParams | undefined;
protected privGenericServerSecret: GenericServerSecretParams | undefined;
protected privBackupServerSecret: GenericServerSecretParams | undefined;
protected https: https.Server | undefined;
address(): AddressInfo;
generateAci(): Promise<AciString>;
generatePni(): Promise<PniString>;
generateProvisionId(): Promise<ProvisionIdString>;
releaseProvisionId(id: ProvisionIdString): Promise<void>;
abstract getProvisioningResponse(id: ProvisionIdString): Promise<ProvisioningResponse>;
registerDevice({ primary, provisionId, number: maybeNumber, registrationId, pniRegistrationId, password, }: RegisterDeviceOptions): Promise<Device>;
getProvisioningCode(id: ProvisionIdString, number: string): Promise<ProvisioningCode>;
provisionDevice({ number, password, provisioningCode, registrationId, pniRegistrationId, }: ProvisionDeviceOptions): Promise<Device>;
private setDeviceAuthPassword;
updateDeviceKeys(device: Device, serviceIdKind: ServiceIdKind, keys: Omit<DeviceKeys, 'identityKey'>): Promise<void>;
changeDeviceNumber(device: Device, options: ChangeNumberOptions): Promise<void>;
auth(username: string, password: string): Promise<Device | undefined>;
setRemoteConfig(key: string, value: RemoteConfigValueType): void;
getRemoteConfig(): Map<string, RemoteConfigValueType>;
protected storeAttachment(attachment: Buffer): Promise<AttachmentId>;
fetchAttachment(id: AttachmentId): Promise<Buffer | undefined>;
fetchStickerPack(packId: string): Promise<Buffer | undefined>;
fetchSticker(packId: string, stickerId: number): Promise<Buffer | undefined>;
storeStickerPack(pack: EncryptedStickerPack): Promise<void>;
getAttachmentUploadForm(folder: string, key: string): Promise<AttachmentUploadForm>;
prepareMultiDeviceMessage(source: Device | undefined, targetServiceId: ServiceIdString, messages: ReadonlyArray<Message>): Promise<PrepareMultiDeviceMessageResult>;
handlePreparedMultiDeviceMessage(source: Device | undefined, targetServiceId: ServiceIdString, prepared: PreparedMultiDeviceMessage): Promise<void>;
abstract handleMessage(source: Device | undefined, serviceIdKind: ServiceIdKind, envelopeType: EnvelopeType, target: Device, encrypted: Buffer): Promise<void>;
addWebSocket(device: Device, socket: WebSocket): Promise<void>;
removeWebSocket(device: Device, socket: WebSocket): void;
send(target: Device, message: Buffer): Promise<void>;
createGroup(group: Proto.IGroup): Promise<ServerGroup>;
modifyGroup({ group, actions, aciCiphertext, pniCiphertext, }: ModifyGroupOptions): Promise<ModifyGroupResult>;
getGroup(publicParams: Buffer): Promise<ServerGroup | undefined>;
getStorageAuth(device: Device): Promise<StorageCredentials>;
storageAuth(username: string, password: string): Promise<Device | undefined>;
getStorageManifest(device: Device): Promise<Proto.IStorageManifest | undefined>;
applyStorageWrite(device: Device, { manifest, clearAll, insertItem, deleteKey }: Proto.IWriteOperation, shouldNotify?: boolean): Promise<StorageWriteResult>;
private clearStorageItems;
private setStorageItem;
getStorageItem(device: Device, key: Buffer): Promise<Buffer | undefined>;
getAllStorageKeys(device: Device): Promise<Array<Buffer>>;
getStorageItems(device: Device, keys: ReadonlyArray<Buffer>): Promise<Array<Proto.IStorageItem> | undefined>;
deleteStorageItem(device: Device, key: Buffer): Promise<void>;
protected abstract onStorageManifestUpdate(device: Device, version: Long): Promise<void>;
reserveUsername(aci: AciString, { usernameHashes }: UsernameReservation): Promise<Buffer | undefined>;
confirmUsername(aci: AciString, { usernameHash, zkProof, encryptedUsername }: UsernameConfirmation): Promise<ConfirmUsernameResult | undefined>;
deleteUsername(aci: AciString): Promise<void>;
lookupByUsernameHash(usernameHash: Buffer): Promise<AciString | undefined>;
replaceUsernameLink(aci: AciString, encryptedValue: Buffer): Promise<string>;
lookupByUsernameLink(lookupId: string): Promise<Buffer | undefined>;
lookupByUsername(username: string): Promise<AciString | undefined>;
setUsername(aci: AciString, username: string): Promise<void>;
setUsernameLink(aci: AciString, username: string): Promise<SetUsernameLinkResult>;
createCallLinkAuth(device: Device, request: CreateCallLinkCredentialRequest): Promise<CreateCallLinkCredentialResponse>;
hasCallLink(roomId: string): boolean;
createCallLink(roomId: string, { adminPasskey }: CreateCallLink): Promise<CallLinkEntry>;
getCallLink(roomId: string): Promise<CallLinkEntry | undefined>;
updateCallLink(roomId: string, { adminPasskey, name, restrictions, revoked }: UpdateCallLink): Promise<CallLinkEntry>;
deleteCallLink(roomId: string, { adminPasskey }: DeleteCallLink): Promise<void>;
getDevice(number: string, deviceId: DeviceId): Promise<Device | undefined>;
removeDevice(number: string, deviceId: DeviceId): Promise<void>;
getDeviceByServiceId(serviceId: ServiceIdString, deviceId?: DeviceId): Promise<Device | undefined>;
getAllDevicesByServiceId(serviceId: ServiceIdString): Promise<ReadonlyArray<Device>>;
getSenderCertificate(device: Device): Promise<SenderCertificate>;
getGroupCredentials({ aci, pni }: Device, range: CredentialsRange): Promise<Credentials>;
verifyGroupCredentials(publicParams: Buffer, credential: Buffer): Promise<AuthCredentialPresentation>;
getCallLinkAuthCredentials({ aci }: Device, range: CredentialsRange): Promise<Credentials>;
issueExpiringProfileKeyCredential({ aci, profileKeyCommitment }: Device, request: ProfileKeyCredentialRequest): Promise<Buffer | undefined>;
setBackupId({ aci }: Device, { messagesBackupAuthCredentialRequest, mediaBackupAuthCredentialRequest, }: SetBackupId): Promise<void>;
setBackupKey(headers: BackupHeaders, { backupIdPublicKey }: SetBackupKey): Promise<void>;
refreshBackup(headers: BackupHeaders): Promise<void>;
getBackupInfo(headers: BackupHeaders): Promise<BackupInfo>;
listBackupMedia(headers: BackupHeaders, { cursor, limit }: ListBackupMediaOptions): Promise<BackupMediaList>;
getBackupMediaUploadForm(headers: BackupHeaders): Promise<AttachmentUploadForm>;
getBackupUploadForm(headers: BackupHeaders): Promise<AttachmentUploadForm>;
backupMediaBatch(headers: BackupHeaders, batch: BackupMediaBatch): Promise<BackupMediaBatchResult>;
getBackupCDNAuth(headers: BackupHeaders): Promise<Record<string, string>>;
authorizeBackupCDN(backupId: string, password: string): Promise<boolean>;
getBackupCredentials({ aci, backupLevel }: Device, range: CredentialsRange): Promise<BackupCredentials | undefined>;
protected onNewBackupMediaObject(backupId: string, media: BackupMediaObject): Promise<void>;
protected abstract backupTransitAttachments(backupId: string, batch: BackupMediaBatch): Promise<Array<BackupMediaBatchResponse>>;
abstract getTransferArchive(device: Device): Promise<TransferArchiveResponse>;
abstract isUnregistered(serviceId: ServiceIdString): boolean;
abstract isSendRateLimited(options: IsSendRateLimitedOptions): boolean;
abstract getResponseForChallenges(): ChallengeResponse | undefined;
protected set certificate(value: ServerCertificate);
protected get certificate(): ServerCertificate;
protected set genericServerSecret(value: GenericServerSecretParams);
protected get genericServerSecret(): GenericServerSecretParams;
protected set backupServerSecret(value: GenericServerSecretParams);
protected get backupServerSecret(): GenericServerSecretParams;
protected set zkSecret(value: ServerSecretParams);
protected get zkSecret(): ServerSecretParams;
private sendQueue;
private issueCredentials;
private authenticateBackup;
}