"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RSA = exports.RSAUtilsImpl = exports.ShaVersion = void 0;
const utils_1 = require("../utils");
var ShaVersion;
(function (ShaVersion) {
    ShaVersion["Sha1"] = "sha-1";
    ShaVersion["Sha256"] = "sha-256";
})(ShaVersion || (exports.ShaVersion = ShaVersion = {}));
class RSAUtilsImpl {
    constructor(crypto = typeof window !== 'undefined' ? window.crypto : typeof self !== 'undefined' ? self.crypto : {}) {
        /********* RSA Config **********/
        //TODO bigger modulus
        //TODO PSS for signing
        this.rsaParams = { name: 'RSA-OAEP' };
        // RSA params for 'import' and 'generate' function.
        this.rsaHashedParams = {
            name: 'RSA-OAEP',
            modulusLength: 2048,
            publicExponent: new Uint8Array([0x01, 0x00, 0x01]), // Equivalent to 65537 (Fermat F4), read http://en.wikipedia.org/wiki/65537_(number)
            hash: { name: 'sha-1' },
        };
        this.rsaWithSha256HashedParams = {
            name: 'RSA-OAEP',
            modulusLength: 2048,
            publicExponent: new Uint8Array([0x01, 0x00, 0x01]), // Equivalent to 65537 (Fermat F4), read http://en.wikipedia.org/wiki/65537_(number)
            hash: { name: 'SHA-256' },
        };
        this.signatureKeysGenerationParams = {
            name: 'RSA-PSS',
            modulusLength: 2048,
            publicExponent: new Uint8Array([0x01, 0x00, 0x01]), // Equivalent to 65537 (Fermat F4), read http://en.wikipedia.org/wiki/65537_(number)
            hash: { name: 'SHA-256' },
        };
        this.crypto = crypto;
    }
    /**
     * Generates a key pair for encryption/decryption of data.
     * @param shaVersion the version of the SHA algorithm to use.
     */
    generateKeyPair(shaVersion) {
        const extractable = true;
        const keyUsages = ['decrypt', 'encrypt'];
        const rsaParams = this.paramsForCreationOrImport(shaVersion);
        return new Promise((resolve, reject) => {
            this.crypto.subtle.generateKey(rsaParams, extractable, keyUsages).then(resolve, reject);
        });
    }
    /**
     * Generates a key pair for signing data and signature verification.
     */
    generateSignatureKeyPair() {
        return __awaiter(this, void 0, void 0, function* () {
            return yield this.crypto.subtle.generateKey(this.signatureKeysGenerationParams, true, ['sign', 'verify']);
        });
    }
    exportKeys(keyPair, privKeyFormat, pubKeyFormat) {
        const pubPromise = this.crypto.subtle.exportKey(pubKeyFormat, keyPair.publicKey);
        const privPromise = this.crypto.subtle.exportKey(privKeyFormat, keyPair.privateKey);
        return Promise.all([pubPromise, privPromise]).then(function (results) {
            return {
                publicKey: results[0],
                privateKey: results[1],
            };
        });
    }
    exportKey(cryptoKey, format) {
        return new Promise((resolve, reject) => {
            this.crypto.subtle.exportKey(format, cryptoKey).then(resolve, reject);
        });
    }
    /**
     *
     * @param publicKey (CryptoKey)
     * @param plainData (Uint8Array)
     */
    encrypt(publicKey, plainData) {
        return new Promise((resolve, reject) => {
            this.crypto.subtle.encrypt(this.rsaParams, publicKey, (0, utils_1.ua2ab)(plainData)).then(resolve, reject);
        });
    }
    /**
     *
     * @param privateKey (CryptoKey)
     * @param encryptedData (Uint8Array)
     */
    decrypt(privateKey, encryptedData) {
        return new Promise((resolve, reject) => {
            this.crypto.subtle.decrypt(this.rsaParams, privateKey, (0, utils_1.ua2ab)(encryptedData)).then(resolve, reject);
        });
    }
    /**
     *
     * @param format 'jwk', 'spki', or 'pkcs8'
     * @param keydata should be the key data based on the format.
     * @param keyUsages Array of usages. For example, ['encrypt'] for public key.
     * @param hashAlgorithm 'sha-1' or 'sha-256'
     * @returns {*}
     */
    importKey(format, keydata, keyUsages, hashAlgorithm) {
        const extractable = true;
        return new Promise((resolve, reject) => {
            const rsaParams = this.paramsForCreationOrImport(hashAlgorithm);
            this.crypto.subtle.importKey(format, keydata, rsaParams, extractable, keyUsages).then(resolve, reject);
        });
    }
    importSignatureKey(format, keydata) {
        const extractable = true;
        return this.crypto.subtle.importKey(format, keydata, this.signatureKeysGenerationParams, extractable, ['sign']);
    }
    importVerificationKey(format, keydata) {
        const extractable = true;
        return this.crypto.subtle.importKey(format, keydata, this.signatureKeysGenerationParams, extractable, ['verify']);
    }
    paramsForCreationOrImport(shaVersion) {
        if (shaVersion === 'sha-1') {
            return this.rsaHashedParams;
        }
        else if (shaVersion === 'sha-256') {
            return this.rsaWithSha256HashedParams;
        }
        else {
            throw new Error('Unexpected error, invalid SHA version');
        }
    }
    /**
     *
     * @param format 'jwk' or 'pkcs8'
     * @param keydata should be the key data based on the format.
     * @param hashAlgorithm 'sha-1' or 'sha-256'
     * @returns {*}
     */
    importPrivateKey(format, keydata, hashAlgorithm) {
        const extractable = true;
        return new Promise((resolve, reject) => {
            const rsaParams = this.paramsForCreationOrImport(hashAlgorithm);
            this.crypto.subtle.importKey(format, keydata, rsaParams, extractable, ['decrypt']).then(resolve, reject);
        });
    }
    /**
     *
     * @param privateKeyFormat 'jwk' or 'pkcs8'
     * @param privateKeydata    should be the key data based on the format.
     * @param publicKeyFormat 'jwk' or 'spki'
     * @param publicKeyData should be the key data based on the format.
     * @param hashAlgorithm 'sha-1' or 'sha-256'
     * @returns {Promise|*}
     */
    importKeyPair(privateKeyFormat, privateKeydata, publicKeyFormat, publicKeyData, hashAlgorithm) {
        const extractable = true;
        const rsaParams = this.paramsForCreationOrImport(hashAlgorithm);
        const privPromise = this.crypto.subtle.importKey(privateKeyFormat, privateKeydata, rsaParams, extractable, ['decrypt']);
        const pubPromise = this.crypto.subtle.importKey(publicKeyFormat, publicKeyData, rsaParams, extractable, ['encrypt']);
        return Promise.all([pubPromise, privPromise]).then(function (results) {
            return {
                publicKey: results[0],
                privateKey: results[1],
            };
        });
    }
    /**
     * Tries to encrypt then decrypt data using a keypair. If both operations succeed without throwing an error and the decrypted data matches the
     * original data returns true, else false.
     * @param keyPair a key pair.
     * @return if the key pair could be successfully used to encrypt then decrypt data.
     */
    checkKeyPairValidity(keyPair) {
        return __awaiter(this, void 0, void 0, function* () {
            try {
                const text = 'shibboleth';
                const encryptedText = yield this.encrypt(keyPair.publicKey, (0, utils_1.utf8_2ua)(text));
                const decryptedText = (0, utils_1.ua2utf8)(yield this.decrypt(keyPair.privateKey, new Uint8Array(encryptedText)));
                return decryptedText === text;
            }
            catch (e) {
                return false;
            }
        });
    }
    /**
     * Generates a signature for some data. The signature algorithm used is RSA-PSS with SHA-256.
     * @param privateKey private key to use for signature
     * @param data the data to sign
     * @return the signature.
     */
    sign(privateKey, data) {
        return __awaiter(this, void 0, void 0, function* () {
            return yield this.crypto.subtle.sign({ name: 'RSA-PSS', saltLength: 32 }, privateKey, data);
        });
    }
    /**
     * Verifies if a signature matches the data. The signature algorithm used is RSA-PSS with sha-256.
     * @param publicKey public key to use for signature verification.
     * @param signature the signature to verify
     * @param data the data that was signed
     * @return if the signature matches the data and key.
     */
    verifySignature(publicKey, signature, data) {
        return __awaiter(this, void 0, void 0, function* () {
            return yield this.crypto.subtle.verify({ name: 'RSA-PSS', saltLength: 32 }, publicKey, signature, data);
        });
    }
}
exports.RSAUtilsImpl = RSAUtilsImpl;
exports.RSA = new RSAUtilsImpl();
//# sourceMappingURL=RSA.js.map