Skip to content

Commit 9d1f008

Browse files
64-bitmanchrisbra
authored andcommitted
patch 9.2.0961: base64_encode() gives wrong result for a zero byte
Problem: base64_encode() encodes a zero byte in the last group as padding. Solution: Decide the padding from the number of remaining input bytes, refactor the code and move to misc2.c (Foxe Chen). related: #21018 Signed-off-by: Foxe Chen <chen.foxe@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
1 parent cd59994 commit 9d1f008

5 files changed

Lines changed: 160 additions & 144 deletions

File tree

‎src/evalfunc.c‎

Lines changed: 3 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -3716,148 +3716,6 @@ f_balloon_split(typval_T *argvars, typval_T *rettv UNUSED)
37163716
# endif
37173717
#endif
37183718

3719-
// Base64 character set
3720-
static const char_u base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
3721-
3722-
// Base64 decoding table (initialized in init_base64_dec_table() below)
3723-
static char_u base64_dec_table[256];
3724-
3725-
/*
3726-
* Initialize the base64 decoding table
3727-
*/
3728-
static void
3729-
init_base64_dec_table(void)
3730-
{
3731-
static int base64_dec_tbl_initialized = FALSE;
3732-
3733-
if (base64_dec_tbl_initialized)
3734-
return;
3735-
3736-
// Unsupported characters are set to 0xFF
3737-
vim_memset(base64_dec_table, 0xFF, sizeof(base64_dec_table));
3738-
3739-
// Initialize the index for the base64 alphabets
3740-
for (size_t i = 0; i < sizeof(base64_table) - 1; i++)
3741-
base64_dec_table[(char_u)base64_table[i]] = (char_u)i;
3742-
3743-
// base64 padding character
3744-
base64_dec_table['='] = 0;
3745-
3746-
base64_dec_tbl_initialized = TRUE;
3747-
}
3748-
3749-
/*
3750-
* Encode the bytes in "blob" using base-64 encoding.
3751-
*/
3752-
static char_u *
3753-
base64_encode(blob_T *blob)
3754-
{
3755-
size_t input_len = blob->bv_ga.ga_len;
3756-
size_t encoded_len = ((input_len + 2) / 3) * 4;
3757-
char_u *data = blob->bv_ga.ga_data;
3758-
3759-
char_u *encoded = alloc(encoded_len + 1);
3760-
if (encoded == NULL)
3761-
return NULL;
3762-
3763-
size_t i, j;
3764-
for (i = 0, j = 0; i < input_len;)
3765-
{
3766-
int_u octet_a = i < input_len ? data[i++] : 0;
3767-
int_u octet_b = i < input_len ? data[i++] : 0;
3768-
int_u octet_c = i < input_len ? data[i++] : 0;
3769-
3770-
int_u triple = (octet_a << 16) | (octet_b << 8) | octet_c;
3771-
3772-
encoded[j++] = base64_table[(triple >> 18) & 0x3F];
3773-
encoded[j++] = base64_table[(triple >> 12) & 0x3F];
3774-
encoded[j++] = (!octet_b && i >= input_len) ? '='
3775-
: base64_table[(triple >> 6) & 0x3F];
3776-
encoded[j++] = (!octet_c && i >= input_len) ? '='
3777-
: base64_table[triple & 0x3F];
3778-
}
3779-
encoded[j] = NUL;
3780-
3781-
return encoded;
3782-
}
3783-
3784-
/*
3785-
* Decode the string "data" using base-64 encoding.
3786-
*/
3787-
static void
3788-
base64_decode(const char_u *data, blob_T *blob)
3789-
{
3790-
size_t input_len = STRLEN(data);
3791-
3792-
if (input_len == 0)
3793-
return;
3794-
3795-
if (input_len % 4 != 0)
3796-
{
3797-
// Invalid input length
3798-
semsg(_(e_invalid_argument_str), data);
3799-
return;
3800-
}
3801-
3802-
init_base64_dec_table();
3803-
3804-
size_t decoded_len = (input_len / 4) * 3;
3805-
if (data[input_len - 1] == '=')
3806-
decoded_len--;
3807-
if (data[input_len - 2] == '=')
3808-
decoded_len--;
3809-
3810-
size_t i, j;
3811-
for (i = 0, j = 0; i < input_len;)
3812-
{
3813-
int_u sextet_a = base64_dec_table[(char_u)data[i++]];
3814-
int_u sextet_b = base64_dec_table[(char_u)data[i++]];
3815-
int_u sextet_c = base64_dec_table[(char_u)data[i++]];
3816-
int_u sextet_d = base64_dec_table[(char_u)data[i++]];
3817-
3818-
if (sextet_a == 0xFF || sextet_b == 0xFF || sextet_c == 0xFF
3819-
|| sextet_d == 0xFF)
3820-
{
3821-
// Invalid character
3822-
semsg(_(e_invalid_argument_str), data);
3823-
ga_clear(&blob->bv_ga);
3824-
return;
3825-
}
3826-
3827-
int_u triple = (sextet_a << 18) | (sextet_b << 12)
3828-
| (sextet_c << 6) | sextet_d;
3829-
3830-
if (j < decoded_len)
3831-
{
3832-
ga_append(&blob->bv_ga, (triple >> 16) & 0xFF);
3833-
j++;
3834-
}
3835-
if (j < decoded_len)
3836-
{
3837-
ga_append(&blob->bv_ga, (triple >> 8) & 0xFF);
3838-
j++;
3839-
}
3840-
if (j < decoded_len)
3841-
{
3842-
ga_append(&blob->bv_ga, triple & 0xFF);
3843-
j++;
3844-
}
3845-
3846-
if (j == decoded_len)
3847-
{
3848-
// Check for invalid padding bytes (based on the
3849-
// "Base64 Malleability in Practice" ACM paper).
3850-
if ((data[input_len - 2] == '=' && ((sextet_b & 0xF) != 0))
3851-
|| ((data[input_len - 1] == '=') && ((sextet_c & 0x3) != 0)))
3852-
{
3853-
semsg(_(e_invalid_argument_str), data);
3854-
ga_clear(&blob->bv_ga);
3855-
return;
3856-
}
3857-
}
3858-
}
3859-
}
3860-
38613719
/*
38623720
* "base64_decode(string)" function
38633721
*/
@@ -3872,7 +3730,7 @@ f_base64_decode(typval_T *argvars, typval_T *rettv)
38723730

38733731
char_u *str = tv_get_string_chk(&argvars[0]);
38743732
if (str != NULL)
3875-
base64_decode(str, rettv->vval.v_blob);
3733+
base64_decode(str, STRLEN(str), &rettv->vval.v_blob->bv_ga);
38763734
}
38773735

38783736
/*
@@ -3889,7 +3747,8 @@ f_base64_encode(typval_T *argvars, typval_T *rettv)
38893747

38903748
blob_T *blob = argvars->vval.v_blob;
38913749
if (blob != NULL)
3892-
rettv->vval.v_string = base64_encode(blob);
3750+
rettv->vval.v_string =
3751+
base64_encode(blob->bv_ga.ga_data, blob->bv_ga.ga_len);
38933752
}
38943753

38953754
/*

‎src/misc2.c‎

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3316,3 +3316,152 @@ mergesort_list(
33163316

33173317
return head;
33183318
}
3319+
3320+
static const char_u base64_table[] =
3321+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
3322+
3323+
static char_u base64_dec_table[256];
3324+
3325+
#define BASE64_ENCODED_LEN(len) ((((len) + 2) / 3) * 4 + 1)
3326+
3327+
/*
3328+
* Initialize the base64 decoding table
3329+
*/
3330+
static void
3331+
init_base64_dec_table(void)
3332+
{
3333+
static int base64_dec_tbl_initialized = FALSE;
3334+
3335+
if (base64_dec_tbl_initialized)
3336+
return;
3337+
3338+
vim_memset(base64_dec_table, 0xFF, sizeof(base64_dec_table));
3339+
for (size_t i = 0; i < sizeof(base64_table) - 1; i++)
3340+
base64_dec_table[(char_u)base64_table[i]] = (char_u)i;
3341+
base64_dec_table['='] = 0;
3342+
3343+
base64_dec_tbl_initialized = TRUE;
3344+
}
3345+
3346+
/*
3347+
* Base64 encode "src[len]" into caller-supplied "dst", which must have room for
3348+
* at least BASE64_ENCODED_LEN(len) bytes (NUL terminated). Returns the number
3349+
* of bytes written, excluding the NUL.
3350+
*/
3351+
long
3352+
base64_encode_buf(char_u *dst, const char_u *src, size_t len)
3353+
{
3354+
size_t i, j;
3355+
3356+
for (i = 0, j = 0; i < len;)
3357+
{
3358+
size_t remaining = len - i;
3359+
3360+
int_u octet_a = src[i++];
3361+
int_u octet_b = remaining > 1 ? src[i++] : 0;
3362+
int_u octet_c = remaining > 2 ? src[i++] : 0;
3363+
3364+
int_u triple = (octet_a << 16) | (octet_b << 8) | octet_c;
3365+
3366+
dst[j++] = base64_table[(triple >> 18) & 0x3F];
3367+
dst[j++] = base64_table[(triple >> 12) & 0x3F];
3368+
dst[j++] = remaining > 1
3369+
? base64_table[(triple >> 6) & 0x3F]
3370+
: '=';
3371+
dst[j++] = remaining > 2
3372+
? base64_table[triple & 0x3F]
3373+
: '=';
3374+
}
3375+
3376+
dst[j] = NUL;
3377+
return (long)j;
3378+
}
3379+
3380+
/*
3381+
* Base64-encode "data[len]". Returns an allocated NUL-terminated string, or
3382+
* NULL on OOM. Caller frees with vim_free().
3383+
*/
3384+
char_u *
3385+
base64_encode(const char_u *data, size_t len)
3386+
{
3387+
char_u *encoded = alloc(BASE64_ENCODED_LEN(len));
3388+
3389+
if (encoded == NULL)
3390+
return NULL;
3391+
base64_encode_buf(encoded, data, len);
3392+
return encoded;
3393+
}
3394+
3395+
/*
3396+
* Decode base64 text "data[len]" (len must be a multiple of 4) into growarray
3397+
* "out" (appended byte-by-byte, e.g. a blob's bv_ga). Returns OK on success and
3398+
* FAIL on failure.
3399+
*/
3400+
int
3401+
base64_decode(const char_u *data, size_t len, garray_T *out)
3402+
{
3403+
if (len == 0)
3404+
return OK;
3405+
3406+
if (len % 4 != 0)
3407+
{
3408+
semsg(_(e_invalid_argument_str), data);
3409+
return FAIL;
3410+
}
3411+
3412+
init_base64_dec_table();
3413+
3414+
size_t decoded_len = (len / 4) * 3;
3415+
if (data[len - 1] == '=')
3416+
decoded_len--;
3417+
if (data[len - 2] == '=')
3418+
decoded_len--;
3419+
3420+
size_t i, j;
3421+
for (i = 0, j = 0; i < len;)
3422+
{
3423+
int_u sextet_a = base64_dec_table[(char_u)data[i++]];
3424+
int_u sextet_b = base64_dec_table[(char_u)data[i++]];
3425+
int_u sextet_c = base64_dec_table[(char_u)data[i++]];
3426+
int_u sextet_d = base64_dec_table[(char_u)data[i++]];
3427+
3428+
if (sextet_a == 0xFF || sextet_b == 0xFF || sextet_c == 0xFF
3429+
|| sextet_d == 0xFF)
3430+
{
3431+
semsg(_(e_invalid_argument_str), data);
3432+
ga_clear(out);
3433+
return FAIL;
3434+
}
3435+
3436+
int_u triple = (sextet_a << 18) | (sextet_b << 12)
3437+
| (sextet_c << 6) | sextet_d;
3438+
3439+
if (j < decoded_len)
3440+
{
3441+
ga_append(out, (triple >> 16) & 0xFF);
3442+
j++;
3443+
}
3444+
if (j < decoded_len)
3445+
{
3446+
ga_append(out, (triple >> 8) & 0xFF);
3447+
j++;
3448+
}
3449+
if (j < decoded_len)
3450+
{
3451+
ga_append(out, triple & 0xFF);
3452+
j++;
3453+
}
3454+
3455+
if (j == decoded_len)
3456+
{
3457+
if ((data[len - 2] == '=' && ((sextet_b & 0xF) != 0))
3458+
|| ((data[len - 1] == '=') && ((sextet_c & 0x3) != 0)))
3459+
{
3460+
semsg(_(e_invalid_argument_str), data);
3461+
ga_clear(out);
3462+
return FAIL;
3463+
}
3464+
}
3465+
}
3466+
return OK;
3467+
}

‎src/proto/misc2.pro‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,7 @@ int cmp_keyvalue_value_n(const void *a, const void *b);
6666
int cmp_keyvalue_value_i(const void *a, const void *b);
6767
int cmp_keyvalue_value_ni(const void *a, const void *b);
6868
void *mergesort_list(void *head, void *(*get_next)(void *), void (*set_next)(void *, void *), void *(*get_prev)(void *), void (*set_prev)(void *, void *), int (*compare)(const void *, const void *));
69+
long base64_encode_buf(char_u *dst, const char_u *src, size_t len);
70+
char_u *base64_encode(const char_u *data, size_t len);
71+
int base64_decode(const char_u *data, size_t len, garray_T *out);
6972
/* vim: set ft=c : */

‎src/testdir/test_functions.vim‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4601,6 +4601,9 @@ func Test_base64_encoding()
46014601
call assert_equal(0z00, base64_decode("===="))
46024602
call assert_equal(0z, base64_decode(""))
46034603

4604+
#" a zero byte in the last group is not padding
4605+
call assert_equal('AQAC', base64_encode(0z010002))
4606+
46044607
#" Test for invalid padding
46054608
call assert_equal('Hello', g:Blob2Str(base64_decode("SGVsbG8=")))
46064609
call assert_fails('call base64_decode("SGVsbG9=")', 'E475:')

‎src/version.c‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,8 @@ static char *(features[]) =
763763

764764
static int included_patches[] =
765765
{ /* Add new patch number below this line */
766+
/**/
767+
961,
766768
/**/
767769
960,
768770
/**/

0 commit comments

Comments
 (0)