UNPKG

@jovian/type-tools

Version:

TypeTools is a Typescript library for providing extensible tooling runtime validations and type helpers.

706 lines 38.6 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; 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 __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.answerSecureChannel = exports.initiateSecureChannel = exports.randHex = exports.saltedDomainSha512 = exports.saltedSha512 = exports.saltedXorCrypt = exports.SecureChannel = exports.SecureHandshake = exports.resolveEntry = exports.SecureChannelCode = void 0; var fourq_1 = require("@jovian/fourq"); var crypto = __importStar(require("crypto")); var security_common_1 = require("../../src/common/security/security.common"); var enum_util_1 = require("../../src/common/util/enum.util"); var type_transform_1 = require("../../src/type-transform"); var verticalBarBuffer = Buffer.from('|', 'ascii'); var SecureChannelCodeEnum; (function (SecureChannelCodeEnum) { SecureChannelCodeEnum[SecureChannelCodeEnum["CONTACT_INITIATION_FAILURE"] = 0] = "CONTACT_INITIATION_FAILURE"; SecureChannelCodeEnum[SecureChannelCodeEnum["CONTACT_INITIATION_TOKEN_NOT_FOUND"] = 1] = "CONTACT_INITIATION_TOKEN_NOT_FOUND"; SecureChannelCodeEnum[SecureChannelCodeEnum["CONTACT_INITIATION_BAD_RESULT"] = 2] = "CONTACT_INITIATION_BAD_RESULT"; SecureChannelCodeEnum[SecureChannelCodeEnum["CONTACT_INITIATION_NULL_RESPONSE"] = 3] = "CONTACT_INITIATION_NULL_RESPONSE"; SecureChannelCodeEnum[SecureChannelCodeEnum["BAD_CHANNEL_ID_CLIENT"] = 4] = "BAD_CHANNEL_ID_CLIENT"; SecureChannelCodeEnum[SecureChannelCodeEnum["TRUSTED_PEER_MISMATCH_CLIENT"] = 5] = "TRUSTED_PEER_MISMATCH_CLIENT"; SecureChannelCodeEnum[SecureChannelCodeEnum["TRUSTED_PEER_MISMATCH_SERVER"] = 6] = "TRUSTED_PEER_MISMATCH_SERVER"; SecureChannelCodeEnum[SecureChannelCodeEnum["TRUST_STAMP_VERIFICACTION_FAILURE_CLIENT"] = 7] = "TRUST_STAMP_VERIFICACTION_FAILURE_CLIENT"; SecureChannelCodeEnum[SecureChannelCodeEnum["TRUST_STAMP_VERIFICACTION_FAILURE_SERVER"] = 8] = "TRUST_STAMP_VERIFICACTION_FAILURE_SERVER"; SecureChannelCodeEnum[SecureChannelCodeEnum["ACCESSOR_TIME_WINDOW_EXCEEDED"] = 9] = "ACCESSOR_TIME_WINDOW_EXCEEDED"; SecureChannelCodeEnum[SecureChannelCodeEnum["ACCESSOR_TOKEN_HASH_MISMATCH"] = 10] = "ACCESSOR_TOKEN_HASH_MISMATCH"; SecureChannelCodeEnum[SecureChannelCodeEnum["SYM_NONCE_TIME_HASH_TIME_WINDOW_EXCEEDED"] = 11] = "SYM_NONCE_TIME_HASH_TIME_WINDOW_EXCEEDED"; SecureChannelCodeEnum[SecureChannelCodeEnum["SYM_NONCE_TIME_HASH_TOKEN_HASH_MISMATCH"] = 12] = "SYM_NONCE_TIME_HASH_TOKEN_HASH_MISMATCH"; SecureChannelCodeEnum[SecureChannelCodeEnum["AUTH_NO_SIGNING_KEY"] = 13] = "AUTH_NO_SIGNING_KEY"; SecureChannelCodeEnum[SecureChannelCodeEnum["AUTH_NO_PUBLIC_KEY"] = 14] = "AUTH_NO_PUBLIC_KEY"; SecureChannelCodeEnum[SecureChannelCodeEnum["AUTH_NO_SIG_DATA_OBJECT"] = 15] = "AUTH_NO_SIG_DATA_OBJECT"; SecureChannelCodeEnum[SecureChannelCodeEnum["AUTH_NO_SIG_DATA"] = 16] = "AUTH_NO_SIG_DATA"; SecureChannelCodeEnum[SecureChannelCodeEnum["AUTH_NO_SIG_PAYLOAD"] = 17] = "AUTH_NO_SIG_PAYLOAD"; SecureChannelCodeEnum[SecureChannelCodeEnum["AUTH_BAD_PUBLIC_KEY"] = 18] = "AUTH_BAD_PUBLIC_KEY"; SecureChannelCodeEnum[SecureChannelCodeEnum["AUTH_BAD_SIG_DATA"] = 19] = "AUTH_BAD_SIG_DATA"; SecureChannelCodeEnum[SecureChannelCodeEnum["AUTH_BAD_SIGNATURE"] = 20] = "AUTH_BAD_SIGNATURE"; SecureChannelCodeEnum[SecureChannelCodeEnum["AUTH_BAD_SIG_PAYLOAD"] = 21] = "AUTH_BAD_SIG_PAYLOAD"; SecureChannelCodeEnum[SecureChannelCodeEnum["AUTH_BAD_SIGNING_KEY"] = 22] = "AUTH_BAD_SIGNING_KEY"; })(SecureChannelCodeEnum || (SecureChannelCodeEnum = {})); exports.SecureChannelCode = (0, enum_util_1.ReturnCodeFamily)('SecureChannelCode', SecureChannelCodeEnum); function resolveEntry(context, resolver, key, returnNullOnNotFound) { if (returnNullOnNotFound === void 0) { returnNullOnNotFound = false; } return __awaiter(this, void 0, void 0, function () { var resolverAny, keys, entry; return __generator(this, function (_a) { switch (_a.label) { case 0: resolverAny = resolver; if (!resolverAny) { return [2, null]; } if (typeof resolverAny === 'string') { if (key === null || key === undefined) { return [2, resolverAny]; } else { throw new Error("Unable to resolve entry ".concat(context, " from direct string value entry when key '").concat(key, "' is given")); } } if (!resolverAny.getter && !resolverAny.list) { if (key === null || key === undefined) { return [2, resolver]; } else { throw new Error("Unable to resolve entry ".concat(context, " from direct object value entry when key '").concat(key, "' is given")); } } if (!key && (resolverAny === null || resolverAny === void 0 ? void 0 : resolverAny.list)) { keys = Object.keys(resolverAny.list); if (keys.length > 0) { entry = resolverAny.list[keys[0]]; if ((entry === null || entry === void 0 ? void 0 : entry.value) === undefined) { return [2, entry]; } else { return [2, entry.value]; } } } if (!key) { key = 'default'; } if (!resolverAny.getter) return [3, 2]; return [4, (0, type_transform_1.punchGrab)(resolverAny.getter(key))]; case 1: return [2, _a.sent()]; case 2: if (!resolverAny.list[key]) return [3, 5]; if (!resolverAny.list[key].getter) return [3, 4]; return [4, (0, type_transform_1.punchGrab)(resolverAny.list[key].getter(key))]; case 3: return [2, _a.sent()]; case 4: if (resolverAny.list[key].value === undefined) { return [2, resolverAny.list[key]]; } else { return [2, resolverAny.list[key].value]; } _a.label = 5; case 5: if (returnNullOnNotFound) { return [2, null]; } else { throw new Error("Unable to resolve entry '".concat(context, "' with key '").concat(key, "' from entry mapping: ").concat(JSON.stringify(resolver, null, 4))); } return [2]; } }); }); } exports.resolveEntry = resolveEntry; var SecureHandshake = (function () { function SecureHandshake() { } SecureHandshake.getIdentityFrom = function (privateKey) { return fourq_1.FourQ.generateIdentity(Buffer.from(privateKey, 'base64')); }; SecureHandshake.getPublicKeyFrom = function (privateKey) { return fourq_1.FourQ.generateIdentity(Buffer.from(privateKey, 'base64')).publicKeySchnorr.toString('base64'); }; SecureHandshake.fromJSON = function (json) { var a = new SecureChannel(); return a.fromJSONObject(JSON.parse(json)); }; SecureHandshake.getLengthBytesUInt32LE = function (length) { var lengthBytes = Buffer.alloc(4); lengthBytes.writeUInt32LE(length, 0); return lengthBytes; }; SecureHandshake.parseLengthBytesUInt32LE = function (lengthBytes) { return lengthBytes.readUInt32LE(); }; SecureHandshake.getAccessorBody = function (user, baseToken, nonceHex, joiner) { if (joiner === void 0) { joiner = '.'; } var timeHex = Date.now().toString(16); if (timeHex.length % 2 === 1) { timeHex = '0' + timeHex; } if (!nonceHex) { nonceHex = randHex(16); } if (!user) { user = 'internal'; } var accessorParts = [user, timeHex, nonceHex]; var accessorBuffer = Buffer.from(user, 'ascii'); var nonceBuffer = Buffer.from(nonceHex, 'hex'); var timeBuffer = Buffer.from(timeHex, 'hex'); var baseTokenBuffer = typeof baseToken === 'string' ? Buffer.from(baseToken, 'ascii') : baseToken; var accessorSecret = saltedSha512(baseTokenBuffer, accessorBuffer); var oneTimeHash = saltedSha512(accessorSecret, timeBuffer, nonceBuffer).toString('base64'); accessorParts.push(oneTimeHash); return accessorParts.join(joiner); }; SecureHandshake.getAccessorHeader = function (user, ecdhPubKeyB64, baseToken, signing, channelExpire, nonceHex) { var sigPart = ''; if (signing) { var signingResult = SecureHandshake.signStamp(signing.private); if (signingResult.bad) { return (0, enum_util_1.passthru)(signingResult); } sigPart = Buffer.from([ signing.type, signingResult.data.payload, signing.public, signingResult.data.sig ].join('_'), 'utf8').toString('base64'); } var accessorHeader = [ "Accessor !!!!.".concat(SecureHandshake.getAccessorBody(user, baseToken, nonceHex)), ecdhPubKeyB64, channelExpire ? channelExpire : 0, sigPart, ].join('_'); var lengthRemainder = accessorHeader.length; var totalContentLength3 = lengthRemainder % 256; lengthRemainder = (lengthRemainder - totalContentLength3) / 256; var totalContentLength2 = lengthRemainder % 256; lengthRemainder = (lengthRemainder - totalContentLength2) / 256; var totalContentLength1 = lengthRemainder % 256; var contentLength = Buffer.from([totalContentLength1, totalContentLength2, totalContentLength3]).toString('base64'); var accessorHeaderWithLengthBytes = accessorHeader.replace('!!!!', contentLength); return (0, enum_util_1.ok)(accessorHeaderWithLengthBytes); }; SecureHandshake.parseAccessor = function (accessorExpression) { var accessorData = accessorExpression.split('.'); var headerLength = getLengthFromBytesOfHashContent(accessorData[0], 'Accessor'); var accessor = accessorData[1]; var accessorBuffer = Buffer.from(accessor, 'ascii'); var timeHex = accessorData[2]; var timestamp = parseInt(timeHex, 16); var nonceHex = accessorData[3]; var nonceBuffer = Buffer.from(nonceHex, 'hex'); var timeBuffer = Buffer.from(timeHex, 'hex'); var oneTimeHash = accessorData[4]; return { headerLength: headerLength, accessorBuffer: accessorBuffer, timeHex: timeHex, timeBuffer: timeBuffer, timestamp: timestamp, nonceHex: nonceHex, nonceBuffer: nonceBuffer, oneTimeHash: oneTimeHash }; }; SecureHandshake.parseAuthHeader = function (authHeader) { var authChunks = authHeader.split('_'); var accessor = SecureHandshake.parseAccessor(authChunks[0]); var _a = authChunks[3] ? Buffer.from(authChunks[3], 'base64').toString('utf8').split('_') : [null, null, null], payloadB64 = _a[0], signerPubKeyB64 = _a[1], signatureB64 = _a[2]; return __assign({ accessorExpression: authChunks[0], peerEcdhPublicKey: authChunks[1], expires: authChunks[2] ? parseInt(authChunks[2], 10) : 0, sigPart: authChunks[3], peerSignaturePublicKey: signerPubKeyB64, peerSignature: signatureB64, peerSignaturePayload: payloadB64 }, accessor); }; SecureHandshake.symNonceTimeHash = function (baseToken, user) { if (user === void 0) { user = 'internal'; } var hashedContent = "!!!!_".concat(this.getAccessorBody(user, baseToken, null, '_')); var lengthRemainder = hashedContent.length; var totalContentLength3 = lengthRemainder % 256; lengthRemainder = (lengthRemainder - totalContentLength3) / 256; var totalContentLength2 = lengthRemainder % 256; lengthRemainder = (lengthRemainder - totalContentLength2) / 256; var totalContentLength1 = lengthRemainder % 256; var contentLength = Buffer.from([totalContentLength1, totalContentLength2, totalContentLength3]).toString('base64'); var encryptedContentWithLengthBytes = hashedContent.replace('!!!!', contentLength); return encryptedContentWithLengthBytes; }; SecureHandshake.symNonceTimeHashHeader = function (baseToken, user) { if (user === void 0) { user = 'internal'; } return "Bearer SYMNT_HASH.".concat(this.symNonceTimeHash(baseToken, user)); }; SecureHandshake.symNonceTimeVerify = function (hashedText, baseToken, timeWindow) { if (timeWindow === void 0) { timeWindow = 5000; } var accessorData = hashedText.split('_'); var headerLength = getLengthFromBytesOfHashContent(accessorData[0]); var hashedContent = accessorData[1]; var hashedContentBuffer = Buffer.from(hashedContent, 'ascii'); var timeHex = accessorData[2]; var nonceHex = accessorData[3]; var nonceBuffer = Buffer.from(nonceHex, 'hex'); var timeBuffer = Buffer.from(timeHex, 'hex'); var oneTimeHash = accessorData[4]; var t = parseInt(timeHex, 16); if (Math.abs(Date.now() - t) > timeWindow) { return exports.SecureChannelCode.error('SYM_NONCE_TIME_HASH_TIME_WINDOW_EXCEEDED'); } var baseTokenBuffer = typeof baseToken === 'string' ? Buffer.from(baseToken, 'ascii') : baseToken; var accessorSecret = saltedSha512(baseTokenBuffer, hashedContentBuffer); var expectedHash = saltedSha512(accessorSecret, timeBuffer, nonceBuffer).toString('base64'); if (oneTimeHash !== expectedHash) { return exports.SecureChannelCode.error('SYM_NONCE_TIME_HASH_TOKEN_HASH_MISMATCH'); } return (0, enum_util_1.ok)(true); }; SecureHandshake.verifyAuthHeaderSignature = function (sigObj) { return SecureHandshake.verifyStamp({ payload: sigObj.peerSignaturePayload, sig: sigObj.peerSignature }, sigObj.peerSignaturePublicKey); }; SecureHandshake.verifyAccessor = function (accessorExpression, baseToken, timeWindow) { if (timeWindow === void 0) { timeWindow = 5000; } var accessor = SecureHandshake.parseAccessor(accessorExpression); var t = parseInt(accessor.timeHex, 16); if (Math.abs(Date.now() - t) > timeWindow) { return exports.SecureChannelCode.error('ACCESSOR_TIME_WINDOW_EXCEEDED'); } var baseTokenBuffer = typeof baseToken === 'string' ? Buffer.from(baseToken, 'ascii') : baseToken; var accessorSecret = saltedSha512(baseTokenBuffer, accessor.accessorBuffer); var expectedHash = saltedSha512(accessorSecret, accessor.timeBuffer, accessor.nonceBuffer).toString('base64'); if (accessor.oneTimeHash !== expectedHash) { return exports.SecureChannelCode.error('ACCESSOR_TOKEN_HASH_MISMATCH'); } return (0, enum_util_1.ok)(accessor); }; SecureHandshake.timeAuth = function () { return 'proof-of-authenticity__t:gaia:ms:' + Date.now(); }; SecureHandshake.signStamp = function (signingKey) { if (!signingKey) { return exports.SecureChannelCode.error('AUTH_NO_SIGNING_KEY'); } var payload = SecureHandshake.timeAuth(); var payloadBuffer = Buffer.from(payload, 'ascii'); var signingKeyBuffer = typeof signingKey === 'string' ? Buffer.from(signingKey, 'base64') : signingKey; if (!signingKeyBuffer || signingKeyBuffer.length !== 32) { return exports.SecureChannelCode.error('AUTH_BAD_SIGNING_KEY', "Signing key must be 32 bytes"); } var signature = fourq_1.FourQ.sign(payloadBuffer, signingKeyBuffer); return (0, enum_util_1.ok)({ payload: payloadBuffer.toString('base64'), sig: signature.data.toString('base64') }); }; SecureHandshake.verifyStamp = function (sigData, publicKey) { if (!sigData) { return exports.SecureChannelCode.error('AUTH_NO_SIG_DATA_OBJECT', 'No signature data object'); } if (!publicKey) { return exports.SecureChannelCode.error('AUTH_NO_PUBLIC_KEY', 'No public key'); } if (!sigData.sig) { return exports.SecureChannelCode.error('AUTH_NO_SIG_DATA', 'No signature'); } if (!sigData.payload) { return exports.SecureChannelCode.error('AUTH_NO_SIG_PAYLOAD', 'No signed message'); } var publicKeyBuffer = typeof publicKey === 'string' ? Buffer.from(publicKey, 'base64') : publicKey; if (!publicKeyBuffer || publicKeyBuffer.length !== 32) { return exports.SecureChannelCode.error('AUTH_BAD_PUBLIC_KEY', 'Public key must be 32 bytes'); } var sigBuffer = Buffer.from(sigData.sig, 'base64'); if (!sigBuffer || sigBuffer.length !== 64) { return exports.SecureChannelCode.error('AUTH_BAD_SIG_DATA', 'Signature must be 64 bytes'); } var payloadBuffer = Buffer.from(sigData.payload, 'base64'); if (!payloadBuffer) { return exports.SecureChannelCode.error('AUTH_BAD_SIG_PAYLOAD', 'Malformed payload; base64 decode failed'); } var valid = fourq_1.FourQ.verify(sigBuffer, payloadBuffer, publicKeyBuffer); return (0, enum_util_1.ok)(valid); }; return SecureHandshake; }()); exports.SecureHandshake = SecureHandshake; var SecureChannel = (function () { function SecureChannel(type, channelId, peerInfo, localKeypair, signing) { this.peerInfo = { ecdhPublicKey: null }; this.signing = null; this.sharedSecret = null; this.expires = 0; this.type = type ? type : security_common_1.SecureChannelTypes.DEFAULT; this.channelId = !channelId || channelId === 'generate' ? fourq_1.FourQ.randomBytes(16).toString('base64') : channelId; this.channelIdLength = this.channelId.length; this.signing = signing; if (this.signing && this.signing.private && !this.signing.public) { this.signing.public = fourq_1.FourQ.ecdhGenerateFromSeed(Buffer.from(this.signing.private, 'base64')).publicKey.toString('base64'); } this.refreshNonce(); if (peerInfo) { this.fromPeerPublicKey(peerInfo, localKeypair); } this.lastActive = Date.now(); } SecureChannel.prototype.fromPeerPublicKey = function (peerInfo, localKeypair) { this.peerInfo = peerInfo; this.localKeyPair = localKeypair ? localKeypair : fourq_1.FourQ.ecdhGenerateKeyPair(); if (this.peerInfo.ecdhPublicKey.length === 32) { this.sharedSecret = fourq_1.FourQ.getSharedSecret(this.localKeyPair.secretKey, this.peerInfo.ecdhPublicKey); } return this; }; SecureChannel.prototype.getSecureChannelResponse = function (extraInfo) { var sigResult = SecureHandshake.signStamp(this.signing.private); if (sigResult.bad) { return (0, enum_util_1.passthru)(sigResult); } return (0, enum_util_1.ok)(__assign(__assign({}, sigResult.data), { channelId: this.channelId, ecdhPublicKey: this.localKeyPair.publicKey.toString('base64'), signaturePublicKey: this.signing.public, extra: extraInfo })); }; SecureChannel.prototype.refreshNonce = function () { this.pregeneratedNonce = fourq_1.FourQ.randomBytes(32); }; SecureChannel.prototype.fromJSONObject = function (src) { this.type = src.type; this.channelId = src.channelId; this.peerInfo = { ecdhPublicKey: src.peerPublicKeyB64 ? Buffer.from(src.peerPublicKeyB64, 'base64') : null, signaturePublicKey: src.peerSignaturePublicKeyB64 ? Buffer.from(src.peerSignaturePublicKeyB64, 'base64') : null, iden: src.peerIden, data: src.peerData, }; this.localKeyPair = { isDH: true, type: fourq_1.CryptoScheme.FourQ, publicKey: src.localKeyPairPublicKeyB64 ? Buffer.from(src.localKeyPairPublicKeyB64, 'base64') : null, secretKey: src.localKeyPairPublicKeyB64 ? Buffer.from(src.localKeyPairSecretKeyB64, 'base64') : null, }; if (src.sharedSecretB64) { this.sharedSecret = Buffer.from(src.sharedSecretB64, 'base64'); } else { this.fromPeerPublicKey(this.peerInfo, this.localKeyPair.publicKey ? this.localKeyPair : null); } this.expires = src.expires; return this; }; SecureChannel.prototype.toJSON = function () { return JSON.stringify({ type: this.type, channelId: this.channelId, peerIden: this.peerInfo.iden, peerData: this.peerInfo.data, peerPublicKeyB64: this.peerInfo.ecdhPublicKey ? this.peerInfo.ecdhPublicKey.toString('base64') : null, peerSignaturePublicKeyB64: this.peerInfo.signaturePublicKey ? this.peerInfo.signaturePublicKey.toString('base64') : null, localKeyPairPublicKeyB64: this.localKeyPair ? this.localKeyPair.publicKey.toString('base64') : null, localKeyPairSecretKeyB64: this.localKeyPair ? this.localKeyPair.secretKey.toString('base64') : null, sharedSecretB64: this.sharedSecret ? this.sharedSecret.toString('base64') : null, expires: this.expires, }); }; SecureChannel.prototype.createTcpPayload = function (payload) { var sharedKey64Bytes = Buffer.concat([this.pregeneratedNonce, this.sharedSecret]); var keyHash = crypto.createHash('sha512').update(sharedKey64Bytes).digest(); var payloadLength = 4 + 32 + payload.length; var lengthBytes = SecureHandshake.getLengthBytesUInt32LE(payloadLength); var tcpPayload = Buffer.concat([lengthBytes, this.pregeneratedNonce, fourq_1.FourQ.xorCryptSHA512(keyHash, payload)]); this.refreshNonce(); return tcpPayload; }; SecureChannel.prototype.decryptTcpPayload = function (tcpPayload) { var nonce = tcpPayload.slice(4, 36); var encryptedPayload = tcpPayload.slice(36); return this.decryptPayload(encryptedPayload, nonce); }; SecureChannel.prototype.createWrappedPayloadFromBuffer = function (payload) { this.lastActive = Date.now(); var sharedKey64Bytes = Buffer.concat([this.pregeneratedNonce, this.sharedSecret]); var keyHash = crypto.createHash('sha512').update(sharedKey64Bytes).digest(); var wrapped = { __scp: true, c: this.channelId, n: this.pregeneratedNonce.toString('base64'), p: fourq_1.FourQ.xorCryptSHA512(keyHash, payload).toString('base64'), }; this.refreshNonce(); return wrapped; }; SecureChannel.prototype.createWrappedPayload = function (payload) { if (typeof payload === 'string') { return this.createWrappedPayloadFromBuffer(Buffer.from(payload, 'utf8')); } else { return this.createWrappedPayloadFromBuffer(payload); } }; SecureChannel.prototype.createWrappedPayloadObject = function (obj) { return this.createWrappedPayloadFromBuffer(Buffer.from(JSON.stringify(obj), 'utf8')); }; SecureChannel.prototype.createWrappedPayloadString = function (obj) { return JSON.stringify(this.createWrappedPayloadObject(obj)); }; SecureChannel.prototype.createWrappedPayloadBase64 = function (obj) { return Buffer.from(this.createWrappedPayloadString(obj), 'utf8').toString('base64'); }; SecureChannel.prototype.decryptPayload = function (payloadBytes, nonce) { this.lastActive = Date.now(); var sharedKey64Bytes = Buffer.concat([nonce, this.sharedSecret]); var keyHash = crypto.createHash('sha512').update(sharedKey64Bytes).digest(); return fourq_1.FourQ.xorCryptSHA512(keyHash, payloadBytes); }; SecureChannel.prototype.decryptSecureChannelPayloadObject = function (wrapped, outputEncoding) { if (outputEncoding === void 0) { outputEncoding = 'utf8'; } var nonce = Buffer.from(wrapped.n, 'base64'); var payloadEnc = Buffer.from(wrapped.p, 'base64'); var decoded = this.decryptPayload(payloadEnc, nonce); switch (outputEncoding) { case 'utf8': { return decoded.toString('utf8'); } default: { return decoded; } } }; SecureChannel.prototype.decryptSecureChannelPayload = function (wrapped) { return this.decryptSecureChannelPayloadObject(wrapped); }; SecureChannel.prototype.decryptSecureChannelPayloadIntoString = function (wrapped) { return this.decryptSecureChannelPayloadObject(wrapped, 'utf8'); }; SecureChannel.prototype.decryptPayloadBase64 = function (payloadStrB64) { var payload = this.parseWrappedPayloadString(Buffer.from(payloadStrB64, 'base64').toString('utf8')); return this.parseSecureChannelPayloadIntoObject(payload); }; SecureChannel.prototype.parseSecureChannelPayloadIntoObject = function (wrapped) { return JSON.parse(this.decryptSecureChannelPayloadIntoString(wrapped)); }; SecureChannel.prototype.parseWrappedPayloadString = function (payloadStr) { return JSON.parse(payloadStr); }; SecureChannel.prototype.parseWrappedPayloadBase64 = function (payloadStrB64) { return this.parseWrappedPayloadString(Buffer.from(payloadStrB64, 'base64').toString('utf8')); }; return SecureChannel; }()); exports.SecureChannel = SecureChannel; function saltedXorCrypt(payload, secret) { var salts = []; for (var _i = 2; _i < arguments.length; _i++) { salts[_i - 2] = arguments[_i]; } return fourq_1.FourQ.xorCryptSHA512(saltedSha512.apply(void 0, __spreadArray([secret], salts, false)), payload); } exports.saltedXorCrypt = saltedXorCrypt; function saltedSha512(message) { var salts = []; for (var _i = 1; _i < arguments.length; _i++) { salts[_i - 1] = arguments[_i]; } var sharedKey64Bytes = Buffer.concat(__spreadArray(__spreadArray([], salts, true), [message], false)); return crypto.createHash('sha512').update(sharedKey64Bytes).digest(); } exports.saltedSha512 = saltedSha512; function saltedDomainSha512(message, domain) { var salts = []; for (var _i = 2; _i < arguments.length; _i++) { salts[_i - 2] = arguments[_i]; } var sharedKey64Bytes = Buffer.concat(__spreadArray(__spreadArray([domain, verticalBarBuffer], salts, true), [verticalBarBuffer, message], false)); return crypto.createHash('sha512').update(sharedKey64Bytes).digest(); } exports.saltedDomainSha512 = saltedDomainSha512; function randHex(length) { var characters = '0123456789abcdef'; var str = []; for (var i = 0; i < length; ++i) { str.push(characters[Math.floor(Math.random() * 16)]); } return str.join(''); } exports.randHex = randHex; function initiateSecureChannel(initiatorFlow) { return __awaiter(this, void 0, void 0, function () { var ecdhKeypair, channelMyPubkey, user, token, e_1, authHeader, initiateContactResult, e_2, response, trusted, valid, peerInfo, channel; return __generator(this, function (_a) { switch (_a.label) { case 0: ecdhKeypair = fourq_1.FourQ.ecdhGenerateKeyPair(); channelMyPubkey = ecdhKeypair.publicKey.toString('base64'); user = initiatorFlow.user ? initiatorFlow.user : 'user'; _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); return [4, resolveEntry('token', initiatorFlow.token)]; case 2: token = _a.sent(); return [3, 4]; case 3: e_1 = _a.sent(); return [2, exports.SecureChannelCode.error('CONTACT_INITIATION_TOKEN_NOT_FOUND', e_1)]; case 4: authHeader = SecureHandshake.getAccessorHeader(user, channelMyPubkey, token, initiatorFlow.signing, initiatorFlow.expire); _a.label = 5; case 5: _a.trys.push([5, 7, , 8]); return [4, initiatorFlow.initiateContact(authHeader.data)]; case 6: initiateContactResult = _a.sent(); return [3, 8]; case 7: e_2 = _a.sent(); return [2, exports.SecureChannelCode.error('CONTACT_INITIATION_FAILURE', e_2)]; case 8: if (initiateContactResult.bad) { return [2, exports.SecureChannelCode.error('CONTACT_INITIATION_BAD_RESULT', initiateContactResult.error)]; } response = initiateContactResult.data; if (!response) { return [2, exports.SecureChannelCode.error('CONTACT_INITIATION_NULL_RESPONSE')]; } if (!response.channelId || !response.channelId.length || response.channelId.length > 128) { return [2, exports.SecureChannelCode.error('BAD_CHANNEL_ID_CLIENT', response.channelId)]; } return [4, resolveEntry('trust', initiatorFlow.trust)]; case 9: trusted = _a.sent(); if (!!trusted) return [3, 11]; return [4, resolveEntry('trust', initiatorFlow.trust, response.signaturePublicKey)]; case 10: trusted = _a.sent(); _a.label = 11; case 11: if (trusted) { if (trusted.publicKey !== response.signaturePublicKey) { return [2, exports.SecureChannelCode.error('TRUSTED_PEER_MISMATCH_CLIENT', "Peer with public key '".concat(response.signaturePublicKey, "' is not found on trust list."))]; } valid = SecureHandshake.verifyStamp(response, response.signaturePublicKey); if (!valid) { return [2, exports.SecureChannelCode.error('TRUST_STAMP_VERIFICACTION_FAILURE_CLIENT')]; } } peerInfo = { ecdhPublicKey: Buffer.from(response.ecdhPublicKey, 'base64'), signaturePublicKey: Buffer.from(response.signaturePublicKey, 'base64'), }; channel = new SecureChannel(security_common_1.SecureChannelTypes.ECC_4Q, initiatorFlow.channelId, peerInfo, ecdhKeypair, initiatorFlow.signing); return [2, (0, enum_util_1.ok)(channel)]; } }); }); } exports.initiateSecureChannel = initiateSecureChannel; function answerSecureChannel(answererFlow) { return __awaiter(this, void 0, void 0, function () { var timeWindow, ecdhKeypair, authInfo, token, accessData, trusted, peerEcdhPublicKey, peerInfo, channel; return __generator(this, function (_a) { switch (_a.label) { case 0: timeWindow = answererFlow.timeWindow ? answererFlow.timeWindow : 5000; ecdhKeypair = fourq_1.FourQ.ecdhGenerateKeyPair(); authInfo = SecureHandshake.parseAuthHeader(answererFlow.authHeader); return [4, resolveEntry('token', answererFlow.token)]; case 1: token = _a.sent(); accessData = SecureHandshake.verifyAccessor(authInfo.accessorExpression, token, timeWindow); if (accessData.bad) { return [2, (0, enum_util_1.passthru)(accessData)]; } if (!answererFlow.trust) return [3, 3]; return [4, resolveEntry('trust', answererFlow.trust, authInfo.peerSignaturePublicKey)]; case 2: trusted = _a.sent(); if (!trusted) { return [2, exports.SecureChannelCode.error('TRUSTED_PEER_MISMATCH_SERVER', "Peer with public key '".concat(authInfo.peerSignaturePublicKey, "' is not found on trust list."))]; } if (!SecureHandshake.verifyAuthHeaderSignature(authInfo)) { return [2, exports.SecureChannelCode.error('TRUST_STAMP_VERIFICACTION_FAILURE_SERVER')]; } _a.label = 3; case 3: peerEcdhPublicKey = Buffer.from(authInfo.peerEcdhPublicKey, 'base64'); peerInfo = { ecdhPublicKey: peerEcdhPublicKey, signaturePublicKey: Buffer.from(authInfo.peerSignaturePublicKey, 'base64') }; channel = new SecureChannel(security_common_1.SecureChannelTypes.ECC_4Q, answererFlow.channelId, peerInfo, ecdhKeypair, answererFlow.signing); return [2, (0, enum_util_1.ok)(channel)]; } }); }); } exports.answerSecureChannel = answerSecureChannel; function getLengthFromBytesOfHashContent(chunk, front) { if (front === void 0) { front = ''; } var lengthBytes = front ? chunk.split("".concat(front, " "))[1].substring(0, 4) : chunk.substring(0, 4); var headerLength; if (lengthBytes === '!!!!') { headerLength = 0; } else { var lengthBuffer = Buffer.from(lengthBytes, 'base64'); headerLength = lengthBuffer[0] * 65536 + lengthBuffer[1] * 256 + lengthBuffer[2]; } return headerLength; } //# sourceMappingURL=secure-channel.js.map