From 9589b22cc1098cd2e8270eeb67e0d44f804d2b31 Mon Sep 17 00:00:00 2001 From: Paul Kulchenko Date: Mon, 9 Oct 2023 22:13:47 -0700 Subject: [PATCH] Add DecodeBase32() to redbean --- net/http/base32.c | 79 ++++++++++++++++++++++++++++++++++++++++++---- net/http/escape.h | 1 + tool/net/help.txt | 8 ++++- tool/net/lfuncs.c | 15 +++++++-- tool/net/lfuncs.h | 1 + tool/net/redbean.c | 1 + 6 files changed, 95 insertions(+), 10 deletions(-) diff --git a/net/http/base32.c b/net/http/base32.c index 4a2f87aeb368..ec65a4b25570 100644 --- a/net/http/base32.c +++ b/net/http/base32.c @@ -33,7 +33,7 @@ int tobits(int b) { return bits; } -// the next function is based on +// these functions are based on // https://github.com/google/google-authenticator-libpam/blob/master/src/base32.c // Copyright 2010 Google Inc.; Author: Markus Gutschke // licensed under Apache License, Version 2.0 @@ -42,6 +42,7 @@ char* EncodeBase32(const char *s, size_t sl, size_t *ol) { size_t count = 0; char *r; + if (sl == -1) sl = s ? strlen(s) : 0; if (al == 0) { a = base32def; al = sizeof(base32def)/sizeof(a[0]); @@ -50,11 +51,11 @@ char* EncodeBase32(const char *s, size_t sl, int bl = tobits(al); int mask = (1 << bl) - 1; *ol = (sl * 8 + bl - 1) / bl; // calculate exact output length - if (!(r = malloc(*ol + 1))) { - *ol = 0; - return r; - } if (sl > 0) { + if (!(r = malloc(*ol))) { + *ol = 0; + return r; + } int buffer = s[0]; size_t next = 1; int bitsLeft = 8; @@ -74,6 +75,72 @@ char* EncodeBase32(const char *s, size_t sl, r[count++] = a[mask & (buffer >> bitsLeft)]; } } - if (count < *ol) r[count] = '\000'; + if (count < *ol) *ol = count; + return r; +} + +static signed char kBase32[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2, -1, -1, -2, -1, -1, // 0x00 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x10 + -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -1, -1, // 0x20 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 0x30 + -1, 10, 11, 12, 13, 14, 15, 16, 17, 1, 18, 19, 1, 20, 21, -1, // 0x40 + 22, 23, 24, 25, 26, 0, 27, 28, 29, 30, 31, -1, -1, -1, -1, -1, // 0x50 + -1, 10, 11, 12, 13, 14, 15, 16, 17, 1, 18, 19, 1, 20, 21, -1, // 0x60 + 22, 23, 24, 25, 26, 0, 27, 28, 29, 30, 31, -1, -1, -1, -1, -1, // 0x70 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x80 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x90 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xa0 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xb0 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xc0 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xd0 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xe0 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xf0 +}; + +char* DecodeBase32(const char *s, size_t sl, + const char *a, size_t al, + size_t *ol) { + size_t count = 0; + char *r; + if (sl == -1) sl = s ? strlen(s) : 0; + if (al == 0) { + a = base32def; + al = sizeof(base32def)/sizeof(a[0]); + } + unassert(2 <= al && al <= 128); + int bl = tobits(al); + *ol = (sl * bl + 1) / 8 + 1; // calculate output length + // process input + if (sl > 0) { + if (!(r = malloc(*ol))) { + *ol = 0; + return r; + } + unsigned int buffer = 0; + signed char *map = kBase32; + int bitsLeft = 0; + // if the provided alphabet matches the default + // then use the pre-populated static map + // otherwise allocate buffer for the map (256) + // and memset(buf, -1, 256) + + // populate the map based on alphabet + + while (count < *ol && *s) { + signed char m = map[*s++ & 0xff]; + if (m == -2) continue; + if (m == -1) break; + buffer <<= bl; + buffer |= m; + bitsLeft += bl; + if (bitsLeft >= 8) { + r[count++] = buffer >> (bitsLeft - 8); + bitsLeft -= 8; + } + } + // free the buffer + } + if (count < *ol) *ol = count; return r; } diff --git a/net/http/escape.h b/net/http/escape.h index 402338e8f6d7..cd2b19b71d13 100644 --- a/net/http/escape.h +++ b/net/http/escape.h @@ -35,6 +35,7 @@ char *EncodeHttpHeaderValue(const char *, size_t, size_t *); char *VisualizeControlCodes(const char *, size_t, size_t *); char *IndentLines(const char *, size_t, size_t *, size_t); char *EncodeBase32(const char *, size_t, const char *, size_t, size_t *); +char *DecodeBase32(const char *, size_t, const char *, size_t, size_t *); char *EncodeBase64(const char *, size_t, size_t *); char *DecodeBase64(const char *, size_t, size_t *); diff --git a/tool/net/help.txt b/tool/net/help.txt index 8c0834bcef25..ac01d136c30a 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -737,12 +737,18 @@ FUNCTIONS Turns ASCII base-16 hexadecimal byte string into binary string, case-insensitively. Non-hex characters may not appear in string. + DecodeBase32(ascii:str[, alphabet:str]) → binary:str + Turns ASCII into binary using provided alphabet. The default + decoding uses Crockford's base32 alphabet in a permissive way + that ignores whitespaces and dash ('-') and stops at the first + character outside of the alphabet. + EncodeBase32(binary:str[, alphabet:str]) → ascii:str Turns binary into ASCII using provided alphabet (using Crockford's base32 encoding by default). DecodeBase64(ascii:str) → binary:str - Turns ASCII into binary, in a permissive way that ignores + Turns ASCII into binary in a permissive way that ignores characters outside the base64 alphabet, such as whitespace. See decodebase64.c. diff --git a/tool/net/lfuncs.c b/tool/net/lfuncs.c index a701102ae26f..f525e6f19967 100644 --- a/tool/net/lfuncs.c +++ b/tool/net/lfuncs.c @@ -605,21 +605,30 @@ int LuaEncodeLatin1(lua_State *L) { } } -int LuaEncodeBase32(lua_State *L) { +dontinline int LuaBase32Impl(lua_State *L, + char *B32(const char *, size_t, const char *, size_t, size_t *)) { char *p; size_t sl, al; // source/output and alphabet lengths const char *s = luaL_checklstring(L, 1, &sl); // use an empty string, as EncodeBase32 provides a default value const char *a = luaL_optlstring(L, 2, "", &al); - if (al & (al - 1) || al > 128 || al == 1) + if (!IS2POW(al) || al > 128 || al == 1) return luaL_error(L, "alphabet length is not a power of 2 in range 2..128"); - if (!(p = EncodeBase32(s, sl, a, al, &sl))) + if (!(p = B32(s, sl, a, al, &sl))) return luaL_error(L, "out of memory"); lua_pushlstring(L, p, sl); free(p); return 1; } +int LuaEncodeBase32(lua_State *L) { + return LuaBase32Impl(L, EncodeBase32); +} + +int LuaDecodeBase32(lua_State *L) { + return LuaBase32Impl(L, DecodeBase32); +} + int LuaEncodeHex(lua_State *L) { char *p; size_t n; diff --git a/tool/net/lfuncs.h b/tool/net/lfuncs.h index 273185172095..47687aa0d4a7 100644 --- a/tool/net/lfuncs.h +++ b/tool/net/lfuncs.h @@ -20,6 +20,7 @@ int LuaCompress(lua_State *); int LuaCrc32(lua_State *); int LuaCrc32c(lua_State *); int LuaDecimate(lua_State *); +int LuaDecodeBase32(lua_State *); int LuaDecodeBase64(lua_State *); int LuaDecodeHex(lua_State *); int LuaDecodeLatin1(lua_State *); diff --git a/tool/net/redbean.c b/tool/net/redbean.c index d43a5be010aa..4009f15b848d 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -5127,6 +5127,7 @@ static const luaL_Reg kLuaFuncs[] = { {"Crc32", LuaCrc32}, // {"Crc32c", LuaCrc32c}, // {"Decimate", LuaDecimate}, // + {"DecodeBase32", LuaDecodeBase32}, // {"DecodeBase64", LuaDecodeBase64}, // {"DecodeHex", LuaDecodeHex}, // {"DecodeJson", LuaDecodeJson}, //