1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2022-2024 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
6 #include <linux/compiler.h>
7 #include <tools/le_byteshift.h>
8 #include <sys/random.h>
13 #include "../kselftest.h"
15 #if defined(__aarch64__)
16 static bool cpu_has_capabilities(void)
18 return getauxval(AT_HWCAP
) & HWCAP_ASIMD
;
20 #elif defined(__s390x__)
21 static bool cpu_has_capabilities(void)
23 return getauxval(AT_HWCAP
) & HWCAP_S390_VXRS
;
26 static bool cpu_has_capabilities(void)
32 static uint32_t rol32(uint32_t word
, unsigned int shift
)
34 return (word
<< (shift
& 31)) | (word
>> ((-shift
) & 31));
37 static void reference_chacha20_blocks(uint8_t *dst_bytes
, const uint32_t *key
, uint32_t *counter
, size_t nblocks
)
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
47 memcpy(x
, s
, sizeof(x
));
48 for (unsigned int r
= 0; r
< 20; r
+= 2) {
49 #define QR(a, b, c, d) ( \
51 x[d] = rol32(x[d] ^ x[a], 16), \
53 x[b] = rol32(x[b] ^ x[c], 12), \
55 x[d] = rol32(x[d] ^ x[a], 8), \
57 x[b] = rol32(x[b] ^ x[c], 7))
68 for (unsigned int i
= 0; i
< 16; ++i
, dst_bytes
+= sizeof(uint32_t))
69 put_unaligned_le32(x
[i
] + s
[i
], dst_bytes
);
77 void __weak
__arch_chacha20_blocks_nostack(uint8_t *dst_bytes
, const uint32_t *key
, uint32_t *counter
, size_t nblocks
)
79 ksft_exit_skip("Not implemented on architecture\n");
82 int main(int argc
, char *argv
[])
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
];
89 if (!cpu_has_capabilities())
90 ksft_exit_skip("Required CPU capabilities missing\n");
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
));
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
);
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;
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");
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");
129 ksft_test_result_pass("chacha: PASS\n");