/** * Group-chat channel key management (browser mirror of the Go * internal/channelkeys package), built on the unified ECIES one-to-many * primitive (eciesWrapForRecipients): a channel owns one symmetric content key, * membership is a set of member encryption keys, and the content key is wrapped * one-to-many to every member as an SDS $ENC/$KMF envelope. Membership changes * rekey the channel as required for forward secrecy. * * This is the key-management layer for WS9 encrypted pub/sub channel chat; the * message layer (AES-256-GCM under the content key) is built on top of it. */ import { EciesKeyExchange } from './ecies'; /** One channel participant's encryption identity. */ export interface ChannelMember { /** Stable member id (peer id / handle); stamped as RECIPIENT_KEY_ID. */ id: string; /** X25519 (32 bytes) or secp256k1 compressed (33 bytes), matching keyExchange. */ publicKey: Uint8Array; keyExchange: EciesKeyExchange; } /** One member's wrapped copy of the channel content key. */ export interface ChannelMemberEnvelope { memberId: string; epoch: number; encBytes: Uint8Array; kmfBytes: Uint8Array; } export interface ChannelKeysOptions { /** ECIES context (domain separator); defaults to a channel-scoped context. */ context?: string; /** Pin the initial content key (deterministic tests); defaults to random. */ contentKey?: Uint8Array; } /** * A keyed group-chat channel: an id, a rotating symmetric content key with an * epoch counter, a member set, and the ECIES context used when wrapping. */ export declare class ChannelKeys { readonly id: string; readonly context: string; private _epoch; private contentKey; private readonly members; private constructor(); /** * Create a channel with a freshly generated content key at epoch 1 and no * members. Add members with addMember, then wrapForMembers to mint envelopes. */ static create(id: string, options?: ChannelKeysOptions): Promise; get epoch(): number; /** A copy of the current content key for message encryption. */ getContentKey(): Uint8Array; /** The current member set, sorted by id. */ getMembers(): ChannelMember[]; /** * Add a member. Adding does NOT rotate the content key: the new member shares * the current key and can read current/future messages. Re-run wrapForMembers * to mint the new member's envelope. */ addMember(m: ChannelMember): void; /** * Remove a member and rekey the channel (fresh content key, epoch bumped) so * the removed member cannot read messages published afterward (forward * secrecy). Throws if the member is absent. */ removeMember(id: string): void; /** Rotate the content key and bump the epoch. */ rekey(): void; /** * Wrap the current content key one-to-many to every current member, returning * one $ENC/$KMF envelope per member (stamped with the member id as * RECIPIENT_KEY_ID and the current epoch). Throws if there are no members. */ wrapForMembers(): Promise; /** * Recover the channel content key from a member's envelope using the member's * private key. The context must match the channel's. */ static unwrapForMember(memberPrivateKey: Uint8Array, encBytes: Uint8Array, kmfBytes: Uint8Array, context: string): Promise; } /** A decrypted + signature-verified channel chat message. */ export interface ChannelMessage { plaintext: Uint8Array; senderPublicKey: Uint8Array; epoch: number; timestampMs: number; } export interface EncryptChannelMessageOptions { /** Pin the 12-byte GCM nonce (deterministic vectors); defaults to random. */ nonce?: Uint8Array; /** Sender clock in unix milliseconds; defaults to 0 (caller-stamped). */ timestampMs?: number; } /** * Seal a chat message for the channel: AES-256-GCM under the channel content * key with the $ENC header as AAD, signed by the sender's ed25519 key. * context/epoch must be the channel's current context/epoch. */ export declare function encryptChannelMessage(contentKey: Uint8Array, senderPrivateKey: Uint8Array, context: string, epoch: number, plaintext: Uint8Array, options?: EncryptChannelMessageOptions): Promise; /** * Open a channel chat envelope with the channel content key, verifying the * sender signature and the AAD-bound header. expectedContext must match the * channel context ('' accepts the header's context). */ export declare function decryptChannelMessage(contentKey: Uint8Array, envelope: Uint8Array, expectedContext: string): Promise; /** * Gossipsub topic for a channel's encrypted chat, mirroring Go * channelkeys.ChatTopic and the CHANNEL_TOPIC_PREFIX convention. */ export declare function channelChatTopic(channelId: string): string; //# sourceMappingURL=channel-keys.d.ts.map