xref: /openbmc/linux/crypto/cipher.c (revision b502d4a0)
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);
37*b502d4a0SHailey Mothershead 	kfree_sensitive(buffer);
38ca7c3938SSebastian Siewior 	return ret;
39ca7c3938SSebastian Siewior 
40ca7c3938SSebastian Siewior }
41ca7c3938SSebastian Siewior 
crypto_cipher_setkey(struct crypto_cipher * tfm,const u8 * key,unsigned int keylen)42e8cfed5eSEric Biggers int crypto_cipher_setkey(struct crypto_cipher *tfm,
43e8cfed5eSEric Biggers 			 const u8 *key, unsigned int keylen)
441da177e4SLinus Torvalds {
45e8cfed5eSEric Biggers 	struct cipher_alg *cia = crypto_cipher_alg(tfm);
46e8cfed5eSEric Biggers 	unsigned long alignmask = crypto_cipher_alignmask(tfm);
471da177e4SLinus Torvalds 
48674f368aSEric Biggers 	if (keylen < cia->cia_min_keysize || keylen > cia->cia_max_keysize)
491da177e4SLinus Torvalds 		return -EINVAL;
50ca7c3938SSebastian Siewior 
51ca7c3938SSebastian Siewior 	if ((unsigned long)key & alignmask)
52ca7c3938SSebastian Siewior 		return setkey_unaligned(tfm, key, keylen);
53ca7c3938SSebastian Siewior 
54e8cfed5eSEric Biggers 	return cia->cia_setkey(crypto_cipher_tfm(tfm), key, keylen);
551da177e4SLinus Torvalds }
560eb76ba2SArd Biesheuvel EXPORT_SYMBOL_NS_GPL(crypto_cipher_setkey, CRYPTO_INTERNAL);
571da177e4SLinus Torvalds 
cipher_crypt_one(struct crypto_cipher * tfm,u8 * dst,const u8 * src,bool enc)58e8cfed5eSEric Biggers static inline void cipher_crypt_one(struct crypto_cipher *tfm,
59e8cfed5eSEric Biggers 				    u8 *dst, const u8 *src, bool enc)
60f28776a3SHerbert Xu {
61e8cfed5eSEric Biggers 	unsigned long alignmask = crypto_cipher_alignmask(tfm);
62e8cfed5eSEric Biggers 	struct cipher_alg *cia = crypto_cipher_alg(tfm);
63e8cfed5eSEric Biggers 	void (*fn)(struct crypto_tfm *, u8 *, const u8 *) =
64e8cfed5eSEric Biggers 		enc ? cia->cia_encrypt : cia->cia_decrypt;
65e8cfed5eSEric Biggers 
66e8cfed5eSEric Biggers 	if (unlikely(((unsigned long)dst | (unsigned long)src) & alignmask)) {
67e8cfed5eSEric Biggers 		unsigned int bs = crypto_cipher_blocksize(tfm);
686650c4deSSalvatore Mesoraca 		u8 buffer[MAX_CIPHER_BLOCKSIZE + MAX_CIPHER_ALIGNMASK];
69f28776a3SHerbert Xu 		u8 *tmp = (u8 *)ALIGN((unsigned long)buffer, alignmask + 1);
70f28776a3SHerbert Xu 
71e8cfed5eSEric Biggers 		memcpy(tmp, src, bs);
72e8cfed5eSEric Biggers 		fn(crypto_cipher_tfm(tfm), tmp, tmp);
73e8cfed5eSEric Biggers 		memcpy(dst, tmp, bs);
74e8cfed5eSEric Biggers 	} else {
75e8cfed5eSEric Biggers 		fn(crypto_cipher_tfm(tfm), dst, src);
76e8cfed5eSEric Biggers 	}
77f28776a3SHerbert Xu }
78f28776a3SHerbert Xu 
crypto_cipher_encrypt_one(struct crypto_cipher * tfm,u8 * dst,const u8 * src)79e8cfed5eSEric Biggers void crypto_cipher_encrypt_one(struct crypto_cipher *tfm,
80f28776a3SHerbert Xu 			       u8 *dst, const u8 *src)
81f28776a3SHerbert Xu {
82e8cfed5eSEric Biggers 	cipher_crypt_one(tfm, dst, src, true);
83f28776a3SHerbert Xu }
840eb76ba2SArd Biesheuvel EXPORT_SYMBOL_NS_GPL(crypto_cipher_encrypt_one, CRYPTO_INTERNAL);
85f28776a3SHerbert Xu 
crypto_cipher_decrypt_one(struct crypto_cipher * tfm,u8 * dst,const u8 * src)86e8cfed5eSEric Biggers void crypto_cipher_decrypt_one(struct crypto_cipher *tfm,
87f28776a3SHerbert Xu 			       u8 *dst, const u8 *src)
88f28776a3SHerbert Xu {
89e8cfed5eSEric Biggers 	cipher_crypt_one(tfm, dst, src, false);
90f28776a3SHerbert Xu }
910eb76ba2SArd Biesheuvel EXPORT_SYMBOL_NS_GPL(crypto_cipher_decrypt_one, CRYPTO_INTERNAL);
9251d8d6d0SHerbert Xu 
crypto_clone_cipher(struct crypto_cipher * cipher)9351d8d6d0SHerbert Xu struct crypto_cipher *crypto_clone_cipher(struct crypto_cipher *cipher)
9451d8d6d0SHerbert Xu {
9551d8d6d0SHerbert Xu 	struct crypto_tfm *tfm = crypto_cipher_tfm(cipher);
9651d8d6d0SHerbert Xu 	struct crypto_alg *alg = tfm->__crt_alg;
9751d8d6d0SHerbert Xu 	struct crypto_cipher *ncipher;
9851d8d6d0SHerbert Xu 	struct crypto_tfm *ntfm;
9951d8d6d0SHerbert Xu 
10051d8d6d0SHerbert Xu 	if (alg->cra_init)
10151d8d6d0SHerbert Xu 		return ERR_PTR(-ENOSYS);
10251d8d6d0SHerbert Xu 
1039979c6e5SDmitry Safonov 	if (unlikely(!crypto_mod_get(alg)))
1049979c6e5SDmitry Safonov 		return ERR_PTR(-ESTALE);
1059979c6e5SDmitry Safonov 
106fa3b3565SHerbert Xu 	ntfm = __crypto_alloc_tfmgfp(alg, CRYPTO_ALG_TYPE_CIPHER,
107fa3b3565SHerbert Xu 				     CRYPTO_ALG_TYPE_MASK, GFP_ATOMIC);
1089979c6e5SDmitry Safonov 	if (IS_ERR(ntfm)) {
1099979c6e5SDmitry Safonov 		crypto_mod_put(alg);
11051d8d6d0SHerbert Xu 		return ERR_CAST(ntfm);
1119979c6e5SDmitry Safonov 	}
11251d8d6d0SHerbert Xu 
11351d8d6d0SHerbert Xu 	ntfm->crt_flags = tfm->crt_flags;
11451d8d6d0SHerbert Xu 
11551d8d6d0SHerbert Xu 	ncipher = __crypto_cipher_cast(ntfm);
11651d8d6d0SHerbert Xu 
11751d8d6d0SHerbert Xu 	return ncipher;
11851d8d6d0SHerbert Xu }
11951d8d6d0SHerbert Xu EXPORT_SYMBOL_GPL(crypto_clone_cipher);
120