base32: add ability to substitute character set.
authorRusty Russell <rusty@rustcorp.com.au>
Thu, 5 Apr 2018 02:31:51 +0000 (12:01 +0930)
committerRusty Russell <rusty@rustcorp.com.au>
Thu, 5 Apr 2018 02:31:51 +0000 (12:01 +0930)
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ccan/str/base32/base32.c
ccan/str/base32/base32.h
ccan/str/base32/test/run-lower.c [new file with mode: 0644]

index 5cdd461bee32a2d4a5841675c3a015dff32f163d..71ca87d582e590ba61745e35d956264d2061d411 100644 (file)
@@ -4,6 +4,8 @@
 #include <ccan/endian/endian.h>
 #include <string.h> /* for memcpy, memset */
 
+const char *base32_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=";
+
 /* RFC 4648:
  *
  * (1) The final quantum of encoding input is an integral multiple of 40
@@ -56,7 +58,7 @@ size_t base32_data_size(const char *str, size_t strlen)
        size_t max = (strlen + 7) / 8 * 5, padding = 0;
 
        /* Count trailing padding bytes. */
-       while (strlen && str[strlen-1] == '=' && padding < 6) {
+       while (strlen && str[strlen-1] == base32_chars[32] && padding < 6) {
                strlen--;
                padding++;
        }
@@ -69,23 +71,21 @@ static bool decode_8_chars(const char c[8], beint64_t *res, int *bytes)
        uint64_t acc = 0;
        size_t num_pad = 0;
        for (int i = 0; i < 8; i++) {
-               uint8_t val;
+               const char *p;
+
                acc <<= 5;
-               if (c[i] >= 'a' && c[i] <= 'z')
-                       val = c[i] - 'a';
-               else if (c[i] >= 'A' && c[i] <= 'Z')
-                       val = c[i] - 'A';
-               else if (c[i] >= '2' && c[i] <= '7')
-                       val = c[i] - '2' + 26;
-               else if (c[i] == '=') {
-                       num_pad++;
-                       continue;
-               } else
+               p = memchr(base32_chars, c[i], 32);
+               if (!p) {
+                       if (c[i] == base32_chars[32]) {
+                               num_pad++;
+                               continue;
+                       }
                        return false;
+               }
                /* Can't have padding then non-pad */
                if (num_pad)
                        return false;
-               acc |= val;
+               acc |= (p - base32_chars);
        }
        *res = cpu_to_be64(acc);
 
@@ -120,21 +120,20 @@ static void encode_8_chars(char *dest, const uint8_t *buf, int bytes)
        beint64_t val = 0;
        uint64_t res;
        int bits = bytes * 8;
-       static const char enc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
 
        assert(bytes > 0 && bytes <= 5);
        memcpy((char *)&val + 3, buf, bytes);
        res = be64_to_cpu(val);
 
        while (bits > 0) {
-               *dest = enc[(res >> 35) & 0x1F];
+               *dest = base32_chars[(res >> 35) & 0x1F];
                dest++;
                res <<= 5;
                bits -= 5;
        }
 
        if (bytes != 5)
-               memset(dest, '=', padlen(bytes));
+               memset(dest, base32_chars[32], padlen(bytes));
 }
 
 bool base32_encode(const void *buf, size_t bufsize, char *dest, size_t destsize)
index 6960fe821e3041e8f847977ce37450acebb26e6c..177aad7e07a4a5f7229a6a344c8675e1db907c94 100644 (file)
@@ -65,4 +65,13 @@ size_t base32_str_size(size_t bytes);
  *     base32_decode(str, strlen(str), buf, sizeof(buf));
  */
 size_t base32_data_size(const char *str, size_t strlen);
+
+/**
+ * base32_chars - the encoding/decoding array to use.
+ *
+ * It must be at least 33 characters long, representing 32 values and
+ * the pad value.  The default array is "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=".
+ */
+extern const char *base32_chars;
+
 #endif /* CCAN_STR_BASE32_H */
diff --git a/ccan/str/base32/test/run-lower.c b/ccan/str/base32/test/run-lower.c
new file mode 100644 (file)
index 0000000..5f64d64
--- /dev/null
@@ -0,0 +1,38 @@
+#include <ccan/str/base32/base32.h>
+/* Include the C files directly. */
+#include <ccan/str/base32/base32.c>
+#include <ccan/tap/tap.h>
+
+static void test(const char *data, const char *b32)
+{
+       char test[1000];
+
+       ok1(base32_str_size(strlen(data)) == strlen(b32) + 1);
+       ok1(base32_data_size(b32, strlen(b32)) == strlen(data));
+       ok1(base32_encode(data, strlen(data), test, strlen(b32)+1));
+       ok1(strcmp(test, b32) == 0);
+       test[strlen(data)] = '\0';
+       ok1(base32_decode(b32, strlen(b32), test, strlen(data)));
+       ok1(strcmp(test, data) == 0);
+}
+
+int main(void)
+{
+       /* This is how many tests you plan to run */
+       plan_tests(8 * 6);
+
+       base32_chars = "abcdefghijklmnopqrstuvwxyz234567=";
+
+       /* Test vectors from RFC, but lower-case */
+       test("", "");
+       test("f", "my======");
+       test("fo", "mzxq====");
+       test("foo", "mzxw6===");
+       test("foob", "mzxw6yq=");
+       test("fooba", "mzxw6ytb");
+       test("r", "oi======");
+       test("foobar", "mzxw6ytboi======");
+
+       /* This exits depending on whether all tests passed */
+       return exit_status();
+}