gowl-client-lib
Version:
The client library for GOWL.
447 lines (436 loc) • 16.2 kB
JavaScript
"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