UNPKG

node-noise

Version:
245 lines 12 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Noise = void 0; var streams = __importStar(require("stream")); var duplexify_1 = __importDefault(require("duplexify")); var stablelib_1 = require("./crypto/stablelib"); var streaming_1 = require("./crypto/streaming"); var metrics_1 = require("./metrics"); var length_prefixed_decoder_1 = require("./length-prefixed-decoder"); var pattern_1 = require("./handshakes/pattern"); var handshake_1 = require("./handshake"); function parseSpec(name) { var m = name.split('_'); if (m.length != 5) { return null; } return { pattern: m[1], dh: m[2], cipher: m[3], hash: m[4] }; } var Noise = /** @class */ (function () { function Noise(init) { if (init === void 0) { init = {}; } var name = init.protocol || 'Noise_XX_25519_ChaChaPoly_SHA256'; var staticNoiseKey = init.staticNoiseKey, crypto = init.crypto, prologueBytes = init.prologueBytes, metrics = init.metrics; this.crypto = crypto !== null && crypto !== void 0 ? crypto : stablelib_1.stablelib; this.metrics = metrics ? (0, metrics_1.registerMetrics)(metrics) : undefined; var spec = parseSpec(name); if (!spec) { throw new Error('invalid protocol name'); } if (spec.dh !== '25519') { throw new Error("not supported dh: ".concat(spec.dh)); } if (spec.cipher !== 'ChaChaPoly') { throw new Error("not supported cipher: ".concat(spec.cipher)); } if (spec.hash !== 'SHA256') { throw new Error("not supported hash: ".concat(spec.hash)); } var pattern = pattern_1.PATTERNS[spec.pattern]; if (!pattern) { throw new Error("not supported pattern: ".concat(spec.pattern)); } this.spec = spec; this.handshake = new pattern_1.PatternHandshake(this.crypto, name, pattern); if (staticNoiseKey) { // accepts x25519 private key of length 32 this.staticKeys = this.crypto.generateX25519KeyPairFromSeed(staticNoiseKey); } else { this.staticKeys = this.crypto.generateX25519KeyPair(); } this.prologue = prologueBytes !== null && prologueBytes !== void 0 ? prologueBytes : new Uint8Array(0); } Noise.prototype.getPublicKey = function () { return this.staticKeys.publicKey; }; Noise.prototype.secureConnection = function (parameters) { return __awaiter(this, void 0, void 0, function () { var handshake, conn; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.performHandshake(parameters)]; case 1: handshake = _a.sent(); return [4 /*yield*/, this.createSecureConnection(parameters.connection, handshake, parameters.noLengthCodec || false, parameters.noiseMsgMaxLengthBytes || -1)]; case 2: conn = _a.sent(); return [2 /*return*/, { conn: conn, remotePublicKey: handshake.getRemoteStaticKey() }]; } }); }); }; // // /** // * Encrypt outgoing data to the remote party (handshake as initiator) // * // * @param {PeerId} localPeer - PeerId of the receiving peer // * @param {Duplex<Uint8Array>} connection - streaming iterable duplex that will be encrypted // * @param {PeerId} remotePeer - PeerId of the remote peer. Used to validate the integrity of the remote peer. // * @returns {Promise<SecuredConnection>} // */ // public async secureOutbound (localPeer: PeerId, wrappedConnection: PbStream, remotePeer?: PeerId): Promise<SecuredConnection<NoiseExtensions>> { // const handshake = await this.performHandshake({ // connection: wrappedConnection, // isInitiator: true, // localPeer, // remotePeer // }) // const conn = await this.createSecureConnection(wrappedConnection, handshake) // // return { // conn, // remoteExtensions: handshake.remoteExtensions, // remotePeer: handshake.remotePeer // } // } // // /** // * Decrypt incoming data (handshake as responder). // * // * @param {PeerId} localPeer - PeerId of the receiving peer. // * @param {Duplex<Uint8Array>} connection - streaming iterable duplex that will be encryption. // * @param {PeerId} remotePeer - optional PeerId of the initiating peer, if known. This may only exist during transport upgrades. // * @returns {Promise<SecuredConnection>} // */ // public async secureInbound (localPeer: PeerId, wrappedConnection: PbStream, remotePeer?: PeerId): Promise<SecuredConnection<NoiseExtensions>> { // const handshake = await this.performHandshake({ // connection: wrappedConnection, // isInitiator: false, // localPeer, // remotePeer // }) // const conn = await this.createSecureConnection(wrappedConnection, handshake) // // return { // conn, // remotePeer: handshake.remotePeer, // remoteExtensions: handshake.remoteExtensions // } // } /** * If Noise pipes supported, tries IK handshake first with XX as fallback if it fails. * If noise pipes disabled or remote peer static key is unknown, use XX. * * @param {HandshakeParams} params */ Noise.prototype.performHandshake = function (params) { var _a, _b; return __awaiter(this, void 0, void 0, function () { var isInitiator, remoteStaticPublicKey, connection, handshakeHandler, handshake, e_1; return __generator(this, function (_c) { switch (_c.label) { case 0: isInitiator = params.isInitiator, remoteStaticPublicKey = params.remoteStaticPublicKey, connection = params.connection, handshakeHandler = params.handshakeHandler; handshake = new handshake_1.Handshake(isInitiator, this.prologue, this.crypto, this.staticKeys, connection, this.handshake, handshakeHandler, remoteStaticPublicKey); _c.label = 1; case 1: _c.trys.push([1, 3, , 4]); return [4 /*yield*/, handshake.doHandshake()]; case 2: _c.sent(); (_a = this.metrics) === null || _a === void 0 ? void 0 : _a.handshakeSuccesses.increment(); return [3 /*break*/, 4]; case 3: e_1 = _c.sent(); (_b = this.metrics) === null || _b === void 0 ? void 0 : _b.handshakeErrors.increment(); if (e_1 instanceof Error) { e_1.message = "Error occurred during ".concat(this.spec.pattern, " handshake: ").concat(e_1.message); } throw e_1; case 4: return [2 /*return*/, handshake]; } }); }); }; Noise.prototype.createSecureConnection = function (connection, handshake, noLengthCodec, noiseMsgMaxLengthBytes) { // Create encryption box/unbox wrapper var network = connection.unwrap(); var userOutbound = new streams.PassThrough(); var userInbound = new streams.PassThrough(); var userStream = new duplexify_1.default(userOutbound, userInbound); var s = userOutbound .pipe((0, streaming_1.encryptStream)(handshake, this.metrics, noLengthCodec, noiseMsgMaxLengthBytes)) .pipe(network); if (!noLengthCodec) { s = s.pipe(new length_prefixed_decoder_1.LengthPrefixedDecoder()); } s.pipe((0, streaming_1.decryptStream)(handshake, this.metrics, noiseMsgMaxLengthBytes)) .pipe(userInbound); return userStream; }; return Noise; }()); exports.Noise = Noise; //# sourceMappingURL=noise.js.map