1// SPDX-License-Identifier: GPL-2.0
2/*
3 * base64_kunit_test.c - KUnit tests for base64 encoding and decoding functions
4 *
5 * Copyright (c) 2025, Guan-Chun Wu <409411716@gms.tku.edu.tw>
6 */
7
8#include <kunit/test.h>
9#include <linux/base64.h>
10
11/* ---------- Benchmark helpers ---------- */
12static u64 bench_encode_ns(const u8 *data, int len, char *dst, int reps,
13 enum base64_variant variant)
14{
15 u64 t0, t1;
16
17 t0 = ktime_get_ns();
18 for (int i = 0; i < reps; i++)
19 base64_encode(src: data, len, dst, padding: true, variant);
20 t1 = ktime_get_ns();
21
22 return div64_u64(dividend: t1 - t0, divisor: (u64)reps);
23}
24
25static u64 bench_decode_ns(const char *data, int len, u8 *dst, int reps,
26 enum base64_variant variant)
27{
28 u64 t0, t1;
29
30 t0 = ktime_get_ns();
31 for (int i = 0; i < reps; i++)
32 base64_decode(src: data, len, dst, padding: true, variant);
33 t1 = ktime_get_ns();
34
35 return div64_u64(dividend: t1 - t0, divisor: (u64)reps);
36}
37
38static void run_perf_and_check(struct kunit *test, const char *label, int size,
39 enum base64_variant variant)
40{
41 const int reps = 1000;
42 size_t outlen = DIV_ROUND_UP(size, 3) * 4;
43 u8 *in = kmalloc(size, GFP_KERNEL);
44 char *enc = kmalloc(outlen, GFP_KERNEL);
45 u8 *decoded = kmalloc(size, GFP_KERNEL);
46
47 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, in);
48 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, enc);
49 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, decoded);
50
51 get_random_bytes(buf: in, len: size);
52 int enc_len = base64_encode(src: in, len: size, dst: enc, padding: true, variant);
53 int dec_len = base64_decode(src: enc, len: enc_len, dst: decoded, padding: true, variant);
54
55 /* correctness sanity check */
56 KUNIT_EXPECT_EQ(test, dec_len, size);
57 KUNIT_EXPECT_MEMEQ(test, decoded, in, size);
58
59 /* benchmark encode */
60
61 u64 t1 = bench_encode_ns(data: in, len: size, dst: enc, reps, variant);
62
63 kunit_info(test, "[%s] encode run : %lluns", label, t1);
64
65 u64 t2 = bench_decode_ns(data: enc, len: enc_len, dst: decoded, reps, variant);
66
67 kunit_info(test, "[%s] decode run : %lluns", label, t2);
68
69 kfree(objp: in);
70 kfree(objp: enc);
71 kfree(objp: decoded);
72}
73
74static void base64_performance_tests(struct kunit *test)
75{
76 /* run on STD variant only */
77 run_perf_and_check(test, label: "64B", size: 64, variant: BASE64_STD);
78 run_perf_and_check(test, label: "1KB", size: 1024, variant: BASE64_STD);
79}
80
81/* ---------- Helpers for encode ---------- */
82static void expect_encode_ok(struct kunit *test, const u8 *src, int srclen,
83 const char *expected, bool padding,
84 enum base64_variant variant)
85{
86 char buf[128];
87 int encoded_len = base64_encode(src, len: srclen, dst: buf, padding, variant);
88
89 buf[encoded_len] = '\0';
90
91 KUNIT_EXPECT_EQ(test, encoded_len, strlen(expected));
92 KUNIT_EXPECT_STREQ(test, buf, expected);
93}
94
95/* ---------- Helpers for decode ---------- */
96static void expect_decode_ok(struct kunit *test, const char *src,
97 const u8 *expected, int expected_len, bool padding,
98 enum base64_variant variant)
99{
100 u8 buf[128];
101 int decoded_len = base64_decode(src, strlen(src), dst: buf, padding, variant);
102
103 KUNIT_EXPECT_EQ(test, decoded_len, expected_len);
104 KUNIT_EXPECT_MEMEQ(test, buf, expected, expected_len);
105}
106
107static void expect_decode_err(struct kunit *test, const char *src,
108 int srclen, bool padding,
109 enum base64_variant variant)
110{
111 u8 buf[64];
112 int decoded_len = base64_decode(src, len: srclen, dst: buf, padding, variant);
113
114 KUNIT_EXPECT_EQ(test, decoded_len, -1);
115}
116
117/* ---------- Encode Tests ---------- */
118static void base64_std_encode_tests(struct kunit *test)
119{
120 /* With padding */
121 expect_encode_ok(test, src: (const u8 *)"", srclen: 0, expected: "", padding: true, variant: BASE64_STD);
122 expect_encode_ok(test, src: (const u8 *)"f", srclen: 1, expected: "Zg==", padding: true, variant: BASE64_STD);
123 expect_encode_ok(test, src: (const u8 *)"fo", srclen: 2, expected: "Zm8=", padding: true, variant: BASE64_STD);
124 expect_encode_ok(test, src: (const u8 *)"foo", srclen: 3, expected: "Zm9v", padding: true, variant: BASE64_STD);
125 expect_encode_ok(test, src: (const u8 *)"foob", srclen: 4, expected: "Zm9vYg==", padding: true, variant: BASE64_STD);
126 expect_encode_ok(test, src: (const u8 *)"fooba", srclen: 5, expected: "Zm9vYmE=", padding: true, variant: BASE64_STD);
127 expect_encode_ok(test, src: (const u8 *)"foobar", srclen: 6, expected: "Zm9vYmFy", padding: true, variant: BASE64_STD);
128
129 /* Extra cases with padding */
130 expect_encode_ok(test, src: (const u8 *)"Hello, world!", srclen: 13, expected: "SGVsbG8sIHdvcmxkIQ==",
131 padding: true, variant: BASE64_STD);
132 expect_encode_ok(test, src: (const u8 *)"ABCDEFGHIJKLMNOPQRSTUVWXYZ", srclen: 26,
133 expected: "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo=", padding: true, variant: BASE64_STD);
134 expect_encode_ok(test, src: (const u8 *)"abcdefghijklmnopqrstuvwxyz", srclen: 26,
135 expected: "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=", padding: true, variant: BASE64_STD);
136 expect_encode_ok(test, src: (const u8 *)"0123456789+/", srclen: 12, expected: "MDEyMzQ1Njc4OSsv",
137 padding: true, variant: BASE64_STD);
138
139 /* Without padding */
140 expect_encode_ok(test, src: (const u8 *)"", srclen: 0, expected: "", padding: false, variant: BASE64_STD);
141 expect_encode_ok(test, src: (const u8 *)"f", srclen: 1, expected: "Zg", padding: false, variant: BASE64_STD);
142 expect_encode_ok(test, src: (const u8 *)"fo", srclen: 2, expected: "Zm8", padding: false, variant: BASE64_STD);
143 expect_encode_ok(test, src: (const u8 *)"foo", srclen: 3, expected: "Zm9v", padding: false, variant: BASE64_STD);
144 expect_encode_ok(test, src: (const u8 *)"foob", srclen: 4, expected: "Zm9vYg", padding: false, variant: BASE64_STD);
145 expect_encode_ok(test, src: (const u8 *)"fooba", srclen: 5, expected: "Zm9vYmE", padding: false, variant: BASE64_STD);
146 expect_encode_ok(test, src: (const u8 *)"foobar", srclen: 6, expected: "Zm9vYmFy", padding: false, variant: BASE64_STD);
147
148 /* Extra cases without padding */
149 expect_encode_ok(test, src: (const u8 *)"Hello, world!", srclen: 13, expected: "SGVsbG8sIHdvcmxkIQ",
150 padding: false, variant: BASE64_STD);
151 expect_encode_ok(test, src: (const u8 *)"ABCDEFGHIJKLMNOPQRSTUVWXYZ", srclen: 26,
152 expected: "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo", padding: false, variant: BASE64_STD);
153 expect_encode_ok(test, src: (const u8 *)"abcdefghijklmnopqrstuvwxyz", srclen: 26,
154 expected: "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo", padding: false, variant: BASE64_STD);
155 expect_encode_ok(test, src: (const u8 *)"0123456789+/", srclen: 12, expected: "MDEyMzQ1Njc4OSsv",
156 padding: false, variant: BASE64_STD);
157}
158
159/* ---------- Decode Tests ---------- */
160static void base64_std_decode_tests(struct kunit *test)
161{
162 /* -------- With padding --------*/
163 expect_decode_ok(test, src: "", expected: (const u8 *)"", expected_len: 0, padding: true, variant: BASE64_STD);
164 expect_decode_ok(test, src: "Zg==", expected: (const u8 *)"f", expected_len: 1, padding: true, variant: BASE64_STD);
165 expect_decode_ok(test, src: "Zm8=", expected: (const u8 *)"fo", expected_len: 2, padding: true, variant: BASE64_STD);
166 expect_decode_ok(test, src: "Zm9v", expected: (const u8 *)"foo", expected_len: 3, padding: true, variant: BASE64_STD);
167 expect_decode_ok(test, src: "Zm9vYg==", expected: (const u8 *)"foob", expected_len: 4, padding: true, variant: BASE64_STD);
168 expect_decode_ok(test, src: "Zm9vYmE=", expected: (const u8 *)"fooba", expected_len: 5, padding: true, variant: BASE64_STD);
169 expect_decode_ok(test, src: "Zm9vYmFy", expected: (const u8 *)"foobar", expected_len: 6, padding: true, variant: BASE64_STD);
170 expect_decode_ok(test, src: "SGVsbG8sIHdvcmxkIQ==", expected: (const u8 *)"Hello, world!", expected_len: 13,
171 padding: true, variant: BASE64_STD);
172 expect_decode_ok(test, src: "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo=",
173 expected: (const u8 *)"ABCDEFGHIJKLMNOPQRSTUVWXYZ", expected_len: 26, padding: true, variant: BASE64_STD);
174 expect_decode_ok(test, src: "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=",
175 expected: (const u8 *)"abcdefghijklmnopqrstuvwxyz", expected_len: 26, padding: true, variant: BASE64_STD);
176
177 /* Error cases */
178 expect_decode_err(test, src: "Zg=!", srclen: 4, padding: true, variant: BASE64_STD);
179 expect_decode_err(test, src: "Zm$=", srclen: 4, padding: true, variant: BASE64_STD);
180 expect_decode_err(test, src: "Z===", srclen: 4, padding: true, variant: BASE64_STD);
181 expect_decode_err(test, src: "Zg", srclen: 2, padding: true, variant: BASE64_STD);
182 expect_decode_err(test, src: "Zm9v====", srclen: 8, padding: true, variant: BASE64_STD);
183 expect_decode_err(test, src: "Zm==A", srclen: 5, padding: true, variant: BASE64_STD);
184
185 {
186 char with_nul[4] = { 'Z', 'g', '\0', '=' };
187
188 expect_decode_err(test, src: with_nul, srclen: 4, padding: true, variant: BASE64_STD);
189 }
190
191 /* -------- Without padding --------*/
192 expect_decode_ok(test, src: "", expected: (const u8 *)"", expected_len: 0, padding: false, variant: BASE64_STD);
193 expect_decode_ok(test, src: "Zg", expected: (const u8 *)"f", expected_len: 1, padding: false, variant: BASE64_STD);
194 expect_decode_ok(test, src: "Zm8", expected: (const u8 *)"fo", expected_len: 2, padding: false, variant: BASE64_STD);
195 expect_decode_ok(test, src: "Zm9v", expected: (const u8 *)"foo", expected_len: 3, padding: false, variant: BASE64_STD);
196 expect_decode_ok(test, src: "Zm9vYg", expected: (const u8 *)"foob", expected_len: 4, padding: false, variant: BASE64_STD);
197 expect_decode_ok(test, src: "Zm9vYmE", expected: (const u8 *)"fooba", expected_len: 5, padding: false, variant: BASE64_STD);
198 expect_decode_ok(test, src: "Zm9vYmFy", expected: (const u8 *)"foobar", expected_len: 6, padding: false, variant: BASE64_STD);
199 expect_decode_ok(test, src: "TWFu", expected: (const u8 *)"Man", expected_len: 3, padding: false, variant: BASE64_STD);
200 expect_decode_ok(test, src: "SGVsbG8sIHdvcmxkIQ", expected: (const u8 *)"Hello, world!", expected_len: 13,
201 padding: false, variant: BASE64_STD);
202 expect_decode_ok(test, src: "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo",
203 expected: (const u8 *)"ABCDEFGHIJKLMNOPQRSTUVWXYZ", expected_len: 26, padding: false, variant: BASE64_STD);
204 expect_decode_ok(test, src: "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo",
205 expected: (const u8 *)"abcdefghijklmnopqrstuvwxyz", expected_len: 26, padding: false, variant: BASE64_STD);
206 expect_decode_ok(test, src: "MDEyMzQ1Njc4OSsv", expected: (const u8 *)"0123456789+/", expected_len: 12,
207 padding: false, variant: BASE64_STD);
208
209 /* Error cases */
210 expect_decode_err(test, src: "Zg=!", srclen: 4, padding: false, variant: BASE64_STD);
211 expect_decode_err(test, src: "Zm$=", srclen: 4, padding: false, variant: BASE64_STD);
212 expect_decode_err(test, src: "Z===", srclen: 4, padding: false, variant: BASE64_STD);
213 expect_decode_err(test, src: "Zg=", srclen: 3, padding: false, variant: BASE64_STD);
214 expect_decode_err(test, src: "Zm9v====", srclen: 8, padding: false, variant: BASE64_STD);
215 expect_decode_err(test, src: "Zm==v", srclen: 4, padding: false, variant: BASE64_STD);
216
217 {
218 char with_nul[4] = { 'Z', 'g', '\0', '=' };
219
220 expect_decode_err(test, src: with_nul, srclen: 4, padding: false, variant: BASE64_STD);
221 }
222}
223
224/* ---------- Variant tests (URLSAFE / IMAP) ---------- */
225static void base64_variant_tests(struct kunit *test)
226{
227 const u8 sample1[] = { 0x00, 0xfb, 0xff, 0x7f, 0x80 };
228 char std_buf[128], url_buf[128], imap_buf[128];
229 u8 back[128];
230 int n_std, n_url, n_imap, m;
231 int i;
232
233 n_std = base64_encode(src: sample1, len: sizeof(sample1), dst: std_buf, padding: false, variant: BASE64_STD);
234 n_url = base64_encode(src: sample1, len: sizeof(sample1), dst: url_buf, padding: false, variant: BASE64_URLSAFE);
235 std_buf[n_std] = '\0';
236 url_buf[n_url] = '\0';
237
238 for (i = 0; i < n_std; i++) {
239 if (std_buf[i] == '+')
240 std_buf[i] = '-';
241 else if (std_buf[i] == '/')
242 std_buf[i] = '_';
243 }
244 KUNIT_EXPECT_STREQ(test, std_buf, url_buf);
245
246 m = base64_decode(src: url_buf, len: n_url, dst: back, padding: false, variant: BASE64_URLSAFE);
247 KUNIT_EXPECT_EQ(test, m, (int)sizeof(sample1));
248 KUNIT_EXPECT_MEMEQ(test, back, sample1, sizeof(sample1));
249
250 n_std = base64_encode(src: sample1, len: sizeof(sample1), dst: std_buf, padding: false, variant: BASE64_STD);
251 n_imap = base64_encode(src: sample1, len: sizeof(sample1), dst: imap_buf, padding: false, variant: BASE64_IMAP);
252 std_buf[n_std] = '\0';
253 imap_buf[n_imap] = '\0';
254
255 for (i = 0; i < n_std; i++)
256 if (std_buf[i] == '/')
257 std_buf[i] = ',';
258 KUNIT_EXPECT_STREQ(test, std_buf, imap_buf);
259
260 m = base64_decode(src: imap_buf, len: n_imap, dst: back, padding: false, variant: BASE64_IMAP);
261 KUNIT_EXPECT_EQ(test, m, (int)sizeof(sample1));
262 KUNIT_EXPECT_MEMEQ(test, back, sample1, sizeof(sample1));
263
264 {
265 const char *bad = "Zg==";
266 u8 tmp[8];
267
268 m = base64_decode(src: bad, strlen(bad), dst: tmp, padding: false, variant: BASE64_URLSAFE);
269 KUNIT_EXPECT_EQ(test, m, -1);
270
271 m = base64_decode(src: bad, strlen(bad), dst: tmp, padding: false, variant: BASE64_IMAP);
272 KUNIT_EXPECT_EQ(test, m, -1);
273 }
274}
275
276/* ---------- Test registration ---------- */
277static struct kunit_case base64_test_cases[] = {
278 KUNIT_CASE(base64_performance_tests),
279 KUNIT_CASE(base64_std_encode_tests),
280 KUNIT_CASE(base64_std_decode_tests),
281 KUNIT_CASE(base64_variant_tests),
282 {}
283};
284
285static struct kunit_suite base64_test_suite = {
286 .name = "base64",
287 .test_cases = base64_test_cases,
288};
289
290kunit_test_suite(base64_test_suite);
291
292MODULE_AUTHOR("Guan-Chun Wu <409411716@gms.tku.edu.tw>");
293MODULE_DESCRIPTION("KUnit tests for Base64 encoding/decoding, including performance checks");
294MODULE_LICENSE("GPL");
295

source code of linux/lib/tests/base64_kunit.c