1#define XS_IMPLEMENTATION
2#include "../xs.h"
3#include "../xs_bearssl.h"
4
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8
9enum encoding_relation {
10 // canonical indicates that the encoding is the expected encoding of the
11 // input.
12 canonical,
13 // valid indicates that the encoding is /a/ valid encoding of the input, but
14 // need not be the canonical one.
15 valid,
16 // invalid indicates that the encoded data is valid.
17 invalid,
18};
19
20struct Base64TestVector {
21 enum encoding_relation relation;
22 const char *decoded;
23 const char *encoded;
24};
25
26static const struct Base64TestVector kTestVectors[] = {
27 // Test vectors from RFC 4648, section 10.
28 {canonical, "", ""},
29 {canonical, "f", "Zg==\n"},
30 {canonical, "fo", "Zm8=\n"},
31 {canonical, "foo", "Zm9v\n"},
32 {canonical, "foob", "Zm9vYg==\n"},
33 {canonical, "fooba", "Zm9vYmE=\n"},
34 {canonical, "foobar", "Zm9vYmFy\n"},
35
36 {invalid, "", "Zm9vYmFy=\n"},
37 {invalid, "", "Zm9vYmFy==\n"},
38 {invalid, "", "Zm9vYmFy===\n"},
39
40 // valid non-canonical encodings due to arbitrary whitespace
41 {valid, "foobar", "Zm9vYmFy\n\n"},
42 {valid, "foobar", " Zm9vYmFy\n\n"},
43 {valid, "foobar", " Z m 9 v Y m F y\n\n"},
44 {valid, "foobar", "Zm9vYmFy\r\n"},
45
46 // The following "valid" encodings are arguably invalid, but they are
47 // commonly accepted by parsers, in particular by OpenSSL.
48 {valid, "v", "dv==\n"},
49 {canonical, "w", "dw==\n"},
50 {valid, "w", "dx==\n"},
51 {valid, "w", "d+==\n"},
52 {valid, "w", "d/==\n"},
53 {invalid, "", "d===\n"},
54 {canonical, "w`", "d2A=\n"},
55 {valid, "w`", "d2B=\n"},
56 {valid, "w`", "d2C=\n"},
57 {valid, "w`", "d2D=\n"},
58 {canonical, "wa", "d2E=\n"},
59
60 {invalid, "", "Z"},
61 {invalid, "", "Z\n"},
62 {invalid, "", "ab!c"},
63 {invalid, "", "ab=c"},
64 {invalid, "", "abc"},
65
66 {canonical, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
67 "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA==\n"},
68 {valid, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
69 "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA\n==\n"},
70 {valid, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
71 "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA=\n=\n"},
72 {invalid, "",
73 "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA=\n==\n"},
74 {canonical, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
75 "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4\neHh4eHh"
76 "4eHh4eHh4\n"},
77 {canonical,
78 "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
79 "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4\neHh4eHh"
80 "4eHh4eHh4eHh4eA==\n"},
81 {valid, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
82 "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh\n4eHh4eHh"
83 "4eHh4eHh4eHh4eA==\n"},
84 {valid, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
85 "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4e"
86 "Hh4eHh4eHh4eA==\n"},
87 {invalid, "",
88 "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA=="
89 "\neHh4eHh4eHh4eHh4eHh4eHh4\n"},
90
91 // A '-' has traditionally been treated as the end of the data by OpenSSL
92 // and anything following would be ignored. BoringSSL does not accept this
93 // non-standard extension.
94 {invalid, "", "Zm9vYmFy-anythinggoes"},
95 {invalid, "", "Zm9vYmFy\n-anythinggoes"},
96
97 // CVE-2015-0292
98 {invalid, "",
99 "ZW5jb2RlIG1lCg==========================================================="
100 "=======\n"},
101};
102
103static int fails;
104
105// RemoveNewlines returns a copy of `in` with all '\n' characters removed.
106static char *RemoveNewlines(const char *in) {
107 size_t n = strlen(in);
108 char *out = malloc(n + 1);
109 size_t j = 0;
110
111 for (size_t i = 0; i < n; i++)
112 if (in[i] != '\n')
113 out[j++] = in[i];
114
115 out[j] = '\0';
116 return out;
117}
118
119static void EncodeBlock(const struct Base64TestVector *t) {
120 if (t->relation != canonical)
121 return;
122
123 xs_str *out = xs_base64_enc((char *)t->decoded, strlen(t->decoded));
124 char *want = RemoveNewlines(t->encoded);
125
126 if (strcmp(out, want) != 0) {
127 printf("EncodeBlock FAIL: decoded=\"%s\" got=\"%s\" want=\"%s\"\n",
128 t->decoded, out, want);
129 fails++;
130 }
131
132 xs_free(out);
133 free(want);
134}
135
136static void DecodeBase64(const struct Base64TestVector *t) {
137 char *in = RemoveNewlines(t->encoded);
138 int len = 0;
139 xs_val *out = xs_base64_dec(in, &len);
140
141 switch (t->relation) {
142 case invalid:
143 if (out != NULL) {
144 printf("DecodeBase64 FAIL: invalid input accepted: \"%s\"\n",
145 t->encoded);
146 fails++;
147 }
148 break;
149
150 case canonical:
151 case valid:
152 if (out == NULL || len != (int)strlen(t->decoded) ||
153 memcmp(out, t->decoded, len) != 0) {
154 printf("DecodeBase64 FAIL: encoded=\"%s\" got %d bytes, want \"%s\"\n",
155 t->encoded, len, t->decoded);
156 fails++;
157 }
158 break;
159 }
160
161 xs_free(out);
162 free(in);
163}
164
165int main(void) {
166 for (size_t i = 0; i < sizeof kTestVectors / sizeof kTestVectors[0]; i++) {
167 EncodeBlock(&kTestVectors[i]);
168 DecodeBase64(&kTestVectors[i]);
169 }
170
171 printf("%d failure(s)\n", fails);
172 return fails != 0;
173}