summaryrefslogtreecommitdiff
path: root/src/pluto/pgp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pluto/pgp.c')
-rw-r--r--src/pluto/pgp.c647
1 files changed, 647 insertions, 0 deletions
diff --git a/src/pluto/pgp.c b/src/pluto/pgp.c
new file mode 100644
index 000000000..307303f6b
--- /dev/null
+++ b/src/pluto/pgp.c
@@ -0,0 +1,647 @@
+/* Support of OpenPGP certificates
+ * Copyright (C) 2002-2004 Andreas Steffen, Zuercher Hochschule Winterthur
+ *
+ * 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; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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.
+ *
+ * RCSID $Id: pgp.c,v 1.7 2006/01/04 21:00:43 as Exp $
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <freeswan.h>
+#include <ipsec_policy.h>
+
+#include "constants.h"
+#include "defs.h"
+#include "mp_defs.h"
+#include "log.h"
+#include "id.h"
+#include "pgp.h"
+#include "certs.h"
+#include "md5.h"
+#include "whack.h"
+#include "pkcs1.h"
+#include "keys.h"
+
+/*
+ * chained list of OpenPGP end certificates
+ */
+static pgpcert_t *pgpcerts = NULL;
+
+/*
+ * OpenPGP packet tags defined in section 4.3 of RFC 2440
+ */
+#define PGP_PKT_RESERVED 0
+#define PGP_PKT_PUBKEY_ENC_SESSION_KEY 1
+#define PGP_PKT_SIGNATURE 2
+#define PGP_PKT_SYMKEY_ENC_SESSION_KEY 3
+#define PGP_PKT_ONE_PASS_SIGNATURE_PKT 4
+#define PGP_PKT_SECRET_KEY 5
+#define PGP_PKT_PUBLIC_KEY 6
+#define PGP_PKT_SECRET_SUBKEY 7
+#define PGP_PKT_COMPRESSED_DATA 8
+#define PGP_PKT_SYMKEY_ENC_DATA 9
+#define PGP_PKT_MARKER 10
+#define PGP_PKT_LITERAL_DATA 11
+#define PGP_PKT_TRUST 12
+#define PGP_PKT_USER_ID 13
+#define PGP_PKT_PUBLIC_SUBKEY 14
+#define PGP_PKT_ROOF 15
+
+static const char *const pgp_packet_type_name[] = {
+ "Reserved",
+ "Public-Key Encrypted Session Key Packet",
+ "Signature Packet",
+ "Symmetric-Key Encrypted Session Key Packet",
+ "One-Pass Signature Packet",
+ "Secret Key Packet",
+ "Public Key Packet",
+ "Secret Subkey Packet",
+ "Compressed Data Packet",
+ "Symmetrically Encrypted Data Packet",
+ "Marker Packet",
+ "Literal Data Packet",
+ "Trust Packet",
+ "User ID Packet",
+ "Public Subkey Packet"
+};
+
+/*
+ * OpenPGP public key algorithms defined in section 9.1 of RFC 2440
+ */
+#define PGP_PUBKEY_ALG_RSA 1
+#define PGP_PUBKEY_ALG_RSA_ENC_ONLY 2
+#define PGP_PUBKEY_ALG_RSA_SIGN_ONLY 3
+#define PGP_PUBKEY_ALG_ELGAMAL_ENC_ONLY 16
+#define PGP_PUBKEY_ALG_DSA 17
+#define PGP_PUBKEY_ALG_ECC 18
+#define PGP_PUBKEY_ALG_ECDSA 19
+#define PGP_PUBKEY_ALG_ELGAMAL 20
+
+/*
+ * OpenPGP symmetric key algorithms defined in section 9.2 of RFC 2440
+ */
+#define PGP_SYM_ALG_PLAIN 0
+#define PGP_SYM_ALG_IDEA 1
+#define PGP_SYM_ALG_3DES 2
+#define PGP_SYM_ALG_CAST5 3
+#define PGP_SYM_ALG_BLOWFISH 4
+#define PGP_SYM_ALG_SAFER 5
+#define PGP_SYM_ALG_DES 6
+#define PGP_SYM_ALG_AES 7
+#define PGP_SYM_ALG_AES_192 8
+#define PGP_SYM_ALG_AES_256 9
+#define PGP_SYM_ALG_TWOFISH 10
+#define PGP_SYM_ALG_ROOF 11
+
+static const char *const pgp_sym_alg_name[] = {
+ "Plaintext",
+ "IDEA",
+ "3DES",
+ "CAST5",
+ "Blowfish",
+ "SAFER",
+ "DES",
+ "AES",
+ "AES-192",
+ "AES-256",
+ "Twofish"
+};
+
+/*
+ * Size of PGP Key ID
+ */
+#define PGP_KEYID_SIZE 8
+
+const pgpcert_t empty_pgpcert = {
+ NULL , /* *next */
+ 0 , /* installed */
+ 0 , /* count */
+ { NULL, 0 }, /* certificate */
+ 0 , /* created */
+ 0 , /* until */
+ 0 , /* pubkeyAlgorithm */
+ { NULL, 0 }, /* modulus */
+ { NULL, 0 }, /* publicExponent */
+ "" /* fingerprint */
+};
+
+static size_t
+pgp_size(chunk_t *blob, int len)
+{
+ size_t size = 0;
+
+ blob->len -= len;
+ while (len-- > 0)
+ size = 256*size + *blob->ptr++;
+ return size;
+}
+
+/*
+ * extracts the length of a PGP packet
+ */
+static size_t
+pgp_old_packet_length(chunk_t *blob)
+{
+ /* bits 0 and 1 define the packet length type */
+ int len_type = 0x03 & *blob->ptr++;
+
+ blob->len--;
+
+ /* len_type: 0 -> 1 byte, 1 -> 2 bytes, 2 -> 4 bytes */
+ return pgp_size(blob, (len_type == 0)? 1: len_type << 1);
+}
+
+/*
+ * extracts PGP packet version (V3 or V4)
+ */
+static u_char
+pgp_version(chunk_t *blob)
+{
+ u_char version = *blob->ptr++;
+ blob->len--;
+ DBG(DBG_PARSING,
+ DBG_log("L3 - version:");
+ DBG_log(" V%d", version)
+ )
+ return version;
+}
+
+/*
+ * Parse OpenPGP public key packet defined in section 5.5.2 of RFC 2440
+ */
+static bool
+parse_pgp_pubkey_packet(chunk_t *packet, pgpcert_t *cert)
+{
+ u_char version = pgp_version(packet);
+
+ if (version < 3 || version > 4)
+ {
+ plog("PGP packet version V%d not supported", version);
+ return FALSE;
+ }
+
+ /* creation date - 4 bytes */
+ cert->created = (time_t)pgp_size(packet, 4);
+ DBG(DBG_PARSING,
+ DBG_log("L3 - created:");
+ DBG_log(" %s", timetoa(&cert->created, TRUE))
+ )
+
+ if (version == 3)
+ {
+ /* validity in days - 2 bytes */
+ cert->until = (time_t)pgp_size(packet, 2);
+
+ /* validity of 0 days means that the key never expires */
+ if (cert->until > 0)
+ cert->until = cert->created + 24*3600*cert->until;
+
+ DBG(DBG_PARSING,
+ DBG_log("L3 - until:");
+ DBG_log(" %s", timetoa(&cert->until, TRUE));
+ )
+ }
+
+ /* public key algorithm - 1 byte */
+ DBG(DBG_PARSING,
+ DBG_log("L3 - public key algorithm:")
+ )
+
+ switch (pgp_size(packet, 1))
+ {
+ case PGP_PUBKEY_ALG_RSA:
+ case PGP_PUBKEY_ALG_RSA_SIGN_ONLY:
+ cert->pubkeyAlg = PUBKEY_ALG_RSA;
+ DBG(DBG_PARSING,
+ DBG_log(" RSA")
+ )
+ /* modulus n */
+ cert->modulus.len = (pgp_size(packet, 2)+7) / BITS_PER_BYTE;
+ cert->modulus.ptr = packet->ptr;
+ packet->ptr += cert->modulus.len;
+ packet->len -= cert->modulus.len;
+ DBG(DBG_PARSING,
+ DBG_log("L3 - modulus:")
+ )
+ DBG_cond_dump_chunk(DBG_RAW, "", cert->modulus);
+
+ /* public exponent e */
+ cert->publicExponent.len = (pgp_size(packet, 2)+7) / BITS_PER_BYTE;
+ cert->publicExponent.ptr = packet->ptr;
+ packet->ptr += cert->publicExponent.len;
+ packet->len -= cert->publicExponent.len;
+ DBG(DBG_PARSING,
+ DBG_log("L3 - public exponent:")
+ )
+ DBG_cond_dump_chunk(DBG_RAW, "", cert->publicExponent);
+
+ if (version == 3)
+ {
+ /* a V3 fingerprint is the MD5 hash of modulus and public exponent */
+ MD5_CTX context;
+ MD5Init(&context);
+ MD5Update(&context, cert->modulus.ptr, cert->modulus.len);
+ MD5Update(&context, cert->publicExponent.ptr, cert->publicExponent.len);
+ MD5Final(cert->fingerprint, &context);
+ }
+ else
+ {
+ plog(" computation of V4 key ID not implemented yet");
+ }
+ break;
+ case PGP_PUBKEY_ALG_DSA:
+ cert->pubkeyAlg = PUBKEY_ALG_DSA;
+ DBG(DBG_PARSING,
+ DBG_log(" DSA")
+ )
+ plog(" DSA public keys not supported");
+ return FALSE;
+ default:
+ cert->pubkeyAlg = 0;
+ DBG(DBG_PARSING,
+ DBG_log(" other")
+ )
+ plog(" exotic not RSA public keys not supported");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ * Parse OpenPGP secret key packet defined in section 5.5.3 of RFC 2440
+ */
+static bool
+parse_pgp_secretkey_packet(chunk_t *packet, RSA_private_key_t *key)
+{
+ int i, s2k;
+ pgpcert_t cert = empty_pgpcert;
+
+ if (!parse_pgp_pubkey_packet(packet, &cert))
+ return FALSE;
+
+ init_RSA_public_key((RSA_public_key_t *)key, cert.publicExponent
+ , cert.modulus);
+
+ /* string-to-key usage */
+ s2k = pgp_size(packet, 1);
+
+ DBG(DBG_PARSING,
+ DBG_log("L3 - string-to-key: %d", s2k)
+ )
+
+ if (s2k == 255)
+ {
+ plog(" string-to-key specifiers not supported");
+ return FALSE;
+ }
+
+ if (s2k >= PGP_SYM_ALG_ROOF)
+ {
+ plog(" undefined symmetric key algorithm");
+ return FALSE;
+ }
+
+ /* a known symmetric key algorithm is specified*/
+ DBG(DBG_PARSING,
+ DBG_log(" %s", pgp_sym_alg_name[s2k])
+ )
+
+ /* private key is unencrypted */
+ if (s2k == PGP_SYM_ALG_PLAIN)
+ {
+ for (i = 2; i < RSA_PRIVATE_FIELD_ELEMENTS; i++)
+ {
+ mpz_t u; /* auxiliary variable */
+
+ /* compute offset to private key component i*/
+ MP_INT *n = (MP_INT*)((char *)key + RSA_private_field[i].offset);
+
+ switch (i)
+ {
+ case 2:
+ case 3:
+ case 4:
+ {
+ size_t len = (pgp_size(packet, 2)+7) / BITS_PER_BYTE;
+
+ n_to_mpz(n, packet->ptr, len);
+ DBG(DBG_PARSING,
+ DBG_log("L3 - %s:", RSA_private_field[i].name)
+ )
+ DBG_cond_dump(DBG_PRIVATE, "", packet->ptr, len);
+ packet->ptr += len;
+ packet->len -= len;
+ }
+ break;
+ case 5: /* dP = d mod (p-1) */
+ mpz_init(u);
+ mpz_sub_ui(u, &key->p, 1);
+ mpz_mod(n, &key->d, u);
+ mpz_clear(u);
+ break;
+ case 6: /* dQ = d mod (q-1) */
+ mpz_init(u);
+ mpz_sub_ui(u, &key->q, 1);
+ mpz_mod(n, &key->d, u);
+ mpz_clear(u);
+ break;
+ case 7: /* qInv = (q^-1) mod p */
+ mpz_invert(n, &key->q, &key->p);
+ if (mpz_cmp_ui(n, 0) < 0)
+ mpz_add(n, n, &key->p);
+ passert(mpz_cmp(n, &key->p) < 0);
+ break;
+ }
+ }
+ return TRUE;
+ }
+
+ plog(" %s encryption not supported", pgp_sym_alg_name[s2k]);
+ return FALSE;
+}
+
+/*
+ * Parse OpenPGP signature packet defined in section 5.2.2 of RFC 2440
+ */
+static bool
+parse_pgp_signature_packet(chunk_t *packet, pgpcert_t *cert)
+{
+ time_t created;
+ chunk_t keyid;
+ u_char sig_type;
+ u_char version = pgp_version(packet);
+
+ /* we parse only V3 signature packets */
+ if (version != 3)
+ return TRUE;
+
+ /* size byte must have the value 5 */
+ if (pgp_size(packet, 1) != 5)
+ {
+ plog(" size must be 5");
+ return FALSE;
+ }
+
+ /* signature type - 1 byte */
+ sig_type = (u_char)pgp_size(packet, 1);
+ DBG(DBG_PARSING,
+ DBG_log("L3 - signature type: 0x%2x", sig_type)
+ )
+
+ /* creation date - 4 bytes */
+ created = (time_t)pgp_size(packet, 4);
+ DBG(DBG_PARSING,
+ DBG_log("L3 - created:");
+ DBG_log(" %s", timetoa(&cert->created, TRUE))
+ )
+
+ /* key ID of signer - 8 bytes */
+ keyid.ptr = packet->ptr;
+ keyid.len = PGP_KEYID_SIZE;
+ DBG_cond_dump_chunk(DBG_PARSING, "L3 - key ID of signer", keyid);
+
+ return TRUE;
+}
+
+bool
+parse_pgp(chunk_t blob, pgpcert_t *cert, RSA_private_key_t *key)
+{
+ DBG(DBG_PARSING,
+ DBG_log("L0 - PGP file:")
+ )
+ DBG_cond_dump_chunk(DBG_RAW, "", blob);
+
+ if (cert != NULL)
+ {
+ /* parse a PGP certificate file */
+ cert->certificate = blob;
+ time(&cert->installed);
+ }
+ else if (key == NULL)
+ {
+ /* should not occur, nothing to parse */
+ return FALSE;
+ }
+
+ while (blob.len > 0)
+ {
+ chunk_t packet = empty_chunk;
+ u_char packet_tag = *blob.ptr;
+
+ DBG(DBG_PARSING,
+ DBG_log("L1 - PGP packet: tag= 0x%2x", packet_tag)
+ )
+
+ /* bit 7 must be set */
+ if (!(packet_tag & 0x80))
+ {
+ plog(" incorrect Packet Tag");
+ return FALSE;
+ }
+
+ /* bit 6 set defines new packet format */
+ if (packet_tag & 0x40)
+ {
+ plog(" new PGP packet format not supported");
+ return FALSE;
+ }
+ else
+ {
+ int packet_type = (packet_tag & 0x3C) >> 2;
+
+ packet.len = pgp_old_packet_length(&blob);
+ packet.ptr = blob.ptr;
+ blob.ptr += packet.len;
+ blob.len -= packet.len;
+ DBG(DBG_PARSING,
+ DBG_log(" %s (%d), old format, %d bytes",
+ (packet_type < PGP_PKT_ROOF) ?
+ pgp_packet_type_name[packet_type] :
+ "Undefined Packet Type", packet_type, (int)packet.len);
+ DBG_log("L2 - body:")
+ )
+ DBG_cond_dump_chunk(DBG_RAW, "", packet);
+
+ if (cert != NULL)
+ {
+ /* parse a PGP certificate */
+ switch (packet_type)
+ {
+ case PGP_PKT_PUBLIC_KEY:
+ if (!parse_pgp_pubkey_packet(&packet, cert))
+ return FALSE;
+ break;
+ case PGP_PKT_SIGNATURE:
+ if (!parse_pgp_signature_packet(&packet, cert))
+ return FALSE;
+ break;
+ case PGP_PKT_USER_ID:
+ DBG(DBG_PARSING,
+ DBG_log("L3 - user ID:");
+ DBG_log(" '%.*s'", (int)packet.len, packet.ptr)
+ )
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ /* parse a PGP private key file */
+ switch (packet_type)
+ {
+ case PGP_PKT_SECRET_KEY:
+ if (!parse_pgp_secretkey_packet(&packet, key))
+ return FALSE;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ return TRUE;
+}
+
+/*
+ * compare two OpenPGP certificates
+ */
+static bool
+same_pgpcert(pgpcert_t *a, pgpcert_t *b)
+{
+ return a->certificate.len == b->certificate.len &&
+ memcmp(a->certificate.ptr, b->certificate.ptr, b->certificate.len) == 0;
+}
+
+/*
+ * for each link pointing to the certificate increase the count by one
+ */
+void
+share_pgpcert(pgpcert_t *cert)
+{
+ if (cert != NULL)
+ cert->count++;
+}
+
+/*
+ * select the OpenPGP keyid as ID
+ */
+void
+select_pgpcert_id(pgpcert_t *cert, struct id *end_id)
+{
+ end_id->kind = ID_KEY_ID;
+ end_id->name.len = PGP_FINGERPRINT_SIZE;
+ end_id->name.ptr = cert->fingerprint;
+ end_id->name.ptr = temporary_cyclic_buffer();
+ memcpy(end_id->name.ptr, cert->fingerprint, PGP_FINGERPRINT_SIZE);
+}
+
+/*
+ * add an OpenPGP user/host certificate to the chained list
+ */
+pgpcert_t*
+add_pgpcert(pgpcert_t *cert)
+{
+ pgpcert_t *c = pgpcerts;
+
+ while (c != NULL)
+ {
+ if (same_pgpcert(c, cert)) /* already in chain, free cert */
+ {
+ free_pgpcert(cert);
+ return c;
+ }
+ c = c->next;
+ }
+
+ /* insert new cert at the root of the chain */
+ cert->next = pgpcerts;
+ pgpcerts = cert;
+ DBG(DBG_CONTROL | DBG_PARSING,
+ DBG_log(" pgp cert inserted")
+ )
+ return cert;
+}
+
+/* release of a certificate decreases the count by one
+ " the certificate is freed when the counter reaches zero
+ */
+void
+release_pgpcert(pgpcert_t *cert)
+{
+ if (cert != NULL && --cert->count == 0)
+ {
+ pgpcert_t **pp = &pgpcerts;
+ while (*pp != cert)
+ pp = &(*pp)->next;
+ *pp = cert->next;
+ free_pgpcert(cert);
+ }
+}
+
+/*
+ * free a PGP certificate
+ */
+void
+free_pgpcert(pgpcert_t *cert)
+{
+ if (cert != NULL)
+ {
+ if (cert->certificate.ptr != NULL)
+ pfree(cert->certificate.ptr);
+ pfree(cert);
+ }
+}
+
+/*
+ * list all PGP end certificates in a chained list
+ */
+void
+list_pgp_end_certs(bool utc)
+{
+ pgpcert_t *cert = pgpcerts;
+ time_t now;
+
+ /* determine the current time */
+ time(&now);
+
+ if (cert != NULL)
+ {
+ whack_log(RC_COMMENT, " ");
+ whack_log(RC_COMMENT, "List of PGP End certificates:");
+ whack_log(RC_COMMENT, " ");
+ }
+
+ while (cert != NULL)
+ {
+ unsigned keysize;
+ char buf[BUF_LEN];
+ cert_t c;
+
+ c.type = CERT_PGP;
+ c.u.pgp = cert;
+
+ whack_log(RC_COMMENT, "%s, count: %d", timetoa(&cert->installed, utc), cert->count);
+ datatot(cert->fingerprint, PGP_FINGERPRINT_SIZE, 'x', buf, BUF_LEN);
+ whack_log(RC_COMMENT, " fingerprint: %s", buf);
+ form_keyid(cert->publicExponent, cert->modulus, buf, &keysize);
+ whack_log(RC_COMMENT, " pubkey: %4d RSA Key %s%s", 8*keysize, buf,
+ (has_private_key(c))? ", has private key" : "");
+ whack_log(RC_COMMENT, " created: %s", timetoa(&cert->created, utc));
+ whack_log(RC_COMMENT, " until: %s %s", timetoa(&cert->until, utc),
+ check_expiry(cert->until, CA_CERT_WARNING_INTERVAL, TRUE));
+ cert = cert->next;
+ }
+}
+