
Automatisiertes differentielles Fuzzing-Tool für kryptografische Software, das Implementierungsfehler, Compliance-Verstöße und Seitenkanal-Leaks durch intelligentes, parallelisiertes Testen über mehrere Sprachen und Plattformen hinweg erkennt.
CDF ist ein Werkzeug zum automatischen Testen der Korrektheit und Sicherheit von kryptographischer Software. CDF kann Implementierungsfehler, Compliance-Verstöße, Seitenkanal-Leaks usw. erkennen.
CDF implementiert eine Kombination von Unit-Tests mit "Differential Fuzzing", einem Ansatz, der das Verhalten verschiedener Implementierungen derselben Primitive vergleicht, wenn sie mit Grenzfällen und Werten gefüttert werden, die die Codeabdeckung maximieren.
Im Gegensatz zu allgemeinen Fuzzern und Testsoftware ist CDF:
Intelligent: CDF weiß, welche Art von Algorithmus es testet und passt sich an die getesteten Funktionen an.
Schnell: CDF testet nur das, was getestet werden muss, und parallelisiert seine Tests so weit wie möglich.
Polyvalent: CDF ist nicht auf eine bestimmte Sprache oder API festgelegt, sondern unterstützt beliebige ausführbare Programme oder Skripte.
Portabel: CDF läuft auf jeder Unix- oder Windows-Plattform, da es in Go ohne plattformspezifische Abhängigkeiten geschrieben ist.
Der Zweck von CDF ist es, Entwicklern und Sicherheitsforschern ein effizienteres Testwerkzeug zur Verfügung zu stellen, das effektiver ist als Testvektoren und billiger als manuelle Überprüfung formaler Verifikation.
CDF wurde erstmals auf der Black Hat USA 2017 vorgestellt. Sie können die Folien unserer Präsentation einsehen, die allgemeine Informationen über die Beweggründe und das Design von CDF enthalten.
CDF ist in Go codiert, die aktuelle Version wurde mit Go 1.8 entwickelt. Es hat keine Abhängigkeiten außerhalb der Standardbibliothek von Go.
Wir stellen jedoch Beispielprogramme zum Testen mit CDF zur Verfügung, die in C, Python, C++, Java und Go geschrieben sind und spezifische Kryptobibliotheken zum Ausführen benötigen. Derzeit erforderliche Bibliotheken sind:
make wird die cdf-Binärdatei erstellen.
Eine Reihe von Beispielprogrammen sind unter example verfügbar: make examples-all erstellt alle Beispiele, während make examples-go nur die Go-Beispiele erstellt.
make test führt Unit-Tests (von CDF) aus.
Für den Anfang können Sie sich die Verwendungsinformationen ansehen, indem Sie cdf -h ausführen.
Sie können dann ein Beispiel wie das rsaenc Interface gegen die RSA-OAEP-Go- und CryptoPP-Beispiele testen. Betrachten Sie CryptoPP als Referenz, können Sie die Go-Implementierung testen mit:
cdf rsaenc /examples/oaep_rsa2048_go /examples/oaep_rsa2048_cryptopp
Dieser Befehl führt verschiedene Tests durch, die für das rsaenc-Interface spezifisch sind.
In diesem Beispiel sollte CDF sich über die maximale Größe des öffentlichen Exponenten beschweren, die die Go-Implementierung unterstützt: Wenn wir ihren Code überprüfen, sehen wir, dass der öffentliche Exponent als normale Ganzzahl gespeichert wird, während er in CryptoPP (und den meisten anderen Implementierungen) als große Ganzzahl gespeichert wird. Dies ist jedoch absichtlich so und wird wahrscheinlich nicht geändert.
Parameter sind in config.json definiert. Die meisten Parameter sind selbsterklärend. Möglicherweise möchten Sie andere private Schlüssel für rsaenc und ecdsa setzen (diese Interfaces werden mit festen Schlüsseln getestet, obwohl einige Schlüsselparameter, wie die Exponenten, in einigen Tests geändert werden).
Der Parameter seed ermöglicht es Ihnen, den Seed zu ändern, der in den pseudozufälligen Generatoren von CDF verwendet wird. (Das getestete Programm könnte jedoch einen anderen PRNG verwenden, wie die OAEP-Beispiele.) Der Parameter concurrency legt die Anzahl der gleichzeitigen Goroutinen fest, die CDF beim Forken der Programme erzeugen soll. Beachten Sie, dass es am besten ist, diese Zahl unter der tatsächlichen Anzahl der Kerne zu halten. Der Parameter verboseLog schreibt, wenn auf true gesetzt, alle Ein- und Ausgaben der Programme, auch für die erfolgreichen Tests, in eine Datei log.txt.
Um Ihre Software mit CDF zu testen, müssen Sie ein Programm erstellen, das Eingaben liest und Ausgaben gemäß den CDF-Schnittstellen schreibt und das intern das getestete Programm aufruft. CDF-Schnittstellen sind Abstraktionen einer Kryptofunktionalität, um Black-Box-Tests beliebiger Implementierungen zu ermöglichen.
Wenn Sie beispielsweise das ECDSA-Signaturschema implementiert haben, sollte Ihr Programm der ecdsa Schnittstelle entsprechen und als Eingaben 4 oder 5 Argumente entgegennehmen, um eine Nachricht zu signieren oder eine Signatur zu verifizieren. Diese Argumente sind die öffentliche X-Koordinate, die öffentliche Y-Koordinate, die private große Ganzzahl D und die Nachricht, die Sie signieren möchten. Dann sollte es nur die großen Ganzzahlen R und S jeweils in einer neuen Zeile ausgeben. Oder, um eine Nachricht zu verifizieren, sollte es X, Y, R, S und die Nachricht akzeptieren und dann nur True oder False ausgeben. Die Spezifikationen der Schnittstellen sind unten detailliert.
Unsere Beispiele für Schnittstellenimplementierungen helfen Ihnen, eigene zu erstellen.
Die Fehlerbehandlung ist dem getesteten Programm überlassen, aber um aussagekräftige Fehler in CDF zu erhalten, ist es am besten, bei Fehlern zu beenden, einen Fehlercode zurückzugeben und eine Fehlermeldung auszugeben.
Das Schnittstellenprogramm kann in jeder Sprache geschrieben werden, es muss nur eine ausführbare Datei sein, die einer CDF-Schnittstelle entspricht. Ein Schnittstellenprogramm wird typischerweise in derselben Sprache wie das getestete Programm geschrieben, aber das ist nicht zwingend erforderlich (es kann ein Wrapper in einer anderen Sprache sein, z.B. für Java-Programme).
CDF unterstützt derzeit die folgenden Schnittstellen, wobei Parameter als hexadezimale ASCII-Strings kodiert sind, sofern nicht anders beschrieben:
Die dsa-Schnittstelle testet Implementierungen des Digital Signature Algorithm (DSA). Sie muss die Signatur- und Verifikationsoperationen unterstützen:
| Operation | Eingabe | Ausgabe |
|---|---|---|
| Signatur | p q g y x m | r s |
| Verifikation | p q g y r s m | Wahrheitswert |
Hier sind p, q, g DSA-Parameter, y ein öffentlicher Schlüssel, x ein privater Schlüssel, m eine Nachricht, r und s bilden die Signatur, die durch eine neue Zeile getrennt zurückgegeben werden muss. Der Wahrheitswert, entweder "true" oder "false", wird als String dargestellt.
Die dsa-Schnittstelle unterstützt einen optionalen Test: der -h erlaubt es, den Hashing-Prozess zu umgehen und direkt den zu signierenden Hashwert bereitzustellen. Dies ermöglicht CDF, weitere Tests durchzuführen, wie z.B. die Überprüfung auf Überläufe oder Hash-Trunkierung.
Die ecdsa-Schnittstelle testet Implementierungen des Elliptic Curve Digital Signature Algorithm (ECDSA). Sie muss die Signatur- und Verifikationsoperationen unterstützen:
| Operation | Eingabe | Ausgabe |
|---|---|---|
| Signatur | x y d m | r s |
| Verifikation | x y r s m | Wahrheitswert |
Hier sind x und y die Koordinaten eines öffentlichen ECDSA-Schlüssels, d ein privater Schlüssel, m eine Nachricht, und r und s bilden die Signatur, die durch eine neue Zeile getrennt zurückgegeben werden muss. Der Wahrheitswert, entweder "true" oder "false", wird als String dargestellt.
Das Flag -h dient dem gleichen Zweck wie bei dsa.
Bitte beachten Sie, dass unser aktuelles Design eine feste, im getesteten Programm definierte Kurve annimmt.
Um reproduzierbare Ergebnisse mit diesen Tests zu erhalten und alle Fähigkeiten von CDF zu nutzen, müssen Sie entweder Ihren Zufallsgenerator mit einem festen Seed initialisieren oder eine deterministische ECDSA-Variante verwenden, sonst kann CDF Probleme wie gleiche Tags nicht automatisch erkennen.
Die enc-Schnittstelle testet symmetrische Verschlüsselungs- und Entschlüsselungsoperationen, typischerweise mit einem Blockchiffre (Stromchiffren können mit der prf-Schnittstelle getestet werden). Sie muss Verschlüsselung und Entschlüsselung unterstützen:
| Operation | Eingabe | Ausgabe |
|---|---|---|
| Verschlüsselung | k m | c |
| Entschlüsselung | k c | r |
Hier ist k ein Schlüssel, m eine Nachricht, c ein Geheimtext und r ein wiederhergestellter Klartext.
Die prf-Schnittstelle testet keyed Hashing (pseudozufällige Funktionen, MACs) sowie Stromchiffren:
| Operation | Eingabe | Ausgabe |
|---|---|---|
| Berechnung | k m | h |
Hier ist k ein Schlüssel, m eine Nachricht (oder Nonce bei einem Stromchiffre) und h das Ergebnis der PRF-Berechnung. Unsere Schnittstelle geht von einer festen Schlüsselgröße und variablen Eingabelängen aus. Wenn ein bestimmter Schlüssel angegeben werden soll, ist es Aufgabe des getesteten Programms, die Schlüsseleingabe zu ignorieren, oder die xof-Schnittstelle könnte besser geeignet sein.
Die rsaenc-Schnittstelle testet RSA-Verschlüsselung und -Entschlüsselung, sowohl OAEP (PKCS 2.1) als auch PKCS 1.5:
| Operation | Eingabe | Ausgabe |
|---|---|---|
| Verschlüsselung | n e m | c |
| Entschlüsselung | p q e d c | r |
Hier ist n ein Modulus, e ein öffentlicher Exponent (aus Kompatibilitätsgründen mit bestimmten Bibliotheken wird e auch für die Entschlüsselung benötigt), m eine Nachricht, p und q sind Faktoren von n (so dass p > q, da Bibliotheken dies üblicherweise verlangen), d ein privater Exponent und r ein wiederhergestellter Klartext.
Die xof-Schnittstelle testet Hash-Funktionen, erweiterbare Ausgabefunktionen (XOFs), deterministische Zufallsbitgeneratoren (DRBGs):
| Operation | Eingabe | Ausgabe |
|---|---|---|
| Berechnung | m | h |
Hier ist m die Nachricht und h das Ergebnis.
CDF basiert auf ersten Ideen von JP Aumasson, die erstmals auf der WarCon 2016 vorgestellt wurden, und der größte Teil des Codes wurde von Yolan Romailler geschrieben.
CDF ist urheberrechtlich geschützt (c) 2016-2017 Nagravision SA, alle Rechte vorbehalten.
CDF wird unter GPLv3 veröffentlicht.