Message ID | 688e92e7db6f2de1778691bb7cdafe3bb39e73c6.1725972334.git.lukas@wunner.de (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | Migrate to sig_alg and templatize ecdsa | expand |
On Tue Sep 10, 2024 at 5:30 PM EEST, Lukas Wunner wrote: > Commit 6cb8815f41a9 ("crypto: sig - Add interface for sign/verify") > began a transition of asymmetric sign/verify operations from > crypto_akcipher to a new crypto_sig frontend. > > Internally, the crypto_sig frontend still uses akcipher_alg as backend, > however: > > "The link between sig and akcipher is meant to be temporary. The > plan is to create a new low-level API for sig and then migrate > the signature code over to that from akcipher." > https://lore.kernel.org/r/ZrG6w9wsb-iiLZIF@gondor.apana.org.au/ > > "having a separate alg for sig is definitely where we want to > be since there is very little that the two types actually share." > https://lore.kernel.org/r/ZrHlpz4qnre0zWJO@gondor.apana.org.au/ > > Take the next step of that migration and augment the crypto_sig frontend > with a sig_alg backend to which all algorithms can be moved. > > During the migration, there will briefly be signature algorithms that > are still based on crypto_akcipher, whilst others are already based on > crypto_sig. Allow for that by building a fork into crypto_sig_*() API > calls (i.e. crypto_sig_maxsize() and friends) such that one of the two > backends is selected based on the transform's cra_type. > > Signed-off-by: Lukas Wunner <lukas@wunner.de> > --- > Documentation/crypto/api-sig.rst | 15 +++ > Documentation/crypto/api.rst | 1 + > Documentation/crypto/architecture.rst | 2 + > crypto/sig.c | 143 +++++++++++++++++++++++++- > crypto/testmgr.c | 115 +++++++++++++++++++++ > crypto/testmgr.h | 13 +++ > include/crypto/internal/sig.h | 80 ++++++++++++++ > include/crypto/sig.h | 61 +++++++++++ > include/uapi/linux/cryptouser.h | 5 + > 9 files changed, 433 insertions(+), 2 deletions(-) > create mode 100644 Documentation/crypto/api-sig.rst > > diff --git a/Documentation/crypto/api-sig.rst b/Documentation/crypto/api-sig.rst > new file mode 100644 > index 000000000000..e5e87e106884 > --- /dev/null > +++ b/Documentation/crypto/api-sig.rst > @@ -0,0 +1,15 @@ > +Asymmetric Signature Algorithm Definitions > +------------------------------------------ > + > +.. kernel-doc:: include/crypto/sig.h > + :functions: sig_alg > + > +Asymmetric Signature API > +------------------------ > + > +.. kernel-doc:: include/crypto/sig.h > + :doc: Generic Public Key Signature API > + > +.. kernel-doc:: include/crypto/sig.h > + :functions: crypto_alloc_sig crypto_free_sig crypto_sig_set_pubkey crypto_sig_set_privkey crypto_sig_maxsize crypto_sig_sign crypto_sig_verify > + > diff --git a/Documentation/crypto/api.rst b/Documentation/crypto/api.rst > index ff31c30561d4..8b2a90521886 100644 > --- a/Documentation/crypto/api.rst > +++ b/Documentation/crypto/api.rst > @@ -10,4 +10,5 @@ Programming Interface > api-digest > api-rng > api-akcipher > + api-sig > api-kpp > diff --git a/Documentation/crypto/architecture.rst b/Documentation/crypto/architecture.rst > index 646c3380a7ed..15dcd62fd22f 100644 > --- a/Documentation/crypto/architecture.rst > +++ b/Documentation/crypto/architecture.rst > @@ -214,6 +214,8 @@ the aforementioned cipher types: > > - CRYPTO_ALG_TYPE_AKCIPHER Asymmetric cipher > > +- CRYPTO_ALG_TYPE_SIG Asymmetric signature > + > - CRYPTO_ALG_TYPE_PCOMPRESS Enhanced version of > CRYPTO_ALG_TYPE_COMPRESS allowing for segmented compression / > decompression instead of performing the operation on one segment I'd split the documentation update. It's not strictly necessary as it is still part of crypto (e.g. not kernel-parameters.txt) but they are still too disjoint logical artifacts that you need to review separately. > diff --git a/crypto/sig.c b/crypto/sig.c > index 7645bedf3a1f..4f36ceb7a90b 100644 > --- a/crypto/sig.c > +++ b/crypto/sig.c > @@ -21,14 +21,38 @@ > > static const struct crypto_type crypto_sig_type; > > +static void crypto_sig_exit_tfm(struct crypto_tfm *tfm) > +{ > + struct crypto_sig *sig = __crypto_sig_tfm(tfm); > + struct sig_alg *alg = crypto_sig_alg(sig); > + > + alg->exit(sig); > +} > + > 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); > > + struct crypto_sig *sig = __crypto_sig_tfm(tfm); > + struct sig_alg *alg = crypto_sig_alg(sig); > + > + if (alg->exit) > + sig->base.exit = crypto_sig_exit_tfm; > + > + if (alg->init) > + return alg->init(sig); 1. alg->exit == NULL, alg->init == NULL 2. alg->exit != NULL, alg->init == NULL 3. alg->exit == NULL, alg->init != NULL Which of the three are legit use of the API and which are not? BR, Jarkko
On Wed, Sep 11, 2024 at 03:12:33PM +0300, Jarkko Sakkinen wrote: > > 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); > > > > + struct crypto_sig *sig = __crypto_sig_tfm(tfm); > > + struct sig_alg *alg = crypto_sig_alg(sig); > > + > > + if (alg->exit) > > + sig->base.exit = crypto_sig_exit_tfm; > > + > > + if (alg->init) > > + return alg->init(sig); > > 1. alg->exit == NULL, alg->init == NULL > 2. alg->exit != NULL, alg->init == NULL > 3. alg->exit == NULL, alg->init != NULL > > Which of the three are legit use of the API and which are not? All three are possible. Same as crypto_akcipher_init_tfm(). Thanks, Lukas
On Thu Sep 12, 2024 at 10:54 AM EEST, Lukas Wunner wrote: > On Wed, Sep 11, 2024 at 03:12:33PM +0300, Jarkko Sakkinen wrote: > > > 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); > > > > > > + struct crypto_sig *sig = __crypto_sig_tfm(tfm); > > > + struct sig_alg *alg = crypto_sig_alg(sig); > > > + > > > + if (alg->exit) > > > + sig->base.exit = crypto_sig_exit_tfm; > > > + > > > + if (alg->init) > > > + return alg->init(sig); > > > > 1. alg->exit == NULL, alg->init == NULL > > 2. alg->exit != NULL, alg->init == NULL > > 3. alg->exit == NULL, alg->init != NULL > > > > Which of the three are legit use of the API and which are not? > > All three are possible. Same as crypto_akcipher_init_tfm(). Lot's of nitpicks but... I try to understand these in detail because I rebase later on my TPM2 ECDSA patches (series last updated in April) on top of this. I'll hold with that for the sake of less possible conflicts with this larger series. Many of the questions rised during the Spring about akcipher so now is my chance to fill the dots by asking them here. BR, Jarkko
On Thu, Sep 12, 2024 at 05:19:15PM +0300, Jarkko Sakkinen wrote: > I try to understand these in detail because I rebase later on my TPM2 > ECDSA patches (series last updated in April) on top of this. I'll hold > with that for the sake of less possible conflicts with this larger > series. > > Many of the questions rised during the Spring about akcipher so now is > my chance to fill the dots by asking them here. I assume you're referring to: https://lore.kernel.org/all/20240528210823.28798-1-jarkko@kernel.org/ Help me understand this: Once you import a private key to a TPM, can you get it out again? Can you generate private keys on the TPM which cannot be retrieved? It would be good if the cover letter or one of the commits in your series explained this. Some of the commit messages are overly terse and consist of just two or three bullet points. The reason I'm asking is, there are security chips such as ATECC508A which allow generating private ECDSA keys on the chip that cannot be retrieved. One can send a message digest to the chip and get a signature back. One can also use the chip for signature verification, but that's less interesting because it's attached via i2c, which is usually slower than verifying on the CPU: https://cdn.sparkfun.com/assets/learn_tutorials/1/0/0/3/Microchip_ATECC508A_Datasheet.pdf If TPMs support unretrievable, maybe even on-device created private keys, they would offer comparable functionality to the ATECC508A and that would suggest adding an asymmetric_key_subtype which uses some kind of abstraction that works for both kinds of devices. I note there are ASN.1 modules in your series. Please provide a spec reference in the .asn1 file so that one knows where it's originating from. If it's originating from an RFC, please add an SPDX identifier as in commit 201c0da4d029. Thanks, Lukas
On Thu, 2024-09-12 at 17:27 +0200, Lukas Wunner wrote: > On Thu, Sep 12, 2024 at 05:19:15PM +0300, Jarkko Sakkinen wrote: > > I try to understand these in detail because I rebase later on my TPM2 > > ECDSA patches (series last updated in April) on top of this. I'll hold > > with that for the sake of less possible conflicts with this larger > > series. > > > > Many of the questions rised during the Spring about akcipher so now is > > my chance to fill the dots by asking them here. > > I assume you're referring to: > https://lore.kernel.org/all/20240528210823.28798-1-jarkko@kernel.org/ > > Help me understand this: > Once you import a private key to a TPM, can you get it out again? > Can you generate private keys on the TPM which cannot be retrieved? It is for implementing TPM2 bits of https://www.ietf.org/archive/id/draft-woodhouse-cert-best-practice-00.html The main use case is to protect signing key coming from outside CA infrastructure. > It would be good if the cover letter or one of the commits in your > series explained this. Some of the commit messages are overly terse > and consist of just two or three bullet points. I keep this in mind for the next version. Thanks! > > The reason I'm asking is, there are security chips such as ATECC508A > which allow generating private ECDSA keys on the chip that cannot > be retrieved. One can send a message digest to the chip and get > a signature back. One can also use the chip for signature verification, > but that's less interesting because it's attached via i2c, which is > usually slower than verifying on the CPU: > > https://cdn.sparkfun.com/assets/learn_tutorials/1/0/0/3/Microchip_ATECC508A_Datasheet.pdf > > If TPMs support unretrievable, maybe even on-device created > private keys, they would offer comparable functionality to the > ATECC508A and that would suggest adding an asymmetric_key_subtype > which uses some kind of abstraction that works for both kinds of > devices. > > I note there are ASN.1 modules in your series. Please provide a > spec reference in the .asn1 file so that one knows where it's > originating from. If it's originating from an RFC, please add > an SPDX identifier as in commit 201c0da4d029. OK this is good to know and I can address this as follows: 1. I explicitly state that the feature that in the scope of the patch set it supports the original use case (or at least how I understood David's specification). 2. It is probably then better make sure that the implementation does not set any possible roadblocks for the possible use cases you described. Also one thing I'm going to do in order to have better focus is to cut out RSA part because they don't have to be in the same patch set and it is way more important to have ECDSA. OFC I'll work on RSA right after that but I think this is the right order :-) > > Thanks, > > Lukas BR, Jarkko
On Tue, 10 Sep 2024 16:30:12 +0200 Lukas Wunner <lukas@wunner.de> wrote: > Commit 6cb8815f41a9 ("crypto: sig - Add interface for sign/verify") > began a transition of asymmetric sign/verify operations from > crypto_akcipher to a new crypto_sig frontend. > > Internally, the crypto_sig frontend still uses akcipher_alg as backend, > however: > > "The link between sig and akcipher is meant to be temporary. The > plan is to create a new low-level API for sig and then migrate > the signature code over to that from akcipher." > https://lore.kernel.org/r/ZrG6w9wsb-iiLZIF@gondor.apana.org.au/ > > "having a separate alg for sig is definitely where we want to > be since there is very little that the two types actually share." > https://lore.kernel.org/r/ZrHlpz4qnre0zWJO@gondor.apana.org.au/ > > Take the next step of that migration and augment the crypto_sig frontend > with a sig_alg backend to which all algorithms can be moved. > > During the migration, there will briefly be signature algorithms that > are still based on crypto_akcipher, whilst others are already based on > crypto_sig. Allow for that by building a fork into crypto_sig_*() API > calls (i.e. crypto_sig_maxsize() and friends) such that one of the two > backends is selected based on the transform's cra_type. > > Signed-off-by: Lukas Wunner <lukas@wunner.de> Hi Lukas, A few trivial comments. Jonathan > diff --git a/crypto/sig.c b/crypto/sig.c > index 7645bedf3a1f..4f36ceb7a90b 100644 > --- a/crypto/sig.c > +++ b/crypto/sig.c > @@ -68,6 +93,14 @@ EXPORT_SYMBOL_GPL(crypto_alloc_sig); > > int crypto_sig_maxsize(struct crypto_sig *tfm) > { > + if (crypto_sig_tfm(tfm)->__crt_alg->cra_type != &crypto_sig_type) > + goto akcipher; > + > + struct sig_alg *alg = crypto_sig_alg(tfm); > + > + return alg->max_size(tfm); > + > +akcipher: Neat trick for temporary retention of the code. Hideous code in the meantime ;) Not that I have a better idea. > struct crypto_akcipher **ctx = crypto_sig_ctx(tfm); > > return crypto_akcipher_maxsize(*ctx); > diff --git a/crypto/testmgr.c b/crypto/testmgr.c > index f02cb075bd68..bb21378aa510 100644 > --- a/crypto/testmgr.c > +++ b/crypto/testmgr.c ... > @@ -4317,6 +4324,114 @@ static int alg_test_akcipher(const struct alg_test_desc *desc, > return err; > } > > +static int test_sig_one(struct crypto_sig *tfm, const struct sig_testvec *vecs) > +{ > + u8 *ptr, *key __free(kfree); I would move definition of key down to where the constructor is. Current pattern is fine until some extra code sneaks inbetween with an error return. > + int err, sig_size; > + > + key = kmalloc(vecs->key_len + 2 * sizeof(u32) + vecs->param_len, > + GFP_KERNEL); > + if (!key) > + return -ENOMEM; git a/include/crypto/internal/sig.h b/include/crypto/internal/sig.h > index 97cb26ef8115..b16648c1a986 100644 > --- a/include/crypto/internal/sig.h > +++ b/include/crypto/internal/sig.h > +static inline struct crypto_sig *crypto_spawn_sig(struct crypto_sig_spawn > + *spawn) That's an odd wrap. I'd just go long where this happens and slightly past 80 chars. > +{ > + return crypto_spawn_tfm2(&spawn->base); > +}
On Thu Sep 12, 2024 at 6:27 PM EEST, Lukas Wunner wrote: > On Thu, Sep 12, 2024 at 05:19:15PM +0300, Jarkko Sakkinen wrote: > > I try to understand these in detail because I rebase later on my TPM2 > > ECDSA patches (series last updated in April) on top of this. I'll hold > > with that for the sake of less possible conflicts with this larger > > series. > > > > Many of the questions rised during the Spring about akcipher so now is > > my chance to fill the dots by asking them here. > > I assume you're referring to: > https://lore.kernel.org/all/20240528210823.28798-1-jarkko@kernel.org/ Returning to this as I started to update the series. Sorry if for possible duplicates with my earelier response. > Help me understand this: > Once you import a private key to a TPM, can you get it out again? No. > Can you generate private keys on the TPM which cannot be retrieved? Yes. > > It would be good if the cover letter or one of the commits in your > series explained this. Some of the commit messages are overly terse > and consist of just two or three bullet points. Yes. I'm picking right now the use case where key is uploaded to the TPM because: 1. The creation part is more complex as data flow starts from user space so it pretty much tests the edges also for a generated private key. 2. I can drop the code related to public key and add only signing operation, not signature verification. My test script will along the lines of [1]. The new version of the series is not yet fully working so also the test is due to change. The idea is to get flow working where a normal public key can verify a signature made by the TPM chip. One area what I know probably might not be correct, is what I put in the 'describe' callbacks: static void tpm2_key_ecc_describe(const struct key *asymmetric_key, struct seq_file *m) { struct tpm2_key *key = asymmetric_key->payload.data[asym_crypto]; if (!key) { pr_err("key missing"); return; } seq_puts(m, "TPM2/ECDSA"); } So any ideas what to put here are welcome (obviously). [1] #!/usr/bin/env bash set -e PRIMARY=0x81000001 function egress { keyctl clear @u tpm2_evictcontrol -C o -c $PRIMARY 2> /dev/null tpm2_getcap handles-transient tpm2_getcap handles-persistent } trap egress EXIT openssl ecparam -name prime256v1 -genkey -noout -out ecc.pem openssl pkcs8 -topk8 -inform PEM -outform DER -nocrypt -in ecc.pem -out ecc_pkcs8.der tpm2_createprimary --hierarchy o -G ecc -c owner.txt tpm2_evictcontrol -c owner.txt $PRIMARY # EC parameters to TPM2 blob: tpm2_import -C $PRIMARY -G ecc -i ecc.pem -u tpm2.pub -r tpm2.priv # TPM2 blob to ASN.1: tpm2_encodeobject -C $PRIMARY -u tpm2.pub -r tpm2.priv -o tpm2.pem openssl asn1parse -inform pem -in tpm2.pem -noout -out tpm2.der # Populate asymmetric keys: tpm2_ecc_key=$(keyctl padd asymmetric "tpm_ecc" @u < tpm2.der) kernel_ecc_key=$(keyctl padd asymmetric "kernel_ecc" @u < ecc_pkcs8.der) echo "SECRET" > doc.txt echo TPM2 ECC SIGN keyctl pkey_sign "$tpm2_ecc_key" 0 doc.txt hash=sha256 > doc.txt.sig echo TPM2 VERIFY keyctl pkey_verify "$kernel_ecc_key" 0 doc.txt doc.txt.sig hash=sha256 BR, Jarkko
diff --git a/Documentation/crypto/api-sig.rst b/Documentation/crypto/api-sig.rst new file mode 100644 index 000000000000..e5e87e106884 --- /dev/null +++ b/Documentation/crypto/api-sig.rst @@ -0,0 +1,15 @@ +Asymmetric Signature Algorithm Definitions +------------------------------------------ + +.. kernel-doc:: include/crypto/sig.h + :functions: sig_alg + +Asymmetric Signature API +------------------------ + +.. kernel-doc:: include/crypto/sig.h + :doc: Generic Public Key Signature API + +.. kernel-doc:: include/crypto/sig.h + :functions: crypto_alloc_sig crypto_free_sig crypto_sig_set_pubkey crypto_sig_set_privkey crypto_sig_maxsize crypto_sig_sign crypto_sig_verify + diff --git a/Documentation/crypto/api.rst b/Documentation/crypto/api.rst index ff31c30561d4..8b2a90521886 100644 --- a/Documentation/crypto/api.rst +++ b/Documentation/crypto/api.rst @@ -10,4 +10,5 @@ Programming Interface api-digest api-rng api-akcipher + api-sig api-kpp diff --git a/Documentation/crypto/architecture.rst b/Documentation/crypto/architecture.rst index 646c3380a7ed..15dcd62fd22f 100644 --- a/Documentation/crypto/architecture.rst +++ b/Documentation/crypto/architecture.rst @@ -214,6 +214,8 @@ the aforementioned cipher types: - CRYPTO_ALG_TYPE_AKCIPHER Asymmetric cipher +- CRYPTO_ALG_TYPE_SIG Asymmetric signature + - CRYPTO_ALG_TYPE_PCOMPRESS Enhanced version of CRYPTO_ALG_TYPE_COMPRESS allowing for segmented compression / decompression instead of performing the operation on one segment diff --git a/crypto/sig.c b/crypto/sig.c index 7645bedf3a1f..4f36ceb7a90b 100644 --- a/crypto/sig.c +++ b/crypto/sig.c @@ -21,14 +21,38 @@ static const struct crypto_type crypto_sig_type; +static void crypto_sig_exit_tfm(struct crypto_tfm *tfm) +{ + struct crypto_sig *sig = __crypto_sig_tfm(tfm); + struct sig_alg *alg = crypto_sig_alg(sig); + + alg->exit(sig); +} + 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); + struct crypto_sig *sig = __crypto_sig_tfm(tfm); + struct sig_alg *alg = crypto_sig_alg(sig); + + if (alg->exit) + sig->base.exit = crypto_sig_exit_tfm; + + if (alg->init) + return alg->init(sig); + return 0; } +static void crypto_sig_free_instance(struct crypto_instance *inst) +{ + struct sig_instance *sig = sig_instance(inst); + + sig->free(sig); +} + static void __maybe_unused crypto_sig_show(struct seq_file *m, struct crypto_alg *alg) { @@ -38,16 +62,17 @@ static void __maybe_unused crypto_sig_show(struct seq_file *m, static int __maybe_unused crypto_sig_report(struct sk_buff *skb, struct crypto_alg *alg) { - struct crypto_report_akcipher rsig = {}; + struct crypto_report_sig rsig = {}; strscpy(rsig.type, "sig", sizeof(rsig.type)); - return nla_put(skb, CRYPTOCFGA_REPORT_AKCIPHER, sizeof(rsig), &rsig); + return nla_put(skb, CRYPTOCFGA_REPORT_SIG, sizeof(rsig), &rsig); } static const struct crypto_type crypto_sig_type = { .extsize = crypto_alg_extsize, .init_tfm = crypto_sig_init_tfm, + .free = crypto_sig_free_instance, #ifdef CONFIG_PROC_FS .show = crypto_sig_show, #endif @@ -68,6 +93,14 @@ EXPORT_SYMBOL_GPL(crypto_alloc_sig); int crypto_sig_maxsize(struct crypto_sig *tfm) { + if (crypto_sig_tfm(tfm)->__crt_alg->cra_type != &crypto_sig_type) + goto akcipher; + + struct sig_alg *alg = crypto_sig_alg(tfm); + + return alg->max_size(tfm); + +akcipher: struct crypto_akcipher **ctx = crypto_sig_ctx(tfm); return crypto_akcipher_maxsize(*ctx); @@ -78,6 +111,14 @@ int crypto_sig_sign(struct crypto_sig *tfm, const void *src, unsigned int slen, void *dst, unsigned int dlen) { + if (crypto_sig_tfm(tfm)->__crt_alg->cra_type != &crypto_sig_type) + goto akcipher; + + struct sig_alg *alg = crypto_sig_alg(tfm); + + return alg->sign(tfm, src, slen, dst, dlen); + +akcipher: struct crypto_akcipher **ctx = crypto_sig_ctx(tfm); struct crypto_akcipher_sync_data data = { .tfm = *ctx, @@ -97,6 +138,14 @@ int crypto_sig_verify(struct crypto_sig *tfm, const void *src, unsigned int slen, const void *digest, unsigned int dlen) { + if (crypto_sig_tfm(tfm)->__crt_alg->cra_type != &crypto_sig_type) + goto akcipher; + + struct sig_alg *alg = crypto_sig_alg(tfm); + + return alg->verify(tfm, src, slen, digest, dlen); + +akcipher: struct crypto_akcipher **ctx = crypto_sig_ctx(tfm); struct crypto_akcipher_sync_data data = { .tfm = *ctx, @@ -120,6 +169,14 @@ EXPORT_SYMBOL_GPL(crypto_sig_verify); int crypto_sig_set_pubkey(struct crypto_sig *tfm, const void *key, unsigned int keylen) { + if (crypto_sig_tfm(tfm)->__crt_alg->cra_type != &crypto_sig_type) + goto akcipher; + + struct sig_alg *alg = crypto_sig_alg(tfm); + + return alg->set_pub_key(tfm, key, keylen); + +akcipher: struct crypto_akcipher **ctx = crypto_sig_ctx(tfm); return crypto_akcipher_set_pub_key(*ctx, key, keylen); @@ -129,11 +186,93 @@ EXPORT_SYMBOL_GPL(crypto_sig_set_pubkey); int crypto_sig_set_privkey(struct crypto_sig *tfm, const void *key, unsigned int keylen) { + if (crypto_sig_tfm(tfm)->__crt_alg->cra_type != &crypto_sig_type) + goto akcipher; + + struct sig_alg *alg = crypto_sig_alg(tfm); + + return alg->set_priv_key(tfm, key, keylen); + +akcipher: struct crypto_akcipher **ctx = crypto_sig_ctx(tfm); return crypto_akcipher_set_priv_key(*ctx, key, keylen); } EXPORT_SYMBOL_GPL(crypto_sig_set_privkey); +static void sig_prepare_alg(struct sig_alg *alg) +{ + struct crypto_alg *base = &alg->base; + + base->cra_type = &crypto_sig_type; + base->cra_flags &= ~CRYPTO_ALG_TYPE_MASK; + base->cra_flags |= CRYPTO_ALG_TYPE_SIG; +} + +static int sig_default_sign(struct crypto_sig *tfm, + const void *src, unsigned int slen, + void *dst, unsigned int dlen) +{ + return -ENOSYS; +} + +static int sig_default_verify(struct crypto_sig *tfm, + const void *src, unsigned int slen, + const void *dst, unsigned int dlen) +{ + return -ENOSYS; +} + +static int sig_default_set_key(struct crypto_sig *tfm, + const void *key, unsigned int keylen) +{ + return -ENOSYS; +} + +int crypto_register_sig(struct sig_alg *alg) +{ + struct crypto_alg *base = &alg->base; + + if (!alg->sign) + alg->sign = sig_default_sign; + if (!alg->verify) + alg->verify = sig_default_verify; + if (!alg->set_priv_key) + alg->set_priv_key = sig_default_set_key; + if (!alg->set_pub_key) + return -EINVAL; + if (!alg->max_size) + return -EINVAL; + + sig_prepare_alg(alg); + return crypto_register_alg(base); +} +EXPORT_SYMBOL_GPL(crypto_register_sig); + +void crypto_unregister_sig(struct sig_alg *alg) +{ + crypto_unregister_alg(&alg->base); +} +EXPORT_SYMBOL_GPL(crypto_unregister_sig); + +int sig_register_instance(struct crypto_template *tmpl, + struct sig_instance *inst) +{ + if (WARN_ON(!inst->free)) + return -EINVAL; + sig_prepare_alg(&inst->alg); + return crypto_register_instance(tmpl, sig_crypto_instance(inst)); +} +EXPORT_SYMBOL_GPL(sig_register_instance); + +int crypto_grab_sig(struct crypto_sig_spawn *spawn, + struct crypto_instance *inst, + const char *name, u32 type, u32 mask) +{ + spawn->base.frontend = &crypto_sig_type; + return crypto_grab_spawn(&spawn->base, inst, name, type, mask); +} +EXPORT_SYMBOL_GPL(crypto_grab_sig); + MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Public Key Signature Algorithms"); diff --git a/crypto/testmgr.c b/crypto/testmgr.c index f02cb075bd68..bb21378aa510 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -33,6 +33,7 @@ #include <crypto/akcipher.h> #include <crypto/kpp.h> #include <crypto/acompress.h> +#include <crypto/sig.h> #include <crypto/internal/cipher.h> #include <crypto/internal/simd.h> @@ -131,6 +132,11 @@ struct akcipher_test_suite { unsigned int count; }; +struct sig_test_suite { + const struct sig_testvec *vecs; + unsigned int count; +}; + struct kpp_test_suite { const struct kpp_testvec *vecs; unsigned int count; @@ -151,6 +157,7 @@ struct alg_test_desc { struct cprng_test_suite cprng; struct drbg_test_suite drbg; struct akcipher_test_suite akcipher; + struct sig_test_suite sig; struct kpp_test_suite kpp; } suite; }; @@ -4317,6 +4324,114 @@ static int alg_test_akcipher(const struct alg_test_desc *desc, return err; } +static int test_sig_one(struct crypto_sig *tfm, const struct sig_testvec *vecs) +{ + u8 *ptr, *key __free(kfree); + int err, sig_size; + + key = kmalloc(vecs->key_len + 2 * sizeof(u32) + vecs->param_len, + GFP_KERNEL); + if (!key) + return -ENOMEM; + + /* ecrdsa expects additional parameters appended to the key */ + memcpy(key, vecs->key, vecs->key_len); + ptr = key + vecs->key_len; + ptr = test_pack_u32(ptr, vecs->algo); + ptr = test_pack_u32(ptr, vecs->param_len); + memcpy(ptr, vecs->params, vecs->param_len); + + if (vecs->public_key_vec) + err = crypto_sig_set_pubkey(tfm, key, vecs->key_len); + else + err = crypto_sig_set_privkey(tfm, key, vecs->key_len); + if (err) + return err; + + /* + * Run asymmetric signature verification first + * (which does not require a private key) + */ + err = crypto_sig_verify(tfm, vecs->c, vecs->c_size, + vecs->m, vecs->m_size); + if (err) { + pr_err("alg: sig: verify test failed: err %d\n", err); + return err; + } + + /* + * Don't invoke sign test (which requires a private key) + * for vectors with only a public key. + */ + if (vecs->public_key_vec) + return 0; + + sig_size = crypto_sig_maxsize(tfm); + if (sig_size < vecs->c_size) { + pr_err("alg: sig: invalid maxsize %u\n", sig_size); + return -EINVAL; + } + + u8 *sig __free(kfree) = kzalloc(sig_size, GFP_KERNEL); + if (!sig) + return -ENOMEM; + + /* Run asymmetric signature generation */ + err = crypto_sig_sign(tfm, vecs->m, vecs->m_size, sig, sig_size); + if (err) { + pr_err("alg: sig: sign test failed: err %d\n", err); + return err; + } + + /* Verify that generated signature equals cooked signature */ + if (memcmp(sig, vecs->c, vecs->c_size) || + memchr_inv(sig + vecs->c_size, 0, sig_size - vecs->c_size)) { + pr_err("alg: sig: sign test failed: invalid output\n"); + hexdump(sig, sig_size); + return -EINVAL; + } + + return 0; +} + +static int test_sig(struct crypto_sig *tfm, const char *alg, + const struct sig_testvec *vecs, unsigned int tcount) +{ + const char *algo = crypto_tfm_alg_driver_name(crypto_sig_tfm(tfm)); + int ret, i; + + for (i = 0; i < tcount; i++) { + ret = test_sig_one(tfm, vecs++); + if (ret) { + pr_err("alg: sig: test %d failed for %s: err %d\n", + i + 1, algo, ret); + return ret; + } + } + return 0; +} + +__maybe_unused +static int alg_test_sig(const struct alg_test_desc *desc, const char *driver, + u32 type, u32 mask) +{ + struct crypto_sig *tfm; + int err = 0; + + tfm = crypto_alloc_sig(driver, type, mask); + if (IS_ERR(tfm)) { + pr_err("alg: sig: Failed to load tfm for %s: %ld\n", + driver, PTR_ERR(tfm)); + return PTR_ERR(tfm); + } + if (desc->suite.sig.vecs) + err = test_sig(tfm, desc->alg, desc->suite.sig.vecs, + desc->suite.sig.count); + + crypto_free_sig(tfm); + return err; +} + static int alg_test_null(const struct alg_test_desc *desc, const char *driver, u32 type, u32 mask) { diff --git a/crypto/testmgr.h b/crypto/testmgr.h index ed1640f3e352..39dd1d558883 100644 --- a/crypto/testmgr.h +++ b/crypto/testmgr.h @@ -162,6 +162,19 @@ struct akcipher_testvec { enum OID algo; }; +struct sig_testvec { + const unsigned char *key; + const unsigned char *params; + const unsigned char *m; + const unsigned char *c; + unsigned int key_len; + unsigned int param_len; + unsigned int m_size; + unsigned int c_size; + bool public_key_vec; + enum OID algo; +}; + struct kpp_testvec { const unsigned char *secret; const unsigned char *b_secret; diff --git a/include/crypto/internal/sig.h b/include/crypto/internal/sig.h index 97cb26ef8115..b16648c1a986 100644 --- a/include/crypto/internal/sig.h +++ b/include/crypto/internal/sig.h @@ -10,8 +10,88 @@ #include <crypto/algapi.h> #include <crypto/sig.h> +struct sig_instance { + void (*free)(struct sig_instance *inst); + union { + struct { + char head[offsetof(struct sig_alg, base)]; + struct crypto_instance base; + }; + struct sig_alg alg; + }; +}; + +struct crypto_sig_spawn { + struct crypto_spawn base; +}; + static inline void *crypto_sig_ctx(struct crypto_sig *tfm) { return crypto_tfm_ctx(&tfm->base); } + +/** + * crypto_register_sig() -- Register public key signature algorithm + * + * Function registers an implementation of a public key signature algorithm + * + * @alg: algorithm definition + * + * Return: zero on success; error code in case of error + */ +int crypto_register_sig(struct sig_alg *alg); + +/** + * crypto_unregister_sig() -- Unregister public key signature algorithm + * + * Function unregisters an implementation of a public key signature algorithm + * + * @alg: algorithm definition + */ +void crypto_unregister_sig(struct sig_alg *alg); + +int sig_register_instance(struct crypto_template *tmpl, + struct sig_instance *inst); + +static inline struct sig_instance *sig_instance(struct crypto_instance *inst) +{ + return container_of(&inst->alg, struct sig_instance, alg.base); +} + +static inline struct sig_instance *sig_alg_instance(struct crypto_sig *tfm) +{ + return sig_instance(crypto_tfm_alg_instance(&tfm->base)); +} + +static inline struct crypto_instance *sig_crypto_instance(struct sig_instance + *inst) +{ + return container_of(&inst->alg.base, struct crypto_instance, alg); +} + +static inline void *sig_instance_ctx(struct sig_instance *inst) +{ + return crypto_instance_ctx(sig_crypto_instance(inst)); +} + +int crypto_grab_sig(struct crypto_sig_spawn *spawn, + struct crypto_instance *inst, + const char *name, u32 type, u32 mask); + +static inline struct crypto_sig *crypto_spawn_sig(struct crypto_sig_spawn + *spawn) +{ + return crypto_spawn_tfm2(&spawn->base); +} + +static inline void crypto_drop_sig(struct crypto_sig_spawn *spawn) +{ + crypto_drop_spawn(&spawn->base); +} + +static inline struct sig_alg *crypto_spawn_sig_alg(struct crypto_sig_spawn + *spawn) +{ + return container_of(spawn->base.alg, struct sig_alg, base); +} #endif diff --git a/include/crypto/sig.h b/include/crypto/sig.h index d25186bb2be3..f0f52a7c5ae7 100644 --- a/include/crypto/sig.h +++ b/include/crypto/sig.h @@ -19,6 +19,52 @@ struct crypto_sig { struct crypto_tfm base; }; +/** + * struct sig_alg - generic public key signature algorithm + * + * @sign: Function performs a sign operation as defined by public key + * algorithm. Optional. + * @verify: Function performs a complete verify operation as defined by + * public key algorithm, returning verification status. Optional. + * @set_pub_key: Function invokes the algorithm specific set public key + * function, which knows how to decode and interpret + * the BER encoded public key and parameters. Mandatory. + * @set_priv_key: Function invokes the algorithm specific set private key + * function, which knows how to decode and interpret + * the BER encoded private key and parameters. Optional. + * @max_size: Function returns key size. Mandatory. + * @init: Initialize the cryptographic transformation object. + * This function is used to initialize the cryptographic + * transformation object. This function is called only once at + * the instantiation time, right after the transformation context + * was allocated. In case the cryptographic hardware has some + * special requirements which need to be handled by software, this + * function shall check for the precise requirement of the + * transformation and put any software fallbacks in place. + * @exit: Deinitialize the cryptographic transformation object. This is a + * counterpart to @init, used to remove various changes set in + * @init. + * + * @base: Common crypto API algorithm data structure + */ +struct sig_alg { + int (*sign)(struct crypto_sig *tfm, + const void *src, unsigned int slen, + void *dst, unsigned int dlen); + int (*verify)(struct crypto_sig *tfm, + const void *src, unsigned int slen, + const void *digest, unsigned int dlen); + int (*set_pub_key)(struct crypto_sig *tfm, + const void *key, unsigned int keylen); + int (*set_priv_key)(struct crypto_sig *tfm, + const void *key, unsigned int keylen); + unsigned int (*max_size)(struct crypto_sig *tfm); + int (*init)(struct crypto_sig *tfm); + void (*exit)(struct crypto_sig *tfm); + + struct crypto_alg base; +}; + /** * DOC: Generic Public Key Signature API * @@ -47,6 +93,21 @@ static inline struct crypto_tfm *crypto_sig_tfm(struct crypto_sig *tfm) return &tfm->base; } +static inline struct crypto_sig *__crypto_sig_tfm(struct crypto_tfm *tfm) +{ + return container_of(tfm, struct crypto_sig, base); +} + +static inline struct sig_alg *__crypto_sig_alg(struct crypto_alg *alg) +{ + return container_of(alg, struct sig_alg, base); +} + +static inline struct sig_alg *crypto_sig_alg(struct crypto_sig *tfm) +{ + return __crypto_sig_alg(crypto_sig_tfm(tfm)->__crt_alg); +} + /** * crypto_free_sig() - free signature tfm handle * diff --git a/include/uapi/linux/cryptouser.h b/include/uapi/linux/cryptouser.h index 20a6c0fc149e..db05e0419972 100644 --- a/include/uapi/linux/cryptouser.h +++ b/include/uapi/linux/cryptouser.h @@ -64,6 +64,7 @@ enum crypto_attr_type_t { CRYPTOCFGA_STAT_AKCIPHER, /* No longer supported, do not use. */ CRYPTOCFGA_STAT_KPP, /* No longer supported, do not use. */ CRYPTOCFGA_STAT_ACOMP, /* No longer supported, do not use. */ + CRYPTOCFGA_REPORT_SIG, /* struct crypto_report_sig */ __CRYPTOCFGA_MAX #define CRYPTOCFGA_MAX (__CRYPTOCFGA_MAX - 1) @@ -207,6 +208,10 @@ struct crypto_report_acomp { char type[CRYPTO_MAX_NAME]; }; +struct crypto_report_sig { + char type[CRYPTO_MAX_NAME]; +}; + #define CRYPTO_REPORT_MAXSIZE (sizeof(struct crypto_user_alg) + \ sizeof(struct crypto_report_blkcipher))
Commit 6cb8815f41a9 ("crypto: sig - Add interface for sign/verify") began a transition of asymmetric sign/verify operations from crypto_akcipher to a new crypto_sig frontend. Internally, the crypto_sig frontend still uses akcipher_alg as backend, however: "The link between sig and akcipher is meant to be temporary. The plan is to create a new low-level API for sig and then migrate the signature code over to that from akcipher." https://lore.kernel.org/r/ZrG6w9wsb-iiLZIF@gondor.apana.org.au/ "having a separate alg for sig is definitely where we want to be since there is very little that the two types actually share." https://lore.kernel.org/r/ZrHlpz4qnre0zWJO@gondor.apana.org.au/ Take the next step of that migration and augment the crypto_sig frontend with a sig_alg backend to which all algorithms can be moved. During the migration, there will briefly be signature algorithms that are still based on crypto_akcipher, whilst others are already based on crypto_sig. Allow for that by building a fork into crypto_sig_*() API calls (i.e. crypto_sig_maxsize() and friends) such that one of the two backends is selected based on the transform's cra_type. Signed-off-by: Lukas Wunner <lukas@wunner.de> --- Documentation/crypto/api-sig.rst | 15 +++ Documentation/crypto/api.rst | 1 + Documentation/crypto/architecture.rst | 2 + crypto/sig.c | 143 +++++++++++++++++++++++++- crypto/testmgr.c | 115 +++++++++++++++++++++ crypto/testmgr.h | 13 +++ include/crypto/internal/sig.h | 80 ++++++++++++++ include/crypto/sig.h | 61 +++++++++++ include/uapi/linux/cryptouser.h | 5 + 9 files changed, 433 insertions(+), 2 deletions(-) create mode 100644 Documentation/crypto/api-sig.rst