xref: /linux/tools/testing/selftests/vDSO/vdso_test_chacha.c (revision 36110669ddf832e6c9ceba4dd203749d5be31d31)
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 <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 
14 #if defined(__aarch64__)
15 static bool cpu_has_capabilities(void)
16 {
17 	return getauxval(AT_HWCAP) & HWCAP_ASIMD;
18 }
19 #elif defined(__s390x__)
20 static bool cpu_has_capabilities(void)
21 {
22 	return getauxval(AT_HWCAP) & HWCAP_S390_VXRS;
23 }
24 #else
25 static bool cpu_has_capabilities(void)
26 {
27 	return true;
28 }
29 #endif
30 
31 static uint32_t rol32(uint32_t word, unsigned int shift)
32 {
33 	return (word << (shift & 31)) | (word >> ((-shift) & 31));
34 }
35 
36 static void reference_chacha20_blocks(uint8_t *dst_bytes, const uint32_t *key, uint32_t *counter, size_t nblocks)
37 {
38 	uint32_t s[16] = {
39 		0x61707865U, 0x3320646eU, 0x79622d32U, 0x6b206574U,
40 		key[0], key[1], key[2], key[3], key[4], key[5], key[6], key[7],
41 		counter[0], counter[1], 0, 0
42 	};
43 
44 	while (nblocks--) {
45 		uint32_t x[16];
46 		memcpy(x, s, sizeof(x));
47 		for (unsigned int r = 0; r < 20; r += 2) {
48 		#define QR(a, b, c, d) ( \
49 			x[a] += x[b], \
50 			x[d] = rol32(x[d] ^ x[a], 16), \
51 			x[c] += x[d], \
52 			x[b] = rol32(x[b] ^ x[c], 12), \
53 			x[a] += x[b], \
54 			x[d] = rol32(x[d] ^ x[a], 8), \
55 			x[c] += x[d], \
56 			x[b] = rol32(x[b] ^ x[c], 7))
57 
58 			QR(0, 4, 8, 12);
59 			QR(1, 5, 9, 13);
60 			QR(2, 6, 10, 14);
61 			QR(3, 7, 11, 15);
62 			QR(0, 5, 10, 15);
63 			QR(1, 6, 11, 12);
64 			QR(2, 7, 8, 13);
65 			QR(3, 4, 9, 14);
66 		}
67 		for (unsigned int i = 0; i < 16; ++i, dst_bytes += sizeof(uint32_t))
68 			put_unaligned_le32(x[i] + s[i], dst_bytes);
69 		if (!++s[12])
70 			++s[13];
71 	}
72 	counter[0] = s[12];
73 	counter[1] = s[13];
74 }
75 
76 typedef uint8_t u8;
77 typedef uint32_t u32;
78 typedef uint64_t u64;
79 #include <vdso/getrandom.h>
80 
81 int main(int argc, char *argv[])
82 {
83 	enum { TRIALS = 1000, BLOCKS = 128, BLOCK_SIZE = 64 };
84 	uint32_t key[8], counter1[2], counter2[2];
85 	uint8_t output1[BLOCK_SIZE * BLOCKS], output2[BLOCK_SIZE * BLOCKS];
86 
87 	ksft_print_header();
88 	if (!cpu_has_capabilities())
89 		ksft_exit_skip("Required CPU capabilities missing\n");
90 	ksft_set_plan(1);
91 
92 	for (unsigned int trial = 0; trial < TRIALS; ++trial) {
93 		if (getrandom(key, sizeof(key), 0) != sizeof(key)) {
94 			printf("getrandom() failed!\n");
95 			return KSFT_SKIP;
96 		}
97 		memset(counter1, 0, sizeof(counter1));
98 		reference_chacha20_blocks(output1, key, counter1, BLOCKS);
99 		for (unsigned int split = 0; split < BLOCKS; ++split) {
100 			memset(output2, 'X', sizeof(output2));
101 			memset(counter2, 0, sizeof(counter2));
102 			if (split)
103 				__arch_chacha20_blocks_nostack(output2, key, counter2, split);
104 			__arch_chacha20_blocks_nostack(output2 + split * BLOCK_SIZE, key, counter2, BLOCKS - split);
105 			if (memcmp(output1, output2, sizeof(output1)) || memcmp(counter1, counter2, sizeof(counter1)))
106 				return KSFT_FAIL;
107 		}
108 	}
109 	memset(counter1, 0, sizeof(counter1));
110 	counter1[0] = (uint32_t)-BLOCKS + 2;
111 	memset(counter2, 0, sizeof(counter2));
112 	counter2[0] = (uint32_t)-BLOCKS + 2;
113 
114 	reference_chacha20_blocks(output1, key, counter1, BLOCKS);
115 	__arch_chacha20_blocks_nostack(output2, key, counter2, BLOCKS);
116 	if (memcmp(output1, output2, sizeof(output1)) || memcmp(counter1, counter2, sizeof(counter1)))
117 		return KSFT_FAIL;
118 
119 	reference_chacha20_blocks(output1, key, counter1, BLOCKS);
120 	__arch_chacha20_blocks_nostack(output2, key, counter2, BLOCKS);
121 	if (memcmp(output1, output2, sizeof(output1)) || memcmp(counter1, counter2, sizeof(counter1)))
122 		return KSFT_FAIL;
123 
124 	ksft_test_result_pass("chacha: PASS\n");
125 	return KSFT_PASS;
126 }
127