All Advisories

gematik lib-vau / lib-vau-csharp

AES-GCM Nonce Reuse in VAU Server Encryption

lib-vau and lib-vau-csharp are gematik's reference implementations of the VAU protocol — the security layer that protects traffic to Germany's electronic patient record (ePA) backend, in use across the ~73 million statutory-insured citizens enrolled since 2025 unless they have opted out. The VAU server in both reference implementations reuses the client-supplied Request-Counter value as the 64-bit counter portion of its own AES-GCM initialisation vector, instead of maintaining an independent server-side encryption counter as required by gemSpec_Krypt A_24631 and A_24632. IV uniqueness collapses to the 4 random bytes the server prepends, putting the encrypted server-to-client channel within reach of the Joux forbidden attack at ~77,163 same-counter responses. Tracked as MS-LIB-VAU-774212 (Java) and MS-LIB-VAU-CSHARP-60819d (C#).

Authored byVolker Schönefeld, Simon Weber2026-05-28
SeverityHighCVSS 7.4CVSS 3.1 VectorAV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:NCWECWE-323 (Reusing a Nonce, Key Pair in Encryption)Productgematik lib-vau / lib-vau-csharpAffected Versionslib-vau 1.0.0 through 1.0.15 (Java); lib-vau-csharp 1.0.0 through 1.0.9 (C#)Fixed InNot patched upstream. The vendor has clarified the libraries' experimental status in the README and considers no advisory necessary. Downstream consumers that use the libraries in production deployments are responsible for implementing the missing server-side counter — see Mitigation.

Description

The VAU protocol specification (gemSpec_Krypt V2.40.0) requires in A_24631 that the server maintain its own independent 64-bit encryption counter for the application data key K2_s2c_app_data. The counter must start at 0 and increment on each encryption. A_24632 then specifies that the server's AES-GCM IV is constructed as random(4) || server_encryption_counter(8), while the response header's Request-Counter field separately echoes the client's stored value from A_24630.

Both reference implementations diverge from this. VauServerStateMachine has no server-side counter field and does not override the encryption method. The server's getRequestCounter() returns clientRequestCounter — the value stored from the client's most recent request — and the same value is used for both the response header's Request-Counter field and the AES-GCM IV.

In the Java reference implementation (lib-vau), the server reuses the client's counter for the IV:

lib-vau VauServerStateMachine.java:181-184

@Override
protected long getRequestCounter() {
return clientRequestCounter;
}

View source →

Server stores the client-supplied counter with no monotonicity or replay check:

lib-vau VauServerStateMachine.java:191-194

@Override
protected void checkRequestCounter(long reqCtr) {
this.clientRequestCounter = reqCtr;
}

View source →

IV construction uses getRequestCounter() for the counter portion:

lib-vau AbstractVauStateMachine.java:112-122

public byte[] encryptVauMessage(byte[] cleartext) {
byte[] reqCtrBytes = ByteBuffer.allocate(8).putLong(getRequestCounter()).array();
byte[] header = unionByteArrays(versionByte, puByte, reqByte, reqCtrBytes, getKeyId());
byte[] a = new byte[4];
new SecureRandom().nextBytes(a);
byte[] iv = unionByteArrays(a, reqCtrBytes);
}

View source →

By contrast, the client side correctly increments its own counter before each encryption, illustrating that the missing server-side increment is asymmetric:

lib-vau VauClientStateMachine.java:169-173

@Override
public byte[] encryptVauMessage(byte[] cleartext) {
try {
requestCounter++;
return super.encryptVauMessage(cleartext);

View source →

The C# reference implementation (lib-vau-csharp) carries the same logic and produces the same effect:

lib-vau-csharp VauServerStateMachine.cs:58-70

protected override void CheckRequestCounter(long requestCounter)
{
clientRequestCounter = requestCounter;
}
protected override long GetRequestCounter()
{
return clientRequestCounter;
}

View source →

lib-vau-csharp AbstractVauStateMachine.cs:47-98

public virtual byte[] EncryptVauMessage(byte[] plaintext)
{
long requestCounter = GetRequestCounter();
// ...
byte[] iv = new byte[12];
// iv[0..4] = random
// iv[4..12] = requestCounter (client-supplied)
}

View source →

Because A_24623 explicitly does not enforce replay protection or sequence ordering in the current ePA build stage (ERP=false, ESO=false), a client can send any counter value it likes — including a previously used one. GCM nonce uniqueness for the server's responses then depends on only the 32 random bits the server prepends.

By the birthday paradox, the collision probability for the 4-byte random prefix grows quickly when the counter portion is held constant:

Birthday collision probability for a fixed 64-bit counter

Messages P(IV collision)
100 < 0.01 %
1,000 0.01 %
10,000 1.16 %
50,000 25.23 %
77,163 50.00 %
100,000 68.55 %

AES-GCM provides no confidentiality or integrity guarantees on nonce reuse. The Joux forbidden attack (2006) lets an attacker who observes two ciphertexts encrypted under the same key and IV recover the GHASH authentication key H from the two authentication tags, and XOR the two ciphertexts to recover plaintext bytes.

Impact

  • In ePA terms, the payloads the inner-VAU layer protects include medication records, diagnostic reports, prescription history, document reads and writes, and the authorization transactions that control access to a patient's record. The two effects below apply to those payloads.
  • A legitimate VAU client (or a network attacker who has completed a VAU handshake) can hold the server's IV counter portion constant by replaying a Request-Counter value, and collect server responses until two share the same 12-byte IV.
  • Once two responses share the same IV, the Joux forbidden attack recovers the GHASH authentication key H. With H, the attacker can forge the GCM authentication tag for ciphertexts under any IV where a collision has been observed, enabling them to tamper with server-to-client VAU messages in a session where nonce reuse is ongoing.
  • XOR of the two colliding ciphertexts yields the XOR of the two plaintexts, leaking information about ePA response payloads protected by the inner-VAU AES-GCM layer.
  • Above the inner-VAU layer there is still TLS to the ePA backend, but the inner-VAU layer is the property that ePA's threat model relies on to defend patient data against compromise of TLS infrastructure that sits outside the TEE boundary. Breaking it removes that defence-in-depth boundary.
  • When combined with the missing server authentication (MS-LIB-VAU-eaea8d), the attacker's position is not hypothetical. A MITM who has substituted their own keys during the handshake can pin the client Request-Counter to any fixed value on every relayed request, reducing the birthday collection from an active attack to passive observation on a channel the attacker already controls.

Mitigation

Downstream consumers that ship either library in a production deployment should add an independent serverEncryptionCounter to VauServerStateMachine (or its C# equivalent), initialise it to 0, and override encryptVauMessage() to increment that counter before encryption. A getRequestCounter() override alone is not sufficient — super.encryptVauMessage() uses that return value for both the IV and the header, so the server must fully override encryptVauMessage() to split the two counter uses explicitly. The override needs to use two distinct counter values per A_24632: the server's own counter for the IV's counter portion, and the client's stored value for the response header's Request-Counter field. Until that change is in place, treat the inner-VAU server-to-client channel as offering integrity and confidentiality only on the order of 2^32 messages per session before the GCM guarantees degrade.

Defender's Checklist

  • Identify whether you ship lib-vau / lib-vau-csharp.

    If your ePA client (or any product that speaks VAU) embeds lib-vau or lib-vau-csharp directly, via a fork, or via a git submodule, the gap applies. The Java library has been pulled in unchanged by at least one production-side consumer (med-united/epa4all); audit your own dependency tree before assuming you're not affected.

  • Add a server-side counter in your build.

    Override the server's encryptVauMessage() to maintain an independent 64-bit counter, increment it before each encryption, and use that counter (not the client's value) for the IV's counter portion. Keep the response header's Request-Counter field echoing the client's value, per A_24632.

  • Treat session length as a security parameter.

    Until a server-side counter is in place, rotate VAU sessions well below the 2^32 message boundary. The IV-collision probability passes 1 % around 10,000 same-counter responses; that is your operational margin.

  • Log Request-Counter values seen on the server.

    Even if you do not change the protocol behaviour, a counter that repeats or steps backwards across many responses in a single session is detectable. Capture and review it; the same metric covers misconfigured clients and active abuse.

Severity Reasoning

AV:NReachable from any peer that can complete a VAU handshake with the affected server — in the ePA deployment topology that includes legitimate clients and any network position that can establish such a handshake.AC:HRequires collecting on the order of 2^16 to 2^32 server responses under a fixed Request-Counter and applying the Joux GHASH-key recovery on a collision. Doable, but not a single-shot exploit.PR:NThe VAU handshake itself is the only credential required; A_24623 does not enforce monotonicity on the counter the client supplies.UI:NNo second-party interaction is needed.S:UImpact is bounded to the cryptographic guarantees of the VAU channel between the client and the server's TEE.C:HPlaintext leakage from XOR of colliding ciphertexts; the inner-VAU layer protects the ePA response payloads.I:HGHASH-key recovery enables universal forgery of authenticated server-to-client VAU messages.A:NNo availability impact.

References

How We Can Help

Who We Are

The security researchers behind this advisory.

Dr. Simon Weber Profile

Dr. rer. nat. Simon Weber

Senior Pentester & MedSec Researcher

I evaluate your SaMD with the same industry-defining security insight I contributed to the BAK MV for the revision of the B3S standard.

  • PhD on Hospital Cybersecurity
  • Critical vulnerabilities found in hospital systems
  • Alumni of THB MedSec Research Group
  • gematik Security Hero
Volker Schönefeld Profile

Dipl.-Inf. Volker Schönefeld

Senior Application Security Expert

As a former CTO and developer turned pentester, I work alongside your team to uncover vulnerabilities and find solutions that fit your architecture.

  • 20+ years as CTO, 50M+ app downloads
  • Architected and secured large-scale IoT fleets
  • Certified Web Exploitation Specialist
  • gematik Security Hero

Looking for a Penetration Test?

Machine Spirits specializes in security assessments for medical devices and healthcare IT. From MDR penetration testing to C5 cloud compliance, we help MedTech companies meet regulatory requirements.