Skip to content

EU Digital Identity Wallet (EUDIW) Deep Dive

Audience Architects and developers building eIDAS 2.0-compliant solutions, and product teams evaluating EUDIW adoption for EU market entry.
Purpose Explain the EU Digital Identity Wallet ecosystem - eIDAS 2.0 regulation, Architecture Reference Framework (ARF), PID/attestation lifecycle, and trust infrastructure - and show how SdJwt.Net.Eudiw implements ARF-compliant flows.
Scope eIDAS 2.0 context, ARF compliance, PID and attestation credential types, mdoc and SD-JWT VC dual-format support, HAIP-level security, trust lists, and Issuer/Verifier/Wallet integration patterns. Out of scope: general SD-JWT mechanics, OID4VP internals.
Success criteria Reader can configure an EUDIW-compliant issuer and verifier, handle PID credentials, apply ARF trust policies, and explain the dual-format (mdoc + SD-JWT VC) strategy.

Prerequisites

Before diving into EUDIW, you should understand these foundational concepts:

What is eIDAS 2.0?

eIDAS 2.0 (Electronic Identification, Authentication and Trust Services) is the EU regulation that mandates digital identity wallets for all EU citizens by 2026. It builds on the original eIDAS regulation to enable:

  • Cross-border digital identity verification
  • Privacy-preserving credential presentation
  • Qualified electronic signatures from mobile devices
  • Trust interoperability between member states

What is the Architecture Reference Framework (ARF)?

The Architecture Reference Framework is the technical specification that defines how EUDIW implementations must work:

  • Credential formats (mdoc for PID/mDL, SD-JWT VC for attestations)
  • Cryptographic algorithms (HAIP-compliant)
  • Trust infrastructure (EU Trust Lists)
  • Protocol requirements (OpenID4VCI, OpenID4VP)

The Problem EUDIW Solves

Citizens across EU member states currently face:

  1. Fragmented identity: Different digital IDs per country and service
  2. Privacy concerns: Over-disclosure of personal data
  3. Cross-border friction: National IDs not accepted elsewhere
  4. Trust complexity: No unified way to verify credential issuers

EUDIW provides:

  • Single wallet app for all digital credentials
  • Selective disclosure (show only what's needed)
  • Cross-border acceptance (French wallet works in Germany)
  • Unified trust infrastructure (EU Trust Lists)

Glossary of Key Terms

Term Definition
EUDIW EU Digital Identity Wallet - the app citizens use
ARF Architecture Reference Framework - technical specification
PID Person Identification Data - core identity credential
mDL Mobile Driving License - per ISO 18013-5
QEAA Qualified Electronic Attestation of Attributes
EAA Electronic Attestation of Attributes (non-qualified)
RP Relying Party - service requesting credentials
LOTL List of Trusted Lists - EU trust anchor
TSP Trust Service Provider - credential issuers
Member State EU country participating in EUDIW ecosystem
DocType Credential type identifier for mdoc format
vct Verifiable Credential Type for SD-JWT VC format

Implementation Overview

Package Structure

The SdJwt.Net.Eudiw package provides:

SdJwt.Net.Eudiw/
   Arf/
      ArfCredentialType.cs       # Credential type enumeration
      ArfProfileValidator.cs     # ARF compliance validation
      ArfValidationResult.cs     # Validation result model
   Credentials/
      PidCredentialHandler.cs    # PID processing and validation
      QeaaHandler.cs             # Qualified attestation handling
   RelyingParty/
      RpRegistration.cs          # RP registration model
      RpRegistrationValidator.cs # RP validation logic
   TrustFramework/
      EuTrustListResolver.cs     # EU Trust List integration
      TrustedServiceProvider.cs  # TSP model
      TrustServiceType.cs        # TSP type enumeration
      TrustValidationResult.cs   # Trust validation result
   EudiwConstants.cs             # Constants and definitions

Core Components

ArfProfileValidator

Validates credentials against ARF requirements:

using SdJwt.Net.Eudiw.Arf;

var validator = new ArfProfileValidator();

// Validate cryptographic algorithm
bool isValidAlg = validator.ValidateAlgorithm("ES256"); // true
bool isInvalidAlg = validator.ValidateAlgorithm("RS256"); // false (not ARF-compliant)

// Validate credential type
var result = validator.ValidateCredentialType("eu.europa.ec.eudi.pid.1");
if (result.IsValid)
{
    Console.WriteLine($"Credential type: {result.CredentialType}"); // Pid
}

// Validate EU member state
bool isEu = validator.ValidateMemberState("DE"); // true
bool isNotEu = validator.ValidateMemberState("US"); // false

Key validations:

  • Algorithm compliance: Only HAIP-approved algorithms
  • Credential types: PID, mDL, QEAA, EAA
  • Member states: All 27 EU countries
  • PID claims: Mandatory and optional fields

PidCredentialHandler

Processes Person Identification Data credentials:

using SdJwt.Net.Eudiw.Credentials;

var handler = new PidCredentialHandler();

var claims = new Dictionary<string, object>
{
    // Mandatory PID claims
    ["family_name"] = "Mustermann",
    ["given_name"] = "Erika",
    ["birth_date"] = "1964-08-12",
    ["issuance_date"] = "2024-01-01",
    ["expiry_date"] = "2029-01-01",
    ["issuing_authority"] = "Bundesdruckerei",
    ["issuing_country"] = "DE",

    // Optional PID claims
    ["birth_place"] = "Berlin",
    ["nationality"] = "DE",
    ["age_over_18"] = true
};

// Validate claims
var validation = handler.Validate(claims);
if (validation.IsValid)
{
    // Convert to typed model
    var credential = handler.ToPidCredential(claims);
    Console.WriteLine($"{credential.GivenName} {credential.FamilyName}");
    Console.WriteLine($"Issued by: {credential.IssuingAuthority}");
}
else
{
    foreach (var error in validation.Errors)
    {
        Console.WriteLine($"Validation error: {error}");
    }
}

Mandatory PID claims per ARF:

Claim Description Example
family_name Current family name "Mustermann"
given_name Current first name "Erika"
birth_date Date of birth "1964-08-12"
issuance_date Credential issuance date "2024-01-01"
expiry_date Credential expiration "2029-01-01"
issuing_authority Authority that issued the PID "Bundesdruckerei"
issuing_country Member state ISO code "DE"

QeaaHandler

Handles Qualified Electronic Attestations of Attributes:

using SdJwt.Net.Eudiw.Credentials;

var handler = new QeaaHandler();

// University diploma as QEAA
var diplomaClaims = new Dictionary<string, object>
{
    ["degree_type"] = "Masters",
    ["field_of_study"] = "Computer Science",
    ["issuing_institution"] = "Technical University of Munich",
    ["graduation_date"] = "2023-07-15",
    ["issuing_country"] = "DE"
};

var validation = handler.Validate(diplomaClaims, QeaaType.EducationalCredential);

QEAA types supported:

  • Educational credentials (diplomas, certificates)
  • Professional qualifications (licenses)
  • Healthcare credentials (prescriptions)
  • Travel documents (visas)
  • Financial attestations (creditworthiness)

RpRegistrationValidator

Validates Relying Party registrations:

using SdJwt.Net.Eudiw.RelyingParty;

var validator = new RpRegistrationValidator();

var registration = new RpRegistration
{
    ClientId = "https://bank.example.de",
    OrganizationName = "Example Bank AG",
    RedirectUris = new[] { "https://bank.example.de/callback" },
    ResponseTypes = new[] { "vp_token" },
    TrustFramework = "eu.eudiw.trust",
    Contacts = new[] { "security@bank.example.de" }
};

var result = validator.Validate(registration);
if (result.IsValid)
{
    Console.WriteLine("RP registration is valid for EUDIW ecosystem");
}
else
{
    foreach (var error in result.Errors)
    {
        Console.WriteLine($"Registration error: {error}");
    }
}

EuTrustListResolver

Resolves and validates issuers against EU Trust Lists:

using SdJwt.Net.Eudiw.TrustFramework;

var resolver = new EuTrustListResolver();

// Resolve Trust Service Provider
var tsp = await resolver.ResolveAsync(
    issuerIdentifier: "https://pid.bundesdruckerei.de",
    memberState: "DE"
);

if (tsp != null)
{
    Console.WriteLine($"TSP: {tsp.Name}");
    Console.WriteLine($"Service Type: {tsp.ServiceType}");
    Console.WriteLine($"Status: {tsp.Status}");

    // Validate trust chain
    var trustResult = await resolver.ValidateTrustChainAsync(tsp);
    if (trustResult.IsValid)
    {
        Console.WriteLine("Issuer is trusted under EU Trust Lists");
    }
}

Credential Types

Person Identification Data (PID)

The PID is the core identity credential in EUDIW:

// DocType for mdoc format
const string PidDocType = "eu.europa.ec.eudi.pid.1";

// Namespace for PID claims
const string PidNamespace = "eu.europa.ec.eudi.pid.1";

PID supports two formats:

Format Use Case Transport
mdoc NFC/BLE proximity Device proximity
SD-JWT VC Online/cross-device OpenID4VP

Mobile Driving License (mDL)

Follows ISO 18013-5 with EUDIW extensions:

// DocType for mDL
const string MdlDocType = "org.iso.18013.5.1.mDL";

// EUDIW-specific mDL claims
var mDlClaims = new Dictionary<string, object>
{
    ["family_name"] = "Mustermann",
    ["given_name"] = "Erika",
    ["birth_date"] = "1964-08-12",
    ["document_number"] = "D1234567890",
    ["driving_privileges"] = new[] { "B", "C1" },
    ["issue_date"] = "2024-01-01",
    ["expiry_date"] = "2034-01-01",
    ["issuing_country"] = "DE",
    ["issuing_authority"] = "Kraftfahrt-Bundesamt"
};

Qualified Attestations (QEAA)

High-trust attestations from qualified TSPs:

// VCT prefix for QEAA
const string QeaaVctPrefix = "eu.europa.ec.eudi.qeaa";

// Example: Professional license as QEAA
var lawyerLicense = new Dictionary<string, object>
{
    ["vct"] = "eu.europa.ec.eudi.qeaa.professional.lawyer.de",
    ["bar_association"] = "Rechtsanwaltskammer Berlin",
    ["license_number"] = "RAK-B-12345",
    ["admission_date"] = "2010-05-15",
    ["specializations"] = new[] { "Corporate Law", "M&A" }
};

Non-Qualified Attestations (EAA)

Lower-trust attestations for general use:

// VCT prefix for EAA
const string EaaVctPrefix = "eu.europa.ec.eudi.eaa";

// Example: Gym membership as EAA
var gymMembership = new Dictionary<string, object>
{
    ["vct"] = "eu.europa.ec.eudi.eaa.membership.gym",
    ["membership_id"] = "GYM-2024-001",
    ["valid_from"] = "2024-01-01",
    ["valid_until"] = "2024-12-31",
    ["membership_type"] = "Premium"
};

Algorithm Requirements

EUDIW enforces HAIP-compliant algorithms:

Algorithm Status Security Level
ES256 Required HAIP Level 2
ES384 Supported HAIP Level 2+
ES512 Supported HAIP Level 3
RS256 Not allowed -
RS384 Not allowed -
HS256 Not allowed -
using SdJwt.Net.Eudiw.Arf;

var validator = new ArfProfileValidator();

// These pass ARF validation
validator.ValidateAlgorithm("ES256"); // true
validator.ValidateAlgorithm("ES384"); // true
validator.ValidateAlgorithm("ES512"); // true

// These fail ARF validation
validator.ValidateAlgorithm("RS256"); // false
validator.ValidateAlgorithm("HS256"); // false
validator.ValidateAlgorithm("EdDSA"); // false (not in ARF yet)

EU Member States

All 27 EU member states are supported:

Code Country Code Country
AT Austria BE Belgium
BG Bulgaria CY Cyprus
CZ Czech Republic DE Germany
DK Denmark EE Estonia
ES Spain FI Finland
FR France GR Greece
HR Croatia HU Hungary
IE Ireland IT Italy
LT Lithuania LU Luxembourg
LV Latvia MT Malta
NL Netherlands PL Poland
PT Portugal RO Romania
SE Sweden SI Slovenia
SK Slovakia
using SdJwt.Net.Eudiw;

// Access all member states
var allStates = EudiwConstants.MemberStates.All;
Console.WriteLine($"Supported states: {string.Join(", ", allStates)}");

Trust Infrastructure

EU Trust Lists

The EUDIW trust infrastructure is built on EU Trust Lists:

LOTL (List of Trusted Lists)
   /--- Member State Trust Lists ---\
   |                                |
   v                                v
 DE Trust List              FR Trust List
   |                          |
   v                          v
TSP 1 (Bundesdruckerei)    TSP 1 (ANTS)
TSP 2 (D-Trust)            TSP 2 (Docaposte)
   ...                        ...

Trust Validation

using SdJwt.Net.Eudiw.TrustFramework;

public class EuTrustService
{
    private readonly EuTrustListResolver _resolver;

    public async Task<TrustValidationResult> ValidateIssuerAsync(
        string issuerIdentifier,
        ArfCredentialType expectedType)
    {
        // Resolve TSP from Trust Lists
        var tsp = await _resolver.ResolveAsync(issuerIdentifier);

        if (tsp == null)
        {
            return TrustValidationResult.Failed("Issuer not found in EU Trust Lists");
        }

        // Validate TSP is authorized for credential type
        var authorizedTypes = GetAuthorizedTypes(tsp.ServiceType);
        if (!authorizedTypes.Contains(expectedType))
        {
            return TrustValidationResult.Failed(
                $"TSP not authorized to issue {expectedType} credentials");
        }

        // Validate TSP status
        if (tsp.Status != TspStatus.Granted)
        {
            return TrustValidationResult.Failed($"TSP status: {tsp.Status}");
        }

        // Validate certificate chain
        return await _resolver.ValidateTrustChainAsync(tsp);
    }
}

Integration with Other Packages

With SdJwt.Net.Oid4Vp

using SdJwt.Net.Eudiw.Arf;
using SdJwt.Net.Oid4Vp.Verifier;

public class EudiwVerifier
{
    private readonly ArfProfileValidator _arfValidator;
    private readonly VpTokenValidator _vpValidator;

    public async Task<VerificationResult> VerifyEudiwCredential(
        string vpToken,
        string expectedCredentialType)
    {
        // First validate VP token
        var vpResult = await _vpValidator.ValidateAsync(vpToken);
        if (!vpResult.IsValid)
        {
            return VerificationResult.Failed(vpResult.ErrorMessage);
        }

        // Validate ARF algorithm compliance
        if (!_arfValidator.ValidateAlgorithm(vpResult.Algorithm))
        {
            return VerificationResult.Failed("Algorithm not ARF-compliant");
        }

        // Validate credential type
        var typeResult = _arfValidator.ValidateCredentialType(vpResult.CredentialType);
        if (!typeResult.IsValid)
        {
            return VerificationResult.Failed(typeResult.ErrorMessage);
        }

        // Validate issuing country is EU member state
        if (!_arfValidator.ValidateMemberState(vpResult.IssuingCountry))
        {
            return VerificationResult.Failed("Issuer not from EU member state");
        }

        return VerificationResult.Success(vpResult.Credentials);
    }
}

With SdJwt.Net.Mdoc

using SdJwt.Net.Eudiw.Arf;
using SdJwt.Net.Mdoc.Verifier;

public class EudiwMdocVerifier
{
    private readonly ArfProfileValidator _arfValidator;
    private readonly MdocVerifier _mdocVerifier;

    public async Task<VerificationResult> VerifyPidAsync(byte[] mdocBytes)
    {
        // Verify mdoc signature
        var mdocResult = await _mdocVerifier.VerifyAsync(mdocBytes);
        if (!mdocResult.IsValid)
        {
            return VerificationResult.Failed(mdocResult.ErrorMessage);
        }

        // Validate DocType is PID
        var typeResult = _arfValidator.ValidateCredentialType(mdocResult.DocType);
        if (!typeResult.IsValid || typeResult.CredentialType != ArfCredentialType.Pid)
        {
            return VerificationResult.Failed("Credential is not a valid PID");
        }

        // Validate PID claims
        var pidResult = _arfValidator.ValidatePidClaims(mdocResult.Claims);
        if (!pidResult.IsValid)
        {
            return VerificationResult.Failed(pidResult.ErrorMessage);
        }

        return VerificationResult.Success(mdocResult.Claims);
    }
}

With SdJwt.Net.HAIP

using SdJwt.Net.Eudiw.Arf;
using SdJwt.Net.HAIP;

public class EudiwHaipValidator
{
    private readonly ArfProfileValidator _arfValidator;
    private readonly HaipCryptoValidator _haipValidator;

    public ValidationResult ValidateSecurityLevel(
        string algorithm,
        string credentialType)
    {
        // ARF validation
        if (!_arfValidator.ValidateAlgorithm(algorithm))
        {
            return ValidationResult.Failed("Algorithm not ARF-compliant");
        }

        // HAIP validation
        var haipResult = _haipValidator.ValidateAlgorithm(
            algorithm,
            HaipSecurityLevel.Level2);

        if (!haipResult.IsValid)
        {
            return ValidationResult.Failed("Algorithm does not meet HAIP Level 2");
        }

        // For PID, require Level 2+
        var typeResult = _arfValidator.ValidateCredentialType(credentialType);
        if (typeResult.CredentialType == ArfCredentialType.Pid)
        {
            var pidHaipResult = _haipValidator.ValidateAlgorithm(
                algorithm,
                HaipSecurityLevel.Level3);

            if (!pidHaipResult.IsValid)
            {
                // Level 2 is acceptable for now, warn about future requirement
                return ValidationResult.Warning(
                    "Level 2 accepted; Level 3 recommended for PID");
            }
        }

        return ValidationResult.Success();
    }
}

EudiWallet Class

The EudiWallet class provides a complete EUDI-compliant wallet implementation extending the generic wallet:

Quick Start

using SdJwt.Net.Eudiw;
using SdJwt.Net.Wallet.Core;
using SdJwt.Net.Wallet.Storage;

// Create EUDI wallet with default settings (ARF enforcement enabled)
var store = new InMemoryCredentialStore();
var keyManager = new SoftwareKeyManager();

var wallet = new EudiWallet(store, keyManager);

// EUDI wallets enforce ARF compliance by default
Console.WriteLine(wallet.IsArfEnforced); // true
Console.WriteLine(wallet.MinimumHaipLevel); // 2 (Very High)

Configuration Options

var options = new EudiWalletOptions
{
    WalletId = "my-eudi-wallet",
    DisplayName = "My EUDI Wallet",
    EnforceArfCompliance = true,
    MinimumHaipLevel = 2,  // HAIP Level 2 (Very High) minimum
    ValidateIssuerTrust = true,
    SupportedCredentialTypes = new[]
    {
        EudiwConstants.Pid.DocType,
        EudiwConstants.Mdl.DocType,
        "eu.europa.ec.eudi.loyalty.1"
    }
};

var wallet = new EudiWallet(store, keyManager, eudiOptions: options);

Algorithm Validation

// Validate algorithms against ARF/HAIP requirements
wallet.ValidateAlgorithm("ES256"); // true - EUDIW compliant
wallet.ValidateAlgorithm("ES384"); // true - EUDIW compliant
wallet.ValidateAlgorithm("RS256"); // false - not ARF compliant

Credential Type Validation

// Validate credential types
var pidResult = wallet.ValidateCredentialType(EudiwConstants.Pid.DocType);
pidResult.IsValid; // true
pidResult.CredentialType; // ArfCredentialType.Pid

var mdlResult = wallet.ValidateCredentialType(EudiwConstants.Mdl.DocType);
mdlResult.IsValid; // true
mdlResult.CredentialType; // ArfCredentialType.Mdl

PID Claims Validation

var claims = new Dictionary<string, object>
{
    ["family_name"] = "Mustermann",
    ["given_name"] = "Erika",
    ["birth_date"] = "1964-08-12",
    ["issuance_date"] = "2024-01-01",
    ["expiry_date"] = "2029-01-01",
    ["issuing_authority"] = "Bundesdruckerei",
    ["issuing_country"] = "DE"
};

var result = wallet.ValidatePidClaims(claims);
if (result.IsValid)
{
    var pid = wallet.ExtractPidCredential(claims);
    Console.WriteLine($"{pid.GivenName} {pid.FamilyName}");
}

Issuer Trust Validation

// Validate issuer against EU Trust List
var trustResult = await wallet.ValidateIssuerTrustAsync(
    "https://pid-provider.bundesdruckerei.de");

if (trustResult.IsTrusted)
{
    Console.WriteLine($"Member State: {trustResult.MemberState}"); // "DE"
    Console.WriteLine($"Service Type: {trustResult.ServiceType}");
}

Member State Validation

// Check EU member states
wallet.ValidateMemberState("DE"); // true
wallet.ValidateMemberState("FR"); // true
wallet.ValidateMemberState("US"); // false

// Get all supported member states
var memberStates = wallet.GetSupportedMemberStates();
// Returns all 27 EU member state codes

Finding Credentials

// Find PID credentials
var pidCredentials = await wallet.FindPidCredentialsAsync();

// Find mDL credentials
var mdlCredentials = await wallet.FindMdlCredentialsAsync();

Storing Credentials with ARF Enforcement

// Credentials are validated against ARF when stored
try
{
    var stored = await wallet.StoreCredentialAsync(parsedCredential);
}
catch (ArfComplianceException ex)
{
    Console.WriteLine($"ARF violations: {string.Join(", ", ex.Violations)}");
}
catch (EudiTrustException ex)
{
    Console.WriteLine($"Trust validation failed: {ex.Message}");
}

Timeline and Adoption

Date Milestone
2024 Q1 eIDAS 2.0 regulation published
2024-2025 Large-Scale Pilots (LSPs) testing
2025 Q4 Member states finalize implementations
2026 EUDIW mandatory acceptance begins
2027+ Full ecosystem operational

References