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
M
Makefile
+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-}