162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ChaCha and XChaCha stream ciphers, including ChaCha20 (RFC7539) 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2015 Martin Willi 662306a36Sopenharmony_ci * Copyright (C) 2018 Google LLC 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <asm/unaligned.h> 1062306a36Sopenharmony_ci#include <crypto/algapi.h> 1162306a36Sopenharmony_ci#include <crypto/internal/chacha.h> 1262306a36Sopenharmony_ci#include <crypto/internal/skcipher.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistatic int chacha_stream_xor(struct skcipher_request *req, 1662306a36Sopenharmony_ci const struct chacha_ctx *ctx, const u8 *iv) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci struct skcipher_walk walk; 1962306a36Sopenharmony_ci u32 state[16]; 2062306a36Sopenharmony_ci int err; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci err = skcipher_walk_virt(&walk, req, false); 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci chacha_init_generic(state, ctx->key, iv); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci while (walk.nbytes > 0) { 2762306a36Sopenharmony_ci unsigned int nbytes = walk.nbytes; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci if (nbytes < walk.total) 3062306a36Sopenharmony_ci nbytes = round_down(nbytes, CHACHA_BLOCK_SIZE); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci chacha_crypt_generic(state, walk.dst.virt.addr, 3362306a36Sopenharmony_ci walk.src.virt.addr, nbytes, ctx->nrounds); 3462306a36Sopenharmony_ci err = skcipher_walk_done(&walk, walk.nbytes - nbytes); 3562306a36Sopenharmony_ci } 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci return err; 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic int crypto_chacha_crypt(struct skcipher_request *req) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); 4362306a36Sopenharmony_ci struct chacha_ctx *ctx = crypto_skcipher_ctx(tfm); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci return chacha_stream_xor(req, ctx, req->iv); 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic int crypto_xchacha_crypt(struct skcipher_request *req) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); 5162306a36Sopenharmony_ci struct chacha_ctx *ctx = crypto_skcipher_ctx(tfm); 5262306a36Sopenharmony_ci struct chacha_ctx subctx; 5362306a36Sopenharmony_ci u32 state[16]; 5462306a36Sopenharmony_ci u8 real_iv[16]; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci /* Compute the subkey given the original key and first 128 nonce bits */ 5762306a36Sopenharmony_ci chacha_init_generic(state, ctx->key, req->iv); 5862306a36Sopenharmony_ci hchacha_block_generic(state, subctx.key, ctx->nrounds); 5962306a36Sopenharmony_ci subctx.nrounds = ctx->nrounds; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci /* Build the real IV */ 6262306a36Sopenharmony_ci memcpy(&real_iv[0], req->iv + 24, 8); /* stream position */ 6362306a36Sopenharmony_ci memcpy(&real_iv[8], req->iv + 16, 8); /* remaining 64 nonce bits */ 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* Generate the stream and XOR it with the data */ 6662306a36Sopenharmony_ci return chacha_stream_xor(req, &subctx, real_iv); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic struct skcipher_alg algs[] = { 7062306a36Sopenharmony_ci { 7162306a36Sopenharmony_ci .base.cra_name = "chacha20", 7262306a36Sopenharmony_ci .base.cra_driver_name = "chacha20-generic", 7362306a36Sopenharmony_ci .base.cra_priority = 100, 7462306a36Sopenharmony_ci .base.cra_blocksize = 1, 7562306a36Sopenharmony_ci .base.cra_ctxsize = sizeof(struct chacha_ctx), 7662306a36Sopenharmony_ci .base.cra_module = THIS_MODULE, 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci .min_keysize = CHACHA_KEY_SIZE, 7962306a36Sopenharmony_ci .max_keysize = CHACHA_KEY_SIZE, 8062306a36Sopenharmony_ci .ivsize = CHACHA_IV_SIZE, 8162306a36Sopenharmony_ci .chunksize = CHACHA_BLOCK_SIZE, 8262306a36Sopenharmony_ci .setkey = chacha20_setkey, 8362306a36Sopenharmony_ci .encrypt = crypto_chacha_crypt, 8462306a36Sopenharmony_ci .decrypt = crypto_chacha_crypt, 8562306a36Sopenharmony_ci }, { 8662306a36Sopenharmony_ci .base.cra_name = "xchacha20", 8762306a36Sopenharmony_ci .base.cra_driver_name = "xchacha20-generic", 8862306a36Sopenharmony_ci .base.cra_priority = 100, 8962306a36Sopenharmony_ci .base.cra_blocksize = 1, 9062306a36Sopenharmony_ci .base.cra_ctxsize = sizeof(struct chacha_ctx), 9162306a36Sopenharmony_ci .base.cra_module = THIS_MODULE, 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci .min_keysize = CHACHA_KEY_SIZE, 9462306a36Sopenharmony_ci .max_keysize = CHACHA_KEY_SIZE, 9562306a36Sopenharmony_ci .ivsize = XCHACHA_IV_SIZE, 9662306a36Sopenharmony_ci .chunksize = CHACHA_BLOCK_SIZE, 9762306a36Sopenharmony_ci .setkey = chacha20_setkey, 9862306a36Sopenharmony_ci .encrypt = crypto_xchacha_crypt, 9962306a36Sopenharmony_ci .decrypt = crypto_xchacha_crypt, 10062306a36Sopenharmony_ci }, { 10162306a36Sopenharmony_ci .base.cra_name = "xchacha12", 10262306a36Sopenharmony_ci .base.cra_driver_name = "xchacha12-generic", 10362306a36Sopenharmony_ci .base.cra_priority = 100, 10462306a36Sopenharmony_ci .base.cra_blocksize = 1, 10562306a36Sopenharmony_ci .base.cra_ctxsize = sizeof(struct chacha_ctx), 10662306a36Sopenharmony_ci .base.cra_module = THIS_MODULE, 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci .min_keysize = CHACHA_KEY_SIZE, 10962306a36Sopenharmony_ci .max_keysize = CHACHA_KEY_SIZE, 11062306a36Sopenharmony_ci .ivsize = XCHACHA_IV_SIZE, 11162306a36Sopenharmony_ci .chunksize = CHACHA_BLOCK_SIZE, 11262306a36Sopenharmony_ci .setkey = chacha12_setkey, 11362306a36Sopenharmony_ci .encrypt = crypto_xchacha_crypt, 11462306a36Sopenharmony_ci .decrypt = crypto_xchacha_crypt, 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci}; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic int __init chacha_generic_mod_init(void) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci return crypto_register_skciphers(algs, ARRAY_SIZE(algs)); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic void __exit chacha_generic_mod_fini(void) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci crypto_unregister_skciphers(algs, ARRAY_SIZE(algs)); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cisubsys_initcall(chacha_generic_mod_init); 12962306a36Sopenharmony_cimodule_exit(chacha_generic_mod_fini); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 13262306a36Sopenharmony_ciMODULE_AUTHOR("Martin Willi <martin@strongswan.org>"); 13362306a36Sopenharmony_ciMODULE_DESCRIPTION("ChaCha and XChaCha stream ciphers (generic)"); 13462306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("chacha20"); 13562306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("chacha20-generic"); 13662306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("xchacha20"); 13762306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("xchacha20-generic"); 13862306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("xchacha12"); 13962306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("xchacha12-generic"); 140