14aa6dc90SEric Biggers // SPDX-License-Identifier: GPL-2.0-or-later 24aa6dc90SEric Biggers /* 34aa6dc90SEric Biggers * Crypto API wrappers for the ChaCha20, XChaCha20, and XChaCha12 stream ciphers 44aa6dc90SEric Biggers * 54aa6dc90SEric Biggers * Copyright (C) 2015 Martin Willi 64aa6dc90SEric Biggers * Copyright (C) 2018 Google LLC 74aa6dc90SEric Biggers */ 84aa6dc90SEric Biggers 94aa6dc90SEric Biggers #include <linux/unaligned.h> 104aa6dc90SEric Biggers #include <crypto/algapi.h> 11d23fce15SEric Biggers #include <crypto/chacha.h> 124aa6dc90SEric Biggers #include <crypto/internal/skcipher.h> 134aa6dc90SEric Biggers #include <linux/module.h> 144aa6dc90SEric Biggers 15d23fce15SEric Biggers struct chacha_ctx { 16d23fce15SEric Biggers u32 key[8]; 17d23fce15SEric Biggers int nrounds; 18d23fce15SEric Biggers }; 19d23fce15SEric Biggers 20d23fce15SEric Biggers static int chacha_setkey(struct crypto_skcipher *tfm, 21d23fce15SEric Biggers const u8 *key, unsigned int keysize, int nrounds) 22d23fce15SEric Biggers { 23d23fce15SEric Biggers struct chacha_ctx *ctx = crypto_skcipher_ctx(tfm); 24d23fce15SEric Biggers int i; 25d23fce15SEric Biggers 26d23fce15SEric Biggers if (keysize != CHACHA_KEY_SIZE) 27d23fce15SEric Biggers return -EINVAL; 28d23fce15SEric Biggers 29d23fce15SEric Biggers for (i = 0; i < ARRAY_SIZE(ctx->key); i++) 30d23fce15SEric Biggers ctx->key[i] = get_unaligned_le32(key + i * sizeof(u32)); 31d23fce15SEric Biggers 32d23fce15SEric Biggers ctx->nrounds = nrounds; 33d23fce15SEric Biggers return 0; 34d23fce15SEric Biggers } 35d23fce15SEric Biggers 36d23fce15SEric Biggers static int chacha20_setkey(struct crypto_skcipher *tfm, 37d23fce15SEric Biggers const u8 *key, unsigned int keysize) 38d23fce15SEric Biggers { 39d23fce15SEric Biggers return chacha_setkey(tfm, key, keysize, 20); 40d23fce15SEric Biggers } 41d23fce15SEric Biggers 42d23fce15SEric Biggers static int chacha12_setkey(struct crypto_skcipher *tfm, 43d23fce15SEric Biggers const u8 *key, unsigned int keysize) 44d23fce15SEric Biggers { 45d23fce15SEric Biggers return chacha_setkey(tfm, key, keysize, 12); 46d23fce15SEric Biggers } 47d23fce15SEric Biggers 484aa6dc90SEric Biggers static int chacha_stream_xor(struct skcipher_request *req, 494aa6dc90SEric Biggers const struct chacha_ctx *ctx, const u8 *iv, 504aa6dc90SEric Biggers bool arch) 514aa6dc90SEric Biggers { 524aa6dc90SEric Biggers struct skcipher_walk walk; 534aa6dc90SEric Biggers u32 state[16]; 544aa6dc90SEric Biggers int err; 554aa6dc90SEric Biggers 564aa6dc90SEric Biggers err = skcipher_walk_virt(&walk, req, false); 574aa6dc90SEric Biggers 584aa6dc90SEric Biggers chacha_init(state, ctx->key, iv); 594aa6dc90SEric Biggers 604aa6dc90SEric Biggers while (walk.nbytes > 0) { 614aa6dc90SEric Biggers unsigned int nbytes = walk.nbytes; 624aa6dc90SEric Biggers 634aa6dc90SEric Biggers if (nbytes < walk.total) 644aa6dc90SEric Biggers nbytes = round_down(nbytes, CHACHA_BLOCK_SIZE); 654aa6dc90SEric Biggers 664aa6dc90SEric Biggers if (arch) 674aa6dc90SEric Biggers chacha_crypt(state, walk.dst.virt.addr, 684aa6dc90SEric Biggers walk.src.virt.addr, nbytes, ctx->nrounds); 694aa6dc90SEric Biggers else 704aa6dc90SEric Biggers chacha_crypt_generic(state, walk.dst.virt.addr, 714aa6dc90SEric Biggers walk.src.virt.addr, nbytes, 724aa6dc90SEric Biggers ctx->nrounds); 734aa6dc90SEric Biggers err = skcipher_walk_done(&walk, walk.nbytes - nbytes); 744aa6dc90SEric Biggers } 754aa6dc90SEric Biggers 764aa6dc90SEric Biggers return err; 774aa6dc90SEric Biggers } 784aa6dc90SEric Biggers 794aa6dc90SEric Biggers static int crypto_chacha_crypt_generic(struct skcipher_request *req) 804aa6dc90SEric Biggers { 814aa6dc90SEric Biggers struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); 824aa6dc90SEric Biggers const struct chacha_ctx *ctx = crypto_skcipher_ctx(tfm); 834aa6dc90SEric Biggers 844aa6dc90SEric Biggers return chacha_stream_xor(req, ctx, req->iv, false); 854aa6dc90SEric Biggers } 864aa6dc90SEric Biggers 874aa6dc90SEric Biggers static int crypto_chacha_crypt_arch(struct skcipher_request *req) 884aa6dc90SEric Biggers { 894aa6dc90SEric Biggers struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); 904aa6dc90SEric Biggers const struct chacha_ctx *ctx = crypto_skcipher_ctx(tfm); 914aa6dc90SEric Biggers 924aa6dc90SEric Biggers return chacha_stream_xor(req, ctx, req->iv, true); 934aa6dc90SEric Biggers } 944aa6dc90SEric Biggers 954aa6dc90SEric Biggers static int crypto_xchacha_crypt(struct skcipher_request *req, bool arch) 964aa6dc90SEric Biggers { 974aa6dc90SEric Biggers struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); 984aa6dc90SEric Biggers const struct chacha_ctx *ctx = crypto_skcipher_ctx(tfm); 994aa6dc90SEric Biggers struct chacha_ctx subctx; 1004aa6dc90SEric Biggers u32 state[16]; 1014aa6dc90SEric Biggers u8 real_iv[16]; 1024aa6dc90SEric Biggers 1034aa6dc90SEric Biggers /* Compute the subkey given the original key and first 128 nonce bits */ 1044aa6dc90SEric Biggers chacha_init(state, ctx->key, req->iv); 1054aa6dc90SEric Biggers if (arch) 1064aa6dc90SEric Biggers hchacha_block(state, subctx.key, ctx->nrounds); 1074aa6dc90SEric Biggers else 1084aa6dc90SEric Biggers hchacha_block_generic(state, subctx.key, ctx->nrounds); 1094aa6dc90SEric Biggers subctx.nrounds = ctx->nrounds; 1104aa6dc90SEric Biggers 1114aa6dc90SEric Biggers /* Build the real IV */ 1124aa6dc90SEric Biggers memcpy(&real_iv[0], req->iv + 24, 8); /* stream position */ 1134aa6dc90SEric Biggers memcpy(&real_iv[8], req->iv + 16, 8); /* remaining 64 nonce bits */ 1144aa6dc90SEric Biggers 1154aa6dc90SEric Biggers /* Generate the stream and XOR it with the data */ 1164aa6dc90SEric Biggers return chacha_stream_xor(req, &subctx, real_iv, arch); 1174aa6dc90SEric Biggers } 1184aa6dc90SEric Biggers 1194aa6dc90SEric Biggers static int crypto_xchacha_crypt_generic(struct skcipher_request *req) 1204aa6dc90SEric Biggers { 1214aa6dc90SEric Biggers return crypto_xchacha_crypt(req, false); 1224aa6dc90SEric Biggers } 1234aa6dc90SEric Biggers 1244aa6dc90SEric Biggers static int crypto_xchacha_crypt_arch(struct skcipher_request *req) 1254aa6dc90SEric Biggers { 1264aa6dc90SEric Biggers return crypto_xchacha_crypt(req, true); 1274aa6dc90SEric Biggers } 1284aa6dc90SEric Biggers 1294aa6dc90SEric Biggers static struct skcipher_alg algs[] = { 1304aa6dc90SEric Biggers { 1314aa6dc90SEric Biggers .base.cra_name = "chacha20", 1324aa6dc90SEric Biggers .base.cra_driver_name = "chacha20-generic", 1334aa6dc90SEric Biggers .base.cra_priority = 100, 1344aa6dc90SEric Biggers .base.cra_blocksize = 1, 1354aa6dc90SEric Biggers .base.cra_ctxsize = sizeof(struct chacha_ctx), 1364aa6dc90SEric Biggers .base.cra_module = THIS_MODULE, 1374aa6dc90SEric Biggers 1384aa6dc90SEric Biggers .min_keysize = CHACHA_KEY_SIZE, 1394aa6dc90SEric Biggers .max_keysize = CHACHA_KEY_SIZE, 1404aa6dc90SEric Biggers .ivsize = CHACHA_IV_SIZE, 1414aa6dc90SEric Biggers .chunksize = CHACHA_BLOCK_SIZE, 1424aa6dc90SEric Biggers .setkey = chacha20_setkey, 1434aa6dc90SEric Biggers .encrypt = crypto_chacha_crypt_generic, 1444aa6dc90SEric Biggers .decrypt = crypto_chacha_crypt_generic, 1454aa6dc90SEric Biggers }, 1464aa6dc90SEric Biggers { 1474aa6dc90SEric Biggers .base.cra_name = "xchacha20", 1484aa6dc90SEric Biggers .base.cra_driver_name = "xchacha20-generic", 1494aa6dc90SEric Biggers .base.cra_priority = 100, 1504aa6dc90SEric Biggers .base.cra_blocksize = 1, 1514aa6dc90SEric Biggers .base.cra_ctxsize = sizeof(struct chacha_ctx), 1524aa6dc90SEric Biggers .base.cra_module = THIS_MODULE, 1534aa6dc90SEric Biggers 1544aa6dc90SEric Biggers .min_keysize = CHACHA_KEY_SIZE, 1554aa6dc90SEric Biggers .max_keysize = CHACHA_KEY_SIZE, 1564aa6dc90SEric Biggers .ivsize = XCHACHA_IV_SIZE, 1574aa6dc90SEric Biggers .chunksize = CHACHA_BLOCK_SIZE, 1584aa6dc90SEric Biggers .setkey = chacha20_setkey, 1594aa6dc90SEric Biggers .encrypt = crypto_xchacha_crypt_generic, 1604aa6dc90SEric Biggers .decrypt = crypto_xchacha_crypt_generic, 1614aa6dc90SEric Biggers }, 1624aa6dc90SEric Biggers { 1634aa6dc90SEric Biggers .base.cra_name = "xchacha12", 1644aa6dc90SEric Biggers .base.cra_driver_name = "xchacha12-generic", 1654aa6dc90SEric Biggers .base.cra_priority = 100, 1664aa6dc90SEric Biggers .base.cra_blocksize = 1, 1674aa6dc90SEric Biggers .base.cra_ctxsize = sizeof(struct chacha_ctx), 1684aa6dc90SEric Biggers .base.cra_module = THIS_MODULE, 1694aa6dc90SEric Biggers 1704aa6dc90SEric Biggers .min_keysize = CHACHA_KEY_SIZE, 1714aa6dc90SEric Biggers .max_keysize = CHACHA_KEY_SIZE, 1724aa6dc90SEric Biggers .ivsize = XCHACHA_IV_SIZE, 1734aa6dc90SEric Biggers .chunksize = CHACHA_BLOCK_SIZE, 1744aa6dc90SEric Biggers .setkey = chacha12_setkey, 1754aa6dc90SEric Biggers .encrypt = crypto_xchacha_crypt_generic, 1764aa6dc90SEric Biggers .decrypt = crypto_xchacha_crypt_generic, 1774aa6dc90SEric Biggers }, 1784aa6dc90SEric Biggers { 1794aa6dc90SEric Biggers .base.cra_name = "chacha20", 1804aa6dc90SEric Biggers .base.cra_driver_name = "chacha20-" __stringify(ARCH), 1814aa6dc90SEric Biggers .base.cra_priority = 300, 1824aa6dc90SEric Biggers .base.cra_blocksize = 1, 1834aa6dc90SEric Biggers .base.cra_ctxsize = sizeof(struct chacha_ctx), 1844aa6dc90SEric Biggers .base.cra_module = THIS_MODULE, 1854aa6dc90SEric Biggers 1864aa6dc90SEric Biggers .min_keysize = CHACHA_KEY_SIZE, 1874aa6dc90SEric Biggers .max_keysize = CHACHA_KEY_SIZE, 1884aa6dc90SEric Biggers .ivsize = CHACHA_IV_SIZE, 1894aa6dc90SEric Biggers .chunksize = CHACHA_BLOCK_SIZE, 1904aa6dc90SEric Biggers .setkey = chacha20_setkey, 1914aa6dc90SEric Biggers .encrypt = crypto_chacha_crypt_arch, 1924aa6dc90SEric Biggers .decrypt = crypto_chacha_crypt_arch, 1934aa6dc90SEric Biggers }, 1944aa6dc90SEric Biggers { 1954aa6dc90SEric Biggers .base.cra_name = "xchacha20", 1964aa6dc90SEric Biggers .base.cra_driver_name = "xchacha20-" __stringify(ARCH), 1974aa6dc90SEric Biggers .base.cra_priority = 300, 1984aa6dc90SEric Biggers .base.cra_blocksize = 1, 1994aa6dc90SEric Biggers .base.cra_ctxsize = sizeof(struct chacha_ctx), 2004aa6dc90SEric Biggers .base.cra_module = THIS_MODULE, 2014aa6dc90SEric Biggers 2024aa6dc90SEric Biggers .min_keysize = CHACHA_KEY_SIZE, 2034aa6dc90SEric Biggers .max_keysize = CHACHA_KEY_SIZE, 2044aa6dc90SEric Biggers .ivsize = XCHACHA_IV_SIZE, 2054aa6dc90SEric Biggers .chunksize = CHACHA_BLOCK_SIZE, 2064aa6dc90SEric Biggers .setkey = chacha20_setkey, 2074aa6dc90SEric Biggers .encrypt = crypto_xchacha_crypt_arch, 2084aa6dc90SEric Biggers .decrypt = crypto_xchacha_crypt_arch, 2094aa6dc90SEric Biggers }, 2104aa6dc90SEric Biggers { 2114aa6dc90SEric Biggers .base.cra_name = "xchacha12", 2124aa6dc90SEric Biggers .base.cra_driver_name = "xchacha12-" __stringify(ARCH), 2134aa6dc90SEric Biggers .base.cra_priority = 300, 2144aa6dc90SEric Biggers .base.cra_blocksize = 1, 2154aa6dc90SEric Biggers .base.cra_ctxsize = sizeof(struct chacha_ctx), 2164aa6dc90SEric Biggers .base.cra_module = THIS_MODULE, 2174aa6dc90SEric Biggers 2184aa6dc90SEric Biggers .min_keysize = CHACHA_KEY_SIZE, 2194aa6dc90SEric Biggers .max_keysize = CHACHA_KEY_SIZE, 2204aa6dc90SEric Biggers .ivsize = XCHACHA_IV_SIZE, 2214aa6dc90SEric Biggers .chunksize = CHACHA_BLOCK_SIZE, 2224aa6dc90SEric Biggers .setkey = chacha12_setkey, 2234aa6dc90SEric Biggers .encrypt = crypto_xchacha_crypt_arch, 2244aa6dc90SEric Biggers .decrypt = crypto_xchacha_crypt_arch, 2254aa6dc90SEric Biggers } 2264aa6dc90SEric Biggers }; 2274aa6dc90SEric Biggers 2284aa6dc90SEric Biggers static unsigned int num_algs; 2294aa6dc90SEric Biggers 2304aa6dc90SEric Biggers static int __init crypto_chacha_mod_init(void) 2314aa6dc90SEric Biggers { 2324aa6dc90SEric Biggers /* register the arch flavours only if they differ from generic */ 2334aa6dc90SEric Biggers num_algs = ARRAY_SIZE(algs); 2344aa6dc90SEric Biggers BUILD_BUG_ON(ARRAY_SIZE(algs) % 2 != 0); 2354aa6dc90SEric Biggers if (!chacha_is_arch_optimized()) 2364aa6dc90SEric Biggers num_algs /= 2; 2374aa6dc90SEric Biggers 2384aa6dc90SEric Biggers return crypto_register_skciphers(algs, num_algs); 2394aa6dc90SEric Biggers } 2404aa6dc90SEric Biggers 2414aa6dc90SEric Biggers static void __exit crypto_chacha_mod_fini(void) 2424aa6dc90SEric Biggers { 2434aa6dc90SEric Biggers crypto_unregister_skciphers(algs, num_algs); 2444aa6dc90SEric Biggers } 2454aa6dc90SEric Biggers 246*ef93f156SHerbert Xu module_init(crypto_chacha_mod_init); 2474aa6dc90SEric Biggers module_exit(crypto_chacha_mod_fini); 2484aa6dc90SEric Biggers 2494aa6dc90SEric Biggers MODULE_LICENSE("GPL"); 2504aa6dc90SEric Biggers MODULE_AUTHOR("Martin Willi <martin@strongswan.org>"); 2514aa6dc90SEric Biggers MODULE_DESCRIPTION("Crypto API wrappers for the ChaCha20, XChaCha20, and XChaCha12 stream ciphers"); 2524aa6dc90SEric Biggers MODULE_ALIAS_CRYPTO("chacha20"); 2534aa6dc90SEric Biggers MODULE_ALIAS_CRYPTO("chacha20-generic"); 2544aa6dc90SEric Biggers MODULE_ALIAS_CRYPTO("chacha20-" __stringify(ARCH)); 2554aa6dc90SEric Biggers MODULE_ALIAS_CRYPTO("xchacha20"); 2564aa6dc90SEric Biggers MODULE_ALIAS_CRYPTO("xchacha20-generic"); 2574aa6dc90SEric Biggers MODULE_ALIAS_CRYPTO("xchacha20-" __stringify(ARCH)); 2584aa6dc90SEric Biggers MODULE_ALIAS_CRYPTO("xchacha12"); 2594aa6dc90SEric Biggers MODULE_ALIAS_CRYPTO("xchacha12-generic"); 2604aa6dc90SEric Biggers MODULE_ALIAS_CRYPTO("xchacha12-" __stringify(ARCH)); 261