@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
175 lines (174 loc) • 6.4 kB
JavaScript
"use strict";
function _classPrivateMethodInitSpec(e, a) { _checkPrivateRedeclaration(e, a), a.add(e); }
function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); }
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"); }
const crypto = require("crypto");
const axios = require("axios");
var _VeriLink_brand = /*#__PURE__*/new WeakSet();
class VeriLink {
constructor(options = {}) {
_classPrivateMethodInitSpec(this, _VeriLink_brand);
this.persistSessionKey = options.persistSessionKey || false;
this.persistSessionID = options.persistSessionID || false;
this.mutator = typeof options.mutator === "string" ? new Uint8Array(Buffer.from(options.mutator)) : options.mutator || new Uint8Array(Buffer.from("vlk"));
this.userAgent = options.userAgent || "verilink-node";
this.deviceID = options.deviceID || null;
this.vaultSGN = options.vaultSGN || "vlx";
this.linkSGN = options.linkSGN || "vli";
this.serverURL = options.serverURL || "";
this.algorithm = "aes-256-gcm";
this.ivLength = 12;
if (options.sessionKey) {
this.setKey(options.sessionKey);
} else {
this.sessionKey = _assertClassBrand(_VeriLink_brand, this, _loadOrGenerateSessionKey).call(this);
}
if (options.sessionID) {
this.sessionID = options.sessionID;
} else {
this.sessionID = _assertClassBrand(_VeriLink_brand, this, _loadOrGenerateSessionID).call(this);
}
}
setServer(url) {
this.serverURL = url;
}
setKey(key) {
if (typeof key === "string") {
this.sessionKey = Buffer.from(key, "base64");
} else if (Buffer.isBuffer(key)) {
this.sessionKey = key;
} else {
throw new Error("Invalid session key data (Base64 string or Buffer)");
}
}
async pair(pairingURL = "/device/pair") {
const sessionKey = this.sessionKey.toString("base64");
const sessionID = this.sessionID;
const response = await axios.post(this.serverURL + pairingURL, {
sessionKey
}, {
headers: {
"x-vlink-session-id": sessionID
}
});
return response.data;
}
async send(method, path, data = {}, headers = {}, options = {
fullResponse: false
}) {
const methodUpper = method.toUpperCase();
const url = this.serverURL + path;
const link = _assertClassBrand(_VeriLink_brand, this, _generateLink).call(this);
const vlinkID = _assertClassBrand(_VeriLink_brand, this, _hmac).call(this, this.sessionKey, link);
const encrypted = _assertClassBrand(_VeriLink_brand, this, _encrypt).call(this, data);
const claim = _assertClassBrand(_VeriLink_brand, this, _signClaim).call(this);
const config = {
method,
url,
headers: {
"Content-Type": "application/json",
"x-vlink-claim-timestamp": claim.timestamp,
"x-vlink-session-id": this.sessionID,
"x-vlink-device": claim.deviceID,
"x-vlink-claim": claim.signature,
"x-vlink-id": vlinkID,
"x-vlink": link,
...headers
}
};
if (methodUpper === "GET") {
config.params = {
encrypted
};
} else {
config.data = {
encrypted
};
}
const response = await axios(config);
if (response.data?.encrypted) {
const decrypted = _assertClassBrand(_VeriLink_brand, this, _decrypt).call(this, response.data.encrypted);
return options.fullResponse ? {
headers: response.headers,
status: response.status,
data: decrypted,
response
} : decrypted;
}
return options.fullResponse ? {
headers: response.headers,
status: response.status,
data: response.data,
response
} : response.data;
}
mutateForStorage(buffer) {
const bytes = new Uint8Array(buffer);
const mutated = bytes.map((b, i) => b ^ this.mutator[i % this.mutator.length]);
return Buffer.from(mutated).toString("base64");
}
unmutateFromStorage(base64) {
const bytes = Buffer.from(base64, "base64");
const restored = bytes.map((b, i) => b ^ this.mutator[i % this.mutator.length]);
return Buffer.from(restored);
}
getMutatorFingerprint() {
return crypto.createHash("sha256").update(this.mutator).digest("hex").slice(0, 12);
}
getSession() {
return {
sessionKey: this.sessionKey.toString("base64"),
fingerprint: this.getMutatorFingerprint(),
sessionID: this.sessionID
};
}
}
function _encrypt(data) {
const iv = crypto.randomBytes(this.ivLength);
const cipher = crypto.createCipheriv(this.algorithm, this.sessionKey, iv);
const json = JSON.stringify(data);
const encrypted = Buffer.concat([cipher.update(json, "utf8"), cipher.final()]);
const tag = cipher.getAuthTag();
const full = Buffer.concat([iv, tag, encrypted]);
return full.toString("base64");
}
function _decrypt(b64) {
const buffer = Buffer.from(b64, "base64");
const ciphertext = buffer.slice(28);
const tag = buffer.slice(12, 28);
const iv = buffer.slice(0, 12);
const decipher = crypto.createDecipheriv(this.algorithm, this.sessionKey, iv);
decipher.setAuthTag(tag);
const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
return JSON.parse(decrypted.toString("utf8"));
}
function _signClaim() {
const timestamp = Date.now();
const payload = {
deviceID: this.deviceID || null,
timestamp,
userAgent: this.userAgent
};
return {
signature: _assertClassBrand(_VeriLink_brand, this, _hmac).call(this, this.sessionKey, JSON.stringify(payload)),
timestamp,
deviceID: payload.deviceID
};
}
function _hmac(key, msg) {
return crypto.createHmac("sha256", key).update(msg).digest("base64");
}
function _generateLink(len = 16) {
return crypto.randomBytes(len).toString("hex");
}
function _loadOrGenerateSessionKey() {
const key = crypto.randomBytes(32);
return key;
}
function _loadOrGenerateSessionID() {
return `vl-${crypto.randomUUID?.() || _assertClassBrand(_VeriLink_brand, this, _fallbackUUID).call(this)}`;
}
function _fallbackUUID() {
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c => (c ^ crypto.randomBytes(1)[0] & 15 >> c / 4).toString(16));
}
module.exports = VeriLink;