@towns-protocol/sdk
Version:
For more details, visit the following resources:
199 lines • 7.67 kB
TypeScript
import TypedEmitter from 'typed-emitter';
import { Permission } from '@towns-protocol/web3';
import { AddEventResponse_Error, EncryptedData, SessionKeys, UserInboxPayload_GroupEncryptionSessions } from '@towns-protocol/proto';
import { DLogger } from '@towns-protocol/dlog';
import { GroupEncryptionAlgorithmId, GroupEncryptionSession, UserDevice, GroupEncryptionCrypto } from '@towns-protocol/encryption';
export interface EntitlementsDelegate {
isEntitled(spaceId: string | undefined, channelId: string | undefined, user: string, permission: Permission): Promise<boolean>;
}
export declare enum DecryptionStatus {
initializing = "initializing",
updating = "updating",
working = "working",
idle = "idle",
done = "done"
}
export type DecryptionEvents = {
decryptionExtStatusChanged: (status: DecryptionStatus) => void;
};
export interface NewGroupSessionItem {
streamId: string;
sessions: UserInboxPayload_GroupEncryptionSessions;
}
export interface EncryptedContentItem {
streamId: string;
eventId: string;
kind: string;
encryptedData: EncryptedData;
}
export interface KeySolicitationContent {
deviceKey: string;
fallbackKey: string;
isNewDevice: boolean;
sessionIds: string[];
}
export interface EventSignatureBundle {
hash: Uint8Array;
signature: Uint8Array | undefined;
event: {
creatorAddress: Uint8Array;
delegateSig: Uint8Array;
delegateExpiryEpochMs: bigint;
};
}
export interface KeySolicitationItem {
streamId: string;
fromUserId: string;
fromUserAddress: Uint8Array;
solicitation: KeySolicitationContent;
respondAfter: number;
sigBundle: EventSignatureBundle;
hashStr: string;
}
export interface KeySolicitationData {
streamId: string;
isNewDevice: boolean;
missingSessionIds: string[];
}
export interface KeyFulfilmentData {
streamId: string;
userAddress: Uint8Array;
deviceKey: string;
sessionIds: string[];
}
export interface GroupSessionsData {
streamId: string;
item: KeySolicitationItem;
sessions: GroupEncryptionSession[];
algorithm: GroupEncryptionAlgorithmId;
}
export interface DecryptionSessionError {
missingSession: boolean;
kind: string;
encryptedData: EncryptedData;
error?: unknown;
}
/**
*
* Responsibilities:
* 1. Download new to-device messages that happened while we were offline
* 2. Decrypt new to-device messages
* 3. Decrypt encrypted content
* 4. Retry decryption failures, request keys for failed decryption
* 5. Respond to key solicitations
*
*
* Notes:
* If in the future we started snapshotting the eventNum of the last message sent by every user,
* we could use that to determine the order we send out keys, and the order that we reply to key solicitations.
*
* It should be easy to introduce a priority stream, where we decrypt messages from that stream first, before
* anything else, so the messages show up quicky in the ui that the user is looking at.
*
* We need code to purge bad sessions (if someones sends us the wrong key, or a key that doesn't decrypt the message)
*/
export declare abstract class BaseDecryptionExtensions {
private _status;
private mainQueues;
private streamQueues;
private upToDateStreams;
private highPriorityIds;
private recentStreamIds;
private decryptionFailures;
private inProgressTick?;
private timeoutId?;
private delayMs;
private started;
private numRecentStreamIds;
private emitter;
protected _onStopFn?: () => void;
protected log: {
debug: DLogger;
info: DLogger;
error: DLogger;
};
readonly crypto: GroupEncryptionCrypto;
readonly entitlementDelegate: EntitlementsDelegate;
readonly userDevice: UserDevice;
readonly userId: string;
constructor(emitter: TypedEmitter<DecryptionEvents>, crypto: GroupEncryptionCrypto, entitlementDelegate: EntitlementsDelegate, userDevice: UserDevice, userId: string, upToDateStreams: Set<string>, inLogId: string);
abstract ackNewGroupSession(session: UserInboxPayload_GroupEncryptionSessions): Promise<void>;
abstract decryptGroupEvent(streamId: string, eventId: string, kind: string, encryptedData: EncryptedData): Promise<void>;
abstract downloadNewMessages(): Promise<void>;
abstract getKeySolicitations(streamId: string): KeySolicitationContent[];
abstract hasStream(streamId: string): boolean;
abstract isUserEntitledToKeyExchange(streamId: string, userId: string, opts?: {
skipOnChainValidation: boolean;
}): Promise<boolean>;
abstract isValidEvent(item: KeySolicitationItem): {
isValid: boolean;
reason?: string;
};
abstract isUserInboxStreamUpToDate(upToDateStreams: Set<string>): boolean;
abstract onDecryptionError(item: EncryptedContentItem, err: DecryptionSessionError): void;
abstract sendKeySolicitation(args: KeySolicitationData): Promise<void>;
abstract sendKeyFulfillment(args: KeyFulfilmentData): Promise<{
error?: AddEventResponse_Error;
}>;
abstract encryptAndShareGroupSessions(args: GroupSessionsData): Promise<void>;
abstract shouldPauseTicking(): boolean;
/**
* uploadDeviceKeys
* upload device keys to the server
*/
abstract uploadDeviceKeys(): Promise<void>;
abstract getPriorityForStream(streamId: string, highPriorityIds: Set<string>, recentStreamIds: Set<string>): number;
enqueueNewGroupSessions(sessions: UserInboxPayload_GroupEncryptionSessions, _senderId: string): void;
enqueueNewEncryptedContent(streamId: string, eventId: string, kind: string, // kind of encrypted data
encryptedData: EncryptedData): void;
enqueueInitKeySolicitations(streamId: string, eventHashStr: string, members: {
userId: string;
userAddress: Uint8Array;
solicitations: KeySolicitationContent[];
}[], sigBundle: EventSignatureBundle): void;
enqueueKeySolicitation(streamId: string, eventHashStr: string, fromUserId: string, fromUserAddress: Uint8Array, keySolicitation: KeySolicitationContent, sigBundle: EventSignatureBundle): void;
setStreamUpToDate(streamId: string): void;
resetUpToDateStreams(): void;
retryDecryptionFailures(streamId: string): void;
start(): void;
enqueueNewMessageDownload(): void;
onStart(): void;
stop(): Promise<void>;
onStop(): Promise<void>;
get status(): DecryptionStatus;
private setStatus;
private compareStreamIds;
private lastPrintedAt;
protected checkStartTicking(): void;
private stopTicking;
private getDelayMs;
private tick;
/**
* processNewGroupSession
* process new group sessions that were sent to our to device stream inbox
* re-enqueue any decryption failures with matching session id
*/
private processNewGroupSession;
/**
* processEncryptedContentItem
* try to decrypt encrytped content
*/
private processEncryptedContentItem;
/**
* processMissingKeys
* process missing keys and send key solicitations to streams
*/
private processMissingKeys;
/**
* processKeySolicitation
* process incoming key solicitations and send keys and key fulfillments
*/
private processKeySolicitation;
/**
* can be overridden to add a delay to the key solicitation response
*/
getRespondDelayMSForKeySolicitation(_streamId: string, _userId: string): number;
setHighPriorityStreams(streamIds: string[]): void;
}
export declare function makeSessionKeys(sessions: GroupEncryptionSession[]): SessionKeys;
//# sourceMappingURL=decryptionExtensions.d.ts.map