LCOV - code coverage report
Current view: top level - tls - s2n_client_cert.c (source / functions) Hit Total Coverage
Test: unit_test_coverage.info Lines: 76 76 100.0 %
Date: 2025-08-15 07:28:39 Functions: 3 3 100.0 %
Branches: 49 80 61.2 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
       3                 :            :  *
       4                 :            :  * Licensed under the Apache License, Version 2.0 (the "License").
       5                 :            :  * You may not use this file except in compliance with the License.
       6                 :            :  * A copy of the License is located at
       7                 :            :  *
       8                 :            :  *  http://aws.amazon.com/apache2.0
       9                 :            :  *
      10                 :            :  * or in the "license" file accompanying this file. This file is distributed
      11                 :            :  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
      12                 :            :  * express or implied. See the License for the specific language governing
      13                 :            :  * permissions and limitations under the License.
      14                 :            :  */
      15                 :            : 
      16                 :            : #include "api/s2n.h"
      17                 :            : #include "crypto/s2n_certificate.h"
      18                 :            : #include "error/s2n_errno.h"
      19                 :            : #include "stuffer/s2n_stuffer.h"
      20                 :            : #include "tls/s2n_cipher_suites.h"
      21                 :            : #include "tls/s2n_config.h"
      22                 :            : #include "tls/s2n_connection.h"
      23                 :            : #include "tls/s2n_tls.h"
      24                 :            : #include "utils/s2n_blob.h"
      25                 :            : #include "utils/s2n_safety.h"
      26                 :            : 
      27                 :            : /* In TLS1.2, the certificate list is just an opaque vector of certificates:
      28                 :            :  *
      29                 :            :  *      opaque ASN.1Cert<1..2^24-1>;
      30                 :            :  *
      31                 :            :  *      struct {
      32                 :            :  *          ASN.1Cert certificate_list<0..2^24-1>;
      33                 :            :  *      } Certificate;
      34                 :            :  *
      35                 :            :  * This construction allowed us to store the entire certificate_list blob
      36                 :            :  * and return it from the s2n_connection_get_client_cert_chain method for
      37                 :            :  * customers to examine.
      38                 :            :  *
      39                 :            :  * However, TLS1.3 introduced per-certificate extensions:
      40                 :            :  *
      41                 :            :  *      struct {
      42                 :            :  *          opaque cert_data<1..2^24-1>;
      43                 :            :  * ---->    Extension extensions<0..2^16-1>;    <----
      44                 :            :  *      } CertificateEntry;
      45                 :            :  *
      46                 :            :  *      struct {
      47                 :            :  *          opaque certificate_request_context<0..2^8-1>;
      48                 :            :  *          CertificateEntry certificate_list<0..2^24-1>;
      49                 :            :  *      } Certificate;
      50                 :            :  *
      51                 :            :  * So in order to store / return the certificates in the same format as in TLS1.2,
      52                 :            :  * we need to first strip out the extensions.
      53                 :            :  */
      54                 :            : static S2N_RESULT s2n_client_cert_chain_store(struct s2n_connection *conn,
      55                 :            :         struct s2n_blob *raw_cert_chain)
      56                 :        120 : {
      57 [ -  + ][ #  # ]:        120 :     RESULT_ENSURE_REF(conn);
      58 [ -  + ][ #  # ]:        120 :     RESULT_ENSURE_REF(raw_cert_chain);
      59                 :            : 
      60                 :            :     /* If a client cert chain has already been stored (e.g. on the re-entry case
      61                 :            :      * of an async callback), no need to store it again.
      62                 :            :      */
      63         [ +  + ]:        120 :     if (conn->handshake_params.client_cert_chain.size > 0) {
      64                 :         12 :         return S2N_RESULT_OK;
      65                 :         12 :     }
      66                 :            : 
      67                 :            :     /* Earlier versions are a basic copy */
      68         [ +  + ]:        108 :     if (conn->actual_protocol_version < S2N_TLS13) {
      69         [ -  + ]:         71 :         RESULT_GUARD_POSIX(s2n_dup(raw_cert_chain, &conn->handshake_params.client_cert_chain));
      70                 :         71 :         return S2N_RESULT_OK;
      71                 :         71 :     }
      72                 :            : 
      73                 :         37 :     DEFER_CLEANUP(struct s2n_blob output = { 0 }, s2n_free);
      74         [ -  + ]:         37 :     RESULT_GUARD_POSIX(s2n_realloc(&output, raw_cert_chain->size));
      75                 :            : 
      76                 :         37 :     struct s2n_stuffer cert_chain_in = { 0 };
      77         [ -  + ]:         37 :     RESULT_GUARD_POSIX(s2n_stuffer_init_written(&cert_chain_in, raw_cert_chain));
      78                 :            : 
      79                 :         37 :     struct s2n_stuffer cert_chain_out = { 0 };
      80         [ -  + ]:         37 :     RESULT_GUARD_POSIX(s2n_stuffer_init(&cert_chain_out, &output));
      81                 :            : 
      82                 :         37 :     uint32_t cert_size = 0;
      83                 :         37 :     uint16_t extensions_size = 0;
      84         [ +  + ]:        119 :     while (s2n_stuffer_data_available(&cert_chain_in)) {
      85         [ -  + ]:         83 :         RESULT_GUARD_POSIX(s2n_stuffer_read_uint24(&cert_chain_in, &cert_size));
      86         [ -  + ]:         83 :         RESULT_GUARD_POSIX(s2n_stuffer_write_uint24(&cert_chain_out, cert_size));
      87         [ -  + ]:         83 :         RESULT_GUARD_POSIX(s2n_stuffer_copy(&cert_chain_in, &cert_chain_out, cert_size));
      88                 :            : 
      89                 :            :         /* The new TLS1.3 format includes extensions, which we must skip.
      90                 :            :          * Customers will not expect TLS extensions in a DER-encoded certificate.
      91                 :            :          */
      92         [ -  + ]:         83 :         RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&cert_chain_in, &extensions_size));
      93         [ +  + ]:         83 :         RESULT_GUARD_POSIX(s2n_stuffer_skip_read(&cert_chain_in, extensions_size));
      94                 :         83 :     }
      95                 :            : 
      96                 :            :     /* We will have allocated more memory than actually necessary.
      97                 :            :      * If this becomes a problem, we should consider reallocing the correct amount of memory here.
      98                 :            :      */
      99                 :         36 :     output.size = s2n_stuffer_data_available(&cert_chain_out);
     100                 :            : 
     101                 :         36 :     conn->handshake_params.client_cert_chain = output;
     102                 :         36 :     ZERO_TO_DISABLE_DEFER_CLEANUP(output);
     103                 :         36 :     return S2N_RESULT_OK;
     104                 :         37 : }
     105                 :            : 
     106                 :            : int s2n_client_cert_recv(struct s2n_connection *conn)
     107                 :        152 : {
     108                 :            :     /* s2n_client_cert_recv() may be re-entered due to handling an async callback.
     109                 :            :      * We operate on a copy of `handshake.io` to ensure the stuffer is initilized properly on the re-entry case.
     110                 :            :      */
     111                 :        152 :     struct s2n_stuffer in = conn->handshake.io;
     112                 :            : 
     113         [ +  + ]:        152 :     if (conn->actual_protocol_version == S2N_TLS13) {
     114                 :         46 :         uint8_t certificate_request_context_len = 0;
     115         [ -  + ]:         46 :         POSIX_GUARD(s2n_stuffer_read_uint8(&in, &certificate_request_context_len));
     116 [ +  + ][ +  - ]:         46 :         S2N_ERROR_IF(certificate_request_context_len != 0, S2N_ERR_BAD_MESSAGE);
     117                 :         46 :     }
     118                 :            : 
     119                 :        151 :     uint32_t cert_chain_size = 0;
     120         [ -  + ]:        151 :     POSIX_GUARD(s2n_stuffer_read_uint24(&in, &cert_chain_size));
     121 [ -  + ][ #  # ]:        151 :     POSIX_ENSURE(cert_chain_size <= s2n_stuffer_data_available(&in), S2N_ERR_BAD_MESSAGE);
     122         [ +  + ]:        151 :     if (cert_chain_size == 0) {
     123         [ +  + ]:         31 :         POSIX_GUARD(s2n_conn_set_handshake_no_client_cert(conn));
     124                 :         27 :         return S2N_SUCCESS;
     125                 :         31 :     }
     126                 :            : 
     127                 :        120 :     uint8_t *cert_chain_data = s2n_stuffer_raw_read(&in, cert_chain_size);
     128 [ -  + ][ #  # ]:        120 :     POSIX_ENSURE_REF(cert_chain_data);
     129                 :            : 
     130                 :        120 :     struct s2n_blob cert_chain = { 0 };
     131         [ -  + ]:        120 :     POSIX_GUARD(s2n_blob_init(&cert_chain, cert_chain_data, cert_chain_size));
     132 [ +  + ][ +  - ]:        120 :     POSIX_ENSURE(s2n_result_is_ok(s2n_client_cert_chain_store(conn, &cert_chain)),
     133                 :        119 :             S2N_ERR_BAD_MESSAGE);
     134                 :            : 
     135                 :        119 :     s2n_cert_public_key public_key = { 0 };
     136         [ -  + ]:        119 :     POSIX_GUARD(s2n_pkey_zero_init(&public_key));
     137                 :            : 
     138                 :            :     /* Determine the Cert Type, Verify the Cert, and extract the Public Key */
     139                 :        119 :     s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN;
     140         [ +  + ]:        119 :     POSIX_GUARD_RESULT(s2n_x509_validator_validate_cert_chain(&conn->x509_validator, conn, cert_chain_data,
     141                 :         89 :             cert_chain_size, &pkey_type, &public_key));
     142                 :            : 
     143                 :         89 :     conn->handshake_params.client_cert_pkey_type = pkey_type;
     144         [ -  + ]:         89 :     POSIX_GUARD_RESULT(s2n_pkey_setup_for_type(&public_key, pkey_type));
     145                 :            : 
     146         [ -  + ]:         89 :     POSIX_GUARD(s2n_pkey_check_key_exists(&public_key));
     147                 :         89 :     conn->handshake_params.client_public_key = public_key;
     148                 :            : 
     149                 :            :     /* Update handshake.io to reflect the true stuffer state after all async callbacks are handled. */
     150                 :         89 :     conn->handshake.io = in;
     151                 :            : 
     152                 :         89 :     return S2N_SUCCESS;
     153                 :         89 : }
     154                 :            : 
     155                 :            : int s2n_client_cert_send(struct s2n_connection *conn)
     156                 :        143 : {
     157                 :        143 :     struct s2n_cert_chain_and_key *chain_and_key = conn->handshake_params.our_chain_and_key;
     158                 :            : 
     159         [ +  + ]:        143 :     if (conn->actual_protocol_version >= S2N_TLS13) {
     160                 :            :         /* If this message is in response to a CertificateRequest, the value of
     161                 :            :          * certificate_request_context in that message.
     162                 :            :          * https://tools.ietf.org/html/rfc8446#section-4.4.2
     163                 :            :          *
     164                 :            :          * This field SHALL be zero length unless used for the post-handshake authentication
     165                 :            :          * https://tools.ietf.org/html/rfc8446#section-4.3.2
     166                 :            :          */
     167                 :         46 :         uint8_t certificate_request_context_len = 0;
     168         [ -  + ]:         46 :         POSIX_GUARD(s2n_stuffer_write_uint8(&conn->handshake.io, certificate_request_context_len));
     169                 :         46 :     }
     170                 :            : 
     171         [ +  + ]:        143 :     if (chain_and_key == NULL) {
     172         [ +  + ]:         38 :         POSIX_GUARD(s2n_conn_set_handshake_no_client_cert(conn));
     173         [ -  + ]:         33 :         POSIX_GUARD(s2n_send_empty_cert_chain(&conn->handshake.io));
     174                 :         33 :         return 0;
     175                 :         33 :     }
     176                 :            : 
     177         [ -  + ]:        105 :     POSIX_GUARD(s2n_send_cert_chain(conn, &conn->handshake.io, chain_and_key));
     178                 :        105 :     return S2N_SUCCESS;
     179                 :        105 : }

Generated by: LCOV version 1.14