UNPKG

pocket-messaging

Version:

A small cryptographic messaging library written in TypeScript both for browser and nodejs supporting TCP and WebSockets

223 lines (222 loc) 7.93 kB
/// <reference types="node" /> import EventEmitter from "eventemitter3"; import { SocketFactoryConfig, SocketFactoryStats, SocketFactoryInterface, ClientInterface, WrappedClientInterface } from "pocket-sockets"; export declare const DEFAULT_PING_INTERVAL = 10000; /** Msg ID length in bytes. */ export declare const MSG_ID_LENGTH = 4; export declare const PING_ROUTE = "_ping"; export declare const PONG_ROUTE = "_pong"; /** * A single message cannot exceed 67 KiB in total for its payload. * * 67 KiB allows the sender to send a payload of 64 KiB with plenty * of space left for its overhead, and 64 KiB is typically a * fitting payload to deal with when reading/storing data. * * Actual data sent comes with some bytes added overhead. */ export declare const MESSAGE_MAX_BYTES: number; export declare type SendReturn = { eventEmitter?: EventEmitter; msgId: Buffer; }; export declare type SentMessage = { timestamp: number; msgId: Buffer; timeout: number; stream: boolean; timeoutStream: number; eventEmitter: EventEmitter; replyCounter: number; isCleared: boolean; }; export declare enum ExpectingReply { NONE = 0, SINGLE = 1, MULTIPLE = 2 } /** * Bytes: * 0 uint8 header version, must be 0 * 1-4 uint32le total length of message including version byte above * 5 uint8 config byte, used for expectingReply flags * 6-9 4 bytes msg ID * 10 uint8 length of target value * 10 x bytes of target value * 10+x data bytes */ export declare type Header = { version: number; target: Buffer; msgId: Buffer; dataLength: number; /** * Only bit 0+1 are used to signal expectingReply */ config: number; }; export declare type OutgoingQueue = { chunks: Buffer[]; }; export declare type InMessage = { target: Buffer; msgId: Buffer; data: Buffer; /** * 0 no reply expected * 1 one reply expected * 2 multiple replies expected */ expectingReply: number; }; export declare type IncomingQueue = { chunks: Buffer[]; messages: InMessage[]; }; export declare type RouteEvent = { target: string; fromMsgId: Buffer; data: Buffer; expectingReply: number; }; export declare type ReplyEvent = { toMsgId: Buffer; fromMsgId: Buffer; data: Buffer; expectingReply: number; }; export declare type TimeoutEvent = Record<string, never>; export declare type ErrorEvent = { error: string; }; export declare type AnyEvent = { type: EventType; event: ReplyEvent | TimeoutEvent | CloseEvent | ErrorEvent; }; export declare type CloseEvent = { hadError: boolean; }; export declare type PongEvent = { roundTripTime: number; }; export declare enum EventType { /** * Data event only emitted on main event emitter on new * incoming messages (which are not reply messages). */ ROUTE = "route", /** * Data event only emitted on message specific event emitters as * replies on sent message. */ REPLY = "reply", /** * Socket error event emitted on all event emitters including main. */ ERROR = "error", /** * Socket close event emitted on all event emitters including main. */ CLOSE = "close", /** * Message reply timeout event emitted on message specific event emitters * who are awaiting replies on sent message. */ TIMEOUT = "timeout", /** * Any event emitted on message specific event emitters for the events: * REPLY, * ERROR, * CLOSE, * TIMEOUT * This is useful for having a catch-all event handler when waiting on replies. */ ANY = "any", /** * Event emitted only on main event emitter when a ping has received its pong message. * It provides the round-time in milliseconds as data. */ PONG = "ping" } export declare type HandshakeResult = { longtermPk: Buffer; peerLongtermPk: Buffer; clientToServerKey: Buffer; clientNonce: Buffer; serverToClientKey: Buffer; serverNonce: Buffer; clockDiff: number; peerData: Buffer; }; export declare type KeyPair = { publicKey: Buffer; secretKey: Buffer; }; export declare type ClientValidatorFunctionInterface = (clientLongTermPk: Buffer) => boolean; export declare type PeerDataGeneratorFunctionInterface = (isServer: boolean) => Buffer; export declare type HandshakeFactoryConfig = { socketFactoryConfig: SocketFactoryConfig; socketFactoryStats?: SocketFactoryStats; /** The keypair to use in the cryptographic handshake. */ keyPair: KeyPair; /** The discriminator which must match the peer's discriminator when handshaking. */ discriminator: Buffer; /** * Arbitrary data sent to the other peer. * If a function then call it to get the peerData buffer. */ peerData?: Buffer | PeerDataGeneratorFunctionInterface; /** If connecting as client the public key of the server must be set. */ serverPublicKey?: Buffer; /** * If opening a server we can discriminate on peers public keys in the handshake. * If set as array the client public key must be in the array to accept to client. * If set as function the function must return boolean true to accept the client. * If not set (undefined) then all clients are accepted. * */ allowedClients?: Buffer[] | ClientValidatorFunctionInterface; /** If set, the maximum no of connections each cryptographically handshaked publicKey is allowed. */ maxConnectionsPerClient?: number; /** * If set, the maximum no of connections per connected client pair. * This is useful for peer-to-peer clients who both have client and server sockets * and we only desire a single connection between the client pair. * When setting maxConnectionsPerClient=1 there can still be two connections open, * but when setting maxConnectionsPerClientPair=1 we can cap the no of connectio to one per pair of clients. */ maxConnectionsPerClientPair?: number; /** * If set > 0 then the Messaging instances will be configured to * frequently send pings to detect silent disconnects of the underlying socket. * Unit is milliseconds. */ pingInterval?: number; }; export declare const EVENT_HANDSHAKEFACTORY_HANDSHAKE = "HANDSHAKE"; export declare const EVENT_HANDSHAKEFACTORY_HANDSHAKE_ERROR = "HANDSHAKE_ERROR"; export declare const EVENT_HANDSHAKEFACTORY_PUBLICKEY_OVERFLOW = "PUBLICKEY_OVERFLOW"; export declare type HandshakeFactoryPublicKeyOverflowCallback = (publicKey: Buffer) => void; /** * Event emitted when client is successfully handshaked and setup for encryption. * The client returned is the original client socket. * The wrappedClient is prepared for being used with encryption but it must be called * with await init() before using, if wanting to the encrypted client. */ export declare type HandshakeFactoryHandshakeCallback = (isServer: boolean, client: ClientInterface, wrappedClient: WrappedClientInterface, handshakeResult: HandshakeResult) => void; /** * Event emitted when client could not handshake. * An error occurred in the handshake process (the Error provided). */ export declare type HandshakeFactoryHandshakeErrorCallback = (error: Error) => void; export interface HandshakeFactoryInterface extends SocketFactoryInterface { getHandshakeFactoryConfig(): HandshakeFactoryConfig; /** A successful handshake. */ onHandshake(callback: HandshakeFactoryHandshakeCallback): void; /** An error in the handshake process, could be wrong key, wrong protocol format, etc. Unrecoverable. */ onHandshakeError(callback: HandshakeFactoryHandshakeErrorCallback): void; /** * Too many connections detected for specific connected peer public key. * The connection is closed and this event triggered. */ onPublicKeyOverflow(callback: HandshakeFactoryPublicKeyOverflowCallback): void; }