"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.SecureDelegationsEncryption = void 0;
const utils_1 = require("../utils");
const utils_2 = require("./utils");
/**
 * @internal this class is for internal use only and may be changed without notice.
 */
class SecureDelegationsEncryption {
    constructor(userKeys, primitives) {
        this.userKeys = userKeys;
        this.primitives = primitives;
        /**
         * WARNING: this values should be ALWAYS be the string 'Cure' and its utf-8 bytes. If changed, it would not be possible to decrypt old secretIds and owningEntityIds
         * anymore.
         * @private
         */
        this.decryptionValidatorPrefix = 'Cure';
        this.decryptionValidatorBytes = (0, utils_1.utf8_2ua)(this.decryptionValidatorPrefix);
    }
    /**
     * Check if a Uint8Array or an Array buffer contains the decryption validator prefix.
     * @param bytes an ArrayBuffer or Uint8Array to verify.
     * @return true if the buffer contains the prefix, false otherwise.
     */
    prefixVerification(bytes) {
        const arrayToVerify = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
        if (arrayToVerify.length < this.decryptionValidatorBytes.length)
            return false;
        let result = true;
        for (let i = 0; i < this.decryptionValidatorBytes.length; i++) {
            result = result && this.decryptionValidatorBytes[i] === arrayToVerify[i];
        }
        return result;
    }
    /**
     * If the secure delegation has an encrypted exchange data id attempts to decrypt it with the available keys for the current user.
     * @param encryptedExchangeIds a map that associates an encrypted exchange data id to the fingerprint of the public key used to encrypt it.
     * @return the id of the exchange data used for the encryption of the provided secure delegation if it was encrypted and could be decrypted,
     * undefined otherwise (there was no encrypted id of the exchange data, or it could not be decrypted as no key was available).
     */
    decryptExchangeDataId(encryptedExchangeIds) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a;
            const decryptionKeys = this.userKeys.getDecryptionKeys();
            const decryptionKeysV2 = Object.fromEntries(Object.entries(decryptionKeys).map(([fp, keyPair]) => [(0, utils_2.fingerprintIsV1)(fp) ? (0, utils_2.fingerprintV1toV2)(fp) : fp, keyPair]));
            for (const [fp, encryptedId] of Object.entries(encryptedExchangeIds)) {
                const key = (_a = decryptionKeys === null || decryptionKeys === void 0 ? void 0 : decryptionKeys[fp]) !== null && _a !== void 0 ? _a : decryptionKeysV2 === null || decryptionKeysV2 === void 0 ? void 0 : decryptionKeysV2[fp];
                if (key)
                    return (0, utils_1.ua2utf8)(yield this.primitives.RSA.decrypt(key.privateKey, (0, utils_1.b64_2ua)(encryptedId)));
            }
            return undefined;
        });
    }
    /**
     * Encrypts the exchange data id for a secure delegation. To avoid leaks of sensitive data the provided public keys should be only keys of users
     * which DO NOT REQUIRE anonymous delegations
     */
    encryptExchangeDataId(exchangeDataId, publicKeys) {
        return __awaiter(this, void 0, void 0, function* () {
            const res = {};
            for (const [fp, key] of Object.entries(publicKeys)) {
                res[fp] = (0, utils_1.ua2b64)(yield this.primitives.RSA.encrypt(key, (0, utils_1.utf8_2ua)(exchangeDataId)));
            }
            return res;
        });
    }
    encryptEncryptionKey(hexKey, key) {
        return __awaiter(this, void 0, void 0, function* () {
            return (0, utils_1.ua2b64)(yield this.primitives.AES.encrypt(key, (0, utils_1.concat_uas)(this.decryptionValidatorBytes, (0, utils_1.hex2ua)(hexKey))));
        });
    }
    encryptEncryptionKeys(hexKeys, key) {
        return __awaiter(this, void 0, void 0, function* () {
            const res = [];
            for (const hexKey of hexKeys) {
                res.push(yield this.encryptEncryptionKey(hexKey, key));
            }
            return res;
        });
    }
    decryptEncryptionKey(encrypted, key) {
        return __awaiter(this, void 0, void 0, function* () {
            const probableKey = yield this.primitives.AES.decrypt(key, (0, utils_1.b64_2ua)(encrypted));
            if (!this.prefixVerification(probableKey)) {
                throw new Error('Invalid encryption key');
            }
            return (0, utils_1.ua2hex)(probableKey.slice(this.decryptionValidatorBytes.length));
        });
    }
    decryptEncryptionKeys(delegation, key) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a;
            const res = [];
            for (const encrypted of (_a = delegation.encryptionKeys) !== null && _a !== void 0 ? _a : []) {
                res.push(yield this.decryptEncryptionKey(encrypted, key));
            }
            return res;
        });
    }
    encryptSecretId(secretId, key) {
        return __awaiter(this, void 0, void 0, function* () {
            return (0, utils_1.ua2b64)(yield this.primitives.AES.encrypt(key, (0, utils_1.utf8_2ua)(this.decryptionValidatorPrefix + secretId)));
        });
    }
    encryptSecretIds(secretIds, key) {
        return __awaiter(this, void 0, void 0, function* () {
            const res = [];
            for (const secretId of secretIds) {
                res.push(yield this.encryptSecretId(secretId, key));
            }
            return res;
        });
    }
    decryptSecretId(encrypted, key) {
        return __awaiter(this, void 0, void 0, function* () {
            const probableSecretId = (0, utils_1.ua2utf8)(yield this.primitives.AES.decrypt(key, (0, utils_1.b64_2ua)(encrypted)));
            if (probableSecretId.substring(0, this.decryptionValidatorPrefix.length) !== this.decryptionValidatorPrefix) {
                throw new Error('Invalid secretID');
            }
            return probableSecretId.substring(this.decryptionValidatorPrefix.length);
        });
    }
    decryptSecretIds(delegation, key) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a;
            const res = [];
            for (const encrypted of (_a = delegation.secretIds) !== null && _a !== void 0 ? _a : []) {
                res.push(yield this.decryptSecretId(encrypted, key));
            }
            return res;
        });
    }
    encryptOwningEntityId(owningEntityId, key) {
        return __awaiter(this, void 0, void 0, function* () {
            return (0, utils_1.ua2b64)(yield this.primitives.AES.encrypt(key, (0, utils_1.utf8_2ua)(this.decryptionValidatorPrefix + owningEntityId)));
        });
    }
    encryptOwningEntityIds(owningEntityIds, key) {
        return __awaiter(this, void 0, void 0, function* () {
            const res = [];
            for (const owningEntityId of owningEntityIds) {
                res.push(yield this.encryptOwningEntityId(owningEntityId, key));
            }
            return res;
        });
    }
    decryptOwningEntityId(encrypted, key) {
        return __awaiter(this, void 0, void 0, function* () {
            const probableOwningEntityId = (0, utils_1.ua2utf8)(yield this.primitives.AES.decrypt(key, (0, utils_1.b64_2ua)(encrypted)));
            if (probableOwningEntityId.substring(0, this.decryptionValidatorPrefix.length) !== this.decryptionValidatorPrefix) {
                throw new Error('Invalid Owning Entity id');
            }
            return probableOwningEntityId.substring(this.decryptionValidatorPrefix.length);
        });
    }
    decryptOwningEntityIds(delegation, key) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a;
            const res = [];
            for (const encrypted of (_a = delegation.owningEntityIds) !== null && _a !== void 0 ? _a : []) {
                res.push(yield this.decryptOwningEntityId(encrypted, key));
            }
            return res;
        });
    }
}
exports.SecureDelegationsEncryption = SecureDelegationsEncryption;
//# sourceMappingURL=SecureDelegationsEncryption.js.map