"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.hexPublicKeysWithSha256Of = hexPublicKeysWithSha256Of;
exports.hexPublicKeysWithSha1Of = hexPublicKeysWithSha1Of;
exports.transferKeysFpGraphOf = transferKeysFpGraphOf;
exports.fingerprintToPublicKeysMapOf = fingerprintToPublicKeysMapOf;
exports.loadPublicKeys = loadPublicKeys;
exports.ensureDelegationForSelf = ensureDelegationForSelf;
exports.getShaVersionForKey = getShaVersionForKey;
exports.fingerprintV1 = fingerprintV1;
exports.fingerprintV2 = fingerprintV2;
exports.fingerprintV1toV2 = fingerprintV1toV2;
exports.fingerprintIsV1 = fingerprintIsV1;
exports.fingerprintIsV2 = fingerprintIsV2;
exports.fingerprintV2ToStandardV1 = fingerprintV2ToStandardV1;
exports.checkStandardPublicKeyTail = checkStandardPublicKeyTail;
// Uses fp as node names
const graph_utils_1 = require("../utils/graph-utils");
const RSA_1 = require("./RSA");
const utils_1 = require("../utils");
const Patient_1 = require("../../icc-api/model/Patient");
const ShareMetadataBehaviour_1 = require("./ShareMetadataBehaviour");
const EntityShareRequest_1 = require("../../icc-api/model/requests/EntityShareRequest");
const DataOwnerTypeEnum_1 = require("../../icc-api/model/DataOwnerTypeEnum");
var RequestedPermissionEnum = EntityShareRequest_1.EntityShareRequest.RequestedPermissionEnum;
/**
 * Get the public keys of a data owner which should be used for RSA-OAEP with sha256.
 * @param dataOwner
 */
function hexPublicKeysWithSha256Of(dataOwner) {
    var _a;
    return new Set([...((_a = dataOwner.publicKeysForOaepWithSha256) !== null && _a !== void 0 ? _a : [])].filter((pubKey) => !!pubKey));
}
/**
 * Get the public keys of a data owner which should be used for RSA-OAEP with sha1.
 * @param dataOwner
 */
function hexPublicKeysWithSha1Of(dataOwner) {
    var _a;
    return new Set([dataOwner.publicKey, ...Object.keys((_a = dataOwner.aesExchangeKeys) !== null && _a !== void 0 ? _a : {}).filter((k) => k[0] != 'x')].filter((pubKey) => !!pubKey));
}
/**
 * @internal this function is meant only for internal use and may be changed without notice.
 * Get the transfer key graph for a data owner. Node names are the public key fingerprints of the data owner's keys, and an edge represents a key
 * which can be retrieved from another key using the transfer keys.
 * @param dataOwner a data owner.
 * @return a graph representing the possible key recovery paths using transfer keys for hte provided data owner.
 */
function transferKeysFpGraphOf(dataOwner) {
    var _a;
    const publicKeys = Array.from([...hexPublicKeysWithSha1Of(dataOwner), ...hexPublicKeysWithSha256Of(dataOwner)]);
    const edges = [];
    Object.entries((_a = dataOwner.transferKeys) !== null && _a !== void 0 ? _a : {}).forEach(([from, tos]) => {
        Object.keys(tos).forEach((to) => {
            edges.push([fingerprintV1(from), fingerprintV1(to)]);
        });
    });
    return (0, graph_utils_1.acyclic)((0, graph_utils_1.graphFromEdges)(edges, publicKeys.map((x) => fingerprintV1(x))));
}
/**
 * @internal this function is meant only for internal use and may be changed without notice.
 * Get a map for converting public keys fingerprint to full public keys for the provided data owner.
 * @param dataOwner a data owner.
 * @param shaVersion gets only the keys that are generated with this SHA version (default: 'sha-1').
 * @return a map to convert fingerprints of the data owner into full public keys.
 */
function fingerprintToPublicKeysMapOf(dataOwner, shaVersion) {
    const publicKeys = shaVersion === 'sha-1' ? Array.from(hexPublicKeysWithSha1Of(dataOwner)) : Array.from(hexPublicKeysWithSha256Of(dataOwner));
    const res = {};
    publicKeys.forEach((pk) => {
        res[fingerprintV1(pk)] = pk;
    });
    return res;
}
/**
 * @internal this function is meant only for internal use and may be changed without notice.
 * Load many public keys in spki format.
 * @param rsa the rsa service
 * @param publicKeysSpkiHex public keys in spki format, hex encoded.
 * @param shaVersion the version of the Sha algorithm used in the keys generations. ()
 * @return public keys as crypto keys by their fingerprint.
 */
function loadPublicKeys(rsa, publicKeysSpkiHex, shaVersion) {
    return __awaiter(this, void 0, void 0, function* () {
        return Object.fromEntries(yield Promise.all(publicKeysSpkiHex.map((x) => __awaiter(this, void 0, void 0, function* () { return [fingerprintV1(x), yield rsa.importKey('spki', (0, utils_1.ua2ab)((0, utils_1.hex2ua)(x)), ['encrypt'], shaVersion)]; }))));
    });
}
/**
 * @internal This method is intended only for internal use and may be changed without notice.
 * Creates a delegation for the current data owner if the data owner is an encrypted entity and there is no delegation to himself.
 * @return the updated self.
 */
function ensureDelegationForSelf(dataOwnerApi, xapi, patientApi, cryptoPrimitives) {
    return __awaiter(this, void 0, void 0, function* () {
        const self = yield dataOwnerApi.getCurrentDataOwner();
        if (self.type === 'patient') {
            const patient = new Patient_1.Patient(self.dataOwner);
            const patientWithType = { entity: patient, type: utils_1.EntityWithDelegationTypeName.Patient };
            const availableSecretIds = yield xapi.secretIdsOf(patientWithType, undefined);
            if (availableSecretIds.length) {
                return self;
            }
            else {
                if (xapi.hasEmptyEncryptionMetadata(patient)) {
                    // This should not really happen, usually some user will have already initialised the patient and its encryption metadata.
                    const updatedPatient = yield xapi.entityWithInitialisedEncryptedMetadata(patient, utils_1.EntityWithDelegationTypeName.Patient, undefined, undefined, true, {}, undefined);
                    return { dataOwner: yield patientApi.modifyPatient(updatedPatient.updatedEntity), type: DataOwnerTypeEnum_1.DataOwnerTypeEnum.Patient };
                }
                else {
                    const updatedPatient = yield xapi.simpleShareOrUpdateEncryptedEntityMetadata({
                        entity: patient,
                        type: utils_1.EntityWithDelegationTypeName.Patient,
                    }, {
                        [patient.id]: {
                            shareEncryptionKeys: ShareMetadataBehaviour_1.ShareMetadataBehaviour.IF_AVAILABLE,
                            shareOwningEntityIds: ShareMetadataBehaviour_1.ShareMetadataBehaviour.NEVER,
                            shareSecretIds: [cryptoPrimitives.randomUuid()],
                            requestedPermissions: RequestedPermissionEnum.FULL_WRITE,
                        },
                    }, (x) => patientApi.bulkSharePatients(x));
                    return { dataOwner: updatedPatient.updatedEntityOrThrow, type: DataOwnerTypeEnum_1.DataOwnerTypeEnum.Patient };
                }
            }
        }
        else {
            return self;
        }
    });
}
/**
 * Search a public key in the data owner and returns the corresponding SHA version used to generate it or undefined if not found.
 * @param dataOwner the data owner.
 * @param publicKey the public key.
 * @return 'sha-1', 'sha-256', undefined
 */
function getShaVersionForKey(dataOwner, publicKey) {
    var _a, _b;
    return dataOwner.publicKey === publicKey || Object.keys((_a = dataOwner.aesExchangeKeys) !== null && _a !== void 0 ? _a : {}).includes(publicKey)
        ? RSA_1.ShaVersion.Sha1
        : !!((_b = dataOwner.publicKeysForOaepWithSha256) === null || _b === void 0 ? void 0 : _b.includes(publicKey))
            ? RSA_1.ShaVersion.Sha256
            : undefined;
}
/**
 * @internal this function is meant only for internal use and may be changed without notice.
 * Calculates the fingerprint from the hexadecimal representation of a SPKI key. The fingerprint is calculated as the last 16 bytes (32 characters)
 * of the SPKI key.
 * @param key the hexadecimal representation of the SPKI key.
 * @return the fingerprint.
 */
function fingerprintV1(key) {
    return key.slice(-32);
}
/**
 * @internal this function is meant only for internal use and may be changed without notice.
 * Calculates the fingerprint from the hexadecimal representation of a SPKI key. The fingerprint is calculated as the last 16 bytes (32 characters) from which the
 * last 5 (10 characters) are removed because they are a constant of the SPKI format.
 * @param key the hexadecimal representation of the SPKI key.
 * @return the fingerprint.
 */
function fingerprintV2(key) {
    return key.slice(-32, -10);
}
/**
 * @internal this function is meant only for internal use and may be changed without notice.
 * Converts the fingerprint of a key from a V1 format to a V2 format.
 * @param fp the fingerprint of the key in the V1 format.
 * @return the fingerprint of the key in the v2 format.
 */
function fingerprintV1toV2(fp) {
    // Conversion resilient to mistakes: if the fingerprint is already in V2 format it will be returned as is.
    return fp.slice(0, 22);
}
/**
 * @internal this function is meant only for internal use and may be changed without notice.
 * @param fp the fingerprint.
 * @return true if the fingerprint is in V1 format, false otherwise.
 */
function fingerprintIsV1(fp) {
    return fp.length === 32;
}
/**
 * @internal this function is meant only for internal use and may be changed without notice.
 * @param fp the fingerprint.
 * @return true if the fingerprint is in V2 format, false otherwise.
 */
function fingerprintIsV2(fp) {
    return fp.length === 22;
}
/**
 * @internal this function is meant only for internal use and may be changed without notice.
 * @param fp the fingerprint v2.
 * @return the fingerprint v1 using the standard tail.
 */
function fingerprintV2ToStandardV1(fp) {
    return fp + '0203010001';
}
/**
 * @internal this function is meant only for internal use and may be changed without notice.
 * @param publicKeyOrFpv1 a public key in spki format or a public key fp v1
 * @throws if the input does not terminate with 0203010001
 */
function checkStandardPublicKeyTail(publicKeyOrFpv1) {
    if (publicKeyOrFpv1.slice(-10) != '0203010001') {
        throw new Error('Illegal state: generated public key should end with 0203010001');
    }
}
//# sourceMappingURL=utils.js.map