162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Cryptographic API.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Single-block cipher operations.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
862306a36Sopenharmony_ci * Copyright (c) 2005 Herbert Xu <herbert@gondor.apana.org.au>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <crypto/algapi.h>
1262306a36Sopenharmony_ci#include <crypto/internal/cipher.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/crypto.h>
1562306a36Sopenharmony_ci#include <linux/errno.h>
1662306a36Sopenharmony_ci#include <linux/slab.h>
1762306a36Sopenharmony_ci#include <linux/string.h>
1862306a36Sopenharmony_ci#include "internal.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic int setkey_unaligned(struct crypto_cipher *tfm, const u8 *key,
2162306a36Sopenharmony_ci			    unsigned int keylen)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	struct cipher_alg *cia = crypto_cipher_alg(tfm);
2462306a36Sopenharmony_ci	unsigned long alignmask = crypto_cipher_alignmask(tfm);
2562306a36Sopenharmony_ci	int ret;
2662306a36Sopenharmony_ci	u8 *buffer, *alignbuffer;
2762306a36Sopenharmony_ci	unsigned long absize;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	absize = keylen + alignmask;
3062306a36Sopenharmony_ci	buffer = kmalloc(absize, GFP_ATOMIC);
3162306a36Sopenharmony_ci	if (!buffer)
3262306a36Sopenharmony_ci		return -ENOMEM;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	alignbuffer = (u8 *)ALIGN((unsigned long)buffer, alignmask + 1);
3562306a36Sopenharmony_ci	memcpy(alignbuffer, key, keylen);
3662306a36Sopenharmony_ci	ret = cia->cia_setkey(crypto_cipher_tfm(tfm), alignbuffer, keylen);
3762306a36Sopenharmony_ci	memset(alignbuffer, 0, keylen);
3862306a36Sopenharmony_ci	kfree(buffer);
3962306a36Sopenharmony_ci	return ret;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ciint crypto_cipher_setkey(struct crypto_cipher *tfm,
4462306a36Sopenharmony_ci			 const u8 *key, unsigned int keylen)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	struct cipher_alg *cia = crypto_cipher_alg(tfm);
4762306a36Sopenharmony_ci	unsigned long alignmask = crypto_cipher_alignmask(tfm);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	if (keylen < cia->cia_min_keysize || keylen > cia->cia_max_keysize)
5062306a36Sopenharmony_ci		return -EINVAL;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	if ((unsigned long)key & alignmask)
5362306a36Sopenharmony_ci		return setkey_unaligned(tfm, key, keylen);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	return cia->cia_setkey(crypto_cipher_tfm(tfm), key, keylen);
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(crypto_cipher_setkey, CRYPTO_INTERNAL);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic inline void cipher_crypt_one(struct crypto_cipher *tfm,
6062306a36Sopenharmony_ci				    u8 *dst, const u8 *src, bool enc)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	unsigned long alignmask = crypto_cipher_alignmask(tfm);
6362306a36Sopenharmony_ci	struct cipher_alg *cia = crypto_cipher_alg(tfm);
6462306a36Sopenharmony_ci	void (*fn)(struct crypto_tfm *, u8 *, const u8 *) =
6562306a36Sopenharmony_ci		enc ? cia->cia_encrypt : cia->cia_decrypt;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	if (unlikely(((unsigned long)dst | (unsigned long)src) & alignmask)) {
6862306a36Sopenharmony_ci		unsigned int bs = crypto_cipher_blocksize(tfm);
6962306a36Sopenharmony_ci		u8 buffer[MAX_CIPHER_BLOCKSIZE + MAX_CIPHER_ALIGNMASK];
7062306a36Sopenharmony_ci		u8 *tmp = (u8 *)ALIGN((unsigned long)buffer, alignmask + 1);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci		memcpy(tmp, src, bs);
7362306a36Sopenharmony_ci		fn(crypto_cipher_tfm(tfm), tmp, tmp);
7462306a36Sopenharmony_ci		memcpy(dst, tmp, bs);
7562306a36Sopenharmony_ci	} else {
7662306a36Sopenharmony_ci		fn(crypto_cipher_tfm(tfm), dst, src);
7762306a36Sopenharmony_ci	}
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_civoid crypto_cipher_encrypt_one(struct crypto_cipher *tfm,
8162306a36Sopenharmony_ci			       u8 *dst, const u8 *src)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	cipher_crypt_one(tfm, dst, src, true);
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(crypto_cipher_encrypt_one, CRYPTO_INTERNAL);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_civoid crypto_cipher_decrypt_one(struct crypto_cipher *tfm,
8862306a36Sopenharmony_ci			       u8 *dst, const u8 *src)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	cipher_crypt_one(tfm, dst, src, false);
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(crypto_cipher_decrypt_one, CRYPTO_INTERNAL);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistruct crypto_cipher *crypto_clone_cipher(struct crypto_cipher *cipher)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	struct crypto_tfm *tfm = crypto_cipher_tfm(cipher);
9762306a36Sopenharmony_ci	struct crypto_alg *alg = tfm->__crt_alg;
9862306a36Sopenharmony_ci	struct crypto_cipher *ncipher;
9962306a36Sopenharmony_ci	struct crypto_tfm *ntfm;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	if (alg->cra_init)
10262306a36Sopenharmony_ci		return ERR_PTR(-ENOSYS);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	if (unlikely(!crypto_mod_get(alg)))
10562306a36Sopenharmony_ci		return ERR_PTR(-ESTALE);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	ntfm = __crypto_alloc_tfmgfp(alg, CRYPTO_ALG_TYPE_CIPHER,
10862306a36Sopenharmony_ci				     CRYPTO_ALG_TYPE_MASK, GFP_ATOMIC);
10962306a36Sopenharmony_ci	if (IS_ERR(ntfm)) {
11062306a36Sopenharmony_ci		crypto_mod_put(alg);
11162306a36Sopenharmony_ci		return ERR_CAST(ntfm);
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	ntfm->crt_flags = tfm->crt_flags;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	ncipher = __crypto_cipher_cast(ntfm);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	return ncipher;
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(crypto_clone_cipher);
121