You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

base64.lua 6.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. context("Base64 encoding", function()
  2. local ffi = require("ffi")
  3. local util = require("rspamd_util")
  4. local logger = require "rspamd_logger"
  5. ffi.cdef[[
  6. void rspamd_cryptobox_init (void);
  7. void ottery_rand_bytes(void *buf, size_t n);
  8. unsigned ottery_rand_unsigned(void);
  9. unsigned char* g_base64_decode (const char *in, size_t *outlen);
  10. char * rspamd_encode_base64 (const unsigned char *in, size_t inlen,
  11. size_t str_len, size_t *outlen);
  12. void g_free(void *ptr);
  13. int memcmp(const void *a1, const void *a2, size_t len);
  14. double base64_test (bool generic, size_t niters, size_t len, size_t str_len);
  15. double rspamd_get_ticks (int);
  16. ]]
  17. ffi.C.rspamd_cryptobox_init()
  18. local function random_buf(max_size)
  19. local l = ffi.C.ottery_rand_unsigned() % max_size + 1
  20. local buf = ffi.new("unsigned char[?]", l)
  21. ffi.C.ottery_rand_bytes(buf, l)
  22. return buf, l
  23. end
  24. local function random_safe_buf(max_size)
  25. local l = ffi.C.ottery_rand_unsigned() % max_size + 1
  26. local buf = ffi.new("unsigned char[?]", l)
  27. for i = 0,l-1 do
  28. buf[i] = ffi.C.ottery_rand_unsigned() % 20 + string.byte('A')
  29. end
  30. buf[l - 1] = 0;
  31. return buf, l
  32. end
  33. test("Base64 encode test", function()
  34. local cases = {
  35. {"", ""},
  36. {"f", "Zg=="},
  37. {"fo", "Zm8="},
  38. {"foo", "Zm9v"},
  39. {"foob", "Zm9vYg=="},
  40. {"fooba", "Zm9vYmE="},
  41. {"foobar", "Zm9vYmFy"},
  42. }
  43. local nl = ffi.new("size_t [1]")
  44. for _,c in ipairs(cases) do
  45. local b = ffi.C.rspamd_encode_base64(c[1], #c[1], 0, nl)
  46. local s = ffi.string(b)
  47. ffi.C.g_free(b)
  48. assert_equal(s, c[2], s .. " not equal " .. c[2])
  49. end
  50. end)
  51. test("Base64 decode test", function()
  52. local cases = {
  53. {"", ""},
  54. {"f", "Zg=="},
  55. {"fo", "Zm8="},
  56. {"foo", "Zm9v"},
  57. {"foob", "Zm9vYg=="},
  58. {"fooba", "Zm9vYmE="},
  59. {"foobar", "Zm9vYmFy"},
  60. }
  61. for _,c in ipairs(cases) do
  62. local b = tostring(util.decode_base64(c[2]))
  63. assert_equal(b, c[1], b .. " not equal " .. c[1])
  64. end
  65. end)
  66. test("Base64 line split encode test", function()
  67. local text = [[
  68. Man is distinguished, not only by his reason, but by this singular passion from
  69. other animals, which is a lust of the mind, that by a perseverance of delight
  70. in the continued and indefatigable generation of knowledge, exceeds the short
  71. vehemence of any carnal pleasure.]]
  72. local b64 = "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz\r\nIHNpbmd1bGFyIHBhc3Npb24gZnJvbQpvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg\r\ndGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodAppbiB0aGUgY29udGlu\r\ndWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo\r\nZSBzaG9ydAp2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4="
  73. local nl = ffi.new("size_t [1]")
  74. local b = ffi.C.rspamd_encode_base64(text, #text, 76, nl)
  75. local cmp = ffi.C.memcmp(b, b64, nl[0])
  76. ffi.C.g_free(b)
  77. assert_equal(cmp, 0)
  78. end)
  79. test("Base64 fuzz test", function()
  80. for i = 1,1000 do
  81. local b, l = random_safe_buf(4096)
  82. local lim = ffi.C.ottery_rand_unsigned() % 64 + 10
  83. local orig = ffi.string(b)
  84. local ben = util.encode_base64(orig, lim)
  85. local dec = util.decode_base64(ben)
  86. assert_equal(orig, tostring(dec), "fuzz test failed for length: " .. #orig)
  87. end
  88. end)
  89. test("Base64 fuzz test (ffi)", function()
  90. for i = 1,1000 do
  91. local b, l = random_buf(4096)
  92. local nl = ffi.new("size_t [1]")
  93. local lim = ffi.C.ottery_rand_unsigned() % 64 + 10
  94. local ben = ffi.C.rspamd_encode_base64(b, l, lim, nl)
  95. local bs = ffi.string(ben)
  96. local ol = ffi.new("size_t [1]")
  97. local nb = ffi.C.g_base64_decode(ben, ol)
  98. local cmp = ffi.C.memcmp(b, nb, l)
  99. ffi.C.g_free(ben)
  100. ffi.C.g_free(nb)
  101. assert_equal(cmp, 0, "fuzz test failed for length: " .. tostring(l))
  102. end
  103. end)
  104. local speed_iters = 10000
  105. local function perform_base64_speed_test(chunk, is_reference, line_len)
  106. local ticks = ffi.C.base64_test(is_reference, speed_iters, chunk, line_len)
  107. local what = 'Optimized'
  108. if is_reference then
  109. what = 'Reference'
  110. end
  111. logger.messagex("%s base64 %s chunk (%s line len): %s ticks per iter, %s ticks per byte",
  112. what, chunk, line_len,
  113. ticks / speed_iters, ticks / speed_iters / chunk)
  114. return 1
  115. end
  116. test("Base64 test reference vectors 78", function()
  117. local res = perform_base64_speed_test(78, true, 0)
  118. assert_not_equal(res, 0)
  119. end)
  120. test("Base64 test optimized vectors 78", function()
  121. local res = perform_base64_speed_test(78, false, 0)
  122. assert_not_equal(res, 0)
  123. end)
  124. test("Base64 test reference vectors 512", function()
  125. local res = perform_base64_speed_test(512, true, 0)
  126. assert_not_equal(res, 0)
  127. end)
  128. test("Base64 test optimized vectors 512", function()
  129. local res = perform_base64_speed_test(512, false, 0)
  130. assert_not_equal(res, 0)
  131. end)
  132. test("Base64 test reference vectors 512 (78 line len)", function()
  133. local res = perform_base64_speed_test(512, true, 78)
  134. assert_not_equal(res, 0)
  135. end)
  136. test("Base64 test optimized vectors 512 (78 line len)", function()
  137. local res = perform_base64_speed_test(512, false, 78)
  138. assert_not_equal(res, 0)
  139. end)
  140. test("Base64 test reference vectors 1K", function()
  141. local res = perform_base64_speed_test(1024, true, 0)
  142. assert_not_equal(res, 0)
  143. end)
  144. test("Base64 test optimized vectors 1K", function()
  145. local res = perform_base64_speed_test(1024, false, 0)
  146. assert_not_equal(res, 0)
  147. end)
  148. test("Base64 test reference vectors 1K (78 line len)", function()
  149. local res = perform_base64_speed_test(1024, true, 78)
  150. assert_not_equal(res, 0)
  151. end)
  152. test("Base64 test optimized vectors 1K (78 line len)", function()
  153. local res = perform_base64_speed_test(1024, false, 78)
  154. assert_not_equal(res, 0)
  155. end)
  156. test("Base64 test reference vectors 10K", function()
  157. local res = perform_base64_speed_test(10 * 1024, true, 0)
  158. assert_not_equal(res, 0)
  159. end)
  160. test("Base64 test optimized vectors 10K", function()
  161. local res = perform_base64_speed_test(10 * 1024, false, 0)
  162. assert_not_equal(res, 0)
  163. end)
  164. test("Base64 test reference vectors 10K (78 line len)", function()
  165. local res = perform_base64_speed_test(10 * 1024, true, 78)
  166. assert_not_equal(res, 0)
  167. end)
  168. test("Base64 test optimized vectors 10K (78 line len)", function()
  169. local res = perform_base64_speed_test(10 * 1024, false, 78)
  170. assert_not_equal(res, 0)
  171. end)
  172. end)