"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RSAKeyPair = exports.MIN_KEY_LENGTH = void 0;
const crypto_1 = __importDefault(require("crypto"));
const util_1 = require("util");
/**
 * The length of encrypted data determines the minimum length. In StreamrClient we use RSA
 * for encrypting 32 byte GroupKeys. In Node environment 585 bits is enough, but in
 * browser environment we need 640.
 * https://en.wikipedia.org/wiki/Optimal_asymmetric_encryption_padding
 */
exports.MIN_KEY_LENGTH = 640;
function getSubtle() {
    const subtle = typeof window !== 'undefined' ? window?.crypto?.subtle : crypto_1.default.webcrypto.subtle;
    if (!subtle) {
        const url = 'https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto';
        throw new Error(`SubtleCrypto not supported. This feature is available only in secure contexts (HTTPS) & Node 16+. ${url}`);
    }
    return subtle;
}
function ab2str(...args) {
    // @ts-expect-error Uint8Array parameters
    return String.fromCharCode.apply(null, new Uint8Array(...args));
}
// shim browser btoa for node
function btoa(str) {
    if (global.btoa) {
        return global.btoa(str);
    }
    let buffer;
    if (Buffer.isBuffer(str)) {
        buffer = str;
    }
    else {
        buffer = Buffer.from(str.toString(), 'binary');
    }
    return buffer.toString('base64');
}
async function exportCryptoKey(key, { isPrivate = false } = {}) {
    const keyType = isPrivate ? 'pkcs8' : 'spki';
    const exported = await getSubtle().exportKey(keyType, key);
    const exportedAsString = ab2str(exported);
    const exportedAsBase64 = btoa(exportedAsString);
    const TYPE = isPrivate ? 'PRIVATE' : 'PUBLIC';
    return `-----BEGIN ${TYPE} KEY-----\n${exportedAsBase64}\n-----END ${TYPE} KEY-----\n`;
}
class RSAKeyPair {
    // the keys are in PEM format
    privateKey;
    publicKey;
    constructor(privateKey, publicKey) {
        this.privateKey = privateKey;
        this.publicKey = publicKey;
    }
    getPublicKey() {
        return this.publicKey;
    }
    getPrivateKey() {
        return this.privateKey;
    }
    static async create(keyLength) {
        return (typeof window !== 'undefined')
            ? RSAKeyPair.create_browserEnvironment(keyLength)
            : RSAKeyPair.create_serverEnvironment(keyLength);
    }
    static async create_serverEnvironment(keyLength) {
        // promisify here to work around browser/server packaging
        const generateKeyPair = (0, util_1.promisify)(crypto_1.default.generateKeyPair);
        const { publicKey, privateKey } = await generateKeyPair('rsa', {
            modulusLength: keyLength,
            publicKeyEncoding: {
                type: 'spki',
                format: 'pem',
            },
            privateKeyEncoding: {
                type: 'pkcs8',
                format: 'pem',
            },
        });
        return new RSAKeyPair(privateKey, publicKey);
    }
    static async create_browserEnvironment(keyLength) {
        const { publicKey, privateKey } = await getSubtle().generateKey({
            name: 'RSA-OAEP',
            modulusLength: keyLength,
            publicExponent: new Uint8Array([1, 0, 1]), // 65537
            hash: 'SHA-256'
        }, true, ['encrypt', 'decrypt']);
        const [exportedPrivate, exportedPublic] = await Promise.all([
            exportCryptoKey(privateKey, {
                isPrivate: true,
            }),
            exportCryptoKey(publicKey, {
                isPrivate: false,
            })
        ]);
        return new RSAKeyPair(exportedPrivate, exportedPublic);
    }
}
exports.RSAKeyPair = RSAKeyPair;
//# sourceMappingURL=RSAKeyPair.js.map