base64_kunit.c 10 KB

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