

import { CredoError } from "../../error/CredoError.mjs";
import "../../error/index.mjs";
import { __decorateMetadata } from "../../_virtual/_@oxc-project_runtime@0.97.0/helpers/decorateMetadata.mjs";
import { __decorate } from "../../_virtual/_@oxc-project_runtime@0.97.0/helpers/decorate.mjs";
import "../../agent/index.mjs";
import { TypedArrayEncoder } from "../../utils/TypedArrayEncoder.mjs";
import { Hasher } from "../../crypto/hashes/Hasher.mjs";
import { dateToSeconds, nowInSeconds } from "../../utils/timestamp.mjs";
import "../../utils/index.mjs";
import { KeyManagementApi } from "../kms/KeyManagementApi.mjs";
import "../kms/index.mjs";
import { X509Certificate } from "../x509/X509Certificate.mjs";
import { X509ModuleConfig } from "../x509/X509ModuleConfig.mjs";
import { X509Service } from "../x509/X509Service.mjs";
import "../x509/index.mjs";
import { JwtPayload } from "../../crypto/jose/jwt/JwtPayload.mjs";
import { parseDid } from "../dids/domain/parse.mjs";
import { getPublicJwkFromVerificationMethod } from "../dids/domain/key-type/keyDidMapping.mjs";
import "../dids/index.mjs";
import { ClaimFormat } from "../vc/models/ClaimFormat.mjs";
import "../../crypto/index.mjs";
import { extractKeyFromHolderBinding, getSdJwtSigner, getSdJwtVerifier, parseHolderBindingFromCredential, resolveDidUrl, resolveSigningPublicJwkFromDidUrl } from "./utils.mjs";
import "../vc/index.mjs";
import { decodeSdJwtVc, sdJwtVcHasher } from "./decodeSdJwtVc.mjs";
import { SdJwtVcRepository } from "./repository/SdJwtVcRepository.mjs";
import "./repository/index.mjs";
import { getDomainFromUrl } from "../../utils/domain.mjs";
import { fetchWithTimeout } from "../../utils/fetch.mjs";
import { buildDisclosureFrameForPayload } from "./disclosureFrame.mjs";
import { SdJwtVcError } from "./SdJwtVcError.mjs";
import { injectable } from "tsyringe";
import { decodeSdJwtSync } from "@sd-jwt/decode";
import { selectDisclosures } from "@sd-jwt/present";
import { SDJwtVcInstance } from "@sd-jwt/sd-jwt-vc";

//#region src/modules/sd-jwt-vc/SdJwtVcService.ts
var _ref;
let SdJwtVcService = class SdJwtVcService$1 {
	constructor(sdJwtVcRepository) {
		this.sdJwtVcRepository = sdJwtVcRepository;
	}
	async sign(agentContext, options) {
		const { payload, disclosureFrame, hashingAlgorithm } = options;
		if (hashingAlgorithm && hashingAlgorithm !== "sha-256") throw new SdJwtVcError(`Unsupported hashing algorithm used: ${hashingAlgorithm}`);
		const issuer = await this.extractKeyFromIssuer(agentContext, options.issuer, true);
		const holderBinding = options.holder ? await extractKeyFromHolderBinding(agentContext, options.holder) : void 0;
		const header = {
			alg: issuer.alg,
			typ: options.headerType ?? "dc+sd-jwt",
			kid: issuer.kid,
			x5c: issuer.x5c?.map((cert) => cert.toString("base64"))
		};
		const sdjwt = new SDJwtVcInstance({
			...this.getBaseSdJwtConfig(agentContext),
			signer: getSdJwtSigner(agentContext, issuer.publicJwk),
			hashAlg: "sha-256",
			signAlg: issuer.alg
		});
		if (!payload.vct || typeof payload.vct !== "string") throw new SdJwtVcError("Missing required parameter 'vct'");
		const compact = await sdjwt.issue({
			...payload,
			cnf: holderBinding?.cnf,
			iss: issuer.iss,
			iat: nowInSeconds(),
			vct: payload.vct
		}, disclosureFrame, { header });
		const prettyClaims = await sdjwt.getClaims(compact);
		const sdjwtPayload = (await sdjwt.decode(compact)).jwt?.payload;
		if (!sdjwtPayload) throw new SdJwtVcError("Invalid sd-jwt-vc state.");
		return {
			compact,
			prettyClaims,
			header,
			payload: sdjwtPayload,
			claimFormat: ClaimFormat.SdJwtDc,
			encoded: compact
		};
	}
	fromCompact(compactSdJwtVc, typeMetadata) {
		return decodeSdJwtVc(compactSdJwtVc, typeMetadata);
	}
	applyDisclosuresForPayload(compactSdJwtVc, requestedPayload) {
		const decoded = decodeSdJwtSync(compactSdJwtVc, Hasher.hash);
		const presentationFrame = buildDisclosureFrameForPayload(requestedPayload) ?? {};
		if (decoded.kbJwt) throw new SdJwtVcError("Cannot apply limit disclosure on an sd-jwt with key binding jwt");
		const requiredDisclosures = selectDisclosures(decoded.jwt.payload, decoded.disclosures.map((d) => ({
			digest: d.digestSync({
				alg: "sha-256",
				hasher: Hasher.hash
			}),
			encoded: d.encode(),
			key: d.key,
			salt: d.salt,
			value: d.value
		})), presentationFrame);
		const [jwt] = compactSdJwtVc.split("~");
		return decodeSdJwtVc(`${jwt}~${requiredDisclosures.length > 0 ? `${requiredDisclosures.map((d) => d.encoded).join("~")}~` : ""}`);
	}
	async present(agentContext, { sdJwtVc, presentationFrame, verifierMetadata, additionalPayload }) {
		const sdjwt = new SDJwtVcInstance(this.getBaseSdJwtConfig(agentContext));
		const compactSdJwtVc = typeof sdJwtVc === "string" ? sdJwtVc : sdJwtVc.compact;
		const holderBinding = parseHolderBindingFromCredential((await sdjwt.decode(compactSdJwtVc)).jwt?.payload);
		if (!holderBinding && verifierMetadata) throw new SdJwtVcError("Verifier metadata provided, but credential has no 'cnf' claim to create a KB-JWT from");
		const holder = holderBinding ? await extractKeyFromHolderBinding(agentContext, holderBinding, {
			forSigning: true,
			jwkKeyId: typeof sdJwtVc !== "string" ? sdJwtVc.kmsKeyId : void 0
		}) : void 0;
		sdjwt.config({
			kbSigner: holder ? getSdJwtSigner(agentContext, holder.publicJwk) : void 0,
			kbSignAlg: holder?.alg
		});
		return await sdjwt.present(compactSdJwtVc, presentationFrame, { kb: verifierMetadata ? { payload: {
			iat: verifierMetadata.issuedAt,
			nonce: verifierMetadata.nonce,
			aud: verifierMetadata.audience,
			...additionalPayload
		} } : void 0 });
	}
	assertValidX5cJwtIssuer(agentContext, iss, leafCertificate) {
		if (!iss.startsWith("https://") && !(iss.startsWith("http://") && agentContext.config.allowInsecureHttpUrls)) throw new SdJwtVcError("The X509 certificate issuer must be a HTTPS URI.");
		if (!leafCertificate.sanUriNames?.includes(iss) && !leafCertificate.sanDnsNames?.includes(getDomainFromUrl(iss))) throw new SdJwtVcError(`The 'iss' claim in the payload does not match a 'SAN-URI' name and the domain extracted from the HTTPS URI does not match a 'SAN-DNS' name in the x5c certificate.`);
	}
	async verify(agentContext, { compactSdJwtVc, keyBinding, requiredClaimKeys, fetchTypeMetadata, trustedCertificates, now }) {
		const sdjwt = new SDJwtVcInstance({ ...this.getBaseSdJwtConfig(agentContext) });
		let sdJwtVc;
		try {
			sdJwtVc = await sdjwt.decode(compactSdJwtVc);
			if (!sdJwtVc.jwt) throw new CredoError("Invalid sd-jwt-vc");
		} catch (error) {
			return {
				isValid: false,
				error
			};
		}
		const returnSdJwtVc = {
			payload: sdJwtVc.jwt.payload,
			header: sdJwtVc.jwt.header,
			compact: compactSdJwtVc,
			prettyClaims: await sdJwtVc.getClaims(sdJwtVcHasher),
			kbJwt: sdJwtVc.kbJwt ? {
				payload: sdJwtVc.kbJwt.payload,
				header: sdJwtVc.kbJwt.header
			} : void 0,
			claimFormat: ClaimFormat.SdJwtDc,
			encoded: compactSdJwtVc
		};
		try {
			const credentialIssuer = await this.parseIssuerFromCredential(agentContext, sdJwtVc, returnSdJwtVc, trustedCertificates);
			const issuer = await this.extractKeyFromIssuer(agentContext, credentialIssuer);
			const holderBinding = parseHolderBindingFromCredential(sdJwtVc.jwt.payload);
			const holder = holderBinding ? await extractKeyFromHolderBinding(agentContext, holderBinding) : void 0;
			sdjwt.config({
				verifier: getSdJwtVerifier(agentContext, issuer.publicJwk),
				kbVerifier: holder ? getSdJwtVerifier(agentContext, holder.publicJwk) : void 0
			});
			try {
				await sdjwt.verify(compactSdJwtVc, {
					requiredClaimKeys: requiredClaimKeys ? [...requiredClaimKeys, "vct"] : ["vct"],
					keyBindingNonce: keyBinding?.nonce,
					currentDate: dateToSeconds(now ?? /* @__PURE__ */ new Date()),
					skewSeconds: 0
				});
			} catch (error) {
				return {
					error,
					isValid: false,
					sdJwtVc: returnSdJwtVc
				};
			}
			if (sdJwtVc.jwt.header?.typ !== "vc+sd-jwt" && sdJwtVc.jwt.header?.typ !== "dc+sd-jwt") return {
				error: new SdJwtVcError(`SD-JWT VC header 'typ' must be 'dc+sd-jwt' or 'vc+sd-jwt'`),
				isValid: false,
				sdJwtVc: returnSdJwtVc
			};
			try {
				JwtPayload.fromJson(returnSdJwtVc.payload).validate({
					now: dateToSeconds(now ?? /* @__PURE__ */ new Date()),
					skewTime: 0
				});
			} catch (error) {
				return {
					error,
					isValid: false,
					sdJwtVc: returnSdJwtVc
				};
			}
			try {
				if (keyBinding) {
					if (!sdJwtVc.kbJwt || !sdJwtVc.kbJwt.payload) throw new SdJwtVcError("Keybinding is required for verification of the sd-jwt-vc");
					if (sdJwtVc.kbJwt.payload.aud !== keyBinding.audience) throw new SdJwtVcError("The key binding JWT does not contain the expected audience");
					if (sdJwtVc.kbJwt.payload.nonce !== keyBinding.nonce) throw new SdJwtVcError("The key binding JWT does not contain the expected nonce");
				}
			} catch (error) {
				return {
					error,
					isValid: false,
					sdJwtVc: returnSdJwtVc
				};
			}
			try {
				const vct = returnSdJwtVc.payload?.vct;
				if (fetchTypeMetadata && typeof vct === "string" && vct.startsWith("https://")) {
					const vctElements = vct.split("/");
					vctElements.splice(3, 0, ".well-known/vct");
					const vctUrl = vctElements.join("/");
					const response = await agentContext.config.agentDependencies.fetch(vctUrl);
					if (response.ok) returnSdJwtVc.typeMetadata = await response.json();
				}
			} catch (_error) {}
		} catch (error) {
			return {
				isValid: false,
				error,
				sdJwtVc: returnSdJwtVc
			};
		}
		return {
			isValid: true,
			sdJwtVc: returnSdJwtVc
		};
	}
	async store(agentContext, options) {
		await this.sdJwtVcRepository.save(agentContext, options.record);
		return options.record;
	}
	async getById(agentContext, id) {
		return await this.sdJwtVcRepository.getById(agentContext, id);
	}
	async getAll(agentContext) {
		return await this.sdJwtVcRepository.getAll(agentContext);
	}
	async findByQuery(agentContext, query, queryOptions) {
		return await this.sdJwtVcRepository.findByQuery(agentContext, query, queryOptions);
	}
	async deleteById(agentContext, id) {
		await this.sdJwtVcRepository.deleteById(agentContext, id);
	}
	async update(agentContext, sdJwtVcRecord) {
		await this.sdJwtVcRepository.update(agentContext, sdJwtVcRecord);
	}
	async extractKeyFromIssuer(agentContext, issuer, forSigning = false) {
		if (issuer.method === "did") {
			const parsedDid = parseDid(issuer.didUrl);
			if (!parsedDid.fragment) throw new SdJwtVcError(`didUrl '${issuer.didUrl}' does not contain a '#'. Unable to derive key from did document`);
			let publicJwk;
			if (forSigning) publicJwk = await resolveSigningPublicJwkFromDidUrl(agentContext, issuer.didUrl);
			else {
				const { verificationMethod } = await resolveDidUrl(agentContext, issuer.didUrl);
				publicJwk = getPublicJwkFromVerificationMethod(verificationMethod);
			}
			const supportedSignatureAlgorithms = publicJwk.supportedSignatureAlgorithms;
			if (supportedSignatureAlgorithms.length === 0) throw new SdJwtVcError(`No supported JWA signature algorithms found for key ${publicJwk.jwkTypehumanDescription}`);
			return {
				alg: supportedSignatureAlgorithms[0],
				publicJwk,
				iss: parsedDid.did,
				kid: `#${parsedDid.fragment}`
			};
		}
		if (issuer.method === "x5c") {
			const leafCertificate = issuer.x5c[0];
			if (!leafCertificate) throw new SdJwtVcError("Empty 'x5c' array provided");
			if (forSigning && !leafCertificate.publicJwk.hasKeyId) throw new SdJwtVcError("Expected leaf certificate in 'x5c' array to have a key id configured.");
			const publicJwk = leafCertificate.publicJwk;
			const supportedSignatureAlgorithms = publicJwk.supportedSignatureAlgorithms;
			if (supportedSignatureAlgorithms.length === 0) throw new SdJwtVcError(`No supported JWA signature algorithms found for key ${publicJwk.jwkTypehumanDescription}`);
			const alg = supportedSignatureAlgorithms[0];
			this.assertValidX5cJwtIssuer(agentContext, issuer.issuer, leafCertificate);
			return {
				publicJwk,
				iss: issuer.issuer,
				x5c: issuer.x5c,
				alg
			};
		}
		throw new SdJwtVcError("Unsupported credential issuer. Only 'did' and 'x5c' is supported at the moment.");
	}
	async parseIssuerFromCredential(agentContext, sdJwtVc, credoSdJwtVc, _trustedCertificates) {
		const x509Config = agentContext.dependencyManager.resolve(X509ModuleConfig);
		if (!sdJwtVc.jwt?.payload) throw new SdJwtVcError("Credential not exist");
		if (!sdJwtVc.jwt?.payload.iss) throw new SdJwtVcError("Credential does not contain an issuer");
		const iss = sdJwtVc.jwt.payload.iss;
		if (sdJwtVc.jwt.header?.x5c) {
			if (!Array.isArray(sdJwtVc.jwt.header.x5c)) throw new SdJwtVcError("Invalid x5c header in credential. Not an array.");
			if (sdJwtVc.jwt.header.x5c.length === 0) throw new SdJwtVcError("Invalid x5c header in credential. Empty array.");
			if (sdJwtVc.jwt.header.x5c.some((x5c) => typeof x5c !== "string")) throw new SdJwtVcError("Invalid x5c header in credential. Not an array of strings.");
			let trustedCertificates = _trustedCertificates;
			const certificateChain = sdJwtVc.jwt.header.x5c.map((cert) => X509Certificate.fromEncodedCertificate(cert));
			if (!trustedCertificates) trustedCertificates = await x509Config.getTrustedCertificatesForVerification?.(agentContext, {
				certificateChain,
				verification: {
					type: "credential",
					credential: credoSdJwtVc
				}
			}) ?? x509Config.trustedCertificates;
			if (!trustedCertificates) throw new SdJwtVcError("No trusted certificates configured for X509 certificate chain validation. Issuer cannot be verified.");
			await X509Service.validateCertificateChain(agentContext, {
				certificateChain: sdJwtVc.jwt.header.x5c,
				trustedCertificates
			});
			return {
				method: "x5c",
				x5c: certificateChain,
				issuer: iss
			};
		}
		if (iss.startsWith("did:")) {
			if (!sdJwtVc.jwt?.header) throw new SdJwtVcError("Credential does not contain a header");
			if (!sdJwtVc.jwt.header.kid) throw new SdJwtVcError("Credential does not contain a kid in the header");
			const issuerKid = sdJwtVc.jwt.header.kid;
			let didUrl;
			if (issuerKid.startsWith("#")) didUrl = `${iss}${issuerKid}`;
			else if (issuerKid.startsWith("did:")) {
				const didFromKid = parseDid(issuerKid);
				if (didFromKid.did !== iss) throw new SdJwtVcError(`kid in header is an absolute DID URL, but the did (${didFromKid.did}) does not match with the 'iss' did (${iss})`);
				didUrl = issuerKid;
			} else throw new SdJwtVcError("Invalid issuer kid for did. Only absolute or relative (starting with #) did urls are supported.");
			return {
				method: "did",
				didUrl
			};
		}
		throw new SdJwtVcError("Unsupported 'iss' value. Only did is supported at the moment.");
	}
	getBaseSdJwtConfig(agentContext) {
		const kms = agentContext.resolve(KeyManagementApi);
		return {
			hasher: sdJwtVcHasher,
			statusListFetcher: this.getStatusListFetcher(agentContext),
			saltGenerator: (length) => TypedArrayEncoder.toBase64URL(kms.randomBytes({ length })).slice(0, length)
		};
	}
	getStatusListFetcher(agentContext) {
		return async (uri) => {
			const response = await fetchWithTimeout(agentContext.config.agentDependencies.fetch, uri, { headers: { Accept: "application/statuslist+jwt" } });
			if (!response.ok) throw new CredoError(`Received invalid response with status ${response.status} when fetching status list from ${uri}. ${await response.text()}`);
			return await response.text();
		};
	}
};
SdJwtVcService = __decorate([injectable(), __decorateMetadata("design:paramtypes", [typeof (_ref = typeof SdJwtVcRepository !== "undefined" && SdJwtVcRepository) === "function" ? _ref : Object])], SdJwtVcService);

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