node-noise
Version:
429 lines • 17.5 kB
JavaScript
"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