@jovian/type-tools
Version:
TypeTools is a Typescript library for providing extensible tooling runtime validations and type helpers.
706 lines • 38.6 kB
JavaScript
"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