stuffer/s2n_stuffer_hex.c (121 lines of code) (raw):
/*
* 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 "error/s2n_errno.h"
#include "stuffer/s2n_stuffer.h"
#include "utils/s2n_safety.h"
static const uint8_t value_to_hex[16] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};
static const uint8_t hex_to_value[] = {
/* clang-format off */
['0'] = 0, ['1'] = 1, ['2'] = 2, ['3'] = 3, ['4'] = 4,
['5'] = 5, ['6'] = 6, ['7'] = 7, ['8'] = 8, ['9'] = 9,
['a'] = 10, ['b'] = 11, ['c'] = 12, ['d'] = 13, ['e'] = 14, ['f'] = 15,
['A'] = 10, ['B'] = 11, ['C'] = 12, ['D'] = 13, ['E'] = 14, ['F'] = 15,
/* clang-format on */
};
S2N_RESULT s2n_hex_digit(uint8_t half_byte, uint8_t *hex_digit)
{
RESULT_ENSURE_REF(hex_digit);
RESULT_ENSURE(half_byte < s2n_array_len(value_to_hex), S2N_ERR_BAD_HEX);
*hex_digit = value_to_hex[half_byte];
return S2N_RESULT_OK;
}
static S2N_RESULT s2n_stuffer_hex_digit_from_char(uint8_t c, uint8_t *i)
{
RESULT_ENSURE(c < s2n_array_len(hex_to_value), S2N_ERR_BAD_HEX);
/* Invalid characters map to 0 in hex_to_value, but so does '0'. */
if (hex_to_value[c] == 0) {
RESULT_ENSURE(c == '0', S2N_ERR_BAD_HEX);
}
*i = hex_to_value[c];
return S2N_RESULT_OK;
}
S2N_RESULT s2n_stuffer_read_hex(struct s2n_stuffer *hex_in, const struct s2n_blob *bytes_out)
{
RESULT_PRECONDITION(s2n_stuffer_validate(hex_in));
RESULT_PRECONDITION(s2n_blob_validate(bytes_out));
if (bytes_out->size == 0) {
return S2N_RESULT_OK;
}
size_t hex_size = bytes_out->size * 2;
RESULT_ENSURE(s2n_stuffer_data_available(hex_in) >= hex_size, S2N_ERR_BAD_HEX);
uint8_t *out = bytes_out->data;
uint8_t *in = hex_in->blob.data + hex_in->read_cursor;
for (size_t i = 0; i < bytes_out->size; i++) {
uint8_t hex_high = 0, hex_low = 0;
RESULT_GUARD(s2n_stuffer_hex_digit_from_char(in[(i * 2)], &hex_high));
RESULT_GUARD(s2n_stuffer_hex_digit_from_char(in[(i * 2) + 1], &hex_low));
out[i] = (hex_high * 16) + hex_low;
}
RESULT_GUARD_POSIX(s2n_stuffer_skip_read(hex_in, hex_size));
return S2N_RESULT_OK;
}
S2N_RESULT s2n_stuffer_write_hex(struct s2n_stuffer *hex_out, const struct s2n_blob *bytes_in)
{
RESULT_PRECONDITION(s2n_stuffer_validate(hex_out));
RESULT_PRECONDITION(s2n_blob_validate(bytes_in));
size_t bytes_size = bytes_in->size;
size_t hex_size = bytes_size * 2;
RESULT_GUARD_POSIX(s2n_stuffer_reserve_space(hex_out, hex_size));
uint8_t *out = hex_out->blob.data + hex_out->write_cursor;
uint8_t *in = bytes_in->data;
for (size_t i = 0; i < bytes_size; i++) {
out[(i * 2)] = value_to_hex[(in[i] >> 4)];
out[(i * 2) + 1] = value_to_hex[(in[i] & 0x0f)];
}
RESULT_GUARD_POSIX(s2n_stuffer_skip_write(hex_out, hex_size));
return S2N_RESULT_OK;
}
static S2N_RESULT s2n_stuffer_hex_read_n_bytes(struct s2n_stuffer *stuffer, uint8_t n, uint64_t *u)
{
RESULT_ENSURE_LTE(n, sizeof(uint64_t));
RESULT_ENSURE_REF(u);
uint8_t hex_data[16] = { 0 };
struct s2n_blob b = { 0 };
RESULT_GUARD_POSIX(s2n_blob_init(&b, hex_data, n * 2));
RESULT_ENSURE_REF(stuffer);
RESULT_ENSURE(s2n_stuffer_read(stuffer, &b) == S2N_SUCCESS, S2N_ERR_BAD_HEX);
/* Start with u = 0 */
*u = 0;
for (size_t i = 0; i < b.size; i++) {
*u <<= 4;
uint8_t hex = 0;
RESULT_GUARD(s2n_stuffer_hex_digit_from_char(b.data[i], &hex));
*u += hex;
}
return S2N_RESULT_OK;
}
S2N_RESULT s2n_stuffer_read_uint16_hex(struct s2n_stuffer *stuffer, uint16_t *u)
{
RESULT_ENSURE_REF(u);
uint64_t u64 = 0;
RESULT_GUARD(s2n_stuffer_hex_read_n_bytes(stuffer, sizeof(uint16_t), &u64));
RESULT_ENSURE_LTE(u64, UINT16_MAX);
*u = u64;
return S2N_RESULT_OK;
}
S2N_RESULT s2n_stuffer_read_uint8_hex(struct s2n_stuffer *stuffer, uint8_t *u)
{
RESULT_ENSURE_REF(u);
uint64_t u64 = 0;
RESULT_GUARD(s2n_stuffer_hex_read_n_bytes(stuffer, sizeof(uint8_t), &u64));
RESULT_ENSURE_LTE(u64, UINT8_MAX);
*u = u64;
return S2N_RESULT_OK;
}
static S2N_RESULT s2n_stuffer_hex_write_n_bytes(struct s2n_stuffer *stuffer, uint8_t n, uint64_t u)
{
RESULT_ENSURE_LTE(n, sizeof(uint64_t));
uint8_t hex_data[16] = { 0 };
struct s2n_blob b = { 0 };
RESULT_GUARD_POSIX(s2n_blob_init(&b, hex_data, n * 2));
for (size_t i = b.size; i > 0; i--) {
b.data[i - 1] = value_to_hex[u & 0x0f];
u >>= 4;
}
RESULT_GUARD_POSIX(s2n_stuffer_write(stuffer, &b));
return S2N_RESULT_OK;
}
S2N_RESULT s2n_stuffer_write_uint16_hex(struct s2n_stuffer *stuffer, uint16_t u)
{
return s2n_stuffer_hex_write_n_bytes(stuffer, sizeof(uint16_t), u);
}
S2N_RESULT s2n_stuffer_write_uint8_hex(struct s2n_stuffer *stuffer, uint8_t u)
{
return s2n_stuffer_hex_write_n_bytes(stuffer, sizeof(uint8_t), u);
}