diff --git a/stuffer/s2n_stuffer_pem.c b/stuffer/s2n_stuffer_pem.c index cf8ae838353..cf5d2ecb205 100644 --- a/stuffer/s2n_stuffer_pem.c +++ b/stuffer/s2n_stuffer_pem.c @@ -20,8 +20,9 @@ #include "stuffer/s2n_stuffer.h" #include "utils/s2n_safety.h" -#define S2N_PEM_DELIMTER_CHAR '-' -#define S2N_PEM_DELIMITER_MIN_COUNT 1 +#define S2N_PEM_DELIMITER_CHAR '-' +#define S2N_PEM_DELIMITER_TOKEN "--" +#define S2N_PEM_DELIMITER_MIN_COUNT 2 #define S2N_PEM_DELIMITER_MAX_COUNT 64 #define S2N_PEM_BEGIN_TOKEN "BEGIN " #define S2N_PEM_END_TOKEN "END " @@ -36,11 +37,15 @@ static int s2n_stuffer_pem_read_encapsulation_line(struct s2n_stuffer *pem, const char *encap_marker, const char *keyword) { - /* Skip any number of Chars until a "-" is reached */ - POSIX_GUARD(s2n_stuffer_skip_to_char(pem, S2N_PEM_DELIMTER_CHAR)); + /* Skip any number of Chars until a "--" is reached. + * We use "--" instead of "-" to account for dashes that appear in comments. + * We do not accept comments that contain "--". + */ + POSIX_GUARD(s2n_stuffer_skip_read_until(pem, S2N_PEM_DELIMITER_TOKEN)); + POSIX_GUARD(s2n_stuffer_rewind_read(pem, strlen(S2N_PEM_DELIMITER_TOKEN))); - /* Ensure between 1 and 64 '-' chars at start of line */ - POSIX_GUARD(s2n_stuffer_skip_expected_char(pem, S2N_PEM_DELIMTER_CHAR, S2N_PEM_DELIMITER_MIN_COUNT, + /* Ensure between 2 and 64 '-' chars at start of line. */ + POSIX_GUARD(s2n_stuffer_skip_expected_char(pem, S2N_PEM_DELIMITER_CHAR, S2N_PEM_DELIMITER_MIN_COUNT, S2N_PEM_DELIMITER_MAX_COUNT, NULL)); /* Ensure next string in stuffer is "BEGIN " or "END " */ @@ -49,18 +54,18 @@ static int s2n_stuffer_pem_read_encapsulation_line(struct s2n_stuffer *pem, cons /* Ensure next string is stuffer is the keyword (Eg "CERTIFICATE", "PRIVATE KEY", etc) */ POSIX_GUARD(s2n_stuffer_read_expected_str(pem, keyword)); - /* Ensure between 1 and 64 '-' chars at end of line */ - POSIX_GUARD(s2n_stuffer_skip_expected_char(pem, S2N_PEM_DELIMTER_CHAR, S2N_PEM_DELIMITER_MIN_COUNT, + /* Ensure between 2 and 64 '-' chars at end of line */ + POSIX_GUARD(s2n_stuffer_skip_expected_char(pem, S2N_PEM_DELIMITER_CHAR, S2N_PEM_DELIMITER_MIN_COUNT, S2N_PEM_DELIMITER_MAX_COUNT, NULL)); /* Check for missing newline between dashes case: "-----END CERTIFICATE----------BEGIN CERTIFICATE-----" */ if (strncmp(encap_marker, S2N_PEM_END_TOKEN, strlen(S2N_PEM_END_TOKEN)) == 0 && s2n_stuffer_peek_check_for_str(pem, S2N_PEM_BEGIN_TOKEN) == S2N_SUCCESS) { - /* Rewind stuffer by 1 byte before BEGIN, so that next read will find the dash before the BEGIN */ - POSIX_GUARD(s2n_stuffer_rewind_read(pem, 1)); + /* Rewind stuffer by 2 bytes before BEGIN, so that next read will find the dashes before the BEGIN */ + POSIX_GUARD(s2n_stuffer_rewind_read(pem, S2N_PEM_DELIMITER_MIN_COUNT)); } - /* Skip newlines and other whitepsace that may be after the dashes */ + /* Skip newlines and other whitespace that may be after the dashes */ return s2n_stuffer_skip_whitespace(pem, NULL); } diff --git a/tests/cbmc/proofs/s2n_stuffer_certificate_from_pem/Makefile b/tests/cbmc/proofs/s2n_stuffer_certificate_from_pem/Makefile index c19cf955ea8..06e5363c302 100644 --- a/tests/cbmc/proofs/s2n_stuffer_certificate_from_pem/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_certificate_from_pem/Makefile @@ -49,6 +49,6 @@ REMOVE_FUNCTION_BODY += s2n_add_overflow UNWINDSET += strlen.0:5 # size of S2N_PEM_PKCS1_RSA_PRIVATE_KEY UNWINDSET += strncmp.0:5 # size of S2N_PEM_END_TOKEN UNWINDSET += __CPROVER_file_local_s2n_stuffer_pem_c_s2n_stuffer_pem_read_contents.11:$(call addone,$(MAX_BLOB_SIZE)) -UNWINDSET += s2n_stuffer_skip_to_char.3:$(call addone,$(MAX_BLOB_SIZE)) +UNWINDSET += s2n_stuffer_skip_read_until.10:$(call addone,$(MAX_BLOB_SIZE)) include ../Makefile.common diff --git a/tests/cbmc/proofs/s2n_stuffer_dhparams_from_pem/Makefile b/tests/cbmc/proofs/s2n_stuffer_dhparams_from_pem/Makefile index 5b65d2e8c5e..6e6394579cc 100644 --- a/tests/cbmc/proofs/s2n_stuffer_dhparams_from_pem/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_dhparams_from_pem/Makefile @@ -49,6 +49,6 @@ REMOVE_FUNCTION_BODY += s2n_add_overflow UNWINDSET += strlen.0:5 # size of S2N_PEM_PKCS1_RSA_PRIVATE_KEY UNWINDSET += strncmp.0:5 # size of S2N_PEM_END_TOKEN UNWINDSET += __CPROVER_file_local_s2n_stuffer_pem_c_s2n_stuffer_pem_read_contents.11:$(call addone,$(MAX_BLOB_SIZE)) -UNWINDSET += s2n_stuffer_skip_to_char.3:$(call addone,$(MAX_BLOB_SIZE)) +UNWINDSET += s2n_stuffer_skip_read_until.10:$(call addone,$(MAX_BLOB_SIZE)) include ../Makefile.common diff --git a/tests/cbmc/proofs/s2n_stuffer_private_key_from_pem/Makefile b/tests/cbmc/proofs/s2n_stuffer_private_key_from_pem/Makefile index 297ee029720..a3cfe4789c8 100644 --- a/tests/cbmc/proofs/s2n_stuffer_private_key_from_pem/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_private_key_from_pem/Makefile @@ -52,5 +52,6 @@ UNWINDSET += strlen.0:5 # size of S2N_PEM_PKCS1_RSA_PRIVATE_KEY UNWINDSET += strncmp.0:5 # size of S2N_PEM_END_TOKEN UNWINDSET += __CPROVER_file_local_s2n_stuffer_pem_c_s2n_stuffer_pem_read_contents.11:$(call addone,$(MAX_BLOB_SIZE)) UNWINDSET += s2n_stuffer_skip_to_char.0:$(call addone,$(MAX_BLOB_SIZE)) +UNWINDSET += s2n_stuffer_skip_read_until.10:$(call addone,$(MAX_BLOB_SIZE)) include ../Makefile.common diff --git a/tests/pems/rsa_2048_weird_dashes_cert.pem b/tests/pems/rsa_2048_weird_dashes_cert.pem index c202037f0d5..43b7a789be0 100644 --- a/tests/pems/rsa_2048_weird_dashes_cert.pem +++ b/tests/pems/rsa_2048_weird_dashes_cert.pem @@ -1,4 +1,4 @@ --BEGIN CERTIFICATE- +--BEGIN CERTIFICATE-- MIICrTCCAZUCAn3VMA0GCSqGSIb3DQEBBQUAMB4xHDAaBgNVBAMME3MyblRlc3RJ bnRlcm1lZGlhdGUwIBcNMTYwMzMwMTg1NzQzWhgPMjExNjAzMDYxODU3NDNaMBgx FjAUBgNVBAMMDXMyblRlc3RTZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw @@ -14,7 +14,7 @@ jxUvy7UQvXrPqaHbODrHe+7f7r1YCzerujiP5SSHphY3GQq88KemfFczp/4GnYas sE50OYe7DQcB4zvnxmAXp51JIN4ooktUU9oKIM5y2cgEWdmJzeqPANYxf0ZIPlTg ETknKw1Dzf8wlK5mFbbG4LPQh1mkDVcwQV3ogG6kGMRa7neH+6SFkNpAKuPCoje4 NAE+WQ5ve1wk7nIRTQwDAF4= --END CERTIFICATE- +--END CERTIFICATE-- -----------------------BEGIN CERTIFICATE------------------------ MIIDKTCCAhGgAwIBAgICVxYwDQYJKoZIhvcNAQEFBQAwFjEUMBIGA1UEAwwLczJu VGVzdFJvb3QwIBcNMTYwMzMwMTg1NzA5WhgPMjExNjAzMDYxODU3MDlaMB4xHDAa @@ -33,7 +33,7 @@ kNhs5xYprdU82AqcaWwEd0kDrhC5rEvs6fj1J0NKmmhbovYxuDboj0a7If7HEqX0 NizyU3M3JONPZgadchZ+F5DosatF1Bpt/gsQRy383IogQ0/FS+juHCCc4VIUemuk YY1J8o5XdrGWrPBBiudTWqCobe+N541b+YLWbajT5UKzvSqJmcqpPTniJGc9eZxc z3cCNd3cKa9bK51stEnQSlA7PQXYs3K+TD3EmSn/G2x6Hmfr7lrpbIhEaD+y --END CERTIFICATE--BEGIN CERTIFICATE- +--END CERTIFICATE--BEGIN CERTIFICATE-- MIIDATCCAemgAwIBAgIJANDUkH+UYdz1MA0GCSqGSIb3DQEBCwUAMBYxFDASBgNV BAMMC3MyblRlc3RSb290MCAXDTE2MDMzMDE4NTYzOVoYDzIxMTYwMzA2MTg1NjM5 WjAWMRQwEgYDVQQDDAtzMm5UZXN0Um9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEP diff --git a/tests/unit/s2n_certificate_parsing_test.c b/tests/unit/s2n_certificate_parsing_test.c new file mode 100644 index 00000000000..9e22a404bc9 --- /dev/null +++ b/tests/unit/s2n_certificate_parsing_test.c @@ -0,0 +1,445 @@ +/* + * 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 "api/s2n.h" +#include "crypto/s2n_crypto.h" +#include "crypto/s2n_openssl_x509.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_safety.h" + +#define CERTIFICATE_1 \ + "MIIBljCCATygAwIBAgIUHxKbtYzLM4Bct5v5Sagb8aZU/BcwCgYIKoZIzj0EAwIw" \ + "HjELMAkGA1UEBhMCVVMxDzANBgNVBAMMBmJyYW5jaDAgFw0yNDAxMjMwMDU3MTha" \ + "GA8yMjAzMDYzMDAwNTcxOFowHDELMAkGA1UEBhMCVVMxDTALBgNVBAMMBGxlYWYw" \ + "WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASAmJKXt9P8hSz9ubntEokFn06+Rexr" \ + "lEujwWUIq5Kl6QwvtfDCzrJN/sUmM5mssjq7pF6XVr6zFcQ6G4BfnwGio1gwVjAU" \ + "BgNVHREEDTALgglsb2NhbGhvc3QwHQYDVR0OBBYEFNlOWJn7XfzC6xORzzjrdnqK" \ + "UlJwMB8GA1UdIwQYMBaAFFkQhpDCJ3bAbXw1tXpmk5Fi7YIGMAoGCCqGSM49BAMC" \ + "A0gAMEUCIB58OBwIruTJIy1f3tUgM/wXoO7fCoU25sMcioHBV9dYAiEA7Ufxa2JF" \ + "I5LP6RGyllsjjnh0MJy1ZMXhw7X6GqFn4Rw=" + +#define CERTIFICATE_2 \ + "MIIBoDCCAUegAwIBAgIUQud1+tNUPAEBnJLb2Lyl3/vuA4EwCgYIKoZIzj0EAwIw" \ + "HDELMAkGA1UEBhMCVVMxDTALBgNVBAMMBHJvb3QwIBcNMjQwMTIzMDA1NzE4WhgP" \ + "MjIwMzA2MzAwMDU3MThaMB4xCzAJBgNVBAYTAlVTMQ8wDQYDVQQDDAZicmFuY2gw" \ + "WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATfScB9w/LkHBAVXiyKN941555oyBpv" \ + "IZeCNXX+gbSvnS0pNRr35BalgFmij86DaXLl68RrHQsnhZByJvnIplN+o2MwYTAP" \ + "BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwICBDAdBgNVHQ4EFgQUWRCGkMIn" \ + "dsBtfDW1emaTkWLtggYwHwYDVR0jBBgwFoAUfCtcQDvXYwkR37fHwe/mi7JyQIQw" \ + "CgYIKoZIzj0EAwIDRwAwRAIgaK6aOuCfMwgAASavkZpoxWag49yco4d9AlxIU+rt" \ + "U2UCIHRieWdQICIWSEHdRTXWPPEnOd7A3UmTgoqbMl+Imhy8" + +#define CERTIFICATE_3 \ + "MIIBnzCCAUWgAwIBAgIUe0/XdFLyc4+Sj1NMkvbagE8DFaUwCgYIKoZIzj0EAwIw" \ + "HDELMAkGA1UEBhMCVVMxDTALBgNVBAMMBHJvb3QwIBcNMjQwMTIzMDA1NzE4WhgP" \ + "MjIwMzA2MzAwMDU3MThaMBwxCzAJBgNVBAYTAlVTMQ0wCwYDVQQDDARyb290MFkw" \ + "EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPPtDvucc4UGOdIjED2P/vDxVYDhO5P8s" \ + "7lyys3QZpKapMuc9wOV0cQ6tN9h4kVY+FJocYgqDAl2vv6Rg/wSbl6NjMGEwHQYD" \ + "VR0OBBYEFHwrXEA712MJEd+3x8Hv5ouyckCEMB8GA1UdIwQYMBaAFHwrXEA712MJ" \ + "Ed+3x8Hv5ouyckCEMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMAoG" \ + "CCqGSM49BAMCA0gAMEUCIQCcbhKRAsqQCj2pCXh+9og3sLw9q8nU4xAB9xuV3vPA" \ + "FAIgBxFWfRdu09dtE0IUkLTY0WPxiWaYYrKexlD4wUquqJE=" + +#define BEGIN_CERT "BEGIN CERTIFICATE" +#define BEGIN_CERT_LINE "-----" BEGIN_CERT "-----" +#define END_CERT "END CERTIFICATE" +#define END_CERT_LINE "-----" END_CERT "-----" + +static S2N_RESULT s2n_test_validate_cert(struct s2n_cert *cert, struct s2n_blob *expected) +{ + RESULT_ENSURE_REF(cert); + RESULT_ENSURE_REF(expected); + RESULT_ENSURE_EQ(cert->raw.size, expected->size); + RESULT_ENSURE_EQ(memcmp(cert->raw.data, expected->data, expected->size), 0); + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + const char *expected_cert_strs[] = { + CERTIFICATE_1, + CERTIFICATE_2, + CERTIFICATE_3, + }; + struct s2n_blob expected_certs[sizeof(expected_cert_strs) / sizeof(expected_cert_strs[0])] = { 0 }; + + for (size_t i = 0; i < s2n_array_len(expected_certs); i++) { + const char *base64_cert = expected_cert_strs[i]; + struct s2n_blob *out = &expected_certs[i]; + + /* base64 requires more than one character per byte, so the binary + * output will be smaller than the base64 input. + */ + EXPECT_SUCCESS(s2n_alloc(out, strlen(base64_cert))); + + struct s2n_stuffer out_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&out_stuffer, out)); + + DEFER_CLEANUP(struct s2n_stuffer in_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_alloc(&in_stuffer, strlen(base64_cert))); + EXPECT_SUCCESS(s2n_stuffer_write_str(&in_stuffer, base64_cert)); + + EXPECT_SUCCESS(s2n_stuffer_read_base64(&in_stuffer, &out_stuffer)); + out->size = s2n_stuffer_data_available(&out_stuffer); + } + + struct s2n_test_case { + const char *name; + const char *input; + }; + + /* clang-format off */ + const struct s2n_test_case test_cases[] = { + { + .name = "basic format", + .input = + BEGIN_CERT_LINE "\n" + CERTIFICATE_1 "\n" + END_CERT_LINE "\n" + BEGIN_CERT_LINE "\n" + CERTIFICATE_2 "\n" + END_CERT_LINE "\n" + BEGIN_CERT_LINE "\n" + CERTIFICATE_3 "\n" + END_CERT_LINE "\n", + }, + { + .name = "no newlines", + .input = + BEGIN_CERT_LINE CERTIFICATE_1 END_CERT_LINE + BEGIN_CERT_LINE CERTIFICATE_2 END_CERT_LINE + BEGIN_CERT_LINE CERTIFICATE_3 END_CERT_LINE, + }, + { + .name = "empty lines", + .input = + "\n" + BEGIN_CERT_LINE "\n" + CERTIFICATE_1 "\n" + END_CERT_LINE "\n" + "\n" + BEGIN_CERT_LINE "\n" + CERTIFICATE_2 "\n" + END_CERT_LINE "\n" + "\n" + BEGIN_CERT_LINE "\n" + CERTIFICATE_3 "\n" + END_CERT_LINE "\n" + "\n", + }, + { + .name = "double empty lines", + .input = + "\n" + "\n" + BEGIN_CERT_LINE "\n" + CERTIFICATE_1 "\n" + END_CERT_LINE "\n" + "\n" + "\n" + BEGIN_CERT_LINE "\n" + CERTIFICATE_2 "\n" + END_CERT_LINE "\n" + "\n" + "\n" + BEGIN_CERT_LINE "\n" + CERTIFICATE_3 "\n" + END_CERT_LINE "\n" + "\n" + "\n", + }, + { + .name = "variable number of newlines", + .input = + "\n" + BEGIN_CERT_LINE "\n" + CERTIFICATE_1 "\n" + END_CERT_LINE "\n" + "\n\n\n" + BEGIN_CERT_LINE "\n" + CERTIFICATE_2 "\n" + END_CERT_LINE "\n" + BEGIN_CERT_LINE "\n" + CERTIFICATE_3 "\n" + END_CERT_LINE "\n" + "\n\n" + }, + { + .name = "whitespace", + .input = + " " BEGIN_CERT_LINE "\n" + CERTIFICATE_1 "\n" + END_CERT_LINE " \n" + " " + BEGIN_CERT_LINE "\n" + CERTIFICATE_2 "\n" + END_CERT_LINE " \n" + BEGIN_CERT_LINE "\n" + CERTIFICATE_3 "\n" + END_CERT_LINE " \n", + }, + { + .name = "extra dashes", + .input = + "-" BEGIN_CERT_LINE "--\n" + CERTIFICATE_1 "\n" + "---" END_CERT_LINE "----\n" + "-----" BEGIN_CERT_LINE "------\n" + CERTIFICATE_2 "\n" + END_CERT_LINE "---------------\n" + "--" BEGIN_CERT_LINE "---\n" + CERTIFICATE_3 "\n" + "-" END_CERT_LINE "-\n", + }, + { + .name = "64 dashes", + .input = + "-----" "-----" "-----" "-----" + "-----" "-----" "-----" "-----" + "-----" "-----" "-----" "-----" + "----" + BEGIN_CERT + "-----" "-----" "-----" "-----" + "-----" "-----" "-----" "-----" + "-----" "-----" "-----" "-----" + "----" + "\n" + CERTIFICATE_1 "\n" + END_CERT_LINE "\n" + BEGIN_CERT_LINE "\n" + CERTIFICATE_2 "\n" + END_CERT_LINE "\n" + BEGIN_CERT_LINE "\n" + CERTIFICATE_3 "\n" + END_CERT_LINE "\n", + }, + { + .name = "missing dashes", + .input = + "--" BEGIN_CERT "--\n" + CERTIFICATE_1 "\n" + "---" END_CERT "---\n" + "----" BEGIN_CERT "----\n" + CERTIFICATE_2 "\n" + "--" END_CERT "---\n" + "---" BEGIN_CERT "----\n" + CERTIFICATE_3 "\n" + "----" END_CERT "--\n", + }, + { + .name = "delimiters share dashes", + .input = + "--" BEGIN_CERT "--" CERTIFICATE_1 "--" END_CERT + "--" BEGIN_CERT "--" CERTIFICATE_2 "--" END_CERT + "--" BEGIN_CERT "--" CERTIFICATE_3 "--" END_CERT "--" , + }, + { + .name = "comments", + .input = + "# cert1" + BEGIN_CERT_LINE "\n" + CERTIFICATE_1 "\n" + END_CERT_LINE "\n" + "\n" + "# cert2" + BEGIN_CERT_LINE "\n" + CERTIFICATE_2 "\n" + END_CERT_LINE "\n" + "\n" + "# cert3" + BEGIN_CERT_LINE "\n" + CERTIFICATE_3 "\n" + END_CERT_LINE "\n", + }, + { + .name = "comments containing dashes", + .input = + "# cert-1\n" + BEGIN_CERT_LINE "\n" + CERTIFICATE_1 "\n" + END_CERT_LINE "\n" + "\n" + "# -c-e-r-t-2-\n" + BEGIN_CERT_LINE "\n" + CERTIFICATE_2 "\n" + END_CERT_LINE "\n" + "\n" + "# -\n" + BEGIN_CERT_LINE "\n" + CERTIFICATE_3 "\n" + END_CERT_LINE "\n", + }, + { + .name = "comments containing other special characters", + .input = + "# cert_1\n" + BEGIN_CERT_LINE "\n" + CERTIFICATE_1 "\n" + END_CERT_LINE "\n" + "\n" + "# cert #2\n" + BEGIN_CERT_LINE "\n" + CERTIFICATE_2 "\n" + END_CERT_LINE "\n" + "\n" + "# cert !@$%! \n" + BEGIN_CERT_LINE "\n" + CERTIFICATE_3 "\n" + END_CERT_LINE "\n", + }, + }; + /* clang-format on */ + + for (size_t i = 0; i < s2n_array_len(test_cases); i++) { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + const struct s2n_test_case *test_case = &test_cases[i]; + + DEFER_CLEANUP(struct s2n_stuffer input = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_alloc(&input, strlen(test_case->input))); + EXPECT_SUCCESS(s2n_stuffer_write_str(&input, test_case->input)); + + struct s2n_cert_chain *cert_chain = chain_and_key->cert_chain; + if (s2n_create_cert_chain_from_stuffer(cert_chain, &input) != S2N_SUCCESS) { + fprintf(stderr, "Failed to parse \"%s\"\n", test_case->name); + FAIL_MSG("Failed to parse certificate chain input"); + } + + struct s2n_cert *cert = cert_chain->head; + for (size_t j = 0; j < s2n_array_len(expected_certs); j++) { + if (s2n_result_is_error(s2n_test_validate_cert(cert, &expected_certs[j]))) { + fprintf(stderr, "\"%s\" failed to parse cert %li\n", test_case->name, j); + FAIL_MSG("Did not correctly read all certificates"); + } + cert = cert->next; + } + } + + /* clang-format off */ + const struct s2n_test_case bad_test_cases[] = { + { + .name = "double begin marker", + .input = + BEGIN_CERT_LINE "\n" + BEGIN_CERT_LINE "\n" + CERTIFICATE_1 "\n" + END_CERT_LINE "\n" + }, + { + .name = "double end marker", + .input = + BEGIN_CERT_LINE "\n" + CERTIFICATE_1 "\n" + END_CERT_LINE "\n" + END_CERT_LINE "\n" + }, + { + .name = "missing begin marker", + .input = + CERTIFICATE_1 "\n" + END_CERT_LINE "\n" + }, + { + .name = "missing end marker", + .input = + BEGIN_CERT_LINE "\n" + CERTIFICATE_1 "\n" + }, + { + .name = "no dashes before marker", + .input = + BEGIN_CERT "-----\n" + CERTIFICATE_1 "\n" + END_CERT_LINE "\n" + }, + { + .name = "no dashes after marker", + .input = + "-----" BEGIN_CERT "\n" + CERTIFICATE_1 "\n" + END_CERT_LINE "\n" + }, + { + .name = "single dash before marker", + .input = + "-" BEGIN_CERT "-----\n" + CERTIFICATE_1 "\n" + END_CERT_LINE "\n" + }, + { + .name = "single dash after marker", + .input = + "-----" BEGIN_CERT "-\n" + CERTIFICATE_1 "\n" + END_CERT_LINE "\n" + }, + { + .name = "65 dashes before marker", + .input = + "-----" "-----" "-----" "-----" + "-----" "-----" "-----" "-----" + "-----" "-----" "-----" "-----" + "-----" + BEGIN_CERT "-----\n" + CERTIFICATE_1 "\n" + END_CERT_LINE "\n" + }, + { + .name = "65 dashes after marker", + .input = + "-----" BEGIN_CERT + "-----" "-----" "-----" "-----" + "-----" "-----" "-----" "-----" + "-----" "-----" "-----" "-----" + "-----" "\n" + CERTIFICATE_1 "\n" + END_CERT_LINE "\n" + }, + { + .name = "dashes in comment", + .input = + "# --cert--\n" + BEGIN_CERT_LINE "\n" + CERTIFICATE_1 "\n" + END_CERT_LINE "\n" + }, + }; + /* clang-format on */ + + for (size_t i = 0; i < s2n_array_len(bad_test_cases); i++) { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + const struct s2n_test_case *test_case = &bad_test_cases[i]; + + DEFER_CLEANUP(struct s2n_stuffer test_input = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_alloc(&test_input, strlen(test_case->input))); + EXPECT_SUCCESS(s2n_stuffer_write_str(&test_input, test_case->input)); + + struct s2n_cert_chain *cert_chain = chain_and_key->cert_chain; + if (s2n_create_cert_chain_from_stuffer(cert_chain, &test_input) == S2N_SUCCESS) { + fprintf(stderr, "Successfully parsed invalid cert chain \"%s\"\n", test_case->name); + FAIL_MSG("Successfully parsed invalid cert chain"); + }; + } + + for (size_t i = 0; i < s2n_array_len(expected_certs); i++) { + EXPECT_SUCCESS(s2n_free(&expected_certs[i])); + } + END_TEST(); +}