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 { Encoder } from './encoder.js';
import { Encryption } from './encryption.js';
import { KeyDerivationScheme } from './hd-key.js';
import { Secp256k1 } from './secp256k1.js';
import { DwnError, DwnErrorCode } from '../core/dwn-error.js';
/**
 * Class containing useful utilities related to the Records interface.
 */
export class Records {
    /**
     * Decrypts the encrypted data in a message reply using the given ancestor private key.
     * @param ancestorPrivateKey Any ancestor private key in the key derivation path.
     */
    static decrypt(recordsWrite, ancestorPrivateKey, cipherStream) {
        return __awaiter(this, void 0, void 0, function* () {
            const { recordId, contextId, descriptor, encryption } = recordsWrite;
            // look for an encrypted symmetric key that is encrypted by the public key corresponding to the given private key
            const matchingEncryptedKey = encryption.keyEncryption.find(key => key.rootKeyId === ancestorPrivateKey.rootKeyId &&
                key.derivationScheme === ancestorPrivateKey.derivationScheme);
            if (matchingEncryptedKey === undefined) {
                throw new DwnError(DwnErrorCode.RecordsDecryptNoMatchingKeyEncryptedFound, `Unable to find a symmetric key encrypted using key \
        with ID '${ancestorPrivateKey.rootKeyId}' and '${ancestorPrivateKey.derivationScheme}' derivation scheme.`);
            }
            const fullDerivationPath = Records.constructKeyDerivationPath(matchingEncryptedKey.derivationScheme, recordId, contextId, descriptor);
            // NOTE: right now only `ECIES-ES256K` algorithm is supported for asymmetric encryption,
            // so we will assume that's the algorithm without additional switch/if statements
            const leafPrivateKey = yield Records.deriveLeafPrivateKey(ancestorPrivateKey, fullDerivationPath);
            const encryptedKeyBytes = Encoder.base64UrlToBytes(matchingEncryptedKey.encryptedKey);
            const ephemeralPublicKey = Secp256k1.publicJwkToBytes(matchingEncryptedKey.ephemeralPublicKey);
            const keyEncryptionInitializationVector = Encoder.base64UrlToBytes(matchingEncryptedKey.initializationVector);
            const messageAuthenticationCode = Encoder.base64UrlToBytes(matchingEncryptedKey.messageAuthenticationCode);
            const dataEncryptionKey = yield Encryption.eciesSecp256k1Decrypt({
                ciphertext: encryptedKeyBytes,
                ephemeralPublicKey,
                initializationVector: keyEncryptionInitializationVector,
                messageAuthenticationCode,
                privateKey: leafPrivateKey
            });
            // NOTE: right now only `A256CTR` algorithm is supported for symmetric encryption,
            // so we will assume that's the algorithm without additional switch/if statements
            const dataEncryptionInitializationVector = Encoder.base64UrlToBytes(encryption.initializationVector);
            const plaintextStream = yield Encryption.aes256CtrDecrypt(dataEncryptionKey, dataEncryptionInitializationVector, cipherStream);
            return plaintextStream;
        });
    }
    /**
     * Constructs full key derivation path using the specified scheme.
     */
    static constructKeyDerivationPath(keyDerivationScheme, recordId, contextId, descriptor) {
        let fullDerivationPath;
        if (keyDerivationScheme === KeyDerivationScheme.DataFormats) {
            fullDerivationPath = Records.constructKeyDerivationPathUsingDataFormatsScheme(descriptor);
        }
        else if (keyDerivationScheme === KeyDerivationScheme.Protocols) {
            fullDerivationPath = Records.constructKeyDerivationPathUsingProtocolsScheme(recordId, contextId, descriptor);
        }
        else {
            // `schemas` scheme
            fullDerivationPath = Records.constructKeyDerivationPathUsingSchemasScheme(descriptor);
        }
        return fullDerivationPath;
    }
    /**
     * Constructs the full key derivation path using `dataFormats` scheme.
     */
    static constructKeyDerivationPathUsingDataFormatsScheme(descriptor) {
        if (descriptor.schema !== undefined) {
            return [
                KeyDerivationScheme.DataFormats,
                descriptor.schema,
                descriptor.dataFormat
            ];
        }
        else {
            return [
                KeyDerivationScheme.DataFormats,
                descriptor.dataFormat
            ];
        }
    }
    /**
     * Constructs the full key derivation path using `protocols` scheme.
     */
    static constructKeyDerivationPathUsingProtocolsScheme(recordId, contextId, descriptor) {
        // ensure `protocol` is defined
        // NOTE: no need to check `protocolPath` and `contextId` because earlier code ensures that if `protocol` is defined, those are defined also
        if (descriptor.protocol === undefined) {
            throw new DwnError(DwnErrorCode.RecordsProtocolsDerivationSchemeMissingProtocol, 'Unable to construct key derivation path using `protocols` scheme because `protocol` is missing.');
        }
        const protocolPathSegments = descriptor.protocolPath.split('/');
        const fullDerivationPath = [
            KeyDerivationScheme.Protocols,
            descriptor.protocol,
            contextId,
            ...protocolPathSegments,
            descriptor.dataFormat,
            recordId
        ];
        return fullDerivationPath;
    }
    /**
     * Constructs the full key derivation path using `schemas` scheme.
     */
    static constructKeyDerivationPathUsingSchemasScheme(descriptor) {
        if (descriptor.schema === undefined) {
            throw new DwnError(DwnErrorCode.RecordsSchemasDerivationSchemeMissingSchema, 'Unable to construct key derivation path using `schemas` scheme because `schema` is missing.');
        }
        const fullDerivationPath = [
            KeyDerivationScheme.Schemas,
            descriptor.schema
        ];
        return fullDerivationPath;
    }
    /**
     * Derives a descendant public key given an ancestor public key.
     * NOTE: right now only `ECIES-ES256K` algorithm is supported for asymmetric encryption,
     *       so we will assume that's the algorithm without additional switch/if statements
     */
    static deriveLeafPublicKey(rootPublicKey, fullDescendantDerivationPath) {
        return __awaiter(this, void 0, void 0, function* () {
            if (rootPublicKey.crv !== 'secp256k1') {
                throw new DwnError(DwnErrorCode.RecordsDeriveLeafPublicKeyUnSupportedCurve, `Curve ${rootPublicKey.crv} is not supported.`);
            }
            const ancestorPublicKeyBytes = Secp256k1.publicJwkToBytes(rootPublicKey);
            const leafPublicKey = yield Secp256k1.derivePublicKey(ancestorPublicKeyBytes, fullDescendantDerivationPath);
            return leafPublicKey;
        });
    }
    /**
     * Derives a descendant private key given an ancestor private key.
     * NOTE: right now only `ECIES-ES256K` algorithm is supported for asymmetric encryption,
     *       so we will assume that's the algorithm without additional switch/if statements
     */
    static deriveLeafPrivateKey(ancestorPrivateKey, fullDescendantDerivationPath) {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            if (ancestorPrivateKey.derivedPrivateKey.crv !== 'secp256k1') {
                throw new DwnError(DwnErrorCode.RecordsDeriveLeafPrivateKeyUnSupportedCurve, `Curve ${ancestorPrivateKey.derivedPrivateKey.crv} is not supported.`);
            }
            const ancestorPrivateKeyDerivationPath = (_a = ancestorPrivateKey.derivationPath) !== null && _a !== void 0 ? _a : [];
            Records.validateAncestorKeyAndDescentKeyDerivationPathsMatch(ancestorPrivateKeyDerivationPath, fullDescendantDerivationPath);
            const subDerivationPath = fullDescendantDerivationPath.slice(ancestorPrivateKeyDerivationPath.length);
            const ancestorPrivateKeyBytes = Secp256k1.privateJwkToBytes(ancestorPrivateKey.derivedPrivateKey);
            const leafPrivateKey = yield Secp256k1.derivePrivateKey(ancestorPrivateKeyBytes, subDerivationPath);
            return leafPrivateKey;
        });
    }
    /**
     * Validates that ancestor derivation path matches the descendant derivation path completely.
     * @throws {DwnError} with `DwnErrorCode.RecordsInvalidAncestorKeyDerivationSegment` if fails validation.
     */
    static validateAncestorKeyAndDescentKeyDerivationPathsMatch(ancestorKeyDerivationPath, descendantKeyDerivationPath) {
        for (let i = 0; i < ancestorKeyDerivationPath.length; i++) {
            const ancestorSegment = ancestorKeyDerivationPath[i];
            const descendantSegment = descendantKeyDerivationPath[i];
            if (ancestorSegment !== descendantSegment) {
                throw new DwnError(DwnErrorCode.RecordsInvalidAncestorKeyDerivationSegment, `Ancestor key derivation segment '${ancestorSegment}' mismatches against the descendant key derivation segment '${descendantSegment}'.`);
            }
        }
    }
}
//# sourceMappingURL=records.js.map