From e3de58e6d8953c4e1db006a4cf1e608148dcc21e Mon Sep 17 00:00:00 2001 From: Lindsay Stewart Date: Wed, 10 Apr 2024 23:23:10 -0700 Subject: [PATCH 1/3] feat: certificate_authorities for TLS1.3 servers --- api/s2n.h | 22 ++ error/s2n_errno.c | 1 + error/s2n_errno.h | 1 + tests/testlib/s2n_testlib.h | 2 + tests/unit/s2n_cert_authorities_test.c | 328 ++++++++++++++++++++++ tls/extensions/s2n_cert_authorities.c | 108 +++++++ tls/extensions/s2n_cert_authorities.h | 28 ++ tls/extensions/s2n_extension_list.h | 2 +- tls/extensions/s2n_extension_type.h | 1 + tls/extensions/s2n_extension_type_lists.c | 12 + tls/s2n_config.c | 1 + tls/s2n_config.h | 3 + 12 files changed, 508 insertions(+), 1 deletion(-) create mode 100644 tests/unit/s2n_cert_authorities_test.c create mode 100644 tls/extensions/s2n_cert_authorities.c create mode 100644 tls/extensions/s2n_cert_authorities.h diff --git a/api/s2n.h b/api/s2n.h index 6ef207e090d..845820730ef 100644 --- a/api/s2n.h +++ b/api/s2n.h @@ -3763,6 +3763,28 @@ S2N_API int s2n_connection_serialize(struct s2n_connection *conn, uint8_t *buffe */ S2N_API int s2n_connection_deserialize(struct s2n_connection *conn, uint8_t *buffer, uint32_t buffer_length); +/* Load all acceptable certificate authorities from the currently configured trust store. + * + * The loaded certificate authorities will be advertised during the handshake. + * This can help your peer select a certificate if they have multiple certificate + * chains available. + * + * For now, s2n-tls only supports advertising certificate authorities to support + * client auth, so only servers will send the list of certificate authorities. + * + * To avoid configuration mistakes, certificate authorities cannot be loaded from + * a trust store that includes the default system certificates. That means that + * s2n_config_new_minimal should be used instead of s2n_config_new to create the + * config object. Additionally, the trust store cannot contain more than 10 + * certificate authorities. + * + * @param config A pointer to the s2n_config object. + * @param count The number of certificate authorities loaded from the trust store. + * Can be used for logging or to sanity check the trust store configuration. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure. + */ +S2N_API int s2n_config_set_cert_authorities_from_trust_store(struct s2n_config *config, size_t *count); + #ifdef __cplusplus } #endif diff --git a/error/s2n_errno.c b/error/s2n_errno.c index 2f64bd5f2fe..cea807ba489 100644 --- a/error/s2n_errno.c +++ b/error/s2n_errno.c @@ -311,6 +311,7 @@ static const char *no_such_error = "Internal s2n error"; ERR_ENTRY(S2N_ERR_MISSING_CERT_REQUEST, "Client requires mutual authentication, but server did not request a cert") \ ERR_ENTRY(S2N_ERR_MISSING_CLIENT_CERT, "Server requires client certificate") \ ERR_ENTRY(S2N_ERR_INVALID_SERIALIZED_CONNECTION, "Serialized connection is invalid"); \ + ERR_ENTRY(S2N_ERR_TOO_MANY_CAS, "Too many certificate authorities in trust store"); \ /* clang-format on */ #define ERR_STR_CASE(ERR, str) \ diff --git a/error/s2n_errno.h b/error/s2n_errno.h index ced98ecc5f5..838f7d10388 100644 --- a/error/s2n_errno.h +++ b/error/s2n_errno.h @@ -328,6 +328,7 @@ typedef enum { S2N_ERR_KTLS_KEY_LIMIT, S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT, S2N_ERR_INVALID_SERIALIZED_CONNECTION, + S2N_ERR_TOO_MANY_CAS, S2N_ERR_T_USAGE_END, } s2n_error; diff --git a/tests/testlib/s2n_testlib.h b/tests/testlib/s2n_testlib.h index 160a439a2fe..78754fed2c1 100644 --- a/tests/testlib/s2n_testlib.h +++ b/tests/testlib/s2n_testlib.h @@ -192,6 +192,8 @@ S2N_RESULT s2n_connection_set_test_master_secret(struct s2n_connection *conn, co #define S2N_MIXED_CHAIN_KEY "../pems/mixed_chains/ecdsa/server-key.pem" #define S2N_MIXED_CHAIN_CA "../pems/mixed_chains/ecdsa/ca-cert.pem" +#define S2N_TEST_TRUST_STORE "../pems/trust-store/ca-bundle.crt" + #define S2N_DEFAULT_TEST_CERT_CHAIN S2N_RSA_2048_PKCS1_CERT_CHAIN #define S2N_DEFAULT_TEST_PRIVATE_KEY S2N_RSA_2048_PKCS1_KEY diff --git a/tests/unit/s2n_cert_authorities_test.c b/tests/unit/s2n_cert_authorities_test.c new file mode 100644 index 00000000000..0aa76267458 --- /dev/null +++ b/tests/unit/s2n_cert_authorities_test.c @@ -0,0 +1,328 @@ +/* + * 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 "utils/s2n_bitmap.h" + +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_config_set_cert_authorities_from_trust_store */ + { + /* Test: Safety */ + { + size_t count = 0; + EXPECT_FAILURE_WITH_ERRNO( + s2n_config_set_cert_authorities_from_trust_store(NULL, &count), + S2N_ERR_NULL); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_FAILURE_WITH_ERRNO( + s2n_config_set_cert_authorities_from_trust_store(config, NULL), + S2N_ERR_NULL); + }; + + /* Test: not allowed with system trust store */ + { + /* s2n_config_new configures the default trust store */ + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + size_t count = 0; + EXPECT_FAILURE_WITH_ERRNO( + s2n_config_set_cert_authorities_from_trust_store(config, &count), + S2N_ERR_INVALID_STATE); + EXPECT_EQUAL(config->cert_authorities.size, 0); + }; + + /* Test: empty trust store */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + size_t count = 0; + EXPECT_SUCCESS(s2n_config_set_cert_authorities_from_trust_store(config, &count)); + EXPECT_EQUAL(count, 0); + EXPECT_EQUAL(config->cert_authorities.size, 0); + }; + + /* Test: too many CAs in trust store */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + /* This is just a copy of the default trust store from an Amazon Linux instance */ + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_TEST_TRUST_STORE, NULL)); + + size_t count = 0; + EXPECT_FAILURE_WITH_ERRNO( + s2n_config_set_cert_authorities_from_trust_store(config, &count), + S2N_ERR_TOO_MANY_CAS); + EXPECT_EQUAL(count, S2N_CERT_AUTHORITIES_MAX); + EXPECT_EQUAL(config->cert_authorities.size, 0); + }; + }; + + /* Test: s2n_certificate_authorities_extension.send */ + { + /* Test: writes whatever CA authorities 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_FALSE(s2n_cert_authorities_extension.should_send(conn)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_TRUE(s2n_cert_authorities_extension.should_send(conn)); + }; + + /* 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)); + + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + + 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)); + }; + }; + + /* Known value test: compare our extension to openssl s_server */ + if (s2n_is_rsa_pss_certs_supported()) { + /* clang-format off */ + const struct { + const char *cert_name; + uint8_t expected_count; + uint8_t expected_bytes_size; + uint8_t expected_bytes[1000]; + } test_cases[] = { + { + .cert_name = S2N_RSA_PSS_2048_SHA256_LEAF_CERT, + .expected_count = 1, + .expected_bytes_size = 32, + .expected_bytes = { + 0x00, 0x2f, 0x00, 0x1c, 0x00, 0x1a, 0x00, 0x18, + 0x30, 0x16, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x0c, 0x0b, 0x65, 0x78, 0x61, + 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d + }, + }, + { + .cert_name = S2N_ECDSA_P512_CERT_CHAIN, + .expected_count = 1, + .expected_bytes_size = 107, + .expected_bytes = { + 0x00, 0x2f, 0x00, 0x67, 0x00, 0x65, 0x00, 0x63, + 0x30, 0x61, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, + 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, + 0x0c, 0x02, 0x57, 0x41, 0x31, 0x10, 0x30, 0x0e, + 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x07, 0x53, + 0x65, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x31, 0x0f, + 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, + 0x06, 0x41, 0x6d, 0x61, 0x7a, 0x6f, 0x6e, 0x31, + 0x0c, 0x30, 0x0a, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x0c, 0x03, 0x73, 0x32, 0x6e, 0x31, 0x14, 0x30, + 0x12, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0b, + 0x73, 0x32, 0x6e, 0x54, 0x65, 0x73, 0x74, 0x43, + 0x65, 0x72, 0x74 + }, + }, + { + .cert_name = S2N_RSA_2048_SHA256_URI_SANS_CERT, + .expected_count = 2, + .expected_bytes_size = 192, + .expected_bytes = { + 0x00, 0x2f, 0x00, 0xbc, 0x00, 0xba, 0x00, 0x53, + 0x30, 0x51, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, + 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, + 0x0c, 0x02, 0x57, 0x41, 0x31, 0x0f, 0x30, 0x0d, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x41, + 0x6d, 0x61, 0x7a, 0x6f, 0x6e, 0x31, 0x0c, 0x30, + 0x0a, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x03, + 0x73, 0x32, 0x6e, 0x31, 0x16, 0x30, 0x14, 0x06, + 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0d, 0x73, 0x32, + 0x6e, 0x54, 0x65, 0x73, 0x74, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x00, 0x63, 0x30, 0x61, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x02, 0x57, + 0x41, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, + 0x04, 0x07, 0x0c, 0x07, 0x53, 0x65, 0x61, 0x74, + 0x74, 0x6c, 0x65, 0x31, 0x0f, 0x30, 0x0d, 0x06, + 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x41, 0x6d, + 0x61, 0x7a, 0x6f, 0x6e, 0x31, 0x0c, 0x30, 0x0a, + 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x03, 0x73, + 0x32, 0x6e, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x0c, 0x0b, 0x73, 0x32, 0x6e, + 0x54, 0x65, 0x73, 0x74, 0x52, 0x6f, 0x6f, 0x74 + }, + }, + { + .cert_name = S2N_RSA_2048_PKCS1_CERT_CHAIN, + .expected_count = 3, + .expected_bytes_size = 94, + .expected_bytes = { + 0x00, 0x2f, 0x00, 0x5a, 0x00, 0x58, 0x00, 0x1a, + 0x30, 0x18, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x0c, 0x0d, 0x73, 0x32, 0x6e, + 0x54, 0x65, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x00, 0x20, 0x30, 0x1e, 0x31, 0x1c, + 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, + 0x13, 0x73, 0x32, 0x6e, 0x54, 0x65, 0x73, 0x74, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65, 0x64, + 0x69, 0x61, 0x74, 0x65, 0x00, 0x18, 0x30, 0x16, + 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x0c, 0x0b, 0x73, 0x32, 0x6e, 0x54, 0x65, + 0x73, 0x74, 0x52, 0x6f, 0x6f, 0x74 + }, + }, + }; + /* clang-format on */ + + for (size_t i = 0; i < s2n_array_len(test_cases); i++) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, + test_cases[i].cert_name, NULL)); + + size_t count = 0; + EXPECT_SUCCESS(s2n_config_set_cert_authorities_from_trust_store(config, &count)); + EXPECT_EQUAL(count, test_cases[i].expected_count); + + 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_extension_send(&s2n_cert_authorities_extension, + conn, &output)); + + size_t output_size = s2n_stuffer_data_available(&output); + EXPECT_EQUAL(test_cases[i].expected_bytes_size, output_size); + + uint8_t *output_bytes = s2n_stuffer_raw_read(&output, output_size); + EXPECT_NOT_NULL(output_bytes); + EXPECT_BYTEARRAY_EQUAL(test_cases[i].expected_bytes, output_bytes, output_size); + } + } + + /* Self-talk test */ + 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_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + size_t count = 0; + EXPECT_SUCCESS(s2n_config_set_cert_authorities_from_trust_store(config, &count)); + EXPECT_EQUAL(count, 3); + + 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(); +} diff --git a/tls/extensions/s2n_cert_authorities.c b/tls/extensions/s2n_cert_authorities.c new file mode 100644 index 00000000000..4759e0d8883 --- /dev/null +++ b/tls/extensions/s2n_cert_authorities.c @@ -0,0 +1,108 @@ +/* + * 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 + +#include "utils/s2n_safety.h" + +S2N_RESULT s2n_cert_authorities_set_from_trust_store(struct s2n_config *config, size_t *count) +{ + RESULT_ENSURE_REF(config); + RESULT_ENSURE_REF(count); + *count = 0; + + if (!config->trust_store.trust_store) { + return S2N_RESULT_OK; + } + + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&output, 256)); + + STACK_OF(X509_OBJECT) *objects = X509_STORE_get0_objects(config->trust_store.trust_store); + RESULT_ENSURE(objects, S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); + + int objects_count = sk_X509_OBJECT_num(objects); + RESULT_ENSURE(objects_count >= 0, S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); + + for (int i = 0; i < objects_count; i++) { + X509 *cert = X509_OBJECT_get0_X509(sk_X509_OBJECT_value(objects, i)); + if (cert == NULL) { + /* X509_OBJECTs can also be CRLs, resulting in NULL here. Skip. */ + continue; + } + + X509_NAME *name = X509_get_subject_name(cert); + RESULT_ENSURE(name, S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); + + const uint8_t *name_bytes = NULL; + size_t name_size = 0; + RESULT_GUARD_OSSL(X509_NAME_get0_der(name, &name_bytes, &name_size), + S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); + + RESULT_ENSURE(*count < S2N_CERT_AUTHORITIES_MAX, S2N_ERR_TOO_MANY_CAS); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint16(&output, name_size)); + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(&output, name_bytes, name_size)); + (*count)++; + } + + RESULT_GUARD_POSIX(s2n_stuffer_extract_blob(&output, &config->cert_authorities)); + return S2N_RESULT_OK; +} + +int s2n_config_set_cert_authorities_from_trust_store(struct s2n_config *config, size_t *count) +{ + POSIX_ENSURE_REF(config); + POSIX_ENSURE(!config->trust_store.loaded_system_certs, S2N_ERR_INVALID_STATE); + POSIX_GUARD_RESULT(s2n_cert_authorities_set_from_trust_store(config, count)); + return S2N_SUCCESS; +} + +static 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 s2n_extension_send_if_tls13_connection(conn) && conn && conn->config + && conn->config->cert_authorities.size > 0; +} + +const s2n_extension_type s2n_cert_authorities_extension = { + .iana_value = TLS_EXTENSION_CERT_AUTHORITIES, + .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#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, +}; diff --git a/tls/extensions/s2n_cert_authorities.h b/tls/extensions/s2n_cert_authorities.h new file mode 100644 index 00000000000..8aa39185aad --- /dev/null +++ b/tls/extensions/s2n_cert_authorities.h @@ -0,0 +1,28 @@ +/* + * 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" + +/* This limit isn't based on any practical requirements, and could be raised. + * It is just intended to avoid situations where customers inadvertently configure + * a large trust store or enable this feature in an unexpected environment. + */ +#define S2N_CERT_AUTHORITIES_MAX 10 + +extern const s2n_extension_type s2n_cert_authorities_extension; diff --git a/tls/extensions/s2n_extension_list.h b/tls/extensions/s2n_extension_list.h index 411c7e4d7cc..ddc1a6be430 100644 --- a/tls/extensions/s2n_extension_list.h +++ b/tls/extensions/s2n_extension_list.h @@ -21,8 +21,8 @@ #define S2N_PARSED_EXTENSIONS_COUNT S2N_SUPPORTED_EXTENSIONS_COUNT typedef struct { - uint16_t extension_type; struct s2n_blob extension; + uint16_t extension_type; uint16_t wire_index; unsigned processed : 1; } s2n_parsed_extension; diff --git a/tls/extensions/s2n_extension_type.h b/tls/extensions/s2n_extension_type.h index a4fd08db501..b1c1ef9f940 100644 --- a/tls/extensions/s2n_extension_type.h +++ b/tls/extensions/s2n_extension_type.h @@ -68,6 +68,7 @@ static const uint16_t s2n_supported_extensions[] = { TLS_EXTENSION_EARLY_DATA, TLS_EXTENSION_EMS, TLS_EXTENSION_NPN, + TLS_EXTENSION_CERT_AUTHORITIES, }; typedef char s2n_extension_bitfield[S2N_SUPPORTED_EXTENSIONS_BITFIELD_LEN]; diff --git a/tls/extensions/s2n_extension_type_lists.c b/tls/extensions/s2n_extension_type_lists.c index 3e2a607fd35..8e389ce58a2 100644 --- a/tls/extensions/s2n_extension_type_lists.c +++ b/tls/extensions/s2n_extension_type_lists.c @@ -16,6 +16,7 @@ #include "tls/extensions/s2n_extension_type_lists.h" #include "api/s2n.h" +#include "tls/extensions/s2n_cert_authorities.h" #include "tls/extensions/s2n_cert_status.h" #include "tls/extensions/s2n_cert_status_response.h" #include "tls/extensions/s2n_client_alpn.h" @@ -131,6 +132,17 @@ static const s2n_extension_type *const encrypted_extensions[] = { static const s2n_extension_type *const cert_req_extensions[] = { &s2n_server_signature_algorithms_extension, &s2n_server_cert_status_request_extension, + /** + *= https://tools.ietf.org/rfc/rfc8446#4.2.4 + *= type=exception + *= reason=Currently only supported for servers -- no client use case + *# The client MAY send the "certificate_authorities" extension in the + *# ClientHello message. + * + *= https://tools.ietf.org/rfc/rfc8446#4.2.4 + *# The server MAY send it in the CertificateRequest message. + */ + &s2n_cert_authorities_extension, }; static const s2n_extension_type *const certificate_extensions[] = { diff --git a/tls/s2n_config.c b/tls/s2n_config.c index f3674c23310..b595a52c3e3 100644 --- a/tls/s2n_config.c +++ b/tls/s2n_config.c @@ -124,6 +124,7 @@ static int s2n_config_cleanup(struct s2n_config *config) POSIX_GUARD(s2n_config_free_cert_chain_and_key(config)); POSIX_GUARD(s2n_config_free_dhparams(config)); POSIX_GUARD(s2n_free(&config->application_protocols)); + POSIX_GUARD(s2n_free(&config->cert_authorities)); POSIX_GUARD_RESULT(s2n_map_free(config->domain_name_to_cert_map)); POSIX_CHECKED_MEMSET(config, 0, sizeof(struct s2n_config)); diff --git a/tls/s2n_config.h b/tls/s2n_config.h index b2dc2c0ea17..714c8a2328b 100644 --- a/tls/s2n_config.h +++ b/tls/s2n_config.h @@ -225,6 +225,9 @@ struct s2n_config { * the new feature to their entire fleet.) */ s2n_serialization_version serialized_connection_version; + + /* List of certificate authorities supported */ + struct s2n_blob cert_authorities; }; S2N_CLEANUP_RESULT s2n_config_ptr_free(struct s2n_config **config); From 8dd2a2bd1f076b39d1f8ea3a6b8d3a9cb3008029 Mon Sep 17 00:00:00 2001 From: Lindsay Stewart Date: Mon, 15 Apr 2024 16:57:33 -0700 Subject: [PATCH 2/3] Exclude openssl-1.0.2 from support --- .../S2N_LIBCRYPTO_SUPPORTS_X509_STORE_LIST.c | 31 ++++++++++++++++++ ...N_LIBCRYPTO_SUPPORTS_X509_STORE_LIST.flags | 0 tests/unit/s2n_cert_authorities_test.c | 32 +++++++++++++++++-- tls/extensions/s2n_cert_authorities.c | 16 +++++++++- tls/extensions/s2n_cert_authorities.h | 2 ++ 5 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 tests/features/S2N_LIBCRYPTO_SUPPORTS_X509_STORE_LIST.c create mode 100644 tests/features/S2N_LIBCRYPTO_SUPPORTS_X509_STORE_LIST.flags diff --git a/tests/features/S2N_LIBCRYPTO_SUPPORTS_X509_STORE_LIST.c b/tests/features/S2N_LIBCRYPTO_SUPPORTS_X509_STORE_LIST.c new file mode 100644 index 00000000000..bd9d370f34a --- /dev/null +++ b/tests/features/S2N_LIBCRYPTO_SUPPORTS_X509_STORE_LIST.c @@ -0,0 +1,31 @@ +/* + * 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 + +int main() { + /* X509_STORE_get0_objects appears to be the earliest method available that + * can retrieve all certificates from an X509_STORE. + * + * X509_STORE_get_by_subject and X509_STORE_get1_certs are available even + * earlier (Openssl-1.0.2), but both require known X509_NAMEs. + */ + STACK_OF(X509_OBJECT) *objects = X509_STORE_get0_objects(NULL); + X509 *cert = X509_OBJECT_get0_X509(NULL); + /* We could use i2d_X509_NAME instead if necessary, but X509_NAME_get0_der + * should be available where X509_STORE_get0_objects is */ + X509_NAME_get0_der(NULL, NULL, NULL); + return 0; +} diff --git a/tests/features/S2N_LIBCRYPTO_SUPPORTS_X509_STORE_LIST.flags b/tests/features/S2N_LIBCRYPTO_SUPPORTS_X509_STORE_LIST.flags new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/unit/s2n_cert_authorities_test.c b/tests/unit/s2n_cert_authorities_test.c index 0aa76267458..f51e51bee09 100644 --- a/tests/unit/s2n_cert_authorities_test.c +++ b/tests/unit/s2n_cert_authorities_test.c @@ -34,6 +34,11 @@ int main(int argc, char **argv) s2n_cert_authorities_extension.iana_value, &temp_id)); const s2n_extension_type_id ca_ext_id = temp_id; + /* Test: awslc should always support loading from the trust store */ + if (s2n_libcrypto_is_awslc()) { + EXPECT_TRUE(s2n_cert_authorities_supported_from_trust_store()); + } + /* Test: s2n_config_set_cert_authorities_from_trust_store */ { /* Test: Safety */ @@ -50,6 +55,27 @@ int main(int argc, char **argv) S2N_ERR_NULL); }; + /* Test: fails if not supported */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, + S2N_ECDSA_P512_CERT_CHAIN, NULL)); + + size_t count = 0; + if (s2n_cert_authorities_supported_from_trust_store()) { + EXPECT_SUCCESS(s2n_config_set_cert_authorities_from_trust_store(config, &count)); + EXPECT_EQUAL(count, 1); + EXPECT_NOT_EQUAL(config->cert_authorities.size, 0); + } else { + EXPECT_FAILURE_WITH_ERRNO( + s2n_config_set_cert_authorities_from_trust_store(config, &count), + S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); + EXPECT_EQUAL(config->cert_authorities.size, 0); + } + }; + /* Test: not allowed with system trust store */ { /* s2n_config_new configures the default trust store */ @@ -74,7 +100,7 @@ int main(int argc, char **argv) }; /* Test: too many CAs in trust store */ - { + if (s2n_cert_authorities_supported_from_trust_store()) { DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); EXPECT_NOT_NULL(config); /* This is just a copy of the default trust store from an Amazon Linux instance */ @@ -163,7 +189,7 @@ int main(int argc, char **argv) }; /* Known value test: compare our extension to openssl s_server */ - if (s2n_is_rsa_pss_certs_supported()) { + if (s2n_is_rsa_pss_certs_supported() && s2n_cert_authorities_supported_from_trust_store()) { /* clang-format off */ const struct { const char *cert_name; @@ -287,7 +313,7 @@ int main(int argc, char **argv) } /* Self-talk test */ - if (s2n_is_tls13_fully_supported()) { + if (s2n_is_tls13_fully_supported() && s2n_cert_authorities_supported_from_trust_store()) { 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)); diff --git a/tls/extensions/s2n_cert_authorities.c b/tls/extensions/s2n_cert_authorities.c index 4759e0d8883..8dd8415d7da 100644 --- a/tls/extensions/s2n_cert_authorities.c +++ b/tls/extensions/s2n_cert_authorities.c @@ -18,6 +18,15 @@ #include "utils/s2n_safety.h" +bool s2n_cert_authorities_supported_from_trust_store() +{ +#if S2N_LIBCRYPTO_SUPPORTS_X509_STORE_LIST + return true; +#else + return false; +#endif +} + S2N_RESULT s2n_cert_authorities_set_from_trust_store(struct s2n_config *config, size_t *count) { RESULT_ENSURE_REF(config); @@ -28,6 +37,7 @@ S2N_RESULT s2n_cert_authorities_set_from_trust_store(struct s2n_config *config, return S2N_RESULT_OK; } +#if S2N_LIBCRYPTO_SUPPORTS_X509_STORE_LIST DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&output, 256)); @@ -60,6 +70,9 @@ S2N_RESULT s2n_cert_authorities_set_from_trust_store(struct s2n_config *config, RESULT_GUARD_POSIX(s2n_stuffer_extract_blob(&output, &config->cert_authorities)); return S2N_RESULT_OK; +#else + RESULT_BAIL(S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); +#endif } int s2n_config_set_cert_authorities_from_trust_store(struct s2n_config *config, size_t *count) @@ -83,7 +96,8 @@ static int s2n_cert_authorities_send(struct s2n_connection *conn, struct s2n_stu static bool s2n_cert_authorities_should_send(struct s2n_connection *conn) { - return s2n_extension_send_if_tls13_connection(conn) && conn && conn->config + return s2n_extension_send_if_tls13_connection(conn) + && conn && conn->config && conn->config->cert_authorities.size > 0; } diff --git a/tls/extensions/s2n_cert_authorities.h b/tls/extensions/s2n_cert_authorities.h index 8aa39185aad..ff1d28dbe3b 100644 --- a/tls/extensions/s2n_cert_authorities.h +++ b/tls/extensions/s2n_cert_authorities.h @@ -26,3 +26,5 @@ #define S2N_CERT_AUTHORITIES_MAX 10 extern const s2n_extension_type s2n_cert_authorities_extension; + +bool s2n_cert_authorities_supported_from_trust_store(); From 596ddf5f4bf1b6fd65d25866fbb6bf233d9352da Mon Sep 17 00:00:00 2001 From: Lindsay Stewart Date: Mon, 15 Apr 2024 23:39:03 -0700 Subject: [PATCH 3/3] Support TLS1.2 --- tests/unit/s2n_server_cert_request_test.c | 78 ++++++++++++++++++++++- tls/extensions/s2n_cert_authorities.c | 2 +- tls/extensions/s2n_cert_authorities.h | 1 + tls/s2n_server_cert_request.c | 9 ++- 4 files changed, 81 insertions(+), 9 deletions(-) diff --git a/tests/unit/s2n_server_cert_request_test.c b/tests/unit/s2n_server_cert_request_test.c index 828eddfceb5..fbdd6392c92 100644 --- a/tests/unit/s2n_server_cert_request_test.c +++ b/tests/unit/s2n_server_cert_request_test.c @@ -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; @@ -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; @@ -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; } diff --git a/tls/extensions/s2n_cert_authorities.c b/tls/extensions/s2n_cert_authorities.c index 8dd8415d7da..92dfe625201 100644 --- a/tls/extensions/s2n_cert_authorities.c +++ b/tls/extensions/s2n_cert_authorities.c @@ -83,7 +83,7 @@ int s2n_config_set_cert_authorities_from_trust_store(struct s2n_config *config, return S2N_SUCCESS; } -static int s2n_cert_authorities_send(struct s2n_connection *conn, struct s2n_stuffer *out) +int s2n_cert_authorities_send(struct s2n_connection *conn, struct s2n_stuffer *out) { POSIX_ENSURE_REF(conn); POSIX_ENSURE_REF(conn->config); diff --git a/tls/extensions/s2n_cert_authorities.h b/tls/extensions/s2n_cert_authorities.h index ff1d28dbe3b..95b36122b22 100644 --- a/tls/extensions/s2n_cert_authorities.h +++ b/tls/extensions/s2n_cert_authorities.h @@ -28,3 +28,4 @@ extern const s2n_extension_type s2n_cert_authorities_extension; bool s2n_cert_authorities_supported_from_trust_store(); +int s2n_cert_authorities_send(struct s2n_connection *conn, struct s2n_stuffer *out); diff --git a/tls/s2n_server_cert_request.c b/tls/s2n_server_cert_request.c index 79929a32cda..d2173960c20 100644 --- a/tls/s2n_server_cert_request.c +++ b/tls/s2n_server_cert_request.c @@ -17,6 +17,7 @@ #include "crypto/s2n_certificate.h" #include "error/s2n_errno.h" #include "extensions/s2n_extension_list.h" +#include "extensions/s2n_cert_authorities.h" #include "stuffer/s2n_stuffer.h" #include "tls/s2n_cipher_suites.h" #include "tls/s2n_config.h" @@ -174,10 +175,8 @@ int s2n_cert_req_send(struct s2n_connection *conn) POSIX_GUARD_RESULT(s2n_signature_algorithms_supported_list_send(conn, out)); } - /* RFC 5246 7.4.4 - If the certificate_authorities list is empty, then the - * client MAY send any certificate of the appropriate ClientCertificateType */ - uint16_t acceptable_cert_authorities_len = 0; - POSIX_GUARD(s2n_stuffer_write_uint16(out, acceptable_cert_authorities_len)); + /* In TLS1.2, certificate_authorities is part of the message instead of an extension */ + POSIX_GUARD(s2n_cert_authorities_send(conn, out)); - return 0; + return S2N_SUCCESS; }