diff options
Diffstat (limited to 'lib/openpgp')
-rw-r--r-- | lib/openpgp/Makefile.am | 50 | ||||
-rw-r--r-- | lib/openpgp/compat.c | 247 | ||||
-rw-r--r-- | lib/openpgp/extras.c | 172 | ||||
-rw-r--r-- | lib/openpgp/gnutls_openpgp.h | 99 | ||||
-rw-r--r-- | lib/openpgp/openpgp.h | 107 | ||||
-rw-r--r-- | lib/openpgp/output.c | 402 | ||||
-rw-r--r-- | lib/openpgp/pgp.c | 1079 | ||||
-rw-r--r-- | lib/openpgp/pgpverify.c | 144 | ||||
-rw-r--r-- | lib/openpgp/privkey.c | 575 |
9 files changed, 2875 insertions, 0 deletions
diff --git a/lib/openpgp/Makefile.am b/lib/openpgp/Makefile.am new file mode 100644 index 0000000000..93c479ee9f --- /dev/null +++ b/lib/openpgp/Makefile.am @@ -0,0 +1,50 @@ +## Process this file with automake to produce Makefile.in +# Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation +# +# Author: Nikos Mavrogiannopoulos +# +# This file is part of GNUTLS-EXTRA. +# +# GNUTLS-EXTRA is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# GNUTLS-EXTRA is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNUTLS-EXTRA; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + +AM_CPPFLAGS = -I$(top_srcdir)/lgl -I$(top_builddir)/lgl \ + -I$(top_srcdir)/crypto -I$(top_srcdir)/lib \ + -I$(top_srcdir)/includes -I../../includes \ + -I$(top_srcdir)/lib/opencdk + +if ENABLE_MINITASN1 +AM_CPPFLAGS += -I$(top_srcdir)/lib/minitasn1 +else +AM_CPPFLAGS += $(LIBTASN1_CFLAGS) +endif + +noinst_LTLIBRARIES = libgnutls_openpgp.la + +COBJECTS = pgp.c pgpverify.c extras.c compat.c privkey.c output.c + +libgnutls_openpgp_la_SOURCES = $(COBJECTS) openpgp.h gnutls_openpgp.h + +EXTRA_DIST = pgp-api.texi + +pgp-api.texi: $(COBJECTS) + @echo "" > pgp-api.texi + @for i in ../gnutls_openpgp.c $(COBJECTS); do \ + echo -n "Creating documentation for file $$i... " && \ + $(top_srcdir)/doc/scripts/gdoc -texinfo $$i >> pgp-api.texi && \ + echo "ok"; \ + done + +dist-hook: pgp-api.texi diff --git a/lib/openpgp/compat.c b/lib/openpgp/compat.c new file mode 100644 index 0000000000..c04d861ab7 --- /dev/null +++ b/lib/openpgp/compat.c @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Timo Schulz, Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS-EXTRA. + * + * GNUTLS-EXTRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GNUTLS-EXTRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* Compatibility functions on OpenPGP key parsing. + */ + +#include <gnutls_int.h> +#include <gnutls_errors.h> +#include <gnutls_openpgp.h> +#include <openpgp.h> + +/*- + * gnutls_openpgp_verify_key - Verify all signatures on the key + * @cert_list: the structure that holds the certificates. + * @cert_list_lenght: the items in the cert_list. + * @status: the output of the verification function + * + * Verify all signatures in the certificate list. When the key + * is not available, the signature is skipped. + * + * The return value is one of the CertificateStatus entries. + * + * NOTE: this function does not verify using any "web of trust". You + * may use GnuPG for that purpose, or any other external PGP application. + -*/ +int +_gnutls_openpgp_verify_key (const gnutls_certificate_credentials_t cred, + const gnutls_datum_t * cert_list, + int cert_list_length, unsigned int *status) +{ + int ret = 0; + gnutls_openpgp_crt_t key = NULL; + unsigned int verify = 0, verify_self = 0; + + if (!cert_list || cert_list_length != 1) + { + gnutls_assert (); + return GNUTLS_E_NO_CERTIFICATE_FOUND; + } + + ret = gnutls_openpgp_crt_init (&key); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = gnutls_openpgp_crt_import (key, &cert_list[0], GNUTLS_OPENPGP_FMT_RAW); + if (ret < 0) + { + gnutls_assert (); + goto leave; + } + +#ifndef KEYRING_HACK + if (cred->keyring != NULL) + { + ret = gnutls_openpgp_crt_verify_ring (key, cred->keyring, 0, &verify); + if (ret < 0) + { + gnutls_assert (); + goto leave; + } + } +#else + { + gnutls_openpgp_keyring_t kring; + + ret = gnutls_openpgp_keyring_init( &kring); + if ( ret < 0) { + gnutls_assert(); + return ret; + } + + ret = gnutls_openpgp_keyring_import( kring, &cred->keyring, cred->keyring_format); + if ( ret < 0) { + gnutls_assert(); + gnutls_openpgp_keyring_deinit( kring); + return ret; + } + + ret = gnutls_openpgp_crt_verify_ring (key, kring, 0, &verify); + if (ret < 0) + { + gnutls_assert (); + gnutls_openpgp_keyring_deinit( kring); + return ret; + } + gnutls_openpgp_keyring_deinit( kring); + } +#endif + + /* Now try the self signature. */ + ret = gnutls_openpgp_crt_verify_self (key, 0, &verify_self); + if (ret < 0) + { + gnutls_assert (); + goto leave; + } + + *status = verify_self | verify; + +#ifndef KEYRING_HACK + /* If we only checked the self signature. */ + if (!cred->keyring) +#else + if (!cred->keyring.data || !cred->keyring.size) +#endif + *status |= GNUTLS_CERT_SIGNER_NOT_FOUND; + + + ret = 0; + +leave: + gnutls_openpgp_crt_deinit (key); + + return ret; +} + +/*- + * gnutls_openpgp_fingerprint - Gets the fingerprint + * @cert: the raw data that contains the OpenPGP public key. + * @fpr: the buffer to save the fingerprint. + * @fprlen: the integer to save the length of the fingerprint. + * + * Returns the fingerprint of the OpenPGP key. Depence on the algorithm, + * the fingerprint can be 16 or 20 bytes. + -*/ +int +_gnutls_openpgp_fingerprint (const gnutls_datum_t * cert, + unsigned char *fpr, size_t * fprlen) +{ + gnutls_openpgp_crt_t key; + int ret; + + ret = gnutls_openpgp_crt_init (&key); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = gnutls_openpgp_crt_import (key, cert, GNUTLS_OPENPGP_FMT_RAW); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = gnutls_openpgp_crt_get_fingerprint (key, fpr, fprlen); + gnutls_openpgp_crt_deinit (key); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return 0; +} + +/*- + * gnutls_openpgp_get_raw_key_creation_time - Extract the timestamp + * @cert: the raw data that contains the OpenPGP public key. + * + * Returns the timestamp when the OpenPGP key was created. + -*/ +time_t +_gnutls_openpgp_get_raw_key_creation_time (const gnutls_datum_t * cert) +{ + gnutls_openpgp_crt_t key; + int ret; + time_t tim; + + ret = gnutls_openpgp_crt_init (&key); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = gnutls_openpgp_crt_import (key, cert, GNUTLS_OPENPGP_FMT_RAW); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + tim = gnutls_openpgp_crt_get_creation_time (key); + + gnutls_openpgp_crt_deinit (key); + + return tim; +} + + +/*- + * gnutls_openpgp_get_raw_key_expiration_time - Extract the expire date + * @cert: the raw data that contains the OpenPGP public key. + * + * Returns the time when the OpenPGP key expires. A value of '0' means + * that the key doesn't expire at all. + -*/ +time_t +_gnutls_openpgp_get_raw_key_expiration_time (const gnutls_datum_t * cert) +{ + gnutls_openpgp_crt_t key; + int ret; + time_t tim; + + ret = gnutls_openpgp_crt_init (&key); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = gnutls_openpgp_crt_import (key, cert, GNUTLS_OPENPGP_FMT_RAW); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + tim = gnutls_openpgp_crt_get_expiration_time (key); + + gnutls_openpgp_crt_deinit (key); + + return tim; +} diff --git a/lib/openpgp/extras.c b/lib/openpgp/extras.c new file mode 100644 index 0000000000..2ce4ce256d --- /dev/null +++ b/lib/openpgp/extras.c @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos, Timo Schulz + * + * This file is part of GNUTLS-EXTRA. + * + * GNUTLS-EXTRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GNUTLS-EXTRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* Functions on OpenPGP keyring parsing + */ + +#include <gnutls_int.h> +#include <gnutls_datum.h> +#include <gnutls_global.h> +#include <gnutls_errors.h> +#include <gnutls_openpgp.h> +#include <gnutls_num.h> +#include <openpgp.h> + +/* Keyring stuff. + */ + +/** + * gnutls_openpgp_keyring_init - This function initializes a gnutls_openpgp_keyring_t structure + * @keyring: The structure to be initialized + * + * This function will initialize an OpenPGP keyring structure. + * + * Returns 0 on success. + * + **/ +int +gnutls_openpgp_keyring_init (gnutls_openpgp_keyring_t * keyring) +{ + *keyring = gnutls_calloc (1, sizeof (gnutls_openpgp_keyring_int)); + + if (*keyring) + return 0; /* success */ + return GNUTLS_E_MEMORY_ERROR; +} + + +/** + * gnutls_openpgp_keyring_deinit - This function deinitializes memory used by a gnutls_openpgp_keyring_t structure + * @keyring: The structure to be initialized + * + * This function will deinitialize a keyring structure. + * + **/ +void +gnutls_openpgp_keyring_deinit (gnutls_openpgp_keyring_t keyring) +{ + if (!keyring) + return; + + if (keyring->db) + { + cdk_keydb_free (keyring->db); + keyring->db = NULL; + } + + /* In some cases the stream is also stored outside the keydb context + and we need to close it here then. */ + if (keyring->db_stream) + { + cdk_stream_close (keyring->db_stream); + keyring->db_stream = NULL; + } + + gnutls_free (keyring); +} + +/** + * gnutls_openpgp_keyring_check_id - Check if a key id exists in the keyring + * @ring: holds the keyring to check against + * @keyid: will hold the keyid to check for. + * @flags: unused (should be 0) + * + * Check if a given key ID exists in the keyring. + * + * Returns 0 on success (if keyid exists) and a negative error code + * on failure. + **/ +int +gnutls_openpgp_keyring_check_id (gnutls_openpgp_keyring_t ring, + gnutls_openpgp_keyid_t keyid, + unsigned int flags) +{ + cdk_pkt_pubkey_t pk; + uint32_t id[2]; + + id[0] = _gnutls_read_uint32 (keyid.keyid); + id[1] = _gnutls_read_uint32 (&keyid.keyid[4]); + + if (!cdk_keydb_get_pk (ring->db, id, &pk)) + { + cdk_pk_release (pk); + return 0; + } + + _gnutls_debug_log ("PGP: key not found %08lX\n", (unsigned long)id[1]); + return GNUTLS_E_NO_CERTIFICATE_FOUND; +} + +/** + * gnutls_openpgp_keyring_import - Import a raw- or Base64-encoded OpenPGP keyring + * @keyring: The structure to store the parsed key. + * @data: The RAW or BASE64 encoded keyring. + * @format: One of #gnutls_openpgp_keyring_fmt elements. + * + * This function will convert the given RAW or Base64 encoded keyring to the + * native #gnutls_openpgp_keyring_t format. The output will be stored in + * 'keyring'. + * + * Returns 0 on success. + * + **/ +int +gnutls_openpgp_keyring_import (gnutls_openpgp_keyring_t keyring, + const gnutls_datum_t *data, + gnutls_openpgp_crt_fmt_t format) +{ + cdk_error_t err; + cdk_stream_t input; + + _gnutls_debug_log ("PGP: keyring import format '%s'\n", + format == GNUTLS_OPENPGP_FMT_RAW? "raw" : "base64"); + + if (format == GNUTLS_OPENPGP_FMT_RAW) + { + err = cdk_keydb_new (&keyring->db, CDK_DBTYPE_DATA, + data->data, data->size); + if (err) + gnutls_assert (); + return _gnutls_map_cdk_rc (err); + } + + /* Create a new stream from the given data, which means to + allocate a new stream and to write the data in the stream. + Then push the armor filter to decode the data and to store + it in the raw format. */ + err = cdk_stream_tmp_from_mem (data->data, data->size, &input); + if (!err) + err = cdk_stream_set_armor_flag (input, 0); + if (!err) + err = cdk_keydb_new_from_stream (&keyring->db, 0, input); + if (err) + { + cdk_stream_close (input); + gnutls_assert (); + } + else + /* The keydb function will not close the stream itself, so we need to + store it separately to close it later. */ + keyring->db_stream = input; + + return _gnutls_map_cdk_rc (err); +} + diff --git a/lib/openpgp/gnutls_openpgp.h b/lib/openpgp/gnutls_openpgp.h new file mode 100644 index 0000000000..067de99831 --- /dev/null +++ b/lib/openpgp/gnutls_openpgp.h @@ -0,0 +1,99 @@ +#include <config.h> + +#ifdef ENABLE_OPENPGP + +#ifndef GNUTLS_OPENPGP_LOCAL_H +#define GNUTLS_OPENPGP_LOCAL_H + +#include <auth_cert.h> +#include <opencdk.h> + +typedef struct +{ + int type; + size_t size; + uint8_t *data; +} keybox_blob; + +typedef enum +{ + KBX_BLOB_FILE = 0x00, + KBX_BLOB_DATA = 0x01 +} keyring_blob_types; + +/* OpenCDK compatible */ +typedef enum +{ + KEY_ATTR_NONE = 0, + KEY_ATTR_SHORT_KEYID = 3, + KEY_ATTR_KEYID = 4, + KEY_ATTR_FPR = 5 +} key_attr_t; + +int +gnutls_certificate_set_openpgp_key_file (gnutls_certificate_credentials_t + res, const char *CERTFILE, + const char *KEYFILE, gnutls_openpgp_crt_fmt_t); + +int gnutls_openpgp_count_key_names (const gnutls_datum_t * cert); + +int gnutls_certificate_set_openpgp_keyring_file + (gnutls_certificate_credentials_t c, const char *file, gnutls_openpgp_crt_fmt_t); + +int +gnutls_certificate_set_openpgp_keyring_mem (gnutls_certificate_credentials_t + c, const opaque * data, + size_t dlen, gnutls_openpgp_crt_fmt_t); + +int gnutls_openpgp_get_key (gnutls_datum_t * key, + gnutls_openpgp_keyring_t keyring, + key_attr_t by, opaque * pattern); + +int gnutls_openpgp_recv_key (const char *host, + short port, uint32_t keyid, + gnutls_datum_t * key); + +/* internal */ +int _gnutls_openpgp_raw_crt_to_gcert (gnutls_cert * cert, + const gnutls_datum_t * raw); + +int +_gnutls_openpgp_raw_privkey_to_gkey (gnutls_privkey * pkey, + const gnutls_datum_t * raw_key); + +int +_gnutls_openpgp_request_key (gnutls_session_t, + gnutls_datum_t * ret, + const gnutls_certificate_credentials_t cred, + opaque * key_fpr, int key_fpr_size); + +int _gnutls_openpgp_verify_key (const gnutls_certificate_credentials_t, + const gnutls_datum_t * cert_list, + int cert_list_length, unsigned int *status); +int _gnutls_openpgp_fingerprint (const gnutls_datum_t * cert, + unsigned char *fpr, size_t * fprlen); +time_t _gnutls_openpgp_get_raw_key_creation_time (const gnutls_datum_t * + cert); +time_t _gnutls_openpgp_get_raw_key_expiration_time (const gnutls_datum_t * + cert); + +int +gnutls_openpgp_privkey_init (gnutls_openpgp_privkey_t * key); + +int +gnutls_openpgp_privkey_init (gnutls_openpgp_privkey_t * key); + +void +gnutls_openpgp_privkey_deinit (gnutls_openpgp_privkey_t key); + +int +gnutls_openpgp_privkey_import (gnutls_openpgp_privkey_t key, + const gnutls_datum_t * data, + gnutls_openpgp_crt_fmt_t format, + const char *pass, unsigned int flags); + +int _gnutls_openpgp_find_valid_subkey( gnutls_openpgp_crt_t crt, gnutls_openpgp_keyid_t* keyid); + +#endif /*GNUTLS_OPENPGP_LOCAL_H */ + +#endif /*ENABLE_OPENPGP */ diff --git a/lib/openpgp/openpgp.h b/lib/openpgp/openpgp.h new file mode 100644 index 0000000000..3ec9ba9111 --- /dev/null +++ b/lib/openpgp/openpgp.h @@ -0,0 +1,107 @@ +#ifndef OPENPGP_LOCAL_H +# define OPENPGP_LOCAL_H + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#ifdef ENABLE_OPENPGP + +#include <opencdk.h> +#include <gnutls/openpgp.h> + +#define KEYID_IMPORT(dst, src) \ + dst[0] = _gnutls_read_uint32( src.keyid); \ + dst[1] = _gnutls_read_uint32( src.keyid+4) + +/* Internal context to store the OpenPGP key. */ +typedef struct gnutls_openpgp_crt_int +{ + cdk_kbnode_t knode; +} gnutls_openpgp_crt_int; + +/* Internal context to store the private OpenPGP key. */ +typedef struct gnutls_openpgp_privkey_int +{ + cdk_kbnode_t knode; +} gnutls_openpgp_privkey_int; + + +typedef struct gnutls_openpgp_keyring_int +{ + cdk_keydb_hd_t db; + cdk_stream_t db_stream; +} gnutls_openpgp_keyring_int; + +int _gnutls_map_cdk_rc (int rc); +int gnutls_openpgp_crt_get_name (gnutls_openpgp_crt_t key, + int idx, char *buf, size_t * sizeof_buf); +int gnutls_openpgp_crt_get_fingerprint (gnutls_openpgp_crt_t key, + void *fpr, size_t * fprlen); +gnutls_pk_algorithm_t +gnutls_openpgp_crt_get_pk_algorithm (gnutls_openpgp_crt_t key, + unsigned int *bits); +int gnutls_openpgp_crt_get_version (gnutls_openpgp_crt_t key); +time_t gnutls_openpgp_crt_get_creation_time (gnutls_openpgp_crt_t key); +time_t gnutls_openpgp_crt_get_expiration_time (gnutls_openpgp_crt_t key); +int gnutls_openpgp_crt_get_id (gnutls_openpgp_crt_t key, + gnutls_openpgp_keyid_t* keyid); + +int gnutls_openpgp_crt_init (gnutls_openpgp_crt_t * key); +void gnutls_openpgp_crt_deinit (gnutls_openpgp_crt_t key); +int gnutls_openpgp_crt_import (gnutls_openpgp_crt_t key, + const gnutls_datum_t * data, + gnutls_openpgp_crt_fmt_t format); +int gnutls_openpgp_crt_export (gnutls_openpgp_crt_t key, + gnutls_openpgp_crt_fmt_t format, + void *output_data, size_t * output_data_size); + +void gnutls_openpgp_keyring_deinit (gnutls_openpgp_keyring_t keyring); +int gnutls_openpgp_keyring_init (gnutls_openpgp_keyring_t * keyring); +int gnutls_openpgp_keyring_import (gnutls_openpgp_keyring_t keyring, + const gnutls_datum_t * data, + gnutls_openpgp_crt_fmt_t format); +int gnutls_openpgp_keyring_check_id (gnutls_openpgp_keyring_t ring, + gnutls_openpgp_keyid_t keyid, + unsigned int flags); + +int gnutls_openpgp_crt_verify_ring (gnutls_openpgp_crt_t key, + gnutls_openpgp_keyring_t keyring, + unsigned int flags, unsigned int *verify); + +int gnutls_openpgp_crt_verify_self (gnutls_openpgp_crt_t key, + unsigned int flags, unsigned int *verify); + +int _gnutls_openpgp_crt_to_gcert (gnutls_cert * gcert, + gnutls_openpgp_crt_t cert, gnutls_openpgp_keyid_t keyid); +int _gnutls_openpgp_privkey_to_gkey (gnutls_privkey * dest, + gnutls_openpgp_privkey_t src, gnutls_openpgp_keyid_t); + +void gnutls_openpgp_privkey_deinit (gnutls_openpgp_privkey_t key); + +cdk_packet_t _gnutls_get_valid_subkey(cdk_kbnode_t knode, int key_type); + +unsigned int _gnutls_get_pgp_key_usage(unsigned int pgp_usage); + +int +_gnutls_openpgp_crt_get_mpis (gnutls_openpgp_crt_t cert, uint32_t keyid[2], + mpi_t * params, int *params_size); + +int +_gnutls_openpgp_privkey_get_mpis (gnutls_openpgp_privkey_t pkey, uint32_t keyid[2], + mpi_t * params, int *params_size); + +cdk_packet_t _gnutls_openpgp_find_key( cdk_kbnode_t knode, uint32_t keyid[2], unsigned int priv); + +int _gnutls_read_pgp_mpi( cdk_packet_t pkt, unsigned int priv, size_t idx, mpi_t* m); + +int _gnutls_openpgp_find_subkey_idx( cdk_kbnode_t knode, uint32_t keyid[2], + unsigned int priv); + +#else /* no opencdk */ + +typedef void *gnutls_openpgp_keyring_t; + +#endif /* ENABLE_OPENPGP */ + +#endif /* OPENPGP_LOCAL_H */ diff --git a/lib/openpgp/output.c b/lib/openpgp/output.c new file mode 100644 index 0000000000..71b997133f --- /dev/null +++ b/lib/openpgp/output.c @@ -0,0 +1,402 @@ +/* + * Copyright (C) 2007 Free Software Foundation + * + * Author: Simon Josefsson, Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +/* Functions for printing X.509 Certificate structures + */ + +#include <gnutls_int.h> +#include <gnutls/openpgp.h> +#include <gnutls_errors.h> + +/* I18n of error codes. */ +#include "gettext.h" +#define _(String) dgettext (PACKAGE, String) +#define N_(String) gettext_noop (String) + +#define addf _gnutls_string_append_printf +#define adds _gnutls_string_append_str + +static void +hexdump (gnutls_string * str, const char *data, size_t len, const char *spc) +{ + size_t j; + + if (spc) + adds (str, spc); + for (j = 0; j < len; j++) + { + if (((j + 1) % 16) == 0) + { + addf (str, "%.2x\n", (unsigned char) data[j]); + if (spc && j != (len - 1)) + adds (str, spc); + } + else if (j == (len - 1)) + addf (str, "%.2x", (unsigned char) data[j]); + else + addf (str, "%.2x:", (unsigned char) data[j]); + } + if ((j % 16) != 0) + adds (str, "\n"); +} + +static void +hexprint (gnutls_string * str, const char *data, size_t len) +{ + size_t j; + + if (len == 0) + adds (str, "00"); + else + { + for (j = 0; j < len; j++) + addf (str, "%.2x", (unsigned char) data[j]); + } +} + + +static void +asciiprint (gnutls_string * str, const char *data, size_t len) +{ + size_t j; + + for (j = 0; j < len; j++) + if (isprint (data[j])) + addf (str, "%c", (unsigned char) data[j]); + else + addf (str, "."); +} + +static void +print_key_usage (gnutls_string * str, gnutls_openpgp_crt_t cert, unsigned int idx) +{ + unsigned int key_usage; + int err; + + addf (str, _("\t\tKey Usage:\n")); + + + if (idx == -1) + err = gnutls_openpgp_crt_get_key_usage (cert, &key_usage); + else + err = gnutls_openpgp_crt_get_subkey_usage (cert, idx, &key_usage); + if (err < 0) + { + addf (str, _("error: get_key_usage: %s\n"), gnutls_strerror (err)); + return; + } + + if (key_usage & GNUTLS_KEY_DIGITAL_SIGNATURE) + addf (str, _("\t\t\tDigital signature.\n")); + if (key_usage & GNUTLS_KEY_NON_REPUDIATION) + addf (str, _("\t\t\tNon repudiation.\n")); + if (key_usage & GNUTLS_KEY_KEY_ENCIPHERMENT) + addf (str, _("\t\t\tKey encipherment.\n")); + if (key_usage & GNUTLS_KEY_DATA_ENCIPHERMENT) + addf (str, _("\t\t\tData encipherment.\n")); + if (key_usage & GNUTLS_KEY_KEY_AGREEMENT) + addf (str, _("\t\t\tKey agreement.\n")); + if (key_usage & GNUTLS_KEY_KEY_CERT_SIGN) + addf (str, _("\t\t\tCertificate signing.\n")); + if (key_usage & GNUTLS_KEY_CRL_SIGN) + addf (str, _("\t\t\tCRL signing.\n")); + if (key_usage & GNUTLS_KEY_ENCIPHER_ONLY) + addf (str, _("\t\t\tKey encipher only.\n")); + if (key_usage & GNUTLS_KEY_DECIPHER_ONLY) + addf (str, _("\t\t\tKey decipher only.\n")); +} + +/* idx == -1 indicates main key + * otherwise the subkey. + */ +static void +print_key_id (gnutls_string * str, gnutls_openpgp_crt_t cert, int idx) +{ + gnutls_openpgp_keyid_t id; + int err; + + if (idx < 0) + err = gnutls_openpgp_crt_get_id (cert, &id); + else + err = gnutls_openpgp_crt_get_subkey_id( cert, idx, &id); + + if (err < 0) + addf (str, "error: get_id: %s\n", gnutls_strerror (err)); + else + { + addf (str, _("\tID (hex): ")); + hexprint (str, id.keyid, sizeof(id.keyid)); + addf (str, "\n"); + } +} + +/* idx == -1 indicates main key + * otherwise the subkey. + */ +static void +print_key_fingerprint (gnutls_string * str, gnutls_openpgp_crt_t cert) +{ + char fpr[128]; + size_t fpr_size = sizeof (fpr); + int err; + + err = gnutls_openpgp_crt_get_fingerprint (cert, fpr, &fpr_size); + if (err < 0) + addf (str, "error: get_fingerprint: %s\n", gnutls_strerror (err)); + else + { + addf (str, _("\tFingerprint (hex): ")); + hexprint (str, fpr, fpr_size); + addf (str, "\n"); + } +} + +static void +print_key_revoked (gnutls_string * str, gnutls_openpgp_crt_t cert, int idx) +{ + char fpr[128]; + size_t fpr_size = sizeof (fpr); + int err; + + if (idx < 0) + err = gnutls_openpgp_crt_get_revoked_status (cert); + else + err = gnutls_openpgp_crt_get_subkey_revoked_status( cert, idx); + + if (err != 0) + addf (str, "Revoked: True"); +} + +static void +print_key_times(gnutls_string * str, gnutls_openpgp_crt_t cert, int idx) +{ + time_t tim; + + addf (str, _("\tTime stamps:\n")); + + if (idx == -1) + tim = gnutls_openpgp_crt_get_creation_time (cert); + else + tim = gnutls_openpgp_crt_get_subkey_creation_time (cert, idx); + + { + char s[42]; + size_t max = sizeof (s); + struct tm t; + + if (gmtime_r (&tim, &t) == NULL) + addf (str, "error: gmtime_r (%d)\n", t); + else if (strftime (s, max, "%a %b %e %H:%M:%S UTC %Y", &t) == 0) + addf (str, "error: strftime (%d)\n", t); + else + addf (str, _("\t\tCreation: %s\n"), s); + } + + if (idx == -1) + tim = gnutls_openpgp_crt_get_expiration_time (cert); + else + tim = gnutls_openpgp_crt_get_subkey_expiration_time (cert, idx); + { + char s[42]; + size_t max = sizeof (s); + struct tm t; + + if (gmtime_r (&tim, &t) == NULL) + addf (str, "error: gmtime_r (%d)\n", t); + else if (strftime (s, max, "%a %b %e %H:%M:%S UTC %Y", &t) == 0) + addf (str, "error: strftime (%d)\n", t); + else + addf (str, _("\t\tExpiration: %s\n"), s); + } +} + +static void +print_key_info(gnutls_string * str, gnutls_openpgp_crt_t cert, int idx) +{ + int err; + unsigned int bits; + + if (idx == -1) + err = gnutls_openpgp_crt_get_pk_algorithm (cert, &bits); + else + err = gnutls_openpgp_crt_get_subkey_pk_algorithm (cert, idx, &bits); + + if (err < 0) + addf (str, "error: get_pk_algorithm: %s\n", gnutls_strerror (err)); + else + { + const char *name = gnutls_pk_algorithm_get_name (err); + if (name == NULL) + name = "Unknown"; + + addf (str, _("\tPublic Key Algorithm: %s\n"), name); +#if 0 + switch (err) + { + case GNUTLS_PK_RSA: + { + gnutls_datum_t m, e; + + err = gnutls_openpgp_crt_get_pk_rsa_raw (cert, &m, &e); + if (err < 0) + addf (str, "error: get_pk_rsa_raw: %s\n", + gnutls_strerror (err)); + else + { + addf (str, _("\t\tModulus (bits %d):\n"), bits); + hexdump (str, m.data, m.size, "\t\t\t"); + addf (str, _("\t\tExponent:\n")); + hexdump (str, e.data, e.size, "\t\t\t"); + } + + gnutls_free (m.data); + gnutls_free (e.data); + } + break; + + case GNUTLS_PK_DSA: + { + gnutls_datum_t p, q, g, y; + + err = gnutls_openpgp_crt_get_pk_dsa_raw (cert, &p, &q, &g, &y); + if (err < 0) + addf (str, "error: get_pk_dsa_raw: %s\n", + gnutls_strerror (err)); + else + { + addf (str, _("\t\tPublic key (bits %d):\n"), bits); + hexdump (str, y.data, y.size, "\t\t\t"); + addf (str, _("\t\tP:\n")); + hexdump (str, p.data, p.size, "\t\t\t"); + addf (str, _("\t\tQ:\n")); + hexdump (str, q.data, q.size, "\t\t\t"); + addf (str, _("\t\tG:\n")); + hexdump (str, g.data, g.size, "\t\t\t"); + } + } + break; + + default: + break; + } +#endif + } +} + + +static void +print_cert (gnutls_string * str, gnutls_openpgp_crt_t cert, unsigned int format) +{ +int i, subkeys; +int err; +char dn[1024]; +size_t dn_size = sizeof (dn); + + /* Version. */ + { + int version = gnutls_openpgp_crt_get_version (cert); + if (version < 0) + addf (str, "error: get_version: %s\n", gnutls_strerror (version)); + else + addf (str, _("\tVersion: %d\n"), version); + } + + /* ID. */ + print_key_id( str, cert, -1); + + print_key_fingerprint( str, cert); + + /* Names. */ + i = 0; + do { + + err = gnutls_openpgp_crt_get_name (cert, i++, dn, &dn_size); + + if (err < 0 && err != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + addf (str, "error: get_name: %s\n", gnutls_strerror (err)); + break; + } + + if (err > 0) + addf (str, _("\tName[%d]: %s\n"), i, dn); + + } while( err > 0); + + print_key_times( str, cert, -1); + + print_key_info( str, cert, -1); + print_key_usage( str, cert, -1); + print_key_revoked( str, cert, -1); + + subkeys = gnutls_openpgp_crt_get_subkey_count( cert); + if (subkeys < 0) + return; + + for (i=0;i<subkeys;i++) { + addf( str, _("\n\tSubkey[%d]:\n"), i); + + print_key_id( str, cert, i); + print_key_times( str, cert, i); + print_key_info( str, cert, i); + print_key_usage( str, cert, i); + print_key_revoked( str, cert, i); + } + +} + +/** + * gnutls_openpgp_crt_print - Pretty print OpenPGP certificates + * @cert: The structure to be printed + * @format: Indicate the format to use + * @out: Newly allocated datum with zero terminated string. + * + * This function will pretty print an OpenPGP certificate, suitable for + * display to a human. + * + * The format should be zero for future compatibility. + * + * The output @out needs to be deallocate using gnutls_free(). + * + * Returns 0 on success. + **/ +int +gnutls_openpgp_crt_print (gnutls_openpgp_crt_t cert, + gnutls_certificate_print_formats_t format, + gnutls_datum_t *out) +{ + gnutls_string str; + + _gnutls_string_init (&str, gnutls_malloc, gnutls_realloc, gnutls_free); + + _gnutls_string_append_str (&str, _("OpenPGP Certificate Information:\n")); + + print_cert (&str, cert, format); + + _gnutls_string_append_data (&str, "\0", 1); + out->data = str.data; + out->size = strlen (str.data); + + return 0; +} + diff --git a/lib/openpgp/pgp.c b/lib/openpgp/pgp.c new file mode 100644 index 0000000000..e0f0472bf6 --- /dev/null +++ b/lib/openpgp/pgp.c @@ -0,0 +1,1079 @@ +/* + * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation + * + * Author: Timo Schulz, Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS-EXTRA. + * + * GNUTLS-EXTRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GNUTLS-EXTRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* Functions on OpenPGP key parsing + */ + +#include <gnutls_int.h> +#include <gnutls_datum.h> +#include <gnutls_global.h> +#include <gnutls_errors.h> +#include <openpgp.h> +#include <x509/rfc2818.h> +#include <gnutls_num.h> + +/** + * gnutls_openpgp_crt_init - This function initializes a gnutls_openpgp_crt_t structure + * @key: The structure to be initialized + * + * This function will initialize an OpenPGP key structure. + * + * Returns 0 on success. + * + **/ +int +gnutls_openpgp_crt_init (gnutls_openpgp_crt_t * key) +{ + *key = gnutls_calloc (1, sizeof (gnutls_openpgp_crt_int)); + + if (*key) + return 0; /* success */ + return GNUTLS_E_MEMORY_ERROR; +} + +/** + * gnutls_openpgp_crt_deinit - This function deinitializes memory used by a gnutls_openpgp_crt_t structure + * @key: The structure to be initialized + * + * This function will deinitialize a key structure. + **/ +void +gnutls_openpgp_crt_deinit (gnutls_openpgp_crt_t key) +{ + if (!key) + return; + + if (key->knode) + { + cdk_kbnode_release (key->knode); + key->knode = NULL; + } + + gnutls_free (key); +} + +/** + * gnutls_openpgp_crt_import - This function will import a RAW or BASE64 encoded key + * @key: The structure to store the parsed key. + * @data: The RAW or BASE64 encoded key. + * @format: One of gnutls_openpgp_crt_fmt_t elements. + * + * This function will convert the given RAW or Base64 encoded key + * to the native gnutls_openpgp_crt_t format. The output will be stored in 'key'. + * + * Returns 0 on success. + **/ +int +gnutls_openpgp_crt_import (gnutls_openpgp_crt_t key, + const gnutls_datum_t * data, + gnutls_openpgp_crt_fmt_t format) +{ + cdk_stream_t inp; + int rc; + + if (format == GNUTLS_OPENPGP_FMT_RAW) + rc = cdk_kbnode_read_from_mem (&key->knode, data->data, data->size); + else + { + rc = cdk_stream_tmp_from_mem (data->data, data->size, &inp); + if (rc) + { + rc = _gnutls_map_cdk_rc (rc); + gnutls_assert (); + return rc; + } + if (cdk_armor_filter_use (inp)) + rc = cdk_stream_set_armor_flag (inp, 0); + if (!rc) + rc = cdk_keydb_get_keyblock (inp, &key->knode); + cdk_stream_close (inp); + if (rc) + { + rc = _gnutls_map_cdk_rc (rc); + gnutls_assert (); + return rc; + } + } + + return 0; +} + +/** + * gnutls_openpgp_crt_export - This function will export a RAW or BASE64 encoded key + * @key: Holds the key. + * @format: One of gnutls_openpgp_crt_fmt_t elements. + * @output_data: will contain the key base64 encoded or raw + * @output_data_size: holds the size of output_data (and will be replaced by the actual size of parameters) + * + * This function will convert the given key to RAW or Base64 format. + * If the buffer provided is not long enough to hold the output, then + * GNUTLS_E_SHORT_MEMORY_BUFFER will be returned. + * + * Returns 0 on success. + * + **/ +int +gnutls_openpgp_crt_export (gnutls_openpgp_crt_t key, + gnutls_openpgp_crt_fmt_t format, + void *output_data, size_t * output_data_size) +{ + size_t input_data_size = *output_data_size; + size_t calc_size; + int rc; + + rc = cdk_kbnode_write_to_mem (key->knode, output_data, output_data_size); + if (rc) + { + rc = _gnutls_map_cdk_rc (rc); + gnutls_assert (); + return rc; + } + + /* If the caller uses output_data == NULL then return what he expects. + */ + if (!output_data) + { + gnutls_assert(); + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + if (format == GNUTLS_OPENPGP_FMT_BASE64) + { + unsigned char *in = cdk_calloc (1, *output_data_size); + memcpy (in, output_data, *output_data_size); + + /* Calculate the size of the encoded data and check if the provided + buffer is large enough. */ + rc = cdk_armor_encode_buffer (in, *output_data_size, + NULL, 0, &calc_size, CDK_ARMOR_PUBKEY); + if (rc || calc_size > input_data_size) + { + cdk_free (in); + *output_data_size = calc_size; + gnutls_assert (); + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + rc = cdk_armor_encode_buffer (in, *output_data_size, + output_data, input_data_size, &calc_size, + CDK_ARMOR_PUBKEY); + cdk_free (in); + *output_data_size = calc_size; + } + + return 0; +} + + +/** + * gnutls_openpgp_crt_get_fingerprint - Gets the fingerprint + * @key: the raw data that contains the OpenPGP public key. + * @fpr: the buffer to save the fingerprint, must hold at least 20 bytes. + * @fprlen: the integer to save the length of the fingerprint. + * + * Returns the fingerprint of the OpenPGP key. Depends on the algorithm, + * the fingerprint can be 16 or 20 bytes. + **/ +int +gnutls_openpgp_crt_get_fingerprint (gnutls_openpgp_crt_t key, + void *fpr, size_t * fprlen) +{ + cdk_packet_t pkt; + cdk_pkt_pubkey_t pk = NULL; + + if (!fpr || !fprlen) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + *fprlen = 0; + + pkt = cdk_kbnode_find_packet (key->knode, CDK_PKT_PUBLIC_KEY); + if (!pkt) + return GNUTLS_E_OPENPGP_GETKEY_FAILED; + + pk = pkt->pkt.public_key; + *fprlen = 20; + + /* FIXME: Check if the draft allows old PGP keys. */ + if (is_RSA (pk->pubkey_algo) && pk->version < 4) + *fprlen = 16; + cdk_pk_get_fingerprint (pk, fpr); + + return 0; +} + +int +_gnutls_openpgp_count_key_names (gnutls_openpgp_crt_t key) +{ + cdk_kbnode_t p, ctx; + cdk_packet_t pkt; + int nuids; + + if (key == NULL) + { + gnutls_assert (); + return 0; + } + + ctx = NULL; + nuids = 0; + while ((p = cdk_kbnode_walk (key->knode, &ctx, 0))) + { + pkt = cdk_kbnode_get_packet (p); + if (pkt->pkttype == CDK_PKT_USER_ID) + nuids++; + } + + return nuids; +} + + +/** + * gnutls_openpgp_crt_get_name - Extracts the userID + * @key: the structure that contains the OpenPGP public key. + * @idx: the index of the ID to extract + * @buf: a pointer to a structure to hold the name + * @sizeof_buf: holds the maximum size of @buf, on return hold the + * actual/required size of @buf. + * + * Extracts the userID from the parsed OpenPGP key. + * + * Returns 0 on success, and GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * if the index of the ID does not exist. + * + **/ +int +gnutls_openpgp_crt_get_name (gnutls_openpgp_crt_t key, + int idx, char *buf, size_t * sizeof_buf) +{ + cdk_kbnode_t ctx = NULL, p; + cdk_packet_t pkt = NULL; + cdk_pkt_userid_t uid = NULL; + int pos = 0; + + if (!key || !buf) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (idx < 0 || idx > _gnutls_openpgp_count_key_names (key)) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + + if (!idx) + pkt = cdk_kbnode_find_packet (key->knode, CDK_PKT_USER_ID); + else + { + pos = 0; + while ((p = cdk_kbnode_walk (key->knode, &ctx, 0))) + { + pkt = cdk_kbnode_get_packet (p); + if (pkt->pkttype == CDK_PKT_USER_ID && ++pos == idx) + break; + } + } + + if (!pkt) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + uid = pkt->pkt.user_id; + if (uid->len >= *sizeof_buf) + { + gnutls_assert (); + *sizeof_buf = uid->len + 1; + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + memcpy (buf, uid->name, uid->len); + buf[uid->len] = '\0'; /* make sure it's a string */ + *sizeof_buf = uid->len + 1; + + if (uid->is_revoked) + return GNUTLS_E_OPENPGP_UID_REVOKED; + + return 0; +} + +/** + * gnutls_openpgp_crt_get_pk_algorithm - This function returns the key's PublicKey algorithm + * @key: is an OpenPGP key + * @bits: if bits is non null it will hold the size of the parameters' in bits + * + * This function will return the public key algorithm of an OpenPGP + * certificate. + * + * If bits is non null, it should have enough size to hold the parameters + * size in bits. For RSA the bits returned is the modulus. + * For DSA the bits returned are of the public exponent. + * + * Returns a member of the GNUTLS_PKAlgorithm enumeration on success, + * or a negative value on error. + * + **/ +gnutls_pk_algorithm_t +gnutls_openpgp_crt_get_pk_algorithm (gnutls_openpgp_crt_t key, + unsigned int *bits) +{ + cdk_packet_t pkt; + int algo; + + if (!key) + return GNUTLS_PK_UNKNOWN; + + algo = 0; + pkt = cdk_kbnode_find_packet (key->knode, CDK_PKT_PUBLIC_KEY); + if (pkt) + { + if (bits) + *bits = cdk_pk_get_nbits (pkt->pkt.public_key); + algo = pkt->pkt.public_key->pubkey_algo; + if (is_RSA (algo)) + algo = GNUTLS_PK_RSA; + else if (is_DSA (algo)) + algo = GNUTLS_PK_DSA; + else + algo = GNUTLS_E_UNKNOWN_PK_ALGORITHM; + } + + return algo; +} + + +/** + * gnutls_openpgp_crt_get_version - Extracts the version of the key. + * @key: the structure that contains the OpenPGP public key. + * + * Extract the version of the OpenPGP key. + **/ +int +gnutls_openpgp_crt_get_version (gnutls_openpgp_crt_t key) +{ + cdk_packet_t pkt; + int version; + + if (!key) + return -1; + + pkt = cdk_kbnode_find_packet (key->knode, CDK_PKT_PUBLIC_KEY); + if (pkt) + version = pkt->pkt.public_key->version; + else + version = 0; + + return version; +} + + +/** + * gnutls_openpgp_crt_get_creation_time - Extract the timestamp + * @key: the structure that contains the OpenPGP public key. + * + * Returns the timestamp when the OpenPGP key was created. + **/ +time_t +gnutls_openpgp_crt_get_creation_time (gnutls_openpgp_crt_t key) +{ + cdk_packet_t pkt; + time_t timestamp; + + if (!key) + return (time_t) - 1; + + pkt = cdk_kbnode_find_packet (key->knode, CDK_PKT_PUBLIC_KEY); + if (pkt) + timestamp = pkt->pkt.public_key->timestamp; + else + timestamp = 0; + + return timestamp; +} + + +/** + * gnutls_openpgp_crt_get_expiration_time - Extract the expire date + * @key: the structure that contains the OpenPGP public key. + * + * Returns the time when the OpenPGP key expires. A value of '0' means + * that the key doesn't expire at all. + **/ +time_t +gnutls_openpgp_crt_get_expiration_time (gnutls_openpgp_crt_t key) +{ + cdk_packet_t pkt; + time_t expiredate; + + if (!key) + return (time_t) - 1; + + pkt = cdk_kbnode_find_packet (key->knode, CDK_PKT_PUBLIC_KEY); + if (pkt) + expiredate = pkt->pkt.public_key->expiredate; + else + expiredate = 0; + + return expiredate; +} + +/** + * gnutls_openpgp_crt_get_id - Gets the keyID + * @key: the structure that contains the OpenPGP public key. + * @keyid: the buffer to save the keyid. + * + * Returns the 64-bit keyID of the OpenPGP key. + **/ +int +gnutls_openpgp_crt_get_id (gnutls_openpgp_crt_t key, gnutls_openpgp_keyid_t* keyid) +{ + cdk_packet_t pkt; + uint32_t kid[2]; + + if (!key || !keyid) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + pkt = cdk_kbnode_find_packet (key->knode, CDK_PKT_PUBLIC_KEY); + if (!pkt) + return GNUTLS_E_OPENPGP_GETKEY_FAILED; + + cdk_pk_get_keyid (pkt->pkt.public_key, kid); + _gnutls_write_uint32( kid[0], keyid->keyid); + _gnutls_write_uint32( kid[1], keyid->keyid+4); + + return 0; +} + +/** + * gnutls_openpgp_crt_get_revoked_status - Gets the revoked status of the key + * @key: the structure that contains the OpenPGP public key. + * + * Returns the true (1) or false (0) based on whether this key has been revoked + * or not. + * + **/ +int +gnutls_openpgp_crt_get_revoked_status (gnutls_openpgp_crt_t key) +{ + cdk_packet_t pkt; + + if (!key) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + pkt = cdk_kbnode_find_packet (key->knode, CDK_PKT_PUBLIC_KEY); + if (!pkt) + return GNUTLS_E_OPENPGP_GETKEY_FAILED; + + if (pkt->pkt.public_key->is_revoked != 0) return 1; + return 0; +} + +/** + * gnutls_openpgp_crt_check_hostname - This function compares the given hostname with the hostname in the key + * @key: should contain an gnutls_openpgp_crt_t structure + * @hostname: A null terminated string that contains a DNS name + * + * This function will check if the given key's owner matches + * the given hostname. This is a basic implementation of the matching + * described in RFC2818 (HTTPS), which takes into account wildcards. + * + * Returns non zero on success, and zero on failure. + * + **/ +int +gnutls_openpgp_crt_check_hostname (gnutls_openpgp_crt_t key, + const char *hostname) +{ + char dnsname[MAX_CN]; + size_t dnsnamesize; + int ret; + int i; + + /* Check through all included names. */ + for (i = 0; !(ret < 0); i++) + { + dnsnamesize = sizeof (dnsname); + ret = gnutls_openpgp_crt_get_name (key, i, dnsname, &dnsnamesize); + /* FIXME: ret is not used */ + if (_gnutls_hostname_compare (dnsname, hostname)) + return 1; + } + + /* not found a matching name */ + return 0; +} + +unsigned int _gnutls_get_pgp_key_usage(unsigned int cdk_usage) +{ +unsigned int usage = 0; + + if (cdk_usage & CDK_KEY_USG_CERT_SIGN) + usage |= GNUTLS_KEY_KEY_CERT_SIGN; + if (cdk_usage & CDK_KEY_USG_DATA_SIGN) + usage |= GNUTLS_KEY_DIGITAL_SIGNATURE; + if (cdk_usage & CDK_KEY_USG_COMM_ENCR) + usage |= GNUTLS_KEY_KEY_ENCIPHERMENT; + if (cdk_usage & CDK_KEY_USG_STORAGE_ENCR) + usage |= GNUTLS_KEY_DATA_ENCIPHERMENT; + if (cdk_usage & CDK_KEY_USG_AUTH) + usage |= GNUTLS_KEY_KEY_AGREEMENT; + + return usage; +} + +/** + * gnutls_openpgp_crt_get_key_usage - This function returns the key's usage + * @key: should contain a gnutls_openpgp_crt_t structure + * @key_usage: where the key usage bits will be stored + * + * This function will return certificate's key usage, by checking the + * key algorithm. The key usage value will ORed values of the: + * GNUTLS_KEY_DIGITAL_SIGNATURE, GNUTLS_KEY_KEY_ENCIPHERMENT. + * + * A negative value may be returned in case of parsing error. + * + */ +int +gnutls_openpgp_crt_get_key_usage (gnutls_openpgp_crt_t key, + unsigned int *key_usage) +{ + cdk_packet_t pkt; + + if (!key) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + pkt = cdk_kbnode_find_packet (key->knode, CDK_PKT_PUBLIC_KEY); + if (!pkt) + return GNUTLS_E_OPENPGP_GETKEY_FAILED; + + *key_usage = _gnutls_get_pgp_key_usage(pkt->pkt.public_key->pubkey_usage); + + return 0; +} + +/** + * gnutls_openpgp_crt_get_subkey_count - This function returns the number of subkeys + * @key: is an OpenPGP key + * + * This function will return the number of subkeys present in the given + * OpenPGP certificate. + * + * Returns then number of subkeys or a negative value on error. + * + **/ +int +gnutls_openpgp_crt_get_subkey_count (gnutls_openpgp_crt_t key) +{ + cdk_kbnode_t p, ctx; + cdk_packet_t pkt; + int subkeys; + + if (key == NULL) + { + gnutls_assert (); + return 0; + } + + ctx = NULL; + subkeys = 0; + while ((p = cdk_kbnode_walk (key->knode, &ctx, 0))) + { + pkt = cdk_kbnode_get_packet (p); + if (pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY) + subkeys++; + } + + return subkeys; +} + +/* returns the subkey with the given index */ +static cdk_packet_t _get_public_subkey(gnutls_openpgp_crt_t key, unsigned int indx) +{ + cdk_kbnode_t p, ctx; + cdk_packet_t pkt; + int subkeys; + + if (key == NULL) + { + gnutls_assert (); + return NULL; + } + + ctx = NULL; + subkeys = 0; + while ((p = cdk_kbnode_walk (key->knode, &ctx, 0))) + { + pkt = cdk_kbnode_get_packet (p); + if (pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY && indx == subkeys++) + return pkt; + } + + return NULL; +} + +/* returns the key with the given keyid + * depending on what requested: + * pkt->pkt.secret_key; + * pkt->pkt.public_key; + */ +cdk_packet_t _gnutls_openpgp_find_key( cdk_kbnode_t knode, uint32_t keyid[2], + unsigned int priv) +{ + cdk_pkt_pubkey_t ret; + cdk_kbnode_t p, ctx; + cdk_packet_t pkt; + int subkeys; + uint32_t local_keyid[2]; + + ctx = NULL; + while ((p = cdk_kbnode_walk (knode, &ctx, 0))) + { + pkt = cdk_kbnode_get_packet (p); + + if ( (priv == 0 && (pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY || pkt->pkttype == CDK_PKT_PUBLIC_KEY)) || \ + (priv != 0 && (pkt->pkttype == CDK_PKT_SECRET_SUBKEY || pkt->pkttype == CDK_PKT_SECRET_KEY))) + { + if (priv == 0) + cdk_pk_get_keyid (pkt->pkt.public_key, local_keyid); + else + cdk_pk_get_keyid (pkt->pkt.secret_key->pk, local_keyid); + + if (local_keyid[0] == keyid[0] && \ + local_keyid[1] == keyid[1]) + { + return pkt; + } + } + } + + return NULL; +} + +/* returns the key with the given keyid + * depending on what requested: + * pkt->pkt.secret_key; + * pkt->pkt.public_key; + */ +int _gnutls_openpgp_find_subkey_idx( cdk_kbnode_t knode, uint32_t keyid[2], + unsigned int priv) +{ + cdk_pkt_pubkey_t ret; + cdk_kbnode_t p, ctx; + cdk_packet_t pkt; + int subkeys, i=0; + uint32_t local_keyid[2]; + + ctx = NULL; + while ((p = cdk_kbnode_walk (knode, &ctx, 0))) + { + pkt = cdk_kbnode_get_packet (p); + + if ( (priv == 0 && (pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY)) || \ + (priv != 0 && (pkt->pkttype == CDK_PKT_SECRET_SUBKEY))) + { + if (priv == 0) + cdk_pk_get_keyid (pkt->pkt.public_key, local_keyid); + else + cdk_pk_get_keyid (pkt->pkt.secret_key->pk, local_keyid); + + if (local_keyid[0] == keyid[0] && \ + local_keyid[1] == keyid[1]) + { + return i; + } + i++; + } + } + + gnutls_assert(); + return GNUTLS_E_OPENPGP_SUBKEY_ERROR; +} + +/** + * gnutls_openpgp_crt_get_subkey_revoked_status - Gets the revoked status of the key + * @key: the structure that contains the OpenPGP public key. + * @idx: is the subkey index + * + * Returns the true (1) or false (0) based on whether this key has been revoked + * or not. A negative value indicates an error. + * + **/ +int +gnutls_openpgp_crt_get_subkey_revoked_status (gnutls_openpgp_crt_t key, unsigned int idx) +{ + cdk_packet_t pkt; + + if (!key) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + pkt = _get_public_subkey( key, idx); + if (!pkt) + return GNUTLS_E_OPENPGP_GETKEY_FAILED; + + if (pkt->pkt.public_key->is_revoked != 0) return 1; + return 0; +} + +/** + * gnutls_openpgp_crt_get_subkey_pk_algorithm - This function returns the subkey's PublicKey algorithm + * @key: is an OpenPGP key + * @idx: is the subkey index + * @bits: if bits is non null it will hold the size of the parameters' in bits + * + * This function will return the public key algorithm of a subkey of an OpenPGP + * certificate. + * + * If bits is non null, it should have enough size to hold the parameters + * size in bits. For RSA the bits returned is the modulus. + * For DSA the bits returned are of the public exponent. + * + * Returns a member of the gnutls_pk_algorithm_t enumeration on success, + * or a negative value on error. + * + **/ +gnutls_pk_algorithm_t +gnutls_openpgp_crt_get_subkey_pk_algorithm (gnutls_openpgp_crt_t key, + unsigned int idx, unsigned int *bits) +{ + cdk_packet_t pkt; + int algo; + + if (!key) + return GNUTLS_PK_UNKNOWN; + + pkt = _get_public_subkey( key, idx); + + algo = 0; + if (pkt) + { + if (bits) + *bits = cdk_pk_get_nbits (pkt->pkt.public_key); + algo = pkt->pkt.public_key->pubkey_algo; + if (is_RSA (algo)) + algo = GNUTLS_PK_RSA; + else if (is_DSA (algo)) + algo = GNUTLS_PK_DSA; + else + algo = GNUTLS_E_UNKNOWN_PK_ALGORITHM; + } + + return algo; +} + +/** + * gnutls_openpgp_crt_get_subkey_creation_time - Extract the timestamp + * @key: the structure that contains the OpenPGP public key. + * @idx: the subkey index + * + * Returns the timestamp when the OpenPGP key was created. + **/ +time_t +gnutls_openpgp_crt_get_subkey_creation_time (gnutls_openpgp_crt_t key, unsigned int idx) +{ + cdk_packet_t pkt; + time_t timestamp; + + if (!key) + return (time_t) - 1; + + pkt = _get_public_subkey( key, idx); + if (pkt) + timestamp = pkt->pkt.public_key->timestamp; + else + timestamp = 0; + + return timestamp; +} + + +/** + * gnutls_openpgp_crt_get_subkey_expiration_time - Extract the expire date + * @key: the structure that contains the OpenPGP public key. + * @idx: the subkey index + * + * Returns the time when the OpenPGP key expires. A value of '0' means + * that the key doesn't expire at all. + **/ +time_t +gnutls_openpgp_crt_get_subkey_expiration_time (gnutls_openpgp_crt_t key, unsigned int idx) +{ + cdk_packet_t pkt; + time_t expiredate; + + if (!key) + return (time_t) - 1; + + pkt = _get_public_subkey( key, idx); + if (pkt) + expiredate = pkt->pkt.public_key->expiredate; + else + expiredate = 0; + + return expiredate; +} + +/** + * gnutls_openpgp_crt_get_subkey_id - Gets the keyID + * @key: the structure that contains the OpenPGP public key. + * @idx: the subkey index + * @keyid: the buffer to save the keyid. + * + * Returns the 64-bit keyID of the OpenPGP key. + **/ +int +gnutls_openpgp_crt_get_subkey_id (gnutls_openpgp_crt_t key, unsigned int idx, gnutls_openpgp_keyid_t* keyid) +{ + cdk_packet_t pkt; + uint32_t kid[2]; + + if (!key || !keyid) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + pkt = _get_public_subkey( key, idx); + if (!pkt) + return GNUTLS_E_OPENPGP_GETKEY_FAILED; + + cdk_pk_get_keyid (pkt->pkt.public_key, kid); + _gnutls_write_uint32( kid[0], keyid->keyid); + _gnutls_write_uint32( kid[1], keyid->keyid+4); + + return 0; +} + +/** + * gnutls_openpgp_crt_get_subkey_idx - Returns the subkey's index + * @key: the structure that contains the OpenPGP public key. + * @keyid: the keyid. + * + * Returns the index of the subkey or a negative error value. + * + **/ +int +gnutls_openpgp_crt_get_subkey_idx (gnutls_openpgp_crt_t key, gnutls_openpgp_keyid_t keyid) +{ + cdk_packet_t pkt; + int ret; + uint32_t kid[2]; + + if (!key) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + KEYID_IMPORT( kid, keyid); + ret = _gnutls_openpgp_find_subkey_idx( key->knode, kid, 0); + + if (ret < 0) + { + gnutls_assert(); + } + + return ret; +} + +/** + * gnutls_openpgp_crt_get_subkey_usage - This function returns the key's usage + * @key: should contain a gnutls_openpgp_crt_t structure + * @idx: the subkey index + * @key_usage: where the key usage bits will be stored + * + * This function will return certificate's key usage, by checking the + * key algorithm. The key usage value will ORed values of the: + * GNUTLS_KEY_DIGITAL_SIGNATURE, GNUTLS_KEY_KEY_ENCIPHERMENT. + * + * A negative value may be returned in case of parsing error. + * + */ +int +gnutls_openpgp_crt_get_subkey_usage (gnutls_openpgp_crt_t key, unsigned int idx, + unsigned int *key_usage) +{ + cdk_packet_t pkt; + + if (!key) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + pkt = _get_public_subkey( key, idx); + if (!pkt) + return GNUTLS_E_OPENPGP_SUBKEY_ERROR; + + *key_usage = _gnutls_get_pgp_key_usage(pkt->pkt.public_key->pubkey_usage); + + return 0; +} + +int _gnutls_read_pgp_mpi( cdk_packet_t pkt, unsigned int priv, size_t idx, mpi_t* m) +{ +size_t buf_size = 512; +opaque * buf = gnutls_malloc( buf_size); +int err; +int max_pub_params; + + if (priv !=0) + max_pub_params = cdk_pk_get_npkey(pkt->pkt.secret_key->pk->pubkey_algo); + + if (buf == NULL) + { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + /* FIXME: Note that opencdk doesn't like the buf to be NULL. + */ + if (priv == 0) + err = cdk_pk_get_mpi (pkt->pkt.public_key, idx, buf, buf_size, &buf_size, NULL); + else + { + if (idx < max_pub_params) + err = cdk_pk_get_mpi (pkt->pkt.secret_key->pk, idx, buf, buf_size, &buf_size, NULL); + else + { + err = cdk_sk_get_mpi (pkt->pkt.secret_key, idx-max_pub_params, buf, buf_size, &buf_size, NULL); + } + } + + if (err == CDK_Too_Short) + { + buf = gnutls_realloc_fast( buf, buf_size); + if (buf == NULL) + { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + if (priv == 0) + err = cdk_pk_get_mpi (pkt->pkt.public_key, idx, buf, buf_size, &buf_size, NULL); + else + { + if (idx < max_pub_params) + err = cdk_pk_get_mpi (pkt->pkt.secret_key->pk, idx, buf, buf_size, &buf_size, NULL); + else + { + err = cdk_sk_get_mpi (pkt->pkt.secret_key, idx-max_pub_params, buf, buf_size, &buf_size, NULL); + } + } + } + + if (err != CDK_Success) + { +_gnutls_x509_log( "err: %d/%d\n", err, idx); + gnutls_assert(); + gnutls_free( buf); + return _gnutls_map_cdk_rc( err); + } + + err = _gnutls_mpi_scan_pgp (m, buf, &buf_size); + gnutls_free( buf); + + if (err < 0) + { + gnutls_assert(); + return err; + } + + return 0; +} + + +/* Extracts DSA and RSA parameters from a certificate. + */ +int +_gnutls_openpgp_crt_get_mpis (gnutls_openpgp_crt_t cert, uint32_t keyid[2], + mpi_t * params, int *params_size) +{ + int result, i; + int pk_algorithm, local_params; + cdk_packet_t pkt; + + /* Read the algorithm's OID + */ + pk_algorithm = gnutls_openpgp_crt_get_pk_algorithm (cert, NULL); + + switch (pk_algorithm) + { + case GNUTLS_PK_RSA: + local_params = RSA_PUBLIC_PARAMS; + break; + case GNUTLS_PK_DSA: + local_params = DSA_PUBLIC_PARAMS; + break; + default: + gnutls_assert (); + return GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE; + } + + if (*params_size < local_params) + { + gnutls_assert(); + return GNUTLS_E_INTERNAL_ERROR; + } + + *params_size = local_params; + + pkt = _gnutls_openpgp_find_key( cert->knode, keyid, 0); + if (pkt == NULL) + { + gnutls_assert(); + return GNUTLS_E_OPENPGP_GETKEY_FAILED; + } + + for (i = 0; i < local_params; i++) + { + result = _gnutls_read_pgp_mpi( pkt, 0, i, ¶ms[i]); + if (result < 0) + { + gnutls_assert(); + goto error; + } + } + + return 0; + +error: + { + int j; + for (j=0;j<i;j++) + _gnutls_mpi_release( ¶ms[j]); + } + + return result; +} diff --git a/lib/openpgp/pgpverify.c b/lib/openpgp/pgpverify.c new file mode 100644 index 0000000000..34b06a0834 --- /dev/null +++ b/lib/openpgp/pgpverify.c @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2002, 2003, 2004, 2005, 2007 Free Software Foundation + * + * Author: Timo Schulz, Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS-EXTRA. + * + * GNUTLS-EXTRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GNUTLS-EXTRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* Functions on OpenPGP key parsing + */ + +#include <gnutls_int.h> +#include <openpgp.h> +#include <gnutls_errors.h> +#include <gnutls_openpgp.h> +#include <gnutls_num.h> +#include <x509/verify.h> /* lib/x509/verify.h */ + + +/** + * gnutls_openpgp_crt_verify_ring - Verify all signatures in the key + * @key: the structure that holds the key. + * @keyring: holds the keyring to check against + * @flags: unused (should be 0) + * @verify: will hold the certificate verification output. + * + * Verify all signatures in the key, using the given set of keys (keyring). + * + * The key verification output will be put in @verify and will be + * one or more of the gnutls_certificate_status_t enumerated elements bitwise or'd. + * + * GNUTLS_CERT_INVALID: A signature on the key is invalid. + * + * GNUTLS_CERT_REVOKED: The key has been revoked. + * + * Note that this function does not verify using any "web of + * trust". You may use GnuPG for that purpose, or any other external + * PGP application. + * + * Returns 0 on success. + **/ +int +gnutls_openpgp_crt_verify_ring (gnutls_openpgp_crt_t key, + gnutls_openpgp_keyring_t keyring, + unsigned int flags, unsigned int *verify) +{ + gnutls_openpgp_keyid_t id; + cdk_error_t rc; + int status; + + if (!key || !keyring) + { + gnutls_assert (); + return GNUTLS_E_NO_CERTIFICATE_FOUND; + } + + *verify = 0; + + rc = cdk_pk_check_sigs (key->knode, keyring->db, &status); + if (rc == CDK_Error_No_Key) + { + rc = GNUTLS_E_NO_CERTIFICATE_FOUND; + gnutls_assert (); + return rc; + } + else if (rc != CDK_Success) + { + _gnutls_x509_log("cdk_pk_check_sigs: error %d\n", rc); + rc = _gnutls_map_cdk_rc (rc); + gnutls_assert (); + return rc; + } + _gnutls_x509_log("status: %x\n", status); + + if (status & CDK_KEY_INVALID) + *verify |= GNUTLS_CERT_INVALID; + if (status & CDK_KEY_REVOKED) + *verify |= GNUTLS_CERT_REVOKED; + if (status & CDK_KEY_NOSIGNER) + *verify |= GNUTLS_CERT_SIGNER_NOT_FOUND; + + /* Check if the key is included in the ring. */ + if (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_SAME)) + { + rc = gnutls_openpgp_crt_get_id (key, &id); + if (rc < 0) + { + gnutls_assert (); + return rc; + } + + rc = gnutls_openpgp_keyring_check_id (keyring, id, 0); + /* If it exists in the keyring don't treat it as unknown. */ + if (rc == 0 && *verify & GNUTLS_CERT_SIGNER_NOT_FOUND) + *verify ^= GNUTLS_CERT_SIGNER_NOT_FOUND; + } + + return 0; +} + + +/** + * gnutls_openpgp_crt_verify_self - Verify the self signature on the key + * @key: the structure that holds the key. + * @flags: unused (should be 0) + * @verify: will hold the key verification output. + * + * Verifies the self signature in the key. + * The key verification output will be put in @verify and will be + * one or more of the gnutls_certificate_status_t enumerated elements bitwise or'd. + * + * GNUTLS_CERT_INVALID: The self signature on the key is invalid. + * + * Returns 0 on success. + **/ +int +gnutls_openpgp_crt_verify_self (gnutls_openpgp_crt_t key, + unsigned int flags, unsigned int *verify) +{ + int status; + cdk_error_t rc; + + rc = cdk_pk_check_self_sig (key->knode, &status); + if (rc || status != CDK_KEY_VALID) + *verify |= GNUTLS_CERT_INVALID; + else + *verify = 0; + + return 0; +} + diff --git a/lib/openpgp/privkey.c b/lib/openpgp/privkey.c new file mode 100644 index 0000000000..1083e30f22 --- /dev/null +++ b/lib/openpgp/privkey.c @@ -0,0 +1,575 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS-EXTRA. + * + * GNUTLS-EXTRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GNUTLS-EXTRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* Functions on OpenPGP privkey parsing + */ + +#include <gnutls_int.h> +#include <gnutls_datum.h> +#include <gnutls_global.h> +#include <gnutls_errors.h> +#include <gnutls_num.h> +#include <openpgp.h> +#include <gnutls_openpgp.h> +#include <x509/rfc2818.h> +#include <gnutls_cert.h> + +/** + * gnutls_openpgp_privkey_init - This function initializes a gnutls_openpgp_privkey_t structure + * @key: The structure to be initialized + * + * This function will initialize an OpenPGP key structure. + * + * Returns 0 on success. + * + **/ +int +gnutls_openpgp_privkey_init (gnutls_openpgp_privkey_t * key) +{ + *key = gnutls_calloc (1, sizeof (gnutls_openpgp_privkey_int)); + + if (*key) + return 0; /* success */ + return GNUTLS_E_MEMORY_ERROR; +} + +/** + * gnutls_openpgp_privkey_deinit - This function deinitializes memory used by a gnutls_openpgp_privkey_t structure + * @key: The structure to be initialized + * + * This function will deinitialize a key structure. + * + **/ +void +gnutls_openpgp_privkey_deinit (gnutls_openpgp_privkey_t key) +{ + if (!key) + return; + + if (key->knode) + { + cdk_kbnode_release (key->knode); + key->knode = NULL; + } + + gnutls_free (key); +} + +/** + * gnutls_openpgp_privkey_import - This function will import a RAW or BASE64 encoded key + * @key: The structure to store the parsed key. + * @data: The RAW or BASE64 encoded key. + * @format: One of gnutls_openpgp_crt_fmt_t elements. + * @pass: Unused for now + * @flags: should be zero + * + * This function will convert the given RAW or Base64 encoded key + * to the native gnutls_openpgp_privkey_t format. The output will be stored in 'key'. + * + * Returns 0 on success. + * + **/ +int +gnutls_openpgp_privkey_import (gnutls_openpgp_privkey_t key, + const gnutls_datum_t * data, + gnutls_openpgp_crt_fmt_t format, + const char *pass, unsigned int flags) +{ + cdk_stream_t inp; + int rc; + + if (format == GNUTLS_OPENPGP_FMT_RAW) + rc = cdk_kbnode_read_from_mem (&key->knode, data->data, data->size); + else + { + rc = cdk_stream_tmp_from_mem (data->data, data->size, &inp); + if (rc) + { + rc = _gnutls_map_cdk_rc (rc); + gnutls_assert (); + return rc; + } + if (cdk_armor_filter_use (inp)) + rc = cdk_stream_set_armor_flag (inp, 0); + if (!rc) + rc = cdk_keydb_get_keyblock (inp, &key->knode); + cdk_stream_close (inp); + if (rc) + { + rc = _gnutls_map_cdk_rc (rc); + gnutls_assert (); + return rc; + } + } + + return 0; +} + + +/** + * gnutls_openpgp_privkey_get_pk_algorithm - This function returns the key's PublicKey algorithm + * @key: is an OpenPGP key + * @bits: if bits is non null it will hold the size of the parameters' in bits + * + * This function will return the public key algorithm of an OpenPGP + * certificate. + * + * If bits is non null, it should have enough size to hold the parameters + * size in bits. For RSA the bits returned is the modulus. + * For DSA the bits returned are of the public exponent. + * + * Returns a member of the GNUTLS_PKAlgorithm enumeration on success, + * or a negative value on error. + * + **/ +gnutls_pk_algorithm_t +gnutls_openpgp_privkey_get_pk_algorithm (gnutls_openpgp_privkey_t key, + unsigned int *bits) +{ + cdk_packet_t pkt; + int algo; + + if (!key) + return GNUTLS_PK_UNKNOWN; + + algo = 0; + pkt = cdk_kbnode_find_packet (key->knode, CDK_PKT_SECRET_KEY); + if (pkt) + { + if (bits) + *bits = cdk_pk_get_nbits (pkt->pkt.secret_key->pk); + algo = pkt->pkt.secret_key->pk->pubkey_algo; + if (is_RSA (algo)) + algo = GNUTLS_PK_RSA; + else if (is_DSA (algo)) + algo = GNUTLS_PK_DSA; + else + algo = GNUTLS_PK_UNKNOWN; + } + + return algo; +} + +/** + * gnutls_openpgp_privkey_get_revoked_ status - Gets the revoked status of the key + * @key: the structure that contains the OpenPGP public key. + * + * Returns the true (1) or false (0) based on whether this key has been revoked + * or not. A negative value indicates an error. + * + **/ +int +gnutls_openpgp_privkey_get_revoked_status (gnutls_openpgp_privkey_t key) +{ + cdk_packet_t pkt; + + if (!key) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + pkt = cdk_kbnode_find_packet (key->knode, CDK_PKT_SECRET_KEY); + if (!pkt) + return GNUTLS_E_OPENPGP_GETKEY_FAILED; + + if (pkt->pkt.secret_key->is_revoked != 0) return 1; + return 0; +} + +/** + * gnutls_openpgp_privkey_get_fingerprint - Gets the fingerprint + * @key: the raw data that contains the OpenPGP secret key. + * @fpr: the buffer to save the fingerprint, must hold at least 20 bytes. + * @fprlen: the integer to save the length of the fingerprint. + * + * Returns the fingerprint of the OpenPGP key. Depends on the algorithm, + * the fingerprint can be 16 or 20 bytes. + **/ +int +gnutls_openpgp_privkey_get_fingerprint (gnutls_openpgp_privkey_t key, + void *fpr, size_t * fprlen) +{ + cdk_packet_t pkt; + cdk_pkt_pubkey_t pk = NULL; + + if (!fpr || !fprlen) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + *fprlen = 0; + + pkt = cdk_kbnode_find_packet (key->knode, CDK_PKT_SECRET_KEY); + if (!pkt) + { + gnutls_assert(); + return GNUTLS_E_OPENPGP_GETKEY_FAILED; + } + + pk = pkt->pkt.secret_key->pk; + *fprlen = 20; + + if (is_RSA (pk->pubkey_algo) && pk->version < 4) + *fprlen = 16; + + cdk_pk_get_fingerprint (pk, fpr); + + return 0; +} + +/** + * gnutls_openpgp_privkey_get_key_id - Gets the keyID + * @key: the structure that contains the OpenPGP secret key. + * @keyid: the buffer to save the keyid. + * + * Returns the 64-bit keyID of the OpenPGP key. + **/ +int +gnutls_openpgp_privkey_get_key_id (gnutls_openpgp_privkey_t key, gnutls_openpgp_keyid_t* keyid) +{ + cdk_packet_t pkt; + uint32_t kid[2]; + + if (!key || !keyid) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + pkt = cdk_kbnode_find_packet (key->knode, CDK_PKT_SECRET_KEY); + if (!pkt) + return GNUTLS_E_OPENPGP_GETKEY_FAILED; + + cdk_sk_get_keyid (pkt->pkt.secret_key, kid); + _gnutls_write_uint32( kid[0], keyid->keyid); + _gnutls_write_uint32( kid[1], keyid->keyid+4); + + return 0; +} + + +/** + * gnutls_openpgp_privkey_get_subkey_count - This function returns the number of subkeys + * @key: is an OpenPGP key + * + * This function will return the number of subkeys present in the given + * OpenPGP certificate. + * + * Returns then number of subkeys or a negative value on error. + * + **/ +int +gnutls_openpgp_privkey_get_subkey_count (gnutls_openpgp_privkey_t key) +{ + cdk_kbnode_t p, ctx; + cdk_packet_t pkt; + int subkeys; + + if (key == NULL) + { + gnutls_assert (); + return 0; + } + + ctx = NULL; + subkeys = 0; + while ((p = cdk_kbnode_walk (key->knode, &ctx, 0))) + { + pkt = cdk_kbnode_get_packet (p); + if (pkt->pkttype == CDK_PKT_SECRET_SUBKEY) + subkeys++; + } + + return subkeys; +} + +/* returns the subkey with the given index */ +static cdk_packet_t _get_secret_subkey(gnutls_openpgp_privkey_t key, unsigned int indx) +{ + cdk_kbnode_t p, ctx; + cdk_packet_t pkt; + int subkeys; + + ctx = NULL; + subkeys = 0; + while ((p = cdk_kbnode_walk (key->knode, &ctx, 0))) + { + pkt = cdk_kbnode_get_packet (p); + if (pkt->pkttype == CDK_PKT_SECRET_SUBKEY && indx == subkeys++) + return pkt; + } + + return NULL; +} + +/** + * gnutls_openpgp_privkey_get_subkey_revoked_ status - Gets the revoked status of the key + * @key: the structure that contains the OpenPGP public key. + * @idx: is the subkey index + * + * Returns the true (1) or false (0) based on whether this key has been revoked + * or not. A negative value indicates an error. + * + **/ +int +gnutls_openpgp_privkey_get_subkey_revoked_status (gnutls_openpgp_privkey_t key, unsigned int idx) +{ + cdk_packet_t pkt; + + if (!key) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + pkt = _get_secret_subkey( key, idx); + if (!pkt) + return GNUTLS_E_OPENPGP_GETKEY_FAILED; + + if (pkt->pkt.secret_key->is_revoked != 0) return 1; + return 0; +} + +/** + * gnutls_openpgp_privkey_get_subkey_pk_algorithm - This function returns the subkey's PublicKey algorithm + * @key: is an OpenPGP key + * @idx: is the subkey index + * @bits: if bits is non null it will hold the size of the parameters' in bits + * + * This function will return the public key algorithm of a subkey of an OpenPGP + * certificate. + * + * If bits is non null, it should have enough size to hold the parameters + * size in bits. For RSA the bits returned is the modulus. + * For DSA the bits returned are of the public exponent. + * + * Returns a member of the gnutls_pk_algorithm_t enumeration on success, + * or a negative value on error. + * + **/ +gnutls_pk_algorithm_t +gnutls_openpgp_privkey_get_subkey_pk_algorithm (gnutls_openpgp_privkey_t key, + unsigned int idx, unsigned int *bits) +{ + cdk_packet_t pkt; + int algo; + + if (!key) + return GNUTLS_PK_UNKNOWN; + + pkt = _get_secret_subkey( key, idx); + + algo = 0; + if (pkt) + { + if (bits) + *bits = cdk_pk_get_nbits (pkt->pkt.secret_key->pk); + algo = pkt->pkt.secret_key->pubkey_algo; + if (is_RSA (algo)) + algo = GNUTLS_PK_RSA; + else if (is_DSA (algo)) + algo = GNUTLS_PK_DSA; + else + algo = GNUTLS_E_UNKNOWN_PK_ALGORITHM; + } + + return algo; +} + +/** + * gnutls_openpgp_privkey_get_subkey_idx - Returns the subkey's index + * @key: the structure that contains the OpenPGP public key. + * @keyid: the keyid. + * + * Returns the index of the subkey or a negative error value. + * + **/ +int +gnutls_openpgp_privkey_get_subkey_idx (gnutls_openpgp_privkey_t key, gnutls_openpgp_keyid_t keyid) +{ + cdk_packet_t pkt; + int ret; + uint32_t kid[2]; + + if (!key) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + KEYID_IMPORT( kid, keyid); + ret = _gnutls_openpgp_find_subkey_idx( key->knode, kid, 1); + + if (ret < 0) + { + gnutls_assert(); + } + + return ret; +} + +/** + * gnutls_openpgp_privkey_get_subkey_creation_time - Extract the timestamp + * @key: the structure that contains the OpenPGP public key. + * @idx: the subkey index + * + * Returns the timestamp when the OpenPGP key was created. + **/ +time_t +gnutls_openpgp_privkey_get_subkey_creation_time (gnutls_openpgp_privkey_t key, unsigned int idx) +{ + cdk_packet_t pkt; + time_t timestamp; + + if (!key) + return (time_t) - 1; + + pkt = _get_secret_subkey( key, idx); + if (pkt) + timestamp = pkt->pkt.secret_key->pk->timestamp; + else + timestamp = 0; + + return timestamp; +} + +/** + * gnutls_openpgp_privkey_get_subkey_expiration_time - Extract the expire date + * @key: the structure that contains the OpenPGP public key. + * @idx: the subkey index + * + * Returns the time when the OpenPGP key expires. A value of '0' means + * that the key doesn't expire at all. + **/ +time_t +gnutls_openpgp_privkey_get_subkey_expiration_time (gnutls_openpgp_privkey_t key, unsigned int idx) +{ + cdk_packet_t pkt; + time_t expiredate; + + if (!key) + return (time_t) - 1; + + pkt = _get_secret_subkey( key, idx); + if (pkt) + expiredate = pkt->pkt.secret_key->expiredate; + else + expiredate = 0; + + return expiredate; +} + +/** + * gnutls_openpgp_privkey_get_subkey_id - Gets the keyID + * @key: the structure that contains the OpenPGP secret key. + * @idx: the subkey index + * @keyid: the buffer to save the keyid. + * + * Returns the 64-bit keyID of the OpenPGP key. + **/ +int +gnutls_openpgp_privkey_get_subkey_id (gnutls_openpgp_privkey_t key, unsigned int idx, gnutls_openpgp_keyid_t* keyid) +{ + cdk_packet_t pkt; + uint32_t kid[2]; + + if (!key || !keyid) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + pkt = _get_secret_subkey( key, idx); + if (!pkt) + return GNUTLS_E_OPENPGP_GETKEY_FAILED; + + cdk_sk_get_keyid (pkt->pkt.secret_key, kid); + _gnutls_write_uint32( kid[0], keyid->keyid); + _gnutls_write_uint32( kid[1], keyid->keyid+4); + + return 0; +} + +/* Extracts DSA and RSA parameters from a certificate. + */ +int +_gnutls_openpgp_privkey_get_mpis (gnutls_openpgp_privkey_t pkey, uint32_t keyid[2], + mpi_t * params, int *params_size) +{ + int result, i; + int pk_algorithm, local_params; + cdk_packet_t pkt; + + /* Read the algorithm's OID + */ + pk_algorithm = gnutls_openpgp_privkey_get_pk_algorithm (pkey, NULL); + + switch (pk_algorithm) + { + case GNUTLS_PK_RSA: + local_params = RSA_PRIVATE_PARAMS; + break; + case GNUTLS_PK_DSA: + local_params = DSA_PRIVATE_PARAMS; + break; + default: + gnutls_assert (); + return GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE; + } + + if (*params_size < local_params) + { + gnutls_assert(); + return GNUTLS_E_INTERNAL_ERROR; + } + + *params_size = local_params; + + pkt = _gnutls_openpgp_find_key( pkey->knode, keyid, 1); + if (pkt == NULL) + { + gnutls_assert(); + return GNUTLS_E_OPENPGP_GETKEY_FAILED; + } + + for (i = 0; i < local_params; i++) + { + result = _gnutls_read_pgp_mpi( pkt, 1, i, ¶ms[i]); + if (result < 0) + { + gnutls_assert(); + goto error; + } + } + + return 0; + +error: + { + int j; + for (j=0;j<i;j++) + _gnutls_mpi_release( ¶ms[j]); + } + + return result; +} |