UNPKG

node-noise

Version:
429 lines 17.5 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.PatternHandshake = exports.PATTERNS = exports.PatternFlag = exports.PatternToken = void 0; var abstract_handshake_1 = require("./abstract-handshake"); var handshake_1 = require("../@types/handshake"); var uint8arraylist_1 = require("../uint8arraylist"); var errors_1 = require("../errors"); var PatternToken; (function (PatternToken) { // Token codes. PatternToken[PatternToken["S"] = 1] = "S"; PatternToken[PatternToken["E"] = 2] = "E"; PatternToken[PatternToken["EE"] = 3] = "EE"; PatternToken[PatternToken["ES"] = 4] = "ES"; PatternToken[PatternToken["SE"] = 5] = "SE"; PatternToken[PatternToken["SS"] = 6] = "SS"; PatternToken[PatternToken["F"] = 7] = "F"; PatternToken[PatternToken["FF"] = 8] = "FF"; PatternToken[PatternToken["FLIP_DIR"] = 255] = "FLIP_DIR"; })(PatternToken = exports.PatternToken || (exports.PatternToken = {})); var PatternFlag; (function (PatternFlag) { PatternFlag[PatternFlag["FLAG_LOCAL_STATIC"] = 1] = "FLAG_LOCAL_STATIC"; PatternFlag[PatternFlag["FLAG_LOCAL_EPHEMERAL"] = 2] = "FLAG_LOCAL_EPHEMERAL"; PatternFlag[PatternFlag["FLAG_LOCAL_REQUIRED"] = 4] = "FLAG_LOCAL_REQUIRED"; PatternFlag[PatternFlag["FLAG_LOCAL_EPHEM_REQ"] = 8] = "FLAG_LOCAL_EPHEM_REQ"; PatternFlag[PatternFlag["FLAG_LOCAL_HYBRID"] = 16] = "FLAG_LOCAL_HYBRID"; PatternFlag[PatternFlag["FLAG_LOCAL_HYBRID_REQ"] = 32] = "FLAG_LOCAL_HYBRID_REQ"; PatternFlag[PatternFlag["FLAG_REMOTE_STATIC"] = 256] = "FLAG_REMOTE_STATIC"; PatternFlag[PatternFlag["FLAG_REMOTE_EPHEMERAL"] = 512] = "FLAG_REMOTE_EPHEMERAL"; PatternFlag[PatternFlag["FLAG_REMOTE_REQUIRED"] = 1024] = "FLAG_REMOTE_REQUIRED"; PatternFlag[PatternFlag["FLAG_REMOTE_EPHEM_REQ"] = 2048] = "FLAG_REMOTE_EPHEM_REQ"; PatternFlag[PatternFlag["FLAG_REMOTE_HYBRID"] = 4096] = "FLAG_REMOTE_HYBRID"; PatternFlag[PatternFlag["FLAG_REMOTE_HYBRID_REQ"] = 8192] = "FLAG_REMOTE_HYBRID_REQ"; })(PatternFlag = exports.PatternFlag || (exports.PatternFlag = {})); var Requirement; (function (Requirement) { Requirement[Requirement["LOCAL_REQUIRED"] = 1] = "LOCAL_REQUIRED"; Requirement[Requirement["REMOTE_REQUIRED"] = 2] = "REMOTE_REQUIRED"; Requirement[Requirement["PSK_REQUIRED"] = 4] = "PSK_REQUIRED"; Requirement[Requirement["FALLBACK_PREMSG"] = 8] = "FALLBACK_PREMSG"; Requirement[Requirement["LOCAL_PREMSG"] = 16] = "LOCAL_PREMSG"; Requirement[Requirement["REMOTE_PREMSG"] = 32] = "REMOTE_PREMSG"; Requirement[Requirement["FALLBACK_POSSIBLE"] = 64] = "FALLBACK_POSSIBLE"; })(Requirement || (Requirement = {})); exports.PATTERNS = Object.freeze({ 'N': { flags: PatternFlag.FLAG_LOCAL_EPHEMERAL | PatternFlag.FLAG_REMOTE_STATIC | PatternFlag.FLAG_REMOTE_REQUIRED, steps: [ PatternToken.E, PatternToken.ES ] }, // 'X': { // flags: // PatternFlag.FLAG_LOCAL_STATIC | // PatternFlag.FLAG_LOCAL_EPHEMERAL | // PatternFlag.FLAG_REMOTE_STATIC | // PatternFlag.FLAG_REMOTE_REQUIRED, // steps: [ // PatternToken.E, // PatternToken.ES, // PatternToken.S, // PatternToken.SS // ] // }, 'NN': { flags: PatternFlag.FLAG_LOCAL_EPHEMERAL | PatternFlag.FLAG_REMOTE_EPHEMERAL, steps: [ PatternToken.E, PatternToken.FLIP_DIR, PatternToken.E, PatternToken.EE ] }, 'NK': { flags: PatternFlag.FLAG_LOCAL_EPHEMERAL | PatternFlag.FLAG_REMOTE_STATIC | PatternFlag.FLAG_REMOTE_EPHEMERAL | PatternFlag.FLAG_REMOTE_REQUIRED, steps: [ PatternToken.E, PatternToken.ES, PatternToken.FLIP_DIR, PatternToken.E, PatternToken.EE ] }, 'NX': { flags: PatternFlag.FLAG_LOCAL_EPHEMERAL | PatternFlag.FLAG_REMOTE_STATIC | PatternFlag.FLAG_REMOTE_EPHEMERAL, steps: [ PatternToken.E, PatternToken.FLIP_DIR, PatternToken.E, PatternToken.EE, PatternToken.S, PatternToken.ES ] }, 'XN': { flags: PatternFlag.FLAG_LOCAL_STATIC | PatternFlag.FLAG_LOCAL_EPHEMERAL | PatternFlag.FLAG_REMOTE_EPHEMERAL, steps: [ PatternToken.E, PatternToken.FLIP_DIR, PatternToken.E, PatternToken.EE, PatternToken.FLIP_DIR, PatternToken.S, PatternToken.SE ] }, 'XK': { flags: PatternFlag.FLAG_LOCAL_STATIC | PatternFlag.FLAG_LOCAL_EPHEMERAL | PatternFlag.FLAG_REMOTE_STATIC | PatternFlag.FLAG_REMOTE_EPHEMERAL | PatternFlag.FLAG_REMOTE_REQUIRED, steps: [ PatternToken.E, PatternToken.ES, PatternToken.FLIP_DIR, PatternToken.E, PatternToken.EE, PatternToken.FLIP_DIR, PatternToken.S, PatternToken.SE ] }, 'XX': { flags: PatternFlag.FLAG_LOCAL_STATIC | PatternFlag.FLAG_LOCAL_EPHEMERAL | PatternFlag.FLAG_REMOTE_STATIC | PatternFlag.FLAG_REMOTE_EPHEMERAL, steps: [ PatternToken.E, PatternToken.FLIP_DIR, PatternToken.E, PatternToken.EE, PatternToken.S, PatternToken.ES, PatternToken.FLIP_DIR, PatternToken.S, PatternToken.SE ] } // 'KN': { // flags: // PatternFlag.FLAG_LOCAL_STATIC | // PatternFlag.FLAG_LOCAL_EPHEMERAL | // PatternFlag.FLAG_LOCAL_REQUIRED | // PatternFlag.FLAG_REMOTE_EPHEMERAL, // steps: [ // PatternToken.E, // PatternToken.FLIP_DIR, // PatternToken.E, // PatternToken.EE, // PatternToken.SE // ] // }, // 'KK': { // flags: // PatternFlag.FLAG_LOCAL_STATIC | // PatternFlag.FLAG_LOCAL_EPHEMERAL | // PatternFlag.FLAG_LOCAL_REQUIRED | // PatternFlag.FLAG_REMOTE_STATIC | // PatternFlag.FLAG_REMOTE_EPHEMERAL | // PatternFlag.FLAG_REMOTE_REQUIRED, // steps: [ // PatternToken.E, // PatternToken.ES, // PatternToken.SS, // PatternToken.FLIP_DIR, // PatternToken.E, // PatternToken.EE, // PatternToken.SE // ] // } }); function reverseFlags(flags) { return (((flags >> 8) & 0x00FF) | ((flags << 8) & 0xFF00)) & 0xffff; } function computeRequirements(flags, isFallback) { var requirements = 0; if ((flags & PatternFlag.FLAG_LOCAL_STATIC) != 0) { requirements |= Requirement.LOCAL_REQUIRED; } if ((flags & PatternFlag.FLAG_LOCAL_REQUIRED) != 0) { requirements |= Requirement.LOCAL_REQUIRED; requirements |= Requirement.LOCAL_PREMSG; } if ((flags & PatternFlag.FLAG_REMOTE_REQUIRED) != 0) { requirements |= Requirement.REMOTE_REQUIRED; requirements |= Requirement.REMOTE_PREMSG; } if ((flags & (PatternFlag.FLAG_REMOTE_EPHEM_REQ | PatternFlag.FLAG_LOCAL_EPHEM_REQ)) != 0) { if (isFallback) requirements |= Requirement.FALLBACK_PREMSG; } // if (prefix.equals("NoisePSK")) { // requirements |= Requirement.PSK_REQUIRED; // } return requirements; } var PatternHandshake = /** @class */ (function (_super) { __extends(PatternHandshake, _super); function PatternHandshake(crypto, name, pattern) { var _this = _super.call(this, crypto) || this; _this.name = name; _this.pattern = pattern; return _this; } PatternHandshake.prototype.initSession = function (initiator, prologue, s, remotePublicKey) { var ss = this.initializeSymmetric(this.name); this.mixHash(ss, prologue); var hs = { ss: ss, s: s, rs: null, psk: null, re: null, e: null }; var flags = initiator ? this.pattern.flags : reverseFlags(this.pattern.flags); var requirements = computeRequirements(flags, false); // const psk: Uint8Array | null = null; // if (psk && psk.length > 0) { // // mixPreSharedKey // } if (this.pattern.flags & PatternFlag.FLAG_REMOTE_REQUIRED) { hs.rs = remotePublicKey; } if (initiator) { if ((requirements & Requirement.LOCAL_PREMSG) != 0) { this.mixHash(ss, s.publicKey); } // if ((requirements & Requirement.FALLBACK_PREMSG) != 0) { // this.mixHash(ss, remoteEphemeral); // if (remoteHybrid != null) // this.mixHash(ss, remoteHybrid); // if (preSharedKey != null) // symmetric.mixPublicKeyIntoCK(remoteEphemeral); // } if ((requirements & Requirement.REMOTE_PREMSG) != 0) { this.mixHash(ss, hs.rs); } } else { if ((requirements & Requirement.REMOTE_PREMSG) != 0) { this.mixHash(ss, hs.rs); } // if ((requirements & Requirement.FALLBACK_PREMSG) != 0) { // this.mixHash(ss, localEphemeral); // if (localHybrid != null) // this.mixHash(ss, localHybrid); // if (preSharedKey != null) // symmetric.mixPublicKeyIntoCK(localEphemeral); // } if ((requirements & Requirement.LOCAL_PREMSG) != 0) { this.mixHash(ss, s.publicKey); } } return { hs: hs, i: initiator, mc: 0, action: initiator ? handshake_1.Action.WRITE_MESSAGE : handshake_1.Action.READ_MESSAGE, patternIndex: 0 }; }; PatternHandshake.prototype.writeMessage = function (session, message) { var buffer = new uint8arraylist_1.Uint8ArrayList(); if (session.action != handshake_1.Action.WRITE_MESSAGE) { throw new Error('invalid state'); } while (true) { if (session.patternIndex >= this.pattern.steps.length) { session.action = handshake_1.Action.SPLIT; break; } var token = this.pattern.steps[session.patternIndex++]; if (token === PatternToken.FLIP_DIR) { session.action = handshake_1.Action.READ_MESSAGE; break; } switch (token) { case PatternToken.E: session.hs.e = this.crypto.generateX25519KeyPair(); this.mixHash(session.hs.ss, session.hs.e.publicKey); buffer.append(session.hs.e.publicKey); // If the protocol is using pre-shared keys, then also mix // the local ephemeral key into the chaining key. if (session.hs.psk && session.hs.psk.length > 0) { this.mixKey(session.hs.ss, session.hs.e.publicKey); } break; case PatternToken.S: buffer.append(this.encryptAndHash(session.hs.ss, session.hs.s.publicKey)); break; case PatternToken.EE: this.mixKey(session.hs.ss, this.dh(session.hs.e.privateKey, session.hs.re)); break; case PatternToken.ES: if (session.i) { this.mixKey(session.hs.ss, this.dh(session.hs.e.privateKey, session.hs.rs)); } else { this.mixKey(session.hs.ss, this.dh(session.hs.s.privateKey, session.hs.re)); } break; case PatternToken.SE: if (session.i) { this.mixKey(session.hs.ss, this.dh(session.hs.s.privateKey, session.hs.re)); } else { this.mixKey(session.hs.ss, this.dh(session.hs.e.privateKey, session.hs.rs)); } break; case PatternToken.SS: this.mixKey(session.hs.ss, this.dh(session.hs.e.privateKey, session.hs.rs)); break; default: throw new Error("not supported token: ".concat(token)); } } buffer.append(this.encryptAndHash(session.hs.ss, message)); if (session.action === handshake_1.Action.SPLIT) { var _a = this.split(session.hs.ss), cs1 = _a.cs1, cs2 = _a.cs2; session.cs1 = cs1; session.cs2 = cs2; } return buffer; }; PatternHandshake.prototype.readMessage = function (session, message) { if (session.action != handshake_1.Action.READ_MESSAGE) { throw new Error('invalid state'); } var publicKeySize = session.hs.s.publicKey.length; var macSize = 16; var messagePos = 0; while (true) { if (session.patternIndex >= this.pattern.steps.length) { session.action = handshake_1.Action.SPLIT; break; } var token = this.pattern.steps[session.patternIndex++]; if (token === PatternToken.FLIP_DIR) { session.action = handshake_1.Action.WRITE_MESSAGE; break; } var space = message.length - messagePos; var size = void 0; switch (token) { case PatternToken.E: if (space < publicKeySize) { throw new Error('short buffer'); } session.hs.re = message.slice(messagePos, messagePos + publicKeySize); messagePos += publicKeySize; this.mixHash(session.hs.ss, session.hs.re); // If the protocol is using pre-shared keys, then also mix // the local ephemeral key into the chaining key. if (session.hs.psk && session.hs.psk.length > 0) { this.mixKey(session.hs.ss, session.hs.re); } break; case PatternToken.S: size = publicKeySize + macSize; if (space < size) { throw new Error('short buffer'); } var decrypted_1 = this.decryptAndHash(session.hs.ss, message.slice(messagePos, messagePos + size)); if (!decrypted_1.valid) { throw new errors_1.InvalidCryptoExchangeError('handshake validation fail'); } session.hs.rs = decrypted_1.plaintext; messagePos += size; break; case PatternToken.EE: this.mixKey(session.hs.ss, this.dh(session.hs.e.privateKey, session.hs.re)); break; case PatternToken.ES: if (session.i) { this.mixKey(session.hs.ss, this.dh(session.hs.e.privateKey, session.hs.rs)); } else { this.mixKey(session.hs.ss, this.dh(session.hs.s.privateKey, session.hs.re)); } break; case PatternToken.SE: if (session.i) { this.mixKey(session.hs.ss, this.dh(session.hs.s.privateKey, session.hs.re)); } else { this.mixKey(session.hs.ss, this.dh(session.hs.e.privateKey, session.hs.rs)); } break; case PatternToken.SS: this.mixKey(session.hs.ss, this.dh(session.hs.e.privateKey, session.hs.rs)); break; default: throw new Error("not supported token: ".concat(token)); } } var ciphertext = message.slice(messagePos); var decrypted = this.decryptAndHash(session.hs.ss, ciphertext); if (!decrypted.valid) { throw new errors_1.InvalidCryptoExchangeError('handshake validation fail'); } return decrypted.plaintext; }; return PatternHandshake; }(abstract_handshake_1.AbstractHandshake)); exports.PatternHandshake = PatternHandshake; //# sourceMappingURL=pattern.js.map