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 15 struct chacha_ctx { 16 u32 key[8]; 17 int nrounds; 18 }; 19 20 static 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 36 static 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 42 static 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 48 static 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 74 static 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 82 static 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 103 static 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 154 static int __init crypto_chacha_mod_init(void) 155 { 156 return crypto_register_skciphers(algs, ARRAY_SIZE(algs)); 157 } 158 159 static void __exit crypto_chacha_mod_fini(void) 160 { 161 crypto_unregister_skciphers(algs, ARRAY_SIZE(algs)); 162 } 163 164 module_init(crypto_chacha_mod_init); 165 module_exit(crypto_chacha_mod_fini); 166 167 MODULE_LICENSE("GPL"); 168 MODULE_AUTHOR("Martin Willi <martin@strongswan.org>"); 169 MODULE_DESCRIPTION("Crypto API wrappers for the ChaCha20, XChaCha20, and XChaCha12 stream ciphers"); 170 MODULE_ALIAS_CRYPTO("chacha20"); 171 MODULE_ALIAS_CRYPTO("chacha20-lib"); 172 MODULE_ALIAS_CRYPTO("xchacha20"); 173 MODULE_ALIAS_CRYPTO("xchacha20-lib"); 174 MODULE_ALIAS_CRYPTO("xchacha12"); 175 MODULE_ALIAS_CRYPTO("xchacha12-lib"); 176