18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ChaCha and XChaCha stream ciphers, including ChaCha20 (RFC7539) 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015 Martin Willi 68c2ecf20Sopenharmony_ci * Copyright (C) 2018 Google LLC 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 108c2ecf20Sopenharmony_ci#include <crypto/algapi.h> 118c2ecf20Sopenharmony_ci#include <crypto/internal/chacha.h> 128c2ecf20Sopenharmony_ci#include <crypto/internal/skcipher.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_cistatic int chacha_stream_xor(struct skcipher_request *req, 168c2ecf20Sopenharmony_ci const struct chacha_ctx *ctx, const u8 *iv) 178c2ecf20Sopenharmony_ci{ 188c2ecf20Sopenharmony_ci struct skcipher_walk walk; 198c2ecf20Sopenharmony_ci u32 state[16]; 208c2ecf20Sopenharmony_ci int err; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci err = skcipher_walk_virt(&walk, req, false); 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci chacha_init_generic(state, ctx->key, iv); 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci while (walk.nbytes > 0) { 278c2ecf20Sopenharmony_ci unsigned int nbytes = walk.nbytes; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci if (nbytes < walk.total) 308c2ecf20Sopenharmony_ci nbytes = round_down(nbytes, CHACHA_BLOCK_SIZE); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci chacha_crypt_generic(state, walk.dst.virt.addr, 338c2ecf20Sopenharmony_ci walk.src.virt.addr, nbytes, ctx->nrounds); 348c2ecf20Sopenharmony_ci err = skcipher_walk_done(&walk, walk.nbytes - nbytes); 358c2ecf20Sopenharmony_ci } 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci return err; 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int crypto_chacha_crypt(struct skcipher_request *req) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); 438c2ecf20Sopenharmony_ci struct chacha_ctx *ctx = crypto_skcipher_ctx(tfm); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci return chacha_stream_xor(req, ctx, req->iv); 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int crypto_xchacha_crypt(struct skcipher_request *req) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); 518c2ecf20Sopenharmony_ci struct chacha_ctx *ctx = crypto_skcipher_ctx(tfm); 528c2ecf20Sopenharmony_ci struct chacha_ctx subctx; 538c2ecf20Sopenharmony_ci u32 state[16]; 548c2ecf20Sopenharmony_ci u8 real_iv[16]; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci /* Compute the subkey given the original key and first 128 nonce bits */ 578c2ecf20Sopenharmony_ci chacha_init_generic(state, ctx->key, req->iv); 588c2ecf20Sopenharmony_ci hchacha_block_generic(state, subctx.key, ctx->nrounds); 598c2ecf20Sopenharmony_ci subctx.nrounds = ctx->nrounds; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci /* Build the real IV */ 628c2ecf20Sopenharmony_ci memcpy(&real_iv[0], req->iv + 24, 8); /* stream position */ 638c2ecf20Sopenharmony_ci memcpy(&real_iv[8], req->iv + 16, 8); /* remaining 64 nonce bits */ 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci /* Generate the stream and XOR it with the data */ 668c2ecf20Sopenharmony_ci return chacha_stream_xor(req, &subctx, real_iv); 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic struct skcipher_alg algs[] = { 708c2ecf20Sopenharmony_ci { 718c2ecf20Sopenharmony_ci .base.cra_name = "chacha20", 728c2ecf20Sopenharmony_ci .base.cra_driver_name = "chacha20-generic", 738c2ecf20Sopenharmony_ci .base.cra_priority = 100, 748c2ecf20Sopenharmony_ci .base.cra_blocksize = 1, 758c2ecf20Sopenharmony_ci .base.cra_ctxsize = sizeof(struct chacha_ctx), 768c2ecf20Sopenharmony_ci .base.cra_module = THIS_MODULE, 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci .min_keysize = CHACHA_KEY_SIZE, 798c2ecf20Sopenharmony_ci .max_keysize = CHACHA_KEY_SIZE, 808c2ecf20Sopenharmony_ci .ivsize = CHACHA_IV_SIZE, 818c2ecf20Sopenharmony_ci .chunksize = CHACHA_BLOCK_SIZE, 828c2ecf20Sopenharmony_ci .setkey = chacha20_setkey, 838c2ecf20Sopenharmony_ci .encrypt = crypto_chacha_crypt, 848c2ecf20Sopenharmony_ci .decrypt = crypto_chacha_crypt, 858c2ecf20Sopenharmony_ci }, { 868c2ecf20Sopenharmony_ci .base.cra_name = "xchacha20", 878c2ecf20Sopenharmony_ci .base.cra_driver_name = "xchacha20-generic", 888c2ecf20Sopenharmony_ci .base.cra_priority = 100, 898c2ecf20Sopenharmony_ci .base.cra_blocksize = 1, 908c2ecf20Sopenharmony_ci .base.cra_ctxsize = sizeof(struct chacha_ctx), 918c2ecf20Sopenharmony_ci .base.cra_module = THIS_MODULE, 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci .min_keysize = CHACHA_KEY_SIZE, 948c2ecf20Sopenharmony_ci .max_keysize = CHACHA_KEY_SIZE, 958c2ecf20Sopenharmony_ci .ivsize = XCHACHA_IV_SIZE, 968c2ecf20Sopenharmony_ci .chunksize = CHACHA_BLOCK_SIZE, 978c2ecf20Sopenharmony_ci .setkey = chacha20_setkey, 988c2ecf20Sopenharmony_ci .encrypt = crypto_xchacha_crypt, 998c2ecf20Sopenharmony_ci .decrypt = crypto_xchacha_crypt, 1008c2ecf20Sopenharmony_ci }, { 1018c2ecf20Sopenharmony_ci .base.cra_name = "xchacha12", 1028c2ecf20Sopenharmony_ci .base.cra_driver_name = "xchacha12-generic", 1038c2ecf20Sopenharmony_ci .base.cra_priority = 100, 1048c2ecf20Sopenharmony_ci .base.cra_blocksize = 1, 1058c2ecf20Sopenharmony_ci .base.cra_ctxsize = sizeof(struct chacha_ctx), 1068c2ecf20Sopenharmony_ci .base.cra_module = THIS_MODULE, 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci .min_keysize = CHACHA_KEY_SIZE, 1098c2ecf20Sopenharmony_ci .max_keysize = CHACHA_KEY_SIZE, 1108c2ecf20Sopenharmony_ci .ivsize = XCHACHA_IV_SIZE, 1118c2ecf20Sopenharmony_ci .chunksize = CHACHA_BLOCK_SIZE, 1128c2ecf20Sopenharmony_ci .setkey = chacha12_setkey, 1138c2ecf20Sopenharmony_ci .encrypt = crypto_xchacha_crypt, 1148c2ecf20Sopenharmony_ci .decrypt = crypto_xchacha_crypt, 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci}; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic int __init chacha_generic_mod_init(void) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci return crypto_register_skciphers(algs, ARRAY_SIZE(algs)); 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic void __exit chacha_generic_mod_fini(void) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci crypto_unregister_skciphers(algs, ARRAY_SIZE(algs)); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cisubsys_initcall(chacha_generic_mod_init); 1298c2ecf20Sopenharmony_cimodule_exit(chacha_generic_mod_fini); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1328c2ecf20Sopenharmony_ciMODULE_AUTHOR("Martin Willi <martin@strongswan.org>"); 1338c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ChaCha and XChaCha stream ciphers (generic)"); 1348c2ecf20Sopenharmony_ciMODULE_ALIAS_CRYPTO("chacha20"); 1358c2ecf20Sopenharmony_ciMODULE_ALIAS_CRYPTO("chacha20-generic"); 1368c2ecf20Sopenharmony_ciMODULE_ALIAS_CRYPTO("xchacha20"); 1378c2ecf20Sopenharmony_ciMODULE_ALIAS_CRYPTO("xchacha20-generic"); 1388c2ecf20Sopenharmony_ciMODULE_ALIAS_CRYPTO("xchacha12"); 1398c2ecf20Sopenharmony_ciMODULE_ALIAS_CRYPTO("xchacha12-generic"); 140