1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/*! # Customising private key usage
By default rustls supports PKCS#8-format[^1] RSA or ECDSA keys, plus PKCS#1-format RSA keys.
However, if your private key resides in a HSM, or in another process, or perhaps
another machine, rustls has some extension points to support this:
The main trait you must implement is [`SigningKey`][signing_key]. The primary method here
is [`choose_scheme()`][choose_scheme] where you are given a set of [`SignatureScheme`s][sig_scheme] the client says
it supports: you must choose one (or return `None` -- this aborts the handshake). Having
done that, you return an implementation of the [`Signer`][signer] trait.
The [`sign()`][sign_method] performs the signature and returns it.
(Unfortunately this is currently designed for keys with low latency access, like in a
PKCS#11 provider, Microsoft CryptoAPI, etc. so is blocking rather than asynchronous.
It's a TODO to make these and other extension points async.)
Once you have these two pieces, configuring a server to use them involves, briefly:
- packaging your [`SigningKey`][signing_key] with the matching certificate chain into a [`Credentials`][credentials]
- making a [`ServerNameResolver`][cert_using_sni] and feeding in your [`Credentials`][credentials] for all SNI hostnames you want to use it for,
- setting that as your `ServerConfig`'s [`cert_resolver`][cert_resolver]
For a complete example of implementing a custom [`SigningKey`][signing_key] and
[`Signer`][signer] see the [`signer` module in the `rustls-cng` crate][rustls-cng-signer].
[signing_key]: crate::crypto::SigningKey
[choose_scheme]: crate::crypto::SigningKey::choose_scheme
[sig_scheme]: crate::crypto::SignatureScheme
[signer]: crate::crypto::Signer
[sign_method]: crate::crypto::Signer::sign
[credentials]: crate::crypto::Credentials
[cert_using_sni]: crate::server::ServerNameResolver
[cert_resolver]: crate::ServerConfig::cert_resolver
[rustls-cng-signer]: https://github.com/rustls/rustls-cng/blob/dev/src/signer.rs
[^1]: For PKCS#8 it does not support password encryption -- there's not a meaningful threat
model addressed by this, and the encryption supported is typically extremely poor.
# Unexpected EOF
TLS has a `close_notify` mechanism to prevent truncation attacks[^2].
According to the TLS RFCs, each party is required to send a `close_notify` message before
closing the write side of the connection. However, some implementations don't send it.
So long as the application layer protocol (for instance HTTP/2) has message length framing
and can reject truncated messages, this is not a security problem.
When an EOF is received from the network using `TlsInputBuffer::read()`, rustls sets an
`has_seen_eof()` flag on that input buffer. If the `MessageHandler::state()` does not have
`peer_has_closed()` yielding `true` and the read side of the connection has seen an EOF, an
`UnexpectedEof` error should be raised unless the application layer protocol knows for sure
that no message is in flight. This could be the case if the connection has been idle or
if application messages are framed with their length.
[^2]: <https://datatracker.ietf.org/doc/html/rfc9846#section-6.1>
# Debugging
If you encounter a bug with Rustls it can be helpful to collect up as much diagnostic
information as possible.
## Collecting logs
If your bug reproduces with one of the [Rustls examples] you can use the
[`RUST_LOG`] environment variable to increase the log verbosity. If you're using
your own application, you may need to configure it with a tracing backend
such as those available from [`tracing-subscriber`].
Consider reproducing your bug with `RUST_LOG=rustls=trace` and sharing the result
in a [GitHub gist].
[Rustls examples]: https://github.com/rustls/rustls/tree/main/examples
[`RUST_LOG`]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html
[`tracing-subscriber`]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/
[GitHub gist]: https://docs.github.com/en/get-started/writing-on-github/editing-and-sharing-content-with-gists/creating-gists
## Taking a packet capture
When logs aren't enough taking a packet capture ("pcap") is another helpful tool.
The details of how to accomplish this vary by operating system/context.
### tcpdump
As one example, on Linux using [`tcpdump`] is often easiest.
If you know the IP address of the remote server your bug demonstrates with you
could take a short packet capture with this command (after replacing
`XX.XX.XX.XX` with the correct IP address):
```bash
sudo tcpdump -i any tcp and dst host XX.XX.XX.XX -C5 -w rustls.pcap
```
The `-i any` captures on any network interface. The `tcp and dst host XX.XX.XX.XX`
portion target the capture to TCP traffic to the specified IP address. The `-C5`
argument limits the capture to at most 5MB. Lastly the `-w` argument writes the
capture to `rustls.pcap`.
Another approach is to use `tcp and port XXXX` instead of `tcp and dst host XX.XX.XX.XX`
to capture all traffic to a specific port instead of a specific host server.
[`tcpdump`]: https://web.archive.org/web/www.redhat.com/en/blog/introduction-using-tcpdump-linux-command-line
### SSLKEYLOGFILE
If the bug you are reporting happens after data is encrypted you may also wish to
share the secret keys required to decrypt the post-handshake traffic.
If you're using one of the [Rustls examples] you can set the `SSLKEYLOGFILE` environment
variable to a path where secrets will be written. E.g. `SSLKEYLOGFILE=rustls.pcap.keys`.
If you're using your own application you may need to customize the Rustls `ClientConfig`
or `ServerConfig`'s `key_log` setting like the example applications do.
With the file from `SSLKEYLOGFILE` it is possible to use [Wireshark] or another tool to
decrypt the post-handshake messages, following [these instructions][curl-sslkeylogfile].
Remember this allows plaintext decryption and should only be done in testing contexts
where no sensitive data (API keys, etc) are being shared.
[Wireshark]: https://www.wireshark.org/download.html
[curl-sslkeylogfile]: https://everything.curl.dev/usingcurl/tls/sslkeylogfile.html
*/