diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2023-06-15 18:28:48 +0800 |
---|---|---|
committer | Herbert Xu <herbert@gondor.apana.org.au> | 2023-06-23 16:15:36 +0800 |
commit | 6cb8815f41a966b217c0d9826c592254d72dcc31 (patch) | |
tree | ddad1988bbe9ec6050e10eeecf2f456039b8bb3c /crypto/sig.c | |
parent | addde1f2c966833f210e9318b17050293086b8c6 (diff) | |
download | linux-6cb8815f41a966b217c0d9826c592254d72dcc31.tar.gz linux-6cb8815f41a966b217c0d9826c592254d72dcc31.tar.bz2 linux-6cb8815f41a966b217c0d9826c592254d72dcc31.zip |
crypto: sig - Add interface for sign/verify
Split out the sign/verify functionality from the existing akcipher
interface. Most algorithms in akcipher either support encryption
and decryption, or signing and verify. Only one supports both.
As a signature algorithm may not support encryption at all, these
two should be spearated.
For now sig is simply a wrapper around akcipher as all algorithms
remain unchanged. This is a first step and allows users to start
allocating sig instead of akcipher.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Diffstat (limited to 'crypto/sig.c')
-rw-r--r-- | crypto/sig.c | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/crypto/sig.c b/crypto/sig.c new file mode 100644 index 000000000000..d812555c88af --- /dev/null +++ b/crypto/sig.c @@ -0,0 +1,159 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Public Key Signature Algorithm + * + * Copyright (c) 2023 Herbert Xu <herbert@gondor.apana.org.au> + */ + +#include <crypto/akcipher.h> +#include <crypto/internal/sig.h> +#include <linux/cryptouser.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/scatterlist.h> +#include <linux/seq_file.h> +#include <linux/string.h> +#include <net/netlink.h> + +#include "internal.h" + +#define CRYPTO_ALG_TYPE_SIG_MASK 0x0000000e + +static const struct crypto_type crypto_sig_type; + +static inline struct crypto_sig *__crypto_sig_tfm(struct crypto_tfm *tfm) +{ + return container_of(tfm, struct crypto_sig, base); +} + +static int crypto_sig_init_tfm(struct crypto_tfm *tfm) +{ + if (tfm->__crt_alg->cra_type != &crypto_sig_type) + return crypto_init_akcipher_ops_sig(tfm); + + return 0; +} + +static void __maybe_unused crypto_sig_show(struct seq_file *m, + struct crypto_alg *alg) +{ + seq_puts(m, "type : sig\n"); +} + +static int __maybe_unused crypto_sig_report(struct sk_buff *skb, + struct crypto_alg *alg) +{ + struct crypto_report_akcipher rsig = {}; + + strscpy(rsig.type, "sig", sizeof(rsig.type)); + + return nla_put(skb, CRYPTOCFGA_REPORT_AKCIPHER, sizeof(rsig), &rsig); +} + +static int __maybe_unused crypto_sig_report_stat(struct sk_buff *skb, + struct crypto_alg *alg) +{ + struct crypto_stat_akcipher rsig = {}; + + strscpy(rsig.type, "sig", sizeof(rsig.type)); + + return nla_put(skb, CRYPTOCFGA_STAT_AKCIPHER, sizeof(rsig), &rsig); +} + +static const struct crypto_type crypto_sig_type = { + .extsize = crypto_alg_extsize, + .init_tfm = crypto_sig_init_tfm, +#ifdef CONFIG_PROC_FS + .show = crypto_sig_show, +#endif +#if IS_ENABLED(CONFIG_CRYPTO_USER) + .report = crypto_sig_report, +#endif +#ifdef CONFIG_CRYPTO_STATS + .report_stat = crypto_sig_report_stat, +#endif + .maskclear = ~CRYPTO_ALG_TYPE_MASK, + .maskset = CRYPTO_ALG_TYPE_SIG_MASK, + .type = CRYPTO_ALG_TYPE_SIG, + .tfmsize = offsetof(struct crypto_sig, base), +}; + +struct crypto_sig *crypto_alloc_sig(const char *alg_name, u32 type, u32 mask) +{ + return crypto_alloc_tfm(alg_name, &crypto_sig_type, type, mask); +} +EXPORT_SYMBOL_GPL(crypto_alloc_sig); + +int crypto_sig_maxsize(struct crypto_sig *tfm) +{ + struct crypto_akcipher **ctx = crypto_sig_ctx(tfm); + + return crypto_akcipher_maxsize(*ctx); +} +EXPORT_SYMBOL_GPL(crypto_sig_maxsize); + +int crypto_sig_sign(struct crypto_sig *tfm, + const void *src, unsigned int slen, + void *dst, unsigned int dlen) +{ + struct crypto_akcipher **ctx = crypto_sig_ctx(tfm); + struct crypto_akcipher_sync_data data = { + .tfm = *ctx, + .src = src, + .dst = dst, + .slen = slen, + .dlen = dlen, + }; + + return crypto_akcipher_sync_prep(&data) ?: + crypto_akcipher_sync_post(&data, + crypto_akcipher_sign(data.req)); +} +EXPORT_SYMBOL_GPL(crypto_sig_sign); + +int crypto_sig_verify(struct crypto_sig *tfm, + const void *src, unsigned int slen, + const void *digest, unsigned int dlen) +{ + struct crypto_akcipher **ctx = crypto_sig_ctx(tfm); + struct crypto_akcipher_sync_data data = { + .tfm = *ctx, + .src = src, + .slen = slen, + .dlen = dlen, + }; + int err; + + err = crypto_akcipher_sync_prep(&data); + if (err) + return err; + + sg_init_table(data.sg, 2); + sg_set_buf(&data.sg[0], src, slen); + sg_set_buf(&data.sg[1], digest, dlen); + + return crypto_akcipher_sync_post(&data, + crypto_akcipher_verify(data.req)); +} +EXPORT_SYMBOL_GPL(crypto_sig_verify); + +int crypto_sig_set_pubkey(struct crypto_sig *tfm, + const void *key, unsigned int keylen) +{ + struct crypto_akcipher **ctx = crypto_sig_ctx(tfm); + + return crypto_akcipher_set_pub_key(*ctx, key, keylen); +} +EXPORT_SYMBOL_GPL(crypto_sig_set_pubkey); + +int crypto_sig_set_privkey(struct crypto_sig *tfm, + const void *key, unsigned int keylen) +{ + struct crypto_akcipher **ctx = crypto_sig_ctx(tfm); + + return crypto_akcipher_set_priv_key(*ctx, key, keylen); +} +EXPORT_SYMBOL_GPL(crypto_sig_set_privkey); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Public Key Signature Algorithms"); |