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