Custom implementation of Base64 decoding function.
Compared to the previous implementation based on OpenSSL, the new implementation
has these advantages:
1. Strict and reliable detection of invalid strings, including strings with
less than 4 bytes of garbage at the end;
2. Allows to use Base64 strings without '=' padding.
This commit is contained in:
@@ -170,6 +170,7 @@ NXT_TEST_SRCS=" \
|
||||
src/test/nxt_rbtree1_test.c \
|
||||
src/test/nxt_http_parse_test.c \
|
||||
src/test/nxt_strverscmp_test.c \
|
||||
src/test/nxt_base64_test.c \
|
||||
"
|
||||
|
||||
|
||||
|
||||
@@ -517,8 +517,8 @@ static nxt_int_t
|
||||
nxt_conf_vldt_ticket_key_element(nxt_conf_validation_t *vldt,
|
||||
nxt_conf_value_t *value)
|
||||
{
|
||||
ssize_t ret;
|
||||
nxt_str_t key;
|
||||
nxt_int_t ret;
|
||||
|
||||
if (nxt_conf_type(value) != NXT_CONF_STRING) {
|
||||
return nxt_conf_vldt_error(vldt, "The \"key\" array must "
|
||||
@@ -527,12 +527,8 @@ nxt_conf_vldt_ticket_key_element(nxt_conf_validation_t *vldt,
|
||||
|
||||
nxt_conf_get_string(value, &key);
|
||||
|
||||
ret = nxt_openssl_base64_decode(NULL, 0, key.start, key.length);
|
||||
if (nxt_slow_path(ret == NXT_ERROR)) {
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
if (ret == NXT_DECLINED) {
|
||||
ret = nxt_base64_decode(NULL, key.start, key.length);
|
||||
if (ret == NXT_ERROR) {
|
||||
return nxt_conf_vldt_error(vldt, "Invalid Base64 format for the ticket "
|
||||
"key \"%V\".", &key);
|
||||
}
|
||||
|
||||
@@ -621,8 +621,8 @@ static nxt_int_t
|
||||
nxt_tls_ticket_keys(nxt_task_t *task, SSL_CTX *ctx, nxt_tls_init_t *tls_init,
|
||||
nxt_mp_t *mp)
|
||||
{
|
||||
size_t len;
|
||||
uint32_t i;
|
||||
nxt_int_t ret;
|
||||
nxt_str_t value;
|
||||
nxt_uint_t count;
|
||||
nxt_conf_value_t *member, *tickets_conf;
|
||||
@@ -686,14 +686,11 @@ nxt_tls_ticket_keys(nxt_task_t *task, SSL_CTX *ctx, nxt_tls_init_t *tls_init,
|
||||
|
||||
nxt_conf_get_string(member, &value);
|
||||
|
||||
ret = nxt_openssl_base64_decode(buf, 80, value.start, value.length);
|
||||
if (nxt_slow_path(ret == NXT_ERROR)) {
|
||||
return NXT_ERROR;
|
||||
}
|
||||
len = nxt_base64_decode(buf, value.start, value.length);
|
||||
|
||||
nxt_memcpy(ticket->name, buf, 16);
|
||||
|
||||
if (ret == 48) {
|
||||
if (len == 48) {
|
||||
nxt_memcpy(ticket->aes_key, buf + 16, 16);
|
||||
nxt_memcpy(ticket->hmac_key, buf + 32, 16);
|
||||
ticket->size = 16;
|
||||
@@ -1818,70 +1815,3 @@ nxt_openssl_copy_error(u_char *p, u_char *end)
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
nxt_int_t
|
||||
nxt_openssl_base64_decode(u_char *d, size_t dlen, const u_char *s, size_t slen)
|
||||
{
|
||||
BIO *bio, *b64;
|
||||
nxt_int_t count, ret;
|
||||
u_char buf[128];
|
||||
|
||||
b64 = BIO_new(BIO_f_base64());
|
||||
if (nxt_slow_path(b64 == NULL)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
bio = BIO_new_mem_buf(s, slen);
|
||||
if (nxt_slow_path(bio == NULL)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
bio = BIO_push(b64, bio);
|
||||
|
||||
BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
|
||||
|
||||
count = 0;
|
||||
|
||||
if (d == NULL) {
|
||||
|
||||
for ( ;; ) {
|
||||
ret = BIO_read(bio, buf, 128);
|
||||
|
||||
if (ret < 0) {
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
count += ret;
|
||||
|
||||
if (ret != 128) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
count = BIO_read(bio, d, dlen);
|
||||
|
||||
if (count < 0) {
|
||||
goto invalid;
|
||||
}
|
||||
}
|
||||
|
||||
BIO_free_all(bio);
|
||||
|
||||
return count;
|
||||
|
||||
error:
|
||||
|
||||
BIO_vfree(b64);
|
||||
ERR_clear_error();
|
||||
|
||||
return NXT_ERROR;
|
||||
|
||||
invalid:
|
||||
|
||||
BIO_free_all(bio);
|
||||
ERR_clear_error();
|
||||
|
||||
return NXT_DECLINED;
|
||||
}
|
||||
|
||||
@@ -745,3 +745,100 @@ nxt_is_complex_uri_encoded(u_char *src, size_t length)
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
nxt_base64_decode(u_char *dst, u_char *src, size_t length)
|
||||
{
|
||||
u_char *end, *p;
|
||||
size_t pad;
|
||||
uint8_t v1, v2, v3, v4;
|
||||
|
||||
static const uint8_t decode[] = {
|
||||
77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
|
||||
77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
|
||||
77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 62, 77, 77, 77, 63,
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 77, 77, 77, 77, 77, 77,
|
||||
77, 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, 77, 77, 77, 77, 77,
|
||||
77, 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, 77, 77, 77, 77, 77,
|
||||
|
||||
77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
|
||||
77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
|
||||
77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
|
||||
77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
|
||||
77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
|
||||
77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
|
||||
77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
|
||||
77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77
|
||||
};
|
||||
|
||||
end = src + length;
|
||||
pad = (4 - (length % 4)) % 4;
|
||||
|
||||
if (dst == NULL) {
|
||||
if (pad > 2) {
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
while (src < end) {
|
||||
if (decode[*src] != 77) {
|
||||
src++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pad == 0) {
|
||||
pad = end - src;
|
||||
|
||||
if ((pad == 1 || (pad == 2 && src[1] == '=')) && src[0] == '=')
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
return (length + 3) / 4 * 3 - pad;
|
||||
}
|
||||
|
||||
nxt_assert(length != 0);
|
||||
|
||||
if (pad == 0) {
|
||||
pad = (end[-1] == '=') + (end[-2] == '=');
|
||||
end -= (pad + 3) & 4;
|
||||
|
||||
} else {
|
||||
end -= 4 - pad;
|
||||
}
|
||||
|
||||
p = dst;
|
||||
|
||||
while (src < end) {
|
||||
v1 = decode[src[0]];
|
||||
v2 = decode[src[1]];
|
||||
v3 = decode[src[2]];
|
||||
v4 = decode[src[3]];
|
||||
|
||||
*p++ = (v1 << 2 | v2 >> 4);
|
||||
*p++ = (v2 << 4 | v3 >> 2);
|
||||
*p++ = (v3 << 6 | v4);
|
||||
|
||||
src += 4;
|
||||
}
|
||||
|
||||
if (pad > 0) {
|
||||
v1 = decode[src[0]];
|
||||
v2 = decode[src[1]];
|
||||
|
||||
*p++ = (v1 << 2 | v2 >> 4);
|
||||
|
||||
if (pad == 1) {
|
||||
v3 = decode[src[2]];
|
||||
*p++ = (v2 << 4 | v3 >> 2);
|
||||
}
|
||||
}
|
||||
|
||||
return (p - dst);
|
||||
}
|
||||
|
||||
@@ -190,6 +190,8 @@ NXT_EXPORT uintptr_t nxt_encode_complex_uri(u_char *dst, u_char *src,
|
||||
size_t length);
|
||||
NXT_EXPORT nxt_bool_t nxt_is_complex_uri_encoded(u_char *s, size_t length);
|
||||
|
||||
NXT_EXPORT ssize_t nxt_base64_decode(u_char *dst, u_char *src, size_t length);
|
||||
|
||||
extern const uint8_t nxt_hex2int[256];
|
||||
|
||||
|
||||
|
||||
@@ -98,8 +98,6 @@ extern const nxt_tls_lib_t nxt_openssl_lib;
|
||||
void nxt_cdecl nxt_openssl_log_error(nxt_task_t *task, nxt_uint_t level,
|
||||
const char *fmt, ...);
|
||||
u_char *nxt_openssl_copy_error(u_char *p, u_char *end);
|
||||
nxt_int_t nxt_openssl_base64_decode(u_char *d, size_t dlen, const u_char *s,
|
||||
size_t slen);
|
||||
#endif
|
||||
|
||||
#if (NXT_HAVE_GNUTLS)
|
||||
|
||||
98
src/test/nxt_base64_test.c
Normal file
98
src/test/nxt_base64_test.c
Normal file
@@ -0,0 +1,98 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) NGINX, Inc.
|
||||
*/
|
||||
|
||||
#include <nxt_main.h>
|
||||
#include "nxt_tests.h"
|
||||
|
||||
|
||||
nxt_int_t
|
||||
nxt_base64_test(nxt_thread_t *thr)
|
||||
{
|
||||
ssize_t ret;
|
||||
nxt_uint_t i;
|
||||
|
||||
static struct {
|
||||
nxt_str_t enc;
|
||||
nxt_str_t dec;
|
||||
|
||||
} tests[] = {
|
||||
{ nxt_string("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+//+9876543210"
|
||||
"zyxwvutsrqponmlkjihgfedcba"
|
||||
"ZYXWVUTSRQPONMLKJIHGFEDCBA"),
|
||||
nxt_string("\x00\x10\x83\x10\x51\x87\x20\x92\x8b\x30\xd3\x8f"
|
||||
"\x41\x14\x93\x51\x55\x97\x61\x96\x9b\x71\xd7\x9f"
|
||||
"\x82\x18\xa3\x92\x59\xa7\xa2\x9a\xab\xb2\xdb\xaf"
|
||||
"\xc3\x1c\xb3\xd3\x5d\xb7\xe3\x9e\xbb\xf3\xdf\xbf"
|
||||
"\xff\xef\x7c\xef\xae\x78\xdf\x6d\x74\xcf\x2c\x70"
|
||||
"\xbe\xeb\x6c\xae\xaa\x68\x9e\x69\x64\x8e\x28\x60"
|
||||
"\x7d\xe7\x5c\x6d\xa6\x58\x5d\x65\x54\x4d\x24\x50"
|
||||
"\x3c\xe3\x4c\x2c\xa2\x48\x1c\x61\x44\x0c\x20\x40") },
|
||||
|
||||
{ nxt_string("Aa=="),
|
||||
nxt_string("\x01") },
|
||||
{ nxt_string("0Z"),
|
||||
nxt_string("\xd1") },
|
||||
{ nxt_string("0aA="),
|
||||
nxt_string("\xd1\xa0") },
|
||||
{ nxt_string("z/+"),
|
||||
nxt_string("\xcf\xff") },
|
||||
{ nxt_string("z9+Npe=="),
|
||||
nxt_string("\xcf\xdf\x8d\xa5") },
|
||||
{ nxt_string("/+98765"),
|
||||
nxt_string("\xff\xef\x7c\xef\xae") },
|
||||
|
||||
{ nxt_string("aBc_"),
|
||||
nxt_null_string },
|
||||
{ nxt_string("5"),
|
||||
nxt_null_string },
|
||||
{ nxt_string("M==="),
|
||||
nxt_null_string },
|
||||
{ nxt_string("===="),
|
||||
nxt_null_string },
|
||||
{ nxt_string("Ab="),
|
||||
nxt_null_string },
|
||||
{ nxt_string("00=0"),
|
||||
nxt_null_string },
|
||||
{ nxt_string("\0"),
|
||||
nxt_null_string },
|
||||
{ nxt_string("\r\naaaa"),
|
||||
nxt_null_string },
|
||||
{ nxt_string("=0000"),
|
||||
nxt_null_string },
|
||||
};
|
||||
|
||||
u_char buf[96];
|
||||
|
||||
nxt_thread_time_update(thr);
|
||||
|
||||
for (i = 0; i < nxt_nitems(tests); i++) {
|
||||
ret = nxt_base64_decode(NULL, tests[i].enc.start, tests[i].enc.length);
|
||||
|
||||
if (ret == NXT_ERROR && tests[i].dec.start == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((size_t) ret != tests[i].dec.length) {
|
||||
nxt_log_alert(thr->log,
|
||||
"nxt_base64_decode() test \"%V\" failed: incorrect "
|
||||
"length of decoded string %z, expected %uz",
|
||||
&tests[i].enc, ret, tests[i].dec.length);
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
ret = nxt_base64_decode(buf, tests[i].enc.start, tests[i].enc.length);
|
||||
|
||||
if (!nxt_str_eq(&tests[i].dec, buf, (size_t) ret)) {
|
||||
nxt_log_alert(thr->log, "nxt_base64_decode() test \"%V\" failed");
|
||||
return NXT_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
nxt_log_error(NXT_LOG_NOTICE, thr->log, "nxt_base64_decode() test passed");
|
||||
|
||||
return NXT_OK;
|
||||
}
|
||||
@@ -162,6 +162,10 @@ main(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (nxt_base64_test(thr) != NXT_OK) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if (NXT_HAVE_CLONE_NEWUSER)
|
||||
if (nxt_clone_creds_test(thr) != NXT_OK) {
|
||||
return 1;
|
||||
|
||||
@@ -64,6 +64,7 @@ nxt_int_t nxt_malloc_test(nxt_thread_t *thr);
|
||||
nxt_int_t nxt_utf8_test(nxt_thread_t *thr);
|
||||
nxt_int_t nxt_http_parse_test(nxt_thread_t *thr);
|
||||
nxt_int_t nxt_strverscmp_test(nxt_thread_t *thr);
|
||||
nxt_int_t nxt_base64_test(nxt_thread_t *thr);
|
||||
nxt_int_t nxt_clone_creds_test(nxt_thread_t *thr);
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user