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 }