Skip to content
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

Merged
merged 22 commits into from
Feb 23, 2018
Merged

Support ECDSA signatures #671

merged 22 commits into from
Feb 23, 2018

Conversation

alexeblee
Copy link
Contributor

@alexeblee alexeblee commented Jan 7, 2018

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 function s2n_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 to s2n_pkey.

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);
Copy link
Contributor

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.

@alexw91
Copy link
Contributor

alexw91 commented Jan 30, 2018

Can you rebase this onto the tip of master?

Copy link
Contributor

@colmmacc colmmacc left a 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))

Copy link
Contributor

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

@@ -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,
Copy link
Contributor

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?

Copy link
Contributor Author

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.

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);
}
Copy link
Contributor

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?

@@ -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));

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra WS

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;
Copy link
Contributor

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?

Copy link
Contributor Author

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);

Copy link
Contributor

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)
Copy link
Contributor

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?

@@ -30,6 +30,14 @@
#include "crypto/s2n_openssl.h"
#include "crypto/s2n_pkey.h"

int s2n_ecdsa_signature_size(const struct s2n_pkey *pkey)
Copy link
Contributor

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() ?

colmmacc
colmmacc previously approved these changes Feb 15, 2018
Copy link
Contributor

@colmmacc colmmacc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM now!

Copy link
Contributor

@colmmacc colmmacc left a 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.

@colmmacc colmmacc merged commit 4b10257 into aws:master Feb 23, 2018
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);
Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Contributor

@raycoll raycoll Feb 23, 2018

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.

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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants