Skip to content

Commit

Permalink
feat: add basic support for certificate_authorities (aws#4506)
Browse files Browse the repository at this point in the history
  • Loading branch information
lrstewart authored Apr 17, 2024
1 parent 3c4aab8 commit 86b6730
Show file tree
Hide file tree
Showing 10 changed files with 390 additions and 9 deletions.
215 changes: 215 additions & 0 deletions tests/unit/s2n_cert_authorities_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

#include "tls/extensions/s2n_cert_authorities.h"

#include "crypto/s2n_rsa_pss.h"
#include "s2n_test.h"
#include "testlib/s2n_testlib.h"
#include "tls/s2n_tls.h"
#include "utils/s2n_bitmap.h"

int s2n_parse_client_hello(struct s2n_connection *conn);

int main(int argc, char **argv)
{
BEGIN_TEST();

DEFER_CLEANUP(struct s2n_cert_chain_and_key *cert_chain = NULL,
s2n_cert_chain_and_key_ptr_free);
EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&cert_chain,
S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY));

s2n_extension_type_id temp_id = s2n_unsupported_extension;
EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(
s2n_cert_authorities_extension.iana_value, &temp_id));
const s2n_extension_type_id ca_ext_id = temp_id;

/* Test: s2n_certificate_authorities_extension.send */
{
/* Test: writes whatever CA data is available */
{
const uint8_t ca_data[] = "these are my CAs";

DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free);
EXPECT_SUCCESS(s2n_alloc(&config->cert_authorities, sizeof(ca_data)));
EXPECT_MEMCPY_SUCCESS(config->cert_authorities.data, ca_data, sizeof(ca_data));

DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER),
s2n_connection_ptr_free);
EXPECT_SUCCESS(s2n_connection_set_config(conn, config));
conn->actual_protocol_version = S2N_TLS13;

DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free);
EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0));

EXPECT_SUCCESS(s2n_cert_authorities_extension.send(conn, &output));

uint16_t size = 0;
EXPECT_SUCCESS(s2n_stuffer_read_uint16(&output, &size));
EXPECT_EQUAL(size, sizeof(ca_data));
EXPECT_EQUAL(size, s2n_stuffer_data_available(&output));

uint8_t *data = s2n_stuffer_raw_read(&output, size);
EXPECT_NOT_NULL(data);
EXPECT_BYTEARRAY_EQUAL(data, ca_data, sizeof(ca_data));
};
};

/* Test: s2n_certificate_authorities_extension.should_send */
{
/* Test: do not send for TLS1.2 */
{
DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free);
EXPECT_SUCCESS(s2n_alloc(&config->cert_authorities, 10));

DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER),
s2n_connection_ptr_free);
EXPECT_SUCCESS(s2n_connection_set_config(conn, config));

DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free);
EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0));

conn->actual_protocol_version = S2N_TLS12;
EXPECT_SUCCESS(s2n_extension_send(&s2n_cert_authorities_extension,
conn, &output));
EXPECT_EQUAL(s2n_stuffer_data_available(&output), 0);

conn->actual_protocol_version = S2N_TLS13;
EXPECT_SUCCESS(s2n_extension_send(&s2n_cert_authorities_extension,
conn, &output));
EXPECT_NOT_EQUAL(s2n_stuffer_data_available(&output), 0);
};

/* Test: do not send if no CA data set */
{
DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free);

DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER),
s2n_connection_ptr_free);
EXPECT_SUCCESS(s2n_connection_set_config(conn, config));

EXPECT_FALSE(s2n_cert_authorities_extension.should_send(conn));

EXPECT_SUCCESS(s2n_alloc(&config->cert_authorities, 10));
EXPECT_TRUE(s2n_cert_authorities_extension.should_send(conn));

EXPECT_SUCCESS(s2n_free(&config->cert_authorities));
EXPECT_FALSE(s2n_cert_authorities_extension.should_send(conn));
};
};

/* Test: ClientHello extension ignored
*
*= https://tools.ietf.org/rfc/rfc8446#section-4.2.4
*= type=test
*# The client MAY send the "certificate_authorities" extension in the
*# ClientHello message.
*/
if (s2n_is_tls13_fully_supported()) {
DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free);
EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config));
EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, cert_chain));
EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13"));
EXPECT_SUCCESS(s2n_alloc(&config->cert_authorities, 10));

DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT),
s2n_connection_ptr_free);
EXPECT_SUCCESS(s2n_connection_set_config(client, config));
EXPECT_SUCCESS(s2n_client_hello_send(client));
EXPECT_SUCCESS(s2n_parse_client_hello(client));
struct s2n_client_hello *client_hello = &client->client_hello;

DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER),
s2n_connection_ptr_free);
EXPECT_SUCCESS(s2n_connection_set_config(server, config));
struct s2n_stuffer *input = &server->handshake.io;

/* Copy ClientHello, except extensions */
size_t size_without_extensions = client_hello->raw_message.size
- client_hello->extensions.raw.size
- sizeof(uint16_t) /* Extensions size */;
EXPECT_SUCCESS(s2n_stuffer_write_bytes(input,
client_hello->raw_message.data, size_without_extensions));

/* Save space for new extensions size */
struct s2n_stuffer_reservation extensions_size = { 0 };
EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(input, &extensions_size));

/* Write the certificate_authorities extension.
* The client isn't allowed to write it, so use the server.
*/
server->actual_protocol_version = S2N_TLS13;
EXPECT_SUCCESS(s2n_extension_send(&s2n_cert_authorities_extension,
server, input));

/* Write the rest of the extensions */
EXPECT_SUCCESS(s2n_stuffer_write(input, &client_hello->extensions.raw));

/* Update the extensions size */
EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&extensions_size));

/* Server should be able to successfully receive the modified ClientHello */
EXPECT_SUCCESS(s2n_client_hello_recv(server));
EXPECT_TRUE(server->client_hello.parsed);
EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS13);

s2n_parsed_extension *extension = &server->client_hello.extensions.parsed_extensions[ca_ext_id];
EXPECT_TRUE(extension->extension.size > 0);
EXPECT_FALSE(extension->processed);
EXPECT_EQUAL(extension->extension_type, s2n_cert_authorities_extension.iana_value);
};

/* Self-talk test: CertificateRequest extension parsed
*
*= https://tools.ietf.org/rfc/rfc8446#section-4.2.4
*= type=test
*# The server MAY send it in the CertificateRequest message.
**/
if (s2n_is_tls13_fully_supported()) {
DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free);
EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config));
EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, cert_chain));
EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13"));
EXPECT_SUCCESS(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_OPTIONAL));
EXPECT_SUCCESS(s2n_alloc(&config->cert_authorities, 10));

DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT),
s2n_connection_ptr_free);
EXPECT_SUCCESS(s2n_connection_set_config(client, config));

DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER),
s2n_connection_ptr_free);
EXPECT_SUCCESS(s2n_connection_set_config(server, config));

DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close);
EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair));
EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair));

EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client));
EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS13);
EXPECT_TRUE(IS_CLIENT_AUTH_HANDSHAKE(server));

/* Server sent extension */
EXPECT_TRUE(S2N_CBIT_TEST(server->extension_requests_sent, ca_ext_id));
EXPECT_FALSE(S2N_CBIT_TEST(server->extension_requests_received, ca_ext_id));

/* Client received extension */
EXPECT_FALSE(S2N_CBIT_TEST(client->extension_requests_sent, ca_ext_id));
EXPECT_TRUE(S2N_CBIT_TEST(client->extension_requests_received, ca_ext_id));
};

END_TEST();
}
78 changes: 75 additions & 3 deletions tests/unit/s2n_server_cert_request_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ int main(int argc, char **argv)
EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER));
EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config));

s2n_cert_req_send(server_conn);
EXPECT_SUCCESS(s2n_cert_req_send(server_conn));
struct s2n_stuffer *in = &server_conn->handshake.io;
uint8_t cert_types_len = 0;

Expand Down Expand Up @@ -88,7 +88,7 @@ int main(int argc, char **argv)
EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config));
EXPECT_SUCCESS(s2n_config_enable_cert_req_dss_legacy_compat(server_config));

s2n_cert_req_send(server_conn);
EXPECT_SUCCESS(s2n_cert_req_send(server_conn));
struct s2n_stuffer *in = &server_conn->handshake.io;
uint8_t cert_types_len = 0;

Expand All @@ -105,6 +105,78 @@ int main(int argc, char **argv)
EXPECT_SUCCESS(s2n_connection_free(server_conn));
};

/* Test: certificate_authorities supported */
{
/* Test: no cert_authorities sent by default */
{
DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(conn);
conn->actual_protocol_version = S2N_TLS12;

DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(),
s2n_config_ptr_free);
EXPECT_NOT_NULL(config);
EXPECT_SUCCESS(s2n_connection_set_config(conn, config));

EXPECT_SUCCESS(s2n_cert_req_send(conn));
struct s2n_stuffer *output = &conn->handshake.io;

uint8_t cert_types_len = 0;
EXPECT_SUCCESS(s2n_stuffer_read_uint8(output, &cert_types_len));
EXPECT_TRUE(cert_types_len > 0);
EXPECT_SUCCESS(s2n_stuffer_skip_read(output, cert_types_len));

uint16_t sig_algs_len = 0;
EXPECT_SUCCESS(s2n_stuffer_read_uint16(output, &sig_algs_len));
EXPECT_TRUE(sig_algs_len > 0);
EXPECT_SUCCESS(s2n_stuffer_skip_read(output, sig_algs_len));

uint16_t cert_authorities_len = 0;
EXPECT_SUCCESS(s2n_stuffer_read_uint16(output, &cert_authorities_len));
EXPECT_EQUAL(cert_authorities_len, 0);

EXPECT_EQUAL(s2n_stuffer_data_available(output), 0);
};

/* Test: cert_authorities sent if configured */
{
DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(conn);

DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(),
s2n_config_ptr_free);
EXPECT_NOT_NULL(config);
EXPECT_SUCCESS(s2n_connection_set_config(conn, config));

/* If we use TLS1.1 instead of TLS1.2, we don't need to worry about
* skipping the signature algorithms.
*/
conn->actual_protocol_version = S2N_TLS11;

const uint8_t ca_data[] = "these are my CAs";
EXPECT_SUCCESS(s2n_alloc(&config->cert_authorities, sizeof(ca_data)));
EXPECT_MEMCPY_SUCCESS(config->cert_authorities.data, ca_data, sizeof(ca_data));

EXPECT_SUCCESS(s2n_cert_req_send(conn));
struct s2n_stuffer *output = &conn->handshake.io;

uint8_t cert_types_len = 0;
EXPECT_SUCCESS(s2n_stuffer_read_uint8(output, &cert_types_len));
EXPECT_SUCCESS(s2n_stuffer_skip_read(output, cert_types_len));

uint16_t cert_authorities_len = 0;
EXPECT_SUCCESS(s2n_stuffer_read_uint16(output, &cert_authorities_len));
EXPECT_EQUAL(cert_authorities_len, sizeof(ca_data));

uint8_t *cert_authorities_data = s2n_stuffer_raw_read(output, cert_authorities_len);
EXPECT_NOT_NULL(cert_authorities_data);
EXPECT_BYTEARRAY_EQUAL(cert_authorities_data, ca_data, sizeof(ca_data));

EXPECT_EQUAL(s2n_stuffer_data_available(output), 0);
};
};

END_TEST();
return 0;
}
54 changes: 54 additions & 0 deletions tls/extensions/s2n_cert_authorities.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
#include "tls/extensions/s2n_cert_authorities.h"

#include "utils/s2n_safety.h"

int s2n_cert_authorities_send(struct s2n_connection *conn, struct s2n_stuffer *out)
{
POSIX_ENSURE_REF(conn);
POSIX_ENSURE_REF(conn->config);
POSIX_ENSURE_EQ(conn->mode, S2N_SERVER);
struct s2n_blob *cert_authorities = &conn->config->cert_authorities;
POSIX_GUARD(s2n_stuffer_write_uint16(out, cert_authorities->size));
POSIX_GUARD(s2n_stuffer_write(out, cert_authorities));
return S2N_SUCCESS;
}

static bool s2n_cert_authorities_should_send(struct s2n_connection *conn)
{
return conn && conn->config && conn->config->cert_authorities.size > 0;
}

const s2n_extension_type s2n_cert_authorities_extension = {
.iana_value = TLS_EXTENSION_CERT_AUTHORITIES,
.minimum_version = S2N_TLS13,
.is_response = false,
.send = s2n_cert_authorities_send,
.should_send = s2n_cert_authorities_should_send,
/* s2n-tls supports sending the extension, but does not support parsing it.
* If received, the extension is ignored.
*
*= https://tools.ietf.org/rfc/rfc8446#section-4.2.4
*= type=exception
*= reason=Extension ignored when received - No customer use case
*# The "certificate_authorities" extension is used to indicate the
*# certificate authorities (CAs) which an endpoint supports and which
*# SHOULD be used by the receiving endpoint to guide certificate
*# selection.
*/
.recv = s2n_extension_recv_noop,
.if_missing = s2n_extension_noop_if_missing,
};
24 changes: 24 additions & 0 deletions tls/extensions/s2n_cert_authorities.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

#pragma once

#include "stuffer/s2n_stuffer.h"
#include "tls/extensions/s2n_extension_type.h"
#include "tls/s2n_connection.h"

extern const s2n_extension_type s2n_cert_authorities_extension;

int s2n_cert_authorities_send(struct s2n_connection *conn, struct s2n_stuffer *out);
Loading

0 comments on commit 86b6730

Please sign in to comment.