18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * The "hash function" used as the core of the ChaCha stream cipher (RFC7539) 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015 Martin Willi 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/bug.h> 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/export.h> 118c2ecf20Sopenharmony_ci#include <linux/bitops.h> 128c2ecf20Sopenharmony_ci#include <linux/string.h> 138c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 148c2ecf20Sopenharmony_ci#include <crypto/chacha.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic void chacha_permute(u32 *x, int nrounds) 178c2ecf20Sopenharmony_ci{ 188c2ecf20Sopenharmony_ci int i; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci /* whitelist the allowed round counts */ 218c2ecf20Sopenharmony_ci WARN_ON_ONCE(nrounds != 20 && nrounds != 12); 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci for (i = 0; i < nrounds; i += 2) { 248c2ecf20Sopenharmony_ci x[0] += x[4]; x[12] = rol32(x[12] ^ x[0], 16); 258c2ecf20Sopenharmony_ci x[1] += x[5]; x[13] = rol32(x[13] ^ x[1], 16); 268c2ecf20Sopenharmony_ci x[2] += x[6]; x[14] = rol32(x[14] ^ x[2], 16); 278c2ecf20Sopenharmony_ci x[3] += x[7]; x[15] = rol32(x[15] ^ x[3], 16); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci x[8] += x[12]; x[4] = rol32(x[4] ^ x[8], 12); 308c2ecf20Sopenharmony_ci x[9] += x[13]; x[5] = rol32(x[5] ^ x[9], 12); 318c2ecf20Sopenharmony_ci x[10] += x[14]; x[6] = rol32(x[6] ^ x[10], 12); 328c2ecf20Sopenharmony_ci x[11] += x[15]; x[7] = rol32(x[7] ^ x[11], 12); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci x[0] += x[4]; x[12] = rol32(x[12] ^ x[0], 8); 358c2ecf20Sopenharmony_ci x[1] += x[5]; x[13] = rol32(x[13] ^ x[1], 8); 368c2ecf20Sopenharmony_ci x[2] += x[6]; x[14] = rol32(x[14] ^ x[2], 8); 378c2ecf20Sopenharmony_ci x[3] += x[7]; x[15] = rol32(x[15] ^ x[3], 8); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci x[8] += x[12]; x[4] = rol32(x[4] ^ x[8], 7); 408c2ecf20Sopenharmony_ci x[9] += x[13]; x[5] = rol32(x[5] ^ x[9], 7); 418c2ecf20Sopenharmony_ci x[10] += x[14]; x[6] = rol32(x[6] ^ x[10], 7); 428c2ecf20Sopenharmony_ci x[11] += x[15]; x[7] = rol32(x[7] ^ x[11], 7); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci x[0] += x[5]; x[15] = rol32(x[15] ^ x[0], 16); 458c2ecf20Sopenharmony_ci x[1] += x[6]; x[12] = rol32(x[12] ^ x[1], 16); 468c2ecf20Sopenharmony_ci x[2] += x[7]; x[13] = rol32(x[13] ^ x[2], 16); 478c2ecf20Sopenharmony_ci x[3] += x[4]; x[14] = rol32(x[14] ^ x[3], 16); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci x[10] += x[15]; x[5] = rol32(x[5] ^ x[10], 12); 508c2ecf20Sopenharmony_ci x[11] += x[12]; x[6] = rol32(x[6] ^ x[11], 12); 518c2ecf20Sopenharmony_ci x[8] += x[13]; x[7] = rol32(x[7] ^ x[8], 12); 528c2ecf20Sopenharmony_ci x[9] += x[14]; x[4] = rol32(x[4] ^ x[9], 12); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci x[0] += x[5]; x[15] = rol32(x[15] ^ x[0], 8); 558c2ecf20Sopenharmony_ci x[1] += x[6]; x[12] = rol32(x[12] ^ x[1], 8); 568c2ecf20Sopenharmony_ci x[2] += x[7]; x[13] = rol32(x[13] ^ x[2], 8); 578c2ecf20Sopenharmony_ci x[3] += x[4]; x[14] = rol32(x[14] ^ x[3], 8); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci x[10] += x[15]; x[5] = rol32(x[5] ^ x[10], 7); 608c2ecf20Sopenharmony_ci x[11] += x[12]; x[6] = rol32(x[6] ^ x[11], 7); 618c2ecf20Sopenharmony_ci x[8] += x[13]; x[7] = rol32(x[7] ^ x[8], 7); 628c2ecf20Sopenharmony_ci x[9] += x[14]; x[4] = rol32(x[4] ^ x[9], 7); 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/** 678c2ecf20Sopenharmony_ci * chacha_block - generate one keystream block and increment block counter 688c2ecf20Sopenharmony_ci * @state: input state matrix (16 32-bit words) 698c2ecf20Sopenharmony_ci * @stream: output keystream block (64 bytes) 708c2ecf20Sopenharmony_ci * @nrounds: number of rounds (20 or 12; 20 is recommended) 718c2ecf20Sopenharmony_ci * 728c2ecf20Sopenharmony_ci * This is the ChaCha core, a function from 64-byte strings to 64-byte strings. 738c2ecf20Sopenharmony_ci * The caller has already converted the endianness of the input. This function 748c2ecf20Sopenharmony_ci * also handles incrementing the block counter in the input matrix. 758c2ecf20Sopenharmony_ci */ 768c2ecf20Sopenharmony_civoid chacha_block_generic(u32 *state, u8 *stream, int nrounds) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci u32 x[16]; 798c2ecf20Sopenharmony_ci int i; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci memcpy(x, state, 64); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci chacha_permute(x, nrounds); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(x); i++) 868c2ecf20Sopenharmony_ci put_unaligned_le32(x[i] + state[i], &stream[i * sizeof(u32)]); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci state[12]++; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ciEXPORT_SYMBOL(chacha_block_generic); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/** 938c2ecf20Sopenharmony_ci * hchacha_block_generic - abbreviated ChaCha core, for XChaCha 948c2ecf20Sopenharmony_ci * @state: input state matrix (16 32-bit words) 958c2ecf20Sopenharmony_ci * @out: output (8 32-bit words) 968c2ecf20Sopenharmony_ci * @nrounds: number of rounds (20 or 12; 20 is recommended) 978c2ecf20Sopenharmony_ci * 988c2ecf20Sopenharmony_ci * HChaCha is the ChaCha equivalent of HSalsa and is an intermediate step 998c2ecf20Sopenharmony_ci * towards XChaCha (see https://cr.yp.to/snuffle/xsalsa-20081128.pdf). HChaCha 1008c2ecf20Sopenharmony_ci * skips the final addition of the initial state, and outputs only certain words 1018c2ecf20Sopenharmony_ci * of the state. It should not be used for streaming directly. 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_civoid hchacha_block_generic(const u32 *state, u32 *stream, int nrounds) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci u32 x[16]; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci memcpy(x, state, 64); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci chacha_permute(x, nrounds); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci memcpy(&stream[0], &x[0], 16); 1128c2ecf20Sopenharmony_ci memcpy(&stream[4], &x[12], 16); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hchacha_block_generic); 115