Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -252,3 +252,7 @@ t_test_str_to_rrtype_LDADD = wdns/libwdns.la
TESTS += t/test-fast_inet_ntop
check_PROGRAMS += t/test-fast_inet_ntop
t_test_fast_inet_ntop_SOURCES = t/test-fast_inet_ntop.c t/test-common.c

TESTS += t/test-b64_decode
check_PROGRAMS += t/test-b64_decode
t_test_b64_decode_SOURCES = t/test-b64_decode.c t/test-common.c
20 changes: 10 additions & 10 deletions libmy/b64_decode.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ For details, see http://sourceforge.net/projects/libb64

int base64_decode_value(char value_in)
{
static const char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51};
static const char decoding_size = sizeof(decoding);
value_in -= 43;
if (value_in < 0 || value_in > decoding_size) return -1;
return decoding[(int)value_in];
static const signed char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51};
static const int decoding_size = sizeof(decoding);
int offset = (int) (unsigned char) value_in - 43;
if (offset < 0 || offset >= decoding_size) return -1;
return decoding[offset];
}

void base64_init_decodestate(base64_decodestate* state_in)
Expand All @@ -26,7 +26,7 @@ int base64_decode_block(const char* code_in, const int length_in, char* plaintex
{
const char* codechar = code_in;
char* plainchar = plaintext_out;
char fragment;
int fragment;

*plainchar = state_in->plainchar;

Expand All @@ -42,7 +42,7 @@ int base64_decode_block(const char* code_in, const int length_in, char* plaintex
state_in->plainchar = *plainchar;
return plainchar - plaintext_out;
}
fragment = (char)base64_decode_value(*codechar++);
fragment = base64_decode_value(*codechar++);
} while (fragment < 0);
*plainchar = (fragment & 0x03f) << 2;
case step_b:
Expand All @@ -53,7 +53,7 @@ int base64_decode_block(const char* code_in, const int length_in, char* plaintex
state_in->plainchar = *plainchar;
return plainchar - plaintext_out;
}
fragment = (char)base64_decode_value(*codechar++);
fragment = base64_decode_value(*codechar++);
} while (fragment < 0);
*plainchar++ |= (fragment & 0x030) >> 4;
*plainchar = (fragment & 0x00f) << 4;
Expand All @@ -65,7 +65,7 @@ int base64_decode_block(const char* code_in, const int length_in, char* plaintex
state_in->plainchar = *plainchar;
return plainchar - plaintext_out;
}
fragment = (char)base64_decode_value(*codechar++);
fragment = base64_decode_value(*codechar++);
} while (fragment < 0);
*plainchar++ |= (fragment & 0x03c) >> 2;
*plainchar = (fragment & 0x003) << 6;
Expand All @@ -77,7 +77,7 @@ int base64_decode_block(const char* code_in, const int length_in, char* plaintex
state_in->plainchar = *plainchar;
return plainchar - plaintext_out;
}
fragment = (char)base64_decode_value(*codechar++);
fragment = base64_decode_value(*codechar++);
} while (fragment < 0);
*plainchar++ |= (fragment & 0x03f);
}
Expand Down
140 changes: 140 additions & 0 deletions t/test-b64_decode.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/*
* Copyright (c) 2026 DomainTools LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "test-common.h"

#include "libmy/b64_decode.h"
#include "libmy/b64_decode.c"

#include <string.h>

#define NAME "test-b64_decode"

/*
* base64_decode_value() must report every non-alphabet octet as negative,
* whatever the sign of a plain char is on this platform. base64_decode_block()
* relies on that to skip padding and whitespace.
*/
static size_t
test_decode_value(void) {
size_t failures = 0;
size_t i;
static const struct {
char input;
int expected;
} testdata[] = {
{ 'A', 0 },
{ 'Z', 25 },
{ 'a', 26 },
{ 'z', 51 },
{ '0', 52 },
{ '9', 61 },
{ '+', 62 },
{ '/', 63 },
{ '=', -2 },
{ ' ', -1 },
{ '\t', -1 },
{ '\n', -1 },
{ '\r', -1 },
{ '-', -1 },
{ '{', -1 }, /* one past the end of the decoding table */
{ '\x7f', -1 },
{ '\xff', -1 },
};

for (i = 0; i < sizeof(testdata)/sizeof(testdata[0]); i++) {
int res = base64_decode_value(testdata[i].input);

if (res == testdata[i].expected) {
fprintf(stderr, "PASS %zu: base64_decode_value(0x%02x) = %d\n",
i, (unsigned char) testdata[i].input, res);
} else {
fprintf(stderr, "FAIL %zu: base64_decode_value(0x%02x) = %d != %d\n",
i, (unsigned char) testdata[i].input, res,
testdata[i].expected);
failures++;
}
}

return (failures);
}

/*
* Decoding must ignore padding and embedded whitespace. DNSKEY, CDNSKEY,
* RRSIG and OPENPGPKEY presentation format all carry both.
*/
static size_t
test_decode_block(void) {
size_t failures = 0;
size_t i;
static const struct {
const char *input;
const char *expected;
size_t elen;
} testdata[] = {
{ "AQIDBAUGBwg=", "\x01\x02\x03\x04\x05\x06\x07\x08", 8 },
{ "ZGVhZGJlZWY=", "deadbeef", 8 },
{ "ZGVhZGJlZWY==", "deadbeef", 8 },
{ "ZGVh ZGJl ZWY=", "deadbeef", 8 },
{ "ZGVhZGJl\nZWY=", "deadbeef", 8 },
{ "ZGVhZGJl\r\n\tZWY=", "deadbeef", 8 },
{ " ZGVhZGJlZWY= ", "deadbeef", 8 },
{ "AA==", "\x00", 1 },
{ "", "", 0 },
};

for (i = 0; i < sizeof(testdata)/sizeof(testdata[0]); i++) {
base64_decodestate b64;
char buf[64];
int len;

memset(buf, 0, sizeof(buf));
base64_init_decodestate(&b64);
len = base64_decode_block(testdata[i].input,
strlen(testdata[i].input),
buf, &b64);

if (len == (int) testdata[i].elen &&
memcmp(buf, testdata[i].expected, testdata[i].elen) == 0)
{
fprintf(stderr, "PASS %zu: base64_decode_block(\"%s\") len %d\n",
i, testdata[i].input, len);
} else {
ubuf *u = ubuf_init(64);

escape(u, (const uint8_t *) buf, len < 0 ? 0 : (size_t) len);
fprintf(stderr, "FAIL %zu: base64_decode_block(\"%s\") len %d != %zu value=%s\n",
i, testdata[i].input, len, testdata[i].elen,
ubuf_cstr(u));
ubuf_destroy(&u);
failures++;
}
}

return (failures);
}

int main (void) {
int ret = 0;

ret |= check(test_decode_value(), "test_decode_value", NAME);
ret |= check(test_decode_block(), "test_decode_block", NAME);

if (ret)
return (EXIT_FAILURE);

return (EXIT_SUCCESS);
}