From c3917fd9dfbc8e5e4ca145398d7dc13e42a2d52f Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Sun, 24 Jan 2016 21:18:19 +0800 Subject: KEYS: Use skcipher This patch replaces uses of blkcipher with skcipher. Signed-off-by: Herbert Xu --- security/keys/encrypted-keys/encrypted.c | 82 +++++++++++++++++++------------- 1 file changed, 50 insertions(+), 32 deletions(-) (limited to 'security') diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index 696ccfa08d10..5adbfc32242f 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -28,11 +28,10 @@ #include #include #include -#include #include #include #include -#include +#include #include "encrypted.h" #include "ecryptfs_format.h" @@ -85,17 +84,17 @@ static const match_table_t key_tokens = { static int aes_get_sizes(void) { - struct crypto_blkcipher *tfm; + struct crypto_skcipher *tfm; - tfm = crypto_alloc_blkcipher(blkcipher_alg, 0, CRYPTO_ALG_ASYNC); + tfm = crypto_alloc_skcipher(blkcipher_alg, 0, CRYPTO_ALG_ASYNC); if (IS_ERR(tfm)) { pr_err("encrypted_key: failed to alloc_cipher (%ld)\n", PTR_ERR(tfm)); return PTR_ERR(tfm); } - ivsize = crypto_blkcipher_ivsize(tfm); - blksize = crypto_blkcipher_blocksize(tfm); - crypto_free_blkcipher(tfm); + ivsize = crypto_skcipher_ivsize(tfm); + blksize = crypto_skcipher_blocksize(tfm); + crypto_free_skcipher(tfm); return 0; } @@ -401,28 +400,37 @@ static int get_derived_key(u8 *derived_key, enum derived_key_type key_type, return ret; } -static int init_blkcipher_desc(struct blkcipher_desc *desc, const u8 *key, - unsigned int key_len, const u8 *iv, - unsigned int ivsize) +static struct skcipher_request *init_skcipher_req(const u8 *key, + unsigned int key_len) { + struct skcipher_request *req; + struct crypto_skcipher *tfm; int ret; - desc->tfm = crypto_alloc_blkcipher(blkcipher_alg, 0, CRYPTO_ALG_ASYNC); - if (IS_ERR(desc->tfm)) { + tfm = crypto_alloc_skcipher(blkcipher_alg, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(tfm)) { pr_err("encrypted_key: failed to load %s transform (%ld)\n", - blkcipher_alg, PTR_ERR(desc->tfm)); - return PTR_ERR(desc->tfm); + blkcipher_alg, PTR_ERR(tfm)); + return ERR_CAST(tfm); } - desc->flags = 0; - ret = crypto_blkcipher_setkey(desc->tfm, key, key_len); + ret = crypto_skcipher_setkey(tfm, key, key_len); if (ret < 0) { pr_err("encrypted_key: failed to setkey (%d)\n", ret); - crypto_free_blkcipher(desc->tfm); - return ret; + crypto_free_skcipher(tfm); + return ERR_PTR(ret); } - crypto_blkcipher_set_iv(desc->tfm, iv, ivsize); - return 0; + + req = skcipher_request_alloc(tfm, GFP_KERNEL); + if (!req) { + pr_err("encrypted_key: failed to allocate request for %s\n", + blkcipher_alg); + crypto_free_skcipher(tfm); + return ERR_PTR(-ENOMEM); + } + + skcipher_request_set_callback(req, 0, NULL, NULL); + return req; } static struct key *request_master_key(struct encrypted_key_payload *epayload, @@ -467,7 +475,8 @@ static int derived_key_encrypt(struct encrypted_key_payload *epayload, { struct scatterlist sg_in[2]; struct scatterlist sg_out[1]; - struct blkcipher_desc desc; + struct crypto_skcipher *tfm; + struct skcipher_request *req; unsigned int encrypted_datalen; unsigned int padlen; char pad[16]; @@ -476,9 +485,9 @@ static int derived_key_encrypt(struct encrypted_key_payload *epayload, encrypted_datalen = roundup(epayload->decrypted_datalen, blksize); padlen = encrypted_datalen - epayload->decrypted_datalen; - ret = init_blkcipher_desc(&desc, derived_key, derived_keylen, - epayload->iv, ivsize); - if (ret < 0) + req = init_skcipher_req(derived_key, derived_keylen); + ret = PTR_ERR(req); + if (IS_ERR(req)) goto out; dump_decrypted_data(epayload); @@ -491,8 +500,12 @@ static int derived_key_encrypt(struct encrypted_key_payload *epayload, sg_init_table(sg_out, 1); sg_set_buf(sg_out, epayload->encrypted_data, encrypted_datalen); - ret = crypto_blkcipher_encrypt(&desc, sg_out, sg_in, encrypted_datalen); - crypto_free_blkcipher(desc.tfm); + skcipher_request_set_crypt(req, sg_in, sg_out, encrypted_datalen, + epayload->iv); + ret = crypto_skcipher_encrypt(req); + tfm = crypto_skcipher_reqtfm(req); + skcipher_request_free(req); + crypto_free_skcipher(tfm); if (ret < 0) pr_err("encrypted_key: failed to encrypt (%d)\n", ret); else @@ -565,15 +578,16 @@ static int derived_key_decrypt(struct encrypted_key_payload *epayload, { struct scatterlist sg_in[1]; struct scatterlist sg_out[2]; - struct blkcipher_desc desc; + struct crypto_skcipher *tfm; + struct skcipher_request *req; unsigned int encrypted_datalen; char pad[16]; int ret; encrypted_datalen = roundup(epayload->decrypted_datalen, blksize); - ret = init_blkcipher_desc(&desc, derived_key, derived_keylen, - epayload->iv, ivsize); - if (ret < 0) + req = init_skcipher_req(derived_key, derived_keylen); + ret = PTR_ERR(req); + if (IS_ERR(req)) goto out; dump_encrypted_data(epayload, encrypted_datalen); @@ -585,8 +599,12 @@ static int derived_key_decrypt(struct encrypted_key_payload *epayload, epayload->decrypted_datalen); sg_set_buf(&sg_out[1], pad, sizeof pad); - ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in, encrypted_datalen); - crypto_free_blkcipher(desc.tfm); + skcipher_request_set_crypt(req, sg_in, sg_out, encrypted_datalen, + epayload->iv); + ret = crypto_skcipher_decrypt(req); + tfm = crypto_skcipher_reqtfm(req); + skcipher_request_free(req); + crypto_free_skcipher(tfm); if (ret < 0) goto out; dump_decrypted_data(epayload); -- cgit v1.2.3 From 42bbaabb12e687e9dcfa6cbb92567094cfbde7c2 Mon Sep 17 00:00:00 2001 From: Tadeusz Struk Date: Tue, 2 Feb 2016 10:08:58 -0800 Subject: integrity: convert digsig to akcipher api Convert asymmetric_verify to akcipher api. Signed-off-by: Tadeusz Struk Signed-off-by: Herbert Xu --- security/integrity/Kconfig | 1 + security/integrity/digsig_asymmetric.c | 10 +++------- 2 files changed, 4 insertions(+), 7 deletions(-) (limited to 'security') diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig index 21d756832b75..5533a011b58e 100644 --- a/security/integrity/Kconfig +++ b/security/integrity/Kconfig @@ -36,6 +36,7 @@ config INTEGRITY_ASYMMETRIC_KEYS select ASYMMETRIC_KEY_TYPE select ASYMMETRIC_PUBLIC_KEY_SUBTYPE select PUBLIC_KEY_ALGO_RSA + select CRYPTO_RSA select X509_CERTIFICATE_PARSER help This option enables digital signature verification using diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c index 5ade2a7517a6..2fa3bc681a1b 100644 --- a/security/integrity/digsig_asymmetric.c +++ b/security/integrity/digsig_asymmetric.c @@ -106,13 +106,9 @@ int asymmetric_verify(struct key *keyring, const char *sig, pks.pkey_hash_algo = hdr->hash_algo; pks.digest = (u8 *)data; pks.digest_size = datalen; - pks.nr_mpi = 1; - pks.rsa.s = mpi_read_raw_data(hdr->sig, siglen); - - if (pks.rsa.s) - ret = verify_signature(key, &pks); - - mpi_free(pks.rsa.s); + pks.s = hdr->sig; + pks.s_size = siglen; + ret = verify_signature(key, &pks); key_put(key); pr_debug("%s() = %d\n", __func__, ret); return ret; -- cgit v1.2.3 From f75516a81548074be331b3e41d2e3f448d1f8b8d Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Tue, 9 Feb 2016 16:18:01 +0800 Subject: crypto: keys - Revert "convert public key to akcipher api" This needs to go through the security tree so I'm reverting the patches for now. Signed-off-by: Herbert Xu --- crypto/asymmetric_keys/Kconfig | 2 +- crypto/asymmetric_keys/Makefile | 7 +- crypto/asymmetric_keys/pkcs7_parser.c | 12 +- crypto/asymmetric_keys/pkcs7_trust.c | 2 +- crypto/asymmetric_keys/pkcs7_verify.c | 2 +- crypto/asymmetric_keys/public_key.c | 64 ++++++--- crypto/asymmetric_keys/public_key.h | 36 +++++ crypto/asymmetric_keys/rsa.c | 213 +++++++++++++++++++----------- crypto/asymmetric_keys/x509_cert_parser.c | 37 +++++- crypto/asymmetric_keys/x509_public_key.c | 17 ++- crypto/asymmetric_keys/x509_rsakey.asn1 | 4 + include/crypto/public_key.h | 48 +++++-- security/integrity/Kconfig | 1 - security/integrity/digsig_asymmetric.c | 10 +- 14 files changed, 316 insertions(+), 139 deletions(-) create mode 100644 crypto/asymmetric_keys/public_key.h create mode 100644 crypto/asymmetric_keys/x509_rsakey.asn1 (limited to 'security') diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 905d745c2f85..4870f28403f5 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -22,7 +22,7 @@ config ASYMMETRIC_PUBLIC_KEY_SUBTYPE config PUBLIC_KEY_ALGO_RSA tristate "RSA public-key algorithm" - select CRYPTO_RSA + select MPILIB help This option enables support for the RSA algorithm (PKCS#1, RFC3447). diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index b78a194ea014..cd1406f9b14a 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -16,18 +16,21 @@ obj-$(CONFIG_X509_CERTIFICATE_PARSER) += x509_key_parser.o x509_key_parser-y := \ x509-asn1.o \ x509_akid-asn1.o \ + x509_rsakey-asn1.o \ x509_cert_parser.o \ x509_public_key.o $(obj)/x509_cert_parser.o: \ $(obj)/x509-asn1.h \ - $(obj)/x509_akid-asn1.h - + $(obj)/x509_akid-asn1.h \ + $(obj)/x509_rsakey-asn1.h $(obj)/x509-asn1.o: $(obj)/x509-asn1.c $(obj)/x509-asn1.h $(obj)/x509_akid-asn1.o: $(obj)/x509_akid-asn1.c $(obj)/x509_akid-asn1.h +$(obj)/x509_rsakey-asn1.o: $(obj)/x509_rsakey-asn1.c $(obj)/x509_rsakey-asn1.h clean-files += x509-asn1.c x509-asn1.h clean-files += x509_akid-asn1.c x509_akid-asn1.h +clean-files += x509_rsakey-asn1.c x509_rsakey-asn1.h # # PKCS#7 message handling diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c index 3ef62dac9771..8f3056cd0399 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.c +++ b/crypto/asymmetric_keys/pkcs7_parser.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include "public_key.h" #include "pkcs7_parser.h" #include "pkcs7-asn1.h" @@ -44,7 +44,7 @@ struct pkcs7_parse_context { static void pkcs7_free_signed_info(struct pkcs7_signed_info *sinfo) { if (sinfo) { - kfree(sinfo->sig.s); + mpi_free(sinfo->sig.mpi[0]); kfree(sinfo->sig.digest); kfree(sinfo->signing_cert_id); kfree(sinfo); @@ -614,14 +614,16 @@ int pkcs7_sig_note_signature(void *context, size_t hdrlen, const void *value, size_t vlen) { struct pkcs7_parse_context *ctx = context; + MPI mpi; BUG_ON(ctx->sinfo->sig.pkey_algo != PKEY_ALGO_RSA); - ctx->sinfo->sig.s = kmemdup(value, vlen, GFP_KERNEL); - if (!ctx->sinfo->sig.s) + mpi = mpi_read_raw_data(value, vlen); + if (!mpi) return -ENOMEM; - ctx->sinfo->sig.s_size = vlen; + ctx->sinfo->sig.mpi[0] = mpi; + ctx->sinfo->sig.nr_mpi = 1; return 0; } diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index 3bbdcc79a3d3..90d6d47965b0 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -17,7 +17,7 @@ #include #include #include -#include +#include "public_key.h" #include "pkcs7_parser.h" /** diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c index f5db1378c096..325575caf6b4 100644 --- a/crypto/asymmetric_keys/pkcs7_verify.c +++ b/crypto/asymmetric_keys/pkcs7_verify.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include "public_key.h" #include "pkcs7_parser.h" /* diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c index b383629b9e62..6db4c01c6503 100644 --- a/crypto/asymmetric_keys/public_key.c +++ b/crypto/asymmetric_keys/public_key.c @@ -18,16 +18,24 @@ #include #include #include -#include +#include "public_key.h" MODULE_LICENSE("GPL"); const char *const pkey_algo_name[PKEY_ALGO__LAST] = { - [PKEY_ALGO_DSA] = "dsa", - [PKEY_ALGO_RSA] = "rsa", + [PKEY_ALGO_DSA] = "DSA", + [PKEY_ALGO_RSA] = "RSA", }; EXPORT_SYMBOL_GPL(pkey_algo_name); +const struct public_key_algorithm *pkey_algo[PKEY_ALGO__LAST] = { +#if defined(CONFIG_PUBLIC_KEY_ALGO_RSA) || \ + defined(CONFIG_PUBLIC_KEY_ALGO_RSA_MODULE) + [PKEY_ALGO_RSA] = &RSA_public_key_algorithm, +#endif +}; +EXPORT_SYMBOL_GPL(pkey_algo); + const char *const pkey_id_type_name[PKEY_ID_TYPE__LAST] = { [PKEY_ID_PGP] = "PGP", [PKEY_ID_X509] = "X509", @@ -35,12 +43,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. */ @@ -51,8 +53,7 @@ static void public_key_describe(const struct key *asymmetric_key, if (key) seq_printf(m, "%s.%s", - pkey_id_type_name[key->id_type], - pkey_algo_name[key->pkey_algo]); + pkey_id_type_name[key->id_type], key->algo->name); } /* @@ -61,31 +62,50 @@ static void public_key_describe(const struct key *asymmetric_key, void public_key_destroy(void *payload) { struct public_key *key = payload; + int i; - if (key) - kfree(key->key); - kfree(key); + if (key) { + for (i = 0; i < ARRAY_SIZE(key->mpi); i++) + mpi_free(key->mpi[i]); + kfree(key); + } } EXPORT_SYMBOL_GPL(public_key_destroy); /* * Verify a signature using a public key. */ -int public_key_verify_signature(const struct public_key *pkey, +int public_key_verify_signature(const struct public_key *pk, const struct public_key_signature *sig) { - BUG_ON(!pkey); + const struct public_key_algorithm *algo; + + BUG_ON(!pk); + BUG_ON(!pk->mpi[0]); + BUG_ON(!pk->mpi[1]); BUG_ON(!sig); BUG_ON(!sig->digest); - BUG_ON(!sig->s); + BUG_ON(!sig->mpi[0]); + + algo = pk->algo; + if (!algo) { + if (pk->pkey_algo >= PKEY_ALGO__LAST) + return -ENOPKG; + algo = pkey_algo[pk->pkey_algo]; + if (!algo) + return -ENOPKG; + } - if (pkey->pkey_algo >= PKEY_ALGO__LAST) - return -ENOPKG; + if (!algo->verify_signature) + return -ENOTSUPP; - if (!alg_verify[pkey->pkey_algo]) - return -ENOPKG; + if (sig->nr_mpi != algo->n_sig_mpi) { + pr_debug("Signature has %u MPI not %u\n", + sig->nr_mpi, algo->n_sig_mpi); + return -EINVAL; + } - return alg_verify[pkey->pkey_algo](pkey, sig); + return algo->verify_signature(pk, sig); } EXPORT_SYMBOL_GPL(public_key_verify_signature); diff --git a/crypto/asymmetric_keys/public_key.h b/crypto/asymmetric_keys/public_key.h new file mode 100644 index 000000000000..5c37a22a0637 --- /dev/null +++ b/crypto/asymmetric_keys/public_key.h @@ -0,0 +1,36 @@ +/* Public key algorithm internals + * + * See Documentation/crypto/asymmetric-keys.txt + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include + +extern struct asymmetric_key_subtype public_key_subtype; + +/* + * Public key algorithm definition. + */ +struct public_key_algorithm { + const char *name; + u8 n_pub_mpi; /* Number of MPIs in public key */ + u8 n_sec_mpi; /* Number of MPIs in secret key */ + u8 n_sig_mpi; /* Number of MPIs in a signature */ + int (*verify_signature)(const struct public_key *key, + const struct public_key_signature *sig); +}; + +extern const struct public_key_algorithm RSA_public_key_algorithm; + +/* + * public_key.c + */ +extern int public_key_verify_signature(const struct public_key *pk, + const struct public_key_signature *sig); diff --git a/crypto/asymmetric_keys/rsa.c b/crypto/asymmetric_keys/rsa.c index 8b08ffcdb809..508b57b77474 100644 --- a/crypto/asymmetric_keys/rsa.c +++ b/crypto/asymmetric_keys/rsa.c @@ -11,10 +11,10 @@ #define pr_fmt(fmt) "RSA: "fmt #include +#include #include -#include -#include #include +#include "public_key.h" MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("RSA Public Key Algorithm"); @@ -84,10 +84,72 @@ static const struct { #undef _ }; -struct rsa_completion { - struct completion completion; - int err; -}; +/* + * RSAVP1() function [RFC3447 sec 5.2.2] + */ +static int RSAVP1(const struct public_key *key, MPI s, MPI *_m) +{ + MPI m; + int ret; + + /* (1) Validate 0 <= s < n */ + if (mpi_cmp_ui(s, 0) < 0) { + kleave(" = -EBADMSG [s < 0]"); + return -EBADMSG; + } + if (mpi_cmp(s, key->rsa.n) >= 0) { + kleave(" = -EBADMSG [s >= n]"); + return -EBADMSG; + } + + m = mpi_alloc(0); + if (!m) + return -ENOMEM; + + /* (2) m = s^e mod n */ + ret = mpi_powm(m, s, key->rsa.e, key->rsa.n); + if (ret < 0) { + mpi_free(m); + return ret; + } + + *_m = m; + return 0; +} + +/* + * Integer to Octet String conversion [RFC3447 sec 4.1] + */ +static int RSA_I2OSP(MPI x, size_t xLen, u8 **pX) +{ + unsigned X_size, x_size; + int X_sign; + u8 *X; + + /* Make sure the string is the right length. The number should begin + * with { 0x00, 0x01, ... } so we have to account for 15 leading zero + * bits not being reported by MPI. + */ + x_size = mpi_get_nbits(x); + pr_devel("size(x)=%u xLen*8=%zu\n", x_size, xLen * 8); + if (x_size != xLen * 8 - 15) + return -ERANGE; + + X = mpi_get_buffer(x, &X_size, &X_sign); + if (!X) + return -ENOMEM; + if (X_sign < 0) { + kfree(X); + return -EBADMSG; + } + if (X_size != xLen - 1) { + kfree(X); + return -EBADMSG; + } + + *pX = X; + return 0; +} /* * Perform the RSA signature verification. @@ -98,7 +160,7 @@ struct rsa_completion { * @asn1_template: The DigestInfo ASN.1 template * @asn1_size: Size of asm1_template[] */ -static int rsa_verify(const u8 *H, const u8 *EM, size_t k, size_t hash_size, +static int RSA_verify(const u8 *H, const u8 *EM, size_t k, size_t hash_size, const u8 *asn1_template, size_t asn1_size) { unsigned PS_end, T_offset, i; @@ -107,10 +169,10 @@ static int rsa_verify(const u8 *H, const u8 *EM, size_t k, size_t hash_size, if (k < 2 + 1 + asn1_size + hash_size) return -EBADMSG; - /* Decode the EMSA-PKCS1-v1_5 - * note: leading zeros are stirpped by the RSA implementation */ - if (EM[0] != 0x01) { - kleave(" = -EBADMSG [EM[0] == %02u]", EM[0]); + + /* Decode the EMSA-PKCS1-v1_5 */ + if (EM[1] != 0x01) { + kleave(" = -EBADMSG [EM[1] == %02u]", EM[1]); return -EBADMSG; } @@ -121,7 +183,7 @@ static int rsa_verify(const u8 *H, const u8 *EM, size_t k, size_t hash_size, return -EBADMSG; } - for (i = 1; i < PS_end; i++) { + for (i = 2; i < PS_end; i++) { if (EM[i] != 0xff) { kleave(" = -EBADMSG [EM[PS%x] == %02u]", i - 2, EM[i]); return -EBADMSG; @@ -142,82 +204,75 @@ static int rsa_verify(const u8 *H, const u8 *EM, size_t k, size_t hash_size, return 0; } -static void public_key_verify_done(struct crypto_async_request *req, int err) +/* + * Perform the verification step [RFC3447 sec 8.2.2]. + */ +static int RSA_verify_signature(const struct public_key *key, + const struct public_key_signature *sig) { - struct rsa_completion *compl = req->data; + size_t tsize; + int ret; - if (err == -EINPROGRESS) - return; + /* Variables as per RFC3447 sec 8.2.2 */ + const u8 *H = sig->digest; + u8 *EM = NULL; + MPI m = NULL; + size_t k; - compl->err = err; - complete(&compl->completion); -} + kenter(""); -int rsa_verify_signature(const struct public_key *pkey, - const struct public_key_signature *sig) -{ - struct crypto_akcipher *tfm; - struct akcipher_request *req; - struct rsa_completion compl; - struct scatterlist sig_sg, sg_out; - void *outbuf = NULL; - unsigned int outlen = 0; - int ret = -ENOMEM; - - tfm = crypto_alloc_akcipher("rsa", 0, 0); - if (IS_ERR(tfm)) - goto error_out; - - 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; - - ret = -EINVAL; - outlen = crypto_akcipher_maxsize(tfm); - if (!outlen) - goto error_free_req; - - /* initlialzie out buf */ - ret = -ENOMEM; - outbuf = kmalloc(outlen, GFP_KERNEL); - if (!outbuf) - goto error_free_req; - - sg_init_one(&sig_sg, sig->s, sig->s_size); - sg_init_one(&sg_out, outbuf, outlen); - akcipher_request_set_crypt(req, &sig_sg, &sg_out, 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); - - ret = crypto_akcipher_verify(req); - if (ret == -EINPROGRESS) { - wait_for_completion(&compl.completion); - ret = compl.err; + if (!RSA_ASN1_templates[sig->pkey_hash_algo].data) + return -ENOTSUPP; + + /* (1) Check the signature size against the public key modulus size */ + k = mpi_get_nbits(key->rsa.n); + tsize = mpi_get_nbits(sig->rsa.s); + + /* According to RFC 4880 sec 3.2, length of MPI is computed starting + * from most significant bit. So the RFC 3447 sec 8.2.2 size check + * must be relaxed to conform with shorter signatures - so we fail here + * only if signature length is longer than modulus size. + */ + pr_devel("step 1: k=%zu size(S)=%zu\n", k, tsize); + if (k < tsize) { + ret = -EBADMSG; + goto error; } - if (ret) - goto error_free_req; + /* Round up and convert to octets */ + k = (k + 7) / 8; - /* - * Output from the operation is an encoded message (EM) of - * length k octets. + /* (2b) Apply the RSAVP1 verification primitive to the public key */ + ret = RSAVP1(key, sig->rsa.s, &m); + if (ret < 0) + goto error; + + /* (2c) Convert the message representative (m) to an encoded message + * (EM) of length k octets. + * + * NOTE! The leading zero byte is suppressed by MPI, so we pass a + * pointer to the _preceding_ byte to RSA_verify()! */ - outlen = req->dst_len; - ret = rsa_verify(sig->digest, outbuf, outlen, sig->digest_size, + ret = RSA_I2OSP(m, k, &EM); + if (ret < 0) + goto error; + + ret = RSA_verify(H, EM - 1, k, sig->digest_size, RSA_ASN1_templates[sig->pkey_hash_algo].data, RSA_ASN1_templates[sig->pkey_hash_algo].size); -error_free_req: - akcipher_request_free(req); -error_free_tfm: - crypto_free_akcipher(tfm); -error_out: - kfree(outbuf); + +error: + kfree(EM); + mpi_free(m); + kleave(" = %d", ret); return ret; } -EXPORT_SYMBOL_GPL(rsa_verify_signature); + +const struct public_key_algorithm RSA_public_key_algorithm = { + .name = "RSA", + .n_pub_mpi = 2, + .n_sec_mpi = 3, + .n_sig_mpi = 1, + .verify_signature = RSA_verify_signature, +}; +EXPORT_SYMBOL_GPL(RSA_public_key_algorithm); diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index 7502029e3385..021d39c0ba75 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -15,10 +15,11 @@ #include #include #include -#include +#include "public_key.h" #include "x509_parser.h" #include "x509-asn1.h" #include "x509_akid-asn1.h" +#include "x509_rsakey-asn1.h" struct x509_parse_context { struct x509_certificate *cert; /* Certificate being constructed */ @@ -55,7 +56,7 @@ void x509_free_certificate(struct x509_certificate *cert) kfree(cert->akid_id); kfree(cert->akid_skid); kfree(cert->sig.digest); - kfree(cert->sig.s); + mpi_free(cert->sig.rsa.s); kfree(cert); } } @@ -102,12 +103,12 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen) } } - cert->pub->key = kmemdup(ctx->key, ctx->key_size, GFP_KERNEL); - if (!cert->pub->key) + /* Decode the public key */ + ret = asn1_ber_decoder(&x509_rsakey_decoder, ctx, + ctx->key, ctx->key_size); + if (ret < 0) goto error_decode; - cert->pub->keylen = ctx->key_size; - /* Generate cert issuer + serial number key ID */ kid = asymmetric_key_generate_id(cert->raw_serial, cert->raw_serial_size, @@ -123,7 +124,6 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen) return cert; error_decode: - kfree(cert->pub->key); kfree(ctx); error_no_ctx: x509_free_certificate(cert); @@ -404,6 +404,29 @@ int x509_extract_key_data(void *context, size_t hdrlen, return 0; } +/* + * Extract a RSA public key value + */ +int rsa_extract_mpi(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct x509_parse_context *ctx = context; + MPI mpi; + + if (ctx->nr_mpi >= ARRAY_SIZE(ctx->cert->pub->mpi)) { + pr_err("Too many public key MPIs in certificate\n"); + return -EBADMSG; + } + + mpi = mpi_read_raw_data(value, vlen); + if (!mpi) + return -ENOMEM; + + ctx->cert->pub->mpi[ctx->nr_mpi++] = mpi; + return 0; +} + /* The keyIdentifier in AuthorityKeyIdentifier SEQUENCE is tag(CONT,PRIM,0) */ #define SEQ_TAG_KEYID (ASN1_CONT << 6) diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 7092d5cbb5d3..9e9e5a6a9ed6 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -13,11 +13,15 @@ #include #include #include +#include +#include +#include #include #include #include #include #include "asymmetric_keys.h" +#include "public_key.h" #include "x509_parser.h" static bool use_builtin_keys; @@ -163,15 +167,13 @@ int x509_get_sig_params(struct x509_certificate *cert) if (cert->unsupported_crypto) return -ENOPKG; - if (cert->sig.s) + if (cert->sig.rsa.s) return 0; - cert->sig.s = kmemdup(cert->raw_sig, cert->raw_sig_size, - GFP_KERNEL); - if (!cert->sig.s) + cert->sig.rsa.s = mpi_read_raw_data(cert->raw_sig, cert->raw_sig_size); + if (!cert->sig.rsa.s) return -ENOMEM; - - cert->sig.s_size = cert->raw_sig_size; + cert->sig.nr_mpi = 1; /* Allocate the hashing algorithm we're going to need and find out how * big the hash operational data will be. @@ -294,6 +296,8 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) if (cert->pub->pkey_algo >= PKEY_ALGO__LAST || cert->sig.pkey_algo >= PKEY_ALGO__LAST || cert->sig.pkey_hash_algo >= PKEY_HASH__LAST || + !pkey_algo[cert->pub->pkey_algo] || + !pkey_algo[cert->sig.pkey_algo] || !hash_algo_name[cert->sig.pkey_hash_algo]) { ret = -ENOPKG; goto error_free_cert; @@ -305,6 +309,7 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) pkey_algo_name[cert->sig.pkey_algo], hash_algo_name[cert->sig.pkey_hash_algo]); + cert->pub->algo = pkey_algo[cert->pub->pkey_algo]; cert->pub->id_type = PKEY_ID_X509; /* Check the signature on the key if it appears to be self-signed */ diff --git a/crypto/asymmetric_keys/x509_rsakey.asn1 b/crypto/asymmetric_keys/x509_rsakey.asn1 new file mode 100644 index 000000000000..4ec7cc6532c1 --- /dev/null +++ b/crypto/asymmetric_keys/x509_rsakey.asn1 @@ -0,0 +1,4 @@ +RSAPublicKey ::= SEQUENCE { + modulus INTEGER ({ rsa_extract_mpi }), -- n + publicExponent INTEGER ({ rsa_extract_mpi }) -- e + } diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index a1693ed77be6..cc2516df0efa 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -14,6 +14,7 @@ #ifndef _LINUX_PUBLIC_KEY_H #define _LINUX_PUBLIC_KEY_H +#include #include enum pkey_algo { @@ -23,6 +24,7 @@ enum pkey_algo { }; extern const char *const pkey_algo_name[PKEY_ALGO__LAST]; +extern const struct public_key_algorithm *pkey_algo[PKEY_ALGO__LAST]; /* asymmetric key implementation supports only up to SHA224 */ #define PKEY_HASH__LAST (HASH_ALGO_SHA224 + 1) @@ -57,10 +59,31 @@ extern const char *const key_being_used_for[NR__KEY_BEING_USED_FOR]; * part. */ struct public_key { - void *key; - u32 keylen; + const struct public_key_algorithm *algo; + u8 capabilities; +#define PKEY_CAN_ENCRYPT 0x01 +#define PKEY_CAN_DECRYPT 0x02 +#define PKEY_CAN_SIGN 0x04 +#define PKEY_CAN_VERIFY 0x08 enum pkey_algo pkey_algo : 8; enum pkey_id_type id_type : 8; + union { + MPI mpi[5]; + struct { + MPI p; /* DSA prime */ + MPI q; /* DSA group order */ + MPI g; /* DSA group generator */ + MPI y; /* DSA public-key value = g^x mod p */ + MPI x; /* DSA secret exponent (if present) */ + } dsa; + struct { + MPI n; /* RSA public modulus */ + MPI e; /* RSA public encryption exponent */ + MPI d; /* RSA secret encryption exponent (if present) */ + MPI p; /* RSA secret prime (if present) */ + MPI q; /* RSA secret prime (if present) */ + } rsa; + }; }; extern void public_key_destroy(void *payload); @@ -69,15 +92,23 @@ extern void public_key_destroy(void *payload); * Public key cryptography signature data */ struct public_key_signature { - u8 *s; /* Signature */ - u32 s_size; /* Number of bytes in signature */ u8 *digest; - u8 digest_size; /* Number of bytes in digest */ + u8 digest_size; /* Number of bytes in digest */ + u8 nr_mpi; /* Occupancy of mpi[] */ enum pkey_algo pkey_algo : 8; enum hash_algo pkey_hash_algo : 8; + union { + MPI mpi[2]; + struct { + MPI s; /* m^d mod n */ + } rsa; + struct { + MPI r; + MPI s; + } dsa; + }; }; -extern struct asymmetric_key_subtype public_key_subtype; struct key; extern int verify_signature(const struct key *key, const struct public_key_signature *sig); @@ -88,9 +119,4 @@ extern struct key *x509_request_asymmetric_key(struct key *keyring, const struct asymmetric_key_id *skid, bool partial); -int public_key_verify_signature(const struct public_key *pkey, - const struct public_key_signature *sig); - -int rsa_verify_signature(const struct public_key *pkey, - const struct public_key_signature *sig); #endif /* _LINUX_PUBLIC_KEY_H */ diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig index 5533a011b58e..21d756832b75 100644 --- a/security/integrity/Kconfig +++ b/security/integrity/Kconfig @@ -36,7 +36,6 @@ config INTEGRITY_ASYMMETRIC_KEYS select ASYMMETRIC_KEY_TYPE select ASYMMETRIC_PUBLIC_KEY_SUBTYPE select PUBLIC_KEY_ALGO_RSA - select CRYPTO_RSA select X509_CERTIFICATE_PARSER help This option enables digital signature verification using diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c index 2fa3bc681a1b..5ade2a7517a6 100644 --- a/security/integrity/digsig_asymmetric.c +++ b/security/integrity/digsig_asymmetric.c @@ -106,9 +106,13 @@ int asymmetric_verify(struct key *keyring, const char *sig, pks.pkey_hash_algo = hdr->hash_algo; pks.digest = (u8 *)data; pks.digest_size = datalen; - pks.s = hdr->sig; - pks.s_size = siglen; - ret = verify_signature(key, &pks); + pks.nr_mpi = 1; + pks.rsa.s = mpi_read_raw_data(hdr->sig, siglen); + + if (pks.rsa.s) + ret = verify_signature(key, &pks); + + mpi_free(pks.rsa.s); key_put(key); pr_debug("%s() = %d\n", __func__, ret); return ret; -- cgit v1.2.3