-
Notifications
You must be signed in to change notification settings - Fork 694
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support ECDSA signatures #671
Conversation
int s2n_config_get_cert_type(struct s2n_config *config, s2n_cert_type *cert_type) | ||
{ | ||
notnull_check(config); | ||
notnull_check(config->cert_and_key_pairs); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had considered adding NONE
types for the optional client auth change instead of just null checking. This change might make that a cleaner choice now for things like the s2n_set_signature_and_hash_algorithm
stuff.
Can you rebase this onto the tip of master? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like how this change is laid out and am excited about getting ECDSA support! some initial comments ...
@@ -50,7 +55,7 @@ def try_gnutls_handshake(endpoint, port, priority_str, mfl_extension_test, enter | |||
|
|||
if mfl_extension_test: | |||
gnutls_cmd.append("--recordsize=" + str(mfl_extension_test)) | |||
|
|||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like some errant whitespace
tls/s2n_cipher_suites.c
Outdated
@@ -148,6 +149,7 @@ struct s2n_cipher_suite s2n_rsa_with_rc4_128_md5 = /* 0x00,0x04 */ { | |||
.name = "RC4-MD5", | |||
.iana_value = { TLS_RSA_WITH_RC4_128_MD5 }, | |||
.key_exchange_alg = &s2n_rsa, | |||
.signature_alg = S2N_SIGNATURE_RSA, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this a bit counter-intuitive? The TLS_RSA_WITH_* cipher suites don't actually use RSA signatures, they use RSA encryption for key exchange, and then HMAC. The DHE/EDH ones do use RSA signatures though. Would these cipher suites break if this were left at null or NONE or something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The relationship between cipher suites, certificates, and signature algorithms was the most difficult part for me to wrap my head around. I think in TLS 1.1, there was a requirement that the algorithm used to sign a certificate must be the same as the key type. I had interpreted that to mean that TLS_RSA_WITH_* cipher suites required certificates that had an RSA signature and that is sort of where this came from.
I tried to rethink what this field was modeling and where it was needed in the handshake. It is used in two places: to check that a received cert is compatible with the cipher suite, and to set a default signature/hash algorithm pair if ECDSA signatures will be used (also related to the cipher suite). I thought of renaming this to represent an authentication method rather than a signature algorithm. For some cipher suites, a signature would be used for authentication, but for the TLS_RSA_WITH_* the authentication would be implicit in the RSA key exchange. I'm very open to thoughts on this approach.
tls/s2n_client_extensions.c
Outdated
GUARD(s2n_stuffer_read_uint8(in, &signature_algorithm)); | ||
|
||
if (signature_algorithm != TLS_SIGNATURE_ALGORITHM_RSA && signature_algorithm != TLS_SIGNATURE_ALGORITHM_ECDSA) { | ||
S2N_ERROR(S2N_ERR_INVALID_SIGNATURE_ALGORITHM); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this going to be too brittle? Especially in client mode ... what happens when folks go add some new signature type in the future?
tls/s2n_handshake_io.c
Outdated
@@ -488,7 +487,7 @@ static int read_full_handshake_message(struct s2n_connection *conn, uint8_t * me | |||
|
|||
/* We don't have the whole message, so we'll need to go again */ | |||
GUARD(s2n_stuffer_reread(&conn->handshake.io)); | |||
|
|||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extra WS
tls/s2n_signature_algorithms.c
Outdated
s2n_signature_algorithm sig_alg_chosen = S2N_SIGNATURE_RSA; | ||
if (cipher_suite_sig_alg == S2N_SIGNATURE_ECDSA) { | ||
sig_alg_chosen = S2N_SIGNATURE_ECDSA; | ||
hash_alg_chosen = S2N_HASH_SHA1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to use SHA1 as our default? why not SHA256?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My interpretation of the RFCs were that this was the required default: https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
If the client does not send the signature_algorithms extension, the
server MUST do the following:
- If the negotiated key exchange algorithm is one of (RSA, DHE_RSA,
DH_RSA, RSA_PSK, ECDH_RSA, ECDHE_RSA), behave as if client had
sent the value {sha1,rsa}.
- If the negotiated key exchange algorithm is one of (DHE_DSS,
DH_DSS), behave as if the client had sent the value {sha1,dsa}.
- If the negotiated key exchange algorithm is one of (ECDH_ECDSA,
ECDHE_ECDSA), behave as if the client had sent value {sha1,ecdsa}.
Note: this is a change from TLS 1.1 where there are no explicit
rules, but as a practical matter one can assume that the peer
supports MD5 and SHA-1.
utils/s2n_map.c
Outdated
@@ -139,7 +139,7 @@ int s2n_map_add(struct s2n_map *map, struct s2n_blob *key, struct s2n_blob *valu | |||
} | |||
|
|||
uint32_t slot = s2n_map_slot(map, key); | |||
|
|||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extra WS
utils/s2n_map.c
Outdated
@@ -160,6 +160,20 @@ int s2n_map_add(struct s2n_map *map, struct s2n_blob *key, struct s2n_blob *valu | |||
return 0; | |||
} | |||
|
|||
int s2n_map_offer(struct s2n_map *map, struct s2n_blob *key, struct s2n_blob *value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we just have an s2n_map_put() that does add-or-replace insertion? The offer() way isn't something I've ever seen before, so it took me a bit to understand what it's doing and why. It favors leaving the former value if it's there, but any reason to favor that?
crypto/s2n_ecdsa.c
Outdated
@@ -30,6 +30,14 @@ | |||
#include "crypto/s2n_openssl.h" | |||
#include "crypto/s2n_pkey.h" | |||
|
|||
int s2n_ecdsa_signature_size(const struct s2n_pkey *pkey) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be worth making this even more specific and seeing s2n_ecdsa_der_signature_size() ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM now!
1961de7
to
c351a79
Compare
- Write out signature length after signing. - Use ECDSA cert when negotiating ECDSA ciphersuites in integration tests. - Initialize certificate keys after parsing based on cert type.
…d signature algorithms, and function names
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for these insanely awesome additions! Time to merge.
tls/s2n_signature_algorithms.c
Outdated
int s2n_set_signature_hash_pair_from_preference_list(struct s2n_connection *conn, uint8_t sig_hash_algs[TLS_SIGNATURE_ALGORITHM_COUNT][TLS_HASH_ALGORITHM_COUNT], | ||
static int s2n_sig_hash_algs_pairs_set(struct s2n_sig_hash_alg_pairs *sig_hash_algs, uint8_t sig_alg, uint8_t hash_alg) | ||
{ | ||
S2N_ERROR_IF(hash_alg >= TLS_HASH_ALGORITHM_COUNT, S2N_ERR_HASH_INVALID_ALGORITHM); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the hash_alg
value is coming from peer's signature_algorithm
extension, shouldn't we skip an unknown value rather than throwing an error? The peer could be sending a code point that s2n has not yet implemented. If there is no overlap between the peer's set and ours we will error later during cipher suite/cert selection.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will follow up with a patch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, this is okay because we aren't checking the return value of s2n_sig_hash_alg_pairs_set
in s2n_recv_supported_signature_algorithms
. I'll validate with a little unit test.
tls/s2n_signature_algorithms.c
Outdated
static int s2n_sig_hash_algs_pairs_set(struct s2n_sig_hash_alg_pairs *sig_hash_algs, uint8_t sig_alg, uint8_t hash_alg) | ||
{ | ||
S2N_ERROR_IF(hash_alg >= TLS_HASH_ALGORITHM_COUNT, S2N_ERR_HASH_INVALID_ALGORITHM); | ||
S2N_ERROR_IF(sig_alg >= TLS_SIGNATURE_ALGORITHM_COUNT, S2N_ERR_INVALID_SIGNATURE_ALGORITHM); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto here
Use the new pkey interface to support ECDSA signatures. I've also added a single cipher suite for testing.
There was some refactoring done to the signature_algorithms extension parsing.
s2n_map
is used to store all signature/hash algorithm pairs to be used later during server certificate selection, cipher suite selection, etc. I was looking for a set-like data structure so I added a functions2n_map_offer
to ignore attempts to add duplicate keys. I'm open to other ways to store the sig/hash pairs.For now, this just sends the only configured certificate and expects the cipher suite list to be compatible with the certificate type. The next step will be to add a function that picks the correct certificate to send based on the cipher suite and signature algorithms during client hello parsing. This might require some fancier certificate parsing during
s2n_config_add_cert_chain_and_key
and adding a type field tos2n_pkey
.