"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.GeneralJwsVerifier = void 0;
const jws_js_1 = require("../../../utils/jws.js");
const memory_cache_js_1 = require("../../../utils/memory-cache.js");
const schema_validator_js_1 = require("../../../schema-validator.js");
class GeneralJwsVerifier {
    constructor(jws, cache) {
        this.jws = jws;
        this.cache = cache || new memory_cache_js_1.MemoryCache(600);
    }
    verify(didResolver) {
        return __awaiter(this, void 0, void 0, function* () {
            const signers = [];
            for (const signatureEntry of this.jws.signatures) {
                let isVerified;
                const cacheKey = `${signatureEntry.protected}.${this.jws.payload}.${signatureEntry.signature}`;
                const kid = jws_js_1.Jws.getKid(signatureEntry);
                const publicJwk = yield GeneralJwsVerifier.getPublicKey(kid, didResolver);
                const cachedValue = yield this.cache.get(cacheKey);
                // explicit strict equality check to avoid potential buggy cache implementation causing incorrect truthy compare e.g. "false"
                if (cachedValue === undefined) {
                    isVerified = yield jws_js_1.Jws.verifySignature(this.jws.payload, signatureEntry, publicJwk);
                    yield this.cache.set(cacheKey, isVerified);
                }
                else {
                    isVerified = cachedValue;
                }
                const did = jws_js_1.Jws.extractDid(kid);
                if (isVerified) {
                    signers.push(did);
                }
                else {
                    throw new Error(`signature verification failed for ${did}`);
                }
            }
            return { signers };
        });
    }
    /**
     * Gets the public key given a fully qualified key ID (`kid`).
     */
    static getPublicKey(kid, didResolver) {
        return __awaiter(this, void 0, void 0, function* () {
            // `resolve` throws exception if DID is invalid, DID method is not supported,
            // or resolving DID fails
            const did = jws_js_1.Jws.extractDid(kid);
            const { didDocument } = yield didResolver.resolve(did);
            const { verificationMethod: verificationMethods = [] } = didDocument || {};
            let verificationMethod;
            for (const vm of verificationMethods) {
                // consider optimizing using a set for O(1) lookups if needed
                // key ID in DID Document may or may not be fully qualified. e.g.
                // `did:ion:alice#key1` or `#key1`
                if (kid.endsWith(vm.id)) {
                    verificationMethod = vm;
                    break;
                }
            }
            if (!verificationMethod) {
                throw new Error('public key needed to verify signature not found in DID Document');
            }
            (0, schema_validator_js_1.validateJsonSchema)('JwkVerificationMethod', verificationMethod);
            const { publicKeyJwk: publicJwk } = verificationMethod;
            return publicJwk;
        });
    }
}
exports.GeneralJwsVerifier = GeneralJwsVerifier;
//# sourceMappingURL=verifier.js.map