vdso_test_chacha.c 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (C) 2022-2024 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
  4. */
  5. #include <linux/compiler.h>
  6. #include <tools/le_byteshift.h>
  7. #include <sys/random.h>
  8. #include <sys/auxv.h>
  9. #include <string.h>
  10. #include <stdint.h>
  11. #include <stdbool.h>
  12. #include "kselftest.h"
  13. #if defined(__aarch64__)
  14. static bool cpu_has_capabilities(void)
  15. {
  16. return getauxval(AT_HWCAP) & HWCAP_ASIMD;
  17. }
  18. #elif defined(__s390x__)
  19. static bool cpu_has_capabilities(void)
  20. {
  21. return getauxval(AT_HWCAP) & HWCAP_S390_VXRS;
  22. }
  23. #else
  24. static bool cpu_has_capabilities(void)
  25. {
  26. return true;
  27. }
  28. #endif
  29. static uint32_t rol32(uint32_t word, unsigned int shift)
  30. {
  31. return (word << (shift & 31)) | (word >> ((-shift) & 31));
  32. }
  33. static void reference_chacha20_blocks(uint8_t *dst_bytes, const uint32_t *key, uint32_t *counter, size_t nblocks)
  34. {
  35. uint32_t s[16] = {
  36. 0x61707865U, 0x3320646eU, 0x79622d32U, 0x6b206574U,
  37. key[0], key[1], key[2], key[3], key[4], key[5], key[6], key[7],
  38. counter[0], counter[1], 0, 0
  39. };
  40. while (nblocks--) {
  41. uint32_t x[16];
  42. memcpy(x, s, sizeof(x));
  43. for (unsigned int r = 0; r < 20; r += 2) {
  44. #define QR(a, b, c, d) ( \
  45. x[a] += x[b], \
  46. x[d] = rol32(x[d] ^ x[a], 16), \
  47. x[c] += x[d], \
  48. x[b] = rol32(x[b] ^ x[c], 12), \
  49. x[a] += x[b], \
  50. x[d] = rol32(x[d] ^ x[a], 8), \
  51. x[c] += x[d], \
  52. x[b] = rol32(x[b] ^ x[c], 7))
  53. QR(0, 4, 8, 12);
  54. QR(1, 5, 9, 13);
  55. QR(2, 6, 10, 14);
  56. QR(3, 7, 11, 15);
  57. QR(0, 5, 10, 15);
  58. QR(1, 6, 11, 12);
  59. QR(2, 7, 8, 13);
  60. QR(3, 4, 9, 14);
  61. }
  62. for (unsigned int i = 0; i < 16; ++i, dst_bytes += sizeof(uint32_t))
  63. put_unaligned_le32(x[i] + s[i], dst_bytes);
  64. if (!++s[12])
  65. ++s[13];
  66. }
  67. counter[0] = s[12];
  68. counter[1] = s[13];
  69. }
  70. void __weak __arch_chacha20_blocks_nostack(uint8_t *dst_bytes, const uint32_t *key, uint32_t *counter, size_t nblocks)
  71. {
  72. ksft_test_result_skip("Not implemented on architecture\n");
  73. ksft_finished();
  74. }
  75. int main(int argc, char *argv[])
  76. {
  77. enum { TRIALS = 1000, BLOCKS = 128, BLOCK_SIZE = 64 };
  78. uint32_t key[8], counter1[2], counter2[2];
  79. uint8_t output1[BLOCK_SIZE * BLOCKS], output2[BLOCK_SIZE * BLOCKS];
  80. ksft_print_header();
  81. if (!cpu_has_capabilities())
  82. ksft_exit_skip("Required CPU capabilities missing\n");
  83. ksft_set_plan(1);
  84. for (unsigned int trial = 0; trial < TRIALS; ++trial) {
  85. if (getrandom(key, sizeof(key), 0) != sizeof(key))
  86. ksft_exit_skip("getrandom() failed unexpectedly\n");
  87. memset(counter1, 0, sizeof(counter1));
  88. reference_chacha20_blocks(output1, key, counter1, BLOCKS);
  89. for (unsigned int split = 0; split < BLOCKS; ++split) {
  90. memset(output2, 'X', sizeof(output2));
  91. memset(counter2, 0, sizeof(counter2));
  92. if (split)
  93. __arch_chacha20_blocks_nostack(output2, key, counter2, split);
  94. __arch_chacha20_blocks_nostack(output2 + split * BLOCK_SIZE, key, counter2, BLOCKS - split);
  95. if (memcmp(output1, output2, sizeof(output1)))
  96. ksft_exit_fail_msg("Main loop outputs do not match on trial %u, split %u\n", trial, split);
  97. if (memcmp(counter1, counter2, sizeof(counter1)))
  98. ksft_exit_fail_msg("Main loop counters do not match on trial %u, split %u\n", trial, split);
  99. }
  100. }
  101. memset(counter1, 0, sizeof(counter1));
  102. counter1[0] = (uint32_t)-BLOCKS + 2;
  103. memset(counter2, 0, sizeof(counter2));
  104. counter2[0] = (uint32_t)-BLOCKS + 2;
  105. reference_chacha20_blocks(output1, key, counter1, BLOCKS);
  106. __arch_chacha20_blocks_nostack(output2, key, counter2, BLOCKS);
  107. if (memcmp(output1, output2, sizeof(output1)))
  108. ksft_exit_fail_msg("Block limit outputs do not match after first round\n");
  109. if (memcmp(counter1, counter2, sizeof(counter1)))
  110. ksft_exit_fail_msg("Block limit counters do not match after first round\n");
  111. reference_chacha20_blocks(output1, key, counter1, BLOCKS);
  112. __arch_chacha20_blocks_nostack(output2, key, counter2, BLOCKS);
  113. if (memcmp(output1, output2, sizeof(output1)))
  114. ksft_exit_fail_msg("Block limit outputs do not match after second round\n");
  115. if (memcmp(counter1, counter2, sizeof(counter1)))
  116. ksft_exit_fail_msg("Block limit counters do not match after second round\n");
  117. ksft_test_result_pass("chacha: PASS\n");
  118. ksft_exit_pass();
  119. return 0;
  120. }