diff options
Diffstat (limited to 'src/core/security/base64.c')
-rw-r--r-- | src/core/security/base64.c | 103 |
1 files changed, 68 insertions, 35 deletions
diff --git a/src/core/security/base64.c b/src/core/security/base64.c index 3b8fea8f73..8dfaef846f 100644 --- a/src/core/security/base64.c +++ b/src/core/security/base64.c @@ -120,7 +120,68 @@ char *grpc_base64_encode(const void *vdata, size_t data_size, int url_safe, } gpr_slice grpc_base64_decode(const char *b64, int url_safe) { - size_t b64_len = strlen(b64); + return grpc_base64_decode_with_len(b64, strlen(b64), url_safe); +} + +static void decode_one_char(const unsigned char *codes, unsigned char *result, + size_t *result_offset) { + gpr_uint32 packed = (codes[0] << 2) | (codes[1] >> 4); + result[(*result_offset)++] = (unsigned char)packed; +} + +static void decode_two_chars(const unsigned char *codes, unsigned char *result, + size_t *result_offset) { + gpr_uint32 packed = (codes[0] << 10) | (codes[1] << 4) | (codes[2] >> 2); + result[(*result_offset)++] = (unsigned char)(packed >> 8); + result[(*result_offset)++] = (unsigned char)(packed); +} + +static int decode_group(const unsigned char *codes, size_t num_codes, + unsigned char *result, size_t *result_offset) { + GPR_ASSERT(num_codes <= 4); + + /* Short end groups that may not have padding. */ + if (num_codes == 1) { + gpr_log(GPR_ERROR, "Invalid group. Must be at least 2 bytes."); + return 0; + } + if (num_codes == 2) { + decode_one_char(codes, result, result_offset); + return 1; + } + if (num_codes == 3) { + decode_two_chars(codes, result, result_offset); + return 1; + } + + /* Regular 4 byte groups with padding or not. */ + GPR_ASSERT(num_codes == 4); + if (codes[0] == GRPC_BASE64_PAD_BYTE || codes[1] == GRPC_BASE64_PAD_BYTE) { + gpr_log(GPR_ERROR, "Invalid padding detected."); + return 0; + } + if (codes[2] == GRPC_BASE64_PAD_BYTE) { + if (codes[3] == GRPC_BASE64_PAD_BYTE) { + decode_one_char(codes, result, result_offset); + } else { + gpr_log(GPR_ERROR, "Invalid padding detected."); + return 0; + } + } else if (codes[3] == GRPC_BASE64_PAD_BYTE) { + decode_two_chars(codes, result, result_offset); + } else { + /* No padding. */ + gpr_uint32 packed = + (codes[0] << 18) | (codes[1] << 12) | (codes[2] << 6) | codes[3]; + result[(*result_offset)++] = (unsigned char)(packed >> 16); + result[(*result_offset)++] = (unsigned char)(packed >> 8); + result[(*result_offset)++] = (unsigned char)(packed); + } + return 1; +} + +gpr_slice grpc_base64_decode_with_len(const char *b64, size_t b64_len, + int url_safe) { gpr_slice result = gpr_slice_malloc(b64_len); unsigned char *current = GPR_SLICE_START_PTR(result); size_t result_size = 0; @@ -128,7 +189,7 @@ gpr_slice grpc_base64_decode(const char *b64, int url_safe) { size_t num_codes = 0; while (b64_len--) { - unsigned char c = *b64++; + unsigned char c = (unsigned char)(*b64++); signed char code; if (c >= GPR_ARRAY_SIZE(base64_bytes)) continue; if (url_safe) { @@ -149,45 +210,17 @@ gpr_slice grpc_base64_decode(const char *b64, int url_safe) { goto fail; } } else { - codes[num_codes++] = code; + codes[num_codes++] = (unsigned char)code; if (num_codes == 4) { - if (codes[0] == GRPC_BASE64_PAD_BYTE || - codes[1] == GRPC_BASE64_PAD_BYTE) { - gpr_log(GPR_ERROR, "Invalid padding detected."); - goto fail; - } - if (codes[2] == GRPC_BASE64_PAD_BYTE) { - if (codes[3] == GRPC_BASE64_PAD_BYTE) { - /* Double padding. */ - gpr_uint32 packed = (codes[0] << 2) | (codes[1] >> 4); - current[result_size++] = (unsigned char)packed; - } else { - gpr_log(GPR_ERROR, "Invalid padding detected."); - goto fail; - } - } else if (codes[3] == GRPC_BASE64_PAD_BYTE) { - /* Single padding. */ - gpr_uint32 packed = - (codes[0] << 10) | (codes[1] << 4) | (codes[2] >> 2); - current[result_size++] = (unsigned char)(packed >> 8); - current[result_size++] = (unsigned char)(packed); - } else { - /* No padding. */ - gpr_uint32 packed = - (codes[0] << 18) | (codes[1] << 12) | (codes[2] << 6) | codes[3]; - current[result_size++] = (unsigned char)(packed >> 16); - current[result_size++] = (unsigned char)(packed >> 8); - current[result_size++] = (unsigned char)(packed); - } + if (!decode_group(codes, num_codes, current, &result_size)) goto fail; num_codes = 0; } } } - if (num_codes != 0) { - gpr_log(GPR_ERROR, "Invalid base64."); - gpr_slice_unref(result); - return gpr_empty_slice(); + if (num_codes != 0 && + !decode_group(codes, num_codes, current, &result_size)) { + goto fail; } GPR_SLICE_SET_LENGTH(result, result_size); return result; |