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 */ 13char * 14sodium_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 40int 41sodium_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 116static int 117b64_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 125static unsigned int 126b64_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 137static int 138b64_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 146static unsigned int 147b64_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 162static void 163sodium_base64_check_variant(const int variant) 164{ 165 if ((((unsigned int) variant) & ~ 0x6U) != 0x1U) { 166 sodium_misuse(); 167 } 168} 169 170size_t 171sodium_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 178char * 179sodium_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 241static 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 265int 266sodium_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