UNPKG

gowl-client-lib

Version:
447 lines (436 loc) 16.2 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // web/index.ts var web_exports = {}; __export(web_exports, { BigIntFromBase64: () => BigIntFromBase64, BigIntToByteArray: () => BigIntToByteArray, BytesToBigInt: () => BytesToBigInt, CalculateCofactor: () => CalculateCofactor, Client: () => Client, CompareTo: () => CompareTo, CurveMap: () => CurveMap, EncodeToBase64: () => EncodeToBase64, GenerateKey: () => GenerateKey, GenerateZKPGProvided: () => GenerateZKPGProvided, GetCurve: () => GetCurve, GetG: () => GetG, HMac: () => HMac, Hash: () => Hash, HighEntropyRandom: () => HighEntropyRandom, IntTo4Bytes: () => IntTo4Bytes, KeyTags: () => KeyTags, Keys: () => Keys, ModuloN: () => ModuloN, PointFromBase64: () => PointFromBase64, SupportedCurves: () => SupportedCurves, ToBytes: () => ToBytes, UrlSafeBase64Decode: () => UrlSafeBase64Decode, UrlSafeBase64Encode: () => UrlSafeBase64Encode, VerifyZKP: () => VerifyZKP, p256: () => import_p256.p256, p384: () => import_p384.p384, p521: () => import_p521.p521 }); module.exports = __toCommonJS(web_exports); // web/types.ts var import_p256 = require("@noble/curves/p256"); var import_p384 = require("@noble/curves/p384"); var import_p521 = require("@noble/curves/p521"); var import_secp256k1 = require("@noble/curves/secp256k1"); var SupportedCurves = /* @__PURE__ */ ((SupportedCurves4) => { SupportedCurves4[SupportedCurves4["P256"] = 256] = "P256"; SupportedCurves4[SupportedCurves4["P384"] = 384] = "P384"; SupportedCurves4[SupportedCurves4["P521"] = 521] = "P521"; SupportedCurves4[SupportedCurves4["SECP256K1"] = 2561] = "SECP256K1"; return SupportedCurves4; })(SupportedCurves || {}); var CurveMap = { [256 /* P256 */]: import_p256.p256, [384 /* P384 */]: import_p384.p384, [521 /* P521 */]: import_p521.p521, [2561 /* SECP256K1 */]: import_secp256k1.secp256k1 }; var Keys = /* @__PURE__ */ ((Keys2) => { Keys2["Session"] = "session_key"; Keys2["Confirmation"] = "confirmation_key"; return Keys2; })(Keys || {}); var KeyTags = /* @__PURE__ */ ((KeyTags2) => { KeyTags2["ClientKC"] = "KC_1_U"; KeyTags2["ServerKC"] = "KC_1_V"; return KeyTags2; })(KeyTags || {}); // web/ops.ts var import_utils2 = require("@noble/curves/abstract/utils"); // web/encoding.ts var import_utils = require("@noble/curves/abstract/utils"); // web/ecc_ops.ts function GetG(curve) { const c = GetCurve(curve); return GetCurve(curve).ProjectivePoint.fromAffine({ x: c.CURVE.Gx, y: c.CURVE.Gy }); } function CalculateCofactor(curve) { return GetCurve(curve).CURVE.h; } function GetCurve(curve) { const ChooseCurve = CurveMap[curve]; if (!ChooseCurve) throw new Error("Invalid curve"); return ChooseCurve; } // web/encoding.ts function BigIntToByteArray(int) { let hexString = int.toString(16); if (hexString.length % 2) hexString = "0" + hexString; const byteArray = new Uint8Array(hexString.length / 2); for (let i = 0; i < byteArray.length; i++) byteArray[i] = parseInt(hexString.substr(i * 2, 2), 16); return byteArray; } function EncodeToBase64(data) { let bytes; if (data instanceof BigInt || typeof data == "bigint") bytes = BigIntToByteArray(data); else if (data instanceof Uint8Array) bytes = data; else throw new Error("Invalid type passed to encodeToBase64"); return UrlSafeBase64Encode(bytes); } function BigIntFromBase64(base64) { return (0, import_utils.bytesToNumberBE)(UrlSafeBase64Decode(base64)); } function PointFromBase64(curve, base64) { return GetCurve(curve).ProjectivePoint.fromHex((0, import_utils.bytesToHex)(UrlSafeBase64Decode(base64))); } function UrlSafeBase64Encode(data) { if (typeof data === "bigint") data = BigIntToByteArray(data); const base64 = Buffer.from(data).toString("base64"); return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); } function UrlSafeBase64Decode(data) { return new Uint8Array(Buffer.from(data.replace(/-/g, "+").replace(/_/g, "/"), "base64")); } // web/ops.ts function ModuloN(x, n) { return (x % n + n) % n; } async function HighEntropyRandom(from, to) { const range = to - from; const rangeBytes = BigIntToByteArray(range); let randomBigInt; do { const randomBytes = new Uint8Array(rangeBytes.length); crypto.getRandomValues(randomBytes); randomBigInt = (0, import_utils2.bytesToNumberBE)(randomBytes); } while (randomBigInt >= range); return from + randomBigInt; } async function GenerateKey(curve) { return await HighEntropyRandom(1n, GetCurve(curve).CURVE.n - 1n); } function IntTo4Bytes(i) { return new Uint8Array([i >> 24, i >> 16, i >> 8, i]); } function ToBytes(data) { if (data instanceof Uint8Array) { const len = IntTo4Bytes(data.length); return (0, import_utils2.concatBytes)(len, data); } else if (typeof data == "bigint") { let bytes = BigIntToByteArray(data); const sign = bytes[0] >= 128 ? 0 : 1; bytes = new Uint8Array([sign, ...bytes]); const len = IntTo4Bytes(bytes.length); return (0, import_utils2.concatBytes)(len, bytes); } else if (typeof data == "string") { const bytes = new TextEncoder().encode(data); const len = IntTo4Bytes(data.length); return (0, import_utils2.concatBytes)(len, bytes); } else if (Object.keys(data).length === 2 && "V" in data && "r" in data) { const vBytes = data.V.toRawBytes(); const rBytes = BigIntToByteArray(data.r); const vLen = IntTo4Bytes(vBytes.length); const rLen = IntTo4Bytes(rBytes.length); return (0, import_utils2.concatBytes)(vLen, vBytes, rLen, rBytes); } throw new Error("Invalid type passed to toBytes"); } function CompareTo(a, b) { if (a < b) return -1; if (a > b) return 1; return 0; } function BytesToBigInt(bytes) { let hex = Array.from(bytes).map((byte) => byte.toString(16).padStart(2, "0")).join(""); return BigInt("0x" + hex); } // web/hash.ts var import_utils3 = require("@noble/curves/abstract/utils"); async function Hash(...args) { const bytes = (0, import_utils3.concatBytes)(...args.map(ToBytes)); const hash = await crypto.subtle.digest("SHA-256", bytes); return (0, import_utils3.bytesToNumberBE)(new Uint8Array(hash)); } // web/hmac.ts var import_utils4 = require("@noble/curves/abstract/utils"); async function HMac(key, messageString, senderID, receiverID, senderKey1, senderKey2, receiverKey1, receiverKey2) { const keyBytes = BigIntToByteArray(key); const mac = await crypto.subtle.importKey("raw", keyBytes, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]); const data = [ new TextEncoder().encode(messageString), new TextEncoder().encode(senderID), new TextEncoder().encode(receiverID), senderKey1, senderKey2, receiverKey1, receiverKey2 ]; const signature = await crypto.subtle.sign("HMAC", mac, (0, import_utils4.concatBytes)(...data)); return BytesToBigInt(new Uint8Array(signature)); } // web/schnorr.ts async function GenerateZKPGProvided(curve, g, n, x, X, prover) { const v = await GenerateKey(curve); const V = g.multiply(v); const h = await Hash(g.toRawBytes(), V.toRawBytes(), X.toRawBytes(), prover); let r = x * h; r = v - r; r = ModuloN(r, n); return { V, r }; } async function VerifyZKP(curve, generator, X, zkp, prover) { const h = await Hash(generator.toRawBytes(), zkp.V.toRawBytes(), X.toRawBytes(), prover); if (!X || !zkp.V || !zkp.r) return false; try { X.assertValidity(); } catch { return false; } const { x: xX, y: yY } = X.toAffine(); if (xX === null || yY === null) return false; const curveParams = GetCurve(curve); if (CompareTo(xX, BigInt(0)) === -1 || CompareTo(xX, curveParams.CURVE.n - BigInt(1)) === 1) return false; if (CompareTo(yY, BigInt(0)) === -1 || CompareTo(yY, curveParams.CURVE.n - BigInt(1)) === 1) return false; const xXh = X.multiply(CalculateCofactor(curve)); const { x: xXhX, y: xXhY } = xXh.toAffine(); if (xXhX === null || xXhY === null) return false; if (xXhX === BigInt(0) && xXhY === BigInt(0)) return false; const gRxhmn = generator.multiply(zkp.r).add(X.multiply(ModuloN(h, curveParams.CURVE.n))); return zkp.V.equals(gRxhmn); } // web/client.ts var Client = class { userName; password; server; curveKey; curve; G; N; clientAuthInit; clientAuthVerify; clientKCKey; clientSessionKey; constructor(userName, password, server, curve) { this.userName = userName; this.password = password; this.server = server; this.curveKey = curve; this.curve = GetCurve(curve); this.G = GetG(curve); this.N = this.curve.CURVE.n; } async Register() { try { if (!this.userName || !this.password) throw new Error("Invalid user name or password"); if (this.userName === this.server) throw new Error("User name cannot be the same as server name"); const t = ModuloN(await Hash(this.userName, this.password), this.curve.CURVE.n); const T = this.G.multiply(t); const PI = ModuloN(await Hash(t), this.curve.CURVE.n); return { User: this.userName, PI: EncodeToBase64(PI), T: EncodeToBase64(T.toRawBytes()) }; } catch (e) { console.error(e); return new Error("Failed to register user"); } } async AuthInit() { try { if (!this.userName || !this.password) throw new Error("Invalid user name or password"); if (this.userName === this.server) throw new Error("User name cannot be the same as server name"); const t = ModuloN(await Hash(this.userName, this.password), this.curve.CURVE.n); const T = this.G.multiply(t); const PI = ModuloN(await Hash(t), this.curve.CURVE.n); const x1 = await GenerateKey(this.curveKey); const X1 = this.G.multiply(x1); const PI1 = await GenerateZKPGProvided(this.curveKey, this.G, this.N, x1, X1, this.userName); const x2 = await GenerateKey(this.curveKey); const X2 = this.G.multiply(x2); const PI2 = await GenerateZKPGProvided(this.curveKey, this.G, this.N, x2, X2, this.userName); this.clientAuthInit = { PI, t, T, x1, x2, X1, X2, PI1, PI2 }; return { User: this.userName, X1: EncodeToBase64(X1.toRawBytes()), X2: EncodeToBase64(X2.toRawBytes()), PI1_V: EncodeToBase64(PI1.V.toRawBytes()), PI2_V: EncodeToBase64(PI2.V.toRawBytes()), PI1_R: EncodeToBase64(PI1.r), PI2_R: EncodeToBase64(PI2.r) }; } catch (e) { console.error(e); return new Error("Failed to authenticate (AuthInit)"); } } ParseServerInit(serverInit) { try { const [X3, X4, PI3, PI4, Beta, PIBeta] = [ PointFromBase64(this.curveKey, serverInit.X3), PointFromBase64(this.curveKey, serverInit.X4), { V: PointFromBase64(this.curveKey, serverInit.PI3_V), r: BigIntFromBase64(serverInit.PI3_R) }, { V: PointFromBase64(this.curveKey, serverInit.PI4_V), r: BigIntFromBase64(serverInit.PI4_R) }, PointFromBase64(this.curveKey, serverInit.Beta), { V: PointFromBase64(this.curveKey, serverInit.PIBeta_V), r: BigIntFromBase64(serverInit.PIBeta_R) } ]; return { X3, X4, PI3, PI4, Beta, PIBeta }; } catch (e) { console.error(e); return new Error("Failed to authenticate (ParseServerInit)"); } } async AuthVerify(serverInit) { const parsedRequest = this.ParseServerInit(serverInit); if (parsedRequest instanceof Error) return parsedRequest; this.clientAuthVerify = parsedRequest; try { if (!this.clientAuthInit) throw new Error("AuthInit must be called before AuthVerify"); if (!this.clientAuthVerify) throw new Error("Failed to authenticate (Init)"); const { X3, X4, PI3, PI4, Beta, PIBeta } = this.clientAuthVerify; const { x1, x2, X1, X2, PI1, PI2, t, PI } = this.clientAuthInit; if (!await VerifyZKP(this.curveKey, this.G, X3, PI3, this.server)) throw new Error("Failed to authenticate PI3 (Verify)"); if (!await VerifyZKP(this.curveKey, this.G, X4, PI4, this.server)) throw new Error("Failed to authenticate PI4 (Verify)"); const GBeta = X1.add(X2).add(X3); if (!await VerifyZKP(this.curveKey, GBeta, Beta, PIBeta, this.server)) throw new Error("Failed to authenticate PIBeta (Verify)"); const GAlpha = X1.add(X3).add(X4); const x2pi = ModuloN(x2 * PI, this.N); const Alpha = GAlpha.multiply(x2pi); const PIAlpha = await GenerateZKPGProvided(this.curveKey, GAlpha, this.N, x2pi, Alpha, this.userName); let rawClientKey = Beta.subtract(X4.multiply(x2pi)); rawClientKey = rawClientKey.multiply(x2); this.clientSessionKey = await Hash(rawClientKey.toRawBytes(), "session_key" /* Session */); this.clientKCKey = await Hash(rawClientKey.toRawBytes(), "confirmation_key" /* Confirmation */); const hTranscript = await Hash( rawClientKey.toRawBytes(), this.userName, X1.toRawBytes(), X2.toRawBytes(), PI1, PI2, this.server, X3.toRawBytes(), X4.toRawBytes(), PI3, PI4, Beta.toRawBytes(), PIBeta, Alpha.toRawBytes(), PIAlpha ); const rValue = ModuloN(x1 - t * hTranscript, this.N); const clientKCTag = await HMac( this.clientKCKey, "KC_1_U" /* ClientKC */, this.userName, this.server, X1.toRawBytes(), X2.toRawBytes(), X3.toRawBytes(), X4.toRawBytes() ); return { Alpha: EncodeToBase64(Alpha.toRawBytes()), PIAlpha_V: EncodeToBase64(PIAlpha.V.toRawBytes()), PIAlpha_R: EncodeToBase64(PIAlpha.r), R: EncodeToBase64(rValue), ClientKCTag: EncodeToBase64(clientKCTag) }; } catch (e) { console.error(e); return Error("Failed to authenticate (Verify)"); } } async ValidateServer(serverVerify) { try { if (!this.clientKCKey) throw new Error("AuthVerify must be called before ValidateServer"); if (!this.clientAuthInit) throw new Error("AuthVerify must be called before ValidateServer"); if (!this.clientAuthVerify) throw new Error("AuthVerify must be called before ValidateServer"); const { X3, X4 } = this.clientAuthVerify; const { X1, X2 } = this.clientAuthInit; const serverKcTag2 = await HMac( this.clientKCKey, "KC_1_V" /* ServerKC */, this.server, this.userName, X3.toRawBytes(), X4.toRawBytes(), X1.toRawBytes(), X2.toRawBytes() ); const serverKCTagc = BigIntFromBase64(serverVerify.ServerKCTag); if (CompareTo(serverKcTag2, serverKCTagc) !== 0) throw new Error("Failed to validate server (KCTag)"); } catch (e) { console.error(e); return new Error("Failed to validate server (KCTag)"); } } GetSessionKey() { if (!this.clientSessionKey) return new Error("Session key not generated"); return this.clientSessionKey; } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { BigIntFromBase64, BigIntToByteArray, BytesToBigInt, CalculateCofactor, Client, CompareTo, CurveMap, EncodeToBase64, GenerateKey, GenerateZKPGProvided, GetCurve, GetG, HMac, Hash, HighEntropyRandom, IntTo4Bytes, KeyTags, Keys, ModuloN, PointFromBase64, SupportedCurves, ToBytes, UrlSafeBase64Decode, UrlSafeBase64Encode, VerifyZKP, p256, p384, p521 }); //# sourceMappingURL=index.js.map