xref: /linux/crypto/cipher.c (revision 9979c6e5)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * Cryptographic API.
41da177e4SLinus Torvalds  *
5e8cfed5eSEric Biggers  * Single-block cipher operations.
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
8c774e93eSHerbert Xu  * Copyright (c) 2005 Herbert Xu <herbert@gondor.apana.org.au>
91da177e4SLinus Torvalds  */
10f1ddcaf3SHerbert Xu 
116650c4deSSalvatore Mesoraca #include <crypto/algapi.h>
120eb76ba2SArd Biesheuvel #include <crypto/internal/cipher.h>
131da177e4SLinus Torvalds #include <linux/kernel.h>
141da177e4SLinus Torvalds #include <linux/crypto.h>
151da177e4SLinus Torvalds #include <linux/errno.h>
16791b4d5fSHerbert Xu #include <linux/slab.h>
171da177e4SLinus Torvalds #include <linux/string.h>
181da177e4SLinus Torvalds #include "internal.h"
191da177e4SLinus Torvalds 
setkey_unaligned(struct crypto_cipher * tfm,const u8 * key,unsigned int keylen)20e8cfed5eSEric Biggers static int setkey_unaligned(struct crypto_cipher *tfm, const u8 *key,
21791b4d5fSHerbert Xu 			    unsigned int keylen)
22ca7c3938SSebastian Siewior {
23e8cfed5eSEric Biggers 	struct cipher_alg *cia = crypto_cipher_alg(tfm);
24e8cfed5eSEric Biggers 	unsigned long alignmask = crypto_cipher_alignmask(tfm);
25ca7c3938SSebastian Siewior 	int ret;
26ca7c3938SSebastian Siewior 	u8 *buffer, *alignbuffer;
27ca7c3938SSebastian Siewior 	unsigned long absize;
28ca7c3938SSebastian Siewior 
29ca7c3938SSebastian Siewior 	absize = keylen + alignmask;
30ca7c3938SSebastian Siewior 	buffer = kmalloc(absize, GFP_ATOMIC);
31ca7c3938SSebastian Siewior 	if (!buffer)
32ca7c3938SSebastian Siewior 		return -ENOMEM;
33ca7c3938SSebastian Siewior 
34ca7c3938SSebastian Siewior 	alignbuffer = (u8 *)ALIGN((unsigned long)buffer, alignmask + 1);
35ca7c3938SSebastian Siewior 	memcpy(alignbuffer, key, keylen);
36e8cfed5eSEric Biggers 	ret = cia->cia_setkey(crypto_cipher_tfm(tfm), alignbuffer, keylen);
3706817176SSebastian Siewior 	memset(alignbuffer, 0, keylen);
38ca7c3938SSebastian Siewior 	kfree(buffer);
39ca7c3938SSebastian Siewior 	return ret;
40ca7c3938SSebastian Siewior 
41ca7c3938SSebastian Siewior }
42ca7c3938SSebastian Siewior 
crypto_cipher_setkey(struct crypto_cipher * tfm,const u8 * key,unsigned int keylen)43e8cfed5eSEric Biggers int crypto_cipher_setkey(struct crypto_cipher *tfm,
44e8cfed5eSEric Biggers 			 const u8 *key, unsigned int keylen)
451da177e4SLinus Torvalds {
46e8cfed5eSEric Biggers 	struct cipher_alg *cia = crypto_cipher_alg(tfm);
47e8cfed5eSEric Biggers 	unsigned long alignmask = crypto_cipher_alignmask(tfm);
481da177e4SLinus Torvalds 
49674f368aSEric Biggers 	if (keylen < cia->cia_min_keysize || keylen > cia->cia_max_keysize)
501da177e4SLinus Torvalds 		return -EINVAL;
51ca7c3938SSebastian Siewior 
52ca7c3938SSebastian Siewior 	if ((unsigned long)key & alignmask)
53ca7c3938SSebastian Siewior 		return setkey_unaligned(tfm, key, keylen);
54ca7c3938SSebastian Siewior 
55e8cfed5eSEric Biggers 	return cia->cia_setkey(crypto_cipher_tfm(tfm), key, keylen);
561da177e4SLinus Torvalds }
570eb76ba2SArd Biesheuvel EXPORT_SYMBOL_NS_GPL(crypto_cipher_setkey, CRYPTO_INTERNAL);
581da177e4SLinus Torvalds 
cipher_crypt_one(struct crypto_cipher * tfm,u8 * dst,const u8 * src,bool enc)59e8cfed5eSEric Biggers static inline void cipher_crypt_one(struct crypto_cipher *tfm,
60e8cfed5eSEric Biggers 				    u8 *dst, const u8 *src, bool enc)
61f28776a3SHerbert Xu {
62e8cfed5eSEric Biggers 	unsigned long alignmask = crypto_cipher_alignmask(tfm);
63e8cfed5eSEric Biggers 	struct cipher_alg *cia = crypto_cipher_alg(tfm);
64e8cfed5eSEric Biggers 	void (*fn)(struct crypto_tfm *, u8 *, const u8 *) =
65e8cfed5eSEric Biggers 		enc ? cia->cia_encrypt : cia->cia_decrypt;
66e8cfed5eSEric Biggers 
67e8cfed5eSEric Biggers 	if (unlikely(((unsigned long)dst | (unsigned long)src) & alignmask)) {
68e8cfed5eSEric Biggers 		unsigned int bs = crypto_cipher_blocksize(tfm);
696650c4deSSalvatore Mesoraca 		u8 buffer[MAX_CIPHER_BLOCKSIZE + MAX_CIPHER_ALIGNMASK];
70f28776a3SHerbert Xu 		u8 *tmp = (u8 *)ALIGN((unsigned long)buffer, alignmask + 1);
71f28776a3SHerbert Xu 
72e8cfed5eSEric Biggers 		memcpy(tmp, src, bs);
73e8cfed5eSEric Biggers 		fn(crypto_cipher_tfm(tfm), tmp, tmp);
74e8cfed5eSEric Biggers 		memcpy(dst, tmp, bs);
75e8cfed5eSEric Biggers 	} else {
76e8cfed5eSEric Biggers 		fn(crypto_cipher_tfm(tfm), dst, src);
77e8cfed5eSEric Biggers 	}
78f28776a3SHerbert Xu }
79f28776a3SHerbert Xu 
crypto_cipher_encrypt_one(struct crypto_cipher * tfm,u8 * dst,const u8 * src)80e8cfed5eSEric Biggers void crypto_cipher_encrypt_one(struct crypto_cipher *tfm,
81f28776a3SHerbert Xu 			       u8 *dst, const u8 *src)
82f28776a3SHerbert Xu {
83e8cfed5eSEric Biggers 	cipher_crypt_one(tfm, dst, src, true);
84f28776a3SHerbert Xu }
850eb76ba2SArd Biesheuvel EXPORT_SYMBOL_NS_GPL(crypto_cipher_encrypt_one, CRYPTO_INTERNAL);
86f28776a3SHerbert Xu 
crypto_cipher_decrypt_one(struct crypto_cipher * tfm,u8 * dst,const u8 * src)87e8cfed5eSEric Biggers void crypto_cipher_decrypt_one(struct crypto_cipher *tfm,
88f28776a3SHerbert Xu 			       u8 *dst, const u8 *src)
89f28776a3SHerbert Xu {
90e8cfed5eSEric Biggers 	cipher_crypt_one(tfm, dst, src, false);
91f28776a3SHerbert Xu }
920eb76ba2SArd Biesheuvel EXPORT_SYMBOL_NS_GPL(crypto_cipher_decrypt_one, CRYPTO_INTERNAL);
9351d8d6d0SHerbert Xu 
crypto_clone_cipher(struct crypto_cipher * cipher)9451d8d6d0SHerbert Xu struct crypto_cipher *crypto_clone_cipher(struct crypto_cipher *cipher)
9551d8d6d0SHerbert Xu {
9651d8d6d0SHerbert Xu 	struct crypto_tfm *tfm = crypto_cipher_tfm(cipher);
9751d8d6d0SHerbert Xu 	struct crypto_alg *alg = tfm->__crt_alg;
9851d8d6d0SHerbert Xu 	struct crypto_cipher *ncipher;
9951d8d6d0SHerbert Xu 	struct crypto_tfm *ntfm;
10051d8d6d0SHerbert Xu 
10151d8d6d0SHerbert Xu 	if (alg->cra_init)
10251d8d6d0SHerbert Xu 		return ERR_PTR(-ENOSYS);
10351d8d6d0SHerbert Xu 
104*9979c6e5SDmitry Safonov 	if (unlikely(!crypto_mod_get(alg)))
105*9979c6e5SDmitry Safonov 		return ERR_PTR(-ESTALE);
106*9979c6e5SDmitry Safonov 
107fa3b3565SHerbert Xu 	ntfm = __crypto_alloc_tfmgfp(alg, CRYPTO_ALG_TYPE_CIPHER,
108fa3b3565SHerbert Xu 				     CRYPTO_ALG_TYPE_MASK, GFP_ATOMIC);
109*9979c6e5SDmitry Safonov 	if (IS_ERR(ntfm)) {
110*9979c6e5SDmitry Safonov 		crypto_mod_put(alg);
11151d8d6d0SHerbert Xu 		return ERR_CAST(ntfm);
112*9979c6e5SDmitry Safonov 	}
11351d8d6d0SHerbert Xu 
11451d8d6d0SHerbert Xu 	ntfm->crt_flags = tfm->crt_flags;
11551d8d6d0SHerbert Xu 
11651d8d6d0SHerbert Xu 	ncipher = __crypto_cipher_cast(ntfm);
11751d8d6d0SHerbert Xu 
11851d8d6d0SHerbert Xu 	return ncipher;
11951d8d6d0SHerbert Xu }
12051d8d6d0SHerbert Xu EXPORT_SYMBOL_GPL(crypto_clone_cipher);
121