This is a framework for building negative tests and fuzzers for TLS 1.3 implementations. The idea is to split the TLS handshake and application data exchange to simple steps which can be easily configured and re-used in various TLS clients and servers.
The framework provides a set of basic steps which can be used in a TLS connection, for example:
- Generating a
- Wrapping a handshake message into a
- Wrapping a handshake message into a
- Key exchange and deriving symmetric keys
- Receiving incoming encrypted data
- Parsing a
- Decrypting a
- and so on
These basic blocks allow to control and test each step in a TLS 1.3 connection.
The framework also provides an engine which runs specified actions. The engine allows adding checks which can be run after a connection finishes. The checks can examine the established connection, and detect potential issues.
- TLS 1.3 protocol defined in RFC 8446
- Client and server modes
- Key exchange with
- Signatures with
- Both client and server authentication
- AES-GCM cipher with 128-bit key
Here is what a simple HTTPS client looks like:
Engine.init() .set("localhost", 433) .set(StructFactory.getDefault()) .set(Negotiator.create(secp256r1)) .run(generatingClientHello() .supportedVersions(TLSv13) .groups(secp256r1) .signatureSchemes(ecdsa_secp256r1_sha256) .keyShareEntries(Negotiator::createKeyShareEntry)) .run(wrappingIntoHandshake() .type(client_hello) .update(Context.Element.first_client_hello)) .run(wrappingIntoTLSPlaintexts() .type(handshake) .version(TLSv12)) .send(OutgoingData::new) .send(OutgoingChangeCipherSpec::new) .until(Condition::serverDone) .receive(IncomingMessages::fromServer) .run(GeneratingFinished::new) .run(wrappingIntoHandshake() .type(finished) .update(Context.Element.client_finished)) .run(WrappingHandshakeDataIntoTLSCiphertext::new) .send(OutgoingData::new) .run(PreparingHttpGetRequest::new) .run(WrappingApplicationDataIntoTLSCiphertext::new) .send(OutgoingData::new) .until(Condition::applicationDataReceived) .receive(IncomingMessages::fromServer) .run() .require(noFatalAlert());
tlsbunny provides several fuzzers for TLS 1.3 structures
Finished and so on.
On the one hand, such a fuzzer is not going to be as fast as, for example, LibFuzzer. On the other hand, the fuzzer can be easily re-used with multiple TLS implementations written in any language (not only C/C++).
Traditionally, fuzzing is used for testing applications written in C/C++ to uncover memory corruption issues which most likely may have security implications. However, fuzzing can also be also used for testing applications written in other languages even if those languages, like Java, prevent using memory directly. See for example AFL-based Java fuzzers and the Java Security Manager.
No matter which programming language is used, a good TLS implementation should properly handle incorrect data and react in an expected way, for example, by throwing a documented exception. An unexpected behavior while processing incorrect data may still have security implications. even it the TLS implementation is written in a memory-safe programming language.
Example: Fuzzing TLSv1.3 server
DeepHandshakeFuzzyClient that fuzzes a TLSv1.3 server.
First, make sure that you use Java 11+. Then, build tlsbunny:
mvn clean install -DskipTests
Next, start a target TLSv1.3 server that you'd like to fuzz. Let's assume that it runs on port
It is better to run the server with AddressSanitizer and other sanitizers. They'll report memory corruptions
that didn't result to a crash.
Then, prepare a config for tlsbunny:
client.certificate.path=certs/client_cert.pem client.key.path=certs/client_key.pkcs8 target.host=localhost target.port=50101 total=10000
The certs directory contains certiticates and keys for testing.
total is a number of iterations for the fuzzer.
Finally, run the fuzzer:
java -cp target/tlsbunny-1.0-SNAPSHOT-all.jar \ com.gypsyengineer.tlsbunny.tls13.client.fuzzer.DeepHandshakeFuzzyClient
Watch how the server handles fuzzed TLS messages.
- tlsfuzzer: SSL and TLS protocol test suite and fuzzer (python)
- TLS-Attacker: TLS-Attacker is a Java-based framework for analyzing TLS libraries. It is developed by the Ruhr University Bochum and the Hackmanit GmbH.