diff options
author | Nikos <nmav@crystal.(none)> | 2008-01-13 15:15:51 +0200 |
---|---|---|
committer | Nikos <nmav@crystal.(none)> | 2008-01-13 15:15:51 +0200 |
commit | eeef9f65970e7461bbf7bc132db9a9984ca09512 (patch) | |
tree | 38aa94dcb64b29edfd7cd273b7f421ffdac2da42 | |
parent | 0aaecd8f619334d59612c9817048c412a95b5a48 (diff) | |
download | gnutls_2_2_x_with_opencdk.tar.gz |
Several additions to openpgp code. Now it correctly handles subkeys.gnutls_2_2_x_with_opencdk
-rw-r--r-- | includes/gnutls/openpgp.h | 37 | ||||
-rw-r--r-- | lib/auth_cert.c | 55 | ||||
-rw-r--r-- | lib/gnutls_cert.c | 25 | ||||
-rw-r--r-- | lib/gnutls_cert.h | 4 | ||||
-rw-r--r-- | lib/gnutls_openpgp.c | 433 | ||||
-rw-r--r-- | lib/opencdk/kbnode.c | 5 | ||||
-rw-r--r-- | lib/opencdk/keydb.c | 16 | ||||
-rw-r--r-- | lib/opencdk/opencdk.h | 13 | ||||
-rw-r--r-- | lib/opencdk/pubkey.c | 6 | ||||
-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 | 9 | ||||
-rw-r--r-- | lib/openpgp/pgp.c | 1079 | ||||
-rw-r--r-- | lib/openpgp/pgpverify.c | 144 | ||||
-rw-r--r-- | lib/openpgp/privkey.c | 575 | ||||
-rw-r--r-- | libextra/Makefile.am | 4 |
19 files changed, 2769 insertions, 311 deletions
diff --git a/includes/gnutls/openpgp.h b/includes/gnutls/openpgp.h index 134ab0c6f5..850dfa08c5 100644 --- a/includes/gnutls/openpgp.h +++ b/includes/gnutls/openpgp.h @@ -37,6 +37,11 @@ extern "C" #include <gnutls/gnutls.h> #include <gnutls/extra.h> + typedef struct + { + unsigned char keyid[8]; + } gnutls_openpgp_keyid_t; + /* gnutls_openpgp_cert_t should be defined in gnutls.h */ @@ -78,7 +83,7 @@ extern "C" time_t gnutls_openpgp_crt_get_expiration_time (gnutls_openpgp_crt_t key); int gnutls_openpgp_crt_get_id (gnutls_openpgp_crt_t key, - unsigned char keyid[8]); + gnutls_openpgp_keyid_t* keyid); int gnutls_openpgp_crt_check_hostname (gnutls_openpgp_crt_t key, const char *hostname); @@ -86,12 +91,13 @@ extern "C" int gnutls_openpgp_crt_get_revoked_status (gnutls_openpgp_crt_t key); int gnutls_openpgp_crt_get_subkey_count (gnutls_openpgp_crt_t key); + int gnutls_openpgp_crt_get_subkey_idx (gnutls_openpgp_crt_t key, gnutls_openpgp_keyid_t keyid); int gnutls_openpgp_crt_get_subkey_revoked_status (gnutls_openpgp_crt_t key, unsigned int idx); gnutls_pk_algorithm_t gnutls_openpgp_crt_get_subkey_pk_algorithm (gnutls_openpgp_crt_t key, unsigned int idx, unsigned int *bits); time_t gnutls_openpgp_crt_get_subkey_creation_time (gnutls_openpgp_crt_t key, unsigned int idx); time_t gnutls_openpgp_crt_get_subkey_expiration_time (gnutls_openpgp_crt_t key, unsigned int idx); - int gnutls_openpgp_crt_get_subkey_id (gnutls_openpgp_crt_t key, unsigned int idx, unsigned char keyid[8]); + int gnutls_openpgp_crt_get_subkey_id (gnutls_openpgp_crt_t key, unsigned int idx, gnutls_openpgp_keyid_t* keyid); int gnutls_openpgp_crt_get_subkey_usage (gnutls_openpgp_crt_t key, unsigned int idx, unsigned int *key_usage); @@ -107,14 +113,33 @@ extern "C" gnutls_openpgp_crt_fmt_t format, const char *pass, unsigned int flags); int gnutls_openpgp_privkey_sign_hash (gnutls_openpgp_privkey_t key, - const gnutls_datum_t * hash, - gnutls_datum_t * signature); + gnutls_openpgp_keyid_t subkeyid, + const gnutls_datum_t * hash, + gnutls_datum_t * signature); + int gnutls_openpgp_privkey_get_fingerprint (gnutls_openpgp_privkey_t key, + void *fpr, size_t * fprlen); + int gnutls_openpgp_privkey_get_key_id (gnutls_openpgp_privkey_t key, gnutls_openpgp_keyid_t* keyid); + int gnutls_openpgp_privkey_get_subkey_count (gnutls_openpgp_privkey_t key); + int gnutls_openpgp_privkey_get_subkey_idx (gnutls_openpgp_privkey_t key, gnutls_openpgp_keyid_t keyid); + + int gnutls_openpgp_privkey_get_subkey_revoked_status (gnutls_openpgp_privkey_t key, unsigned int idx); + + int gnutls_openpgp_privkey_get_revoked_status (gnutls_openpgp_privkey_t key); + + gnutls_pk_algorithm_t gnutls_openpgp_privkey_get_subkey_pk_algorithm (gnutls_openpgp_privkey_t key, + unsigned int idx, unsigned int *bits); + + time_t gnutls_openpgp_privkey_get_subkey_expiration_time (gnutls_openpgp_privkey_t key, unsigned int idx); + + int gnutls_openpgp_privkey_get_subkey_id (gnutls_openpgp_privkey_t key, unsigned int idx, gnutls_openpgp_keyid_t* keyid); + + time_t gnutls_openpgp_privkey_get_subkey_creation_time (gnutls_openpgp_privkey_t key, unsigned int idx); /* Keyring stuff. */ struct gnutls_openpgp_keyring_int; /* object to hold (parsed) openpgp keyrings */ typedef struct gnutls_openpgp_keyring_int *gnutls_openpgp_keyring_t; - + int gnutls_openpgp_keyring_init (gnutls_openpgp_keyring_t * keyring); void gnutls_openpgp_keyring_deinit (gnutls_openpgp_keyring_t keyring); @@ -123,7 +148,7 @@ extern "C" gnutls_openpgp_crt_fmt_t format); int gnutls_openpgp_keyring_check_id (gnutls_openpgp_keyring_t ring, - const unsigned char keyid[8], + gnutls_openpgp_keyid_t keyid, unsigned int flags); diff --git a/lib/auth_cert.c b/lib/auth_cert.c index c17b9df3ac..3f97b1e50f 100644 --- a/lib/auth_cert.c +++ b/lib/auth_cert.c @@ -48,9 +48,11 @@ #ifdef ENABLE_OPENPGP # include "openpgp/gnutls_openpgp.h" -static gnutls_cert *alloc_and_load_pgp_certs (gnutls_openpgp_crt_t cert); -static gnutls_privkey *alloc_and_load_pgp_key (const gnutls_openpgp_privkey_t - key); +static gnutls_privkey * +alloc_and_load_pgp_key (const gnutls_openpgp_privkey_t key, gnutls_openpgp_keyid_t keyid); +static gnutls_cert * +alloc_and_load_pgp_certs (gnutls_openpgp_crt_t cert, gnutls_openpgp_keyid_t* keyid); + #endif static gnutls_cert *alloc_and_load_x509_certs (gnutls_x509_crt_t * certs, @@ -445,8 +447,16 @@ call_get_cert_callback (gnutls_session_t session, if (type == GNUTLS_CRT_X509) { local_certs = alloc_and_load_x509_certs (st.cert.x509, st.ncerts); - if (local_certs != NULL) - local_key = alloc_and_load_x509_key (st.key.x509); + if (local_certs != NULL) + { + local_key = alloc_and_load_x509_key (st.key.x509); + if (local_key == NULL) + { + gnutls_assert(); + ret = GNUTLS_E_INTERNAL_ERROR; + goto cleanup; + } + } } else @@ -459,9 +469,21 @@ call_get_cert_callback (gnutls_session_t session, } #ifdef ENABLE_OPENPGP - local_certs = alloc_and_load_pgp_certs (st.cert.pgp); - if (local_certs != NULL) - local_key = alloc_and_load_pgp_key (st.key.pgp); + { + gnutls_openpgp_keyid_t selected_keyid; + + local_certs = alloc_and_load_pgp_certs (st.cert.pgp, &selected_keyid); + if (local_certs != NULL) + { + local_key = alloc_and_load_pgp_key (st.key.pgp, selected_keyid); + if (local_key == NULL) + { + gnutls_assert(); + ret = GNUTLS_E_INTERNAL_ERROR; + goto cleanup; + } + } + } #endif } @@ -1102,7 +1124,7 @@ _gnutls_proc_openpgp_server_certificate (gnutls_session_t session, peer_certificate_list_size); if ((ret = - _gnutls_openpgp_raw_key_to_gcert (&peer_certificate_list[0], + _gnutls_openpgp_raw_crt_to_gcert (&peer_certificate_list[0], &tmp)) < 0) { gnutls_assert (); @@ -1578,7 +1600,7 @@ alloc_and_load_x509_key (gnutls_x509_privkey_t key) */ #ifdef ENABLE_OPENPGP static gnutls_cert * -alloc_and_load_pgp_certs (gnutls_openpgp_crt_t cert) +alloc_and_load_pgp_certs (gnutls_openpgp_crt_t cert, gnutls_openpgp_keyid_t* keyid) { gnutls_cert *local_certs; int ret = 0; @@ -1593,7 +1615,14 @@ alloc_and_load_pgp_certs (gnutls_openpgp_crt_t cert) return NULL; } - ret = _gnutls_openpgp_crt_to_gcert (local_certs, cert); + ret = _gnutls_openpgp_find_valid_subkey( cert, keyid); + if (ret < 0) + { + gnutls_assert(); + return NULL; + } + + ret = _gnutls_openpgp_crt_to_gcert (local_certs, cert, *keyid); if (ret < 0) { gnutls_assert (); @@ -1615,7 +1644,7 @@ alloc_and_load_pgp_certs (gnutls_openpgp_crt_t cert) * space for it. */ static gnutls_privkey * -alloc_and_load_pgp_key (const gnutls_openpgp_privkey_t key) +alloc_and_load_pgp_key (const gnutls_openpgp_privkey_t key, gnutls_openpgp_keyid_t keyid) { gnutls_privkey *local_key; int ret = 0; @@ -1630,7 +1659,7 @@ alloc_and_load_pgp_key (const gnutls_openpgp_privkey_t key) return NULL; } - ret = _gnutls_openpgp_privkey_to_gkey (local_key, key); + ret = _gnutls_openpgp_privkey_to_gkey (local_key, key, keyid); if (ret < 0) { gnutls_assert (); diff --git a/lib/gnutls_cert.c b/lib/gnutls_cert.c index f0378f4fbb..669deb33cc 100644 --- a/lib/gnutls_cert.c +++ b/lib/gnutls_cert.c @@ -695,7 +695,7 @@ _gnutls_raw_cert_to_gcert (gnutls_cert * gcert, return _gnutls_x509_raw_cert_to_gcert (gcert, raw_cert, flags); #ifdef ENABLE_OPENPGP case GNUTLS_CRT_OPENPGP: - return _gnutls_openpgp_raw_key_to_gcert (gcert, raw_cert); + return _gnutls_openpgp_raw_crt_to_gcert (gcert, raw_cert); #endif default: gnutls_assert (); @@ -703,29 +703,6 @@ _gnutls_raw_cert_to_gcert (gnutls_cert * gcert, } } -int -_gnutls_raw_privkey_to_gkey (gnutls_privkey * key, - gnutls_certificate_type_t type, - const gnutls_datum_t * raw_key, - int key_enc /* DER or PEM */ ) -{ - switch (type) - { - case GNUTLS_CRT_X509: - return _gnutls_x509_raw_privkey_to_gkey (key, raw_key, key_enc); -#ifdef ENABLE_OPENPGP - case GNUTLS_CRT_OPENPGP: - return _gnutls_openpgp_raw_privkey_to_gkey (key, raw_key, - (gnutls_openpgp_crt_fmt_t) - key_enc); -#endif - default: - gnutls_assert (); - return GNUTLS_E_INTERNAL_ERROR; - } -} - - /* This function will convert a der certificate to a format * (structure) that gnutls can understand and use. Actually the * important thing on this function is that it extracts the diff --git a/lib/gnutls_cert.h b/lib/gnutls_cert.h index 6ce0847368..3101e6825c 100644 --- a/lib/gnutls_cert.h +++ b/lib/gnutls_cert.h @@ -124,9 +124,5 @@ int _gnutls_raw_cert_to_gcert (gnutls_cert * gcert, gnutls_certificate_type_t type, const gnutls_datum_t * raw_cert, int flags /* OR of ConvFlags */ ); -int _gnutls_raw_privkey_to_gkey (gnutls_privkey * key, - gnutls_certificate_type_t type, - const gnutls_datum_t * raw_key, - int key_enc /* DER or PEM */ ); #endif diff --git a/lib/gnutls_openpgp.c b/lib/gnutls_openpgp.c index 0584cb9804..8e95b53806 100644 --- a/lib/gnutls_openpgp.c +++ b/lib/gnutls_openpgp.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation * - * Author: Timo Schulz + * Author: Timo Schulz, Nikos Mavrogiannopoulos * * This file is part of GNUTLS-EXTRA. * @@ -22,6 +22,7 @@ #include "gnutls_int.h" #include "gnutls_errors.h" #include "gnutls_mpi.h" +#include "gnutls_num.h" #include "gnutls_cert.h" #include "gnutls_datum.h" #include "gnutls_global.h" @@ -34,8 +35,6 @@ #include <time.h> #include <sys/stat.h> -#define OPENPGP_NAME_SIZE 256 - #define datum_append(x, y, z) _gnutls_datum_append_m (x, y, z, gnutls_realloc) @@ -82,185 +81,53 @@ _gnutls_map_cdk_rc (int rc) } } - -static unsigned long -buftou32 (const uint8_t * buf) -{ - unsigned a; - a = buf[0] << 24; - a |= buf[1] << 16; - a |= buf[2] << 8; - a |= buf[3]; - return a; -} - -static int -openpgp_pk_to_gnutls_cert (gnutls_cert * cert, cdk_pkt_pubkey_t pk) +/* Finds a subkey to use for authentication + */ +int _gnutls_openpgp_find_valid_subkey( gnutls_openpgp_crt_t crt, gnutls_openpgp_keyid_t* keyid) { - uint8_t buf[512+2]; - size_t nbytes; - int algo, i; - int rc = 0; + int ret, subkeys, i; + unsigned int usage; - if (!cert || !pk) + subkeys = gnutls_openpgp_crt_get_subkey_count( crt); + if (subkeys <= 0) { - gnutls_assert (); - return GNUTLS_E_INVALID_REQUEST; - } - - /* GnuTLS OpenPGP does not support ELG keys */ - if (is_ELG (pk->pubkey_algo)) - return GNUTLS_E_UNWANTED_ALGORITHM; - - algo = is_DSA (pk->pubkey_algo) ? GNUTLS_PK_DSA : GNUTLS_PK_RSA; - cert->subject_pk_algorithm = algo; - cert->version = pk->version; - cert->cert_type = GNUTLS_CRT_OPENPGP; - - cert->key_usage = 0; - if (pk->pubkey_usage & CDK_KEY_USG_SIGN) - cert->key_usage = KEY_DIGITAL_SIGNATURE; - if (pk->pubkey_usage & CDK_KEY_USG_ENCR) - cert->key_usage = KEY_KEY_ENCIPHERMENT; - if (!cert->key_usage) /* Fallback code. */ - { - if (pk->pubkey_algo == GCRY_PK_DSA || pk->pubkey_algo == GCRY_PK_RSA_S) - cert->key_usage = KEY_DIGITAL_SIGNATURE; - else if (pk->pubkey_algo == GCRY_PK_RSA_E) - cert->key_usage = KEY_KEY_ENCIPHERMENT; - else if (pk->pubkey_algo == GCRY_PK_RSA) - cert->key_usage = KEY_DIGITAL_SIGNATURE | KEY_KEY_ENCIPHERMENT; - } - - cert->params_size = cdk_pk_get_npkey (pk->pubkey_algo); - for (i = 0; i < cert->params_size; i++) - { - nbytes = sizeof (buf) / sizeof (buf[0]); - cdk_pk_get_mpi (pk, i, buf, nbytes, &nbytes, NULL); - rc = _gnutls_mpi_scan_pgp (&cert->params[i], buf, &nbytes); - if (rc) - { - rc = GNUTLS_E_MPI_SCAN_FAILED; - break; - } + gnutls_assert(); + return GNUTLS_E_OPENPGP_SUBKEY_ERROR; } - if (rc) - release_mpi_array (cert->params, i - 1); - return rc; -} - -/*- - * _gnutls_openpgp_raw_privkey_to_gkey - Converts an OpenPGP secret key to GnuTLS - * @pkey: the GnuTLS private key context to store the key. - * @raw_key: the raw data which contains the whole key packets. - * @format: the format of the key packets. - * - * The RFC2440 (OpenPGP Message Format) data is converted into the - * GnuTLS specific data which is need to perform secret key operations. - * - * This function can read both BASE64 and RAW keys. - * - * FIXME: use the internal API - -*/ -int -_gnutls_openpgp_raw_privkey_to_gkey (gnutls_privkey * pkey, - const gnutls_datum_t * raw_key, - gnutls_openpgp_crt_fmt_t format) -{ - cdk_kbnode_t snode = NULL; - cdk_packet_t pkt; - cdk_stream_t out; - cdk_pkt_seckey_t sk = NULL; - int pke_algo, i, j; - size_t nbytes = 0; - uint8_t buf[512]; - int rc = 0; - - if (!pkey || raw_key->size <= 0) + /* Try to find a subkey with the authentication flag set. + * if none exists use the last one found + */ + for (i=0;i<subkeys;i++) { - gnutls_assert (); - return GNUTLS_E_CERTIFICATE_ERROR; - } - rc = cdk_stream_tmp_new (&out); - if (rc) - return GNUTLS_E_CERTIFICATE_ERROR; + ret = gnutls_openpgp_crt_get_subkey_revoked_status(crt, i); + if (ret != 0) /* it is revoked. ignore it */ + continue; - if (format == GNUTLS_OPENPGP_FMT_BASE64) - { - rc = cdk_stream_set_armor_flag (out, 0); - if (rc) - { - cdk_stream_close (out); - rc = _gnutls_map_cdk_rc (rc); - gnutls_assert (); - goto leave; - } - } + ret = gnutls_openpgp_crt_get_subkey_id( crt, i, keyid); + if (ret < 0) + { + gnutls_assert(); + return ret; + } - cdk_stream_write (out, raw_key->data, raw_key->size); - cdk_stream_seek (out, 0); + ret = gnutls_openpgp_crt_get_subkey_usage( crt, i, &usage); + if (ret < 0) + { + gnutls_assert(); + return ret; + } - rc = cdk_keydb_get_keyblock (out, &snode); - cdk_stream_close (out); - if (rc) - { - rc = GNUTLS_E_OPENPGP_GETKEY_FAILED; - goto leave; - } - - pkt = _gnutls_get_valid_subkey( snode, CDK_PKT_SECRET_SUBKEY); - if (!pkt) - { - rc = GNUTLS_E_OPENPGP_SUBKEY_ERROR; - goto leave; - } - sk = pkt->pkt.secret_key; - pke_algo = sk->pk->pubkey_algo; - pkey->params_size = cdk_pk_get_npkey (pke_algo); - for (i = 0; i < pkey->params_size; i++) - { - nbytes = sizeof (buf) / sizeof (buf[0]); - cdk_pk_get_mpi (sk->pk, i, buf, nbytes, &nbytes, NULL); - rc = _gnutls_mpi_scan_pgp (&pkey->params[i], buf, &nbytes); - if (rc) - { - rc = GNUTLS_E_MPI_SCAN_FAILED; - release_mpi_array (pkey->params, i - 1); - goto leave; - } - } - - pkey->params_size += cdk_pk_get_nskey (pke_algo); - for (j = 0; j < cdk_pk_get_nskey (pke_algo); j++, i++) - { - nbytes = sizeof (buf) / sizeof (buf[0]); - cdk_sk_get_mpi (sk, j, buf, nbytes, &nbytes, NULL); - rc = _gnutls_mpi_scan_pgp (&pkey->params[i], buf, &nbytes); - if (rc) - { - rc = GNUTLS_E_MPI_SCAN_FAILED; - release_mpi_array (pkey->params, i - 1); - goto leave; - } + if (usage & GNUTLS_KEY_KEY_AGREEMENT) + break; } - if (is_ELG (pke_algo)) - return GNUTLS_E_UNWANTED_ALGORITHM; - else if (is_DSA (pke_algo)) - pkey->pk_algorithm = GNUTLS_PK_DSA; - else if (is_RSA (pke_algo)) - pkey->pk_algorithm = GNUTLS_PK_RSA; - -leave: - cdk_kbnode_release (snode); - return rc; + return 0; } - /*- - * _gnutls_openpgp_raw_key_to_gcert - Converts raw OpenPGP data to GnuTLS certs + * _gnutls_openpgp_raw_crt_to_gcert - Converts raw OpenPGP data to GnuTLS certs * @cert: the certificate to store the data. * @raw: the buffer which contains the whole OpenPGP key packets. * @@ -268,37 +135,40 @@ leave: * specific certificate. -*/ int -_gnutls_openpgp_raw_key_to_gcert (gnutls_cert * cert, +_gnutls_openpgp_raw_crt_to_gcert (gnutls_cert * gcert, const gnutls_datum_t * raw) { - cdk_kbnode_t knode = NULL; - cdk_packet_t pkt = NULL; - int rc; + int ret; + gnutls_openpgp_crt_t pcrt; + gnutls_openpgp_keyid_t keyid; - if (!cert) + ret = gnutls_openpgp_crt_init (&pcrt); + if (ret < 0) { gnutls_assert (); - return GNUTLS_E_INVALID_REQUEST; + return ret; } - memset (cert, 0, sizeof *cert); - - rc = cdk_kbnode_read_from_mem (&knode, raw->data, raw->size); - if (!(rc = _gnutls_map_cdk_rc (rc))) { - pkt = _gnutls_get_valid_subkey( knode, CDK_PKT_PUBLIC_SUBKEY); - } - if (!pkt) + ret = gnutls_openpgp_crt_import (pcrt, raw, GNUTLS_OPENPGP_FMT_RAW); + if (ret < 0) { gnutls_assert (); - rc = GNUTLS_E_OPENPGP_SUBKEY_ERROR; + gnutls_openpgp_crt_deinit (pcrt); + return ret; } - if (!rc) - rc = _gnutls_set_datum (&cert->raw, raw->data, raw->size); - if (!rc) - rc = openpgp_pk_to_gnutls_cert (cert, pkt->pkt.public_key); - cdk_kbnode_release (knode); - return rc; + ret = _gnutls_openpgp_find_valid_subkey( pcrt, &keyid); + + if (ret < 0) + { + gnutls_assert(); + return ret; + } + + ret = _gnutls_openpgp_crt_to_gcert (gcert, pcrt, keyid); + gnutls_openpgp_crt_deinit (pcrt); + + return ret; } /** @@ -319,6 +189,15 @@ gnutls_certificate_set_openpgp_key (gnutls_certificate_credentials_t gnutls_openpgp_privkey_t pkey) { int ret; + gnutls_openpgp_keyid_t keyid; + + ret = _gnutls_openpgp_find_valid_subkey( crt, &keyid); + + if (ret < 0) + { + gnutls_assert(); + return ret; + } /* this should be first */ @@ -331,7 +210,7 @@ gnutls_certificate_set_openpgp_key (gnutls_certificate_credentials_t return GNUTLS_E_MEMORY_ERROR; } - ret = _gnutls_openpgp_privkey_to_gkey (&res->pkey[res->ncerts], pkey); + ret = _gnutls_openpgp_privkey_to_gkey (&res->pkey[res->ncerts], pkey, keyid); if (ret < 0) { gnutls_assert (); @@ -366,7 +245,7 @@ gnutls_certificate_set_openpgp_key (gnutls_certificate_credentials_t res->cert_list_length[res->ncerts] = 1; - ret = _gnutls_openpgp_crt_to_gcert (res->cert_list[res->ncerts], crt); + ret = _gnutls_openpgp_crt_to_gcert (res->cert_list[res->ncerts], crt, keyid); if (ret < 0) { gnutls_assert (); @@ -413,13 +292,13 @@ gnutls_openpgp_get_key (gnutls_datum_t * key, if (by == KEY_ATTR_SHORT_KEYID) { - keyid[0] = buftou32 (pattern); + keyid[0] = _gnutls_read_uint32(pattern); desc = keyid; } else if (by == KEY_ATTR_KEYID) { - keyid[0] = buftou32 (pattern); - keyid[1] = buftou32 (pattern + 4); + keyid[0] = _gnutls_read_uint32(pattern); + keyid[1] = _gnutls_read_uint32(pattern + 4); desc = keyid; } else @@ -841,84 +720,123 @@ gnutls_openpgp_set_recv_key_function (gnutls_session_t session, /* Copies a gnutls_openpgp_privkey_t to a gnutls_privkey structure. */ int _gnutls_openpgp_privkey_to_gkey (gnutls_privkey * dest, - gnutls_openpgp_privkey_t src) + gnutls_openpgp_privkey_t src, gnutls_openpgp_keyid_t keyid) { - int i, ret; - - memset (dest, 0, sizeof (gnutls_privkey)); - - for (i = 0; i < src->pkey.params_size; i++) + int ret = 0, idx; + uint32_t kid32[2]; + + if (dest==NULL || src == NULL) { - dest->params[i] = _gnutls_mpi_copy (src->pkey.params[i]); - if (dest->params[i] == NULL) - { - gnutls_assert (); - ret = GNUTLS_E_MEMORY_ERROR; - goto cleanup; - } + gnutls_assert (); + return GNUTLS_E_CERTIFICATE_ERROR; } - dest->pk_algorithm = src->pkey.pk_algorithm; - dest->params_size = src->pkey.params_size; - + KEYID_IMPORT(kid32, keyid); + + idx = gnutls_openpgp_privkey_get_subkey_idx( src, keyid); + if (idx < 0) + { + gnutls_assert(); + return idx; + } + + dest->pk_algorithm = gnutls_openpgp_privkey_get_subkey_pk_algorithm( src, idx, NULL); + + dest->params_size = MAX_PRIV_PARAMS_SIZE; + ret = _gnutls_openpgp_privkey_get_mpis( src, kid32, dest->params, &dest->params_size); + + if (ret < 0) + { + gnutls_assert(); + return ret; + } + return 0; -cleanup: - for (i = 0; i < src->pkey.params_size; i++) - _gnutls_mpi_release (&dest->params[i]); - return ret; } /* Converts a parsed gnutls_openpgp_crt_t to a gnutls_cert structure. */ int -_gnutls_openpgp_crt_to_gcert (gnutls_cert * gcert, gnutls_openpgp_crt_t cert) +_gnutls_openpgp_crt_to_gcert (gnutls_cert * gcert, gnutls_openpgp_crt_t cert, + gnutls_openpgp_keyid_t keyid) { - opaque *der; - size_t der_size = 0; - gnutls_datum_t raw; - int ret; + int ret, idx; + uint32_t kid32[2]; + + KEYID_IMPORT(kid32, keyid); memset (gcert, 0, sizeof (gnutls_cert)); gcert->cert_type = GNUTLS_CRT_OPENPGP; - - ret = gnutls_openpgp_crt_export (cert, GNUTLS_OPENPGP_FMT_RAW, - NULL, &der_size); - if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER) + idx = gnutls_openpgp_crt_get_subkey_idx( cert, keyid); + if (idx < 0) { - gnutls_assert (); - return ret; + gnutls_assert(); + return idx; } - der = gnutls_malloc (der_size); - if (der == NULL) - { - gnutls_assert (); - return GNUTLS_E_MEMORY_ERROR; - } + gcert->subject_pk_algorithm = gnutls_openpgp_crt_get_subkey_pk_algorithm( cert, idx, NULL); + gcert->version = gnutls_openpgp_crt_get_version( cert); + + gnutls_openpgp_crt_get_subkey_usage( cert, idx, &gcert->key_usage); - ret = gnutls_openpgp_crt_export (cert, GNUTLS_OPENPGP_FMT_RAW, - der, &der_size); + gcert->params_size = MAX_PUBLIC_PARAMS_SIZE; + ret = _gnutls_openpgp_crt_get_mpis( cert, kid32, gcert->params, &gcert->params_size); + if (ret < 0) { - gnutls_assert (); - gnutls_free (der); + gnutls_assert(); return ret; } - raw.data = der; - raw.size = der_size; + { /* copy the raw certificate */ +#define SMALL_RAW 512 + opaque *raw; + size_t raw_size = SMALL_RAW; - ret = _gnutls_openpgp_raw_key_to_gcert (gcert, &raw); - if (ret < 0) - { - gnutls_assert (); - gnutls_free (der); - return ret; - } + /* initially allocate a bogus size, just in case the certificate + * fits in it. That way we minimize the DER encodings performed. + */ + raw = gnutls_malloc (raw_size); + if (raw == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } - gnutls_free (der); + ret = + gnutls_openpgp_crt_export (cert, GNUTLS_OPENPGP_FMT_RAW, raw, &raw_size); + if (ret < 0 && ret != GNUTLS_E_SHORT_MEMORY_BUFFER) + { + gnutls_assert (); + gnutls_free (raw); + return ret; + } + + if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) + { + raw = gnutls_realloc (raw, raw_size); + if (raw == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + ret = + gnutls_openpgp_crt_export (cert, GNUTLS_OPENPGP_FMT_RAW, raw, + &raw_size); + if (ret < 0) + { + gnutls_assert (); + gnutls_free (raw); + return ret; + } + } + + gcert->raw.data = raw; + gcert->raw.size = raw_size; + } return 0; @@ -928,20 +846,28 @@ _gnutls_openpgp_crt_to_gcert (gnutls_cert * gcert, gnutls_openpgp_crt_t cert) /** * gnutls_openpgp_privkey_sign_hash - This function will sign the given data using the private key params * @key: Holds the key + * @src_keyid: Holds the keyid to be used * @hash: holds the data to be signed * @signature: will contain newly allocated signature * * This function will sign the given hash using the private key. + * You should use gnutls_openpgp_privkey_set_subkey() before calling this function + * to set the subkey to use. * * Return value: In case of failure a negative value will be returned, * and 0 on success. **/ int gnutls_openpgp_privkey_sign_hash (gnutls_openpgp_privkey_t key, + gnutls_openpgp_keyid_t src_keyid, const gnutls_datum_t * hash, gnutls_datum_t * signature) { - int result; +int result, i; +uint32_t keyid[2]; +mpi_t params[MAX_PUBLIC_PARAMS_SIZE]; +int params_size = MAX_PUBLIC_PARAMS_SIZE; +int pk_algorithm; if (key == NULL) { @@ -949,8 +875,23 @@ gnutls_openpgp_privkey_sign_hash (gnutls_openpgp_privkey_t key, return GNUTLS_E_INVALID_REQUEST; } - result = _gnutls_sign (key->pkey.pk_algorithm, key->pkey.params, - key->pkey.params_size, hash, signature); + KEYID_IMPORT( keyid, src_keyid); + + result = _gnutls_openpgp_privkey_get_mpis( key, keyid, params, ¶ms_size); + if (result < 0) + { + gnutls_assert (); + return result; + } + + pk_algorithm = gnutls_openpgp_privkey_get_pk_algorithm (key, NULL); + + result = _gnutls_sign (pk_algorithm, params, + params_size, hash, signature); + + for (i=0;i<params_size;i++) + _gnutls_mpi_release( ¶ms[i]); + if (result < 0) { gnutls_assert (); diff --git a/lib/opencdk/kbnode.c b/lib/opencdk/kbnode.c index 9106356dbc..dd76fd74d5 100644 --- a/lib/opencdk/kbnode.c +++ b/lib/opencdk/kbnode.c @@ -529,7 +529,10 @@ cdk_kbnode_write_to_mem (cdk_kbnode_t node, byte *buf, size_t *r_nbytes) return 0; } if (*r_nbytes < len) - rc = CDK_Too_Short; + { + *r_nbytes = len; + rc = CDK_Too_Short; + } if (!rc) *r_nbytes = cdk_stream_read (s, buf, len); cdk_stream_close (s); diff --git a/lib/opencdk/keydb.c b/lib/opencdk/keydb.c index 588c233ab7..79b7b89c5a 100644 --- a/lib/opencdk/keydb.c +++ b/lib/opencdk/keydb.c @@ -1504,12 +1504,20 @@ keydb_merge_selfsig (cdk_kbnode_t key, u32 *keyid) s = cdk_subpkt_find (sig->hashed, CDK_SIGSUBPKT_KEY_FLAGS); if (s) { - if (s->d[0] & 0x03) /* cert + sign data */ - key_usage |= CDK_KEY_USG_SIGN; - if (s->d[0] & 0x0C) /* encrypt comm. + storage */ - key_usage |= CDK_KEY_USG_ENCR; + if (s->d[0] & 0x01) /* cert + sign data */ + key_usage |= CDK_KEY_USG_CERT_SIGN; + if (s->d[0] & 0x02) /* cert + sign data */ + key_usage |= CDK_KEY_USG_DATA_SIGN; + if (s->d[0] & 0x04) /* encrypt comm. + storage */ + key_usage |= CDK_KEY_USG_COMM_ENCR; + if (s->d[0] & 0x08) /* encrypt comm. + storage */ + key_usage |= CDK_KEY_USG_STORAGE_ENCR; + if (s->d[0] & 0x10) /* encrypt comm. + storage */ + key_usage |= CDK_KEY_USG_SPLIT_KEY; if (s->d[0] & 0x20) key_usage |= CDK_KEY_USG_AUTH; + if (s->d[0] & 0x80) /* encrypt comm. + storage */ + key_usage |= CDK_KEY_USG_SHARED_KEY; } s = cdk_subpkt_find (sig->hashed, CDK_SIGSUBPKT_PREFS_SYM); if (s) diff --git a/lib/opencdk/opencdk.h b/lib/opencdk/opencdk.h index 83ca487e91..da2a869f95 100644 --- a/lib/opencdk/opencdk.h +++ b/lib/opencdk/opencdk.h @@ -281,12 +281,17 @@ enum cdk_crypto_mode_t { CDK_CRYPTYPE_IMPORT = 6 }; - +#define CDK_KEY_USG_ENCR CDK_KEY_USG_COMM_ENCR|CDK_KEY_USG_STORAGE_ENCR +#define CDK_KEY_USG_SIGN CDK_KEY_USG_DATA_SIGN|CDK_KEY_USG_CERT_SIGN /* A list of valid public key usages. */ enum cdk_key_usage_t { - CDK_KEY_USG_ENCR = 1, /* Key can be used for encryption. */ - CDK_KEY_USG_SIGN = 2, /* Key can be used for signing and certifying. */ - CDK_KEY_USG_AUTH = 4 /* Key can be used for authentication. */ + CDK_KEY_USG_CERT_SIGN = 1, + CDK_KEY_USG_DATA_SIGN = 2, + CDK_KEY_USG_COMM_ENCR = 4, + CDK_KEY_USG_STORAGE_ENCR = 8, + CDK_KEY_USG_SPLIT_KEY = 16, + CDK_KEY_USG_AUTH = 32, + CDK_KEY_USG_SHARED_KEY = 128 }; diff --git a/lib/opencdk/pubkey.c b/lib/opencdk/pubkey.c index ae6fac9269..5a072b56ba 100644 --- a/lib/opencdk/pubkey.c +++ b/lib/opencdk/pubkey.c @@ -605,14 +605,15 @@ _cdk_pk_algo_usage (int algo) return usage; } - +/* You can use a NULL buf to get the output size only + */ static cdk_error_t mpi_to_buffer (gcry_mpi_t a, byte *buf, size_t buflen, size_t *r_nwritten, size_t *r_nbits) { size_t nbits; - if (!a || !buf || !r_nwritten) + if (!a || !r_nwritten) return CDK_Inv_Value; nbits = gcry_mpi_get_nbits (a); @@ -621,6 +622,7 @@ mpi_to_buffer (gcry_mpi_t a, byte *buf, size_t buflen, if ((nbits+7)/8+2 > buflen) return CDK_Too_Short; *r_nwritten = (nbits+7)/8+2; + if (gcry_mpi_print (GCRYMPI_FMT_PGP, buf, buflen, r_nwritten, a)) return CDK_Wrong_Format; return 0; 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 index bcfb5f7996..71b997133f 100644 --- a/lib/openpgp/output.c +++ b/lib/openpgp/output.c @@ -133,21 +133,20 @@ print_key_usage (gnutls_string * str, gnutls_openpgp_crt_t cert, unsigned int id static void print_key_id (gnutls_string * str, gnutls_openpgp_crt_t cert, int idx) { - char fpr[8]; - size_t fpr_size = sizeof (fpr); + gnutls_openpgp_keyid_t id; int err; if (idx < 0) - err = gnutls_openpgp_crt_get_id (cert, fpr); + err = gnutls_openpgp_crt_get_id (cert, &id); else - err = gnutls_openpgp_crt_get_subkey_id( cert, idx, fpr); + 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, fpr, fpr_size); + hexprint (str, id.keyid, sizeof(id.keyid)); addf (str, "\n"); } } 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; +} diff --git a/libextra/Makefile.am b/libextra/Makefile.am index 2eebe2501f..33f9e6ba58 100644 --- a/libextra/Makefile.am +++ b/libextra/Makefile.am @@ -52,7 +52,7 @@ lib_LTLIBRARIES = libgnutls-extra.la libgnutls_extra_la_SOURCES = gnutls_extra.c -libgnutls_openssl_la_LDFLAGS = -no-undefined +libgnutls_openssl_la_LDFLAGS = -no-undefined -L$(top_srcdir)/lib/.libs # OpenSSL @@ -82,7 +82,7 @@ endif # OpenPGP libgnutls_extra_la_LIBADD = -libgnutls_extra_la_LDFLAGS = -no-undefined +libgnutls_extra_la_LDFLAGS = -no-undefined -L$(top_srcdir)/lib/.libs # TLS/IA |