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());
    });
};
import { HDKey } from 'ed25519-keygen/hdkey';
import { wordlist } from '@scure/bip39/wordlists/english';
import { BearerDid, DidDht, isPortableDid } from '@enbox/dids';
import { Convert, MemoryStore } from '@enbox/common';
import { generateMnemonic, mnemonicToSeed, validateMnemonic } from '@scure/bip39';
import { AgentCryptoApi } from './crypto-api.js';
import { CompactJwe } from './prototyping/crypto/jose/jwe-compact.js';
import { DeterministicKeyGenerator } from './utils-internal.js';
import { LocalKeyManager } from './local-key-manager.js';
/**
 * Type guard function to check if a given object is an empty string or a string containing only
 * whitespace.
 *
 * This is an internal utility function used to validate password inputs, ensuring they are not
 * empty or filled with only whitespace characters, which are considered invalid for password
 * purposes.
 *
 * @param obj - The object to be checked, typically expected to be a password string.
 * @returns A boolean value indicating whether the object is an empty string or a string with only
 *          whitespace.
 */
function isEmptyString(obj) {
    return typeof obj !== 'string' || obj.trim().length === 0;
}
/**
 * Type guard function to check if a given object conforms to the {@link IdentityVaultBackup}
 * interface.
 *
 * This function is an internal utility meant to ensure the integrity and structure of the data
 * assumed to be an {@link IdentityVaultBackup}. It verifies the presence and types of the
 * `dateCreated`, `size`, and `data` properties, aligning with the expected structure of a backup
 * object in the context of an {@link IdentityVault}.
 *
 * @param obj - The object to be verified against the {@link IdentityVaultBackup} interface.
 * @returns A boolean value indicating whether the object is a valid {@link IdentityVaultBackup}.
 */
function isIdentityVaultBackup(obj) {
    return typeof obj === 'object' && obj !== null
        && 'dateCreated' in obj && typeof obj.dateCreated === 'string'
        && 'size' in obj && typeof obj.size === 'number'
        && 'data' in obj && typeof obj.data === 'string';
}
/**
 * Internal-only type guard function that checks if a given object conforms to the
 * {@link IdentityVaultStatus} interface.
 *
 * This function is utilized within the {@link HdIdentityVault} implementation to ensure the
 * integrity of the object representing the vault's status, verifying the presence and types of
 * required properties. It aasserts the presence and correct types of `initialized`, `lastBackup`,
 * and `lastRestore` properties, ensuring they align with the expected structure of an identity
 * vault's status.
 *
 * @param obj - The object to be checked against the {{@link IdentityVaultStatus} interface.
 * @returns A boolean indicating whether the object is an instance of {@link IdentityVaultStatus}.
 */
function isIdentityVaultStatus(obj) {
    return typeof obj === 'object' && obj !== null
        && 'initialized' in obj && typeof obj.initialized === 'boolean'
        && 'lastBackup' in obj
        && 'lastRestore' in obj;
}
/**
 * The `HdIdentityVault` class provides secure storage and management of identity data.
 *
 * The `HdIdentityVault` class implements the `IdentityVault` interface, providing secure storage
 * and management of identity data with an added layer of security using Hierarchical Deterministic
 * (HD) key derivation based on the SLIP-0010 standard for Ed25519 keys. It enhances identity
 * protection by generating and securing the identity using a derived HD key, allowing for the
 * deterministic regeneration of keys from a recovery phrase.
 *
 * The vault is capable of:
 * - Secure initialization with a password and an optional recovery phrase, employing HD key
 *   derivation.
 * - Encrypting the identity data using a derived content encryption key (CEK) which is securely
 *   encrypted and stored, accessible only by the correct password.
 * - Securely backing up and restoring the vault’s contents, including the HD-derived keys and
 *   associated DID.
 * - Locking and unlocking the vault, which encrypts and decrypts the CEK for secure access to the
 *   vault's contents.
 * - Managing the DID associated with the identity, providing a secure identity layer for
 *   applications.
 *
 * Usage involves initializing the vault with a secure password (and optionally a recovery phrase),
 * which then allows for the secure storage, backup, and retrieval of the identity data.
 *
 * Note: Ensure the password is strong and securely managed, as it is crucial for the security of the
 * vault's encrypted contents.
 *
 * @example
 * ```typescript
 * const vault = new HdIdentityVault();
 * await vault.initialize({ password: 'secure-unique-phrase', recoveryPhrase: 'twelve words ...' });
 * const backup = await vault.backup();
 * await vault.restore({ backup, password: 'secure-unique-phrase' });
 * ```
 */
export class HdIdentityVault {
    /**
     * Constructs an instance of `HdIdentityVault`, initializing the key derivation factor and data
     * store. It sets the default key derivation work factor and initializes the internal data store,
     * either with the provided store or a default in-memory store. It also establishes the initial
     * status of the vault as uninitialized and locked.
     *
     * @param params - Optional parameters when constructing a vault instance.
     * @param params.keyDerivationWorkFactor - Optionally set the computational effort for key derivation.
     * @param params.store - Optionally specify a custom key-value store for vault data.
     */
    constructor({ keyDerivationWorkFactor, store } = {}) {
        /** Provides cryptographic functions needed for secure storage and management of the vault. */
        this.crypto = new AgentCryptoApi();
        this._keyDerivationWorkFactor = keyDerivationWorkFactor !== null && keyDerivationWorkFactor !== void 0 ? keyDerivationWorkFactor : 210000;
        this._store = store !== null && store !== void 0 ? store : new MemoryStore();
    }
    /**
     * Creates a backup of the vault's current state, including the encrypted DID and content
     * encryption key, and returns it as an `IdentityVaultBackup` object. The backup includes a
     * Base64Url-encoded string representing the vault's encrypted data, encapsulating the
     * {@link PortableDid}, the content encryption key, and the vault's status.
     *
     * This method ensures that the vault is initialized and unlocked before proceeding with the
     * backup operation.
     *
     * @throws Error if the vault is not initialized or is locked, preventing the backup.
     * @returns A promise that resolves to the `IdentityVaultBackup` object containing the vault's
     *          encrypted backup data.
     */
    backup() {
        return __awaiter(this, void 0, void 0, function* () {
            // Verify the identity vault has already been initialized and unlocked.
            if (this.isLocked() || (yield this.isInitialized()) === false) {
                throw new Error('HdIdentityVault: Unable to proceed with the backup operation because the identity vault ' +
                    'has not been initialized and unlocked. Please ensure the vault is properly initialized ' +
                    'with a secure password before attempting to backup its contents.');
            }
            // Encode the encrypted CEK and DID as a single Base64Url string.
            const backupData = {
                did: yield this.getStoredDid(),
                contentEncryptionKey: yield this.getStoredContentEncryptionKey(),
                status: yield this.getStatus()
            };
            const backupDataString = Convert.object(backupData).toBase64Url();
            // Create a backup object containing the encrypted vault contents.
            const backup = {
                data: backupDataString,
                dateCreated: new Date().toISOString(),
                size: backupDataString.length
            };
            // Update the last backup timestamp in the data store.
            yield this.setStatus({ lastBackup: backup.dateCreated });
            return backup;
        });
    }
    /**
     * Changes the password used to secure the vault.
     *
     * This method decrypts the existing content encryption key (CEK) with the old password, then
     * re-encrypts it with the new password, updating the vault's stored encrypted CEK. It ensures
     * that the vault is initialized and unlocks the vault if the password is successfully changed.
     *
     * @param params - Parameters required for changing the vault password.
     * @param params.oldPassword - The current password used to unlock the vault.
     * @param params.newPassword - The new password to replace the existing one.
     * @throws Error if the vault is not initialized or the old password is incorrect.
     * @returns A promise that resolves when the password change is complete.
     */
    changePassword(_a) {
        return __awaiter(this, arguments, void 0, function* ({ oldPassword, newPassword }) {
            // Verify the identity vault has already been initialized.
            if ((yield this.isInitialized()) === false) {
                throw new Error('HdIdentityVault: Unable to proceed with the change password operation because the ' +
                    'identity vault has not been initialized. Please ensure the vault is properly ' +
                    'initialized with a secure password before trying again.');
            }
            // Lock the vault.
            yield this.lock();
            // Retrieve the content encryption key (CEK) record as a compact JWE from the data store.
            const cekJwe = yield this.getStoredContentEncryptionKey();
            // Decrypt the compact JWE using the given `oldPassword` to verify it is correct.
            let protectedHeader;
            let contentEncryptionKey;
            try {
                let contentEncryptionKeyBytes;
                ({ plaintext: contentEncryptionKeyBytes, protectedHeader } = yield CompactJwe.decrypt({
                    jwe: cekJwe,
                    key: Convert.string(oldPassword).toUint8Array(),
                    crypto: this.crypto,
                    keyManager: new LocalKeyManager(),
                    options: { minP2cCount: 1 }, // Vault decrypts its own JWEs; no external-input floor needed.
                }));
                contentEncryptionKey = Convert.uint8Array(contentEncryptionKeyBytes).toObject();
            }
            catch (_b) {
                throw new Error(`HdIdentityVault: Unable to change the vault password due to an incorrectly entered old password.`);
            }
            // Re-encrypt the vault content encryption key (CEK) using the new password.
            const newCekJwe = yield CompactJwe.encrypt({
                key: Convert.string(newPassword).toUint8Array(),
                protectedHeader, // Re-use the protected header from the original JWE.
                plaintext: Convert.object(contentEncryptionKey).toUint8Array(),
                crypto: this.crypto,
                keyManager: new LocalKeyManager()
            });
            // Update the vault with the new CEK JWE.
            yield this._store.set('contentEncryptionKey', newCekJwe);
            // Update the vault CEK in memory, effectively unlocking the vault.
            this._contentEncryptionKey = contentEncryptionKey;
        });
    }
    /**
     * Retrieves the DID (Decentralized Identifier) associated with the vault.
     *
     * This method ensures the vault is initialized and unlocked before decrypting and returning the
     * DID. The DID is stored encrypted and  is decrypted using the vault's content encryption key.
     *
     * @throws Error if the vault is not initialized, is locked, or the DID cannot be decrypted.
     * @returns A promise that resolves with a {@link BearerDid}.
     */
    getDid() {
        return __awaiter(this, void 0, void 0, function* () {
            // Verify the identity vault is unlocked.
            if (this.isLocked()) {
                throw new Error(`HdIdentityVault: Vault has not been initialized and unlocked.`);
            }
            // Retrieve the encrypted DID record as compact JWE from the vault store.
            const didJwe = yield this.getStoredDid();
            // Decrypt the compact JWE to obtain the PortableDid as a byte array.
            const { plaintext: portableDidBytes } = yield CompactJwe.decrypt({
                jwe: didJwe,
                key: this._contentEncryptionKey,
                crypto: this.crypto,
                keyManager: new LocalKeyManager(),
                options: { minP2cCount: 1 }, // Vault decrypts its own JWEs; no external-input floor needed.
            });
            // Convert the DID from a byte array to PortableDid format.
            const portableDid = Convert.uint8Array(portableDidBytes).toObject();
            if (!isPortableDid(portableDid)) {
                throw new Error('HdIdentityVault: Unable to decode malformed DID in identity vault');
            }
            // Return the DID in Bearer DID format.
            return yield BearerDid.import({ portableDid });
        });
    }
    /**
     * Fetches the current status of the `HdIdentityVault`, providing details on whether it's
     * initialized and the timestamps of the last backup and restore operations.
     *
     * @returns A promise that resolves with the current status of the `HdIdentityVault`, detailing
     *          its initialization, lock state, and the timestamps of the last backup and restore.
     */
    getStatus() {
        return __awaiter(this, void 0, void 0, function* () {
            const storedStatus = yield this._store.get('vaultStatus');
            // On the first run, the store will not contain an IdentityVaultStatus object yet, so return an
            // uninitialized status.
            if (!storedStatus) {
                return {
                    initialized: false,
                    lastBackup: null,
                    lastRestore: null
                };
            }
            const vaultStatus = Convert.string(storedStatus).toObject();
            if (!isIdentityVaultStatus(vaultStatus)) {
                throw new Error('HdIdentityVault: Invalid IdentityVaultStatus object in store');
            }
            // Only cache the `true` state — `initialized` is write-once (false → true) and never reverts,
            // so a cached `true` is always valid. Leaving `false` uncached ensures a subsequent
            // `isInitialized()` call after `initialize()` correctly reads the updated store.
            if (vaultStatus.initialized) {
                this._cachedInitialized = true;
            }
            return vaultStatus;
        });
    }
    /**
     * Initializes the `HdIdentityVault` with a password and an optional recovery phrase.
     *
     * If a recovery phrase is not provided, a new one is generated. This process sets up the vault,
     * deriving the necessary cryptographic keys and preparing the vault for use. It ensures the vault
     * is ready to securely store and manage identity data.
     *
     * @example
     * ```ts
     * const identityVault = new HdIdentityVault();
     * const recoveryPhrase = await identityVault.initialize({
     *   password: 'your-secure-phrase'
     * });
     * console.log('Vault initialized. Recovery phrase:', recoveryPhrase);
     * ```
     *
     * @param params - The initialization parameters.
     * @param params.password - The password used to secure the vault.
     * @param params.recoveryPhrase - An optional 12-word recovery phrase for key derivation. If
     *                                omitted, a new recovery is generated.
     * @returns A promise that resolves with the recovery phrase used during the initialization, which
     *          should be securely stored by the user.
     */
    initialize(_a) {
        return __awaiter(this, arguments, void 0, function* ({ password, recoveryPhrase, dwnEndpoints }) {
            /**
             * STEP 0: Validate the input parameters and verify the identity vault is not already
             * initialized.
             */
            // Verify that the identity vault was not previously initialized.
            if (yield this.isInitialized()) {
                throw new Error(`HdIdentityVault: Vault has already been initialized.`);
            }
            // Verify that the password is not empty.
            if (isEmptyString(password)) {
                throw new Error(`HdIdentityVault: The password is required and cannot be blank. Please provide a ' +
        'valid, non-empty password.`);
            }
            // If provided, verify that the recovery phrase is not empty.
            if (recoveryPhrase && isEmptyString(recoveryPhrase)) {
                throw new Error(`HdIdentityVault: The password is required and cannot be blank. Please provide a ' +
        'valid, non-empty password.`);
            }
            /**
             * STEP 1: Derive a Hierarchical Deterministic (HD) key pair from the given (or generated)
             * recoveryPhrase.
             */
            // Generate a 12-word (128-bit) mnemonic, if one was not provided.
            recoveryPhrase !== null && recoveryPhrase !== void 0 ? recoveryPhrase : (recoveryPhrase = generateMnemonic(wordlist, 128));
            // Validate the mnemonic for being 12-24 words contained in `wordlist`.
            if (!validateMnemonic(recoveryPhrase, wordlist)) {
                throw new Error('HdIdentityVault: The provided recovery phrase is invalid. Please ensure that the ' +
                    'recovery phrase is a correctly formatted series of 12 words.');
            }
            // Derive a root seed from the mnemonic.
            const rootSeed = yield mnemonicToSeed(recoveryPhrase);
            // Derive a root key for the DID from the root seed.
            const rootHdKey = HDKey.fromMasterSeed(rootSeed);
            /**
             * STEP 2: Derive the vault HD key pair from the root key.
             */
            // The vault HD key is derived using account 0 and index 0 so that it can be
            // deterministically re-derived. The vault key pair serves as input keying material for:
            // - deriving the vault content encryption key (CEK)
            // - deriving the salt that serves as input to derive the key that encrypts the vault CEK
            const vaultHdKey = rootHdKey.derive(`m/44'/0'/0'/0'/0'`);
            /**
             * STEP 3: Derive the vault Content Encryption Key (CEK) from the vault private
             * key and a non-secret static info value.
             */
            // A non-secret static info value is combined with the vault private key as input to HKDF
            // (Hash-based Key Derivation Function) to derive a 32-byte content encryption key (CEK).
            const contentEncryptionKey = yield this.crypto.deriveKey({
                algorithm: 'HKDF-512', // key derivation function
                baseKeyBytes: vaultHdKey.privateKey, // input keying material
                salt: '', // empty salt because private key is sufficiently random
                info: 'vault_cek', // non-secret application specific information
                derivedKeyAlgorithm: 'A256GCM' // derived key algorithm
            });
            /**
             * STEP 4: Using the given `password` and a `salt` derived from the vault public key, encrypt
             * the vault CEK and store it in the data store as a compact JWE.
             */
            // A non-secret static info value is combined with the vault public key as input to HKDF
            // (Hash-based Key Derivation Function) to derive a new 32-byte salt.
            const saltInput = yield this.crypto.deriveKeyBytes({
                algorithm: 'HKDF-512', // key derivation function
                baseKeyBytes: vaultHdKey.publicKey, // input keying material
                salt: '', // empty salt because public key is sufficiently random
                info: 'vault_unlock_salt', // non-secret application specific information
                length: 256, // derived key length, in bits
            });
            // Construct the JWE header.
            const cekJweProtectedHeader = {
                alg: 'PBES2-HS512+A256KW',
                enc: 'A256GCM',
                cty: 'text/plain',
                p2c: this._keyDerivationWorkFactor,
                p2s: Convert.uint8Array(saltInput).toBase64Url()
            };
            // Encrypt the vault content encryption key (CEK) to compact JWE format.
            const cekJwe = yield CompactJwe.encrypt({
                key: Convert.string(password).toUint8Array(),
                protectedHeader: cekJweProtectedHeader,
                plaintext: Convert.object(contentEncryptionKey).toUint8Array(),
                crypto: this.crypto,
                keyManager: new LocalKeyManager()
            });
            // Store the compact JWE in the data store.
            yield this._store.set('contentEncryptionKey', cekJwe);
            /**
             * STEP 5: Create a DID using identity, signing, and encryption keys derived from the root key.
             */
            // Derive the identity key pair using index 0 and convert to JWK format.
            // Note: The account is set to Unix epoch time so that in the future, the keys for a DID DHT
            //       document can be deterministically derived based on the versionId returned in a DID
            //       resolution result.
            const identityHdKey = rootHdKey.derive(`m/44'/0'/1708523827'/0'/0'`);
            const identityPrivateKey = yield this.crypto.bytesToPrivateKey({
                algorithm: 'Ed25519',
                privateKeyBytes: identityHdKey.privateKey
            });
            // Derive the signing key using index 1 and convert to JWK format.
            const signingHdKey = rootHdKey.derive(`m/44'/0'/1708523827'/0'/1'`);
            const signingPrivateKey = yield this.crypto.bytesToPrivateKey({
                algorithm: 'Ed25519',
                privateKeyBytes: signingHdKey.privateKey
            });
            // Derive the encryption key using index 2 (X25519 for ECDH-ES JWE encryption).
            const encryptionHdKey = rootHdKey.derive(`m/44'/0'/1708523827'/0'/2'`);
            const encryptionPrivateKey = yield this.crypto.bytesToPrivateKey({
                algorithm: 'X25519',
                privateKeyBytes: encryptionHdKey.privateKey
            });
            // Add the identity, signing, and encryption keys to the deterministic key generator so that
            // when the DID is created it will use the derived keys.
            const deterministicKeyGenerator = new DeterministicKeyGenerator();
            yield deterministicKeyGenerator.addPredefinedKeys({
                privateKeys: [identityPrivateKey, signingPrivateKey, encryptionPrivateKey]
            });
            // Create the DID using the derived identity, signing, and encryption keys.
            const options = {
                verificationMethods: [
                    {
                        algorithm: 'Ed25519',
                        id: 'sig',
                        purposes: ['assertionMethod', 'authentication']
                    },
                    {
                        algorithm: 'X25519',
                        id: 'enc',
                        purposes: ['keyAgreement']
                    }
                ]
            };
            if (dwnEndpoints && !!dwnEndpoints.length) {
                options.services = [
                    {
                        id: 'dwn',
                        type: 'DecentralizedWebNode',
                        serviceEndpoint: dwnEndpoints,
                    }
                ];
            }
            const did = yield DidDht.create({ keyManager: deterministicKeyGenerator, options });
            /**
             * STEP 6: Convert the DID to portable format and store it in the data store as a
             * compact JWE.
             */
            // Convert the DID to a portable format.
            const portableDid = yield did.export();
            // Construct the JWE header.
            const didJweProtectedHeader = {
                alg: 'dir',
                enc: 'A256GCM',
                cty: 'json'
            };
            // Encrypt the DID to compact JWE format.
            const didJwe = yield CompactJwe.encrypt({
                key: contentEncryptionKey,
                plaintext: Convert.object(portableDid).toUint8Array(),
                protectedHeader: didJweProtectedHeader,
                crypto: this.crypto,
                keyManager: new LocalKeyManager()
            });
            // Store the compact JWE in the data store.
            yield this._store.set('did', didJwe);
            /**
             * STEP 7: Set the vault CEK (effectively unlocking the vault), set the status to initialized,
             * and return the mnemonic used to generate the vault key.
             */
            this._contentEncryptionKey = contentEncryptionKey;
            yield this.setStatus({ initialized: true });
            // Return the recovery phrase in case it was generated so that it can be displayed to the user
            // for safekeeping.
            return recoveryPhrase;
        });
    }
    /**
     * Determines whether the vault has been initialized.
     *
     * This method checks the vault's current status to determine if it has been
     * initialized. Initialization is a prerequisite for most operations on the vault,
     * ensuring that it is ready for use.
     *
     * @example
     * ```ts
     * const isInitialized = await identityVault.isInitialized();
     * console.log('Is the vault initialized?', isInitialized);
     * ```
     *
     * @returns A promise that resolves to `true` if the vault has been initialized, otherwise `false`.
     */
    isInitialized() {
        return __awaiter(this, void 0, void 0, function* () {
            if (this._cachedInitialized === true) {
                return true;
            }
            return this.getStatus().then(({ initialized }) => initialized);
        });
    }
    /**
     * Checks if the vault is currently locked.
     *
     * This method assesses the vault's current state to determine if it is locked.
     * A locked vault restricts access to its contents, requiring the correct password
     * to unlock and access the stored identity data. The vault must be unlocked to
     * perform operations that access or modify its contents.
     *
     * @example
     * ```ts
     * const isLocked = await identityVault.isLocked();
     * console.log('Is the vault locked?', isLocked);
     * ```
     *
     * @returns `true` if the vault is locked, otherwise `false`.
     */
    isLocked() {
        return !this._contentEncryptionKey;
    }
    /**
     * Locks the `HdIdentityVault`, securing its contents by clearing the in-memory encryption key.
     *
     * This method ensures that the vault's sensitive data cannot be accessed without unlocking the
     * vault again with the correct password. It's an essential security feature for safeguarding
     * the vault's contents against unauthorized access.
     *
     * @example
     * ```ts
     * const identityVault = new HdIdentityVault();
     * await identityVault.lock();
     * console.log('Vault is now locked.');
     * ```
     * @throws An error if the identity vault has not been initialized.
     * @returns A promise that resolves when the vault is successfully locked.
     */
    lock() {
        return __awaiter(this, void 0, void 0, function* () {
            // Verify the identity vault has already been initialized.
            if ((yield this.isInitialized()) === false) {
                throw new Error(`HdIdentityVault: Lock operation failed. Vault has not been initialized.`);
            }
            // Clear the vault content encryption key (CEK), effectively locking the vault.
            if (this._contentEncryptionKey) {
                this._contentEncryptionKey.k = '';
            }
            this._contentEncryptionKey = undefined;
        });
    }
    /**
     * Restores the vault's data from a backup object, decrypting and reinitializing the vault's
     * content with the provided backup data.
     *
     * This operation is crucial for data recovery scenarios, allowing users to regain access to their
     * encrypted data using a previously saved backup and their password.
     *
     * @example
     * ```ts
     * const identityVault = new HdIdentityVault();
     * await identityVault.initialize({ password: 'your-secure-phrase' });
     * // Create a backup of the vault's contents.
     * const backup = await identityVault.backup();
     * // Restore the vault with the same password.
     * await identityVault.restore({ backup: backup, password: 'your-secure-phrase' });
     * console.log('Vault restored successfully.');
     * ```
     *
     * @param params - The parameters required for the restore operation.
     * @param params.backup - The backup object containing the encrypted vault data.
     * @param params.password - The password used to encrypt the backup, necessary for decryption.
     * @returns A promise that resolves when the vault has been successfully restored.
     * @throws An error if the backup object is invalid or if the password is incorrect.
     */
    restore(_a) {
        return __awaiter(this, arguments, void 0, function* ({ backup, password }) {
            // Validate the backup object.
            if (!isIdentityVaultBackup(backup)) {
                throw new Error(`HdIdentityVault: Restore operation failed due to invalid backup object.`);
            }
            // Temporarily save the status and contents of the data store while attempting to restore the
            // backup so that they are not lost in case the restore operation fails.
            let previousStatus;
            let previousContentEncryptionKey;
            let previousDid;
            try {
                previousDid = yield this.getStoredDid();
                previousContentEncryptionKey = yield this.getStoredContentEncryptionKey();
                previousStatus = yield this.getStatus();
            }
            catch (_b) {
                throw new Error('HdIdentityVault: The restore operation cannot proceed because the existing vault ' +
                    'contents are missing or inaccessible. If the problem persists consider re-initializing ' +
                    'the vault and retrying the restore.');
            }
            try {
                // Convert the backup data to a JSON object.
                const backupData = Convert.base64Url(backup.data).toObject();
                // Restore the backup to the data store.
                yield this._store.set('did', backupData.did);
                yield this._store.set('contentEncryptionKey', backupData.contentEncryptionKey);
                yield this.setStatus(backupData.status);
                // Attempt to unlock the vault with the given `password`.
                yield this.unlock({ password });
            }
            catch (_c) {
                // If the restore operation fails, revert the data store to the status and contents that were
                // saved before the restore operation was attempted.
                yield this.setStatus(previousStatus);
                yield this._store.set('contentEncryptionKey', previousContentEncryptionKey);
                yield this._store.set('did', previousDid);
                throw new Error('HdIdentityVault: Restore operation failed due to invalid backup data or an incorrect ' +
                    'password. Please verify the password is correct for the provided backup and try again.');
            }
            // Update the last restore timestamp in the data store.
            yield this.setStatus({ lastRestore: new Date().toISOString() });
        });
    }
    /**
     * Unlocks the vault by decrypting the stored content encryption key (CEK) using the provided
     * password.
     *
     * This method is essential for accessing the vault's encrypted contents, enabling the decryption
     * of stored data and the execution of further operations requiring the vault to be unlocked.
     *
     * @example
     * ```ts
     * const identityVault = new HdIdentityVault();
     * await identityVault.initialize({ password: 'your-initial-phrase' });
     * // Unlock the vault with the correct password before accessing its contents
     * await identityVault.unlock({ password: 'your-initial-phrase' });
     * console.log('Vault unlocked successfully.');
     * ```
     *
     *
     * @param params - The parameters required for the unlock operation.
     * @param params.password - The password used to encrypt the vault's CEK, necessary for
     *                            decryption.
     * @returns A promise that resolves when the vault has been successfully unlocked.
     * @throws An error if the vault has not been initialized or if the provided password is
     *         incorrect.
     */
    unlock(_a) {
        return __awaiter(this, arguments, void 0, function* ({ password }) {
            // Lock the vault.
            yield this.lock();
            // Retrieve the content encryption key (CEK) record as a compact JWE from the data store.
            const cekJwe = yield this.getStoredContentEncryptionKey();
            // Decrypt the compact JWE.
            try {
                const { plaintext: contentEncryptionKeyBytes } = yield CompactJwe.decrypt({
                    jwe: cekJwe,
                    key: Convert.string(password).toUint8Array(),
                    crypto: this.crypto,
                    keyManager: new LocalKeyManager(),
                    options: { minP2cCount: 1 }, // Vault decrypts its own JWEs; no external-input floor needed.
                });
                const contentEncryptionKey = Convert.uint8Array(contentEncryptionKeyBytes).toObject();
                // Save the content encryption key in memory, thereby unlocking the vault.
                this._contentEncryptionKey = contentEncryptionKey;
            }
            catch (_b) {
                throw new Error(`HdIdentityVault: Unable to unlock the vault due to an incorrect password.`);
            }
        });
    }
    /**
     * Retrieves the Decentralized Identifier (DID) associated with the identity vault from the vault
     * store.
     *
     * This DID is encrypted in compact JWE format and needs to be decrypted after the vault is
     * unlocked. The method is intended to be used internally within the HdIdentityVault class to access
     * the encrypted PortableDid.
     *
     * @returns A promise that resolves to the encrypted DID stored in the vault as a compact JWE.
     * @throws Will throw an error if the DID cannot be retrieved from the vault.
     */
    getStoredDid() {
        return __awaiter(this, void 0, void 0, function* () {
            // Retrieve the DID record as a compact JWE from the data store.
            const didJwe = yield this._store.get('did');
            if (!didJwe) {
                throw new Error('HdIdentityVault: Unable to retrieve the DID record from the vault. Please check the ' +
                    'vault status and if the problem persists consider re-initializing the vault and ' +
                    'restoring the contents from a previous backup.');
            }
            return didJwe;
        });
    }
    /**
     * Retrieves the encrypted Content Encryption Key (CEK) from the vault's storage.
     *
     * This CEK is used for encrypting and decrypting the vault's contents. It is stored as a
     * compact JWE and should be decrypted with the user's password to be used for further
     * cryptographic operations.
     *
     * @returns A promise that resolves to the stored CEK as a string in compact JWE format.
     * @throws Will throw an error if the CEK cannot be retrieved, indicating potential issues with
     *         the vault's integrity or state.
     */
    getStoredContentEncryptionKey() {
        return __awaiter(this, void 0, void 0, function* () {
            // Retrieve the content encryption key (CEK) record as a compact JWE from the data store.
            const cekJwe = yield this._store.get('contentEncryptionKey');
            if (!cekJwe) {
                throw new Error('HdIdentityVault: Unable to retrieve the Content Encryption Key record from the vault. ' +
                    'Please check the vault status and if the problem persists consider re-initializing the ' +
                    'vault and restoring the contents from a previous backup.');
            }
            return cekJwe;
        });
    }
    /**
     * Updates the status of the `HdIdentityVault`, reflecting changes in its initialization, lock
     * state, and the timestamps of the last backup and restore operations.
     *
     * This method directly manipulates the internal state stored in the vault's key-value store.
     *
     * @param params - The status properties to be updated.
     * @param params.initialized - Updates the initialization state of the vault.
     * @param params.lastBackup - Updates the timestamp of the last successful backup.
     * @param params.lastRestore - Updates the timestamp of the last successful restore.
     * @returns A promise that resolves to a boolean indicating successful status update.
     * @throws Will throw an error if the status cannot be updated in the key-value store.
     */
    setStatus(_a) {
        return __awaiter(this, arguments, void 0, function* ({ initialized, lastBackup, lastRestore }) {
            // Get the current status values from the store, if any.
            const vaultStatus = yield this.getStatus();
            // Update the status properties with new values specified, if any.
            vaultStatus.initialized = initialized !== null && initialized !== void 0 ? initialized : vaultStatus.initialized;
            vaultStatus.lastBackup = lastBackup !== null && lastBackup !== void 0 ? lastBackup : vaultStatus.lastBackup;
            vaultStatus.lastRestore = lastRestore !== null && lastRestore !== void 0 ? lastRestore : vaultStatus.lastRestore;
            // Write the changes to the store.
            yield this._store.set('vaultStatus', JSON.stringify(vaultStatus));
            // Update the in-memory cache so subsequent reads skip the store.
            this._cachedInitialized = vaultStatus.initialized;
            return true;
        });
    }
}
//# sourceMappingURL=hd-identity-vault.js.map