UNPKG

gowl-client-lib

Version:
393 lines (384 loc) 14.1 kB
// web/types.ts import { p256 } from "@noble/curves/p256"; import { p384 } from "@noble/curves/p384"; import { p521 } from "@noble/curves/p521"; import { secp256k1 } from "@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 */]: p256, [384 /* P384 */]: p384, [521 /* P521 */]: p521, [2561 /* 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 import { bytesToNumberBE as bytesToNumberBE2, concatBytes } from "@noble/curves/abstract/utils"; // web/encoding.ts import { bytesToHex, bytesToNumberBE } from "@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 bytesToNumberBE(UrlSafeBase64Decode(base64)); } function PointFromBase64(curve, base64) { return GetCurve(curve).ProjectivePoint.fromHex(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 = bytesToNumberBE2(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 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 concatBytes(len, bytes); } else if (typeof data == "string") { const bytes = new TextEncoder().encode(data); const len = IntTo4Bytes(data.length); return 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 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 import { bytesToNumberBE as bytesToNumberBE3, concatBytes as concatBytes2 } from "@noble/curves/abstract/utils"; async function Hash(...args) { const bytes = concatBytes2(...args.map(ToBytes)); const hash = await crypto.subtle.digest("SHA-256", bytes); return bytesToNumberBE3(new Uint8Array(hash)); } // web/hmac.ts import { concatBytes as concatBytes3 } from "@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, concatBytes3(...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; } }; export { 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.mjs.map