Repository: secdev/scapy

Issue:
TLS Certificate Verify decoding issue
#### Description

When decoding a TLS Certificate Verify packet I have the following results:

```python
from scapy.all import rdpcap
from scapy.layers.tls.record import TLS

packets = rdpcap('certificate-verify.pcapng')

for packet in packets:
    if packet.haslayer(TLS):
        print(packet[TLS].show())
```

```
###[ TLS ]### 
  type      = handshake
  version   = TLS 1.2
  len       = 80    [deciphered_len= 80]
  iv        = b''
  \msg       \
   |###[ Raw ]### 
   |  load      = '\x0f\x00\x00L\x04\x03\x00H0F\x02!\x00\xcf\xf1\xd0:1\xb8\xe4JCU\x00\x8c\xcdg\xf9=g\x84\xa3h;V@\xfd\xd1\\\xf0\xc4f\xfa\x18\xdc\x02!\x00\x82\x1dF\xc1\xd1\xab\x86\xaa\xb9"\x0eA\xf2\xc3Rj\xd7\xf1\xe9\xaf\x9b\xa5?R\n\xca\x15\xfe)\xa9j\x84'
  mac       = b''
  pad       = b''
  padlen    = None

None
```

However, the class **TLSCertificateVerify** should return a decoded signature not raw bytes.

#### Environment

-   Scapy version: `2.4.3.dev259`
-   Python version: `3.7.5`
-   Operating System: `Ubuntu 19.04`

#### How to reproduce

> pcap is attached: [certificate-verify.zip](https://github.com/secdev/scapy/files/4134036/certificate-verify.zip)


#### Related resources

>  It seems to be related to #945






Discussion:

Resolving patch:
diff --git a/scapy/layers/tls/handshake.py b/scapy/layers/tls/handshake.py
--- a/scapy/layers/tls/handshake.py
+++ b/scapy/layers/tls/handshake.py
@@ -1083,7 +1083,10 @@ def build(self, *args, **kargs):
         if sig is None:
             s = self.tls_session
             m = b"".join(s.handshake_messages)
-            if s.tls_version >= 0x0304:
+            tls_version = s.tls_version
+            if tls_version is None:
+                tls_version = s.advertised_tls_version
+            if tls_version >= 0x0304:
                 if s.connection_end == "client":
                     context_string = b"TLS 1.3, client CertificateVerify"
                 elif s.connection_end == "server":
@@ -1100,7 +1103,10 @@ def build(self, *args, **kargs):
     def post_dissection(self, pkt):
         s = self.tls_session
         m = b"".join(s.handshake_messages)
-        if s.tls_version >= 0x0304:
+        tls_version = s.tls_version
+        if tls_version is None:
+            tls_version = s.advertised_tls_version
+        if tls_version >= 0x0304:
             if s.connection_end == "client":
                 context_string = b"TLS 1.3, server CertificateVerify"
             elif s.connection_end == "server":
@@ -1206,7 +1212,10 @@ def build(self, *args, **kargs):
             s = self.tls_session
             handshake_msg = b"".join(s.handshake_messages)
             con_end = s.connection_end
-            if s.tls_version < 0x0304:
+            tls_version = s.tls_version
+            if tls_version is None:
+                tls_version = s.advertised_tls_version
+            if tls_version < 0x0304:
                 ms = s.master_secret
                 self.vdata = s.wcs.prf.compute_verify_data(con_end, "write",
                                                            handshake_msg, ms)
@@ -1218,7 +1227,10 @@ def post_dissection(self, pkt):
         s = self.tls_session
         if not s.frozen:
             handshake_msg = b"".join(s.handshake_messages)
-            if s.tls_version < 0x0304 and s.master_secret is not None:
+            tls_version = s.tls_version
+            if tls_version is None:
+                tls_version = s.advertised_tls_version
+            if tls_version < 0x0304 and s.master_secret is not None:
                 ms = s.master_secret
                 con_end = s.connection_end
                 verify_data = s.rcs.prf.compute_verify_data(con_end, "read",
@@ -1226,7 +1238,7 @@ def post_dissection(self, pkt):
                 if self.vdata != verify_data:
                     pkt_info = pkt.firstlayer().summary()
                     log_runtime.info("TLS: invalid Finished received [%s]", pkt_info)  # noqa: E501
-            elif s.tls_version >= 0x0304:
+            elif tls_version >= 0x0304:
                 con_end = s.connection_end
                 verify_data = s.compute_tls13_verify_data(con_end, "read")
                 if self.vdata != verify_data:
@@ -1236,7 +1248,10 @@ def post_dissection(self, pkt):
     def post_build_tls_session_update(self, msg_str):
         self.tls_session_update(msg_str)
         s = self.tls_session
-        if s.tls_version >= 0x0304:
+        tls_version = s.tls_version
+        if tls_version is None:
+            tls_version = s.advertised_tls_version
+        if tls_version >= 0x0304:
             s.pwcs = writeConnState(ciphersuite=type(s.wcs.ciphersuite),
                                     connection_end=s.connection_end,
                                     tls_version=s.tls_version)
@@ -1250,7 +1265,10 @@ def post_build_tls_session_update(self, msg_str):
     def post_dissection_tls_session_update(self, msg_str):
         self.tls_session_update(msg_str)
         s = self.tls_session
-        if s.tls_version >= 0x0304:
+        tls_version = s.tls_version
+        if tls_version is None:
+            tls_version = s.advertised_tls_version
+        if tls_version >= 0x0304:
             s.prcs = readConnState(ciphersuite=type(s.rcs.ciphersuite),
                                    connection_end=s.connection_end,
                                    tls_version=s.tls_version)
diff --git a/scapy/layers/tls/keyexchange.py b/scapy/layers/tls/keyexchange.py
--- a/scapy/layers/tls/keyexchange.py
+++ b/scapy/layers/tls/keyexchange.py
@@ -880,7 +880,10 @@ def dispatch_hook(cls, _pkt=None, *args, **kargs):
     def pre_dissect(self, m):
         s = self.tls_session
         tbd = m
-        if s.tls_version >= 0x0301:
+        tls_version = s.tls_version
+        if tls_version is None:
+            tls_version = s.advertised_tls_version
+        if tls_version >= 0x0301:
             if len(m) < 2:      # Should not happen
                 return m
             tmp_len = struct.unpack("!H", m[:2])[0]
@@ -928,7 +931,10 @@ def post_build(self, pkt, pay):
             warning("No material to encrypt Pre Master Secret")
 
         tmp_len = b""
-        if s.tls_version >= 0x0301:
+        tls_version = s.tls_version
+        if tls_version is None:
+            tls_version = s.advertised_tls_version
+        if tls_version >= 0x0301:
             tmp_len = struct.pack("!H", len(enc))
         return tmp_len + enc + pay
 


Test patch:
diff --git a/test/tls.uts b/test/tls.uts
--- a/test/tls.uts
+++ b/test/tls.uts
@@ -1307,6 +1307,12 @@ def test_tls_tools():
 
 test_tls_tools()
 
+= Dissect TLSCertificateVerify
+
+t = TLS(b'\x16\x03\x03\x00P\x0f\x00\x00L\x04\x03\x00H0F\x02!\x00\xcf\xf1\xd0:1\xb8\xe4JCU\x00\x8c\xcdg\xf9=g\x84\xa3h;V@\xfd\xd1\\\xf0\xc4f\xfa\x18\xdc\x02!\x00\x82\x1dF\xc1\xd1\xab\x86\xaa\xb9"\x0eA\xf2\xc3Rj\xd7\xf1\xe9\xaf\x9b\xa5?R\n\xca\x15\xfe)\xa9j\x84')
+assert TLSCertificateVerify in t
+assert t[TLSCertificateVerify].sig.sig_len == 72
+
 
 ###############################################################################
 ############################ Automaton behaviour ##############################