/* silcpgp_pubkey.c Author: Pekka Riikonen Copyright (C) 2007 - 2008 Pekka Riikonen This program 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; version 2 of the License. This program 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. */ #include "silccrypto.h" #include "rsa.h" #include "dsa.h" /************************ Static utility functions **************************/ /* Computes fingerprint of the OpenPGP public key and saves it to the key. Saves also the key IDs to public key context. */ static SilcBool silc_pgp_compute_fingerprint(SilcBuffer keybuf, SilcPGPPublicKey pubkey) { SILC_LOG_DEBUG(("Computing fingerprint")); if (pubkey->version >= 4) { /* Version 4 */ SilcHash sha1; unsigned char tmp[3]; if (!silc_hash_alloc("sha1", &sha1)) return FALSE; tmp[0] = 0x99; SILC_PUT16_MSB(silc_buffer_len(keybuf), tmp + 1); silc_hash_init(sha1); silc_hash_update(sha1, tmp, 3); silc_hash_update(sha1, silc_buffer_data(keybuf), silc_buffer_len(keybuf)); silc_hash_final(sha1, pubkey->fingerprint); silc_hash_free(sha1); /* Save key ID */ memcpy(pubkey->key_id, pubkey->fingerprint + 12, 8); } else { /* Versions 2 and 3 */ SilcHash md5; unsigned char *n, *e; SilcUInt16 n_len, e_len; if (!silc_hash_alloc("md5", &md5)) return FALSE; silc_buffer_format(keybuf, SILC_STR_OFFSET(8), SILC_STR_UI16_NSTRING(&n, &n_len), SILC_STR_UI16_NSTRING(&e, &e_len), SILC_STR_END); n_len = (n_len + 7) / 8; e_len = (e_len + 7) / 8; silc_hash_init(md5); silc_hash_update(md5, n, n_len); silc_hash_update(md5, e, e_len); silc_hash_final(md5, pubkey->fingerprint); silc_hash_free(md5); /* Save key ID */ memcpy(pubkey->key_id, n + (n_len - 8), 8); } return TRUE; } /*************************** Public Key Routines ****************************/ /* Decode OpenPGP Public Key packet */ int silc_pgp_packet_public_key_decode(unsigned char *key, SilcUInt32 key_len, SilcPGPPublicKey pubkey) { SilcBufferStruct keybuf, fbuf; const SilcPKCSAlgorithm *pkcs; int ret; SILC_LOG_DEBUG(("Parse OpenPGP public key packet")); if (!key || !key_len) return 0; silc_buffer_set(&keybuf, key, key_len); SILC_LOG_HEXDUMP(("OpenPGP public key"), key, key_len); /* Decode the key */ if (silc_buffer_unformat(&keybuf, SILC_STR_ADVANCE, SILC_STR_UINT8(&pubkey->version), SILC_STR_UI_INT(&pubkey->created), SILC_STR_END) < 0) { SILC_LOG_DEBUG(("Malformed public key")); goto err; } if (pubkey->version < 2) { SILC_LOG_DEBUG(("Invalid version %d", pubkey->version)); goto err; } SILC_LOG_DEBUG(("Public key version %d", pubkey->version)); if (pubkey->version <= 3) { /* Versions 2 and 3 */ if (silc_buffer_unformat(&keybuf, SILC_STR_ADVANCE, SILC_STR_UINT16(&pubkey->valid), SILC_STR_UINT8(&pubkey->algorithm), SILC_STR_END) < 0) { SILC_LOG_DEBUG(("Malformed public key")); goto err; } } else { /* Version 4 */ if (silc_buffer_unformat(&keybuf, SILC_STR_ADVANCE, SILC_STR_UINT8(&pubkey->algorithm), SILC_STR_END) < 0) { SILC_LOG_DEBUG(("Malformed public key")); goto err; } } SILC_LOG_DEBUG(("Parse algorithm %d", pubkey->algorithm)); /* Decode the public key algorithm */ switch (pubkey->algorithm) { case SILC_PGP_PKCS_RSA: case SILC_PGP_PKCS_RSA_ENC_ONLY: case SILC_PGP_PKCS_RSA_SIG_ONLY: /* Get PKCS object */ pkcs = silc_pkcs_find_algorithm("rsa", "openpgp"); if (!pkcs) { SILC_LOG_ERROR(("Unsupported PKCS algorithm (rsa/openpgp)")); goto err; } break; case SILC_PGP_PKCS_DSA: /* Get PKCS object */ pkcs = silc_pkcs_find_algorithm("dsa", "openpgp"); if (!pkcs) { SILC_LOG_ERROR(("Unsupported PKCS algorithm (dsa/openpgp)")); goto err; } break; case SILC_PGP_PKCS_ELGAMAL_ENC_ONLY: case SILC_PGP_PKCS_ELGAMAL: /* Get PKCS object */ pkcs = silc_pkcs_find_algorithm("elgamal", "openpgp"); if (!pkcs) { SILC_LOG_ERROR(("Unsupported PKCS algorithm (elgamal/openpgp)")); goto err; } break; default: SILC_LOG_DEBUG(("Unsupported OpenPGP public key algorithm %d", pubkey->algorithm)); goto err; } pubkey->pkcs = pkcs; /* Import the algorithm public key */ ret = pkcs->import_public_key(pkcs, silc_buffer_data(&keybuf), silc_buffer_len(&keybuf), &pubkey->public_key); if (!ret) { SILC_LOG_DEBUG(("Malformed public key")); goto err; } /* Compute and save fingerprint */ silc_buffer_set(&fbuf, key, silc_buffer_headlen(&keybuf) + ret); if (!silc_pgp_compute_fingerprint(&fbuf, pubkey)) goto err; return silc_buffer_headlen(&keybuf) + ret; err: return 0; } /* Decode public key from PGP packets */ SilcBool silc_pgp_public_key_decode(SilcList *list, SilcPGPPublicKey *ret_public_key) { SilcPGPPublicKey pubkey, subkey; unsigned char *data; SilcUInt32 data_len; SilcPGPPacket pub, packet; SILC_LOG_DEBUG(("Parse OpenPGP public key")); pubkey = silc_calloc(1, sizeof(*pubkey)); if (!pubkey) goto err; /* First packet must be public key packet */ pub = silc_list_get(*list); if (!pub) goto err; if (silc_pgp_packet_get_tag(pub) != SILC_PGP_PACKET_PUBKEY && silc_pgp_packet_get_tag(pub) != SILC_PGP_PACKET_PUBKEY_SUB) goto err; /* Parse the public key */ data = silc_pgp_packet_get_data(pub, &data_len); if (!silc_pgp_packet_public_key_decode(data, data_len, pubkey)) goto err; /* Parse any and all packets until we hit end of the packets or next public key in the list. We simply copy the raw data, and actual parsing is done later if and when the packets are needed. */ if (silc_pgp_packet_get_tag(pub) == SILC_PGP_PACKET_PUBKEY) { silc_list_init(pubkey->packets, struct SilcPGPPacketStruct, next); /* Copy the raw public key packet */ packet = silc_pgp_packet_copy(pub); if (packet) silc_list_add(pubkey->packets, packet); while ((packet = silc_list_get(*list))) { SILC_LOG_DEBUG(("Adding %d (%s) packet to public key", silc_pgp_packet_get_tag(packet), silc_pgp_packet_name(silc_pgp_packet_get_tag(packet)))); switch (silc_pgp_packet_get_tag(packet)) { case SILC_PGP_PACKET_PUBKEY: /* Next public key, stop decoding. Set list pointer so that the list points to the next public key. */ list->current = packet; break; case SILC_PGP_PACKET_PUBKEY_SUB: /* Parse subkeys recursively */ list->current = packet; if (!silc_pgp_public_key_decode(list, &subkey)) goto err; if (!pubkey->subkeys) { pubkey->subkeys = silc_dlist_init(); if (!pubkey->subkeys) goto err; } silc_dlist_add(pubkey->subkeys, subkey); default: /* Copy packet to the public key */ packet = silc_pgp_packet_copy(packet); if (packet) silc_list_add(pubkey->packets, packet); break; } } } if (ret_public_key) *ret_public_key = pubkey; return TRUE; err: silc_free(pubkey); return FALSE; } /* Free public key */ void silc_pgp_public_key_free(SilcPGPPublicKey public_key) { SilcPGPPublicKey p; SilcPGPPacket packet; if (public_key->pkcs) public_key->pkcs->public_key_free(public_key->pkcs, public_key->public_key); if (public_key->subkeys) { silc_dlist_start(public_key->subkeys); while ((p = silc_dlist_get(public_key->subkeys))) silc_pgp_public_key_free(p); silc_dlist_uninit(public_key->subkeys); } silc_list_start(public_key->packets); while ((packet = silc_list_get(public_key->packets))) silc_pgp_packet_free(packet); silc_free(public_key); }