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
TypeScript
/// <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;
}