Re: [PATCH 16/21] KEYS: PGP-based public key signature verification [ver #3]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]


On Fri, Dec 2, 2011 at 8:45 PM, David Howells <dhowells@xxxxxxxxxx> 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 <dhowells@xxxxxxxxxx>
> ---
>
>  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@xxxxxxxxxx)
> + *
> + * 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 <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/pgp.h>
> +#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("");
> +}
>
--
To unsubscribe from this list: send the line "unsubscribe linux-crypto" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Kernel]     [Gnu Classpath]     [Gnu Crypto]     [DM Crypt]     [Netfilter]     [Bugtraq]

Add to Google