Merge pull request #365 from libtom/pr/base64-decode-less-relaxed
Make base64_decode relaxed mode less relaxed
This commit is contained in:
commit
ef1fba20b7
@ -6549,7 +6549,8 @@ int base64_decode( const char *in,
|
||||
unsigned long *outlen);
|
||||
\end{verbatim}
|
||||
|
||||
The function \textit{base64\_decode} works in a relaxed way which allows decoding some inputs that do not strictly follow the standard.
|
||||
The function \textit{base64\_decode} works in a dangerously relaxed way which allows decoding some inputs that do not strictly follow the standard.
|
||||
|
||||
If you want to be strict during decoding you can use:
|
||||
\index{base64\_strict\_decode()}
|
||||
\begin{verbatim}
|
||||
@ -6559,6 +6560,16 @@ int base64_strict_decode( const char *in,
|
||||
unsigned long *outlen);
|
||||
\end{verbatim}
|
||||
|
||||
There is also so called sane mode that ignores white-spaces (\textit{CR}, \textit{LF}, \textit{TAB}, \textit{space}),
|
||||
does not care about trailing \textit{=} and also ignores the last input byte in case it is \textit{NUL}.
|
||||
\index{base64\_sane\_decode()}
|
||||
\begin{verbatim}
|
||||
int base64_sane_decode( const char *in,
|
||||
unsigned long len,
|
||||
unsigned char *out,
|
||||
unsigned long *outlen);
|
||||
\end{verbatim}
|
||||
|
||||
\subsection{URL--safe 'base64url' encoding}
|
||||
The characters used in the mappings are:
|
||||
\begin{verbatim}
|
||||
@ -6567,6 +6578,11 @@ ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_
|
||||
Those characters are sometimes also called URL and filename safe alphabet.
|
||||
The interface is analogous to \textit{base64\_xxxx} functions in previous chapter.
|
||||
|
||||
\index{base64url\_encode()}
|
||||
\index{base64url\_strict\_encode()}
|
||||
\index{base64url\_decode()}
|
||||
\index{base64url\_strict\_decode()}
|
||||
\index{base64url\_sane\_decode()}
|
||||
\begin{verbatim}
|
||||
int base64url_encode(const unsigned char *in, unsigned long len,
|
||||
char *out, unsigned long *outlen);
|
||||
@ -6579,6 +6595,9 @@ int base64url_decode( const char *in, unsigned long len,
|
||||
|
||||
int base64url_strict_decode( const char *in, unsigned long len,
|
||||
unsigned char *out, unsigned long *outlen);
|
||||
|
||||
int base64url_sane_decode( const char *in, unsigned long len,
|
||||
unsigned char *out, unsigned long *outlen);
|
||||
\end{verbatim}
|
||||
|
||||
\mysection{Base32 Encoding and Decoding}
|
||||
|
@ -16,6 +16,8 @@ int base64_decode(const char *in, unsigned long len,
|
||||
unsigned char *out, unsigned long *outlen);
|
||||
int base64_strict_decode(const char *in, unsigned long len,
|
||||
unsigned char *out, unsigned long *outlen);
|
||||
int base64_sane_decode(const char *in, unsigned long inlen,
|
||||
unsigned char *out, unsigned long *outlen);
|
||||
#endif
|
||||
|
||||
#ifdef LTC_BASE64_URL
|
||||
@ -28,6 +30,8 @@ int base64url_decode(const char *in, unsigned long len,
|
||||
unsigned char *out, unsigned long *outlen);
|
||||
int base64url_strict_decode(const char *in, unsigned long len,
|
||||
unsigned char *out, unsigned long *outlen);
|
||||
int base64url_sane_decode(const char *in, unsigned long inlen,
|
||||
unsigned char *out, unsigned long *outlen);
|
||||
#endif
|
||||
|
||||
/* ---- BASE32 Routines ---- */
|
||||
|
@ -17,11 +17,16 @@
|
||||
|
||||
#if defined(LTC_BASE64) || defined (LTC_BASE64_URL)
|
||||
|
||||
/* 253 - ignored in "relaxed" + "insane" mode: TAB(9), CR(13), LF(10), space(32)
|
||||
* 254 - padding character '=' (allowed only at the end)
|
||||
* 255 - ignored in "insane" mode, but not allowed in "relaxed" + "strict" mode
|
||||
*/
|
||||
|
||||
#if defined(LTC_BASE64)
|
||||
static const unsigned char map_base64[256] = {
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 253, 255,
|
||||
255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 253, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255,
|
||||
255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6,
|
||||
@ -45,9 +50,9 @@ static const unsigned char map_base64[256] = {
|
||||
|
||||
static const unsigned char map_base64url[] = {
|
||||
#if defined(LTC_BASE64_URL)
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 253, 255,
|
||||
255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 253, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255,
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255,
|
||||
255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6,
|
||||
@ -71,13 +76,14 @@ static const unsigned char map_base64url[] = {
|
||||
};
|
||||
|
||||
enum {
|
||||
relaxed = 0,
|
||||
strict = 1
|
||||
insane = 0,
|
||||
strict = 1,
|
||||
relaxed = 2
|
||||
};
|
||||
|
||||
static int _base64_decode_internal(const char *in, unsigned long inlen,
|
||||
unsigned char *out, unsigned long *outlen,
|
||||
const unsigned char *map, int is_strict)
|
||||
const unsigned char *map, int mode)
|
||||
{
|
||||
unsigned long t, x, y, z;
|
||||
unsigned char c;
|
||||
@ -89,20 +95,29 @@ static int _base64_decode_internal(const char *in, unsigned long inlen,
|
||||
|
||||
g = 0; /* '=' counter */
|
||||
for (x = y = z = t = 0; x < inlen; x++) {
|
||||
if ((in[x] == 0) && (x == (inlen - 1)) && (mode != strict)) {
|
||||
continue; /* allow the last byte to be NUL (relaxed+insane) */
|
||||
}
|
||||
c = map[(unsigned char)in[x]&0xFF];
|
||||
if (c == 254) {
|
||||
g++;
|
||||
continue;
|
||||
}
|
||||
else if (is_strict && g > 0) {
|
||||
/* we only allow '=' to be at the end */
|
||||
return CRYPT_INVALID_PACKET;
|
||||
}
|
||||
if (c == 255) {
|
||||
if (is_strict)
|
||||
if (c == 253) {
|
||||
if (mode == strict)
|
||||
return CRYPT_INVALID_PACKET;
|
||||
else
|
||||
continue;
|
||||
continue; /* allow to ignore white-spaces (relaxed+insane) */
|
||||
}
|
||||
if (c == 255) {
|
||||
if (mode == insane)
|
||||
continue; /* allow to ignore invalid garbage (insane) */
|
||||
else
|
||||
return CRYPT_INVALID_PACKET;
|
||||
}
|
||||
if ((g > 0) && (mode != insane)) {
|
||||
/* we only allow '=' to be at the end (strict+relaxed) */
|
||||
return CRYPT_INVALID_PACKET;
|
||||
}
|
||||
|
||||
t = (t<<6)|c;
|
||||
@ -118,7 +133,7 @@ static int _base64_decode_internal(const char *in, unsigned long inlen,
|
||||
|
||||
if (y != 0) {
|
||||
if (y == 1) return CRYPT_INVALID_PACKET;
|
||||
if ((y + g) != 4 && is_strict && map != map_base64url) return CRYPT_INVALID_PACKET;
|
||||
if (((y + g) != 4) && (mode == strict) && (map != map_base64url)) return CRYPT_INVALID_PACKET;
|
||||
t = t << (6 * (4 - y));
|
||||
if (z + y - 1 > *outlen) return CRYPT_BUFFER_OVERFLOW;
|
||||
if (y >= 2) out[z++] = (unsigned char) ((t >> 16) & 255);
|
||||
@ -130,7 +145,7 @@ static int _base64_decode_internal(const char *in, unsigned long inlen,
|
||||
|
||||
#if defined(LTC_BASE64)
|
||||
/**
|
||||
Relaxed base64 decode a block of memory
|
||||
Dangerously relaxed base64 decode a block of memory
|
||||
@param in The base64 data to decode
|
||||
@param inlen The length of the base64 data
|
||||
@param out [out] The destination of the binary decoded data
|
||||
@ -140,7 +155,7 @@ static int _base64_decode_internal(const char *in, unsigned long inlen,
|
||||
int base64_decode(const char *in, unsigned long inlen,
|
||||
unsigned char *out, unsigned long *outlen)
|
||||
{
|
||||
return _base64_decode_internal(in, inlen, out, outlen, map_base64, relaxed);
|
||||
return _base64_decode_internal(in, inlen, out, outlen, map_base64, insane);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -156,11 +171,25 @@ int base64_strict_decode(const char *in, unsigned long inlen,
|
||||
{
|
||||
return _base64_decode_internal(in, inlen, out, outlen, map_base64, strict);
|
||||
}
|
||||
|
||||
/**
|
||||
Sane base64 decode a block of memory
|
||||
@param in The base64 data to decode
|
||||
@param inlen The length of the base64 data
|
||||
@param out [out] The destination of the binary decoded data
|
||||
@param outlen [in/out] The max size and resulting size of the decoded data
|
||||
@return CRYPT_OK if successful
|
||||
*/
|
||||
int base64_sane_decode(const char *in, unsigned long inlen,
|
||||
unsigned char *out, unsigned long *outlen)
|
||||
{
|
||||
return _base64_decode_internal(in, inlen, out, outlen, map_base64, relaxed);
|
||||
}
|
||||
#endif /* LTC_BASE64 */
|
||||
|
||||
#if defined(LTC_BASE64_URL)
|
||||
/**
|
||||
Relaxed base64 (URL Safe, RFC 4648 section 5) decode a block of memory
|
||||
Dangerously relaxed base64 (URL Safe, RFC 4648 section 5) decode a block of memory
|
||||
@param in The base64 data to decode
|
||||
@param inlen The length of the base64 data
|
||||
@param out [out] The destination of the binary decoded data
|
||||
@ -170,7 +199,7 @@ int base64_strict_decode(const char *in, unsigned long inlen,
|
||||
int base64url_decode(const char *in, unsigned long inlen,
|
||||
unsigned char *out, unsigned long *outlen)
|
||||
{
|
||||
return _base64_decode_internal(in, inlen, out, outlen, map_base64url, relaxed);
|
||||
return _base64_decode_internal(in, inlen, out, outlen, map_base64url, insane);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -186,6 +215,20 @@ int base64url_strict_decode(const char *in, unsigned long inlen,
|
||||
{
|
||||
return _base64_decode_internal(in, inlen, out, outlen, map_base64url, strict);
|
||||
}
|
||||
|
||||
/**
|
||||
Sane base64 (URL Safe, RFC 4648 section 5) decode a block of memory
|
||||
@param in The base64 data to decode
|
||||
@param inlen The length of the base64 data
|
||||
@param out [out] The destination of the binary decoded data
|
||||
@param outlen [in/out] The max size and resulting size of the decoded data
|
||||
@return CRYPT_OK if successful
|
||||
*/
|
||||
int base64url_sane_decode(const char *in, unsigned long inlen,
|
||||
unsigned char *out, unsigned long *outlen)
|
||||
{
|
||||
return _base64_decode_internal(in, inlen, out, outlen, map_base64url, relaxed);
|
||||
}
|
||||
#endif /* LTC_BASE64_URL */
|
||||
|
||||
#endif
|
||||
|
@ -9,6 +9,8 @@
|
||||
#include <tomcrypt_test.h>
|
||||
|
||||
#if defined(LTC_BASE64) || defined(LTC_BASE64_URL)
|
||||
enum { insane = 0, strict = 1, relaxed = 2, invalid = 666 };
|
||||
|
||||
int base64_test(void)
|
||||
{
|
||||
unsigned char in[64], tmp[64];
|
||||
@ -47,39 +49,68 @@ int base64_test(void)
|
||||
#ifdef LTC_BASE64_URL
|
||||
const struct {
|
||||
const char* s;
|
||||
int is_strict;
|
||||
int flag;
|
||||
} url_cases[] = {
|
||||
{"vuiSPKIl8PiR5O-rC4z9_xTQKZ0", 0},
|
||||
{"vuiSPKIl8PiR5O-rC4z9_xTQKZ0=", 1},
|
||||
{"vuiS*PKIl8P*iR5O-rC4*z9_xTQKZ0", 0},
|
||||
{"vuiS*PKIl8P*iR5O-rC4*z9_xTQKZ0=", 0},
|
||||
{"vuiS*PKIl8P*iR5O-rC4*z9_xTQKZ0==", 0},
|
||||
{"vuiS*PKIl8P*iR5O-rC4*z9_xTQKZ0===", 0},
|
||||
{"vuiS*PKIl8P*iR5O-rC4*z9_xTQKZ0====", 0},
|
||||
{"vuiS*=PKIl8P*iR5O-rC4*z9_xTQKZ0=", 0},
|
||||
{"vuiS*==PKIl8P*iR5O-rC4*z9_xTQKZ0=", 0},
|
||||
{"vuiS*===PKIl8P*iR5O-rC4*z9_xTQKZ0=", 0},
|
||||
{"vuiSPKIl8PiR5O-rC4z9_xTQKZ0", strict}, /* 0 */
|
||||
{"vuiSPKIl8PiR5O-rC4z9_xTQKZ0=", strict},
|
||||
{"vuiS*PKIl8P*iR5O-rC4*z9_xTQKZ0", insane},
|
||||
{"vuiS*PKIl8P*iR5O-rC4*z9_xTQKZ0=", insane},
|
||||
{"vuiS*PKIl8P*iR5O-rC4*z9_xTQKZ0==", insane},
|
||||
{"vuiS*PKIl8P*iR5O-rC4*z9_xTQKZ0===", insane}, /* 5 */
|
||||
{"vuiS*PKIl8P*iR5O-rC4*z9_xTQKZ0====", insane},
|
||||
{"vuiS*=PKIl8P*iR5O-rC4*z9_xTQKZ0=", insane},
|
||||
{"vuiS*==PKIl8P*iR5O-rC4*z9_xTQKZ0=", insane},
|
||||
{"vuiS*==\xffPKIl8P*iR5O-rC4*z9_xTQKZ0=", insane},
|
||||
{"vuiS PKIl8P\niR5O-rC4\tz9_xTQKZ0", relaxed}, /* 10 */
|
||||
{"vuiS PKIl8P\niR5O-rC4\tz9_xTQKZ0=", relaxed},
|
||||
{"vuiS PKIl8P\niR5O-rC4\tz9_xTQKZ0==", relaxed},
|
||||
{"vuiS PKIl8P\niR5O-rC4\tz9_xTQKZ0===", relaxed},
|
||||
{"vuiS PKIl8P\niR5O-rC4\tz9_xTQKZ0====", relaxed},
|
||||
{"vuiS\rPKIl8P\niR5O-rC4\tz9_xTQKZ0=", relaxed}, /* 15 */
|
||||
{"vuiS\rPKIl8P\niR5O-rC4\tz9_xTQKZ0= = =\x00", relaxed},
|
||||
{"\nvuiS\rPKIl8P\niR5O-rC4\tz9_xTQKZ0=\n", relaxed},
|
||||
{"vuiSPKIl8PiR5O-rC4z9_xTQK", invalid},
|
||||
};
|
||||
|
||||
for (x = 0; x < sizeof(url_cases)/sizeof(url_cases[0]); ++x) {
|
||||
slen1 = strlen(url_cases[x].s);
|
||||
l1 = sizeof(tmp);
|
||||
if(url_cases[x].is_strict)
|
||||
if(url_cases[x].flag == strict) {
|
||||
DO(base64url_strict_decode(url_cases[x].s, slen1, tmp, &l1));
|
||||
else
|
||||
DO(do_compare_testvector(tmp, l1, special_case, sizeof(special_case) - 1, "base64url_strict_decode", x));
|
||||
DO(base64url_sane_decode(url_cases[x].s, slen1, tmp, &l1));
|
||||
DO(do_compare_testvector(tmp, l1, special_case, sizeof(special_case) - 1, "base64url_sane_decode/strict", x));
|
||||
DO(base64url_decode(url_cases[x].s, slen1, tmp, &l1));
|
||||
DO(do_compare_testvector(tmp, l1, special_case, sizeof(special_case) - 1, "base64url decode", x));
|
||||
if(x < 2) {
|
||||
l2 = sizeof(out);
|
||||
if(x == 0)
|
||||
DO(base64url_encode(tmp, l1, out, &l2));
|
||||
else
|
||||
DO(base64url_strict_encode(tmp, l1, out, &l2));
|
||||
DO(do_compare_testvector(out, l2, url_cases[x].s, strlen(url_cases[x].s), "base64url encode", x));
|
||||
DO(do_compare_testvector(tmp, l1, special_case, sizeof(special_case) - 1, "base64url_decode/strict", x));
|
||||
}
|
||||
else if(url_cases[x].flag == relaxed) {
|
||||
DO(base64url_strict_decode(url_cases[x].s, slen1, tmp, &l1) == CRYPT_INVALID_PACKET ? CRYPT_OK : CRYPT_FAIL_TESTVECTOR);
|
||||
DO(base64url_sane_decode(url_cases[x].s, slen1, tmp, &l1));
|
||||
DO(do_compare_testvector(tmp, l1, special_case, sizeof(special_case) - 1, "base64url_sane_decode/relaxed", x));
|
||||
DO(base64url_decode(url_cases[x].s, slen1, tmp, &l1));
|
||||
DO(do_compare_testvector(tmp, l1, special_case, sizeof(special_case) - 1, "base64url_decode/relaxed", x));
|
||||
}
|
||||
else if(url_cases[x].flag == insane) {
|
||||
DO(base64url_strict_decode(url_cases[x].s, slen1, tmp, &l1) == CRYPT_INVALID_PACKET ? CRYPT_OK : CRYPT_FAIL_TESTVECTOR);
|
||||
DO(base64url_sane_decode(url_cases[x].s, slen1, tmp, &l1) == CRYPT_INVALID_PACKET ? CRYPT_OK : CRYPT_FAIL_TESTVECTOR);
|
||||
DO(base64url_decode(url_cases[x].s, slen1, tmp, &l1));
|
||||
DO(do_compare_testvector(tmp, l1, special_case, sizeof(special_case) - 1, "base64url_decode/insane", x));
|
||||
}
|
||||
else { /* invalid */
|
||||
DO(base64url_strict_decode(url_cases[x].s, slen1, tmp, &l1) == CRYPT_INVALID_PACKET ? CRYPT_OK : CRYPT_FAIL_TESTVECTOR);
|
||||
DO(base64url_sane_decode(url_cases[x].s, slen1, tmp, &l1) == CRYPT_INVALID_PACKET ? CRYPT_OK : CRYPT_FAIL_TESTVECTOR);
|
||||
DO(base64url_decode(url_cases[x].s, slen1, tmp, &l1) == CRYPT_INVALID_PACKET ? CRYPT_OK : CRYPT_FAIL_TESTVECTOR);
|
||||
}
|
||||
l2 = sizeof(out);
|
||||
if(x == 0) {
|
||||
DO(base64url_encode(tmp, l1, out, &l2));
|
||||
DO(do_compare_testvector(out, l2, url_cases[x].s, strlen(url_cases[x].s), "base64url_encode", x));
|
||||
}
|
||||
if(x == 1) {
|
||||
DO(base64url_strict_encode(tmp, l1, out, &l2));
|
||||
DO(do_compare_testvector(out, l2, url_cases[x].s, strlen(url_cases[x].s), "base64url_strict_encode", x));
|
||||
}
|
||||
}
|
||||
|
||||
DO(base64url_strict_decode(url_cases[4].s, slen1, tmp, &l1) == CRYPT_INVALID_PACKET ? CRYPT_OK : CRYPT_INVALID_PACKET);
|
||||
#endif
|
||||
|
||||
#if defined(LTC_BASE64)
|
||||
@ -89,10 +120,14 @@ int base64_test(void)
|
||||
slen1 = strlen(cases[x].s);
|
||||
l1 = sizeof(out);
|
||||
DO(base64_encode((unsigned char*)cases[x].s, slen1, out, &l1));
|
||||
DO(do_compare_testvector(out, l1, cases[x].b64, strlen(cases[x].b64), "base64_encode", x));
|
||||
l2 = sizeof(tmp);
|
||||
DO(base64_strict_decode(out, l1, tmp, &l2));
|
||||
DO(do_compare_testvector(out, l1, cases[x].b64, strlen(cases[x].b64), "base64 encode", x));
|
||||
DO(do_compare_testvector(tmp, l2, cases[x].s, slen1, "base64 decode", x));
|
||||
DO(do_compare_testvector(tmp, l2, cases[x].s, slen1, "base64_strict_decode", x));
|
||||
DO(base64_sane_decode(out, l1, tmp, &l2));
|
||||
DO(do_compare_testvector(tmp, l2, cases[x].s, slen1, "base64_sane_decode", x));
|
||||
DO(base64_decode(out, l1, tmp, &l2));
|
||||
DO(do_compare_testvector(tmp, l2, cases[x].s, slen1, "base64_decode", x));
|
||||
}
|
||||
|
||||
for (x = 0; x < 64; x++) {
|
||||
@ -106,15 +141,20 @@ int base64_test(void)
|
||||
|
||||
x--;
|
||||
memmove(&out[11], &out[10], l1 - 10);
|
||||
out[10] = '=';
|
||||
l1++;
|
||||
l2 = sizeof(tmp);
|
||||
|
||||
out[10] = 0;
|
||||
DO(base64_decode(out, l1, tmp, &l2));
|
||||
if (compare_testvector(tmp, l2, in, l2, "relaxed base64 decoding", -1)) {
|
||||
print_hex("input ", out, l1);
|
||||
return 1;
|
||||
}
|
||||
l2 = sizeof(tmp);
|
||||
DO(compare_testvector(tmp, l2, in, l2, "insane base64 decoding (NUL)", -1));
|
||||
DO(base64_sane_decode(out, l1, tmp, &l2) == CRYPT_INVALID_PACKET ? CRYPT_OK : CRYPT_INVALID_PACKET);
|
||||
DO(base64_strict_decode(out, l1, tmp, &l2) == CRYPT_INVALID_PACKET ? CRYPT_OK : CRYPT_INVALID_PACKET);
|
||||
|
||||
out[10] = 9; /* tab */
|
||||
DO(base64_decode(out, l1, tmp, &l2));
|
||||
DO(compare_testvector(tmp, l2, in, l2, "insane base64 decoding (TAB)", -1));
|
||||
DO(base64_sane_decode(out, l1, tmp, &l2));
|
||||
DO(compare_testvector(tmp, l2, in, l2, "relaxed base64 decoding (TAB)", -1));
|
||||
DO(base64_strict_decode(out, l1, tmp, &l2) == CRYPT_INVALID_PACKET ? CRYPT_OK : CRYPT_INVALID_PACKET);
|
||||
#endif
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user