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__) 16 static bool cpu_has_capabilities(void) 17 { 18 return getauxval(AT_HWCAP) & HWCAP_ASIMD; 19 } 20 #elif defined(__s390x__) 21 static bool cpu_has_capabilities(void) 22 { 23 return getauxval(AT_HWCAP) & HWCAP_S390_VXRS; 24 } 25 #else 26 static bool cpu_has_capabilities(void) 27 { 28 return true; 29 } 30 #endif 31 32 static uint32_t rol32(uint32_t word, unsigned int shift) 33 { 34 return (word << (shift & 31)) | (word >> ((-shift) & 31)); 35 } 36 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 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 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