From 9abacaa6f3925ada0a641386e9ce4ab4d0672926 Mon Sep 17 00:00:00 2001 From: Paul Kulchenko Date: Sat, 8 Jul 2023 15:04:51 -0700 Subject: [PATCH] Add EncodeBase32 to Redbean --- net/http/base32.c | 79 +++++++++++++++++++++++++++++++++++ net/http/escape.h | 1 + test/tool/net/lfuncs_test.lua | 5 +++ tool/net/help.txt | 4 ++ tool/net/lfuncs.c | 15 +++++++ tool/net/lfuncs.h | 1 + tool/net/redbean.c | 1 + 7 files changed, 106 insertions(+) create mode 100644 net/http/base32.c diff --git a/net/http/base32.c b/net/http/base32.c new file mode 100644 index 00000000000..4a2f87aeb36 --- /dev/null +++ b/net/http/base32.c @@ -0,0 +1,79 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" +#include "libc/macros.internal.h" +#include "libc/mem/mem.h" +#include "libc/str/str.h" + +asm(".ident\t\"\\n\\n\ +Apache License, Version 2.0\\n\ +Copyright 2010 Google Inc.\""); +asm(".include \"libc/disclaimer.inc\""); + +const char base32def[] = "0123456789abcdefghjkmnpqrstvwxyz"; + +int tobits(int b) { + int bits = 0; while (b && (b >>= 1)) bits++; + return bits; +} + +// the next function is 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 +char* EncodeBase32(const char *s, size_t sl, + const char *a, size_t al, + size_t *ol) { + size_t count = 0; + char *r; + if (al == 0) { + a = base32def; + al = sizeof(base32def)/sizeof(a[0]); + } + unassert(2 <= al && al <= 128); + 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) { + int buffer = s[0]; + size_t next = 1; + int bitsLeft = 8; + while (count < *ol && (bitsLeft > 0 || next < sl)) { + if (bitsLeft < bl) { + if (next < sl) { + buffer <<= 8; + buffer |= s[next++] & 0xFF; + bitsLeft += 8; + } else { + int pad = bl - bitsLeft; + buffer <<= pad; + bitsLeft += pad; + } + } + bitsLeft -= bl; + r[count++] = a[mask & (buffer >> bitsLeft)]; + } + } + if (count < *ol) r[count] = '\000'; + return r; +} diff --git a/net/http/escape.h b/net/http/escape.h index b770370828a..402338e8f6d 100644 --- a/net/http/escape.h +++ b/net/http/escape.h @@ -34,6 +34,7 @@ char *EncodeLatin1(const char *, size_t, size_t *, int); 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 *EncodeBase64(const char *, size_t, size_t *); char *DecodeBase64(const char *, size_t, size_t *); diff --git a/test/tool/net/lfuncs_test.lua b/test/tool/net/lfuncs_test.lua index 871869cbfaa..757dc20fe3f 100644 --- a/test/tool/net/lfuncs_test.lua +++ b/test/tool/net/lfuncs_test.lua @@ -50,6 +50,11 @@ assert(bin(0x1940efe9d47ae889) == "0b0001100101000000111011111110100111010100011 assert(EncodeHex("\1\2\3\4\255") == "01020304ff") assert(DecodeHex("01020304ff") == "\1\2\3\4\255") +assert(EncodeBase32("123456") == "64s36d1n6r") +assert(EncodeBase32("12") == "64s0") +assert(EncodeBase32("\33", "01") == "00100001") +assert(EncodeBase32("\33", "0123456789abcdef") == "21") + assert(EscapeHtml(nil) == nil) assert(EscapeHtml("?hello&there<>") == "?hello&there<>") diff --git a/tool/net/help.txt b/tool/net/help.txt index 27581c36f2b..8c0834bcef2 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -737,6 +737,10 @@ FUNCTIONS Turns ASCII base-16 hexadecimal byte string into binary string, case-insensitively. Non-hex characters may not appear in string. + 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 characters outside the base64 alphabet, such as whitespace. See diff --git a/tool/net/lfuncs.c b/tool/net/lfuncs.c index 829b5928ed3..a701102ae26 100644 --- a/tool/net/lfuncs.c +++ b/tool/net/lfuncs.c @@ -605,6 +605,21 @@ int LuaEncodeLatin1(lua_State *L) { } } +int LuaEncodeBase32(lua_State *L) { + 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) + return luaL_error(L, "alphabet length is not a power of 2 in range 2..128"); + if (!(p = EncodeBase32(s, sl, a, al, &sl))) + return luaL_error(L, "out of memory"); + lua_pushlstring(L, p, sl); + free(p); + return 1; +} + int LuaEncodeHex(lua_State *L) { char *p; size_t n; diff --git a/tool/net/lfuncs.h b/tool/net/lfuncs.h index 76d2d1d379b..27318517209 100644 --- a/tool/net/lfuncs.h +++ b/tool/net/lfuncs.h @@ -24,6 +24,7 @@ int LuaDecodeBase64(lua_State *); int LuaDecodeHex(lua_State *); int LuaDecodeLatin1(lua_State *); int LuaDeflate(lua_State *); +int LuaEncodeBase32(lua_State *); int LuaEncodeBase64(lua_State *); int LuaEncodeHex(lua_State *); int LuaEncodeLatin1(lua_State *); diff --git a/tool/net/redbean.c b/tool/net/redbean.c index c716d350f4a..d43a5be010a 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -5132,6 +5132,7 @@ static const luaL_Reg kLuaFuncs[] = { {"DecodeJson", LuaDecodeJson}, // {"DecodeLatin1", LuaDecodeLatin1}, // {"Deflate", LuaDeflate}, // + {"EncodeBase32", LuaEncodeBase32}, // {"EncodeBase64", LuaEncodeBase64}, // {"EncodeHex", LuaEncodeHex}, // {"EncodeJson", LuaEncodeJson}, //