quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

codecs.c (9347B)


      1 #include <assert.h>
      2 #include <errno.h>
      3 #include <limits.h>
      4 #include <stddef.h>
      5 #include <stdint.h>
      6 #include <stdlib.h>
      7 #include <string.h>
      8 
      9 #include "core.h"
     10 #include "private/common.h"
     11 #include "utils.h"
     12 
     13 /* Derived from original code by CodesInChaos */
     14 char *
     15 sodium_bin2hex(char *const hex, const size_t hex_maxlen,
     16                const unsigned char *const bin, const size_t bin_len)
     17 {
     18     size_t       i = (size_t) 0U;
     19     unsigned int x;
     20     int          b;
     21     int          c;
     22 
     23     if (bin_len >= SIZE_MAX / 2 || hex_maxlen <= bin_len * 2U) {
     24         sodium_misuse(); /* LCOV_EXCL_LINE */
     25     }
     26     while (i < bin_len) {
     27         c = bin[i] & 0xf;
     28         b = bin[i] >> 4;
     29         x = (unsigned char) (87U + c + (((c - 10U) >> 8) & ~38U)) << 8 |
     30             (unsigned char) (87U + b + (((b - 10U) >> 8) & ~38U));
     31         hex[i * 2U] = (char) x;
     32         x >>= 8;
     33         hex[i * 2U + 1U] = (char) x;
     34         i++;
     35     }
     36     hex[i * 2U] = 0U;
     37 
     38     return hex;
     39 }
     40 
     41 int
     42 sodium_hex2bin(unsigned char *const bin, const size_t bin_maxlen,
     43                const char *const hex, const size_t hex_len,
     44                const char *const ignore, size_t *const bin_len,
     45                const char **const hex_end)
     46 {
     47     size_t        bin_pos = (size_t) 0U;
     48     size_t        hex_pos = (size_t) 0U;
     49     int           ret     = 0;
     50     unsigned char c;
     51     unsigned char c_acc = 0U;
     52     unsigned char c_alpha0, c_alpha;
     53     unsigned char c_num0, c_num;
     54     unsigned char c_val;
     55     unsigned char state = 0U;
     56 
     57     while (hex_pos < hex_len) {
     58         c        = (unsigned char) hex[hex_pos];
     59         c_num    = c ^ 48U;
     60         c_num0   = (c_num - 10U) >> 8;
     61         c_alpha  = (c & ~32U) - 55U;
     62         c_alpha0 = ((c_alpha - 10U) ^ (c_alpha - 16U)) >> 8;
     63         if ((c_num0 | c_alpha0) == 0U) {
     64             if (ignore != NULL && state == 0U && strchr(ignore, c) != NULL) {
     65                 hex_pos++;
     66                 continue;
     67             }
     68             break;
     69         }
     70         c_val = (c_num0 & c_num) | (c_alpha0 & c_alpha);
     71         if (bin_pos >= bin_maxlen) {
     72             ret   = -1;
     73             errno = ERANGE;
     74             break;
     75         }
     76         if (state == 0U) {
     77             c_acc = c_val * 16U;
     78         } else {
     79             bin[bin_pos++] = c_acc | c_val;
     80         }
     81         state = ~state;
     82         hex_pos++;
     83     }
     84     if (state != 0U) {
     85         hex_pos--;
     86         errno = EINVAL;
     87         ret = -1;
     88     }
     89     if (ret != 0) {
     90         bin_pos = (size_t) 0U;
     91     }
     92     if (hex_end != NULL) {
     93         *hex_end = &hex[hex_pos];
     94     } else if (hex_pos != hex_len) {
     95         errno = EINVAL;
     96         ret = -1;
     97     }
     98     if (bin_len != NULL) {
     99         *bin_len = bin_pos;
    100     }
    101     return ret;
    102 }
    103 
    104 /*
    105  * Some macros for constant-time comparisons. These work over values in
    106  * the 0..255 range. Returned value is 0x00 on "false", 0xFF on "true".
    107  *
    108  * Original code by Thomas Pornin.
    109  */
    110 #define EQ(x, y) \
    111     ((((0U - ((unsigned int) (x) ^ (unsigned int) (y))) >> 8) & 0xFF) ^ 0xFF)
    112 #define GT(x, y) ((((unsigned int) (y) - (unsigned int) (x)) >> 8) & 0xFF)
    113 #define GE(x, y) (GT(y, x) ^ 0xFF)
    114 #define LT(x, y) GT(y, x)
    115 #define LE(x, y) GE(y, x)
    116 
    117 static int
    118 b64_byte_to_char(unsigned int x)
    119 {
    120     return (LT(x, 26) & (x + 'A')) |
    121            (GE(x, 26) & LT(x, 52) & (x + ('a' - 26))) |
    122            (GE(x, 52) & LT(x, 62) & (x + ('0' - 52))) | (EQ(x, 62) & '+') |
    123            (EQ(x, 63) & '/');
    124 }
    125 
    126 static unsigned int
    127 b64_char_to_byte(int c)
    128 {
    129     const unsigned int x =
    130         (GE(c, 'A') & LE(c, 'Z') & (c - 'A')) |
    131         (GE(c, 'a') & LE(c, 'z') & (c - ('a' - 26))) |
    132         (GE(c, '0') & LE(c, '9') & (c - ('0' - 52))) | (EQ(c, '+') & 62) |
    133         (EQ(c, '/') & 63);
    134 
    135     return x | (EQ(x, 0) & (EQ(c, 'A') ^ 0xFF));
    136 }
    137 
    138 static int
    139 b64_byte_to_urlsafe_char(unsigned int x)
    140 {
    141     return (LT(x, 26) & (x + 'A')) |
    142            (GE(x, 26) & LT(x, 52) & (x + ('a' - 26))) |
    143            (GE(x, 52) & LT(x, 62) & (x + ('0' - 52))) | (EQ(x, 62) & '-') |
    144            (EQ(x, 63) & '_');
    145 }
    146 
    147 static unsigned int
    148 b64_urlsafe_char_to_byte(int c)
    149 {
    150     const unsigned x =
    151         (GE(c, 'A') & LE(c, 'Z') & (c - 'A')) |
    152         (GE(c, 'a') & LE(c, 'z') & (c - ('a' - 26))) |
    153         (GE(c, '0') & LE(c, '9') & (c - ('0' - 52))) | (EQ(c, '-') & 62) |
    154         (EQ(c, '_') & 63);
    155 
    156     return x | (EQ(x, 0) & (EQ(c, 'A') ^ 0xFF));
    157 }
    158 
    159 
    160 #define VARIANT_NO_PADDING_MASK 0x2U
    161 #define VARIANT_URLSAFE_MASK    0x4U
    162 
    163 static void
    164 sodium_base64_check_variant(const int variant)
    165 {
    166     if ((((unsigned int) variant) & ~ 0x6U) != 0x1U) {
    167         sodium_misuse();
    168     }
    169 }
    170 
    171 size_t
    172 sodium_base64_encoded_len(const size_t bin_len, const int variant)
    173 {
    174     sodium_base64_check_variant(variant);
    175 
    176     return sodium_base64_ENCODED_LEN(bin_len, variant);
    177 }
    178 
    179 char *
    180 sodium_bin2base64(char * const b64, const size_t b64_maxlen,
    181                   const unsigned char * const bin, const size_t bin_len,
    182                   const int variant)
    183 {
    184     size_t       acc_len = (size_t) 0;
    185     size_t       b64_len;
    186     size_t       b64_pos = (size_t) 0;
    187     size_t       bin_pos = (size_t) 0;
    188     size_t       nibbles;
    189     size_t       remainder;
    190     unsigned int acc = 0U;
    191 
    192     sodium_base64_check_variant(variant);
    193     nibbles = bin_len / 3;
    194     remainder = bin_len - 3 * nibbles;
    195     b64_len = nibbles * 4;
    196     if (remainder != 0) {
    197         if ((((unsigned int) variant) & VARIANT_NO_PADDING_MASK) == 0U) {
    198             b64_len += 4;
    199         } else {
    200             b64_len += 2 + (remainder >> 1);
    201         }
    202     }
    203     if (b64_maxlen <= b64_len) {
    204         sodium_misuse();
    205     }
    206     if ((((unsigned int) variant) & VARIANT_URLSAFE_MASK) != 0U) {
    207         while (bin_pos < bin_len) {
    208             acc = (acc << 8) + bin[bin_pos++];
    209             acc_len += 8;
    210             while (acc_len >= 6) {
    211                 acc_len -= 6;
    212                 b64[b64_pos++] = (char) b64_byte_to_urlsafe_char((acc >> acc_len) & 0x3F);
    213             }
    214         }
    215         if (acc_len > 0) {
    216             b64[b64_pos++] = (char) b64_byte_to_urlsafe_char((acc << (6 - acc_len)) & 0x3F);
    217         }
    218     } else {
    219         while (bin_pos < bin_len) {
    220             acc = (acc << 8) + bin[bin_pos++];
    221             acc_len += 8;
    222             while (acc_len >= 6) {
    223                 acc_len -= 6;
    224                 b64[b64_pos++] = (char) b64_byte_to_char((acc >> acc_len) & 0x3F);
    225             }
    226         }
    227         if (acc_len > 0) {
    228             b64[b64_pos++] = (char) b64_byte_to_char((acc << (6 - acc_len)) & 0x3F);
    229         }
    230     }
    231     assert(b64_pos <= b64_len);
    232     while (b64_pos < b64_len) {
    233         b64[b64_pos++] = '=';
    234     }
    235     do {
    236         b64[b64_pos++] = 0U;
    237     } while (b64_pos < b64_maxlen);
    238 
    239     return b64;
    240 }
    241 
    242 static int
    243 _sodium_base642bin_skip_padding(const char * const b64, const size_t b64_len,
    244                                 size_t * const b64_pos_p,
    245                                 const char * const ignore, size_t padding_len)
    246 {
    247     int c;
    248 
    249     while (padding_len > 0) {
    250         if (*b64_pos_p >= b64_len) {
    251             errno = ERANGE;
    252             return -1;
    253         }
    254         ACQUIRE_FENCE;
    255         c = b64[*b64_pos_p];
    256         if (c == '=') {
    257             padding_len--;
    258         } else if (ignore == NULL || strchr(ignore, c) == NULL) {
    259             errno = EINVAL;
    260             return -1;
    261         }
    262         (*b64_pos_p)++;
    263     }
    264     return 0;
    265 }
    266 
    267 int
    268 sodium_base642bin(unsigned char * const bin, const size_t bin_maxlen,
    269                   const char * const b64, const size_t b64_len,
    270                   const char * const ignore, size_t * const bin_len,
    271                   const char ** const b64_end, const int variant)
    272 {
    273     size_t       acc_len = (size_t) 0;
    274     size_t       b64_pos = (size_t) 0;
    275     size_t       bin_pos = (size_t) 0;
    276     int          is_urlsafe;
    277     int          ret = 0;
    278     unsigned int acc = 0U;
    279     unsigned int d;
    280     char         c;
    281 
    282     sodium_base64_check_variant(variant);
    283     is_urlsafe = ((unsigned int) variant) & VARIANT_URLSAFE_MASK;
    284     while (b64_pos < b64_len) {
    285         c = b64[b64_pos];
    286         if (is_urlsafe) {
    287             d = b64_urlsafe_char_to_byte(c);
    288         } else {
    289             d = b64_char_to_byte(c);
    290         }
    291         if (d == 0xFF) {
    292             if (ignore != NULL && strchr(ignore, c) != NULL) {
    293                 b64_pos++;
    294                 continue;
    295             }
    296             break;
    297         }
    298         acc = (acc << 6) + d;
    299         acc_len += 6;
    300         if (acc_len >= 8) {
    301             acc_len -= 8;
    302             if (bin_pos >= bin_maxlen) {
    303                 errno = ERANGE;
    304                 ret = -1;
    305                 break;
    306             }
    307             bin[bin_pos++] = (acc >> acc_len) & 0xFF;
    308         }
    309         b64_pos++;
    310     }
    311     if (acc_len > 4U || (acc & ((1U << acc_len) - 1U)) != 0U) {
    312         ret = -1;
    313     } else if (ret == 0 &&
    314                (((unsigned int) variant) & VARIANT_NO_PADDING_MASK) == 0U) {
    315         ret = _sodium_base642bin_skip_padding(b64, b64_len, &b64_pos, ignore,
    316                                               acc_len / 2);
    317     }
    318     if (ret != 0) {
    319         bin_pos = (size_t) 0U;
    320     } else if (ignore != NULL) {
    321         while (b64_pos < b64_len && strchr(ignore, b64[b64_pos]) != NULL) {
    322             b64_pos++;
    323         }
    324     }
    325     if (b64_end != NULL) {
    326         *b64_end = &b64[b64_pos];
    327     } else if (b64_pos != b64_len) {
    328         errno = EINVAL;
    329         ret = -1;
    330     }
    331     if (bin_len != NULL) {
    332         *bin_len = bin_pos;
    333     }
    334     return ret;
    335 }