@trap_stevo/verilink
Version:
Encrypted from the start. Trusted to the end. This client-side protocol redefines secure communication — forging a direct bridge to zero-trust architecture through encrypted sessions, intelligent attestation, and seamless claim validation. Engineered for
442 lines • 19.2 kB
JavaScript
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
import _createClass from "@babel/runtime/helpers/createClass";
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
import _regeneratorRuntime from "@babel/runtime/regenerator";
function _classPrivateMethodInitSpec(e, a) { _checkPrivateRedeclaration(e, a), a.add(e); }
function _classPrivateFieldInitSpec(e, t, a) { _checkPrivateRedeclaration(e, t), t.set(e, a); }
function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); }
function _classPrivateFieldGet(s, a) { return s.get(_assertClassBrand(s, a)); }
function _classPrivateFieldSet(s, a, r) { return s.set(_assertClassBrand(s, a), r), r; }
function _assertClassBrand(e, t, n) { if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n; throw new TypeError("Private element is not present on this object"); }
import axios from "axios";
var _algorithm = /*#__PURE__*/new WeakMap();
var _ivLength = /*#__PURE__*/new WeakMap();
var _sessionKey = /*#__PURE__*/new WeakMap();
var _sessionID = /*#__PURE__*/new WeakMap();
var _serverURL = /*#__PURE__*/new WeakMap();
var _vaultSGN = /*#__PURE__*/new WeakMap();
var _deviceID = /*#__PURE__*/new WeakMap();
var _linkSGN = /*#__PURE__*/new WeakMap();
var _paired = /*#__PURE__*/new WeakMap();
var _mutator = /*#__PURE__*/new WeakMap();
var _userAgent = /*#__PURE__*/new WeakMap();
var _VeriLink_brand = /*#__PURE__*/new WeakSet();
var VeriLink = /*#__PURE__*/function () {
function VeriLink() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
_classCallCheck(this, VeriLink);
_classPrivateMethodInitSpec(this, _VeriLink_brand);
_classPrivateFieldInitSpec(this, _algorithm, "AES-GCM");
_classPrivateFieldInitSpec(this, _ivLength, 12);
_classPrivateFieldInitSpec(this, _sessionKey, void 0);
_classPrivateFieldInitSpec(this, _sessionID, void 0);
_classPrivateFieldInitSpec(this, _serverURL, void 0);
_classPrivateFieldInitSpec(this, _vaultSGN, void 0);
_classPrivateFieldInitSpec(this, _deviceID, void 0);
_classPrivateFieldInitSpec(this, _linkSGN, void 0);
_classPrivateFieldInitSpec(this, _paired, void 0);
_classPrivateFieldInitSpec(this, _mutator, void 0);
_classPrivateFieldInitSpec(this, _userAgent, void 0);
this.persistSessionKey = options.persistSessionKey || false;
this.persistSessionID = options.persistSessionID || false;
_classPrivateFieldSet(_mutator, this, typeof options.mutator === "string" ? new TextEncoder().encode(options.mutator) : options.mutator || new TextEncoder().encode("vlk"));
_classPrivateFieldSet(_userAgent, this, options.userAgent || "verilink-browser");
_classPrivateFieldSet(_deviceID, this, options.deviceID || null);
_classPrivateFieldSet(_vaultSGN, this, options.vaultSGN || "vlx");
_classPrivateFieldSet(_linkSGN, this, options.linkSGN || "vli");
_classPrivateFieldSet(_paired, this, false);
this.setServer(options.serverURL || "");
if (options.sessionKey) {
this.setKey(options.sessionKey);
} else {
_classPrivateFieldSet(_sessionKey, this, _assertClassBrand(_VeriLink_brand, this, _loadOrGenerateSessionKey).call(this));
}
if (options.sessionID) {
_classPrivateFieldSet(_sessionID, this, options.sessionID);
} else {
_classPrivateFieldSet(_sessionID, this, _assertClassBrand(_VeriLink_brand, this, _loadOrGenerateSessionID).call(this, this.persistSessionID));
}
}
return _createClass(VeriLink, [{
key: "setServer",
value: function setServer(url) {
_classPrivateFieldSet(_serverURL, this, url);
}
}, {
key: "setKey",
value: function setKey(key) {
if (typeof key === "string") {
_classPrivateFieldSet(_sessionKey, this, _assertClassBrand(_VeriLink_brand, this, _base64ToArrayBuffer).call(this, key));
} else if (key instanceof ArrayBuffer) {
_classPrivateFieldSet(_sessionKey, this, key);
} else {
throw new Error("Invalid session key data (Base64 string or ArrayBuffer).");
}
if (this.persistSessionKey) {
var mutated = _assertClassBrand(_VeriLink_brand, this, _mutateForStorage).call(this, _classPrivateFieldGet(_sessionKey, this));
localStorage.setItem(_classPrivateFieldGet(_vaultSGN, this), mutated);
}
}
}, {
key: "pair",
value: function () {
var _pair = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
var _response$data$succes, _response$data;
var pairingURL,
sessionKey,
sessionID,
response,
_args = arguments;
return _regeneratorRuntime.wrap(function _callee$(_context) {
while (1) switch (_context.prev = _context.next) {
case 0:
pairingURL = _args.length > 0 && _args[0] !== undefined ? _args[0] : "/device/pair";
sessionKey = _assertClassBrand(_VeriLink_brand, this, _arrayBufferToBase).call(this, _classPrivateFieldGet(_sessionKey, this));
sessionID = _classPrivateFieldGet(_sessionID, this);
_context.next = 5;
return axios.post(_classPrivateFieldGet(_serverURL, this) + pairingURL, {
sessionKey: sessionKey
}, {
headers: {
"x-vlink-session-id": sessionID
}
});
case 5:
response = _context.sent;
_classPrivateFieldSet(_paired, this, (_response$data$succes = (_response$data = response.data) === null || _response$data === void 0 ? void 0 : _response$data.success) !== null && _response$data$succes !== void 0 ? _response$data$succes : false);
return _context.abrupt("return", response.data);
case 8:
case "end":
return _context.stop();
}
}, _callee, this);
}));
function pair() {
return _pair.apply(this, arguments);
}
return pair;
}()
}, {
key: "paired",
value: function paired() {
return _classPrivateFieldGet(_paired, this);
}
}, {
key: "send",
value: function () {
var _send = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee2(method, path) {
var _response$data2;
var data,
headers,
options,
methodUpper,
url,
link,
vlinkID,
encrypted,
claim,
config,
response,
decrypted,
_args2 = arguments;
return _regeneratorRuntime.wrap(function _callee2$(_context2) {
while (1) switch (_context2.prev = _context2.next) {
case 0:
data = _args2.length > 2 && _args2[2] !== undefined ? _args2[2] : {};
headers = _args2.length > 3 && _args2[3] !== undefined ? _args2[3] : {};
options = _args2.length > 4 && _args2[4] !== undefined ? _args2[4] : {
fullResponse: false
};
methodUpper = method.toUpperCase();
url = _classPrivateFieldGet(_serverURL, this) + path;
link = _assertClassBrand(_VeriLink_brand, this, _generateLink).call(this);
_context2.next = 8;
return _assertClassBrand(_VeriLink_brand, this, _hmac).call(this, _classPrivateFieldGet(_sessionKey, this), link);
case 8:
vlinkID = _context2.sent;
_context2.next = 11;
return _assertClassBrand(_VeriLink_brand, this, _encrypt).call(this, data);
case 11:
encrypted = _context2.sent;
_context2.next = 14;
return _assertClassBrand(_VeriLink_brand, this, _signClaim).call(this);
case 14:
claim = _context2.sent;
config = {
method: methodUpper,
url: url,
headers: _objectSpread({
"Content-Type": "application/json",
"x-vlink-claim-timestamp": claim.timestamp,
"x-vlink-session-id": _classPrivateFieldGet(_sessionID, this),
"x-vlink-device": claim.deviceID,
"x-vlink-claim": claim.signature,
"x-vlink-id": vlinkID,
"x-vlink": link
}, headers)
};
if (methodUpper === "GET") {
config.params = {
encrypted: encrypted
};
} else {
config.data = {
encrypted: encrypted
};
}
_context2.next = 19;
return axios(config);
case 19:
response = _context2.sent;
if (!((_response$data2 = response.data) !== null && _response$data2 !== void 0 && _response$data2.encrypted)) {
_context2.next = 25;
break;
}
_context2.next = 23;
return _assertClassBrand(_VeriLink_brand, this, _decrypt).call(this, response.data.encrypted);
case 23:
decrypted = _context2.sent;
return _context2.abrupt("return", options.fullResponse ? {
headers: response.headers,
status: response.status,
data: decrypted,
response: response
} : decrypted);
case 25:
return _context2.abrupt("return", options.fullResponse ? {
headers: response.headers,
status: response.status,
data: response.data,
response: response
} : response.data);
case 26:
case "end":
return _context2.stop();
}
}, _callee2, this);
}));
function send(_x, _x2) {
return _send.apply(this, arguments);
}
return send;
}()
}]);
}();
function _encrypt(_x3) {
return _encrypt2.apply(this, arguments);
}
function _encrypt2() {
_encrypt2 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee3(data) {
var iv, key, encoded, encrypted, tag, combined;
return _regeneratorRuntime.wrap(function _callee3$(_context3) {
while (1) switch (_context3.prev = _context3.next) {
case 0:
iv = crypto.getRandomValues(new Uint8Array(_classPrivateFieldGet(_ivLength, this)));
_context3.next = 3;
return crypto.subtle.importKey("raw", _classPrivateFieldGet(_sessionKey, this), {
name: _classPrivateFieldGet(_algorithm, this)
}, false, ["encrypt"]);
case 3:
key = _context3.sent;
encoded = new TextEncoder().encode(JSON.stringify(data));
_context3.next = 7;
return crypto.subtle.encrypt({
name: _classPrivateFieldGet(_algorithm, this),
iv: iv
}, key, encoded);
case 7:
encrypted = _context3.sent;
tag = encrypted.slice(-16);
combined = new Uint8Array(iv.length + tag.byteLength + encrypted.byteLength - 16);
combined.set(iv, 0);
combined.set(new Uint8Array(tag), iv.length);
combined.set(new Uint8Array(encrypted).slice(0, -16), iv.length + tag.byteLength);
return _context3.abrupt("return", _assertClassBrand(_VeriLink_brand, this, _arrayBufferToBase).call(this, combined.buffer));
case 14:
case "end":
return _context3.stop();
}
}, _callee3, this);
}));
return _encrypt2.apply(this, arguments);
}
function _decrypt(_x4) {
return _decrypt2.apply(this, arguments);
}
function _decrypt2() {
_decrypt2 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee4(b64) {
var buffer, iv, tag, ciphertext, key, encrypted, decrypted;
return _regeneratorRuntime.wrap(function _callee4$(_context4) {
while (1) switch (_context4.prev = _context4.next) {
case 0:
buffer = new Uint8Array(atob(b64).split("").map(function (c) {
return c.charCodeAt(0);
}));
iv = buffer.slice(0, _classPrivateFieldGet(_ivLength, this));
tag = buffer.slice(_classPrivateFieldGet(_ivLength, this), _classPrivateFieldGet(_ivLength, this) + 16);
ciphertext = buffer.slice(_classPrivateFieldGet(_ivLength, this) + 16);
_context4.next = 6;
return crypto.subtle.importKey("raw", _classPrivateFieldGet(_sessionKey, this), {
name: _classPrivateFieldGet(_algorithm, this)
}, false, ["decrypt"]);
case 6:
key = _context4.sent;
encrypted = new Uint8Array([].concat(_toConsumableArray(ciphertext), _toConsumableArray(tag)));
_context4.next = 10;
return crypto.subtle.decrypt({
name: _classPrivateFieldGet(_algorithm, this),
iv: iv
}, key, encrypted);
case 10:
decrypted = _context4.sent;
return _context4.abrupt("return", JSON.parse(new TextDecoder().decode(decrypted)));
case 12:
case "end":
return _context4.stop();
}
}, _callee4, this);
}));
return _decrypt2.apply(this, arguments);
}
function _signClaim() {
return _signClaim2.apply(this, arguments);
}
function _signClaim2() {
_signClaim2 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee5() {
var timestamp, payload, signature;
return _regeneratorRuntime.wrap(function _callee5$(_context5) {
while (1) switch (_context5.prev = _context5.next) {
case 0:
timestamp = Date.now();
payload = {
deviceID: _classPrivateFieldGet(_deviceID, this) || null,
timestamp: timestamp,
userAgent: _classPrivateFieldGet(_userAgent, this)
};
_context5.next = 4;
return _assertClassBrand(_VeriLink_brand, this, _hmac).call(this, _classPrivateFieldGet(_sessionKey, this), JSON.stringify(payload));
case 4:
signature = _context5.sent;
return _context5.abrupt("return", {
signature: signature,
timestamp: timestamp,
deviceID: payload.deviceID
});
case 6:
case "end":
return _context5.stop();
}
}, _callee5, this);
}));
return _signClaim2.apply(this, arguments);
}
function _hmac(_x5, _x6) {
return _hmac2.apply(this, arguments);
}
function _hmac2() {
_hmac2 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee6(keyBuffer, data) {
var key, encoded, signature;
return _regeneratorRuntime.wrap(function _callee6$(_context6) {
while (1) switch (_context6.prev = _context6.next) {
case 0:
_context6.next = 2;
return crypto.subtle.importKey("raw", keyBuffer, {
name: "HMAC",
hash: "SHA-256"
}, false, ["sign"]);
case 2:
key = _context6.sent;
encoded = new TextEncoder().encode(data);
_context6.next = 6;
return crypto.subtle.sign("HMAC", key, encoded);
case 6:
signature = _context6.sent;
return _context6.abrupt("return", _assertClassBrand(_VeriLink_brand, this, _arrayBufferToBase).call(this, signature));
case 8:
case "end":
return _context6.stop();
}
}, _callee6, this);
}));
return _hmac2.apply(this, arguments);
}
function _generateLink() {
var length = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 16;
var array = new Uint8Array(length);
crypto.getRandomValues(array);
return Array.from(array).map(function (b) {
return b.toString(16).padStart(2, "0");
}).join("");
}
function _loadOrGenerateSessionKey() {
if (this.persistSessionKey) {
var stored = localStorage.getItem(_classPrivateFieldGet(_vaultSGN, this));
if (stored) {
return _assertClassBrand(_VeriLink_brand, this, _unmutateFromStorage).call(this, stored).buffer;
}
}
var key = crypto.getRandomValues(new Uint8Array(32));
if (this.persistSessionKey) {
localStorage.setItem(_classPrivateFieldGet(_vaultSGN, this), _assertClassBrand(_VeriLink_brand, this, _mutateForStorage).call(this, key.buffer));
}
return key.buffer;
}
function _loadOrGenerateSessionID(persist) {
var _crypto$randomUUID, _crypto;
if (persist) {
var stored = localStorage.getItem(_classPrivateFieldGet(_linkSGN, this));
if (stored) {
return stored;
}
}
var newID = "vl-".concat(((_crypto$randomUUID = (_crypto = crypto).randomUUID) === null || _crypto$randomUUID === void 0 ? void 0 : _crypto$randomUUID.call(_crypto)) || _assertClassBrand(_VeriLink_brand, this, _fallbackUUID).call(this));
if (persist) {
localStorage.setItem(_classPrivateFieldGet(_linkSGN, this), newID);
}
return newID;
}
function _fallbackUUID() {
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, function (c) {
return (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16);
});
}
function _mutateForStorage(buffer) {
var _this = this;
var bytes = new Uint8Array(buffer);
var mutated = bytes.map(function (b, i) {
return b ^ _classPrivateFieldGet(_mutator, _this)[i % _classPrivateFieldGet(_mutator, _this).length];
});
return btoa(String.fromCharCode.apply(String, _toConsumableArray(mutated)));
}
function _unmutateFromStorage(base64) {
var _this2 = this;
var bytes = Uint8Array.from(atob(base64), function (c) {
return c.charCodeAt(0);
});
var restored = bytes.map(function (b, i) {
return b ^ _classPrivateFieldGet(_mutator, _this2)[i % _classPrivateFieldGet(_mutator, _this2).length];
});
return restored;
}
function _arrayBufferToBase(buffer) {
var binary = "";
var bytes = new Uint8Array(buffer);
for (var i = 0; i < bytes.byteLength; i++) {
binary += String.fromCharCode(bytes[i]);
}
return btoa(binary);
}
function _base64ToArrayBuffer(base64) {
var binary = atob(base64);
var bytes = new Uint8Array(binary.length);
for (var i = 0; i < binary.length; i++) {
bytes[i] = binary.charCodeAt(i);
}
return bytes.buffer;
}
;
export { VeriLink };