tls-attacker / TLS-Attacker

TLS-Attacker is a Java-based framework for analyzing TLS libraries. It can be used to manually test TLS clients and servers or as as a software library for more advanced tools.
Apache License 2.0
792 stars 137 forks source link

How to receive only one protocol message? #178

Open GSoJC234 opened 5 days ago

GSoJC234 commented 5 days ago

Hello,

I'm looking to implement a custom action that reads only any single protocol message. I came across your comments on issue #177 and I believe the action for receiving just one protocol message might involve working with LayerConfiguration. (Is that correct?)

However, I'm having trouble deeply understanding how LayerConfiguration and LayerStack work. Could you provide an example or sample code that demonstrates how to receive just any single protocol message?

Thank you for providing such a great TLS library!

Thank you!

ic0ns commented 5 days ago

For this you can take a look at the TightReceiveAction:

https://github.com/tls-attacker/TLS-Attacker/blob/main/TLS-Core/src/main/java/de/rub/nds/tlsattacker/core/workflow/action/TightReceiveAction.java

A layer configuration is your encoding on how TLS-Attacker should behave on a given layer, i.e. which containers it should receive or which containers it should send. To limit the receiving of only the specified messages, you can use a TightReceiveLayerConfiguration, which is just a regular receiveConfiguration that sets "isProcessTrailingContainers" to false to tell the layer that once all messages are there no further messages should be processed.

GSoJC234 commented 5 days ago

Thanks for your quick response!!

From what I understand, TightReceiveAction takes expected messages as arguments and stops receiving once those expected messages are received.

However, in my case, the expected message isn't known in advance. It can be a ClientHello, ServerHello, ServerKeyExchange, or any other protocol message.

Is there still a way to receive just a single protocol message using TightReceiveAction in this situation or is there any alternative way?

ic0ns commented 5 days ago

Ok this we did not have before. To realize this you need to write a custom layer configuration. I would simply extend the TightReceiveLayerConfiguration class and overwrite the function

executedAsPlanned(List<Container> list)

The function basically tells the layer when you are successful in the execution. In your case, if you overwrite it with:

return list.size() == 1;

the layer should then stop receiving once the first message is received.

GSoJC234 commented 5 days ago

Great! Thank you for quick and detailed comments!

GSoJC234 commented 2 days ago

Hello,

I defined the custom layer TightReceiveOneLayerConfiguration, along with a ReceiveOneAction to receive only one valid handshake messages.

However, I encountered an issue with the receiveData method in LayerStack.java, which is responsible for receiving data

...
for (int i = 0; i < getLayerList().size(); i++) {
    ProtocolLayer layer = getLayerList().get(i);
    layer.clear();
    layer.setLayerConfiguration(layerConfigurationList.get(i));
}
...

The receiveData method always clear each layer including the Message Layer, Record Layer and TCP Layer, which are default layers used in TLS handshake process.

While it is okay to receive TCP packets corresponding a single TLS handshake message, the problem arises when I try to execute a sequence of actions like the following, in cases where the server sends multiple messages at once:

trace.addTlsAction(new ReceiveOneAction());  // Receive ServerHello
...
trace.addTlsAction(new ReceiveOneAction());  // Receive Certificate
...
trace.addTlsAction(new ReceiveOneAction());  // Receive ServerKeyExchange
...
trace.addTlsAction(new ReceiveOneAction());  // Receive ServerHelloDone
...

After the first action executed (receiving the ServerHello message), the remaining TCP packet data (stored in currentInputStream) gets discarded due to the layer.clear(). As a result, the subsequent ReceiveOneAction actions fail to receive the remaining messages.

To resolve this, I modified the receiveData method to avoid discarding currentInputStream when there is available data:

for (int i = 0; i < getLayerList().size(); i++) {
        ProtocolLayer layer = getLayerList().get(i);
        try {
            if (layer.currentInputStream == null || layer.currentInputStream.available() == 0) {
                layer.clear();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        layer.setLayerConfiguration(layerConfigurationList.get(i));
    }

Maybe TLS-Attacker's design intends to prevent parsing or processing only one valid message from a stream of received packets. I believe allowing this could be useful.

What do you think about enabling the option to receive only one valid message from a batch of received packets?

ic0ns commented 1 day ago

Thats a good analysis. I think the mentioned behavior also kind of prevents the tight receive action from behaving as intended. I think your patch is not enough as the second action would now receive both more containers as expected since the produced data containers did not get reset - but the general idea seems right

GSoJC234 commented 1 day ago

I agree with your thoughts and suggestions! Thank you for taking the time to review my issue.