Alle Sicherheitshinweise

gematik lib-vau / lib-vau-csharp

AES-GCM-Nonce-Wiederverwendung in der VAU-Server-Verschlüsselung

Der VAU-Server verwendet den vom Client gelieferten Request-Counter als 64-Bit-Counter-Teil seines eigenen AES-GCM-IV, anstatt einen unabhängigen serverseitigen Verschlüsselungscounter zu führen. Die IV-Eindeutigkeit reduziert sich auf die 4 zufälligen Bytes, die der Server voranstellt, sodass der Server-zu-Client-Kanal nach ~77.000 Antworten mit gleichem Counter dem Joux Forbidden Attack offensteht.

Verfasst vonVolker Schönefeld, Simon Weber2026-05-28
SchweregradHochCVSS 7.4CVSS-3.1-VektorAV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:NCWECWE-323 (Reusing a Nonce, Key Pair in Encryption)Produktgematik lib-vau / lib-vau-csharpBetroffene Versionenlib-vau 1.0.0 bis einschließlich 1.0.15 (Java); lib-vau-csharp 1.0.0 bis einschließlich 1.0.9 (C#)Behoben inUpstream nicht behoben. Der Hersteller hat den experimentellen Status der Bibliotheken in der README präzisiert und sieht keinen Anlass für ein eigenes Advisory. Konsumenten, die die Bibliotheken produktiv einsetzen, sind selbst dafür verantwortlich, den fehlenden serverseitigen Counter zu ergänzen (siehe Mitigation).

Beschreibung

Die VAU-Protokollspezifikation (gemSpec_Krypt V2.40.0) verlangt in A_24631, dass der Server seinen eigenen unabhängigen 64-Bit-Verschlüsselungscounter für den Application-Data-Key K2_s2c_app_data führt. Der Counter muss bei 0 beginnen und vor jeder Verschlüsselung inkrementiert werden. A_24632 spezifiziert weiter, dass der AES-GCM-IV des Servers als random(4) || server_encryption_counter(8) konstruiert wird, während das Request-Counter-Feld im Antwort-Header separat den vom Client gespeicherten Wert aus A_24630 zurückspiegelt.

Beide Referenzimplementierungen weichen davon ab. VauServerStateMachine hat kein serverseitiges Counter-Feld und überschreibt die Verschlüsselungsmethode nicht. getRequestCounter() auf der Server-Seite gibt clientRequestCounter zurück (den aus der jüngsten Client-Anfrage gespeicherten Wert), und derselbe Wert wird sowohl für das Request-Counter-Feld im Antwort-Header als auch für den AES-GCM-IV verwendet.

In der Java-Referenzimplementierung (lib-vau) verwendet der Server den Counter des Clients für den IV:

lib-vau VauServerStateMachine.java:181-184

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

View source →

Der Server speichert den vom Client gelieferten Counter ohne Monotonie- oder Replay-Prüfung:

lib-vau VauServerStateMachine.java:191-194

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

View source →

Die IV-Konstruktion verwendet getRequestCounter() für den Counter-Teil:

lib-vau AbstractVauStateMachine.java:112-124

public byte[] encryptVauMessage(byte[] cleartext) {
byte versionByte = 2;
byte puByte = 0;
byte reqByte = getRequestByte();
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);
byte[] ciphertext = encryptWithAesGcm(encryptionVauKey.getAppData(), iv, cleartext, header);

View source →

Die Client-Seite inkrementiert ihren eigenen Counter korrekt vor jeder Verschlüsselung:

lib-vau VauClientStateMachine.java:169-173

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

View source →

Die C#-Referenzimplementierung (lib-vau-csharp) trägt dieselbe Logik und erzeugt denselben Effekt:

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-61

public virtual byte[] EncryptVauMessage(byte[] plaintext)
{
byte versionByte = 2;
byte puByte = (byte)(isPu ? 1 : 0);
byte requestByte = GetRequestByte();
long requestCounter = GetRequestCounter();
byte[] requestCounterBytes = BitConverter.GetBytes(requestCounter).Reverse().ToArray();
byte[][] headerBytes = new byte[][] { new byte[] { versionByte }, new byte[] { puByte }, new byte[] { requestByte }, requestCounterBytes, KeyId };
byte[] header = Arrays.ConcatenateAll(headerBytes);
byte[] random = new byte[4];
new SecureRandom().NextBytes(random);
AesGcm aesGcm = new AesGcm();
aesGcm.initAESForEncryption(random, requestCounter, header, encryptionVauKey);

View source →

Der IV selbst wird in crypto/AesGcm.cs zusammengesetzt; gematiks eigener Kommentar nennt A_24628:

lib-vau-csharp crypto/AesGcm.cs:81-91

private static byte[] initializeIV(byte[] random, long lCounter)
{
// A_24628 -> 32 Bit Random + 64 Bit Verschlüsselungszähler
if (random?.Length != 4)
{
throw new ArgumentNullException(nameof(random), "Invalid random value!");
}
byte[] counter = BitConverter.GetBytes(lCounter).Reverse().ToArray(); // A_24629, A_24631 -> 64 Bit encryption counter
return random.Concat(counter).ToArray(); // A_24628 -> concat random and counter
}

View source →

Da A_24623 in der aktuellen ePA-Ausbaustufe Replay-Schutz und Sequenzordnung nicht erzwingt (ERP=false, ESO=false), kann ein Client jeden Counter-Wert senden, einschließlich eines bereits verwendeten. Die GCM-Nonce-Eindeutigkeit der Server-Antworten hängt dann nur von den 32 Zufallsbits ab, die der Server voranstellt.

Nach dem Geburtstagsparadoxon wächst die Kollisionswahrscheinlichkeit für das 4-Byte-Zufallspräfix schnell, wenn der Counter-Teil konstant gehalten wird:

Geburtstags-Kollisionswahrscheinlichkeit bei konstantem 64-Bit-Counter

Nachrichten P(IV-Kollision)
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 bietet bei Nonce-Wiederverwendung keine Vertraulichkeits- oder Integritätsgarantien mehr. Der Joux Forbidden Attack (2006) erlaubt einem Angreifer, der zwei unter demselben Schlüssel und IV verschlüsselte Ciphertexte beobachtet, aus den beiden Authentifizierungs-Tags den GHASH-Authentifizierungsschlüssel H zu rekonstruieren und durch XOR der beiden Ciphertexte Klartext-Bytes zurückzugewinnen.

Auswirkung

  • Die innere VAU-Schicht schützt Medikationsdaten, Diagnoseberichte, Verordnungshistorien, Dokumenten-Reads und -Writes sowie die Autorisierungstransaktionen, die den Zugriff auf eine Patientenakte steuern.
  • Ein legitimer VAU-Client (oder ein Angreifer im Netzwerk, der einen VAU-Handshake abgeschlossen hat) kann den Counter-Teil des Server-IV konstant halten, indem er einen Request-Counter-Wert wiederverwendet, und so lange Server-Antworten sammeln, bis zwei denselben 12-Byte-IV teilen.
  • Sobald zwei Antworten denselben IV teilen, rekonstruiert der Joux Forbidden Attack den GHASH-Authentifizierungsschlüssel H. Mit H kann der Angreifer das GCM-Authentifizierungs-Tag für Ciphertexte unter jedem IV fälschen, für den eine Kollision beobachtet wurde, und Server-zu-Client-VAU-Nachrichten in einer Sitzung mit fortlaufender Nonce-Wiederverwendung manipulieren.
  • Das XOR der beiden kollidierenden Ciphertexte ergibt das XOR der beiden Klartexte und gibt damit Informationen über die durch die innere VAU-AES-GCM-Schicht geschützten ePA-Antwort-Payloads preis.
  • Die innere VAU-Schicht ist die Verteidigung gegen einen kompromittierten TLS-Terminator außerhalb der TEE. Bricht diese Schicht, verschwindet die Verteidigung.
  • In Kombination mit gematik: VAU-Handshake führt nur 2 von 6 vorgeschriebenen Server-Schlüssel-Prüfungen aus wird der Angriff praktikabel. Ein MITM, der während des Handshakes seine eigenen Schlüssel eingeschleust hat, kann den Client-Request-Counter bei jeder weitergeleiteten Anfrage auf einen festen Wert fixieren, und die Kollisionssammlung wird zur passiven Beobachtung eines Kanals, den der Angreifer bereits kontrolliert.

Abhilfe

Konsumenten, die eine der Bibliotheken produktiv einsetzen, sollten einen unabhängigen serverEncryptionCounter zu VauServerStateMachine (oder dessen C#-Äquivalent) hinzufügen, ihn auf 0 initialisieren und encryptVauMessage() so überschreiben, dass dieser Counter vor der Verschlüsselung inkrementiert wird. Eine reine Überschreibung von getRequestCounter() reicht nicht aus, weil super.encryptVauMessage() den Rückgabewert sowohl für den IV als auch für den Header nutzt; der Server muss encryptVauMessage() vollständig überschreiben und die beiden Counter-Verwendungen trennen. Gemäß A_24632 nutzt der Counter-Teil des IV den eigenen Counter des Servers, und das Request-Counter-Feld im Antwort-Header spiegelt den gespeicherten Client-Wert. Bis das umgesetzt ist, sollte der innere VAU-Server-zu-Client-Kanal so behandelt werden, als biete er Integrität und Vertraulichkeit nur bis zur Größenordnung von 2^32 Nachrichten pro Sitzung.

Checkliste für Betreiber

  • Prüfen, ob Sie lib-vau / lib-vau-csharp ausliefern.

    Wenn Ihr ePA-Client (oder irgendein Produkt, das VAU spricht) lib-vau oder lib-vau-csharp direkt, über einen Fork oder über ein Git-Submodul einbindet, betrifft Sie die Lücke. Die Java-Bibliothek wurde von mindestens einem produktiven Konsumenten unverändert übernommen (med-united/epa4all); prüfen Sie Ihren eigenen Abhängigkeitsbaum, bevor Sie davon ausgehen, nicht betroffen zu sein.

  • Einen serverseitigen Counter in Ihrem Build hinzufügen.

    Überschreiben Sie encryptVauMessage() des Servers so, dass ein unabhängiger 64-Bit-Counter geführt, vor jeder Verschlüsselung inkrementiert und genau dieser Counter (nicht der Wert des Clients) für den Counter-Teil des IV verwendet wird. Lassen Sie das Request-Counter-Feld im Antwort-Header gemäß A_24632 weiterhin den Client-Wert zurückspiegeln.

  • Sitzungslänge als Sicherheitsparameter behandeln.

    Bis ein serverseitiger Counter etabliert ist, rotieren Sie VAU-Sitzungen deutlich unterhalb der 2^32-Nachrichten-Grenze. Die IV-Kollisionswahrscheinlichkeit übersteigt bereits bei rund 10.000 Antworten mit gleichem Counter die 1-Prozent-Schwelle; das ist Ihre operative Marge.

  • Auf dem Server gesehene Request-Counter-Werte protokollieren.

    Auch wenn Sie das Protokollverhalten nicht ändern, ist ein Counter, der sich innerhalb einer Sitzung über viele Antworten hinweg wiederholt oder rückwärts läuft, erkennbar. Erfassen und prüfen Sie ihn; dieselbe Metrik deckt fehlkonfigurierte Clients und aktiven Missbrauch ab.

Bewertung im Detail

AV:NErreichbar von jedem Peer, der einen VAU-Handshake mit dem betroffenen Server abschließen kann. In der ePA-Bereitstellungstopologie umfasst das legitime Clients und jede Netzwerkposition, die einen solchen Handshake aufbauen kann.AC:HErfordert das Sammeln in der Größenordnung von 2^16 bis 2^32 Server-Antworten unter einem fixen Request-Counter und anschließend die Joux'sche GHASH-Schlüsselrekonstruktion auf eine Kollision. Wenn ein nachgelagerter Konsument den Client-Counter nie inkrementiert (wie in fbeta: AES-GCM-Nonce-Wiederverwendung durch eingefrorenen VAU-Request-Counter), wird die Vorbedingung passiv und das systemweite AC sinkt auf L.PR:NDer VAU-Handshake selbst ist die einzige benötigte Berechtigung; A_24623 erzwingt keine Monotonie auf dem vom Client gelieferten Counter.UI:NKeine Interaktion eines zweiten Beteiligten erforderlich.S:UDie Auswirkung ist auf die kryptografischen Garantien des VAU-Kanals zwischen Client und Server-TEE beschränkt.C:HKlartext-Leak durch XOR kollidierender Ciphertexte; die innere VAU-Schicht schützt die ePA-Antwort-Payloads.I:HDie GHASH-Schlüsselrekonstruktion ermöglicht universelle Fälschung authentisierter Server-zu-Client-VAU-Nachrichten.A:NKeine Auswirkung auf die Verfügbarkeit.

Referenzen

So können wir helfen

Wer wir sind

Die Sicherheitsforscher hinter diesem Sicherheitshinweis.

Dr. Simon Weber Profile

Dr. rer. nat. Simon Weber

Senior Pentester & MedSec-Forscher

Ich evaluiere Ihr SaMD mit derselben branchenprägenden Sicherheitsexpertise, die ich dem BAK MV für die Überarbeitung des B3S-Standards beigetragen habe.

  • Promotion über Krankenhaus-Cybersicherheit
  • Kritische Schwachstellen in Krankenhaussystemen gefunden
  • Alumni der THB MedSec-Forschungsgruppe
  • gematik Security Hero
Volker Schönefeld Profile

Dipl.-Inf. Volker Schönefeld

Senior Application Security Expert

Als ehemaliger CTO und Entwickler, der zum Pentester wurde, arbeite ich mit Ihrem Team zusammen, um Schwachstellen aufzudecken und Lösungen zu finden, die zu Ihrer Architektur passen.

  • 20+ Jahre als CTO, 50+ Mio. App-Downloads
  • Architektur und Absicherung großer IoT-Flotten
  • Certified Web Exploitation Specialist
  • gematik Security Hero

Penetrationstest gesucht?

Machine Spirits ist spezialisiert auf Sicherheitsbewertungen für Medizinprodukte und Gesundheits-IT. Von MDR-Penetrationstests bis C5-Cloud-Compliance helfen wir MedTech-Unternehmen, regulatorische Anforderungen zu erfüllen.