#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "cryptoauthlib.h"
#include "atca_helpers.h"
uint8_t atcab_b64rules_default[4] = { '+', '/', '=', 64 };
uint8_t atcab_b64rules_mime[4] = { '+', '/', '=', 76 };
uint8_t atcab_b64rules_urlsafe[4] = { '-', '_', 0, 0 };
ATCA_STATUS atcab_bin2hex(const uint8_t* bin, size_t bin_size, char* hex, size_t* hex_size)
{
return atcab_bin2hex_(bin, bin_size, hex, hex_size, true, true, true);
}
static void uint8_to_hex(uint8_t num, char* hex_str)
{
uint8_t nibble = (num >> 4) & 0x0F;
if (nibble < 10)
{
*(hex_str++) = '0' + nibble;
}
else
{
*(hex_str++) = 'A' + (nibble - 10);
}
nibble = num & 0x0F;
if (nibble < 10)
{
*(hex_str++) = '0' + nibble;
}
else
{
*(hex_str++) = 'A' + (nibble - 10);
}
}
static void hex_to_lowercase(char *buffer, size_t length)
{
size_t index;
if ((buffer != NULL) && (length > 0))
{
for (index = 0; index < length; index++)
{
buffer[index] = tolower(buffer[index]);
}
}
}
static void hex_to_uppercase(char *buffer, size_t length)
{
size_t index;
if ((buffer != NULL) && (length > 0))
{
for (index = 0; index < length; index++)
{
buffer[index] = toupper(buffer[index]);
}
}
}
ATCA_STATUS atcab_reversal(const uint8_t* bin, size_t bin_size, uint8_t* dest, size_t* dest_size)
{
size_t last, i;
if ((bin == NULL) || (dest == NULL))
{
return ATCA_BAD_PARAM;
}
if (*dest_size < bin_size)
{
return ATCA_SMALL_BUFFER;
}
last = bin_size - 1;
for (i = 0; i < bin_size; i++)
{
dest[i] = bin[last];
last--;
}
*dest_size = bin_size;
return ATCA_SUCCESS;
}
ATCA_STATUS atcab_bin2hex_(const uint8_t* bin, size_t bin_size, char* hex, size_t* hex_size, bool is_pretty, bool is_space, bool is_upper)
{
size_t i;
size_t cur_hex_size = 0;
size_t max_hex_size;
if (bin == NULL || hex == NULL || hex_size == NULL)
{
return ATCA_BAD_PARAM;
}
max_hex_size = *hex_size;
*hex_size = 0;
for (i = 0; i < bin_size; i++)
{
if (cur_hex_size > max_hex_size)
{
break;
}
if (i != 0)
{
if (is_pretty && (i % 16 == 0))
{
if (cur_hex_size + 2 > max_hex_size)
{
return ATCA_SMALL_BUFFER;
}
memcpy(&hex[cur_hex_size], "\r\n", 2);
cur_hex_size += 2;
}
else
{
if (is_space)
{
if (cur_hex_size + 1 > max_hex_size)
{
return ATCA_SMALL_BUFFER;
}
hex[cur_hex_size] = ' ';
cur_hex_size += 1;
}
}
}
if (cur_hex_size + 2 > max_hex_size)
{
return ATCA_SMALL_BUFFER;
}
uint8_to_hex(bin[i], &hex[cur_hex_size]);
cur_hex_size += 2;
}
if (is_upper)
{
hex_to_uppercase(hex, cur_hex_size);
}
else
{
hex_to_lowercase(hex, cur_hex_size);
}
*hex_size = cur_hex_size;
if (cur_hex_size < max_hex_size)
{
hex[cur_hex_size] = 0;
}
return ATCA_SUCCESS;
}
inline static uint8_t hex_digit_to_num(char c)
{
if (c >= '0' && c <= '9')
{
return (uint8_t)(c - '0');
}
if (c >= 'a' && c <= 'f')
{
return (uint8_t)(c - 'a') + 10;
}
if (c >= 'A' && c <= 'F')
{
return (uint8_t)(c - 'A') + 10;
}
return 16;
}
ATCA_STATUS atcab_hex2bin_(const char* hex, size_t hex_size, uint8_t* bin, size_t* bin_size, bool is_space)
{
size_t hex_index;
size_t bin_index = 0;
bool is_upper_nibble = true;
for (hex_index = 0; hex_index < hex_size; hex_index++)
{
if (!isHexDigit(hex[hex_index]))
{
if (((hex_index + 1) % 3 == 0) && is_space)
{
if (hex[hex_index] != ' ')
{
return ATCA_BAD_PARAM;
}
}
continue; }
if (bin_index >= *bin_size)
{
return ATCA_SMALL_BUFFER;
}
if (is_upper_nibble)
{
bin[bin_index] = hex_digit_to_num(hex[hex_index]) << 4;
}
else
{
bin[bin_index] += hex_digit_to_num(hex[hex_index]);
bin_index++;
}
is_upper_nibble = !is_upper_nibble;
}
if (!is_upper_nibble)
{
return ATCA_BAD_PARAM;
}
*bin_size = bin_index;
return ATCA_SUCCESS;
}
ATCA_STATUS atcab_hex2bin(const char* hex, size_t hex_size, uint8_t* bin, size_t* bin_size)
{
return atcab_hex2bin_(hex, hex_size, bin, bin_size, false);
}
bool isDigit(char c)
{
return (c >= '0') && (c <= '9');
}
bool isWhiteSpace(char c)
{
return (c == '\n') || (c == '\r') || (c == '\t') || (c == ' ');
}
bool isAlpha(char c)
{
return ((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z'));
}
bool isHexAlpha(char c)
{
return ((c >= 'A') && (c <= 'F')) || ((c >= 'a') && (c <= 'f'));
}
bool isHex(char c)
{
return isHexDigit(c) || isWhiteSpace(c);
}
bool isHexDigit(char c)
{
return isDigit(c) || isHexAlpha(c);
}
ATCA_STATUS packHex(const char* ascii_hex, size_t ascii_hex_len, char* packed_hex, size_t* packed_len)
{
size_t i = 0;
size_t j = 0;
if ((ascii_hex == NULL) || (packed_hex == NULL) || (packed_len == NULL))
{
return ATCA_BAD_PARAM;
}
for (i = 0; i < ascii_hex_len; i++)
{
if (isHexDigit(ascii_hex[i]))
{
if (j > *packed_len)
{
break;
}
packed_hex[j++] = ascii_hex[i];
}
}
*packed_len = j;
return ATCA_SUCCESS;
}
#ifdef ATCAPRINTF
ATCA_STATUS atcab_printbin_label(const char* label, uint8_t* binary, size_t bin_len)
{
printf("%s", label);
return atcab_printbin(binary, bin_len, true);
}
ATCA_STATUS atcab_printbin_sp(uint8_t* binary, size_t bin_len)
{
return atcab_printbin(binary, bin_len, true);
}
ATCA_STATUS atcab_printbin(uint8_t* binary, size_t bin_len, bool add_space)
{
size_t i = 0;
size_t line_len = 16;
if (binary == NULL)
{
return ATCA_BAD_PARAM;
}
line_len = add_space ? 16 : 32;
for (i = 0; i < bin_len; i++)
{
if (add_space)
{
printf("%02X ", binary[i]);
}
else
{
printf("%02X", binary[i]);
}
if ((i + 1) % line_len == 0)
{
printf("\r\n");
}
}
printf("\r\n");
return ATCA_SUCCESS;
}
#endif
#define B64_IS_EQUAL (uint8_t)64
#define B64_IS_INVALID (uint8_t)0xFF
bool isBase64(char c, const uint8_t * rules)
{
return isBase64Digit(c, rules) || isWhiteSpace(c);
}
bool isBase64Digit(char c, const uint8_t * rules)
{
return isDigit(c) || isAlpha(c) || c == rules[0] || c == rules[1] || c == rules[2];
}
uint8_t base64Index(char c, const uint8_t * rules)
{
if ((c >= 'A') && (c <= 'Z'))
{
return (uint8_t)(c - 'A');
}
if ((c >= 'a') && (c <= 'z'))
{
return (uint8_t)(26 + c - 'a');
}
if ((c >= '0') && (c <= '9'))
{
return (uint8_t)(52 + c - '0');
}
if (c == rules[0])
{
return (uint8_t)62;
}
if (c == rules[1])
{
return (uint8_t)63;
}
if (c == rules[2])
{
return B64_IS_EQUAL;
}
return B64_IS_INVALID;
}
char base64Char(uint8_t id, const uint8_t * rules)
{
if (id >= 0 && (id < 26))
{
return (char)('A' + id);
}
if ((id >= 26) && (id < 52))
{
return (char)('a' + id - 26);
}
if ((id >= 52) && (id < 62))
{
return (char)('0' + id - 52);
}
if (id == 62)
{
return rules[0];
}
if (id == 63)
{
return rules[1];
}
if (id == B64_IS_EQUAL)
{
return rules[2];
}
return B64_IS_INVALID;
}
static ATCA_STATUS atcab_base64decode_block(const uint8_t id[4], uint8_t* data, size_t* data_size, size_t data_max_size)
{
ATCA_STATUS status = ATCA_SUCCESS;
size_t new_bytes = 0;
do
{
if ((id[0] == B64_IS_EQUAL) ||
(id[1] == B64_IS_EQUAL) ||
(id[2] == B64_IS_EQUAL && id[3] != B64_IS_EQUAL))
{
status = ATCA_BAD_PARAM;
BREAK(status, "Base64 chars after end padding");
}
if (id[2] == B64_IS_EQUAL)
{
new_bytes = 1;
}
else if (id[3] == B64_IS_EQUAL)
{
new_bytes = 2;
}
else
{
new_bytes = 3;
}
if ((*data_size) + new_bytes > data_max_size)
{
status = ATCA_BAD_PARAM;
BREAK(status, "decoded buffer too small");
}
data[(*data_size)++] = ((id[0] << 2) | (id[1] >> 4));
if (id[2] == B64_IS_EQUAL)
{
break;
}
data[(*data_size)++] = ((id[1] << 4) | (id[2] >> 2));
if (id[3] == B64_IS_EQUAL)
{
break;
}
data[(*data_size)++] = ((id[2] << 6) | id[3]);
}
while (false);
return status;
}
ATCA_STATUS atcab_base64decode_(const char* encoded, size_t encoded_size, uint8_t* data, size_t* data_size, const uint8_t * rules)
{
ATCA_STATUS status = ATCA_SUCCESS;
uint8_t id[4];
int id_index = 0;
size_t enc_index = 0;
size_t data_max_size;
bool is_done = false;
do
{
if (encoded == NULL || data == NULL || data_size == NULL || rules == NULL)
{
status = ATCA_BAD_PARAM;
BREAK(status, "Null input parameter");
}
data_max_size = *data_size;
*data_size = 0;
for (enc_index = 0; enc_index < encoded_size; enc_index++)
{
if (isWhiteSpace(encoded[enc_index]))
{
continue; }
if (!isBase64Digit(encoded[enc_index], rules))
{
status = ATCA_BAD_PARAM;
BREAK(status, "Invalid base64 character");
}
if (is_done)
{
status = ATCA_BAD_PARAM;
BREAK(status, "Base64 chars after end padding");
}
id[id_index++] = base64Index(encoded[enc_index], rules);
if (id_index >= 4)
{
id_index = 0;
status = atcab_base64decode_block(id, data, data_size, data_max_size);
if (status != ATCA_SUCCESS)
{
break;
}
is_done = (id[3] == B64_IS_EQUAL);
}
}
if (status != ATCA_SUCCESS)
{
break;
}
if (id_index)
{
if (id_index < 2)
{
status = ATCA_BAD_PARAM;
BREAK(status, "Invalid number of base64 chars");
}
for (; id_index < 4; id_index++)
{
id[id_index] = B64_IS_EQUAL;
}
status = atcab_base64decode_block(id, data, data_size, data_max_size);
}
}
while (false);
return status;
}
ATCA_STATUS atcab_base64encode_(
const uint8_t* data,
size_t data_size,
char* encoded,
size_t* encoded_size,
const uint8_t * rules
)
{
ATCA_STATUS status = ATCA_SUCCESS;
size_t data_idx = 0;
size_t b64_idx = 0;
size_t offset = 0;
uint8_t id = 0;
size_t b64_len;
do
{
if (encoded == NULL || data == NULL || encoded_size == NULL || !rules)
{
status = ATCA_BAD_PARAM;
BREAK(status, "Null input parameter");
}
b64_len = (data_size / 3 + (data_size % 3 != 0)) * 4; if (rules[3])
{
if (rules[3] % 4 != 0)
{
status = ATCA_BAD_PARAM;
BREAK(status, "newline rules[3] must be multiple of 4");
}
b64_len += (b64_len / rules[3]) * 2;
}
b64_len += 1; if (*encoded_size < b64_len)
{
status = ATCA_SMALL_BUFFER;
BREAK(status, "Length of encoded buffer too small");
}
*encoded_size = 0;
for (data_idx = 0; data_idx < data_size; data_idx += 3)
{
if (rules[3] && data_idx > 0 && (b64_idx - offset) % rules[3] == 0)
{
encoded[b64_idx++] = '\r';
encoded[b64_idx++] = '\n';
offset += 2;
}
id = (data[data_idx] & 0xFC) >> 2;
encoded[b64_idx++] = base64Char(id, rules);
id = (data[data_idx] & 0x03) << 4;
if (data_idx + 1 < data_size)
{
id |= (data[data_idx + 1] & 0xF0) >> 4;
encoded[b64_idx++] = base64Char(id, rules);
id = (data[data_idx + 1] & 0x0F) << 2;
if (data_idx + 2 < data_size)
{
id |= (data[data_idx + 2] & 0xC0) >> 6;
encoded[b64_idx++] = base64Char(id, rules);
id = data[data_idx + 2] & 0x3F;
encoded[b64_idx++] = base64Char(id, rules);
}
else
{
encoded[b64_idx++] = base64Char(id, rules);
encoded[b64_idx++] = base64Char(B64_IS_EQUAL, rules);
}
}
else
{
encoded[b64_idx++] = base64Char(id, rules);
encoded[b64_idx++] = base64Char(B64_IS_EQUAL, rules);
encoded[b64_idx++] = base64Char(B64_IS_EQUAL, rules);
}
}
while (b64_idx > 1 && encoded[b64_idx - 1] == 0)
{
b64_idx--;
}
encoded[b64_idx++] = 0;
*encoded_size = b64_idx - 1;
}
while (false);
return status;
}
ATCA_STATUS atcab_base64encode(const uint8_t* byte_array, size_t array_len, char* encoded, size_t* encoded_len)
{
return atcab_base64encode_(byte_array, array_len, encoded, encoded_len, atcab_b64rules_default);
}
ATCA_STATUS atcab_base64decode(const char* encoded, size_t encoded_len, uint8_t* byte_array, size_t* array_len)
{
return atcab_base64decode_(encoded, encoded_len, byte_array, array_len, atcab_b64rules_default);
}