at master 4.7 kB view raw
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Crypto API wrappers for the ChaCha20, XChaCha20, and XChaCha12 stream ciphers 4 * 5 * Copyright (C) 2015 Martin Willi 6 * Copyright (C) 2018 Google LLC 7 */ 8 9#include <linux/unaligned.h> 10#include <crypto/algapi.h> 11#include <crypto/chacha.h> 12#include <crypto/internal/skcipher.h> 13#include <linux/module.h> 14 15struct chacha_ctx { 16 u32 key[8]; 17 int nrounds; 18}; 19 20static int chacha_setkey(struct crypto_skcipher *tfm, 21 const u8 *key, unsigned int keysize, int nrounds) 22{ 23 struct chacha_ctx *ctx = crypto_skcipher_ctx(tfm); 24 int i; 25 26 if (keysize != CHACHA_KEY_SIZE) 27 return -EINVAL; 28 29 for (i = 0; i < ARRAY_SIZE(ctx->key); i++) 30 ctx->key[i] = get_unaligned_le32(key + i * sizeof(u32)); 31 32 ctx->nrounds = nrounds; 33 return 0; 34} 35 36static int chacha20_setkey(struct crypto_skcipher *tfm, 37 const u8 *key, unsigned int keysize) 38{ 39 return chacha_setkey(tfm, key, keysize, 20); 40} 41 42static int chacha12_setkey(struct crypto_skcipher *tfm, 43 const u8 *key, unsigned int keysize) 44{ 45 return chacha_setkey(tfm, key, keysize, 12); 46} 47 48static int chacha_stream_xor(struct skcipher_request *req, 49 const struct chacha_ctx *ctx, 50 const u8 iv[CHACHA_IV_SIZE]) 51{ 52 struct skcipher_walk walk; 53 struct chacha_state state; 54 int err; 55 56 err = skcipher_walk_virt(&walk, req, false); 57 58 chacha_init(&state, ctx->key, iv); 59 60 while (walk.nbytes > 0) { 61 unsigned int nbytes = walk.nbytes; 62 63 if (nbytes < walk.total) 64 nbytes = round_down(nbytes, CHACHA_BLOCK_SIZE); 65 66 chacha_crypt(&state, walk.dst.virt.addr, walk.src.virt.addr, 67 nbytes, ctx->nrounds); 68 err = skcipher_walk_done(&walk, walk.nbytes - nbytes); 69 } 70 71 return err; 72} 73 74static int crypto_chacha_crypt(struct skcipher_request *req) 75{ 76 struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); 77 const struct chacha_ctx *ctx = crypto_skcipher_ctx(tfm); 78 79 return chacha_stream_xor(req, ctx, req->iv); 80} 81 82static int crypto_xchacha_crypt(struct skcipher_request *req) 83{ 84 struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); 85 const struct chacha_ctx *ctx = crypto_skcipher_ctx(tfm); 86 struct chacha_ctx subctx; 87 struct chacha_state state; 88 u8 real_iv[16]; 89 90 /* Compute the subkey given the original key and first 128 nonce bits */ 91 chacha_init(&state, ctx->key, req->iv); 92 hchacha_block(&state, subctx.key, ctx->nrounds); 93 subctx.nrounds = ctx->nrounds; 94 95 /* Build the real IV */ 96 memcpy(&real_iv[0], req->iv + 24, 8); /* stream position */ 97 memcpy(&real_iv[8], req->iv + 16, 8); /* remaining 64 nonce bits */ 98 99 /* Generate the stream and XOR it with the data */ 100 return chacha_stream_xor(req, &subctx, real_iv); 101} 102 103static struct skcipher_alg algs[] = { 104 { 105 .base.cra_name = "chacha20", 106 .base.cra_driver_name = "chacha20-lib", 107 .base.cra_priority = 300, 108 .base.cra_blocksize = 1, 109 .base.cra_ctxsize = sizeof(struct chacha_ctx), 110 .base.cra_module = THIS_MODULE, 111 112 .min_keysize = CHACHA_KEY_SIZE, 113 .max_keysize = CHACHA_KEY_SIZE, 114 .ivsize = CHACHA_IV_SIZE, 115 .chunksize = CHACHA_BLOCK_SIZE, 116 .setkey = chacha20_setkey, 117 .encrypt = crypto_chacha_crypt, 118 .decrypt = crypto_chacha_crypt, 119 }, 120 { 121 .base.cra_name = "xchacha20", 122 .base.cra_driver_name = "xchacha20-lib", 123 .base.cra_priority = 300, 124 .base.cra_blocksize = 1, 125 .base.cra_ctxsize = sizeof(struct chacha_ctx), 126 .base.cra_module = THIS_MODULE, 127 128 .min_keysize = CHACHA_KEY_SIZE, 129 .max_keysize = CHACHA_KEY_SIZE, 130 .ivsize = XCHACHA_IV_SIZE, 131 .chunksize = CHACHA_BLOCK_SIZE, 132 .setkey = chacha20_setkey, 133 .encrypt = crypto_xchacha_crypt, 134 .decrypt = crypto_xchacha_crypt, 135 }, 136 { 137 .base.cra_name = "xchacha12", 138 .base.cra_driver_name = "xchacha12-lib", 139 .base.cra_priority = 300, 140 .base.cra_blocksize = 1, 141 .base.cra_ctxsize = sizeof(struct chacha_ctx), 142 .base.cra_module = THIS_MODULE, 143 144 .min_keysize = CHACHA_KEY_SIZE, 145 .max_keysize = CHACHA_KEY_SIZE, 146 .ivsize = XCHACHA_IV_SIZE, 147 .chunksize = CHACHA_BLOCK_SIZE, 148 .setkey = chacha12_setkey, 149 .encrypt = crypto_xchacha_crypt, 150 .decrypt = crypto_xchacha_crypt, 151 } 152}; 153 154static int __init crypto_chacha_mod_init(void) 155{ 156 return crypto_register_skciphers(algs, ARRAY_SIZE(algs)); 157} 158 159static void __exit crypto_chacha_mod_fini(void) 160{ 161 crypto_unregister_skciphers(algs, ARRAY_SIZE(algs)); 162} 163 164module_init(crypto_chacha_mod_init); 165module_exit(crypto_chacha_mod_fini); 166 167MODULE_LICENSE("GPL"); 168MODULE_AUTHOR("Martin Willi <martin@strongswan.org>"); 169MODULE_DESCRIPTION("Crypto API wrappers for the ChaCha20, XChaCha20, and XChaCha12 stream ciphers"); 170MODULE_ALIAS_CRYPTO("chacha20"); 171MODULE_ALIAS_CRYPTO("chacha20-lib"); 172MODULE_ALIAS_CRYPTO("xchacha20"); 173MODULE_ALIAS_CRYPTO("xchacha20-lib"); 174MODULE_ALIAS_CRYPTO("xchacha12"); 175MODULE_ALIAS_CRYPTO("xchacha12-lib");