tiny-crypto-suite
Version: 
Tiny tools, big crypto — seamless encryption and certificate handling for modern web and Node apps.
650 lines (649 loc) • 31.7 kB
JavaScript
import { readFileSync } from 'fs';
import { Buffer } from 'buffer';
import clone from 'clone';
import { isBrowser } from './lib/os.mjs';
import TinyCrypto from './TinyCrypto.mjs';
/**
 * Class representing a certificate and key management utility.
 *
 * This class provides functionality to load, initialize, and manage X.509 certificates and RSA keys.
 * It supports encryption and decryption operations using RSA keys, and also includes utility methods
 * for certificate handling and metadata extraction.
 *
 * @class
 */
class TinyCertCrypto {
    /** @typedef {import('node-forge')} NodeForge */
    /** @typedef {import('node-forge').pki.Certificate} Certificate */
    /** @typedef {import('node-forge').pki.CertificateField} CertificateField */
    /** @typedef {import('node-forge').pki.rsa.PublicKey} PublicKey */
    /** @typedef {import('node-forge').pki.rsa.PrivateKey} PrivateKey */
    /** @typedef {import('node-forge').pki.rsa.EncryptionScheme} EncryptionScheme */
    /** @typedef {import('node-forge').pki.PEM} PEM */
    /** @typedef {Record<string|number, any>|any[]} CryptoResult */
    /** @typedef {string} Base64 */
    /** @type {PublicKey|null} */ publicKey = null;
    /** @type {PrivateKey|null} */ privateKey = null;
    /** @type {Certificate|null} */ publicCert = null;
    /** @type {Record<string, any>|null} */ metadata = null;
    /** @type {string|null} */ source = null;
    /** @type {TinyCrypto|null} */ #tinyCrypto = null;
    /**
     * Regular expression for matching X.509 PEM certificates.
     *
     * This pattern captures the base64-encoded body of a certificate
     * enclosed between `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----`.
     * It supports multi-line content.
     *
     * @type {RegExp}
     */
    #certRegex = /-----BEGIN CERTIFICATE-----([\s\S]+?)-----END CERTIFICATE-----/;
    /**
     * Regular expression for matching RSA PEM keys.
     *
     * This pattern captures both RSA public and private keys, supporting
     * the standard PEM formatting:
     * `-----BEGIN [RSA] PUBLIC|PRIVATE KEY-----` to `-----END ... KEY-----`.
     * It captures the key type (PUBLIC|PRIVATE) and the base64 content.
     *
     * @type {RegExp}
     */
    #keyRegex = /-----BEGIN\s+(?:RSA\s+)?(PUBLIC|PRIVATE)\s+KEY-----([\s\S]+?)-----END\s+(?:RSA\s+)?\1\s+KEY-----/;
    /**
     * Constructs a new instance of TinyCertCrypto.
     *
     * This class provides cross-platform (Node.js and browser) support
     * for handling X.509 certificates and performing RSA-based encryption and decryption.
     *
     * The constructor accepts optional paths or PEM buffers for the public certificate and private key.
     * If used in a browser environment, either `publicCertPath` or `publicCertBuffer` is required.
     *
     * @param {Object} [options={}] - Initialization options.
     * @param {string|null} [options.publicCertPath=null] - Path or URL to the public certificate in PEM format.
     * @param {string|null} [options.privateKeyPath=null] - Path or URL to the private key in PEM format.
     * @param {string|Buffer|null} [options.publicCertBuffer=null] - The public certificate as a PEM string or Buffer.
     * @param {string|Buffer|null} [options.privateKeyBuffer=null] - The private key as a PEM string or Buffer.
     * @param {EncryptionScheme} [options.cryptoType='RSA-OAEP'] - The algorithm identifier used with Crypto API.
     *
     * @throws {Error} If in a browser and neither `publicCertPath` nor `publicCertBuffer` is provided.
     * @throws {Error} If provided buffers are not strings or Buffers.
     */
    constructor({ publicCertPath = null, privateKeyPath = null, publicCertBuffer = null, privateKeyBuffer = null, cryptoType = 'RSA-OAEP', } = {}) {
        if (publicCertBuffer &&
            typeof publicCertBuffer !== 'string' &&
            !Buffer.isBuffer(publicCertBuffer))
            throw new Error('publicCertBuffer must be a string or Buffer');
        if (privateKeyBuffer &&
            typeof privateKeyBuffer !== 'string' &&
            !Buffer.isBuffer(privateKeyBuffer))
            throw new Error('privateKeyBuffer must be a string or Buffer');
        this.cryptoType = cryptoType;
        this.publicCertPath = publicCertPath;
        this.privateKeyPath = privateKeyPath;
        this.publicCertBuffer = publicCertBuffer;
        this.privateKeyBuffer = privateKeyBuffer;
    }
    /**
     * Starts the internal TinyCrypto instance.
     * If an internal TinyCrypto instance is already set, an error will be thrown to prevent overriding it.
     *
     * @param {TinyCrypto} [tinyCrypto] - The TinyCrypto instance to be set.
     * @throws {Error} Throws if TinyCrypto instance has already been set.
     */
    startCrypto(tinyCrypto) {
        if (this.#tinyCrypto)
            throw new Error('TinyCrypto instance is already set.');
        if (typeof tinyCrypto === 'undefined')
            this.#tinyCrypto = new TinyCrypto();
        else
            this.#tinyCrypto = tinyCrypto;
    }
    /**
     * Returns the previously loaded TinyCrypto instance.
     * Assumes the module has already been loaded.
     *
     * @returns {TinyCrypto} The TinyCrypto module.
     */
    getCrypto() {
        if (typeof this.#tinyCrypto === 'undefined' || this.#tinyCrypto === null)
            throw new Error(`Failed to get TinyCrypto: Module is ${this.#tinyCrypto !== null ? 'undefined' : 'null'}.\n` +
                'Please make sure to use this.startCrypto().');
        return this.#tinyCrypto;
    }
    /**
     * Checks if the TinyCrypto instance exists.
     *
     * This method returns `true` if the TinyCrypto module has been set and is not null,
     * otherwise returns `false`.
     *
     * @returns {boolean} Whether the TinyCrypto module exists.
     */
    existsCrypto() {
        return typeof this.#tinyCrypto !== 'undefined' && this.#tinyCrypto !== null ? true : false;
    }
    /**
     * Add a new value type and its converter function.
     * @param {string} typeName
     * @param {(data: any) => any} getFunction
     * @param {(data: any) => { __type: string, value?: any }} convertFunction
     */
    addValueType(typeName, getFunction, convertFunction) {
        return this.getCrypto().addValueType(typeName, getFunction, convertFunction);
    }
    /**
     * Sets the deep serialization and deserialization mode in the TinyCrypto instance.
     * If the argument is a boolean, updates the deep mode accordingly.
     * Throws an error if the value is not a boolean.
     *
     * @param {boolean} value - A boolean indicating whether deep mode should be enabled.
     * @throws {Error} Throws if the provided value is not a boolean.
     */
    setDeepMode(value) {
        const tinyCrypto = this.getCrypto();
        return tinyCrypto.setDeepMode(value);
    }
    /**
     * Dynamically imports the `node-forge` module and stores it in the instance.
     * Ensures the module is loaded only once (lazy singleton).
     *
     * This method is private and should not be called directly from outside.
     *
     * @returns {Promise<NodeForge>} The loaded `node-forge` module.
     */
    async #fetchNodeForge() {
        if (!this.forge) {
            const forge = await import(/* webpackMode: "eager" */ 'node-forge').catch(() => {
                console.warn('[TinyCertCrypto] Warning: "node-forge" is not installed. ' +
                    'TinyCertCrypto requires "node-forge" to function properly. ' +
                    'Please install it with "npm install node-forge" if you intend to use certificate-related features.');
                return null;
            });
            if (forge) {
                // @ts-ignore
                this.forge = forge?.default ?? forge;
            }
        }
        return this.#getNodeForge();
    }
    /**
     * Public wrapper for fetching the `node-forge` module.
     * Useful for on-demand loading in environments like browsers.
     *
     * @returns {Promise<NodeForge>} The loaded `node-forge` module.
     */
    async fetchNodeForge() {
        return this.#fetchNodeForge();
    }
    /**
     * Returns the previously loaded `node-forge` instance.
     * Assumes the module has already been loaded.
     *
     * @returns {NodeForge} The `node-forge` module.
     */
    #getNodeForge() {
        if (typeof this.forge === 'undefined' || this.forge === null)
            throw new Error(`Failed to initialize Forge: Module is ${this.forge !== null ? 'undefined' : 'null'}.\n` +
                'Please make sure "node-forge" is installed.\n' +
                'You can install it by running: npm install node-forge');
        return this.forge;
    }
    /**
     * Public wrapper for accessing the `node-forge` instance.
     *
     * @returns {NodeForge} The `node-forge` module.
     */
    getNodeForge() {
        return this.#getNodeForge();
    }
    /**
     * Detects the type of a PEM-formatted string.
     *
     * Recognizes X.509 certificates and RSA keys (public/private).
     * Returns a string describing the type, such as `'certificate'`, `'public_key'`, `'private_key'`,
     * or `'unknown'` if the format is not recognized.
     *
     * @param {string} pemString - The PEM string to inspect.
     * @returns {string} The detected PEM type.
     */
    #detectPemType(pemString) {
        if (this.#certRegex.test(pemString))
            return 'certificate';
        const keyMatch = this.#keyRegex.exec(pemString);
        if (keyMatch && typeof keyMatch[1] === 'string')
            return keyMatch[1].toLowerCase() + '_key'; // "public_key" or "private_key"
        return 'unknown';
    }
    /**
     * Generates a new X.509 certificate along with a public/private RSA key pair.
     *
     * This method can only be used in Node.js environments. It throws an error if a certificate
     * or key is already loaded into the instance.
     *
     * @param {Object<string, string>} subjectFields - An object representing the subject fields of the certificate (e.g., CN, O, C).
     * @param {Object} [options={}] - Optional configuration for key and certificate generation.
     * @param {number} [options.modulusLength=2048] - Length of the RSA key in bits.
     * @param {number} [options.validityInYears=1] - Number of years the certificate will be valid.
     * @param {number} [options.randomBytesLength=16] - Number of random bytes to use for serial number generation.
     *
     * @returns {Promise<{publicKey: string, privateKey: string, cert: string}>} The generated keys and certificate in PEM format.
     * @throws {Error} If running in a browser or if a cert/key is already loaded.
     */
    async generateX509Cert(subjectFields, options = {}) {
        // Errors
        if (this.publicKey || this.privateKey || this.publicCert)
            throw new Error('A certificate is already loaded into the instance.');
        // Prepare cert
        const { pki } = await this.#fetchNodeForge();
        /**
         * @property {number} [modulusLength=2048] - The length of the RSA modulus (in bits). Default is 2048.
         * @property {number} [validityInYears=1] - The validity period of the key pair in years. Default is 1 year.
         * @property {number} [randomBytesLength=16] - The length of random bytes used in key generation. Default is 16 bytes.
         */
        const { modulusLength = 2048, validityInYears = 1, randomBytesLength = 16 } = options;
        // Generate keys
        const { publicKey, privateKey } = pki.rsa.generateKeyPair(modulusLength);
        const encodedPublicKey = pki.publicKeyToPem(publicKey);
        const encodedPrivateKey = pki.privateKeyToPem(privateKey);
        // Get pem files
        const { cert, publicPem, privatePem } = this.#generateCertificate(subjectFields, encodedPublicKey, encodedPrivateKey, validityInYears, randomBytesLength);
        // Insert data
        this.publicKey = publicPem;
        this.privateKey = privatePem;
        this.publicCertPath = 'MEMORY';
        this.privateKeyPath = 'MEMORY';
        this.source = 'memory';
        this.publicCertBuffer = Buffer.from(encodedPublicKey);
        this.privateKeyBuffer = Buffer.from(encodedPrivateKey);
        this.#loadX509Certificate(cert);
        return { publicKey: encodedPublicKey, privateKey: encodedPrivateKey, cert };
    }
    /**
     * @typedef {Object} CertificateDetails
     * @property {PEM} cert - The certificate in PEM format.
     * @property {PublicKey} publicPem - The public key in PEM format.
     * @property {PrivateKey} privatePem - The private key in PEM format.
     */
    /**
     * Generates a self-signed X.509 certificate using the given public and private RSA keys.
     *
     * This method creates a certificate, assigns the subject and issuer fields,
     * sets the validity period, and signs it with the private key.
     *
     * @param {Object<string, string>} subject - An object representing the subject/issuer fields (e.g., { CN: 'example.com' }).
     * @param {string} publicKey - The public key in PEM format.
     * @param {string} privateKey - The private key in PEM format.
     * @param {number} validityInYears - Number of years the certificate will remain valid.
     * @param {number} randomBytesLength - Number of random bytes used to generate the serial number.
     *
     * @returns {CertificateDetails} An object containing:
     *   - {string} cert: The generated certificate in PEM format.
     *   - {Object} publicPem: The parsed public key object (from node-forge).
     *   - {Object} privatePem: The parsed private key object (from node-forge).
     */
    #generateCertificate(subject, publicKey, privateKey, validityInYears, randomBytesLength) {
        const { pki, random } = this.#getNodeForge();
        const cert = pki.createCertificate();
        const publicPem = pki.publicKeyFromPem(publicKey);
        const privatePem = pki.privateKeyFromPem(privateKey);
        if (typeof publicPem !== 'object')
            throw new Error('Public pem must be a publicPem.');
        if (typeof privatePem !== 'object')
            throw new Error('Private pem must be a privatePem.');
        cert.publicKey = publicPem;
        cert.serialNumber = Buffer.from(random.getBytesSync(randomBytesLength)).toString('hex');
        cert.validity.notBefore = new Date();
        cert.validity.notAfter = new Date();
        cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + validityInYears);
        const attrs = [];
        for (const name in subject)
            attrs.push({ name, value: subject[name] });
        cert.setSubject(attrs);
        cert.setIssuer(attrs);
        cert.sign(privatePem);
        return { cert: pki.certificateToPem(cert), publicPem, privatePem };
    }
    /**
     * Initializes the instance by loading the public certificate and, optionally, the private key.
     *
     * This method must be called before using cryptographic operations that depend on a loaded certificate.
     * It supports both Node.js and browser environments.
     *
     * In Node.js:
     * - It loads PEM data from provided file paths or buffers.
     *
     * In browsers:
     * - It loads PEM data from provided URLs or ArrayBuffers.
     *
     * @async
     * @throws {Error} If a certificate is already loaded.
     * @throws {Error} If no public certificate is provided.
     * @throws {Error} If the PEM type cannot be determined or is invalid.
     */
    async init() {
        // Errors
        if (this.publicKey || this.privateKey || this.publicCert)
            throw new Error('A certificate is already loaded into the instance.');
        if (!this.publicCertPath && !this.publicCertBuffer)
            throw new Error('Public certificate is required to initialize');
        // Load public key
        this.metadata = {};
        const { pki } = await this.#fetchNodeForge();
        /**
         * Loads the public key from a PEM-encoded certificate or public key.
         *
         * @param {string|null} publicPem - The PEM-encoded public key or certificate.
         * @throws {Error} If the PEM string is not recognized or if the key cannot be parsed.
         */
        const loadPublicKey = (publicPem) => {
            if (typeof publicPem !== 'string')
                throw new Error('Expected publicPem to be a string containing a PEM-encoded key or certificate.');
            // File type
            const fileType = this.#detectPemType(publicPem);
            // Cert
            if (fileType === 'certificate') {
                const cert = pki.certificateFromPem(publicPem);
                // @ts-ignore
                this.publicKey = cert.publicKey;
                this.#loadX509Certificate(cert);
            }
            // Public key
            else if (fileType === 'public_key')
                this.publicKey = pki.publicKeyFromPem(publicPem);
            else
                throw new Error('Public key is required to initialize');
        };
        /**
         * Loads the private key from a PEM-encoded private key.
         *
         * @param {string|null} privatePem - The PEM-encoded private key.
         * @throws {Error} If the PEM string is not recognized or if the key cannot be parsed.
         */
        const loadPrivateKey = (privatePem) => {
            if (typeof privatePem !== 'string')
                throw new Error('Expected privatePem to be a string containing a PEM-encoded private key.');
            const fileType = this.#detectPemType(privatePem);
            if (fileType === 'private_key')
                this.privateKey = pki.privateKeyFromPem(privatePem);
            else
                throw new Error('Private key is required to initialize');
        };
        if (typeof this.publicCertPath !== 'string')
            throw new Error(`Expected 'publicCertPath' to be a string, but got ${typeof this.publicCertPath}`);
        if (typeof this.privateKeyPath !== 'string')
            throw new Error(`Expected 'privateKeyPath' to be a string, but got ${typeof this.privateKeyPath}`);
        // Nodejs
        if (!isBrowser()) {
            const usedPublicBuffer = !!this.publicCertBuffer;
            const usedPrivateBuffer = !!this.privateKeyBuffer;
            // Public key
            const publicPem = usedPublicBuffer
                ? typeof this.publicCertBuffer === 'string'
                    ? this.publicCertBuffer
                    : Buffer.isBuffer(this.publicCertBuffer)
                        ? this.publicCertBuffer.toString('utf-8')
                        : null
                : readFileSync(this.publicCertPath, 'utf-8');
            loadPublicKey(publicPem);
            // Private Key
            if (this.privateKeyPath || this.privateKeyBuffer) {
                const privatePem = usedPrivateBuffer
                    ? typeof this.privateKeyBuffer === 'string'
                        ? this.privateKeyBuffer
                        : Buffer.isBuffer(this.privateKeyBuffer)
                            ? this.privateKeyBuffer.toString('utf-8')
                            : null
                    : readFileSync(this.privateKeyPath, 'utf-8');
                loadPrivateKey(privatePem);
            }
            // Insert source
            this.source = this.publicCertBuffer || this.privateKeyBuffer ? 'memory' : 'file';
        }
        // Browser
        else {
            // Public key
            const publicPem = this.publicCertBuffer
                ? typeof this.publicCertBuffer === 'string'
                    ? this.publicCertBuffer
                    : new TextDecoder().decode(this.publicCertBuffer)
                : await fetch(this.publicCertPath).then((r) => r.text());
            loadPublicKey(publicPem);
            // Private key
            if (this.privateKeyPath || this.privateKeyBuffer) {
                const privatePem = this.privateKeyBuffer
                    ? typeof this.privateKeyBuffer === 'string'
                        ? this.privateKeyBuffer
                        : new TextDecoder().decode(this.privateKeyBuffer)
                    : await fetch(this.privateKeyPath).then((r) => r.text());
                loadPrivateKey(privatePem);
            }
            // Insert key
            this.source = 'url';
        }
    }
    /**
     * Parses and stores an X.509 certificate in the instance, extracting metadata such as
     * subject, issuer, serial number, and validity period.
     *
     * This method supports both PEM strings and already-parsed Forge certificate objects.
     * The parsed certificate is saved to `this.publicCert` and its metadata is extracted to `this.metadata`.
     *
     * @param {string|Certificate} certPem - A PEM-encoded certificate string or a `forge.pki.Certificate` object.
     * @throws {Error} If the certificate cannot be parsed or processed.
     */
    #loadX509Certificate(certPem) {
        const { pki } = this.#getNodeForge();
        try {
            const cert = typeof certPem === 'string' ? pki.certificateFromPem(certPem) : certPem;
            this.publicCert = cert;
            /**
             * @param {Array<CertificateField>} attributes - The list of attributes, each containing `name`, `shortName`, and `value`.
             * @returns {{names: { [key: string]: string }, shortNames: { [key: string]: string }, raw: string}} The processed data containing the organized attributes.
             */
            const insertData = (attributes) => {
                /** @type {{[key: string]: string}} */
                const names = {};
                /** @type {{[key: string]: string}} */
                const shortNames = {};
                const raw = attributes
                    .filter((attr) => typeof attr.shortName === 'string' && typeof attr.value === 'string')
                    .map((attr) => `${attr.shortName}=${attr.value}`)
                    .join(',');
                for (const item of attributes) {
                    if (typeof item.name === 'string' && typeof item.value === 'string')
                        names[item.name] = item.value;
                    if (typeof item.shortName === 'string' && typeof item.value === 'string')
                        shortNames[item.shortName] = item.value;
                }
                return { names, shortNames, raw };
            };
            this.metadata = {
                subject: insertData(cert.subject.attributes),
                issuer: insertData(cert.issuer.attributes),
                serialNumber: cert.serialNumber,
                validFrom: cert.validity.notBefore,
                validTo: cert.validity.notAfter,
            };
        }
        catch (err) {
            throw new Error(`Failed to parse X.509 certificate in browser: ${err instanceof Error && typeof err.message === 'string' ? err.message : 'Unknown error'}`);
        }
    }
    /**
     * Extracts the metadata of the loaded X.509 certificate.
     *
     * Returns an object containing the certificate's metadata such as the subject, issuer,
     * serial number, and validity period. If no certificate is loaded, an empty object is returned.
     *
     * @returns {Record<string, any>} The metadata of the certificate, or an empty object if no certificate is loaded.
     */
    extractCertMetadata() {
        return this.metadata ? clone(this.metadata) : {};
    }
    /**
     * @param {string} data - The JSON object to be encrypted.
     * @returns {Base64} The encrypted JSON object, encoded in Base64 format.
     * @throws {Error} If the public key is not initialized (i.e., if `init()` or `generateKeyPair()` has not been called).
     */
    #encrypt(data) {
        const forge = this.#getNodeForge();
        if (!this.publicKey)
            throw new Error('Public key is not initialized. Call init() or generateKeyPair() first.');
        const encrypted = this.publicKey.encrypt(data, this.cryptoType);
        return forge.util.encode64(encrypted);
    }
    /**
     * @param {Base64} encryptedBase64 - The encrypted JSON string in Base64 format to be decrypted.
     * @returns {*} The decrypted JSON object.
     * @throws {Error} If the private key is not initialized.
     */
    #decrypt(encryptedBase64) {
        const forge = this.#getNodeForge();
        if (!this.privateKey)
            throw new Error('Private key is required for decryption');
        const data = forge.util.decode64(encryptedBase64);
        const decrypted = this.privateKey.decrypt(data, this.cryptoType);
        return decrypted;
    }
    /**
     * Encrypts a JSON object using the initialized public key.
     *
     * This method serializes the provided JSON object to a string and encrypts it using the
     * public key in PEM format. The encryption is done using the algorithm defined in the
     * `cryptoType` property (e.g., 'RSA-OAEP').
     *
     * @param {CryptoResult} jsonObject - The JSON object to be encrypted.
     * @returns {Base64} The encrypted JSON object, encoded in Base64 format.
     * @throws {Error} If the public key is not initialized (i.e., if `init()` or `generateKeyPair()` has not been called).
     */
    encryptJson(jsonObject) {
        return this.#encrypt(JSON.stringify(jsonObject));
    }
    /**
     * Decrypts a Base64-encoded encrypted JSON string using the initialized private key.
     *
     * This method takes the encrypted Base64 string, decodes it, and decrypts it using the
     * private key in PEM format. It then parses the decrypted string back into a JSON object.
     *
     * @param {Base64} encryptedBase64 - The encrypted JSON string in Base64 format to be decrypted.
     * @returns {CryptoResult} The decrypted JSON object.
     * @throws {Error} If the private key is not initialized.
     */
    decryptToJson(encryptedBase64) {
        return JSON.parse(this.#decrypt(encryptedBase64));
    }
    /**
     * @typedef {Object} EncryptedDataParamsNoKeys
     * @property {Base64} auth - The Initialization Vector (IV) encrypted by the TinyCertCrypto used in encryption, encoded with the output encoding.
     * @property {string} encrypted - The encrypted data to decrypt, encoded with the output encoding.
     */
    /**
     * @typedef {Object} EncryptedDataParams
     * @property {Base64} auth - The Initialization Vector (IV) encrypted by the TinyCertCrypto used in encryption, encoded with the output encoding.
     * @property {string} iv - The Initialization Vector (IV) used in encryption, encoded with the output encoding.
     * @property {string} encrypted - The encrypted data to decrypt, encoded with the output encoding.
     * @property {string} authTag - The authentication tag used to verify the integrity of the encrypted data.
     */
    /**
     * Encrypts a value using the initialized public key.
     *
     * This method serializes the provided value to a string and encrypts it using the
     * public key in PEM format. The encryption is done using the algorithm defined in the
     * `cryptoType` property (e.g., 'RSA-OAEP').
     *
     * @param {*} data - The value to be encrypted.
     * @returns {EncryptedDataParams} The encrypted value, encoded in Base64 format.
     * @throws {Error} If the public key is not initialized (i.e., if `init()` or `generateKeyPair()` has not been called).
     */
    encrypt(data) {
        const tinyCrypto = this.getCrypto();
        const { iv, encrypted, authTag } = tinyCrypto.encrypt(data);
        const auth = this.#encrypt(tinyCrypto.getKey());
        return { auth, iv, authTag, encrypted };
    }
    /**
     * Decrypts a Base64-encoded encrypted value using the initialized private key.
     *
     * This method takes the encrypted Base64 string, decodes it, and decrypts it using the
     * private key in PEM format. It then parses the decrypted string back into a value.
     *
     * @param {EncryptedDataParams} params - The encrypted value in Base64 format to be decrypted.
     * @param {string|null} [expectedType=null] - Optionally specify the expected type of the decrypted data. If provided, the method will validate the type of the deserialized value.
     * @returns {*} The decrypted value.
     * @throws {Error} If the private key is not initialized.
     */
    decrypt({ auth, iv, authTag, encrypted }, expectedType = null) {
        const tinyCrypto = this.getCrypto();
        const hexKey = this.#decrypt(auth);
        tinyCrypto.setKey(hexKey);
        return tinyCrypto.decrypt({ iv, encrypted, authTag }, expectedType);
    }
    /**
     * Encrypts a value using the initialized public key.
     *
     * This method serializes the provided value to a string and encrypts it using the
     * public key in PEM format. The encryption is done using the algorithm defined in the
     * `cryptoType` property (e.g., 'RSA-OAEP').
     *
     * @param {*} data - The value to be encrypted.
     * @returns {EncryptedDataParamsNoKeys} The encrypted value, encoded in Base64 format.
     * @throws {Error} If the public key is not initialized (i.e., if `init()` or `generateKeyPair()` has not been called).
     */
    encryptWithoutKey(data) {
        const tinyCrypto = this.getCrypto();
        const { iv, encrypted, authTag } = tinyCrypto.encrypt(data);
        const auth = this.#encrypt(JSON.stringify({ iv, authTag }));
        return { auth, encrypted };
    }
    /**
     * Decrypts a Base64-encoded encrypted value using the initialized private key.
     *
     * This method takes the encrypted Base64 string, decodes it, and decrypts it using the
     * private key in PEM format. It then parses the decrypted string back into a value.
     *
     * @param {EncryptedDataParamsNoKeys} params - The encrypted value in Base64 format to be decrypted.
     * @param {string|null} [expectedType=null] - Optionally specify the expected type of the decrypted data. If provided, the method will validate the type of the deserialized value.
     * @returns {*} The decrypted value.
     * @throws {Error} If the private key is not initialized.
     */
    decryptWithoutKey({ auth, encrypted }, expectedType = null) {
        const tinyCrypto = this.getCrypto();
        const { iv, authTag } = JSON.parse(this.#decrypt(auth));
        return tinyCrypto.decrypt({ iv, encrypted, authTag }, expectedType);
    }
    /**
     * Checks if both the public and private keys are initialized.
     *
     * This method verifies if both the public key and private key have been initialized
     * in the instance. It returns `true` if both keys are present, otherwise `false`.
     *
     * @returns {boolean} `true` if both public and private keys are initialized, `false` otherwise.
     */
    hasKeys() {
        return this.publicKey !== null && this.privateKey !== null;
    }
    /**
     * Checks if a public certificate is initialized.
     *
     * This method checks if the public certificate has been loaded or initialized in the instance.
     * It returns `true` if the public certificate is available, otherwise `false`.
     *
     * @returns {boolean} `true` if the public certificate is initialized, `false` otherwise.
     */
    hasCert() {
        return this.publicCert !== null;
    }
    /**
     * Resets the instance by clearing the keys and certificate data.
     *
     * This method sets the public and private keys, the public certificate, metadata, and
     * the source to `null`, effectively resetting the instance to its initial state.
     */
    reset() {
        this.publicKey = null;
        this.privateKey = null;
        this.publicCert = null;
        this.metadata = null;
        this.source = null;
    }
}
export default TinyCertCrypto;