-
Notifications
You must be signed in to change notification settings - Fork 111
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #641 from tomato42/advanced-decision-tree
Advanced uses of decision graph
- Loading branch information
Showing
5 changed files
with
206 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ fuzzers | |
GnuTLS | ||
hostname | ||
http | ||
interoperate | ||
kario | ||
khaitovich | ||
OpenSSL | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
.. _Decision graph: | ||
|
||
============== | ||
Decision graph | ||
============== | ||
|
||
While this documentation calls the structure traversed by the runner a | ||
"decision graph," as it can contain loops, it's more precisely described as a | ||
directed graph. Older parts of this documentation and object names refer to | ||
this structure as a "decision tree"—this name | ||
reflects the most common use case in the bundled tests, not the most | ||
complex supported one. | ||
|
||
Node fields | ||
=========== | ||
|
||
A decision graph node has two pointers, to a ``child`` and to a ``next_sibling``. | ||
On initialisation nodes set them to ``None``. | ||
|
||
Child nodes | ||
----------- | ||
|
||
When a node matches received message and processes it without errors, | ||
runner continues execution by switching to the child. | ||
|
||
If ``child`` points to ``None``, runner closes open connections and ends | ||
execution. | ||
|
||
To create loops the ``child`` can point to itself or nodes that point to it, | ||
either directly or transitively. | ||
You need to use this mechanism to allow receiving arbitrary number of messages. | ||
|
||
Sibling nodes | ||
-------------- | ||
|
||
The runner uses nodes pointed to by ``next_sibling`` when received message | ||
doesn't match the current node. When sending messages, runner looks | ||
into ``next_sibling`` when connection got closed. | ||
|
||
You can use this mechanism to either break out of loops or to define | ||
alternatives in execution. | ||
|
||
Advanced decision graph structures | ||
================================== | ||
|
||
As mentioned before, the decision graph allows for non-linear relationship | ||
between nodes. | ||
|
||
Loops | ||
----- | ||
|
||
Test case runner in tlsfuzzer can accept arbitrary number of messages if | ||
the node points to itself as its child. | ||
|
||
For example, to accept zero or more NewSessionTicket messages in | ||
:term:`TLS` 1.3 connection, the script needs to include the following code: | ||
|
||
.. code:: python | ||
cycle = ExpectNewSessionTicket() | ||
node = node.add_child(cycle) | ||
node.add_child(cycle) | ||
Servers that send the NewSessionTicket after Finished and before | ||
any other messages, require the preceding code after | ||
:py:class:`~tlsfuzzer.expect.ExpectFinished`. | ||
That handles OpenSSL-using servers and others that behave similarly. | ||
|
||
.. note:: | ||
:term:`TLS` standard does allow sending NewSessionTicket messages at | ||
arbitrary times after Finished. | ||
|
||
Write the following code to make the runner finish the loop once an | ||
ApplicationData message is received: | ||
|
||
.. code:: python | ||
node.next_sibling = ExpectApplicationData() | ||
node = node.next_sibling | ||
.. tip:: | ||
If you want to accept arbitrary number of NewSessionTicket messages, but | ||
no fewer than a specified amount, add more | ||
:py:class:`~tlsfuzzer.expect.ExpectNewSessionTicket` nodes before the | ||
loop to ensure that server sends them. | ||
|
||
You can find a working example of this code in | ||
`test-tls13-conversation.py | ||
<https://github.com/tomato42/tlsfuzzer/blob/master/scripts/test-tls13-conversation.py>`_. | ||
|
||
Alternatives | ||
------------ | ||
|
||
Servers configured with client certificate based authentication send | ||
CertificateRequest message. | ||
For a script to interoperate with such servers it needs to expect that message. | ||
If a client receives it, it needs to reply with a Certificate message, | ||
even if it doesn't have a certificate (it sends an empty message then). | ||
Since a node doesn't have a limit on the number of parent nodes, script | ||
can specify a branch to handle such connections. | ||
|
||
Start with specifying the exceptional path, save reference to the fork point: | ||
|
||
.. code:: python | ||
node = node.add_child(ExpectCertificateRequest()) | ||
fork = node | ||
node = node.add_child(ExpectServerHelloDone()) | ||
node = node.add_child(CertificateGenerator()) | ||
Then specify the usual path, for servers that don't ask for client | ||
certificates: | ||
|
||
.. code:: python | ||
fork.next_sibling = ExpectServerHelloDone() | ||
In both handshake scenarios the client sends ClientKeyExchange message, | ||
this joins the paths: | ||
|
||
.. code:: python | ||
join = ClientKeyExchangeGenerator() | ||
# join regular path: | ||
fork.next_sibling.add_child(join) | ||
# join CR path: | ||
node = node.add_child(join) | ||
After that, handshake continues as usual with ChangeCipherSpec, Finished, etc. | ||
|
||
.. note:: | ||
When specifying alternative messages, you must take care not to allow | ||
message exchanges forbidden by the standards. | ||
Place all the messages that depend on the branch in the branch to ensure | ||
that (but check if using a command line switch to build different graphs | ||
doesn't lead to simpler test scripts). | ||
|
||
You can find a working example of this code in | ||
`test-fuzzed-plaintext.py | ||
<https://github.com/tomato42/tlsfuzzer/blob/master/scripts/test-fuzzed-plaintext.py>`_. | ||
|
||
Error handling | ||
-------------- | ||
|
||
If you want to allow the server to abort connection while *sending* data, | ||
use the sibling mechanism too. | ||
|
||
To allow the server to close the connection while writing to it, | ||
specify the :py:class:`~tlsfuzzer.expect.ExpectClose` as sibling of the node: | ||
|
||
.. code:: python | ||
node = node.add_child(CertificateVerifyGenerator(private_key)) | ||
node.next_sibling = ExpectClose() | ||
node = node.add_child(ChangeCipherSpecGenerator()) | ||
node.next_sibling = ExpectClose() | ||
node = node.add_child(FinishedGenerator()) | ||
node.next_sibling = ExpectClose() | ||
Use :py:class:`~tlsfuzzer.expect.ExpectAlert` the same way. | ||
|
||
.. note:: | ||
Runner supports only :py:class:`~tlsfuzzer.expect.ExpectAlert` and | ||
:py:class:`~tlsfuzzer.expect.ExpectClose` as siblings of generator nodes. | ||
Since connection close triggers this path, you can read only already | ||
buffered messages. | ||
|
||
You can find a working example of this code in | ||
`test-certificate-verify-malformed-sig.py | ||
<https://github.com/tomato42/tlsfuzzer/blob/master/scripts/test-certificate-verify-malformed-sig.py>`_. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters