### Quick Start: IsChainTrustedForContext Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/consultation-dss/README.md This snippet shows the minimal setup for validating a certificate chain using `IsChainTrustedForContext`. It configures a LOTL source, sets up trust anchor retrieval with a file cache suitable for low-concurrency scenarios, and initializes the validator. ```kotlin import eu.europa.ec.eudi.etsi1196x2.consultation.dss.* import eu.europa.ec.eudi.etsi1196x2.consultation.IsChainTrustedForContext import eu.europa.ec.eudi.etsi1196x2.consultation.VerificationContext import eu.europa.ec.eudi.etsi1196x2.consultation.ValidateCertificateChainUsingPKIXJvm import kotlin.time.Duration.Companion.hours // 1. Define your LOTL Source val lotlSource = LOTLSource().apply { url = "https://ec.europa.eu/tools/lotl/eu-lotl.xml" // Configure predicates to filter trust anchors for your use case } // 2. Create GetTrustAnchorsFromLoTL with standard file cache // Note: usingFileCacheDataLoader() is suitable for low-concurrency use cases // like mobile EUDIW wallets or desktop applications val getTrustAnchors = GetTrustAnchorsFromLoTL( dssOptions = DssOptions.usingFileCacheDataLoader( fileCacheExpiration = 24.hours, cacheDirectory = createTempDirectory("lotl-cache"), ) ) // 3. Map LOTLSource to VerificationContext and create validator val isChainTrustedForContext = IsChainTrustedForContext( supportedContexts = setOf(VerificationContext.PID), getTrustAnchors = getTrustAnchors.transform(mapOf(VerificationContext.PID to lotlSource)), validateCertificateChain = ValidateCertificateChainUsingPKIXJvm.Default ) // 4. Use it val result = isChainTrustedForContext(certificateChain, VerificationContext.PID) ``` -------------------------------- ### High-Concurrency Setup (Server-Side) Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/consultation-dss/README.md Demonstrates a server-side setup optimized for high concurrency using thread-safe loaders, dual-layer caching, and asynchronous validation. This configuration ensures efficient handling of numerous validation requests. ```kotlin import eu.europa.ec.eudi.etsi1196x2.consultation.dss.* import eu.europa.ec.eudi.etsi1196x2.consultation.* import eu.europa.ec.eudi.etsi1196x2.consultation.ValidateCertificateChainUsingPKIXJvm import kotlinx.coroutines.* import kotlin.time.Duration.Companion.hours import kotlin.time.Duration.Companion.minutes // Define LOTL Source val lotlSource = LOTLSource().apply { url = "https://ec.europa.eu/tools/lotl/eu-lotl.xml" // Configure predicates... } useResoures { // 1. Create thread-safe loader with dual-layer caching val loader = ConcurrentCacheDataLoader( httpLoader = NativeHTTPDataLoader(), fileCacheExpiration = 24.hours, cacheDirectory = Paths.get("/cache/lotl"), ).bind() // 2. Configure DSS with the concurrent-safe loader val dssOptions = DssOptions(loader = loader) // 3. Create GetTrustAnchorsFromLoTL val getTrustAnchors = GetTrustAnchorsFromLoTL(dssOptions) // 4. Wrap with AsyncCache for additional deduplication val cachedGetTrustAnchors = getTrustAnchors.cached(ttl = 10.minutes, expectedQueries = 100,).bind() // 5. Create validator with cached GetTrustAnchors val isChainTrustedForContext = IsChainTrustedForContext( supportedContexts = setOf(VerificationContext.PID), getTrustAnchors = cachedGetTrustAnchors.transform( mapOf(VerificationContext.PID to lotlSource) ), validateCertificateChain = ValidateCertificateChainUsingPKIXJvm.Default ) // 6. Handle concurrent validation requests efficiently runBlocking { (1..100).map { async { val result = isChainTrustedForContext(chain, VerificationContext.PID) // Process result... } }.awaitAll() } } ``` -------------------------------- ### High-Concurrency DSS Options Setup Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/consultation-dss/README.md This setup is for high-concurrency environments, utilizing `ConcurrentCacheDataLoader`. It includes parameters for HTTP loading, cache expiration, directory, dispatcher, TTL, and maximum cache size. ```kotlin ConcurrentCacheDataLoader( httpLoader = NativeHTTPDataLoader(), fileCacheExpiration = 24.hours, cacheDirectory = Paths.get("/cache/lotl"), cacheDispatcher = Dispatchers.IO, httpCacheTtl = 5.seconds, maxCacheSize = 100, ) ``` -------------------------------- ### Branch Naming Convention Examples Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/CONTRIBUTING.md Examples of valid branch names following the specified convention: /. ```text feat/add-new-button fix/typo-in-readme docs/update-contributing-guide style/format-code refactor/extract-method test/add-unit-tests chore/update-dependencies ``` -------------------------------- ### Low-Concurrency DSS Options Setup Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/consultation-dss/README.md Use this factory method for low-concurrency scenarios. It configures DSS options with a file cache data loader, specifying expiration, cache directory, and cleaning options. ```kotlin DssOptions.usingFileCacheDataLoader( fileCacheExpiration = 24.hours, cacheDirectory = Paths.get(cachePath), cleanMemory = true, cleanFileSystem = true, httpLoader = NativeHTTPDataLoader(), synchronizationStrategy = ExpirationAndSignatureCheckStrategy(), ) ``` -------------------------------- ### Configure Attestation Classifications Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/consultation/README.md Define how different attestation types map to your `VerificationContext`. This example shows matching for MDoc and SD-JWT VC types. ```kotlin val classifications = AttestationClassifications( pids = AttestationIdentifierPredicate.mdocMatching(Regex(".*PID.*")) pubEAAs = AttestationIdentifierPredicate.sdJwtVcMatching(Regex(".*PublicEAA.*")) ) ``` -------------------------------- ### Gradle Setup for ETSI TS 119 602 LoTE Validation Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/README.md Add this dependency for EUDI Wallet certificate validation against Lists of Trusted Entities (LoTE) using ETSI TS 119 602. It includes transitive dependencies for data models and core validation abstractions. ```kotlin implementation("eu.europa.ec.eudi:etsi-119602-consultation:$version") ``` -------------------------------- ### Gradle Setup for ETSI TS 119 612 Trusted Lists Support (JVM/Android) Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/README.md Use this dependency for ETSI TS 119 612 Trusted Lists support via DSS on JVM/Android. It includes core validation abstractions and requires additional DSS dependencies. ```kotlin implementation("eu.europa.ec.eudi:etsi-1196x2-consultation-dss:$version") ``` ```kotlin implementation("eu.europa.ec.joinup.sd-dss:dss-utils-apache-commons:$dssVersion") ``` ```kotlin implementation("eu.europa.ec.joinup.sd-dss:dss-utils-google-guava:$dssVersion") ``` ```kotlin implementation("eu.europa.ec.joinup.sd-dss:dss-policy-jaxb:$dssVersion") ``` -------------------------------- ### Configure and Use ProvisionTrustAnchorsFromLoTEs Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/119602-consultation/README.md Set up an HTTP client and file cache, configure LoTE locations, and create the `ProvisionTrustAnchorsFromLoTEs` entry point. Use `nonCached()` for simple scenarios or `cached()` for high-concurrency applications. ```kotlin import eu.europa.ec.eudi.etsi119602.consultation.* import eu.europa.ec.eudi.etsi119602.consultation.eu.* import eu.europa.ec.eudi.etsi1196x2.consultation.* import eu.europa.ec.eudi.etsi1196x2.consultation.VerificationContext import io.ktor.client.* import io.ktor.client.engine.java.* import kotlinx.coroutines.* import kotlinx.io.files.Path import java.nio.file.Files import java.security.cert.TrustAnchor import java.security.cert.X509Certificate import kotlin.time.Duration.Companion.hours import kotlin.time.Duration.Companion.minutes // 1. Setup HTTP client and file cache val httpClient = HttpClient(Java) val fileStore = LoTEFileStore( cacheDirectory = Path(Files.createTempDirectory("lote-cache").toString()) ) val loadLoTE = LoadSingleLoTEWithFileCache( fileStore = fileStore, downloadSingleLoTE = DownloadSingleLoTE(httpClient), fileCacheExpiration = 24.hours ) // 2. Configure LoTE locations and service types val loteLocations = SupportedLists( pidProviders = Uri.parse("https://example.com/pid-providers.json"), walletProviders = Uri.parse("https://example.com/wallet-providers.json") ) // 3. Create the main entry point val provisionTrustAnchors = ProvisionTrustAnchorsFromLoTEs.eudiwJvm(loadLoTEAndPointers = loadLoTE) // 4a. Use nonCached() for simple/low-concurrency scenarios val isChainTrustedForContext = provisionTrustAnchors.nonCached(loteLocations) // 4b. Use cached() for high-concurrency scenarios (e.g., server-side) useResources { scope -> val cachedValidator = provisionTrustAnchors.cached( disposableScope = scope, loteLocationsSupported = loteLocations, ttl = 10.minutes ) // Use cachedValidator for concurrent requests } ``` -------------------------------- ### Create and Validate Certificate Profile Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/consultation/README.md Demonstrates how to create a certificate profile for PID Provider end-entity certificates and validate a certificate against it using a platform-specific validator. Ensure necessary imports are included. ```kotlin import eu.europa.ec.eudi.etsi1196x2.consultation.certs.* // Create a profile for PID Provider end-entity certificates val pidProviderProfile = certificateProfile { requireEndEntityCertificate() requireQcStatement(qcType = "0.4.0.194126.1.1", requireCompliance = true) requireDigitalSignature() requireValidAt() requirePolicyPresence() requireAiaForCaIssued() } // Create a validator using platform-specific operations val validator = CertificateProfileValidator(CertificateOperationsJvm) // Validate a certificate against the profile val result = validator.validate(pidProviderProfile, certificate) if (result.isMet()) { println("Certificate satisfies PID Provider profile") } else { println("Certificate violations: ${result.violations}") } ``` -------------------------------- ### Core Consultation Dependencies Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/119602-consultation/README.md Adds the core consultation abstractions and the LoTE data model. Includes Ktor client for HTTP and Kotlinx IO for file operations. ```kotlin dependencies { // Core consultation abstractions api("eu.europa.ec.eudi:etsi-1196x2-consultation:$version") // LoTE data model (transitive via api dependency) api("eu.europa.ec.eudi:etsi-119602-data-model:$version") // Ktor client for HTTP operations api("io.ktor:ktor-client-core:$ktorVersion") // Kotlinx IO for file operations implementation("org.jetbrains.kotlinx:kotlinx-io-core:$kotlinxIoVersion") // AtomicFU for atomic operations (transitive) implementation("org.jetbrains.kotlinx:atomicfu:$atomicfuVersion") } ``` -------------------------------- ### Combine Trust Anchors from Multiple Sources Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/consultation/README.md Demonstrates how to define custom trust fetchers for different contexts (e.g., national IDs, university diplomas), create validators for each context, and combine them into a single validator using `ComposeChainTrust`. This is useful when dealing with diverse trust requirements. ```kotlin // 1. Define your specific trust fetchers val nationalIdFetcher = GetTrustAnchors { query -> // Logic to fetch anchors from a local Secure Element or Government LOTL loadGovernmentRoots() } val universityFetcher = GetTrustAnchors { query -> // Logic to fetch anchors from a Sector-Specific University Trust List loadEducationRoots() } // 2. Create IsChainTrustedForContext instances val isChainTrustedForPID = IsChainTrustedForContext( supportedContexts = setOf(VerificationContext.PID), getTrustAnchors = nationalIdFetcher, validateCertificateChain = VerifyCertificateChainUsingDirectTrust() ) val isChainTrustedForUniversityDiploma = IsChainTrustedForContext( supportedContexts = setOf(VerificationContext.EAA("UniversityDiploma")), getTrustAnchors = universityFetcher, validateCertificateChain = VerifyCertificateChainUsingDirectTrust() ) // 3. Combine the validators using ComposeChainTrust val isChainTrusted = ComposeChainTrust.of(isChainTrustedForPID, isChainTrustedForUniversityDiploma) // 4. Usage in the validation engine val pidIssuanceResult = isChainTrusted(chain, VerificationContext.PID) ``` -------------------------------- ### Commit and Push Changes Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/CONTRIBUTING.md Stage, commit, and push your code changes to your forked repository. ```bash git add . git commit -m "Add a new feature" git push origin my-branch ``` -------------------------------- ### Configure EU LOTL Source Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/consultation-dss/README.md Define a LOTLSource for the EU List of Trusted Lists, specifying its URL. Further configuration for predicates can be added. ```kotlin val euLotl = LOTLSource().apply { url = "https://ec.europa.eu/tools/lotl/eu-lotl.xml" // Configure predicates... } ``` -------------------------------- ### Configure File Cache DataLoader for Low Concurrency Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/consultation-dss/README.md Set up GetTrustAnchorsFromLoTL using DssOptions with a file cache DataLoader. This is suitable for low-concurrency scenarios like mobile applications. ```kotlin val getTrustAnchors = GetTrustAnchorsFromLoTL( dssOptions = DssOptions.usingFileCacheDataLoader( fileCacheExpiration = 24.hours, cacheDirectory = createTempDirectory("lotl-cache"), ) ) ``` -------------------------------- ### Create a New Branch Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/CONTRIBUTING.md Follow these commands to create a new branch for your contributions from the main branch. ```bash git checkout main git pull git checkout -b my-branch ``` -------------------------------- ### LoTE Document Architecture Overview Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/119602-consultation/README.md Mermaid diagram illustrating the flow of LoTE documents and trust anchor validation paths based on provider types. ```mermaid flowchart TD A[LoTE Document
ETSI TS 119 602] --> B{Extract Trust Anchors
by Provider Type} B --> C1[PID Providers
Annex D] B --> C2[Wallet Providers
Annex E] B --> C3[WRPAC Providers
Annex F] B --> C4[WRPRC Providers
Annex G] subgraph TrustAnchorTypes["Trust Anchor Types"] C1 --> D1["End-entity or CA Certificate"] C2 --> D2["End-entity or CA Certificate"] C3 --> D3["CA Certificate
Policy: NCP/QCP"] C4 --> D4["CA Certificate
Policy: WRPRC"] end subgraph ValidationMethods["Validation Methods"] D1 --> E1["Direct Certificate Match, or
PKIX Path Validation"] D2 --> E2["Direct Certificate Match, or
PKIX Path Validation"] D3 --> E3["PKIX Path Validation
Chain: WRPAC → LoTE CA"] D4 --> E4["PKIX Path Validation
Chain: WRPRC → LoTE CA"] end E1 --> F1[Trusted / NotTrusted] E2 --> F1[Trusted / NotTrusted] E3 --> F1[Trusted / NotTrusted] E4 --> F1[Trusted / NotTrusted] style A fill: #e1f5fe, stroke: #0288d1, color: #000 style B fill: #fff3e0, stroke: #f57c00, color: #000 style TrustAnchorTypes fill: #f3e5f5, stroke: #7b1fa2, color: #000 style ValidationMethods fill: #e8f5e9, stroke: #388e3c, color: #000 style F1 fill: #c8e6c9, stroke: #2e7d32, color: #000 ``` -------------------------------- ### Single Context Validation (Mobile Wallet) Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/consultation-dss/README.md Sets up a validator for a single context (e.g., PID) using a LOTL source and a file cache. Suitable for low-concurrency mobile applications. ```kotlin import eu.europa.ec.eudi.etsi1196x2.consultation.dss.* import eu.europa.ec.eudi.etsi1196x2.consultation.IsChainTrustedForContext import eu.europa.ec.eudi.etsi1196x2.consultation.VerificationContext import eu.europa.ec.eudi.etsi1196x2.consultation.ValidateCertificateChainUsingPKIXJvm import kotlin.time.Duration.Companion.hours // Define LOTL Source for PID val pidLotl = LOTLSource().apply { url = "https://ec.europa.eu/tools/lotl/eu-lotl.xml" // Configure predicates to filter PID-issuing trust anchors } // Create GetTrustAnchorsFromLoTL with standard file cache val getTrustAnchors = GetTrustAnchorsFromLoTL( dssOptions = DssOptions.usingFileCacheDataLoader( fileCacheExpiration = 24.hours, cacheDirectory = createTempDirectory("lotl-cache"), ) ) // Create validator for PID context val isChainTrustedForPid = IsChainTrustedForContext( supportedContexts = setOf(VerificationContext.PID), getTrustAnchors = getTrustAnchors.transform( mapOf(VerificationContext.PID to pidLotl) ), validateCertificateChain = ValidateCertificateChainUsingPKIXJvm.Default ) // Use it val result = isChainTrustedForPid(certificateChain, VerificationContext.PID) ``` -------------------------------- ### Configure National LOTL Source Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/consultation-dss/README.md Define a LOTLSource for a national List of Trusted Lists, specifying its URL. Further configuration for predicates can be added. ```kotlin val nationalLotl = LOTLSource().apply { url = "https://example.com/national-lotl.xml" // Configure predicates... } ``` -------------------------------- ### Add Consultation Module Dependency Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/consultation/README.md Add the Consultation module dependency to your project's build.gradle.kts file. Replace `$version` with the latest release version. ```kotlin dependencies { // Replace $version with the latest release version // All modules share the same version number implementation("eu.europa.ec.eudi:etsi-1196x2-consultation:$version") } ``` -------------------------------- ### Define Wallet Relying Party Access Certificate Profile Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/docs/wrpac-assessment.md Configures the certificate profile for WRP Access Certificates, including basic requirements, X.509 v3 extensions, validity, subject alternative names, and public key constraints. It dynamically requires QC Statements based on the certificate's policy OID. ```kotlin public fun wrpAccessCertificateProfile( at: Instant? = null, maxShortTermDuration: Duration = 7.days, ): CertificateProfile = certificateProfile { // Basic certificate requirements endEntity() keyUsageDigitalSignature() wrpacExplicitExtensionCriticality() validAt(at) policyOneOf(NCP_N_EUDIWRP, NCP_L_EUDIWRP, QCP_N_EUDIWRP, QCP_L_EUDIWRP) notSelfSigned() // X.509 v3 required (for extensions) version3() // Serial number must be positive (RFC 5280) positiveSerialNumber() // AIA required for CA-issued certificates authorityInformationAccessIfCAIssued() // Authority Key Identifier required (EN 319 412-2) authorityKeyIdentifier() // Validity-assured short-term certificate requirements validityAssuredShortTerm(maxShortTermDuration) // Subject Alternative Name with contact info required (TS 119 411-8) wrpacSubjectAlternativeNames() // CRL Distribution Points required if no OCSP (EN 319 412-2) crlDistributionPointsIfNoOcspAndNotValAssured() // Public key requirements (TS 119 312) publicKey( options = PublicKeyAlgorithmOptions.of( PublicKeyAlgorithmOptions.AlgorithmRequirement.RSA_2048, PublicKeyAlgorithmOptions.AlgorithmRequirement.EC_256, PublicKeyAlgorithmOptions.AlgorithmRequirement.ECDSA_256, ), ) // QCStatements required based on the certificate's ACTUAL policy (EN 319 412-5). // NCP_N and NCP_L do not require QC statements; QCP_N and QCP_L do. requireQcStatementsForPolicy { policyOid -> when (policyOid) { QCP_N_EUDIWRP -> listOf(ETSI319412.QC_COMPLIANCE, ETSI319412.QC_SSCD) QCP_L_EUDIWRP -> listOf(ETSI319412.QC_COMPLIANCE, ETSI319412.QC_SSCD, ETSI319412.QC_TYPE) else -> emptyList() } } // Subject DN attributes required based on certificate policy (natural person vs legal person) wrpacSubject() // Issuer DN attributes required (WRPAC Provider CA is always a legal person) issuerLegalPerson() } ``` -------------------------------- ### Configure Extension Criticality for WRP Access Certificates Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/docs/wrpac-assessment.md Sets the criticality for certificate extensions based on whether they are basic constraints or key usage, or other types. This adheres to EN 319 412-1 GEN-4.1-2. ```kotlin /** * EN 319 412-1 GEN-4.1-2 */ internal fun ProfileBuilder.wrpacExplicitExtensionCriticality() { fun basicConstraintOrKeyUsage(oid: String) = oid == RFC5280.EXT_BASIC_CONSTRAINTS || oid == RFC5280.EXT_KEY_USAGE extensionCriticality(mustBeCritical = true) { oid -> basicConstraintOrKeyUsage(oid) } extensionCriticality(mustBeCritical = false) { oid -> !basicConstraintOrKeyUsage(oid) } } ``` -------------------------------- ### Public Key Algorithm and Size Validation Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/docs/wrpac-assessment.md Validates the public key algorithm and size according to specified options. ```kotlin publicKey(options = ...) ``` -------------------------------- ### Configure PID Provider Extension Criticality Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/docs/pid-assessment.md Sets the criticality for certificate extensions based on whether they are basic constraints or key usage, as per ETSI TS 119 412-6. ```kotlin internal fun ProfileBuilder.pidProviderExplicitExtensionCriticality() { fun basicConstraintOrKeyUsage(oid: String) = oid == RFC5280.EXT_BASIC_CONSTRAINTS || oid == RFC5280.EXT_KEY_USAGE extensionCriticality(mustBeCritical = true) { oid -> basicConstraintOrKeyUsage(oid) } extensionCriticality(mustBeCritical = false) { oid -> !basicConstraintOrKeyUsage(oid) } } ``` -------------------------------- ### JVM DSS Utils Dependencies Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/consultation-dss/README.md Choose one of the DSS Utils implementations for JVM projects. Ensure the dssVersion variable is defined. ```kotlin implementation("eu.europa.ec.joinup.sd-dss:dss-utils-apache-commons:$dssVersion") // OR implementation("eu.europa.ec.joinup.sd-dss:dss-utils-google-guava:$dssVersion") ``` -------------------------------- ### Explicit Extension Criticality Control Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/docs/wrpac-assessment.md Enables explicit control over extension criticality as per ETSI EN 319 412-1. ```kotlin wrpacExplicitExtensionCriticality() ``` -------------------------------- ### Configure Concurrent Cache DataLoader for High Concurrency Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/consultation-dss/README.md Implement a high-concurrency DataLoader using ConcurrentCacheDataLoader for server-side applications. This includes HTTP loading, file caching, and optional asynchronous caching. ```kotlin useResoures { val loader = ConcurrentCacheDataLoader( httpLoader = NativeHTTPDataLoader(), fileCacheExpiration = 24.hours, cacheDirectory = Paths.get("/cache/lotl"), ).bind() val dssOptions = DssOptions(loader = loader) val getTrustAnchors = GetTrustAnchorsFromLoTL(dssOptions) // Optionally wrap with AsyncCache for additional deduplication val cachedGetTrustAnchors = getTrustAnchors.cached(ttl = 10.minutes, expectedQueries = 100,).bind() // use cached } ``` -------------------------------- ### Define PID Signing Certificate Profile Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/docs/pid-assessment.md Configures the certificate profile for PID providers, specifying version, key usage, extensions, and public key requirements according to ETSI standards. ```kotlin public fun pidSigningCertificateProfile(at: Instant? = null): CertificateProfile = certificateProfile { // X.509 v3 required (for extensions) version3() endEntity() mandatoryQcStatement(qcType = ETSI119412Part6.ID_ETSI_QCT_PID, requireCompliance = true) keyUsageDigitalSignature() pidProviderExplicitExtensionCriticality() validAt(at) // Per EN 319 412-2 §4.3.3: certificatePolicies extension shall be present (TSP-defined OID) policyIsPresent() authorityInformationAccessIfCAIssued() // Serial number must be positive (RFC 5280) positiveSerialNumber() // Public key requirements (TS 119 312) publicKey( options = PublicKeyAlgorithmOptions.of( PublicKeyAlgorithmOptions.AlgorithmRequirement.RSA_2048, PublicKeyAlgorithmOptions.AlgorithmRequirement.EC_256, PublicKeyAlgorithmOptions.AlgorithmRequirement.ECDSA_256, ), ) // (TS 119 412-6, PID-4.2-01) // (TS 119 412-6, PID-4.3-01, PID-4.3-02) pidProviderIssuerAndSubject() // Subject Key Identifier required (TS 119 412-6, PID-4.4.2-01) subjectKeyIdentifier() } ``` -------------------------------- ### Create Validator for Single Verification Context Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/consultation-dss/README.md Instantiate IsChainTrustedForContext for a single verification context (e.g., PID), mapping the LOTL source and using a default PKIX validation. ```kotlin val validator = IsChainTrustedForContext( supportedContexts = setOf(VerificationContext.PID), getTrustAnchors = getTrustAnchors.transform( mapOf(VerificationContext.PID to lotlSource) ), validateCertificateChain = ValidateCertificateChainUsingPKIXJvm.Default ) ``` -------------------------------- ### Use High-Level API for Attestation Issuance Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/consultation/README.md Instantiate `IsChainTrustedForAttestation` and use its `issuance` method to verify a certificate chain for a given attestation. Ensure `isChainTrustedForContext` is properly implemented. ```kotlin val isChainTrusted: IsChainTrustedForEUDIW // Implementation of IsChainTrustedForEUDIW val isChainTrustedForAttestation = IsChainTrustedForAttestation( isChainTrustedForContext = isChainTrusted, // Implementation of IsChainTrustedForEUDIW classifications = classifications ) val result = isChainTrustedForAttestation.issuance(chain, MDoc("eu.europa.ec.eudi.pid.1")) ``` -------------------------------- ### Define Wallet Provider Signing Certificate Profile Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/docs/wallet-provider-assessment.md Configures the certificate profile for a wallet provider, specifying mandatory fields, extensions, and public key requirements according to ETSI standards. ```kotlin public fun walletProviderSigningCertificateProfile(at: Instant? = null): CertificateProfile = certificateProfile { endEntity() version3() mandatoryQcStatement(qcType = ETSI119412Part6.ID_ETSI_QCT_WAL, requireCompliance = true) keyUsageDigitalSignature() walletProviderExplicitExtensionCriticality() validAt(at) policyIsPresent() authorityInformationAccessIfCAIssued() // Serial number must be positive (RFC 5280) positiveSerialNumber() // Public key requirements (TS 119 312) publicKey( options = PublicKeyAlgorithmOptions.of( PublicKeyAlgorithmOptions.AlgorithmRequirement.RSA_2048, PublicKeyAlgorithmOptions.AlgorithmRequirement.EC_256, PublicKeyAlgorithmOptions.AlgorithmRequirement.ECDSA_256, ), ) // (TS 119 412-6, WAL-5.1-01, PID-4.2 and PID-4.3) // Same as PID Provider //pidProviderIssuerAndSubject() // Subject Key Identifier required (TS 119 412-6,WAL-5.1-01, PID-4.4.2-01) // Same as PID Provider subjectKeyIdentifier() } internal fun ProfileBuilder.walletProviderExplicitExtensionCriticality() { fun basicConstraintOrKeyUsage(oid: String) = oid == RFC5280.EXT_BASIC_CONSTRAINTS || oid == RFC5280.EXT_KEY_USAGE extensionCriticality(mustBeCritical = true) { oid -> basicConstraintOrKeyUsage(oid) } extensionCriticality(mustBeCritical = false) { oid -> !basicConstraintOrKeyUsage(oid) } } ``` -------------------------------- ### Multiple Context Validation (Mobile Wallet) Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/consultation-dss/README.md Configures validators for multiple contexts (e.g., PID and PubEAA) and combines them for unified validation. Ideal for mobile wallets handling various document types. ```kotlin import eu.europa.ec.eudi.etsi1196x2.consultation.dss.* import eu.europa.ec.eudi.etsi1196x2.consultation.* import eu.europa.ec.eudi.etsi1196x2.consultation.ValidateCertificateChainUsingPKIXJvm import kotlin.time.Duration.Companion.hours // Define LOTL Sources for different contexts val pidLotl = LOTLSource().apply { url = "https://ec.europa.eu/tools/lotl/eu-lotl.xml" // Configure for PID } val pubEaaLotl = LOTLSource().apply { url = "https://ec.europa.eu/tools/lotl/eu-lotl.xml" // Configure for PubEAA } // Create GetTrustAnchorsFromLoTL val getTrustAnchors = GetTrustAnchorsFromLoTL( dssOptions = DssOptions.usingFileCacheDataLoader( fileCacheExpiration = 24.hours, cacheDirectory = createTempDirectory("lotl-cache"), ) ) // Create separate validators for each context val pidValidator = IsChainTrustedForContext( supportedContexts = setOf(VerificationContext.PID), getTrustAnchors = getTrustAnchors.transform( mapOf(VerificationContext.PID to pidLotl) ), validateCertificateChain = ValidateCertificateChainUsingPKIXJvm.Default ) val pubEAAValidator = IsChainTrustedForContext( supportedContexts = setOf(VerificationContext.PubEAA), getTrustAnchors = getTrustAnchors.transform( mapOf(VerificationContext.PubEAA to pubEaaLotl) ), validateCertificateChain = ValidateCertificateChainUsingPKIXJvm.Default ) // Combine validators val trustValidator = ComposeChainTrust.of(pidValidator, pubEAAValidator) val isTrusted = IsChainTrustedForEUDIW(trustValidator) // Use it val pidResult = isTrusted(pidChain, VerificationContext.PID) val pubEAAResult = isTrusted(pubEAAChain, VerificationContext.PubEAA) ``` -------------------------------- ### Create and Combine Validators for Multiple Contexts Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/consultation-dss/README.md Create separate IsChainTrustedForContext validators for different contexts (PID, PubEAA) and then combine them using ComposeChainTrust for unified validation. ```kotlin // Create separate validators for each context val pidValidator = IsChainTrustedForContext( supportedContexts = setOf(VerificationContext.PID), getTrustAnchors = getTrustAnchors.transform( mapOf(VerificationContext.PID to pidLotlSource) ), validateCertificateChain = ValidateCertificateChainUsingPKIXJvm.Default ) val pubEAAValidator = IsChainTrustedForContext( supportedContexts = setOf(VerificationContext.PubEAA), getTrustAnchors = getTrustAnchors.transform( mapOf(VerificationContext.PubEAA to pubEaaLotlSource) ), validateCertificateChain = ValidateCertificateChainUsingPKIXJvm.Default ) // Combine them val trustValidator = ComposeChainTrust.of(pidValidator, pubEAAValidator) val isTrusted = IsChainTrustedForEUDIW(trustValidator) ``` -------------------------------- ### Android Consultation DSS Dependency Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/consultation-dss/README.md Add the Consultation DSS library as a dependency for Android projects. Replace with the appropriate version. ```kotlin dependencies { implementation("eu.europa.ec.eudi:etsi-1196x2-consultation-dss:") } ``` -------------------------------- ### Key Usage Criticality Requirement Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/docs/wrpac-assessment.md Enforces the criticality requirement for the Key Usage extension. ```kotlin requireKeyUsageCritical() ``` -------------------------------- ### In-Memory Caching for Trust Anchors Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/consultation/README.md Shows how to add transparent in-memory caching to a `GetTrustAnchors` source using the `cached()` decorator. The decorated source is `Disposable` and must be managed to release resources. This improves performance by preventing duplicate computations and refreshing entries after a specified TTL. ```kotlin // 1. Define a base trust anchors source // and decorate it with caching val cachedGetTrustAnchors: GetTrustAnchors = GetTrustAnchors { ctx -> // Fetch anchors for the given context fetchAnchorsFor(ctx) }.cached( ttl = 10.minutes, expectedQueries = 10 ) // 3. Use the cached source with IsChainTrustedForContext useResources { val getTrustAnchors = cachedGetTrustAnchors.bind() val validator = IsChainTrustedForContext( supportedContexts = setOf(VerificationContext.PID), getTrustAnchors = getTrustAnchors, validateCertificateChain = ValidateCertificateChainUsingPKIXJvm() ) val isChainTrusted = IsChainTrustedForEUDIW(validator) val result = isChainTrusted(chain, VerificationContext.PID) } ``` -------------------------------- ### Add Signature Algorithm Validation Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/docs/pid-assessment.md Configure allowed signature algorithms for enhanced security. This is an optional enhancement for defensive programming and not a mandatory requirement. ```kotlin signatureAlgorithm( allowedAlgorithms = listOf( "1.2.840.113549.1.1.11", // sha256WithRSAEncryption "1.2.840.113549.1.1.12", // sha384WithRSAEncryption "1.2.840.113549.1.1.13", // sha512WithRSAEncryption "1.2.840.10045.4.3.2", // ecdsa-with-SHA256 "1.2.840.10045.4.3.3", // ecdsa-with-SHA384 "1.2.840.10045.4.3.4" // ecdsa-with-SHA512 ) ) ``` -------------------------------- ### Add ETSI 119602 Consultation Dependency Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/119602-consultation/README.md Add the ETSI 119602 Consultation library dependency to your `build.gradle.kts` file. Ensure you replace `$version` with the latest release version. ```kotlin dependencies { // Replace $version with the latest release version // All modules share the same same version number implementation("eu.europa.ec.eudi:etsi-119602-consultation:$version") } ``` -------------------------------- ### Add Kotlin Serialization Dependency Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/119602-data-model/README.md Include the kotlinx-serialization-json dependency for JSON handling in your project. ```kotlin dependencies { implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinxSerializationVersion") } ``` -------------------------------- ### JVM/Android Specific Dependencies Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/119602-consultation/README.md Includes the Bouncy Castle library for cryptographic operations on JVM and Android platforms. ```kotlin dependencies { // Bouncy Castle for cryptographic operations implementation("org.bouncycastle:bcprov-jdk18on:$bouncyCastleVersion") } ``` -------------------------------- ### Add Dependency to build.gradle.kts Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/119602-data-model/README.md Include the data model module dependency in your project's build file. ```kotlin dependencies { implementation("eu.europa.ec.eudi:etsi-119602-data-model:$version") } ``` -------------------------------- ### Check Certificate Policy Presence Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/docs/pid-assessment.md Verifies that the Certificate Policy extension is present in the certificate, as required by EN 319 412-2 §4.3.3. ```kotlin policyIsPresent() ``` -------------------------------- ### Validate QC Statements Extension Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/docs/pid-assessment.md Ensures the qcStatements extension, specifically for id-etsi-qct-pid, is present and fully validated with a compliance flag. ```kotlin qcStatements (id-etsi-qct-pid) ``` -------------------------------- ### Certificate Validity Check Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/docs/wrpac-assessment.md Checks if the certificate is valid at a specific point in time. ```kotlin validAt(at) ``` -------------------------------- ### Android DSS Patch Gradle Configuration Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/consultation-dss/README.md Apply the provided Gradle script to patch DSS dependencies for Android compatibility. This script handles the XMLInputFactory issue. ```kotlin apply(from = "gradle/dss-android-patch.gradle.kts") ``` -------------------------------- ### Deserialize LoTE JSON to Kotlin Object Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/119602-data-model/README.md Parses a JSON string representing a List of Trusted Entities into a Kotlin object. Ensure the JSON structure conforms to the ETSI TS 119 602 schema. ```kotlin import eu.europa.ec.eudi.etsi119602.datamodel.ListOfTrustedEntities import kotlinx.serialization.json.Json val json = """ { "ListAndSchemeInformation": { "LoTEVersionIdentifier": "1.1.1", "LoTESequenceNumber": 1, "LoTEType": "http://uri.etsi.org/19602/LoTEType", "SchemeOperatorName": [{"lang": "en", "value": "Example Operator"}], "SchemeName": [{"lang": "en", "value": "Example Scheme"}], "SchemeInformationURI": [{"lang": "en", "uriValue": "https://example.com/scheme"}], "StatusDeterminationApproach": "http://uri.etsi.org/19602/StatusDetn/EU", "SchemeTypeCommunityRules": ["http://uri.etsi.org/19602/schemerules/EU"], "SchemeTerritory": "EU", "ListIssueDateTime": "2024-01-01T00:00:00Z", "NextUpdate": {"dateTime": "2025-01-01T00:00:00Z"} }, "TrustedEntitiesList": [ { "TrustedEntityInformation": { "TEName": [{"lang": "en", "value": "Example Provider"}], "TEAddress": { "TEPostalAddress": [{"lang": "en", "StreetAddress": "Example St", "Locality": "Brussels", "Country": "BE"}], "TEElectronicAddress": [{"lang": "en", "uriValue": "https://example.com"}] }, "TEInformationURI": [{"lang": "en", "uriValue": "https://example.com/info"}] }, "TrustedEntityServices": [ { "ServiceInformation": { "ServiceName": [{"lang": "en", "value": "PID Issuance"}], "ServiceDigitalIdentity": { "X509Certificates": [{"encoding": "base64", "val": "MIID..."}] }, "ServiceTypeIdentifier": "http://uri.etsi.org/19602/SvcType/PID/Issuance", "ServiceStatus": "http://uri.etsi.org/19602/Status/granted" } } ] } ] } """.trimIndent() val jsonFormat = Json { ignoreUnknownKeys = true } val lote = jsonFormat.decodeFromString(json) // Access LoTE data println("LoTE Version: ${lote.schemeInformation.loteVersionIdentifier}") println("Trusted Entities: ${lote.entities?.size}") ``` -------------------------------- ### Access LoTE Scheme and Entity Information Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/119602-data-model/README.md Deserialize a ListOfTrustedEntities from JSON and access its scheme and entity details. This is useful for retrieving information about trusted entities and their services. ```kotlin val lote: ListOfTrustedEntities = // ... deserialize from JSON // Get scheme information val schemeInfo = lote.schemeInformation println("Scheme: ${schemeInfo.schemeName.firstOrNull()?.value}") println("Operator: ${schemeInfo.schemeOperatorName.firstOrNull()?.value}") println("Territory: ${schemeInfo.schemeTerritory.code}") // Iterate over trusted entities lote.entities?.forEach { entity -> println("Entity: ${entity.information.name.firstOrNull()?.value}") entity.services.forEach { service -> println(" Service: ${service.information.name.firstOrNull()?.value}") println(" Type: ${service.information.typeIdentifier?.value}") println(" Status: ${service.information.status?.value}") } } ``` -------------------------------- ### Validate Key Usage Extension Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/docs/pid-assessment.md Ensures the keyUsage extension has the digitalSignature bit set and is critically marked as required. ```kotlin keyUsage ``` -------------------------------- ### QC Statement Requirements Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/docs/wrpac-assessment.md Ensures that the certificate requires specific QC Statements for policy compliance, including QcCompliance, QcSSCD, and QcType (QCP-l only). ```kotlin requireQcStatementsForPolicy ``` -------------------------------- ### Validate QC Statements Extension Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/docs/wallet-provider-assessment.md Fully validates the qcStatements extension (id-etsi-qct-wal) with the compliance flag. ```kotlin qcStatements (id-etsi-qct-wal) ``` -------------------------------- ### Validate Mandatory QCStatement for PID Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/docs/pid-assessment.md Ensures the presence and compliance of the id-etsi-qct-pid QCStatement. This is a requirement for PID certificates as per TS 119 412-6 PID-4.5-01. ```kotlin mandatoryQcStatement(qcType = ID_ETSI_QCT_PID, requireCompliance = true) ``` -------------------------------- ### Organization Identifier Format Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/docs/wrpac-assessment.md Defines the regular expression pattern for validating organization identifiers according to ETSI EN 319 412-1. ```kotlin ETSI319412Part1.ORG_ID_PATTERN ``` -------------------------------- ### Serialize Kotlin Object to LoTE JSON Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/119602-data-model/README.md Converts a Kotlin ListOfTrustedEntities object into a JSON string. Useful for generating LoTE documents. ```kotlin import eu.europa.ec.eudi.etsi119602.datamodel.* import kotlinx.serialization.json.Json // Create LoTE structure val schemeInfo = ListAndSchemeInformation( loteVersionIdentifier = ETSI19602.LOTE_VERSION, loteSequenceNumber = ETSI19602.INITIAL_SEQUENCE_NUMBER, loteType = URI(ETSI19602.LOTE_TYPE_URI), schemeOperatorName = listOf(MultilanguageString("en", "Example Operator")), schemeName = listOf(MultilanguageString("en", "Example Scheme")), schemeInformationURI = listOf(MultiLanguageURI("en", URI("https://example.com/scheme"))), statusDeterminationApproach = URI(ETSI19602.EU_PID_PROVIDERS_STATUS_DETERMINATION_APPROACH), schemeTypeCommunityRules = listOf(URI(ETSI19602.EU_PID_PROVIDERS_SCHEME_COMMUNITY_RULES)), schemeTerritory = CountryCode("EU"), listIssueDateTime = LoTEDateTime.now(), nextUpdate = NextUpdate(dateTime = LoTEDateTime.now().plusDays(365)) ) val lote = ListOfTrustedEntities(schemeInformation = schemeInfo, entities = emptyList()) // Serialize to JSON val jsonFormat = Json { prettyPrint = true } val json = jsonFormat.encodeToString(ListOfTrustedEntities.serializer(), lote) ``` -------------------------------- ### X.509 Version 3 Validation Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/docs/wrpac-assessment.md Validates that the certificate is an X.509 version 3 certificate. ```kotlin version3() ``` -------------------------------- ### Validate Organization Identifier Format Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/docs/pid-assessment.md Confirms that the format of the organizationIdentifier attribute is validated for legal persons. ```kotlin organizationIdentifier fmt ``` -------------------------------- ### Validate PID Provider Explicit Extension Criticality Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/docs/pid-assessment.md Ensures that extension criticality is handled explicitly for PID providers, according to TS 119 412-6 PID-4.1-02. ```kotlin pidProviderExplicitExtensionCriticality() ``` -------------------------------- ### JVM Validation Policy Dependency Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/consultation-dss/README.md Include the DSS policy dependency for validation capabilities on JVM. More details are available in the DSS cookbook. ```kotlin implementation("eu.europa.ec.joinup.sd-dss:dss-policy-jaxb:$dssVersion") ``` -------------------------------- ### Validate Subject Key Identifier Extension Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/docs/pid-assessment.md Confirms the presence of the subjectKeyIdentifier extension, which is mandatory and not critical. ```kotlin subjectKeyIdentifier ``` -------------------------------- ### Enforce QCStatement Compliance Flag Source: https://github.com/eu-digital-identity-wallet/eudi-lib-kmp-etsi-1196x2/blob/main/docs/pid-assessment.md Validates that the compliance flag within the QCStatement is set to true, adhering to EN 319 412-5. ```kotlin requireCompliance = true ```