pocket-messaging
Version:
A small cryptographic messaging library written in TypeScript both for browser and nodejs supporting TCP and WebSockets
143 lines (142 loc) • 6.82 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.HandshakeFactory = void 0;
const pocket_sockets_1 = require("pocket-sockets");
const types_1 = require("./types");
const Handshake_1 = require("./Handshake");
const EncryptedClient_1 = require("./EncryptedClient");
/**
* This class extends the SocketFactory with handshake capabilties.
* The SocketFactory EVENTS objects is redeclared here and extended.
*/
class HandshakeFactory extends pocket_sockets_1.SocketFactory {
constructor(handshakeFactoryConfig) {
super(handshakeFactoryConfig.socketFactoryConfig, handshakeFactoryConfig.socketFactoryStats);
this.handleOnConnect = async (client, isServer) => {
try {
let handshakeResult;
let peerData;
if (typeof this.handshakeFactoryConfig.peerData === "function") {
peerData = this.handshakeFactoryConfig.peerData(isServer);
}
else {
peerData = this.handshakeFactoryConfig.peerData;
}
let encryptedClient;
if (isServer) {
handshakeResult = await (0, Handshake_1.HandshakeAsServer)(client, this.handshakeFactoryConfig.keyPair.secretKey, this.handshakeFactoryConfig.keyPair.publicKey, this.handshakeFactoryConfig.discriminator, this.handshakeFactoryConfig.allowedClients, peerData);
encryptedClient = new EncryptedClient_1.EncryptedClient(client, handshakeResult.serverToClientKey, handshakeResult.serverNonce, handshakeResult.clientToServerKey, handshakeResult.clientNonce, handshakeResult.peerLongtermPk);
}
else {
if (!this.handshakeFactoryConfig.serverPublicKey) {
client.close();
return;
}
handshakeResult = await (0, Handshake_1.HandshakeAsClient)(client, this.handshakeFactoryConfig.keyPair.secretKey, this.handshakeFactoryConfig.keyPair.publicKey, this.handshakeFactoryConfig.serverPublicKey, this.handshakeFactoryConfig.discriminator, peerData);
encryptedClient = new EncryptedClient_1.EncryptedClient(client, handshakeResult.clientToServerKey, handshakeResult.clientNonce, handshakeResult.serverToClientKey, handshakeResult.serverNonce, handshakeResult.peerLongtermPk);
}
if (!handshakeResult || !encryptedClient) {
return;
}
const publicKeyStr = handshakeResult.peerLongtermPk.toString("hex");
if (this.checkClientsOverflow(publicKeyStr)) {
const publicKeyOverflowEvent = [handshakeResult.peerLongtermPk];
this.triggerEvent(types_1.EVENT_HANDSHAKEFACTORY_PUBLICKEY_OVERFLOW, ...publicKeyOverflowEvent);
client.close();
return;
}
this.increaseClientsCounter(publicKeyStr);
client.onClose(() => {
this.decreaseClientsCounter(publicKeyStr);
});
const handshakeEvent = [isServer, client, encryptedClient, handshakeResult];
this.triggerEvent(types_1.EVENT_HANDSHAKEFACTORY_HANDSHAKE, ...handshakeEvent);
}
catch (error) {
const handshakeErrorEvent = [error];
this.triggerEvent(types_1.EVENT_HANDSHAKEFACTORY_HANDSHAKE_ERROR, ...handshakeErrorEvent);
client.close();
}
};
this.handshakeFactoryConfig = handshakeFactoryConfig;
}
/** Override from parent. */
checkConnectionsOverflow(address, isServer = false) {
if (super.checkConnectionsOverflow(address, isServer)) {
return true;
}
if (!isServer) {
if (this.handshakeFactoryConfig.maxConnectionsPerClientPair !== undefined) {
const pair = [this.handshakeFactoryConfig.serverPublicKey?.toString("hex"),
this.handshakeFactoryConfig.keyPair.publicKey.toString("hex")];
pair.sort();
const pairKey = pair.join(":");
const clientPairCount = this.readCounter(pairKey);
if (clientPairCount >= this.handshakeFactoryConfig.maxConnectionsPerClientPair) {
return true;
}
}
}
return false;
}
init() {
this.onConnect(this.handleOnConnect);
super.init();
}
getHandshakeFactoryConfig() {
return this.handshakeFactoryConfig;
}
/**
* Increase the counter connections per client public key.
*/
increaseClientsCounter(peerPublicKey) {
this.increaseCounter(peerPublicKey);
const pair = [peerPublicKey, this.handshakeFactoryConfig.keyPair.publicKey.toString("hex")];
pair.sort();
const pairKey = pair.join(":");
this.increaseCounter(pairKey);
}
decreaseClientsCounter(peerPublicKey) {
this.decreaseCounter(peerPublicKey);
const pair = [peerPublicKey, this.handshakeFactoryConfig.keyPair.publicKey.toString("hex")];
pair.sort();
const pairKey = pair.join(":");
this.decreaseCounter(pairKey);
}
/**
* @params peerPublicKey
* @returns true if any limit is reached.
*/
checkClientsOverflow(peerPublicKey) {
if (this.handshakeFactoryConfig.maxConnectionsPerClient !== undefined) {
const clientCount = this.readCounter(peerPublicKey);
if (clientCount >= this.handshakeFactoryConfig.maxConnectionsPerClient) {
return true;
}
}
if (this.handshakeFactoryConfig.maxConnectionsPerClientPair !== undefined) {
const pair = [peerPublicKey, this.handshakeFactoryConfig.keyPair.publicKey.toString("hex")];
pair.sort();
const pairKey = pair.join(":");
const clientPairCount = this.readCounter(pairKey);
if (clientPairCount >= this.handshakeFactoryConfig.maxConnectionsPerClientPair) {
return true;
}
}
return false;
}
/**
* Detect specific error in the handshake process.
* This error is also emitted on the general onError event hook provided by SocketFactory.
*/
onHandshakeError(callback) {
this.hookEvent(types_1.EVENT_HANDSHAKEFACTORY_HANDSHAKE_ERROR, callback);
}
onHandshake(callback) {
this.hookEvent(types_1.EVENT_HANDSHAKEFACTORY_HANDSHAKE, callback);
}
onPublicKeyOverflow(callback) {
this.hookEvent(types_1.EVENT_HANDSHAKEFACTORY_PUBLICKEY_OVERFLOW, callback);
}
}
exports.HandshakeFactory = HandshakeFactory;