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