How to use EUDIW / ARF reference helpers¶
| Field | Value |
|---|---|
| Package maturity | Reference (SdJwt.Net.Eudiw) |
| Code status | Runnable package APIs with illustrative ARF validation wiring |
| Related concept | Verifiable Credentials |
| Audience | Developers building wallet-framework integrations and architects evaluating eIDAS 2.0 / ARF-aligned patterns. |
| Purpose | Walk through configuring EUDIW / ARF reference helpers for PID/mDL validation, member-state checks, trust-list models, and HAIP profile-oriented validation using SdJwt.Net.Eudiw. |
| Scope | Reference wallet configuration, algorithm/type/claim validation, member-state and issuer trust checks, ARF-oriented storage and presentation policies, and error handling. Out of scope: general wallet concepts (see Wallet Integration). |
| Success criteria | Reader can configure the reference wrapper, validate PID-style credentials, understand EU LOTL integration points, and identify where certified EUDIW ecosystem components are still required. |
SdJwt.Net.Eudiwis reference infrastructure. It is not a certified EU Digital Identity Wallet, not a trust service provider, and not a replacement for national onboarding, conformity assessment, relying-party registration, or EU trust-list governance.
What your application still owns¶
This guide does not provide: certified EUDIW wallet builds, national PID issuance, trust service provider integration, relying-party registration, EU LOTL key management, conformity assessment, or eIDAS 2.0 legal compliance review.
Key decisions¶
| Decision | Options | Guidance |
|---|---|---|
| ARF-oriented policy? | Strict, Warning, Disabled | Strict for high-assurance deployments |
| HAIP Final profile? | SD-JWT VC, mdoc, OID4VP redirect, DC API | Match the wallet credential formats and presentation flows |
| Trust validation? | LOTL, Per-issuer, Disabled | LOTL for production |
| Credential storage? | In-memory, Secure, HSM | HSM-backed for production |
Prerequisites¶
dotnet add package SdJwt.Net.Eudiw
dotnet add package SdJwt.Net.Wallet
EUDIW / ARF overview¶
EUDIW-style ecosystems under eIDAS 2.0 / ARF concepts require:
- ARF-oriented algorithm policy: only ES256/ES384/ES512 algorithms
- HAIP Final flow/profile validation for the selected wallet capabilities
- EU Trust Lists: issuer validation via LOTL
- PID/mDL support: core credential formats
1. Create an EUDIW-style reference wallet¶
using SdJwt.Net.Eudiw;
using SdJwt.Net.Wallet.Storage;
// Create storage and key management
var store = new InMemoryCredentialStore();
var keyManager = new SoftwareKeyManager();
// Create EUDI wallet with defaults (ARF enforced)
var wallet = new EudiWallet(store, keyManager);
Console.WriteLine($"ARF Enforced: {wallet.IsArfEnforced}"); // true
2. Configure options¶
var options = new EudiWalletOptions
{
WalletId = "citizen-wallet-001",
DisplayName = "My EU Wallet",
EnforceArfValidation = true,
ValidateIssuerTrust = true, // Validate against EU Trust Lists
TrustListCacheHours = 6, // Cache LOTL for 6 hours
SupportedCredentialTypes = new[]
{
EudiwConstants.Pid.DocType, // Person Identification Data
EudiwConstants.Mdl.DocType, // Mobile Driving License
"eu.europa.ec.eudi.health.1" // Health credentials
}
};
var wallet = new EudiWallet(store, keyManager, eudiOptions: options);
3. Validate algorithms¶
EUDIW only allows specific ECDSA algorithms per ARF:
// Valid algorithms per EUDIW ARF
wallet.ValidateAlgorithm("ES256"); // true
wallet.ValidateAlgorithm("ES384"); // true
wallet.ValidateAlgorithm("ES512"); // true
// Invalid algorithms
wallet.ValidateAlgorithm("RS256"); // false - RSA not allowed
wallet.ValidateAlgorithm("PS256"); // false - PS not in ARF
wallet.ValidateAlgorithm("HS256"); // false - symmetric not allowed
4. Validate credential types¶
// Validate PID credential type
var pidResult = wallet.ValidateCredentialType(EudiwConstants.Pid.DocType);
if (pidResult.IsValid)
{
Console.WriteLine($"Type: {pidResult.CredentialType}"); // Pid
}
// Validate mDL credential type
var mdlResult = wallet.ValidateCredentialType(EudiwConstants.Mdl.DocType);
if (mdlResult.IsValid)
{
Console.WriteLine($"Type: {mdlResult.CredentialType}"); // Mdl
}
// Unknown types are rejected
var unknownResult = wallet.ValidateCredentialType("custom.credential");
Console.WriteLine(unknownResult.IsValid); // false
5. Validate PID claims¶
Person Identification Data must contain mandatory claims:
// Complete PID claims
var validClaims = new Dictionary<string, object>
{
// Mandatory claims per ARF
["family_name"] = "Garcia",
["given_name"] = "Sofia",
["birth_date"] = "1990-05-15",
["issuance_date"] = "2025-01-01",
["expiry_date"] = "2030-01-01",
["issuing_authority"] = "Spanish Ministry of Interior",
["issuing_country"] = "ES",
// Optional claims
["age_over_18"] = true,
["nationality"] = "ES"
};
var result = wallet.ValidatePidClaims(validClaims);
if (result.IsValid)
{
// Extract typed PID credential
var pid = wallet.ExtractPidCredential(validClaims);
Console.WriteLine($"Name: {pid.GivenName} {pid.FamilyName}");
Console.WriteLine($"Issued by: {pid.IssuingAuthority}");
}
// Incomplete claims are rejected
var incompleteClaims = new Dictionary<string, object>
{
["family_name"] = "Garcia",
["given_name"] = "Sofia"
// Missing: birth_date, issuance_date, expiry_date, etc.
};
var invalidResult = wallet.ValidatePidClaims(incompleteClaims);
Console.WriteLine($"Valid: {invalidResult.IsValid}"); // false
Console.WriteLine($"Missing: {string.Join(", ", invalidResult.MissingClaims)}");
6. Validate member states¶
Only EU member state issuers are accepted:
// EU member states
wallet.ValidateMemberState("DE"); // true - Germany
wallet.ValidateMemberState("FR"); // true - France
wallet.ValidateMemberState("ES"); // true - Spain
wallet.ValidateMemberState("IT"); // true - Italy
// Non-EU countries
wallet.ValidateMemberState("US"); // false
wallet.ValidateMemberState("GB"); // false - UK left EU
wallet.ValidateMemberState("CH"); // false - Switzerland not EU
// Get all supported member states
var memberStates = wallet.GetSupportedMemberStates();
Console.WriteLine($"Supported: {memberStates.Count} member states"); // 27
7. Validate issuer trust¶
Issuers must appear in EU Trust Lists:
// German PID provider
var trustResult = await wallet.ValidateIssuerTrustAsync(
"https://pid-provider.bundesdruckerei.de");
if (trustResult.IsTrusted)
{
Console.WriteLine($"Country: {trustResult.MemberState}"); // DE
Console.WriteLine($"Type: {trustResult.ServiceType}"); // QualifiedAttestation
}
// Non-EU issuer
var untrustedResult = await wallet.ValidateIssuerTrustAsync(
"https://issuer.example.com");
if (!untrustedResult.IsTrusted)
{
Console.WriteLine($"Errors: {string.Join(", ", untrustedResult.Errors)}");
}
8. Store credentials with enforcement¶
Credentials are validated against ARF when stored:
try
{
// Store with automatic ARF validation
var stored = await wallet.StoreCredentialAsync(parsedCredential);
Console.WriteLine($"Stored: {stored.Id}");
}
catch (ArfComplianceException ex)
{
// Credential type or format is not allowed by the configured ARF-oriented policy
Console.WriteLine($"ARF Violations: {string.Join(", ", ex.Violations)}");
}
catch (EudiTrustException ex)
{
// Issuer not in EU Trust List
Console.WriteLine($"Trust Error: {ex.Message}");
}
9. Find specific credential types¶
// Find all PID credentials
var pidCredentials = await wallet.FindPidCredentialsAsync();
foreach (var cred in pidCredentials)
{
Console.WriteLine($"PID: {cred.Id} from {cred.Issuer}");
}
// Find all mDL credentials
var mdlCredentials = await wallet.FindMdlCredentialsAsync();
foreach (var cred in mdlCredentials)
{
Console.WriteLine($"mDL: {cred.Id} from {cred.Issuer}");
}
10. Create presentations with ARF validation¶
// Presentation is validated against ARF before creation
try
{
var presentation = await wallet.CreatePresentationAsync(
credentialId: storedCredential.Id,
disclosurePaths: new[] { "family_name", "birth_date", "age_over_18" },
audience: "https://verifier.example.eu",
nonce: "unique-nonce-123"
);
}
catch (ArfComplianceException ex)
{
// Credential validity or type issues
Console.WriteLine($"Cannot present: {ex.Message}");
}
Error handling¶
ARF-oriented policy exceptions¶
try
{
// ... wallet operations
}
catch (ArfComplianceException ex)
{
// List of specific ARF violations
foreach (var violation in ex.Violations)
{
Console.WriteLine($"- {violation}");
}
}
Trust exceptions¶
try
{
// ... wallet operations
}
catch (EudiTrustException ex)
{
// Issuer not trusted by EU Trust Lists
Console.WriteLine($"Trust failure: {ex.Message}");
}
Best practices¶
- Keep
EnforceArfValidation = truein production. - Set
RequireHardwareKeys = truefor high-assurance scenarios. - Choose an appropriate
TrustListCacheHoursto balance freshness and performance. - Validate credentials before storing or presenting.
- Audit all trust validation results for compliance.
- Catch both
ArfComplianceExceptionandEudiTrustExceptionexplicitly.