LCOV - code coverage report
Current view: top level - stuffer - s2n_stuffer_base64.c (source / functions) Hit Total Coverage
Test: unit_test_coverage.info Lines: 60 60 100.0 %
Date: 2025-08-15 07:28:39 Functions: 3 3 100.0 %
Branches: 26 48 54.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 <openssl/evp.h>
      17                 :            : #include <string.h>
      18                 :            : 
      19                 :            : #include "error/s2n_errno.h"
      20                 :            : #include "stuffer/s2n_stuffer.h"
      21                 :            : #include "utils/s2n_safety.h"
      22                 :            : 
      23                 :            : bool s2n_is_base64_char(unsigned char c)
      24                 :    2767913 : {
      25                 :            :     /* use bitwise operations to minimize branching */
      26                 :    2767913 :     uint8_t out = 0;
      27                 :    2767913 :     out ^= (c >= 'A') & (c <= 'Z');
      28                 :    2767913 :     out ^= (c >= 'a') & (c <= 'z');
      29                 :    2767913 :     out ^= (c >= '0') & (c <= '9');
      30                 :    2767913 :     out ^= c == '+';
      31                 :    2767913 :     out ^= c == '/';
      32                 :    2767913 :     out ^= c == '=';
      33                 :            : 
      34                 :    2767913 :     return out == 1;
      35                 :    2767913 : }
      36                 :            : 
      37                 :            : /* We use the base64 decoding implementation from the libcrypto to allow for
      38                 :            :  * sidechannel-resistant base64 decoding. While OpenSSL doesn't support this,
      39                 :            :  * AWS-LC does.
      40                 :            :  */
      41                 :            : int s2n_stuffer_read_base64(struct s2n_stuffer *stuffer, struct s2n_stuffer *out)
      42                 :      43772 : {
      43 [ -  + ][ +  - ]:      43772 :     POSIX_PRECONDITION(s2n_stuffer_validate(stuffer));
      44 [ -  + ][ +  - ]:      43772 :     POSIX_PRECONDITION(s2n_stuffer_validate(out));
      45                 :            : 
      46                 :      43772 :     int base64_groups = s2n_stuffer_data_available(stuffer) / 4;
      47         [ +  + ]:      43772 :     if (base64_groups == 0) {
      48                 :          5 :         return S2N_SUCCESS;
      49                 :          5 :     }
      50                 :      43767 :     int base64_data_size = base64_groups * 4;
      51                 :      43767 :     int binary_output_size = base64_groups * 3;
      52                 :            : 
      53                 :      43767 :     const uint32_t base64_data_offset = stuffer->read_cursor;
      54         [ -  + ]:      43767 :     POSIX_GUARD(s2n_stuffer_skip_read(stuffer, base64_data_size));
      55                 :      43767 :     const uint8_t *start_of_base64_data = stuffer->blob.data + base64_data_offset;
      56                 :            : 
      57                 :      43767 :     const uint32_t binary_output_offset = out->write_cursor;
      58         [ -  + ]:      43767 :     POSIX_GUARD(s2n_stuffer_skip_write(out, binary_output_size));
      59                 :      43767 :     uint8_t *start_of_binary_output = out->blob.data + binary_output_offset;
      60                 :            : 
      61                 :            :     /* https://docs.openssl.org/master/man3/EVP_EncodeInit/
      62                 :            :      * > This function will return the length of the data decoded or -1 on error. */
      63                 :      43767 :     int res = EVP_DecodeBlock(start_of_binary_output, start_of_base64_data, base64_data_size);
      64 [ -  + ][ #  # ]:      43767 :     POSIX_ENSURE(res == binary_output_size, S2N_ERR_INVALID_BASE64);
      65                 :            : 
      66                 :            :     /* https://docs.openssl.org/1.1.1/man3/EVP_EncodeInit/
      67                 :            :      * > The output will be padded with 0 bits if necessary to ensure that the 
      68                 :            :      * > output is always 3 bytes for every 4 input bytes. 
      69                 :            :      * FFFF -> 0x14 0x51 0x45
      70                 :            :      * FFF= -> 0x14 0x51 0x00
      71                 :            :      * FF== -> 0x14 0x00 0x00
      72                 :            :      * F=== -> INVALID
      73                 :            :      */
      74                 :            :     /* manually unrolled loop to prevent CBMC errors */
      75 [ #  # ][ -  + ]:      43767 :     POSIX_ENSURE_GTE(stuffer->read_cursor, 2);
      76         [ +  + ]:      43767 :     if (stuffer->blob.data[stuffer->read_cursor - 1] == '=') {
      77                 :       2135 :         out->write_cursor -= 1;
      78                 :       2135 :     }
      79         [ +  + ]:      43767 :     if (stuffer->blob.data[stuffer->read_cursor - 2] == '=') {
      80                 :       1026 :         out->write_cursor -= 1;
      81                 :       1026 :     }
      82                 :            : 
      83                 :      43767 :     return S2N_SUCCESS;
      84                 :      43767 : }
      85                 :            : 
      86                 :            : int s2n_stuffer_write_base64(struct s2n_stuffer *stuffer, struct s2n_stuffer *in)
      87                 :         56 : {
      88 [ -  + ][ +  - ]:         56 :     POSIX_PRECONDITION(s2n_stuffer_validate(stuffer));
      89 [ -  + ][ +  - ]:         56 :     POSIX_PRECONDITION(s2n_stuffer_validate(in));
      90                 :            : 
      91                 :         56 :     int binary_data_size = s2n_stuffer_data_available(in);
      92         [ +  + ]:         56 :     if (binary_data_size == 0) {
      93                 :          1 :         return S2N_SUCCESS;
      94                 :          1 :     }
      95                 :            : 
      96                 :         55 :     int base64_groups = binary_data_size / 3;
      97                 :            :     /* we will need to add a final padded block */
      98         [ +  + ]:         55 :     if (binary_data_size % 3 != 0) {
      99                 :         37 :         base64_groups++;
     100                 :         37 :     }
     101                 :            : 
     102                 :         55 :     int base64_output_size = base64_groups * 4;
     103                 :            :     /* Null terminator is added */
     104                 :         55 :     base64_output_size += 1;
     105                 :            : 
     106                 :         55 :     const uint32_t binary_data_offset = in->read_cursor;
     107         [ -  + ]:         55 :     POSIX_GUARD(s2n_stuffer_skip_read(in, binary_data_size));
     108                 :         55 :     const uint8_t *start_of_binary_data = in->blob.data + binary_data_offset;
     109                 :            : 
     110                 :         55 :     const uint32_t base64_output_offset = stuffer->write_cursor;
     111         [ -  + ]:         55 :     POSIX_GUARD(s2n_stuffer_skip_write(stuffer, base64_output_size));
     112                 :         55 :     uint8_t *start_of_base64_output = stuffer->blob.data + base64_output_offset;
     113                 :            : 
     114                 :            :     /* https://docs.openssl.org/master/man3/EVP_EncodeInit/
     115                 :            :      * > The length of the data generated without the NUL terminator is returned from the function. */
     116                 :         55 :     int res = EVP_EncodeBlock(start_of_base64_output, start_of_binary_data, binary_data_size);
     117 [ -  + ][ #  # ]:         55 :     POSIX_ENSURE(res == base64_output_size - 1, S2N_ERR_INVALID_BASE64);
     118         [ -  + ]:         55 :     POSIX_GUARD(s2n_stuffer_wipe_n(stuffer, 1));
     119                 :            : 
     120                 :         55 :     return S2N_SUCCESS;
     121                 :         55 : }

Generated by: LCOV version 1.14