From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756583Ab2ARLgY (ORCPT ); Wed, 18 Jan 2012 06:36:24 -0500 Received: from mga05.intel.com ([192.55.52.89]:57501 "EHLO fmsmga101.fm.intel.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1756084Ab2ARLgV convert rfc822-to-8bit (ORCPT ); Wed, 18 Jan 2012 06:36:21 -0500 MIME-Version: 1.0 In-Reply-To: <20111202184548.21874.69507.stgit@warthog.procyon.org.uk> References: <20111202184229.21874.25782.stgit@warthog.procyon.org.uk> <20111202184548.21874.69507.stgit@warthog.procyon.org.uk> Date: Wed, 18 Jan 2012 13:36:20 +0200 Message-ID: Subject: Re: [PATCH 16/21] KEYS: PGP-based public key signature verification [ver #3] From: "Kasatkin, Dmitry" To: David Howells Cc: keyrings@linux-nfs.org, linux-crypto@vger.kernel.org, linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org, zohar@linux.vnet.ibm.com, arjan.van.de.ven@intel.com, alan.cox@intel.com Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8BIT Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Fri, Dec 2, 2011 at 8:45 PM, David Howells wrote: > Provide handlers for PGP-based public-key algorithm signature verification. > This does most of the work involved in signature verification as most of it is > public-key algorithm agnostic.  The public-key verification algorithm itself > is just the last little bit and is supplied the complete hash data to process. > > This requires glue logic putting on top to make use of it - something the next > patch provides. > > Signed-off-by: David Howells > --- > >  security/keys/Makefile         |    3 >  security/keys/pgp_parser.h     |    6 + >  security/keys/pgp_pubkey_sig.c |  323 ++++++++++++++++++++++++++++++++++++++++ >  3 files changed, 331 insertions(+), 1 deletions(-) >  create mode 100644 security/keys/pgp_pubkey_sig.c > > > diff --git a/security/keys/Makefile b/security/keys/Makefile > index 242a087..fc1968e 100644 > --- a/security/keys/Makefile > +++ b/security/keys/Makefile > @@ -34,4 +34,5 @@ obj-$(CONFIG_CRYPTO_KEY_PGP_PARSER) += pgp_parser.o >  crypto_keys-y := crypto_type.o crypto_verify.o > >  pgp_parser-y := \ > -       pgp_key_parser.o > +       pgp_key_parser.o \ > +       pgp_pubkey_sig.o > diff --git a/security/keys/pgp_parser.h b/security/keys/pgp_parser.h > index 1cda231..a6192ce 100644 > --- a/security/keys/pgp_parser.h > +++ b/security/keys/pgp_parser.h > @@ -21,3 +21,9 @@ >  */ >  extern const >  struct public_key_algorithm *pgp_public_key_algorithms[PGP_PUBKEY__LAST]; > + > +/* > + * pgp_pubkey_sig.c > + */ > +extern struct crypto_key_verify_context *pgp_pkey_verify_sig_begin( > +       struct key *crypto_key, const u8 *sigdata, size_t siglen); > diff --git a/security/keys/pgp_pubkey_sig.c b/security/keys/pgp_pubkey_sig.c > new file mode 100644 > index 0000000..b4b7cb0 > --- /dev/null > +++ b/security/keys/pgp_pubkey_sig.c > @@ -0,0 +1,323 @@ > +/* Handling for PGP public key signature data [RFC 4880] > + * > + * Copyright (C) 2011 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. > + */ > + > +#define pr_fmt(fmt) "PGPSIG: "fmt > +#include > +#include > +#include > +#include > +#include "public_key.h" > +#include "pgp_parser.h" > + > +const struct { > +       enum pkey_hash_algo algo : 8; > +} pgp_pubkey_hash[PGP_HASH__LAST] = { > +       [PGP_HASH_MD5].algo             = PKEY_HASH_MD5, > +       [PGP_HASH_SHA1].algo            = PKEY_HASH_SHA1, > +       [PGP_HASH_RIPE_MD_160].algo     = PKEY_HASH_RIPE_MD_160, > +       [PGP_HASH_SHA256].algo          = PKEY_HASH_SHA256, > +       [PGP_HASH_SHA384].algo          = PKEY_HASH_SHA384, > +       [PGP_HASH_SHA512].algo          = PKEY_HASH_SHA512, > +       [PGP_HASH_SHA224].algo          = PKEY_HASH_SHA224, > +}; > + > +static int pgp_pkey_verify_sig_add_data(struct crypto_key_verify_context *ctx, > +                                       const void *data, size_t datalen); > +static int pgp_pkey_verify_sig_end(struct crypto_key_verify_context *ctx, > +                                  const u8 *sig, size_t siglen); > +static void pgp_pkey_verify_sig_cancel(struct crypto_key_verify_context *ctx); > + > +struct pgp_pkey_sig_parse_context { > +       struct pgp_parse_context pgp; > +       struct pgp_sig_parameters params; > +}; > + > +static int pgp_pkey_parse_signature(struct pgp_parse_context *context, > +                                   enum pgp_packet_tag type, > +                                   u8 headerlen, > +                                   const u8 *data, > +                                   size_t datalen) > +{ > +       struct pgp_pkey_sig_parse_context *ctx = > +               container_of(context, struct pgp_pkey_sig_parse_context, pgp); > + > +       return pgp_parse_sig_params(&data, &datalen, &ctx->params); > +} > + > +/* > + * Begin the process of verifying a DSA signature. > + * > + * This involves allocating the hash into which first the data and then the > + * metadata will be put, and parsing the signature to check that it matches the > + * key. > + */ > +struct crypto_key_verify_context *pgp_pkey_verify_sig_begin( > +       struct key *crypto_key, const u8 *sigdata, size_t siglen) > +{ > +       struct pgp_pkey_sig_parse_context p; > +       struct public_key_signature *sig; > +       struct crypto_shash *tfm; > +       const struct public_key *key = crypto_key->payload.data; > +       size_t digest_size, desc_size; > +       int ret; > + > +       kenter("{%d},,%zu", key_serial(crypto_key), siglen); > + > +       if (!key) { > +               kleave(" = -ENOKEY [no public key]"); > +               return ERR_PTR(-ENOKEY); > +       } > + > +       p.pgp.types_of_interest = (1 << PGP_PKT_SIGNATURE); > +       p.pgp.process_packet = pgp_pkey_parse_signature; > +       ret = pgp_parse_packets(sigdata, siglen, &p.pgp); > +       if (ret < 0) > +               return ERR_PTR(ret); > + > +       if (p.params.pubkey_algo >= PGP_PUBKEY__LAST || > +           !pgp_public_key_algorithms[p.params.pubkey_algo]) { > +               pr_debug("Unsupported public key algorithm %u\n", > +                        p.params.pubkey_algo); > +               return ERR_PTR(-ENOKEY); > +       } > + > +       if (pgp_public_key_algorithms[p.params.pubkey_algo] != key->algo) { > +               kleave(" = -ENOKEY [wrong pk algo]"); > +               return ERR_PTR(-ENOKEY); > +       } > + > +       if (!(key->capabilities & PKEY_CAN_VERIFY)) { > +               kleave(" = -EKEYREJECTED [key can't verify]"); > +               return ERR_PTR(-EKEYREJECTED); > +       } > + > +       if (p.params.hash_algo >= PGP_HASH__LAST || > +           !pgp_hash_algorithms[p.params.hash_algo]) { > +               kleave(" = -ENOPKG [hash]"); > +               return ERR_PTR(-ENOPKG); > +       } > + > +       pr_debug("Signature generated with %s hash\n", > +                pgp_hash_algorithms[p.params.hash_algo]); > + > +       if (memcmp(&p.params.issuer, key->key_id, 8) != 0) { > +               kleave(" = -ENOKEY [wrong key ID]"); > +               return ERR_PTR(-ENOKEY); > +       } > + > +       if (p.params.signature_type != PGP_SIG_BINARY_DOCUMENT_SIG && > +           p.params.signature_type != PGP_SIG_STANDALONE_SIG) { > +               /* We don't want to canonicalise */ > +               kleave(" = -EOPNOTSUPP [canon]"); > +               return ERR_PTR(-EOPNOTSUPP); > +       } > + > +       /* Allocate the hashing algorithm we're going to need and find out how > +        * big the hash operational data will be. > +        */ > +       tfm = crypto_alloc_shash(pgp_hash_algorithms[p.params.hash_algo], 0, 0); > +       if (IS_ERR(tfm)) > +               return PTR_ERR(tfm) == -ENOENT ? > +                       ERR_PTR(-ENOPKG) : ERR_CAST(tfm); > + > +       desc_size = crypto_shash_descsize(tfm); > +       digest_size = crypto_shash_digestsize(tfm); > + > +       /* We allocate the hash operational data storage on the end of our > +        * context data. > +        */ > +       sig = kzalloc(sizeof(*sig) + desc_size + digest_size, GFP_KERNEL); > +       if (!sig) { > +               crypto_free_shash(tfm); > +               return ERR_PTR(-ENOMEM); > +       } > + > +       sig->base.key           = crypto_key; > +       sig->base.add_data      = pgp_pkey_verify_sig_add_data; > +       sig->base.end           = pgp_pkey_verify_sig_end; > +       sig->base.cancel        = pgp_pkey_verify_sig_cancel; > +       sig->pkey_hash_algo     = pgp_pubkey_hash[p.params.hash_algo].algo; > +       sig->digest             = (u8 *)sig + sizeof(*sig) + desc_size; > +       sig->digest_size        = digest_size; > +       sig->hash.tfm           = tfm; > +       sig->hash.flags         = CRYPTO_TFM_REQ_MAY_SLEEP; > + > +       ret = crypto_shash_init(&sig->hash); > +       if (ret < 0) { > +               crypto_free_shash(sig->hash.tfm); > +               kfree(sig); > +               return ERR_PTR(ret); > +       } > + > +       key_get(sig->base.key); > +       kleave(" = %p", sig); > +       return &sig->base; > +} > + > +/* > + * Load data into the hash > + */ > +static int pgp_pkey_verify_sig_add_data(struct crypto_key_verify_context *ctx, > +                                       const void *data, size_t datalen) > +{ > +       struct public_key_signature *sig = > +               container_of(ctx, struct public_key_signature, base); > + > +       return crypto_shash_update(&sig->hash, data, datalen); > +} Hello, Synchronous hash SHASH is used only for software hash implementation... HW acceleration is not supported by this hash. It is good for short data. But when calculating a hash over long data as files can be, async hash AHASH is a preferred choice as enables HW acceleration. As in my response to [PATCH 08/21] KEYS: Add signature verification facility [ver #3] It would be nice to have API to pass pre-computed hash, then client might tackle async peculiarities by itself... - Dmitry > + > +struct pgp_pkey_sig_digest_context { > +       struct pgp_parse_context pgp; > +       const struct public_key *key; > +       struct public_key_signature *sig; > +}; > + > +/* > + * Extract required metadata from the signature packet and add what we need to > + * to the hash. > + */ > +static int pgp_pkey_digest_signature(struct pgp_parse_context *context, > +                                    enum pgp_packet_tag type, > +                                    u8 headerlen, > +                                    const u8 *data, > +                                    size_t datalen) > +{ > +       struct pgp_pkey_sig_digest_context *ctx = > +               container_of(context, struct pgp_pkey_sig_digest_context, pgp); > +       enum pgp_signature_version version; > +       int i; > + > +       kenter(",%u,%u,,%zu", type, headerlen, datalen); > + > +       version = *data; > +       if (version == PGP_SIG_VERSION_3) { > +               /* We just include an excerpt of the metadata from a V3 > +                * signature. > +                */ > +               crypto_shash_update(&ctx->sig->hash, data + 1, 5); > +               data += sizeof(struct pgp_signature_v3_packet); > +               datalen -= sizeof(struct pgp_signature_v3_packet); > +       } else if (version == PGP_SIG_VERSION_4) { > +               /* We add the whole metadata header and some of the hashed data > +                * for a V4 signature, plus a trailer. > +                */ > +               size_t hashedsz, unhashedsz; > +               u8 trailer[6]; > + > +               hashedsz = 4 + 2 + (data[4] << 8) + data[5]; > +               crypto_shash_update(&ctx->sig->hash, data, hashedsz); > + > +               trailer[0] = version; > +               trailer[1] = 0xffU; > +               trailer[2] = hashedsz >> 24; > +               trailer[3] = hashedsz >> 16; > +               trailer[4] = hashedsz >> 8; > +               trailer[5] = hashedsz; > + > +               crypto_shash_update(&ctx->sig->hash, trailer, 6); > +               data += hashedsz; > +               datalen -= hashedsz; > + > +               unhashedsz = 2 + (data[0] << 8) + data[1]; > +               data += unhashedsz; > +               datalen -= unhashedsz; > +       } > + > +       if (datalen <= 2) { > +               kleave(" = -EBADMSG"); > +               return -EBADMSG; > +       } > + > +       /* There's a quick check on the hash available. */ > +       ctx->sig->signed_hash_msw[0] = *data++; > +       ctx->sig->signed_hash_msw[1] = *data++; > +       datalen -= 2; > + > +       /* And then the cryptographic data, which we'll need for the > +        * algorithm. > +        */ > +       for (i = 0; i < ctx->key->algo->n_sig_mpi; i++) { > +               unsigned int remaining = datalen; > +               if (remaining == 0) { > +                       pr_debug("short %zu mpi %d\n", datalen, i); > +                       return -EBADMSG; > +               } > +               ctx->sig->mpi[i] = mpi_read_from_buffer(data, &remaining); > +               if (!ctx->sig->mpi[i]) > +                       return -ENOMEM; > +               data += remaining; > +               datalen -= remaining; > +       } > + > +       if (datalen != 0) { > +               kleave(" = -EBADMSG [trailer %zu]", datalen); > +               return -EBADMSG; > +       } > + > +       kleave(" = 0"); > +       return 0; > +} > + > +/* > + * The data is now all loaded into the hash; load the metadata, finalise the > + * hash and perform the verification step. > + */ > +static int pgp_pkey_verify_sig_end(struct crypto_key_verify_context *ctx, > +                                  const u8 *sigdata, size_t siglen) > +{ > +       struct public_key_signature *sig = > +               container_of(ctx, struct public_key_signature, base); > +       const struct public_key *key = sig->base.key->payload.data; > +       struct pgp_pkey_sig_digest_context p; > +       int ret; > + > +       kenter(""); > + > +       /* Firstly we add metadata, starting with some of the data from the > +        * signature packet */ > +       p.pgp.types_of_interest = (1 << PGP_PKT_SIGNATURE); > +       p.pgp.process_packet = pgp_pkey_digest_signature; > +       p.key = key; > +       p.sig = sig; > +       ret = pgp_parse_packets(sigdata, siglen, &p.pgp); > +       if (ret < 0) > +               goto error_free_ctx; > + > +       crypto_shash_final(&sig->hash, sig->digest); > + > +       ret = key->algo->verify(key, sig); > + > +error_free_ctx: > +       pgp_pkey_verify_sig_cancel(ctx); > +       kleave(" = %d", ret); > +       return ret; > +} > + > +/* > + * Cancel an in-progress data loading > + */ > +static void pgp_pkey_verify_sig_cancel(struct crypto_key_verify_context *ctx) > +{ > +       struct public_key_signature *sig = > +               container_of(ctx, struct public_key_signature, base); > +       int i; > + > +       kenter(""); > + > +       /* !!! Do we need to tell the crypto layer to cancel too? */ > +       crypto_free_shash(sig->hash.tfm); > +       key_put(sig->base.key); > +       for (i = 0; i < ARRAY_SIZE(sig->mpi); i++) > +               mpi_free(sig->mpi[i]); > +       kfree(sig); > + > +       kleave(""); > +} >