Closed colmmacc closed 4 months ago
After some initial discussion about this feature, the current proposal that callers should load certificates and s2n should "do the right thing" and figure out the correct certificate to serve. The API might look like something like this;
struct s2n_bundle *bundle = s2n_new_bundle();
s2n_bundle_add_certificate_chain_and_key(bundle, certificate_chain, key, name, signature_algorithm, signature_digest);
where name is a DNS name, and signature_algorithm is RSA | ECDSA and signature_digest is SHA, SHA256, SHA384 ... etc. A bundle can be associated with a connection. At runtime s2n will figure out which certificate chain to vend and which key to use based on the following search order:
ECDSA + SHA384 + exact_name
ECDSA + SHA256 + exact_name
ECDSA + SHA + exact_name
RSA + SHA384 + exact_name
RSA + SHA256 + exact_name
RSA + SHA + exact_name
ECDSA + SHA384 + wildcard_matches
ECDSA + SHA256 + wildcard_matches
ECDSA + SHA + wildcard_matches
RSA + SHA384 + wildcard_matches
RSA + SHA256 + wildcard_matches
RSA + SHA + wildcard_matches
This kind of search order can be implemented efficiently using a prefix tree or trie, with names stored backwards, e.g.
com.amazon.www | is_wildcard_flag | algorithm | digest
in a later iteration, once X509 parsing is implemented, the names (including subject alternate names) can be automatically pulled from certificates and the function signature changed to avoid needing to specify the name.
That sounds reasonable for most implementations, but thinking of our use case which binds certs to vips and not (necessarily) hostnames, how do we handle that? I'll follow up with you offline
s2n works at the file descriptor level, so an application is free to use different bundles with different connections/file descriptors - but it would have to keep some kind of ip -> bundle lookup table itself.
We could make s2n aware of IP addresses and to use getpeername() and so on, but this does end up muddling the API somewhat. Definitely something to talk/think about.
Based on looking at how certs are used in the real world, I'm leaning towards some kind of hash table rather than a tree for the index. The main reasons are 1. Real world clients only support one level of wildcards, so a given SNI name should require at most two searches (e.g. www.amazon.com and *.amazon.com) to find a certificate, and 2. When running as a client, following certificates to the root is not a "nearest match" kind of thing.
So with that in mind, I'm thinking of implementing an index that looks something like;
www.amazon.com -> [ entry ]
*.amazonaws.com -> [ entry ]
and then the entry will have a simple array which is ordered as follows:
SHA384 + ECDSA -> certificate_key_pair
SHA384 + RSA -> certificate_key_pair
SHA256 + ECDSA -> certificate_key_pair
SHA256 + RSA -> certificate_key_pair
SHA1 + ECDSA -> certificate_key_pair
SHA1 + RSA -> certificate_key_pair
With the top-most option supported by the client used.
I was thinking of picking up this task and have been trying to wrap my head around it. I had a couple of questions/comments since things have changed since the original discussion a few years ago:
1) I think I like the idea of the hash map over the trie. It would be nice to keep this lookup logic simple if possible. A few other TLS libraries that I've read seem to punt the SNI servername->cert matching to a callback provided by the application and only focus on matching ciphersuite/sigalg->cert. The hash map solution seems to balance that theme of library simplicity with having s2n try to do a little more work by "doing the right thing". The client hello callback could continue to provide further flexibility for SNI support for applications if they want to be more complex.
2) We now have X509 parsing, so we should parse names from certificates directly.
3) Could we extend the meaning of s2n_config_add_cert_chain_and_key
and allow applications to call that function multiple times to load up their certs or should this be a new API (along the lines of the bundle idea that was proposed). Things I can think of to choose a bundle-like interface: we don't want to change the current meaning of the API, or we want to swap cert groups/bundles instead of entire s2n_configs in the client hello cb.
4) I think its possible to have two+ certificates with overlapping SANs (say for self-signed-certs). In the event that an application tries to load up multiple certs that overlap, should s2n just serve the first one that was added for that overloaded hostname?
I'm open to any comments, clarifications, or additional requirements.
At present s2n supports one certificate chain per configuration which effectively limits it to one certificate per listening IP address. It should be possible to provide s2n multiple certificates and to have s2n negotiate which certificate should be used based on 1) certificate name matching, including wildcards 2) signature algorithm type and 3) key agreement type (e.g. DSA, ECDSA, RSA ...).