

import { CredoError } from "../error/CredoError.mjs";
import "../error/index.mjs";
import { injectable } from "../plugins/index.mjs";
import { __decorate } from "../_virtual/_@oxc-project_runtime@0.97.0/helpers/decorate.mjs";
import { KeyManagementError } from "../modules/kms/error/KeyManagementError.mjs";
import { TypedArrayEncoder } from "../utils/TypedArrayEncoder.mjs";
import { JsonEncoder } from "../utils/JsonEncoder.mjs";
import "../utils/index.mjs";
import { getJwkHumanDescription } from "../modules/kms/jwk/humanDescription.mjs";
import { assymetricPublicJwkMatches } from "../modules/kms/jwk/equals.mjs";
import { isKnownJwaSignatureAlgorithm } from "../modules/kms/jwk/jwa.mjs";
import { assertJwkAsymmetric } from "../modules/kms/jwk/knownJwk.mjs";
import { PublicJwk } from "../modules/kms/jwk/PublicJwk.mjs";
import { KeyManagementApi } from "../modules/kms/KeyManagementApi.mjs";
import "../modules/kms/index.mjs";
import { X509ModuleConfig } from "../modules/x509/X509ModuleConfig.mjs";
import { X509Service } from "../modules/x509/X509Service.mjs";
import "../modules/x509/index.mjs";
import { isJsonObject } from "../types.mjs";
import { JWS_COMPACT_FORMAT_MATCHER } from "./JwsTypes.mjs";
import { JwtPayload } from "./jose/jwt/JwtPayload.mjs";
import "./jose/jwt/index.mjs";

//#region src/crypto/JwsService.ts
let JwsService = class JwsService$1 {
	async createJwsBase(agentContext, options) {
		const { jwk, alg, x5c } = options.protectedHeaderOptions;
		const kms = agentContext.dependencyManager.resolve(KeyManagementApi);
		const key = await kms.getPublicKey({ keyId: options.keyId });
		assertJwkAsymmetric(key);
		const publicJwk = PublicJwk.fromPublicJwk(key);
		if (x5c) {
			if (!assymetricPublicJwkMatches(X509Service.getLeafCertificate(agentContext, { certificateChain: x5c }).publicJwk.toJson(), key)) throw new CredoError("Protected header x5c does not match key for signing.");
		}
		const jwkInstance = jwk instanceof PublicJwk ? jwk : jwk ? PublicJwk.fromUnknown(jwk) : void 0;
		if (jwkInstance && !assymetricPublicJwkMatches(jwkInstance.toJson(), key)) throw new CredoError("Protected header JWK does not match key for signing.");
		if (!publicJwk.supportedSignatureAlgorithms.includes(alg)) throw new CredoError(`alg '${alg}' is not a valid JWA signature algorithm for this jwk with ${publicJwk.jwkTypehumanDescription}. Supported algorithms are ${publicJwk.supportedSignatureAlgorithms.join(", ")}`);
		const payload = options.payload instanceof JwtPayload ? JsonEncoder.toBuffer(options.payload.toJson()) : options.payload;
		const base64Payload = TypedArrayEncoder.toBase64URL(payload);
		const base64UrlProtectedHeader = JsonEncoder.toBase64URL(this.buildProtected(options.protectedHeaderOptions));
		const signResult = await kms.sign({
			algorithm: alg,
			data: TypedArrayEncoder.fromString(`${base64UrlProtectedHeader}.${base64Payload}`),
			keyId: options.keyId
		});
		return {
			base64Payload,
			base64UrlProtectedHeader,
			signature: TypedArrayEncoder.toBase64URL(signResult.signature)
		};
	}
	async createJws(agentContext, { payload, keyId, header, protectedHeaderOptions }) {
		const { base64UrlProtectedHeader, signature, base64Payload } = await this.createJwsBase(agentContext, {
			payload,
			keyId,
			protectedHeaderOptions
		});
		return {
			protected: base64UrlProtectedHeader,
			signature,
			header,
			payload: base64Payload
		};
	}
	/**
	*  @see {@link https://www.rfc-editor.org/rfc/rfc7515#section-3.1}
	* */
	async createJwsCompact(agentContext, { payload, keyId, protectedHeaderOptions }) {
		const { base64Payload, base64UrlProtectedHeader, signature } = await this.createJwsBase(agentContext, {
			payload,
			keyId,
			protectedHeaderOptions
		});
		return `${base64UrlProtectedHeader}.${base64Payload}.${signature}`;
	}
	/**
	* Verify a JWS
	*/
	async verifyJws(agentContext, { jws, resolveJwsSigner, trustedCertificates, jwsSigner: expectedJwsSigner, allowedJwsSignerMethods = [
		"did",
		"jwk",
		"x5c"
	] }) {
		let signatures = [];
		let payload;
		if (expectedJwsSigner && !allowedJwsSignerMethods.includes(expectedJwsSigner.method)) throw new CredoError(`jwsSigner provided with method '${expectedJwsSigner.method}', but allowed jws signer methods are ${allowedJwsSignerMethods.join(", ")}.`);
		if (typeof jws === "string") {
			if (!JWS_COMPACT_FORMAT_MATCHER.test(jws)) throw new CredoError(`Invalid JWS compact format for value '${jws}'.`);
			const [protectedHeader, _payload, signature] = jws.split(".");
			payload = _payload;
			signatures.push({
				header: {},
				protected: protectedHeader,
				signature
			});
		} else if ("signatures" in jws) {
			signatures = jws.signatures;
			payload = jws.payload;
		} else {
			signatures.push(jws);
			payload = jws.payload;
		}
		if (signatures.length === 0) throw new CredoError("Unable to verify JWS, no signatures present in JWS.");
		const jwsFlattened = {
			signatures,
			payload
		};
		const jwsSigners = [];
		for (const jws$1 of signatures) {
			const protectedJson = JsonEncoder.fromBase64(jws$1.protected);
			if (!isJsonObject(protectedJson)) throw new CredoError("Unable to verify JWS, protected header is not a valid JSON object.");
			if (!protectedJson.alg || typeof protectedJson.alg !== "string") throw new CredoError("Unable to verify JWS, protected header alg is not provided or not a string.");
			const jwsSigner = expectedJwsSigner ?? await this.jwsSignerFromJws(agentContext, {
				jws: jws$1,
				payload,
				protectedHeader: {
					...protectedJson,
					alg: protectedJson.alg
				},
				allowedJwsSignerMethods,
				resolveJwsSigner
			});
			await this.verifyJwsSigner(agentContext, {
				jwsSigner,
				trustedCertificates
			});
			if (!jwsSigner.jwk.supportedSignatureAlgorithms.includes(protectedJson.alg)) throw new CredoError(`alg '${protectedJson.alg}' is not a valid JWA signature algorithm for this jwk ${getJwkHumanDescription(jwsSigner.jwk.toJson())}. Supported algorithms are ${jwsSigner.jwk.supportedSignatureAlgorithms.join(", ")}`);
			const data = TypedArrayEncoder.fromString(`${jws$1.protected}.${payload}`);
			const signature = TypedArrayEncoder.fromBase64(jws$1.signature);
			jwsSigners.push(jwsSigner);
			const kms = agentContext.dependencyManager.resolve(KeyManagementApi);
			try {
				const { verified } = await kms.verify({
					key: { publicJwk: jwsSigner.jwk.toJson() },
					data,
					signature,
					algorithm: protectedJson.alg
				});
				if (!verified) return {
					isValid: false,
					jwsSigners: [],
					jws: jwsFlattened
				};
			} catch (error) {
				if (error instanceof KeyManagementError) return {
					isValid: false,
					jwsSigners: [],
					jws: jwsFlattened
				};
				throw error;
			}
		}
		return {
			isValid: true,
			jwsSigners,
			jws: jwsFlattened
		};
	}
	buildProtected(options) {
		return {
			...options,
			alg: options.alg,
			jwk: options.jwk instanceof PublicJwk ? options.jwk.toJson() : options.jwk,
			kid: options.kid
		};
	}
	async verifyJwsSigner(agentContext, options) {
		const { jwsSigner } = options;
		if (jwsSigner.method === "x5c") {
			const trustedCertificatesFromConfig = agentContext.dependencyManager.resolve(X509ModuleConfig).trustedCertificates ?? [];
			const trustedCertificates = options.trustedCertificates ?? trustedCertificatesFromConfig;
			if (trustedCertificates.length === 0) throw new CredoError(`trustedCertificates is required when the JWS protected header contains an 'x5c' property.`);
			await X509Service.validateCertificateChain(agentContext, {
				certificateChain: jwsSigner.x5c,
				trustedCertificates
			});
		}
	}
	async jwsSignerFromJws(agentContext, options) {
		const { protectedHeader, resolveJwsSigner, jws, payload, allowedJwsSignerMethods } = options;
		const alg = protectedHeader.alg;
		if (!isKnownJwaSignatureAlgorithm(alg)) throw new CredoError(`Unsupported JWA signature algorithm '${protectedHeader.alg}'`);
		if (protectedHeader.x5c && allowedJwsSignerMethods.includes("x5c")) {
			if (!Array.isArray(protectedHeader.x5c) || protectedHeader.x5c.some((certificate) => typeof certificate !== "string")) throw new CredoError("x5c header is not a valid JSON array of strings.");
			return {
				method: "x5c",
				jwk: X509Service.getLeafCertificate(agentContext, { certificateChain: protectedHeader.x5c }).publicJwk,
				x5c: protectedHeader.x5c
			};
		}
		if (protectedHeader.jwk && allowedJwsSignerMethods.includes("jwk")) {
			if (!isJsonObject(protectedHeader.jwk)) throw new CredoError("JWK is not a valid JSON object.");
			return {
				method: "jwk",
				jwk: PublicJwk.fromUnknown(protectedHeader.jwk)
			};
		}
		if (!resolveJwsSigner) throw new CredoError(`resolveJwsSigner is required for resolving jws signers other than 'jwk' and 'x5c'.`);
		try {
			const jwsSigner = await resolveJwsSigner({
				jws,
				protectedHeader: {
					...protectedHeader,
					alg
				},
				payload
			});
			if (!allowedJwsSignerMethods.includes(jwsSigner.method)) throw new CredoError(`resolveJwsSigner returned jws signer with method '${jwsSigner.method}', but allowed jws signer methods are ${allowedJwsSignerMethods.join(", ")}.`);
			return jwsSigner;
		} catch (error) {
			throw new CredoError(`Error when resolving jws signer for jws in resolveJwsSigner. ${error.message}`, { cause: error });
		}
	}
};
JwsService = __decorate([injectable()], JwsService);

//#endregion
export { JwsService };
//# sourceMappingURL=JwsService.mjs.map