lkml.org 
[lkml]   [2011]   [Dec]   [2]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 15/21] KEYS: PGP data parser [ver #3]
    Date
    Implement a PGP data parser for the crypto key type to use when instantiating a
    key.

    This parser attempts to parse the instantiation data as a PGP packet sequence
    (RFC 4880) and if it parses okay, attempts to extract a public-key algorithm
    key or subkey from it.

    If it finds such a key, it will set up a public_key subtype payload with
    appropriate handler routines (DSA or RSA) and attach it to the key.

    Signed-off-by: David Howells <dhowells@redhat.com>
    ---

    security/keys/Kconfig | 12 +
    security/keys/Makefile | 4
    security/keys/pgp_key_parser.c | 342 ++++++++++++++++++++++++++++++++++++++++
    security/keys/pgp_parser.h | 23 +++
    4 files changed, 381 insertions(+), 0 deletions(-)
    create mode 100644 security/keys/pgp_key_parser.c
    create mode 100644 security/keys/pgp_parser.h


    diff --git a/security/keys/Kconfig b/security/keys/Kconfig
    index 3196ba8..8ffe822 100644
    --- a/security/keys/Kconfig
    +++ b/security/keys/Kconfig
    @@ -105,3 +105,15 @@ config PGP_LIBRARY
    help
    This option enables a library that provides a number of simple
    utility functions for parsing PGP (RFC 4880) packet-based messages.
    +
    +config CRYPTO_KEY_PGP_PARSER
    + tristate "PGP key blob parser"
    + depends on CRYPTO_KEY_TYPE
    + select CRYPTO_KEY_PUBLIC_KEY_SUBTYPE
    + select PGP_LIBRARY
    + select MD5 # V3 fingerprint generation
    + select SHA1 # V4 fingerprint generation
    + help
    + This option provides support for parsing PGP (RFC 4880) format blobs
    + for key data and provides the ability to instantiate a crypto key
    + from a public key packet found inside the blob.
    diff --git a/security/keys/Makefile b/security/keys/Makefile
    index 3c3918a..242a087 100644
    --- a/security/keys/Makefile
    +++ b/security/keys/Makefile
    @@ -29,5 +29,9 @@ obj-$(CONFIG_CRYPTO_KEY_PUBLIC_KEY_SUBTYPE) += public_key.o
    obj-$(CONFIG_CRYPTO_KEY_PKEY_ALGO_DSA) += crypto_dsa.o
    obj-$(CONFIG_CRYPTO_KEY_PKEY_ALGO_RSA) += crypto_rsa.o
    obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o
    +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
    diff --git a/security/keys/pgp_key_parser.c b/security/keys/pgp_key_parser.c
    new file mode 100644
    index 0000000..d0d8a86
    --- /dev/null
    +++ b/security/keys/pgp_key_parser.c
    @@ -0,0 +1,342 @@
    +/* Parser for PGP format key 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) "PGP: "fmt
    +#include <keys/crypto-subtype.h>
    +#include <linux/module.h>
    +#include <linux/kernel.h>
    +#include <linux/slab.h>
    +#include <linux/mpi.h>
    +#include <linux/pgp.h>
    +#include <crypto/hash.h>
    +#include "public_key.h"
    +#include "pgp_parser.h"
    +
    +MODULE_LICENSE("GPL");
    +
    +const
    +struct public_key_algorithm *pgp_public_key_algorithms[PGP_PUBKEY__LAST] = {
    +#if defined(CONFIG_CRYPTO_KEY_PKEY_ALGO_RSA) || \
    + defined(CONFIG_CRYPTO_KEY_PKEY_ALGO_RSA_MODULE)
    + [PGP_PUBKEY_RSA_ENC_OR_SIG] = &RSA_public_key_algorithm,
    + [PGP_PUBKEY_RSA_ENC_ONLY] = &RSA_public_key_algorithm,
    + [PGP_PUBKEY_RSA_SIG_ONLY] = &RSA_public_key_algorithm,
    +#endif
    + [PGP_PUBKEY_ELGAMAL] = NULL,
    +#if defined(CONFIG_CRYPTO_KEY_PKEY_ALGO_DSA) || \
    + defined(CONFIG_CRYPTO_KEY_PKEY_ALGO_DSA_MODULE)
    + [PGP_PUBKEY_DSA] = &DSA_public_key_algorithm,
    +#endif
    +};
    +
    +static const u8 pgp_public_key_capabilities[PGP_PUBKEY__LAST] = {
    + [PGP_PUBKEY_RSA_ENC_OR_SIG] = PKEY_CAN_ENCDEC | PKEY_CAN_SIGVER,
    + [PGP_PUBKEY_RSA_ENC_ONLY] = PKEY_CAN_ENCDEC,
    + [PGP_PUBKEY_RSA_SIG_ONLY] = PKEY_CAN_SIGVER,
    + [PGP_PUBKEY_ELGAMAL] = 0,
    + [PGP_PUBKEY_DSA] = PKEY_CAN_SIGVER,
    +};
    +
    +static inline void digest_putc(struct shash_desc *digest, uint8_t ch)
    +{
    + crypto_shash_update(digest, &ch, 1);
    +}
    +
    +struct pgp_key_data_parse_context {
    + struct pgp_parse_context pgp;
    + struct crypto_key_subtype *subtype;
    + char *fingerprint;
    + void *payload;
    +};
    +
    +/*
    + * Calculate the public key ID (RFC4880 12.2)
    + */
    +static int pgp_calc_pkey_keyid(struct shash_desc *digest,
    + struct pgp_parse_pubkey *pgp,
    + struct public_key *key)
    +{
    + unsigned nb[ARRAY_SIZE(key->mpi)];
    + unsigned nn[ARRAY_SIZE(key->mpi)];
    + unsigned n;
    + u8 *pp[ARRAY_SIZE(key->mpi)];
    + u32 a32;
    + int npkey = key->algo->n_pub_mpi;
    + int i, ret = -ENOMEM;
    +
    + kenter("");
    +
    + n = (pgp->version < PGP_KEY_VERSION_4) ? 8 : 6;
    + for (i = 0; i < npkey; i++) {
    + nb[i] = mpi_get_nbits(key->mpi[i]);
    + pp[i] = mpi_get_buffer(key->mpi[i], nn + i, NULL);
    + if (!pp[i])
    + goto error;
    + n += 2 + nn[i];
    + }
    +
    + digest_putc(digest, 0x99); /* ctb */
    + digest_putc(digest, n >> 8); /* 16-bit header length */
    + digest_putc(digest, n);
    + digest_putc(digest, pgp->version);
    +
    + a32 = pgp->creation_time;
    + digest_putc(digest, a32 >> 24);
    + digest_putc(digest, a32 >> 16);
    + digest_putc(digest, a32 >> 8);
    + digest_putc(digest, a32 >> 0);
    +
    + if (pgp->version < PGP_KEY_VERSION_4) {
    + u16 a16;
    +
    + if( pgp->expires_at)
    + a16 = (pgp->expires_at - pgp->creation_time) / 86400UL;
    + else
    + a16 = 0;
    + digest_putc(digest, a16 >> 8);
    + digest_putc(digest, a16 >> 0);
    + }
    +
    + digest_putc(digest, pgp->pubkey_algo);
    +
    + for (i = 0; i < npkey; i++) {
    + digest_putc(digest, nb[i] >> 8);
    + digest_putc(digest, nb[i]);
    + crypto_shash_update(digest, pp[i], nn[i]);
    + }
    + ret = 0;
    +
    +error:
    + for (i = 0; i < npkey; i++)
    + kfree(pp[i]);
    + kleave(" = %d", ret);
    + return ret;
    +}
    +
    +/*
    + * Calculate the public key ID fingerprint
    + */
    +static int pgp_generate_fingerprint(struct pgp_key_data_parse_context *ctx,
    + struct pgp_parse_pubkey *pgp,
    + struct public_key *key)
    +{
    + struct crypto_shash *tfm;
    + struct shash_desc *digest;
    + char *fingerprint;
    + u8 *raw_fingerprint;
    + int digest_size, offset;
    + int ret, i;
    +
    + ret = -ENOMEM;
    + tfm = crypto_alloc_shash(pgp->version < PGP_KEY_VERSION_4 ?
    + "md5" : "sha1", 0, 0);
    + if (!tfm)
    + goto cleanup;
    +
    + digest = kmalloc(sizeof(*digest) + crypto_shash_descsize(tfm),
    + GFP_KERNEL);
    + if (!digest)
    + goto cleanup_tfm;
    +
    + digest->tfm = tfm;
    + digest->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
    + ret = crypto_shash_init(digest);
    + if (ret < 0)
    + goto cleanup_hash;
    +
    + ret = pgp_calc_pkey_keyid(digest, pgp, key);
    + if (ret < 0)
    + goto cleanup_hash;
    +
    + digest_size = crypto_shash_digestsize(tfm);
    +
    + raw_fingerprint = kmalloc(digest_size, GFP_KERNEL);
    + if (!raw_fingerprint)
    + goto cleanup_hash;
    +
    + ret = crypto_shash_final(digest, raw_fingerprint);
    + if (ret < 0)
    + goto cleanup_raw_fingerprint;
    +
    + fingerprint = kmalloc(digest_size * 2 + 1, GFP_KERNEL);
    + if (!fingerprint)
    + goto cleanup_raw_fingerprint;
    +
    + offset = digest_size - 8;
    + pr_debug("offset %u/%u\n", offset, digest_size);
    +
    + for (i = 0; i < digest_size; i++)
    + sprintf(fingerprint + i * 2, "%02x", raw_fingerprint[i]);
    + pr_debug("fingerprint %s\n", fingerprint);
    +
    + memcpy(&key->key_id, raw_fingerprint + offset, 8);
    + key->key_id_size = 8;
    +
    + ctx->fingerprint = fingerprint;
    + ret = 0;
    +cleanup_raw_fingerprint:
    + kfree(raw_fingerprint);
    +cleanup_hash:
    + kfree(digest);
    +cleanup_tfm:
    + crypto_free_shash(tfm);
    +cleanup:
    + kleave(" = %d", ret);
    + return ret;
    +}
    +
    +/*
    + * Extract a public key or public subkey from the PGP stream.
    + */
    +static int pgp_process_public_key(struct pgp_parse_context *context,
    + enum pgp_packet_tag type,
    + u8 headerlen,
    + const u8 *data,
    + size_t datalen)
    +{
    + const struct public_key_algorithm *algo;
    + struct pgp_key_data_parse_context *ctx =
    + container_of(context, struct pgp_key_data_parse_context, pgp);
    + struct pgp_parse_pubkey pgp;
    + struct public_key *key;
    + int i, ret;
    +
    + kenter(",%u,%u,,%zu", type, headerlen, datalen);
    +
    + if (ctx->subtype) {
    + kleave(" = -ENOKEY [already]");
    + return -EBADMSG;
    + }
    +
    + key = kzalloc(sizeof(struct public_key), GFP_KERNEL);
    + if (!key)
    + return -ENOMEM;
    +
    + ret = pgp_parse_public_key(&data, &datalen, &pgp);
    + if (ret < 0)
    + goto cleanup;
    +
    + if (pgp.pubkey_algo >= PGP_PUBKEY__LAST ||
    + !pgp_public_key_algorithms[pgp.pubkey_algo]) {
    + pr_debug("Unsupported public key algorithm %u\n",
    + pgp.pubkey_algo);
    + ret = -ENOPKG;
    + goto cleanup;
    + }
    +
    + algo = key->algo = pgp_public_key_algorithms[pgp.pubkey_algo];
    +
    + /* It's a public key, so that only gives us encrypt and verify
    + * capabilities.
    + */
    + key->capabilities = pgp_public_key_capabilities[pgp.pubkey_algo] &
    + (PKEY_CAN_ENCRYPT | PKEY_CAN_VERIFY);
    +
    + ret = -ENOMEM;
    + for (i = 0; i < algo->n_pub_mpi; i++) {
    + unsigned int remaining = datalen;
    + ret = -EBADMSG;
    + if (remaining == 0) {
    + pr_debug("short %zu mpi %d\n", datalen, i);
    + goto cleanup;
    + }
    + key->mpi[i] = mpi_read_from_buffer(data, &remaining);
    + if (!key->mpi[i])
    + goto cleanup;
    + data += remaining;
    + datalen -= remaining;
    + }
    +
    + ret = -EBADMSG;
    + if (datalen != 0) {
    + pr_debug("excess %zu\n", datalen);
    + goto cleanup;
    + }
    +
    + ret = pgp_generate_fingerprint(ctx, &pgp, key);
    + if (ret < 0)
    + goto cleanup;
    +
    + ctx->subtype = &public_key_crypto_key_subtype;
    + ctx->payload = key;
    + kleave(" = 0 [use]");
    + return 0;
    +
    +cleanup:
    + pr_devel("cleanup");
    + if (key) {
    + for (i = 0; i < ARRAY_SIZE(key->mpi); i++)
    + mpi_free(key->mpi[i]);
    + kfree(key);
    + }
    + kleave(" = %d", ret);
    + return ret;
    +}
    +
    +/*
    + * Attempt to parse the instantiation data blob for a key as a PGP packet
    + * message holding a key.
    + */
    +static int pgp_key_instantiate(struct key *key,
    + const void *data, size_t datalen)
    +{
    + struct pgp_key_data_parse_context ctx;
    + int ret;
    +
    + kenter("");
    +
    + ret = key_payload_reserve(key, datalen);
    + if (ret < 0)
    + return ret;
    +
    + ctx.pgp.types_of_interest =
    + (1 << PGP_PKT_PUBLIC_KEY) | (1 << PGP_PKT_PUBLIC_SUBKEY);
    + ctx.pgp.process_packet = pgp_process_public_key;
    + ctx.subtype = NULL;
    + ctx.fingerprint = NULL;
    + ctx.payload = NULL;
    +
    + ret = pgp_parse_packets(data, datalen, &ctx.pgp);
    + if (ret < 0) {
    + if (ctx.payload)
    + ctx.subtype->destroy(ctx.payload);
    + kfree(ctx.fingerprint);
    + key_payload_reserve(key, 0);
    + return ret;
    + }
    +
    + key->type_data.p[0] = ctx.subtype;
    + key->type_data.p[1] = ctx.fingerprint;
    + key->payload.data = ctx.payload;
    + return 0;
    +}
    +
    +static struct crypto_key_parser pgp_key_parser = {
    + .owner = THIS_MODULE,
    + .name = "pgp",
    + .instantiate = pgp_key_instantiate,
    +};
    +
    +/*
    + * Module stuff
    + */
    +static int __init pgp_key_init(void)
    +{
    + return register_crypto_key_parser(&pgp_key_parser);
    +}
    +
    +static void __exit pgp_key_exit(void)
    +{
    + unregister_crypto_key_parser(&pgp_key_parser);
    +}
    +
    +module_init(pgp_key_init);
    +module_exit(pgp_key_exit);
    diff --git a/security/keys/pgp_parser.h b/security/keys/pgp_parser.h
    new file mode 100644
    index 0000000..1cda231
    --- /dev/null
    +++ b/security/keys/pgp_parser.h
    @@ -0,0 +1,23 @@
    +/* PGP crypto data parser internal definitions
    + *
    + * 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.
    + */
    +
    +#include <linux/pgp.h>
    +
    +#define kenter(FMT, ...) \
    + pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__)
    +#define kleave(FMT, ...) \
    + pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__)
    +
    +/*
    + * pgp_key_parser.c
    + */
    +extern const
    +struct public_key_algorithm *pgp_public_key_algorithms[PGP_PUBKEY__LAST];


    \
     
     \ /
      Last update: 2011-12-02 19:49    [W:4.088 / U:0.060 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site