Skip to content

Commit

Permalink
akcipher: Move the RSA DER encoding check to the crypto layer
Browse files Browse the repository at this point in the history
Move the RSA EMSA-PKCS1-v1_5 encoding from the asymmetric-key public_key
subtype to the rsa crypto module's pkcs1pad template.  This means that the
public_key subtype no longer has any dependencies on public key type.

To make this work, the following changes have been made:

 (1) The rsa pkcs1pad template is now used for RSA keys.  This strips off the
     padding and returns just the message hash.

 (2) In a previous patch, the pkcs1pad template gained an optional second
     parameter that, if given, specifies the hash used.  We now give this,
     and pkcs1pad checks the encoded message E(M) for the EMSA-PKCS1-v1_5
     encoding and verifies that the correct digest OID is present.

 (3) The crypto driver in crypto/asymmetric_keys/rsa.c is now reduced to
     something that doesn't care about what the encryption actually does
     and and has been merged into public_key.c.

 (4) CONFIG_PUBLIC_KEY_ALGO_RSA is gone.  Module signing must set
     CONFIG_CRYPTO_RSA=y instead.

Thoughts:

 (*) Should the encoding style (eg. raw, EMSA-PKCS1-v1_5) also be passed to
     the padding template?  Should there be multiple padding templates
     registered that share most of the code?

Signed-off-by: David Howells <[email protected]>
Signed-off-by: Tadeusz Struk <[email protected]>
Acked-by: Herbert Xu <[email protected]>
  • Loading branch information
dhowells committed Mar 3, 2016
1 parent a49de37 commit d43de6c
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 246 deletions.
7 changes: 0 additions & 7 deletions crypto/asymmetric_keys/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,13 @@ if ASYMMETRIC_KEY_TYPE
config ASYMMETRIC_PUBLIC_KEY_SUBTYPE
tristate "Asymmetric public-key crypto algorithm subtype"
select MPILIB
select PUBLIC_KEY_ALGO_RSA
select CRYPTO_HASH_INFO
help
This option provides support for asymmetric public key type handling.
If signature generation and/or verification are to be used,
appropriate hash algorithms (such as SHA-1) must be available.
ENOPKG will be reported if the requisite algorithm is unavailable.

config PUBLIC_KEY_ALGO_RSA
tristate "RSA public-key algorithm"
select CRYPTO_RSA
help
This option enables support for the RSA algorithm (PKCS#1, RFC3447).

config X509_CERTIFICATE_PARSER
tristate "X.509 certificate parser"
depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
Expand Down
1 change: 0 additions & 1 deletion crypto/asymmetric_keys/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys.o
asymmetric_keys-y := asymmetric_type.o signature.o

obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o
obj-$(CONFIG_PUBLIC_KEY_ALGO_RSA) += rsa.o

#
# X.509 Certificate handling
Expand Down
104 changes: 93 additions & 11 deletions crypto/asymmetric_keys/public_key.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/seq_file.h>
#include <linux/scatterlist.h>
#include <keys/asymmetric-subtype.h>
#include <crypto/public_key.h>
#include <crypto/akcipher.h>

MODULE_LICENSE("GPL");

Expand All @@ -35,12 +37,6 @@ const char *const pkey_id_type_name[PKEY_ID_TYPE__LAST] = {
};
EXPORT_SYMBOL_GPL(pkey_id_type_name);

static int (*alg_verify[PKEY_ALGO__LAST])(const struct public_key *pkey,
const struct public_key_signature *sig) = {
NULL,
rsa_verify_signature
};

/*
* Provide a part of a description of the key for /proc/keys.
*/
Expand Down Expand Up @@ -68,24 +64,110 @@ void public_key_destroy(void *payload)
}
EXPORT_SYMBOL_GPL(public_key_destroy);

struct public_key_completion {
struct completion completion;
int err;
};

static void public_key_verify_done(struct crypto_async_request *req, int err)
{
struct public_key_completion *compl = req->data;

if (err == -EINPROGRESS)
return;

compl->err = err;
complete(&compl->completion);
}

/*
* Verify a signature using a public key.
*/
int public_key_verify_signature(const struct public_key *pkey,
const struct public_key_signature *sig)
{
struct public_key_completion compl;
struct crypto_akcipher *tfm;
struct akcipher_request *req;
struct scatterlist sig_sg, digest_sg;
const char *alg_name;
char alg_name_buf[CRYPTO_MAX_ALG_NAME];
void *output;
unsigned int outlen;
int ret = -ENOMEM;

pr_devel("==>%s()\n", __func__);

BUG_ON(!pkey);
BUG_ON(!sig);
BUG_ON(!sig->digest);
BUG_ON(!sig->s);

if (pkey->pkey_algo >= PKEY_ALGO__LAST)
return -ENOPKG;
alg_name = pkey_algo_name[sig->pkey_algo];
if (sig->pkey_algo == PKEY_ALGO_RSA) {
/* The data wangled by the RSA algorithm is typically padded
* and encoded in some manner, such as EMSA-PKCS1-1_5 [RFC3447
* sec 8.2].
*/
if (snprintf(alg_name_buf, CRYPTO_MAX_ALG_NAME,
"pkcs1pad(rsa,%s)",
hash_algo_name[sig->pkey_hash_algo]
) >= CRYPTO_MAX_ALG_NAME)
return -EINVAL;
alg_name = alg_name_buf;
}

tfm = crypto_alloc_akcipher(alg_name, 0, 0);
if (IS_ERR(tfm))
return PTR_ERR(tfm);

req = akcipher_request_alloc(tfm, GFP_KERNEL);
if (!req)
goto error_free_tfm;

ret = crypto_akcipher_set_pub_key(tfm, pkey->key, pkey->keylen);
if (ret)
goto error_free_req;

outlen = crypto_akcipher_maxsize(tfm);
output = kmalloc(outlen, GFP_KERNEL);
if (!output)
goto error_free_req;

sg_init_one(&sig_sg, sig->s, sig->s_size);
sg_init_one(&digest_sg, output, outlen);
akcipher_request_set_crypt(req, &sig_sg, &digest_sg, sig->s_size,
outlen);
init_completion(&compl.completion);
akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
CRYPTO_TFM_REQ_MAY_SLEEP,
public_key_verify_done, &compl);

/* Perform the verification calculation. This doesn't actually do the
* verification, but rather calculates the hash expected by the
* signature and returns that to us.
*/
ret = crypto_akcipher_verify(req);
if (ret == -EINPROGRESS) {
wait_for_completion(&compl.completion);
ret = compl.err;
}
if (ret < 0)
goto out_free_output;

if (!alg_verify[pkey->pkey_algo])
return -ENOPKG;
/* Do the actual verification step. */
if (req->dst_len != sig->digest_size ||
memcmp(sig->digest, output, sig->digest_size) != 0)
ret = -EKEYREJECTED;

return alg_verify[pkey->pkey_algo](pkey, sig);
out_free_output:
kfree(output);
error_free_req:
akcipher_request_free(req);
error_free_tfm:
crypto_free_akcipher(tfm);
pr_devel("<==%s() = %d\n", __func__, ret);
return ret;
}
EXPORT_SYMBOL_GPL(public_key_verify_signature);

Expand Down
224 changes: 0 additions & 224 deletions crypto/asymmetric_keys/rsa.c

This file was deleted.

Loading

0 comments on commit d43de6c

Please sign in to comment.