commit 6fc1a9c

V. Luna  ·  2026-10-07 23:38:16 +0000 UTC
parent 26a102c
add tests for b64 from boringssl
5 files changed,  +177, -89
+4, -4
 1@@ -8,16 +8,16 @@ snac: snac.o main.o sandbox.o data.o http.o httpd.o webfinger.o \
 2     activitypub.o html.o utils.o format.o upgrade.o mastoapi.o rss.o
 3 	$(CC) $(CFLAGS) -L$(PREFIX)/lib *.o -lcurl -lcrypto $(LDFLAGS) -pthread -o $@
 4 
 5-test: tests/smtp
 6+test: tests/b64_vectors
 7 
 8-tests/smtp: tests/smtp.o
 9-	$(CC) $(CFLAGS) -L$(PREFIX)/lib $< -lcurl $(LDFLAGS) -o $@
10+tests/b64_vectors: tests/b64_vectors.o
11+	$(CC) $(CFLAGS) -L$(PREFIX)/lib $< $(LDFLAGS) -o $@
12 
13 .c.o:
14 	$(CC) $(CFLAGS) $(CPPFLAGS) -I$(PREFIX)/include -c $< -o $@
15 
16 clean:
17-	rm -rf *.o tests/*.o tests/smtp *.core snac makefile.depend
18+	rm -rf *.o tests/*.o tests/b64_vectors *.core snac makefile.depend
19 
20 dep:
21 	$(CC) -I$(PREFIX)/include -MM *.c > makefile.depend
+0, -61
 1@@ -1,61 +0,0 @@
 2-#include <stdio.h>
 3-#include <stdlib.h>
 4-#include <string.h>
 5-
 6-#define MASK 0x3f
 7-#define PAD '='
 8-
 9-static const char b64_tab[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
10-int get_block_size(int size) {
11-        int block_size = 4096;
12-
13-        if (size < 256) {
14-                block_size = 32;
15-        } else if (size < 4096) {
16-                block_size = 256;
17-        }
18-
19-        return ((((size) + block_size) / block_size) * block_size);
20-}
21-
22-char* encode(const unsigned char* data, int size) {
23-        const int b64_bytes = 3;
24-        const int b64_chars = 4;
25-
26-        int output_len = ((size + b64_bytes - 1) / b64_bytes) * b64_chars;
27-        char* s = realloc(NULL, get_block_size(output_len + 1));
28-        
29-        int i = 0, j = 0;
30-        while (i + b64_bytes - 1 < size) {
31-                unsigned int v = (data[i] << 16) | (data[i + 1] << 8) | data[i + 2];
32-
33-                s[j++] = b64_tab[(v >> 18) & MASK];
34-                s[j++] = b64_tab[(v >> 12) & MASK];
35-                s[j++] = b64_tab[(v >> 6) & MASK];
36-                i += b64_bytes;
37-        }
38-
39-        if (i < size) {
40-                unsigned int v = data[i] << 16;
41-                int rem = size - i;
42-
43-                if (rem == 2) { v |= data[i + 1] << 8; }
44-
45-                s[j++] = b64_tab[(v >> 18) & MASK];
46-                s[j++] = b64_tab[(v >> 12) & MASK];
47-                s[j++] = (rem == 2) ? b64_tab[(v >> 6) & MASK] : PAD;
48-                s[j++] = PAD;
49-        }
50-
51-        s[j] = '\0';
52-        
53-        return s;
54-}
55-
56-int main(int argc, char* argv[]) {
57-        const char* s = "hello";
58-        const unsigned char* msg = (const unsigned char *)s;
59-
60-        char* code = encode(msg, strlen(s));
61-        printf("code: %s\n", code);
62-}
+0, -0
+173, -0
  1@@ -0,0 +1,173 @@
  2+#define XS_IMPLEMENTATION
  3+#include "../xs.h"
  4+#include "../xs_bearssl.h"
  5+
  6+#include <stdio.h>
  7+#include <stdlib.h>
  8+#include <string.h>
  9+
 10+enum encoding_relation {
 11+  // canonical indicates that the encoding is the expected encoding of the
 12+  // input.
 13+  canonical,
 14+  // valid indicates that the encoding is /a/ valid encoding of the input, but
 15+  // need not be the canonical one.
 16+  valid,
 17+  // invalid indicates that the encoded data is valid.
 18+  invalid,
 19+};
 20+
 21+struct Base64TestVector {
 22+  enum encoding_relation relation;
 23+  const char *decoded;
 24+  const char *encoded;
 25+};
 26+
 27+static const struct Base64TestVector kTestVectors[] = {
 28+    // Test vectors from RFC 4648, section 10.
 29+    {canonical, "", ""},
 30+    {canonical, "f", "Zg==\n"},
 31+    {canonical, "fo", "Zm8=\n"},
 32+    {canonical, "foo", "Zm9v\n"},
 33+    {canonical, "foob", "Zm9vYg==\n"},
 34+    {canonical, "fooba", "Zm9vYmE=\n"},
 35+    {canonical, "foobar", "Zm9vYmFy\n"},
 36+
 37+    {invalid, "", "Zm9vYmFy=\n"},
 38+    {invalid, "", "Zm9vYmFy==\n"},
 39+    {invalid, "", "Zm9vYmFy===\n"},
 40+
 41+    // valid non-canonical encodings due to arbitrary whitespace
 42+    {valid, "foobar", "Zm9vYmFy\n\n"},
 43+    {valid, "foobar", " Zm9vYmFy\n\n"},
 44+    {valid, "foobar", " Z m 9 v Y m F y\n\n"},
 45+    {valid, "foobar", "Zm9vYmFy\r\n"},
 46+
 47+    // The following "valid" encodings are arguably invalid, but they are
 48+    // commonly accepted by parsers, in particular by OpenSSL.
 49+    {valid, "v", "dv==\n"},
 50+    {canonical, "w", "dw==\n"},
 51+    {valid, "w", "dx==\n"},
 52+    {valid, "w", "d+==\n"},
 53+    {valid, "w", "d/==\n"},
 54+    {invalid, "", "d===\n"},
 55+    {canonical, "w`", "d2A=\n"},
 56+    {valid, "w`", "d2B=\n"},
 57+    {valid, "w`", "d2C=\n"},
 58+    {valid, "w`", "d2D=\n"},
 59+    {canonical, "wa", "d2E=\n"},
 60+
 61+    {invalid, "", "Z"},
 62+    {invalid, "", "Z\n"},
 63+    {invalid, "", "ab!c"},
 64+    {invalid, "", "ab=c"},
 65+    {invalid, "", "abc"},
 66+
 67+    {canonical, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
 68+     "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA==\n"},
 69+    {valid, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
 70+     "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA\n==\n"},
 71+    {valid, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
 72+     "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA=\n=\n"},
 73+    {invalid, "",
 74+     "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA=\n==\n"},
 75+    {canonical, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
 76+     "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4\neHh4eHh"
 77+     "4eHh4eHh4\n"},
 78+    {canonical,
 79+     "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
 80+     "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4\neHh4eHh"
 81+     "4eHh4eHh4eHh4eA==\n"},
 82+    {valid, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
 83+     "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh\n4eHh4eHh"
 84+     "4eHh4eHh4eHh4eA==\n"},
 85+    {valid, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
 86+     "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4e"
 87+     "Hh4eHh4eHh4eA==\n"},
 88+    {invalid, "",
 89+     "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA=="
 90+     "\neHh4eHh4eHh4eHh4eHh4eHh4\n"},
 91+
 92+    // A '-' has traditionally been treated as the end of the data by OpenSSL
 93+    // and anything following would be ignored. BoringSSL does not accept this
 94+    // non-standard extension.
 95+    {invalid, "", "Zm9vYmFy-anythinggoes"},
 96+    {invalid, "", "Zm9vYmFy\n-anythinggoes"},
 97+
 98+    // CVE-2015-0292
 99+    {invalid, "",
100+     "ZW5jb2RlIG1lCg==========================================================="
101+     "=======\n"},
102+};
103+
104+static int fails;
105+
106+// RemoveNewlines returns a copy of `in` with all '\n' characters removed.
107+static char *RemoveNewlines(const char *in) {
108+    size_t n = strlen(in);
109+    char *out = malloc(n + 1);
110+    size_t j = 0;
111+
112+    for (size_t i = 0; i < n; i++)
113+        if (in[i] != '\n')
114+            out[j++] = in[i];
115+
116+    out[j] = '\0';
117+    return out;
118+}
119+
120+static void EncodeBlock(const struct Base64TestVector *t) {
121+    if (t->relation != canonical)
122+        return;
123+
124+    xs_str *out = xs_base64_enc((char *)t->decoded, strlen(t->decoded));
125+    char *want = RemoveNewlines(t->encoded);
126+
127+    if (strcmp(out, want) != 0) {
128+        printf("EncodeBlock FAIL: decoded=\"%s\" got=\"%s\" want=\"%s\"\n",
129+               t->decoded, out, want);
130+        fails++;
131+    }
132+
133+    xs_free(out);
134+    free(want);
135+}
136+
137+static void DecodeBase64(const struct Base64TestVector *t) {
138+    char *in = RemoveNewlines(t->encoded);
139+    int len = 0;
140+    xs_val *out = xs_base64_dec(in, &len);
141+
142+    switch (t->relation) {
143+    case invalid:
144+        if (out != NULL) {
145+            printf("DecodeBase64 FAIL: invalid input accepted: \"%s\"\n",
146+                   t->encoded);
147+            fails++;
148+        }
149+        break;
150+
151+    case canonical:
152+    case valid:
153+        if (out == NULL || len != (int)strlen(t->decoded) ||
154+            memcmp(out, t->decoded, len) != 0) {
155+            printf("DecodeBase64 FAIL: encoded=\"%s\" got %d bytes, want \"%s\"\n",
156+                   t->encoded, len, t->decoded);
157+            fails++;
158+        }
159+        break;
160+    }
161+
162+    xs_free(out);
163+    free(in);
164+}
165+
166+int main(void) {
167+    for (size_t i = 0; i < sizeof kTestVectors / sizeof kTestVectors[0]; i++) {
168+        EncodeBlock(&kTestVectors[i]);
169+        DecodeBase64(&kTestVectors[i]);
170+    }
171+
172+    printf("%d failure(s)\n", fails);
173+    return fails != 0;
174+}
+0, -24
 1@@ -1,24 +0,0 @@
 2-/* snac - A simple, minimalistic ActivityPub instance */
 3-/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */
 4-
 5-#define XS_IMPLEMENTATION
 6-#include "../xs.h"
 7-#include "../xs_curl.h"
 8-
 9-#define FROM "<snac-smtp-test@locahost>"
10-
11-int main(void) {
12-    xs *to   = xs_fmt("<%s@localhost>", getenv("USER")),
13-       *body = xs_fmt(""
14-        "To: %s \r\n"
15-        "From: " FROM "\r\n"
16-        "Subject: snac smtp test\r\n"
17-        "\r\n"
18-        "If you read this as an email, it probably worked!\r\n",
19-        to);
20-
21-    return xs_smtp_request("smtp://localhost", NULL, NULL, 
22-        FROM,
23-        to,
24-        body, 0);
25-}