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