let p, q, pubKey, privKey;

class PubKey {
    constructor(p, q) {
        this.n = p * q;
        // this.g = this.n + 1n;
    }

    encrypt(m) {
        // Compute g^m r^n mod n^2
        let r = random2048();

        // Resample to avoid modulo bias.
        while (r >= this.n) {
            r = random2048();
        }

        // Compute g^m by binomial theorem.
        let gm = (1n + this.n * m) % this.n ** 2n;
        // Compute g^m r^n from crt
        return (gm * fastModularExponentiation(r, this.n, this.n ** 2n)) % this.n ** 2n;
    }
}

class PrivKey {
    constructor(p, q) {
        this.n = p * q;
        this.lambda = (p - 1n) * (q - 1n);
        this.mu = fastModularExponentiation(this.lambda, this.lambda - 1n, this.n);
    }

    decrypt(c) {
        return (
            (((fastModularExponentiation(c, this.lambda, this.n ** 2n) - 1n) / this.n) *
                this.mu) %
            this.n
        );
    }
}

document.addEventListener("DOMContentLoaded", () => {
    if (window.sessionStorage.getItem("p") === null) {
        p = generate_prime();
        window.sessionStorage.setItem("p", p);
    } else {
        p = BigInt(window.sessionStorage.getItem("p"));
    }

    if (window.sessionStorage.getItem("q") === null) {
        q = generate_prime();
        window.sessionStorage.setItem("q", q);
    } else {
        q = BigInt(window.sessionStorage.getItem("q"));
    }

    pubKey = new PubKey(p, q);
    privKey = new PrivKey(p, q);
});
