aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core
diff options
context:
space:
mode:
authorGravatar Nicolas Noble <nicolasnoble@users.noreply.github.com>2015-07-01 09:26:21 -0700
committerGravatar Nicolas Noble <nicolasnoble@users.noreply.github.com>2015-07-01 09:26:21 -0700
commit6ab0de15ea219594c702ed1e497ec8b157239c66 (patch)
tree4e317d8620661aeedaa02ee0a8a4f57c44dcd575 /src/core
parent8993307137821dfea441fa69c31b57c4a6b7baf8 (diff)
parent73d8b57a6cea0875af0ccdd34a81f7b7c8822adf (diff)
Merge pull request #2216 from jboeuf/unpadded_base64
Base64 decode improvements
Diffstat (limited to 'src/core')
-rw-r--r--src/core/security/base64.c99
-rw-r--r--src/core/security/base64.h4
2 files changed, 70 insertions, 33 deletions
diff --git a/src/core/security/base64.c b/src/core/security/base64.c
index 3f28c09611..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;
@@ -151,43 +212,15 @@ gpr_slice grpc_base64_decode(const char *b64, int url_safe) {
} else {
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 = (gpr_uint32)((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 =
- (gpr_uint32)((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 =
- (gpr_uint32)((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;
diff --git a/src/core/security/base64.h b/src/core/security/base64.h
index 6a7cd8e96c..b9abc07b52 100644
--- a/src/core/security/base64.h
+++ b/src/core/security/base64.h
@@ -45,4 +45,8 @@ char *grpc_base64_encode(const void *data, size_t data_size, int url_safe,
slice in case of failure. */
gpr_slice grpc_base64_decode(const char *b64, int url_safe);
+/* Same as above except that the length is provided by the caller. */
+gpr_slice grpc_base64_decode_with_len(const char *b64, size_t b64_len,
+ int url_safe);
+
#endif /* GRPC_INTERNAL_CORE_SECURITY_BASE64_H */