Added OpenPGP library to lib/silcpgp
authorPekka Riikonen <priikone@silcnet.org>
Wed, 13 Feb 2008 15:25:59 +0000 (17:25 +0200)
committerPekka Riikonen <priikone@silcnet.org>
Wed, 13 Feb 2008 15:25:59 +0000 (17:25 +0200)
The OpenPGP libary adds OpenPGP certificate and private key support.

This version is still preliminary and supports only loading of
public and private keys.  See TODO file for things to do.

22 files changed:
.gitignore [new file with mode: 0644]
configure.ad
distdir/crypto
lib/Makefile.ad
lib/silccrypt/silccrypto.h.in
lib/silccrypt/silcpk.c
lib/silccrypt/silcpkcs.c
lib/silccrypt/silcpkcs.h
lib/silccrypt/silcpkcs_i.h
lib/silcpgp/Makefile.ad [new file with mode: 0644]
lib/silcpgp/silcpgp.c [new file with mode: 0644]
lib/silcpgp/silcpgp.h [new file with mode: 0644]
lib/silcpgp/silcpgp_i.h [new file with mode: 0644]
lib/silcpgp/silcpgp_pkcs.c [new file with mode: 0644]
lib/silcpgp/silcpgp_pkcs.h [new file with mode: 0644]
lib/silcpgp/silcpgp_pubkey.c [new file with mode: 0644]
lib/silcpgp/silcpgp_seckey.c [new file with mode: 0644]
lib/silcpgp/tests/Makefile.am [new file with mode: 0644]
lib/silcpgp/tests/pubkey.asc [new file with mode: 0644]
lib/silcpgp/tests/seckey.prv [new file with mode: 0644]
lib/silcpgp/tests/test_silcpgp.c [new file with mode: 0644]
lib/silcssh/silcssh_pkcs.c

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..83c2875
--- /dev/null
@@ -0,0 +1,31 @@
+autodist.dist
+autodist.log
+makedist.log
+Makefile
+Makefile.in
+Makefile.defines
+Makefile.defines_int
+acconfig.h
+aclocal.m4
+autom4te.cache
+autom4te-*.cache
+config.cache
+config.guess
+config.h
+config.h.in
+config.log
+config.status
+config.sub
+configure
+configure.in
+configure.ac
+libtool
+libtool-shared
+ltconfig
+ltmain.sh
+stamp-h*
+stamp-h*.in
+*.o
+*.lo
+*.la
+TAGS
index f50b4a3837cb043b8b01db413314fe70b79c0642..7b0dfaf811e0551037ff4ea186f1abe9ff98ebd3 100644 (file)
@@ -421,7 +421,6 @@ AC_ARG_ENABLE(debug,
     case "${enableval}" in
       yes)
         AC_MSG_RESULT(yes)
-        AC_DEFINE([SILC_DEBUG], [], [enable-debug])
        summary_debug="yes"
         ;;
       *)
@@ -485,8 +484,13 @@ SILC_LIB_INCLUDES="$SILC_LIB_INCLUDES -I$SILC_TOP_SRCDIR/lib/silccrypt"
 SILC_LIB_INCLUDES="$SILC_LIB_INCLUDES -I$SILC_TOP_SRCDIR/lib/silcasn1"
 SILC_LIB_INCLUDES="$SILC_LIB_INCLUDES -I$SILC_TOP_SRCDIR/lib/silcacc"
 SILC_LIB_INCLUDES="$SILC_LIB_INCLUDES -I$SILC_TOP_SRCDIR/lib/silcskr"
-SILC_LIB_INCLUDES="$SILC_LIB_INCLUDES -I$SILC_TOP_SRCDIR/lib/silcssh"
 SILC_LIB_INCLUDES="$SILC_LIB_INCLUDES -I$SILC_TOP_SRCDIR/lib/silcmath"
+#ifdef SILC_DIST_SSH
+SILC_LIB_INCLUDES="$SILC_LIB_INCLUDES -I$SILC_TOP_SRCDIR/lib/silcssh"
+#endif SILC_DIST_SSH
+#ifdef SILC_DIST_PGP
+SILC_LIB_INCLUDES="$SILC_LIB_INCLUDES -I$SILC_TOP_SRCDIR/lib/silcpgp"
+#endif SILC_DIST_PGP
 
 # SILC Runtime Toolkit checking
 AC_ARG_WITH(srt-includes,
@@ -648,8 +652,14 @@ lib/silcasn1/Makefile
 lib/silcasn1/tests/Makefile
 lib/silcacc/Makefile
 lib/silcacc/tests/Makefile
+#ifdef SILC_DIST_SSH
 lib/silcssh/Makefile
 lib/silcssh/tests/Makefile
+#endif SILC_DIST_SSH
+#ifdef SILC_DIST_PGP
+lib/silcpgp/Makefile
+lib/silcpgp/tests/Makefile
+#endif SILC_DIST_PGP
 lib/silcskr/Makefile
 lib/silcskr/tests/Makefile
 lib/silcmath/Makefile
index b76248bf2b7c5f63a007cc09c7cfb4b2e51ee427..4dd3231d3ab5b4e4938898037fe0f0be822bee04 100644 (file)
@@ -10,8 +10,9 @@ include distdir/BSD BSD
 license-header distdir/GPL-header distdir/CRYPTO-header
 
 # Distdefs
-define SILC_DIST_TMA
 define SILC_DIST_SSH
+define SILC_DIST_PGP
+define SILC_DIST_TMA
 define SILC_DIST_ASN1UTILS
 
 # TFM not enabled for now
index dde84c5165d6b3a3c00982ac82e38d2582aaf08e..0191e8472121438165085ced5c59bc8e8afda384 100644 (file)
@@ -27,6 +27,9 @@ CRYPTO_DIRS =                 \
 #ifdef SILC_DIST_SSH
        silcssh         \
 #endif SILC_DIST_SSH
+#ifdef SILC_DIST_PGP
+       silcpgp         \
+#endif SILC_DIST_PGP
 
 if SILC_ENABLE_SHARED
 if SILC_WIN32
index c84e0858f100a10f67911df383dd77353ae701b7..af8d0a671251ba333b2e6a70be9c0b6e2b935312 100644 (file)
@@ -94,13 +94,16 @@ extern "C" {
 #include <silcpkcs.h>
 #include <silcpk.h>
 #include <silcpkcs1.h>
+#include <silcasn1.h>
+#include <silcber.h>
+#include <silcskr.h>
 #include <silcacc.h>
 #ifdef SILC_DIST_SSH
 #include <silcssh.h>
 #endif /* SILC_DIST_SSH */
-#include <silcasn1.h>
-#include <silcber.h>
-#include <silcskr.h>
+#ifdef SILC_DIST_PGP
+#include <silcpgp.h>
+#endif /* SILC_DIST_PGP */
 
 /****f* silccrypt/SilcCryptoAPI/silc_crypto_init
  *
index 8c7517993001d64ab832109cd597e579606fd030..b96cb3e9a12e26e71b3e0e6b117ced6bd81bab18 100644 (file)
@@ -965,7 +965,7 @@ SILC_PKCS_IMPORT_PRIVATE_KEY_FILE(silc_pkcs_silc_import_private_key_file)
   silc_cipher_free(aes);
 
   /* Import the private key */
-  ret = silc_pkcs_silc_import_private_key(pkcs, NULL, filedata,
+  ret = silc_pkcs_silc_import_private_key(pkcs, NULL, NULL, 0, filedata,
                                          len, ret_private_key, ret_alg);
 
   silc_free(data);
index ac0e7b443d2fe29f1aaed0a5ae745158f98acb72..6b8e68fb0f367bec3b1d590457bbd338efcb84fc 100644 (file)
@@ -24,6 +24,9 @@
 #ifdef SILC_DIST_SSH
 #include "silcssh_pkcs.h"
 #endif /* SILC_DIST_SSH */
+#ifdef SILC_DIST_PGP
+#include "silcpgp_pkcs.h"
+#endif /* SILC_DIST_PGP */
 
 #ifndef SILC_SYMBIAN
 /* Dynamically registered list of PKCS. */
@@ -89,6 +92,32 @@ const SilcPKCSObject silc_default_pkcs[] =
   },
 #endif /* SILC_DIST_SSH */
 
+#ifdef SILC_DIST_PGP
+  /* OpenPGP PKCS */
+  {
+    SILC_PKCS_OPENPGP,
+    silc_pkcs_pgp_get_algorithm,
+    silc_pkcs_pgp_import_public_key_file,
+    silc_pkcs_pgp_import_public_key,
+    silc_pkcs_pgp_export_public_key_file,
+    silc_pkcs_pgp_export_public_key,
+    silc_pkcs_pgp_public_key_bitlen,
+    silc_pkcs_pgp_public_key_copy,
+    silc_pkcs_pgp_public_key_compare,
+    silc_pkcs_pgp_public_key_free,
+    silc_pkcs_pgp_import_private_key_file,
+    silc_pkcs_pgp_import_private_key,
+    silc_pkcs_pgp_export_private_key_file,
+    silc_pkcs_pgp_export_private_key,
+    silc_pkcs_pgp_private_key_bitlen,
+    silc_pkcs_pgp_private_key_free,
+    silc_pkcs_pgp_encrypt,
+    silc_pkcs_pgp_decrypt,
+    silc_pkcs_pgp_sign,
+    silc_pkcs_pgp_verify,
+  },
+#endif /* SILC_DIST_PGP */
+
   {
     0, NULL, NULL, NULL, NULL, NULL,
        NULL, NULL, NULL, NULL, NULL
@@ -191,7 +220,7 @@ const SilcPKCSAlgorithm silc_default_pkcs_alg[] =
   {
     "dsa",
     "ssh",
-    "sha1",
+    "sha1,sha224,sha256,sha384,sha512",
     silc_dsa_generate_key,
     silc_ssh_dsa_import_public_key,
     silc_ssh_dsa_export_public_key,
@@ -210,6 +239,52 @@ const SilcPKCSAlgorithm silc_default_pkcs_alg[] =
   },
 #endif /* SILC_DIST_SSH */
 
+#ifdef SILC_DIST_PGP
+  /* PKCS #1, OpenPGP style public keys */
+  {
+    "rsa",
+    "openpgp",
+    "sha1",
+    silc_pkcs1_generate_key,
+    silc_pgp_rsa_import_public_key,
+    silc_pgp_rsa_export_public_key,
+    silc_pkcs1_public_key_bitlen,
+    silc_pkcs1_public_key_copy,
+    silc_pkcs1_public_key_compare,
+    silc_pkcs1_public_key_free,
+    silc_pgp_rsa_import_private_key,
+    silc_pgp_rsa_export_private_key,
+    silc_pkcs1_private_key_bitlen,
+    silc_pkcs1_private_key_free,
+    silc_pkcs1_encrypt,
+    silc_pkcs1_decrypt,
+    silc_pkcs1_sign,
+    silc_pkcs1_verify
+  },
+
+  /* DSS, OpenPGP style public keys */
+  {
+    "dsa",
+    "openpgp",
+    "sha1,sha224,sha256,sha384,sha512",
+    silc_dsa_generate_key,
+    silc_pgp_dsa_import_public_key,
+    silc_pgp_dsa_export_public_key,
+    silc_dsa_public_key_bitlen,
+    silc_dsa_public_key_copy,
+    silc_dsa_public_key_compare,
+    silc_dsa_public_key_free,
+    silc_pgp_dsa_import_private_key,
+    silc_pgp_dsa_export_private_key,
+    silc_dsa_private_key_bitlen,
+    silc_dsa_private_key_free,
+    silc_dsa_encrypt,
+    silc_dsa_decrypt,
+    silc_dsa_sign,
+    silc_dsa_verify
+  },
+#endif /* SILC_DIST_PGP */
+
   {
     NULL, NULL, NULL, NULL,
     NULL, NULL, NULL, NULL,
@@ -653,7 +728,7 @@ SilcBool silc_pkcs_private_key_alloc(SilcPKCSType type,
   }
 
   /* Import the PKCS private key */
-  if (!pkcs->import_private_key(pkcs, NULL, key, key_len,
+  if (!pkcs->import_private_key(pkcs, NULL, NULL, 0, key, key_len,
                                &private_key->private_key,
                                &private_key->alg)) {
     silc_free(private_key);
index 2fd34f7490650fb731eda7c30007401524b1f626..66bebbbc7280f7f9f7195db45dea979dbeaf5f02 100644 (file)
@@ -66,9 +66,10 @@ typedef enum {
  * DESCRIPTION
  *
  *    This context represents any kind of PKCS public key.  It can be
- *    allocated by silc_pkcs_public_key_alloc and is freed by the
- *    silc_pkcs_public_key_free.  The PKCS specific public key context
- *    can be retrieved by calling silc_pkcs_public_key_get_pkcs.
+ *    allocated by silc_pkcs_public_key_alloc or silc_pkcs_load_public_key
+ *    and is freed by the silc_pkcs_public_key_free.  The PKCS specific
+ *    public key context can be retrieved by calling
+ *    silc_pkcs_public_key_get_pkcs.
  *
  * SOURCE
  */
@@ -87,8 +88,10 @@ typedef struct SilcPublicKeyStruct {
  *
  * DESCRIPTION
  *
- *    This context represents any kind of PKCS private key.  The PKCS specific
- *    key context can be retrieved by calling silc_pkcs_private_key_get_pkcs.
+ *    This context represents any kind of PKCS private key.  It can be
+ *    allocated by silc_pkcs_private_key_alloc or more commonly by calling
+ *    silc_pkcs_load_private_key.  The PKCS specific key context can be
+ *    retrieved by calling silc_pkcs_private_key_get_pkcs.
  *
  * SOURCE
  */
@@ -555,6 +558,9 @@ SilcPublicKey silc_pkcs_public_key_copy(SilcPublicKey public_key);
  *    `key' of length of `key_len' bytes.  Returns FALSE if the `key'
  *    is malformed or unsupported private key type.
  *
+ *    Usually this function is not needed.  Typical application calls
+ *    silc_pkcs_load_private_key instead.
+ *
  ***/
 SilcBool silc_pkcs_private_key_alloc(SilcPKCSType type,
                                     unsigned char *key,
index 62df5108857e74f4414f80a75c0816cbb848a3df..b730307a216563289382936e450bac1177df01b8 100644 (file)
@@ -232,6 +232,7 @@ struct SilcPKCSAlgorithmStruct {
 #define SILC_PKCS_IMPORT_PRIVATE_KEY(name)                             \
   int name(const struct SilcPKCSObjectStruct *pkcs,                    \
           const struct SilcPKCSAlgorithmStruct *alg,                   \
+          const char *passphrase, SilcUInt32 passphrase_len,           \
           void *key, SilcUInt32 key_len, void **ret_private_key,       \
           const struct SilcPKCSAlgorithmStruct **ret_alg)
 
diff --git a/lib/silcpgp/Makefile.ad b/lib/silcpgp/Makefile.ad
new file mode 100644 (file)
index 0000000..b8dd860
--- /dev/null
@@ -0,0 +1,33 @@
+#
+#  Makefile.ad
+#
+#  Author: Pekka Riikonen <priikone@silcnet.org>
+#
+#  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.
+#
+
+AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
+
+noinst_LTLIBRARIES = libsilcpgp.la
+
+libsilcpgp_la_SOURCES =        \
+       silcpgp.c               \
+       silcpgp_pubkey.c        \
+       silcpgp_seckey.c        \
+       silcpgp_pkcs.c
+
+include_HEADERS =              \
+       silcpgp.h
+
+EXTRA_DIST = *.h tests
+
+include $(top_srcdir)/Makefile.defines.in
diff --git a/lib/silcpgp/silcpgp.c b/lib/silcpgp/silcpgp.c
new file mode 100644 (file)
index 0000000..f882c9e
--- /dev/null
@@ -0,0 +1,606 @@
+/*
+
+  silcpgp.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  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"
+
+/************************* Static utility functions *************************/
+
+/* Parse PGP packet */
+
+static int
+silc_pgp_packet_parse(const unsigned char *data, SilcUInt32 data_len,
+                     SilcPGPPacket *ret_packet)
+{
+  SilcPGPPacket packet;
+  SilcBufferStruct buf;
+  SilcUInt8 tag;
+  SilcBool partial = FALSE;
+  SilcUInt32 len;
+
+  SILC_LOG_DEBUG(("Parsing OpenPGP packet"));
+
+  if (!data || data_len < 2)
+    return 0;
+  silc_buffer_set(&buf, (unsigned char *)data, data_len);
+
+  packet = silc_calloc(1, sizeof(*packet));
+  if (!packet)
+    return 0;
+
+  while (silc_buffer_len(&buf) > 0) {
+    tag = buf.data[0];
+    silc_buffer_pull(&buf, 1);
+
+    if (!(tag & 0x80)) {
+      SILC_LOG_DEBUG(("Invalid tag"));
+      goto err;
+    }
+
+    if (tag & 0x40) {
+      /* New format */
+
+      /* Packet type */
+      if (!packet->tag) {
+       packet->tag = tag & 0x3f;
+       SILC_LOG_DEBUG(("Packet type %d (%s)", packet->tag,
+                       silc_pgp_packet_name(packet->tag)));
+      }
+
+      /* Packet length */
+      len = buf.data[0];
+      if (len >= 192 && len <= 223) {
+       /* 2 byte length */
+       if (silc_buffer_len(&buf) < 2)
+         goto err;
+       len = ((len - 192) << 8) + buf.data[1] + 192;
+       silc_buffer_pull(&buf, 2);
+      } else if (len == 255) {
+       /* 5 byte length */
+       if (silc_buffer_len(&buf) < 5)
+         goto err;
+       silc_buffer_pull(&buf, 1);
+       SILC_GET32_MSB(len, buf.data);
+       silc_buffer_pull(&buf, 4);
+      } else if (len >= 224 && len < 255) {
+       /* Partial length */
+       if (silc_buffer_len(&buf) < 1)
+         goto err;
+       len = 1 << (len & 0x1f);
+       silc_buffer_pull(&buf, 1);
+       partial = TRUE;
+      }
+    } else {
+      /* Old format */
+      SilcUInt8 llen;
+
+      /* Pakcet type */
+      if (!packet->tag) {
+       packet->tag = (tag >> 2) & 0x0f;
+       SILC_LOG_DEBUG(("Packet type %d (%s)", packet->tag,
+                       silc_pgp_packet_name(packet->tag)));
+      }
+
+      if ((tag & 0x03) == 3) {
+       /* Indeterminate length, use whole buffer */
+       len = silc_buffer_len(&buf);
+      } else {
+       for (llen = 1 << (tag & 0x03), len = 0 ; llen; llen--) {
+         len <<= 8;
+         len |= buf.data[0];
+         if (!silc_buffer_pull(&buf, 1))
+           goto err;
+       }
+      }
+    }
+
+    if (silc_buffer_len(&buf) < len) {
+      SILC_LOG_DEBUG(("Too short packet (%d < %d)",
+                     silc_buffer_len(&buf), len));
+      goto err;
+    }
+
+    /* Get data */
+    if (silc_buffer_format(&packet->data,
+                          SILC_STR_ADVANCE,
+                          SILC_STR_DATA(silc_buffer_data(&buf), len),
+                          SILC_STR_END) < 0)
+      goto err;
+
+    silc_buffer_pull(&buf, len);
+
+    if (!partial)
+      break;
+  }
+
+  silc_buffer_start(&packet->data);
+
+  SILC_LOG_HEXDUMP(("Packet, len %d", silc_buffer_len(&packet->data)),
+                   silc_buffer_data(&packet->data),
+                   silc_buffer_len(&packet->data));
+
+  *ret_packet = packet;
+
+  return silc_buffer_headlen(&buf);
+
+ err:
+  silc_buffer_purge(&packet->data);
+  silc_free(packet);
+  return 0;
+}
+
+/****************************** PGP Algorithms ******************************/
+
+/* Allocates cipher */
+
+SilcCipher silc_pgp_cipher_alloc(SilcPGPCipher cipher)
+{
+  SilcCipher c;
+
+  SILC_LOG_DEBUG(("Allocate cipher %d", cipher));
+
+  switch (cipher) {
+  case SILC_PGP_CIPHER_IDEA:
+    if (!silc_cipher_alloc("idea-128-cfb", &c)) {
+      SILC_LOG_ERROR(("Unsupported algorithm idea-128-cfb"));
+      return NULL;
+    }
+    break;
+
+  case SILC_PGP_CIPHER_3DES:
+    if (!silc_cipher_alloc("3des-168-cfb", &c)) {
+      SILC_LOG_ERROR(("Unsupported algorithm 3des-168-cfb"));
+      return NULL;
+    }
+    break;
+
+  case SILC_PGP_CIPHER_CAST5:
+    if (!silc_cipher_alloc("cast5-128-cfb", &c)) {
+      SILC_LOG_ERROR(("Unsupported algorithm cast5-168-cfb"));
+      return NULL;
+    }
+    break;
+
+  case SILC_PGP_CIPHER_BLOWFISH:
+    if (!silc_cipher_alloc("blowfish-128-cfb", &c)) {
+      SILC_LOG_ERROR(("Unsupported algorithm blowfish-128-cfb"));
+      return NULL;
+    }
+    break;
+
+  case SILC_PGP_CIPHER_AES128:
+    if (!silc_cipher_alloc("aes-128-cfb", &c)) {
+      SILC_LOG_ERROR(("Unsupported algorithm aes-128-cfb"));
+      return NULL;
+    }
+    break;
+
+  case SILC_PGP_CIPHER_AES192:
+    if (!silc_cipher_alloc("aes-192-cfb", &c)) {
+      SILC_LOG_ERROR(("Unsupported algorithm aes-192-cfb"));
+      return NULL;
+    }
+    break;
+
+  case SILC_PGP_CIPHER_AES256:
+    if (!silc_cipher_alloc("aes-256-cfb", &c)) {
+      SILC_LOG_ERROR(("Unsupported algorithm aes-256-cfb"));
+      return NULL;
+    }
+    break;
+
+  case SILC_PGP_CIPHER_TWOFISH:
+    if (!silc_cipher_alloc("twofish-256-cfb", &c)) {
+      SILC_LOG_ERROR(("Unsupported algorithm twofish-256-cfb"));
+      return NULL;
+    }
+    break;
+
+  default:
+    return NULL;
+    break;
+  }
+
+  return c;
+}
+
+/* Allocates hash function */
+
+SilcHash silc_pgp_hash_alloc(SilcPGPHash hash)
+{
+  SilcHash h;
+
+  SILC_LOG_DEBUG(("Allocate hash %d", hash));
+
+  switch (hash) {
+  case SILC_PGP_HASH_MD5:
+    if (!silc_hash_alloc("md5", &h)) {
+      SILC_LOG_ERROR(("Unsupported algorithm md5"));
+      return NULL;
+    }
+    break;
+
+  case SILC_PGP_HASH_SHA1:
+    if (!silc_hash_alloc("sha1", &h)) {
+      SILC_LOG_ERROR(("Unsupported algorithm sha1"));
+      return NULL;
+    }
+    break;
+
+  case SILC_PGP_HASH_RIPEMD160:
+    if (!silc_hash_alloc("ripemd160", &h)) {
+      SILC_LOG_ERROR(("Unsupported algorithm ripemd160"));
+      return NULL;
+    }
+    break;
+
+  case SILC_PGP_HASH_SHA256:
+    if (!silc_hash_alloc("sha256", &h)) {
+      SILC_LOG_ERROR(("Unsupported algorithm sha256"));
+      return NULL;
+    }
+    break;
+
+  case SILC_PGP_HASH_SHA384:
+    if (!silc_hash_alloc("sha384", &h)) {
+      SILC_LOG_ERROR(("Unsupported algorithm sha384"));
+      return NULL;
+    }
+    break;
+
+  case SILC_PGP_HASH_SHA512:
+    if (!silc_hash_alloc("sha512", &h)) {
+      SILC_LOG_ERROR(("Unsupported algorithm sha512"));
+      return NULL;
+    }
+    break;
+
+  case SILC_PGP_HASH_SHA224:
+    if (!silc_hash_alloc("sha244", &h)) {
+      SILC_LOG_ERROR(("Unsupported algorithm sha224"));
+      return NULL;
+    }
+    break;
+
+  default:
+    return NULL;
+    break;
+  }
+
+  return h;
+}
+
+/************************* OpenPGP Packet routines **************************/
+
+#ifdef SILC_DEBUG
+/* Return packet tag as string */
+
+const char *silc_pgp_packet_name(SilcPGPPacketTag tag)
+{
+  if (tag == SILC_PGP_PACKET_PKENC_SK)
+    return "PKENC_SK";
+  if (tag == SILC_PGP_PACKET_SIGNATURE)
+    return "SIGNATURE";
+  if (tag == SILC_PGP_PACKET_SENC_SK)
+    return "SENC_SK";
+  if (tag == SILC_PGP_PACKET_OP_SIGNATURE)
+    return "OP_SIGNATUER";
+  if (tag == SILC_PGP_PACKET_SECKEY)
+    return "SECKEY";
+  if (tag == SILC_PGP_PACKET_PUBKEY)
+    return "PUBKEY";
+  if (tag == SILC_PGP_PACKET_SECKEY_SUB)
+    return "SECKEY_SUB";
+  if (tag == SILC_PGP_PACKET_COMP_DATA)
+    return "COMP_DATA";
+  if (tag == SILC_PGP_PACKET_SENC_DATA)
+    return "SENC_DATA";
+  if (tag == SILC_PGP_PACKET_MARKER)
+    return "MARKER";
+  if (tag == SILC_PGP_PACKET_LITERAL_DATA)
+    return "LITERAL_DATA";
+  if (tag == SILC_PGP_PACKET_TRUST)
+    return "TRUST";
+  if (tag == SILC_PGP_PACKET_USER_ID)
+    return "USER_ID";
+  if (tag == SILC_PGP_PACKET_PUBKEY_SUB)
+    return "PUBKEY_SUB";
+  if (tag == SILC_PGP_PACKET_USER_ATTR)
+    return "USER_ATTR";
+  if (tag == SILC_PGP_PACKET_SENC_I_DATA)
+    return "SENC_I_DATA";
+  if (tag == SILC_PGP_PACKET_MDC)
+    return "MDC";
+  return "UNKNOWN";
+}
+#endif /* SILC_DEBUG */
+
+/* Copy packet */
+
+SilcPGPPacket silc_pgp_packet_copy(SilcPGPPacket packet)
+{
+  SilcPGPPacket newpacket;
+  unsigned char *data;
+
+  newpacket = silc_calloc(1, sizeof(*newpacket));
+  if (!newpacket)
+    return NULL;
+
+  data = silc_memdup(packet->data.head, silc_buffer_truelen(&packet->data));
+  if (!data) {
+    silc_free(newpacket);
+    return NULL;
+  }
+
+  silc_buffer_set(&newpacket->data, data, silc_buffer_truelen(&packet->data));
+  newpacket->tag = packet->tag;
+
+  return newpacket;
+}
+
+/* Decode all PGP packets into a list */
+
+int silc_pgp_packet_decode(const unsigned char *data,
+                          SilcUInt32 data_len,
+                          SilcBool *success,
+                          SilcList *ret_list)
+{
+  SilcBufferStruct buf;
+  SilcPGPPacket packet;
+  int ret;
+
+  SILC_LOG_DEBUG(("Parsing OpenPGP packets"));
+
+  if (success)
+    *success = TRUE;
+
+  if (!data || data_len < 2)
+    return 0;
+
+  silc_buffer_set(&buf, (unsigned char *)data, data_len);
+  silc_list_init(*ret_list, struct SilcPGPPacketStruct, next);
+
+  /* Parse one by one */
+  while (silc_buffer_len(&buf) > 0) {
+    ret = silc_pgp_packet_parse(silc_buffer_data(&buf),
+                               silc_buffer_len(&buf), &packet);
+    if (!ret) {
+      if (success)
+       *success = FALSE;
+      break;
+    }
+
+    silc_buffer_pull(&buf, ret);
+    silc_list_add(*ret_list, packet);
+  }
+
+  SILC_LOG_DEBUG(("Parsed %d packets", silc_list_count(*ret_list)));
+
+  silc_list_start(*ret_list);
+
+  return silc_list_count(*ret_list);
+}
+
+/* Get PGP packet tag (packet type) */
+
+SilcPGPPacketTag silc_pgp_packet_get_tag(SilcPGPPacket packet)
+{
+  return packet->tag;
+}
+
+/* Get PGP packet data */
+
+unsigned char *silc_pgp_packet_get_data(SilcPGPPacket packet,
+                                       SilcUInt32 *data_len)
+{
+  unsigned char *ptr = silc_buffer_data(&packet->data);
+  if (data_len)
+    *data_len = silc_buffer_len(&packet->data);
+  return ptr;
+}
+
+/* Free PGP packet from  */
+
+void silc_pgp_packet_free(SilcPGPPacket packet)
+{
+  silc_buffer_purge(&packet->data);
+  silc_free(packet);
+}
+
+/* Free PGP packets from list */
+
+void silc_pgp_packet_free_list(SilcList *list)
+{
+  SilcPGPPacket packet;
+
+  silc_list_start(*list);
+  while ((packet = silc_list_get(*list))) {
+    silc_buffer_purge(&packet->data);
+    silc_free(packet);
+  }
+}
+
+/****************************** String to Key *******************************/
+
+/* PGP String-to-key.  Converts passphrases to encryption and decryption
+   keys.  This can be used to create both encryption and decryption key. */
+
+unsigned char *silc_pgp_s2k(SilcPGPS2KType type,
+                           SilcPGPHash hash,
+                           const char *passphrase,
+                           SilcUInt32 passphrase_len,
+                           SilcUInt32 key_len,
+                           unsigned char *salt,
+                           SilcUInt32 iter_octet_count,
+                           SilcRng rng)
+{
+  SilcHash h = NULL;
+  unsigned char *key = NULL, digest[SILC_HASH_MAXLEN], preload[8], esalt[8];
+  SilcUInt32 hash_len;
+  int i, k;
+
+  if (!passphrase)
+    return NULL;
+
+  SILC_LOG_DEBUG(("Compute S2K for %s", salt ? "decryption" : "encryption"));
+
+  h = silc_pgp_hash_alloc(hash);
+  if (!h)
+    return NULL;
+  hash_len = silc_hash_len(h);
+
+  key = silc_malloc(key_len);
+  if (!key)
+    goto err;
+
+  memset(preload, 0, sizeof(preload));
+  silc_hash_init(h);
+
+  /* If salt is NULL, we'll create one for encryption */
+  if (!salt) {
+    silc_rng_get_rn_data(rng, 8, esalt, sizeof(esalt));
+    salt = esalt;
+  }
+
+  switch (type) {
+  case SILC_PGP_S2K_SIMPLE:
+    /* Hash passphrase */
+    for (i = 0; i < key_len; i += hash_len) {
+      if (i && i < sizeof(preload)) {
+       silc_hash_init(h);
+       silc_hash_update(h, preload, i);
+      }
+
+      silc_hash_update(h, passphrase, passphrase_len);
+      silc_hash_final(h, digest);
+      memcpy(key + i, digest,
+            (key_len - i) > hash_len ? hash_len : key_len - i);
+    }
+    break;
+
+  case SILC_PGP_S2K_SALTED:
+    /* Hash passphrase with salt */
+    for (i = 0; i < key_len; i += hash_len) {
+      if (i && i < sizeof(preload)) {
+       silc_hash_init(h);
+       silc_hash_update(h, preload, i);
+      }
+
+      silc_hash_update(h, salt, 8);
+      silc_hash_update(h, passphrase, passphrase_len);
+      silc_hash_final(h, digest);
+      memcpy(key + i, digest,
+            (key_len - i) > hash_len ? hash_len : key_len - i);
+    }
+    break;
+
+  case SILC_PGP_S2K_ITERATED_SALTED:
+    /* Hash passphrase with salt iteratively.  This is very poorly defined
+       in the RFC. */
+    if (iter_octet_count < 8 + passphrase_len)
+      iter_octet_count = 8 + passphrase_len;
+
+    for (i = 0; i < key_len; i += hash_len) {
+      if (i && i < sizeof(preload)) {
+       silc_hash_init(h);
+       silc_hash_update(h, preload, i);
+      }
+
+      for (k = 0; k < iter_octet_count; k += (8 + passphrase_len)) {
+       if (iter_octet_count - k < 8) {
+         silc_hash_update(h, salt, iter_octet_count - k);
+       } else {
+         silc_hash_update(h, salt, 8);
+         if (iter_octet_count - k - 8 < passphrase_len)
+           silc_hash_update(h, passphrase, iter_octet_count - k - 8);
+         else
+           silc_hash_update(h, passphrase, passphrase_len);
+       }
+      }
+
+      silc_hash_final(h, digest);
+      memcpy(key + i, digest,
+            (key_len - i) > hash_len ? hash_len : key_len - i);
+    }
+    break;
+
+  default:
+    goto err;
+    break;
+  }
+
+  memset(digest, 0, sizeof(digest));
+  memset(esalt, 0, sizeof(esalt));
+  silc_hash_free(h);
+
+  return key;
+
+ err:
+  silc_hash_free(h);
+  silc_free(key);
+  return NULL;
+}
+
+/****************************** ASCII armoring ******************************/
+
+/* Adds ASCII armor */
+
+unsigned char *silc_pgp_armor(unsigned char *data,
+                             SilcUInt32 data_len)
+{
+  /* XXX TODO */
+  return NULL;
+}
+
+/* Removes ASCII armoring */
+
+unsigned char *silc_pgp_dearmor(unsigned char *data,
+                               SilcUInt32 data_len,
+                               SilcUInt32 *ret_len)
+{
+  int i, k;
+
+  if (data_len < 28)
+    return NULL;
+
+  if (memcmp(data, "-----BEGIN PGP ", 15))
+    return NULL;
+
+  /* Get beginning of base64 encoded data */
+  for (i = 0; i < data_len; i++) {
+    if (i + 3 < data_len && data[i] == '\n' && data[i + 1] == '\n') {
+      i += 2;
+      break;
+    }
+    if (i + 3 < data_len &&
+       data[i] == '\n' && data[i + 1] == ' ' && data[i + 2] == '\n') {
+      i += 3;
+      break;
+    }
+  }
+
+  /* Get end of base64 encoded data, ignore OpenPGP radix64 CRC */
+  for (k = i; k < data_len; k++) {
+    if (k + 1 < data_len && data[k] == '=') {
+      data_len -= (data_len - ++k);
+      break;
+    }
+  }
+
+  return silc_base64_decode(NULL, data + i, data_len, ret_len);
+}
diff --git a/lib/silcpgp/silcpgp.h b/lib/silcpgp/silcpgp.h
new file mode 100644 (file)
index 0000000..643a70a
--- /dev/null
@@ -0,0 +1,527 @@
+/*
+
+  silcpgp.h
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  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.
+
+*/
+
+/****h* silcpgp/OpenPGP Interface
+ *
+ * DESCRIPTION
+ *
+ * This implementation supports OpenPGP public key versions 2, 3 and 4.
+ * OpenPGP private key support exist only for version 4.  This means that
+ * this API can be used verify signatures with all versions of OpenPGP public
+ * keys, but signatures can only be computed with version 4 private keys.
+ * This implementation also only generates version 4 private keys.
+ *
+ * The interface implements the RFC 2440 and rfc2440bis-22 Internet Draft
+ * specifications.
+ *
+ ***/
+
+#ifndef SILCPGP_H
+#define SILCPGP_H
+
+/****s* silcpgp/SilcPGPPublicKey
+ *
+ * NAME
+ *
+ *    typedef struct { ... } *SilcPGPPublicKey;
+ *
+ * DESCRIPTION
+ *
+ *    This structure defines the OpenPGP public key (certificate).  This
+ *    context can be retrieved from SilcPublicKey by calling
+ *    silc_pkcs_public_key_get_pkcs for the PKCS type SILC_PKCS_OPENPGP.
+ *
+ * SOURCE
+ */
+typedef struct SilcPGPPublicKeyStruct {
+  SilcList packets;               /* Packets making this public key, contains
+                                     main key, subkeys, signatures etc. */
+  SilcDList subkeys;              /* Subkeys, each is SilcPGPPublicKey */
+
+  const SilcPKCSAlgorithm *pkcs;   /* PKCS Algorithm */
+  void *public_key;               /* PKCS Algorithm specific public key */
+
+  unsigned char key_id[8];        /* Public key ID */
+  unsigned char fingerprint[20];   /* Fingerprint of the public key */
+
+  SilcUInt32 created;             /* Time when public key was created */
+  SilcUInt16 valid;               /* Validity period (V3 keys) */
+  SilcUInt8 version;              /* Version, 2, 3 or 4 */
+  SilcUInt8 algorithm;            /* Algorithm, SilcPGPPKCSAlgorithm */
+} *SilcPGPPublicKey;
+/***/
+
+/****s* silcpgp/SilcPGPPrivateKey
+ *
+ * NAME
+ *
+ *    typedef struct { ... } *SilcPGPPrivateKey;
+ *
+ * DESCRIPTION
+ *
+ *    This structure defines the OpenPGP private key.  This context can be
+ *    retrieved from SilcPublicKey by calling silc_pkcs_private_key_get_pkcs
+ *    for the PKCS type SILC_PKCS_OPENPGP.
+ *
+ * SOURCE
+ */
+typedef struct SilcPGPPrivateKeyStruct {
+  SilcList packets;               /* Packets making this private key, contains
+                                     main key, subkeys, signatures etc. */
+  SilcDList subkeys;              /* Subkeys, each is SilcPGPPrivateKey */
+
+  SilcPGPPublicKey public_key;    /* Public key */
+  void *private_key;              /* Algorithm specific private key */
+
+  SilcUInt32 s2k_count;                   /* S2K iterate octet count */
+  SilcUInt8 cipher;               /* Cipher, SilcPGPCipher */
+  SilcUInt8 s2k_type;             /* S2K type, SilcPGPS2KType */
+  SilcUInt8 s2k_hash;             /* Hash, SilcPGPHash */
+} *SilcPGPPrivateKey;
+/***/
+
+/****s* silcpgp/SilcPGPPacket
+ *
+ * NAME
+ *
+ *    typedef struct SilcPGPPacketStruct *SilcPGPPacket;
+ *
+ * DESCRIPTION
+ *
+ *    OpenPGP packet context.  This context is allocated by calling
+ *    silc_pgp_packet_decode.
+ *
+ ***/
+typedef struct SilcPGPPacketStruct *SilcPGPPacket;
+
+/****d* silcpgp/SilcPGPPacketTag
+ *
+ * NAME
+ *
+ *    typedef enum { ... } SilcPGPPacketTag;
+ *
+ * DESCRIPTION
+ *
+ *    OpenPGP packet types.
+ *
+ * SOURCE
+ */
+typedef enum {
+  SILC_PGP_PACKET_PKENC_SK        = 1,   /* Public key enc session key */
+  SILC_PGP_PACKET_SIGNATURE       = 2,  /* Signature packet */
+  SILC_PGP_PACKET_SENC_SK         = 3,  /* Symmetric-key enc session key */
+  SILC_PGP_PACKET_OP_SIGNATURE    = 4,  /* One pass signature packet */
+  SILC_PGP_PACKET_SECKEY          = 5,  /* Secret key packet */
+  SILC_PGP_PACKET_PUBKEY          = 6,  /* Public key packet */
+  SILC_PGP_PACKET_SECKEY_SUB      = 7,  /* Secret subkey packet */
+  SILC_PGP_PACKET_COMP_DATA       = 8,  /* Compressed data packet */
+  SILC_PGP_PACKET_SENC_DATA       = 9,  /* Symmetrically enc data packet */
+  SILC_PGP_PACKET_MARKER          = 10,         /* Marker packet */
+  SILC_PGP_PACKET_LITERAL_DATA    = 11,         /* Literal data packet */
+  SILC_PGP_PACKET_TRUST           = 12,         /* Trust packet */
+  SILC_PGP_PACKET_USER_ID         = 13,         /* User ID packet */
+  SILC_PGP_PACKET_PUBKEY_SUB      = 14,         /* Public subkey packet */
+  SILC_PGP_PACKET_USER_ATTR       = 17,         /* User attribute packet */
+  SILC_PGP_PACKET_SENC_I_DATA     = 18,         /* Symmetric key enc/integ data */
+  SILC_PGP_PACKET_MDC             = 19,         /* Modification detection code */
+} SilcPGPPacketTag;
+/***/
+
+/****d* silcpgp/SilcPGPPKCSAlgorithm
+ *
+ * NAME
+ *
+ *    typedef enum { ... } SilcPGPPKCSAlgorithm;
+ *
+ * DESCRIPTION
+ *
+ *    OpenPGP public key cryptosystem algorithms.
+ *
+ * SOURCE
+ */
+typedef enum {
+  SILC_PGP_PKCS_RSA               = 1,   /* RSA */
+  SILC_PGP_PKCS_RSA_ENC_ONLY      = 2,   /* RSA encryption allowed only */
+  SILC_PGP_PKCS_RSA_SIG_ONLY      = 3,   /* RSA signatures allowed only */
+  SILC_PGP_PKCS_ELGAMAL_ENC_ONLY  = 16,         /* Elgamal encryption only */
+  SILC_PGP_PKCS_DSA               = 17,         /* DSA */
+  SILC_PGP_PKCS_ECDSA             = 19,         /* ECDSA */
+  SILC_PGP_PKCS_ELGAMAL           = 20,         /* Elgamal encryption/signatures */
+  SILC_PGP_PKCS_DH                = 21,         /* Diffie-Hellman */
+} SilcPGPPKCSAlgorithm;
+/***/
+
+/****d* silcpgp/SilcPGPCipher
+ *
+ * NAME
+ *
+ *    typedef enum { ... } SilcPGPCipher;
+ *
+ * DESCRIPTION
+ *
+ *    OpenPGP ciphers.
+ *
+ * SOURCE
+ */
+typedef enum {
+  SILC_PGP_CIPHER_NONE            = 0,   /* No cipher, plaintext */
+  SILC_PGP_CIPHER_IDEA            = 1,  /* IDEA */
+  SILC_PGP_CIPHER_3DES            = 2,  /* Triple-DES */
+  SILC_PGP_CIPHER_CAST5           = 3,  /* CAST5 (CAST-128) */
+  SILC_PGP_CIPHER_BLOWFISH        = 4,  /* Blowfish */
+  SILC_PGP_CIPHER_AES128          = 7,  /* AES 128-bit key */
+  SILC_PGP_CIPHER_AES192          = 8,  /* AES 192-bit key */
+  SILC_PGP_CIPHER_AES256          = 9,  /* AES 256-bit key */
+  SILC_PGP_CIPHER_TWOFISH         = 10,         /* Twofish 256-bit key */
+} SilcPGPCipher;
+/***/
+
+/****d* silcpgp/SilcPGPHash
+ *
+ * NAME
+ *
+ *    typedef enum { ... } SilcPGPHash;
+ *
+ * DESCRIPTION
+ *
+ *    OpenPGP hash functions.
+ *
+ * SOURCE
+ */
+typedef enum {
+  SILC_PGP_HASH_MD5               = 1,  /* MD5 */
+  SILC_PGP_HASH_SHA1              = 2,  /* SHA-1 */
+  SILC_PGP_HASH_RIPEMD160         = 3,  /* RIPE-MD160 */
+  SILC_PGP_HASH_SHA256            = 8,  /* SHA-256 */
+  SILC_PGP_HASH_SHA384            = 9,  /* SHA-394 */
+  SILC_PGP_HASH_SHA512            = 10,         /* SHA-512 */
+  SILC_PGP_HASH_SHA224            = 11,         /* SHA-224 */
+} SilcPGPHash;
+/***/
+
+/****d* silcpgp/SilcPGPS2KType
+ *
+ * NAME
+ *
+ *    typedef enum { ... } SilcPGPS2KType;
+ *
+ * DESCRIPTION
+ *
+ *    String-to-key (S2K) specifier types.  These define how the passphrase
+ *    is converted into encryption and decryption key.
+ *
+ * SOURCE
+ */
+typedef enum {
+  SILC_PGP_S2K_SIMPLE            = 0,   /* Simple S2K */
+  SILC_PGP_S2K_SALTED             = 1,  /* Salted S2K */
+  SILC_PGP_S2K_ITERATED_SALTED    = 3,  /* Iterated and salted S2K */
+} SilcPGPS2KType;
+/***/
+
+/****d* silcpgp/SilcPGPKeyType
+ *
+ * NAME
+ *
+ *    typedef enum { ... } SilcPGPKeyType;
+ *
+ * DESCRIPTION
+ *
+ *    PGP key generation types.  These types define what kind of PGP key
+ *    is created with silc_pgp_generate_key.
+ *
+ *    SILC_PGP_RSA
+ *
+ *      Generates RSA key that can be used for both signatures and encryption.
+ *      This is default.  If key type is not specified, this is used as
+ *      default key type.
+ *
+ *    SILC_PGP_DSA_SIG
+ *
+ *      Generates signature only DSA key.  The key cannot be used for
+ *      encryption.
+ *
+ *    SILC_PGP_ECDSA_SIG
+ *
+ *      Generates signature only ECDSA key.  The key cannot be used for
+ *      encryption.
+ *
+ *    SILC_PGP_DSA_SIG_ELGAMAL_ENC
+ *
+ *      Generates key with DSA for signatures and Elgamal for encryption.
+ *
+ *    SILC_PGP_DSA_SIG_RSA_ENC
+ *
+ *      Generates key with DSA for signatures and RSA for encryption.
+ *
+ * SOURCE
+ */
+typedef enum {
+  SILC_PGP_RSA                     = 0,         /* Generate RSA key */
+  SILC_PGP_DSA_SIG                 = 1,         /* Generate signature only DSA key */
+  SILC_PGP_ECDSA_SIG               = 2,         /* Generate signature only ECDSA key */
+  SILC_PGP_DSA_SIG_ELGAMAL_ENC     = 3,         /* Generate DSA and Elgamal key */
+  SILC_PGP_DSA_SIG_RSA_ENC         = 4,         /* Generate DSA and RSA key */
+} SilcPGPKeyType;
+/***/
+
+typedef struct SilcPgpKeygenParamsStruct {
+  SilcPGPKeyType type;
+  int key_len_bits;
+
+  int expire_days;
+  int expire_weeks;
+  int expire_months;
+  int expire_years;
+} SilcPgpKeygenParams;
+
+/* XXX TODO */
+SilcBool silc_pgp_generate_key(SilcPgpKeygenParams *params,
+                              const char *user_id,
+                              SilcRng rng,
+                              SilcPublicKey *ret_public_key,
+                              SilcPrivateKey *ret_private_key);
+
+/****f* silcpgp/silc_pgp_packet_decode
+ *
+ * SYNOPSIS
+ *
+ *    SilcBool silc_pgp_packet_decode(const unsigned char *data,
+ *                                    SilcUInt32 data_len,
+ *                                    SilcBool *success,
+ *                                    SilcList *ret_list);
+ *
+ * DESCRIPTION
+ *
+ *    Decodes PGP packets from the data buffer indicated by `data' of length
+ *    of `data_len' bytes.  The data buffer may include one or more packets
+ *    that are decoded and returned to the `ret_list'.  The caller must free
+ *    the returned packets with silc_pgp_packet_free_list.  Each entry in
+ *    the `ret_list' is SilcPGPPacket.
+ *
+ *    Returns the number of packets decoded or 0 on error.  If the `success'
+ *    is FALSE but this returns > 0 then not all packets were decoded
+ *    successfully and the `ret_list' includes the packets that were decoded.
+ *    When the `success' is TRUE all packets were decoded successfully.
+ *
+ * EXAMPLE
+ *
+ *    SilcList list;
+ *    SilcBool success;
+ *    unsigned char *data;
+ *    SilcUInt32 data_len;
+ *    SilcPGPPublicKey public_key;
+ *
+ *    // Open public key file (binary format) and parse all PGP packets
+ *    data = silc_file_readfile("pubkey.bin", &data_len, NULL);
+ *    silc_pgp_packet_decode(data, data_len, &success, &list);
+ *
+ *    // Parse public key
+ *    silc_pgp_public_key_decode(&list, &public_key);
+ *
+ ***/
+int silc_pgp_packet_decode(const unsigned char *data,
+                          SilcUInt32 data_len,
+                          SilcBool *success,
+                          SilcList *ret_list);
+
+/****f* silcpgp/silc_pgp_packet_get_tag
+ *
+ * SYNOPSIS
+ *
+ *    SilcPGPPacketTag silc_pgp_packet_get_tag(SilcPGPPacket packet);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the OpenPGP packet tag (packet type) from `packet'.
+ *
+ ***/
+SilcPGPPacketTag silc_pgp_packet_get_tag(SilcPGPPacket packet);
+
+/****f* silcpgp/silc_pgp_packet_get_data
+ *
+ * SYNOPSIS
+ *
+ *    unsigned char *silc_pgp_packet_get_data(SilcPGPPacket packet,
+ *                                            SilcUInt32 *data_len);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the packet data from the `packet'.  The returned pointer
+ *    must not be freed by the caller.  The length of the data is returned
+ *    into `data_len' pointer.
+ *
+ ***/
+unsigned char *silc_pgp_packet_get_data(SilcPGPPacket packet,
+                                       SilcUInt32 *data_len);
+
+/****f* silcpgp/silc_pgp_packet_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_pgp_packet_free(SilcPGPPacket packet);
+ *
+ * DESCRIPTION
+ *
+ *    Free PGP packet.
+ *
+ ***/
+void silc_pgp_packet_free(SilcPGPPacket packet);
+
+/****f* silcpgp/silc_pgp_packet_free_list
+ *
+ * SYNOPSIS
+ *
+ *    void silc_pgp_packet_free_list(SilcList *list);
+ *
+ * DESCRIPTION
+ *
+ *    Free all PGP packets from the `list'.  All packets in the list will
+ *    become invalid after this call.
+ *
+ ***/
+void silc_pgp_packet_free_list(SilcList *list);
+
+/****f* silcpgp/silc_pgp_public_key_decode
+ *
+ * SYNOPSIS
+ *
+ *    SilcBool silc_pgp_public_key_decode(SilcList *list,
+ *                                        SilcPGPPublicKey *ret_public_key);
+ *
+ * DESCRIPTION
+ *
+ *    Decodes OpenPGP public key (certificate) from decoded PGP packets list
+ *    indicated by `list'.  The decoded public key is returned into the
+ *    `ret_public_key' which the caller must free by calling the
+ *    silc_pgp_public_key_free function.  Returns FALSE on error.
+ *
+ *    The `list' can be allocated by calling silc_pgp_packet_decode.
+ *    If the `list' contains more that one public keys this only decodes
+ *    the first one.  The public key `list' is advanced while decoding the
+ *    public key.  If another public key follows the first public key, this
+ *    function may be called again to decode that public key as well.
+ *
+ ***/
+SilcBool silc_pgp_public_key_decode(SilcList *pubkey,
+                                   SilcPGPPublicKey *ret_public_key);
+
+/****f* silcpgp/silc_pgp_public_key_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_pgp_public_key_free(SilcPGPPublicKey public_key);
+ *
+ * DESCRIPTION
+ *
+ *    Frees the public key.
+ *
+ ***/
+void silc_pgp_public_key_free(SilcPGPPublicKey public_key);
+
+/****f* silcpgp/silc_pgp_private_key_decode
+ *
+ * SYNOPSIS
+ *
+ *    SilcBool silc_pgp_private_key_decode(SilcList *list,
+ *                                         const char *passphrase,
+ *                                         SilcUInt32 passphrase_len,
+ *                                         SilcPGPPrivateKey *ret_private_key);
+ *
+ * DESCRIPTION
+ *
+ *    Decodes OpenPGP secret key (private key) from decoded PGP packets list
+ *    indicated by `list'.  The decoded private key is returned into the
+ *    `ret_private_key' which the caller must free by calling the
+ *    silc_pgp_private_key_free function.  Returns FALSE on error.
+ *
+ *    The `passphrase' can be provided in case the private key is
+ *    encrypted.  Usually all OpenPGP private keys are encrypted so the
+ *    passphrase should be always provided.
+ *
+ *    The `list' can be allocated by calling silc_pgp_packet_decode.
+ *    If the `list' contains more that one private keys this only decodes
+ *    the first one.  The private key `list' is advanced while decoding the
+ *    public key.  If another private key follows the first public key, this
+ *    function may be called again to decode that private key as well.
+ *
+ ***/
+SilcBool silc_pgp_private_key_decode(SilcList *list,
+                                    const char *passphrase,
+                                    SilcUInt32 passphrase_len,
+                                    SilcPGPPrivateKey *ret_private_key);
+
+/****f* silcpgp/silc_pgp_private_key_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_pgp_private_key_free(SilcPGPPrivateKey private_key);
+ *
+ * DESCRIPTION
+ *
+ *    Frees the private key.
+ *
+ ***/
+void silc_pgp_private_key_free(SilcPGPPrivateKey private_key);
+
+/****f* silcpgp/silc_pgp_s2k
+ *
+ * SYNOPSIS
+ *
+ *    unsigned char *silc_pgp_s2k(SilcPGPS2KType type,
+ *                                SilcPGPHash hash,
+ *                                const char *passphrase,
+ *                                SilcUInt32 passphrase_len,
+ *                                SilcUInt32 key_len,
+ *                                unsigned char *salt,
+ *                                SilcUInt32 iter_octet_count,
+ *                                SilcRng rng);
+ *
+ * DESCRIPTION
+ *
+ *   Computes the OpenPGP string-to-key (S2K).  Converts passphrases to
+ *   encryption and decryption keys.  The `passphrase' must be non-NULL.
+ *
+ *   The `type' specifies the S2K specifier type.  The `hash' is the
+ *   hash algorithm used if the `type' is SILC_PGP_S2K_SALTED or
+ *   SILC_PGP_S2K_ITERATED_SALTED.  If the `type' is
+ *   SILC_PGP_S2K_ITERATED_SALTED the `iter_octet_count' is the number of
+ *   bytes to iteratively hash (max value is 65536).
+ *
+ *   The `key_len' is the length of the key to produce in bytes.  If `salt'
+ *   is NULL this will generate an encryption key.  If it is non-NULL this
+ *   will use the salt to compute the decryption key.
+ *
+ ***/
+unsigned char *silc_pgp_s2k(SilcPGPS2KType type,
+                           SilcPGPHash hash,
+                           const char *passphrase,
+                           SilcUInt32 passphrase_len,
+                           SilcUInt32 key_len,
+                           unsigned char *salt,
+                           SilcUInt32 iter_count,
+                           SilcRng rng);
+
+unsigned char *silc_pgp_dearmor(unsigned char *data,
+                               SilcUInt32 data_len,
+                               SilcUInt32 *ret_len);
+
+#include "silcpgp_i.h"
+
+#endif /* SILCPGP_H */
diff --git a/lib/silcpgp/silcpgp_i.h b/lib/silcpgp/silcpgp_i.h
new file mode 100644 (file)
index 0000000..7c95393
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+
+  silcpgp_i.h
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  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.
+
+*/
+
+#ifndef SILCPGP_I_H
+#define SILCPGP_I_H
+
+#ifndef SILCPGP_H
+#error "Do not include this header directly"
+#endif
+
+/* OpenPGP packet.  Contains the raw data and packet tag. */
+struct SilcPGPPacketStruct {
+  struct SilcPGPPacketStruct *next;
+  SilcBufferStruct data;
+  SilcUInt8 tag;
+};
+
+/* Armoring headers */
+#define SILC_PGP_ARMOR_MESSAGE   "BEGIN PGP MESSAGE"
+#define SILC_PGP_ARMOR_PUBKEY    "BEGIN PGP PUBLIC KEY BLOCK"
+#define SILC_PGP_ARMOR_PRIVKEY   "BEGIN PGP PRIVATE KEY BLOCK"
+#define SILC_PGP_ARMOR_SIGNATURE "BEGIN PGP SIGNATURE"
+
+SilcPGPPacket silc_pgp_packet_copy(SilcPGPPacket packet);
+SilcCipher silc_pgp_cipher_alloc(SilcPGPCipher cipher);
+int silc_pgp_packet_public_key_decode(unsigned char *key, SilcUInt32 key_len,
+                                     SilcPGPPublicKey pubkey);
+int silc_pgp_packet_private_key_decode(unsigned char *key, SilcUInt32 key_len,
+                                      const char *passphrase,
+                                      SilcUInt32 passphrase_len,
+                                      SilcPGPPrivateKey privkey);
+#ifdef SILC_DEBUG
+const char *silc_pgp_packet_name(SilcPGPPacketTag tag);
+#endif /* SILC_DEBUG */
+
+#endif /* SILCPGP_I_H */
diff --git a/lib/silcpgp/silcpgp_pkcs.c b/lib/silcpgp/silcpgp_pkcs.c
new file mode 100644 (file)
index 0000000..b653c54
--- /dev/null
@@ -0,0 +1,763 @@
+/*
+
+  silcpgp_pkcs.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  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"
+
+/**************************** OpenPGP PKCS API ******************************/
+
+/* Get algorithm context */
+
+SILC_PKCS_GET_ALGORITHM(silc_pkcs_pgp_get_algorithm)
+{
+  SilcPGPPublicKey pubkey = public_key;
+  return pubkey->pkcs;
+}
+
+/* Import PGP public key file */
+
+SILC_PKCS_IMPORT_PUBLIC_KEY_FILE(silc_pkcs_pgp_import_public_key_file)
+{
+  SilcList list;
+  SilcBool ret;
+  unsigned char *data = NULL;
+  SilcPGPPublicKey pubkey;
+
+  SILC_LOG_DEBUG(("Parsing OpenPGP public key file"));
+
+  if (!ret_public_key)
+    return FALSE;
+
+  switch (encoding) {
+  case SILC_PKCS_FILE_BIN:
+    break;
+
+  case SILC_PKCS_FILE_BASE64:
+    data = silc_pgp_dearmor(filedata, filedata_len, &filedata_len);
+    if (!data)
+      return FALSE;
+    filedata = data;
+    break;
+  }
+
+  /* Parse PGP packets */
+  if (!silc_pgp_packet_decode(filedata, filedata_len, NULL, &list)) {
+    silc_free(data);
+    return FALSE;
+  }
+  silc_free(data);
+
+  /* Parse the public key */
+  ret = silc_pgp_public_key_decode(&list, &pubkey);
+  if (ret) {
+    if (ret_alg)
+      *ret_alg = pubkey->pkcs;
+    if (ret_public_key)
+      *ret_public_key = pubkey;
+  }
+
+  silc_pgp_packet_free_list(&list);
+
+  return ret;
+}
+
+/* Import OpenPGP public key packet (OpenPGP certificate). */
+
+SILC_PKCS_IMPORT_PUBLIC_KEY(silc_pkcs_pgp_import_public_key)
+{
+  SilcPGPPublicKey pubkey;
+  int ret;
+
+  pubkey = silc_calloc(1, sizeof(*pubkey));
+  if (!pubkey)
+    return 0;
+
+  ret = silc_pgp_packet_public_key_decode(key, key_len, pubkey);
+  if (ret) {
+    if (ret_alg)
+      *ret_alg = pubkey->pkcs;
+    if (ret_public_key)
+      *ret_public_key = pubkey;
+  } else {
+    silc_free(pubkey);
+  }
+
+  return ret;
+}
+
+/* Export PGP public key file */
+
+SILC_PKCS_EXPORT_PUBLIC_KEY_FILE(silc_pkcs_pgp_export_public_key_file)
+{
+  return 0;
+}
+
+/* Export OpenPGP public key */
+
+SILC_PKCS_EXPORT_PUBLIC_KEY(silc_pkcs_pgp_export_public_key)
+{
+  return 0;
+}
+
+/* Return public key length in bits */
+
+SILC_PKCS_PUBLIC_KEY_BITLEN(silc_pkcs_pgp_public_key_bitlen)
+
+{
+  SilcPGPPublicKey pubkey = public_key;
+  return pubkey->pkcs->public_key_bitlen(pubkey->pkcs, pubkey->public_key);
+}
+
+/* Copy public key */
+
+SILC_PKCS_PUBLIC_KEY_COPY(silc_pkcs_pgp_public_key_copy)
+{
+  SilcPGPPublicKey pubkey = public_key, new_pubkey, p;
+  SilcPGPPacket packet;
+
+  new_pubkey = silc_calloc(1, sizeof(*new_pubkey));
+  if (!new_pubkey)
+    return NULL;
+
+  if (pubkey->subkeys) {
+    new_pubkey->subkeys = silc_dlist_init();
+    if (!new_pubkey->subkeys) {
+      silc_free(new_pubkey);
+      return NULL;
+    }
+
+    silc_dlist_start(pubkey->subkeys);
+    while ((p = silc_dlist_get(pubkey->subkeys))) {
+      p = silc_pkcs_pgp_public_key_copy(pkcs, p);
+      if (p)
+       silc_dlist_add(new_pubkey->subkeys, p);
+    }
+  }
+
+  silc_list_init(new_pubkey->packets, struct SilcPGPPacketStruct, next);
+  silc_list_start(pubkey->packets);
+  while ((packet = silc_list_get(pubkey->packets))) {
+    packet = silc_pgp_packet_copy(packet);
+    if (packet) {
+      silc_free(new_pubkey);
+      return NULL;
+    }
+    silc_list_add(new_pubkey->packets, packet);
+  }
+
+  memcpy(new_pubkey->key_id, pubkey->key_id, sizeof(pubkey->key_id));
+  memcpy(new_pubkey->fingerprint, pubkey->fingerprint,
+        sizeof(pubkey->fingerprint));
+  new_pubkey->created = pubkey->created;
+  new_pubkey->valid = pubkey->valid;
+  new_pubkey->version = pubkey->version;
+  new_pubkey->algorithm = pubkey->algorithm;
+
+  new_pubkey->public_key = pubkey->pkcs->public_key_copy(pubkey->pkcs,
+                                                        pubkey->public_key);
+  if (!new_pubkey->public_key) {
+    silc_free(new_pubkey);
+    return NULL;
+  }
+
+  return new_pubkey;
+}
+
+/* Compares public keys */
+
+SILC_PKCS_PUBLIC_KEY_COMPARE(silc_pkcs_pgp_public_key_compare)
+{
+  SilcPGPPublicKey k1 = key1, k2 = key2;
+
+  if (k1->version != k2->version)
+    return FALSE;
+  if (k1->created != k2->created)
+    return FALSE;
+  if (k1->valid != k2->valid)
+    return FALSE;
+  if (k1->algorithm != k2->algorithm)
+    return FALSE;
+  if (memcmp(k1->key_id, k2->key_id, sizeof(k1->key_id)))
+    return FALSE;
+  if (memcmp(k1->fingerprint, k2->fingerprint, sizeof(k1->fingerprint)))
+    return FALSE;
+
+  return k1->pkcs->public_key_compare(k1->pkcs,
+                                     k1->public_key, k2->public_key);
+}
+
+/* Free public key */
+
+SILC_PKCS_PUBLIC_KEY_FREE(silc_pkcs_pgp_public_key_free)
+{
+  silc_pgp_public_key_free(public_key);
+}
+
+/* Import PGP private key file */
+
+SILC_PKCS_IMPORT_PRIVATE_KEY_FILE(silc_pkcs_pgp_import_private_key_file)
+{
+  SilcList list;
+  SilcBool ret;
+  unsigned char *data = NULL;
+  SilcPGPPrivateKey privkey;
+
+  SILC_LOG_DEBUG(("Parsing OpenPGP private key file"));
+
+  if (!ret_private_key)
+    return FALSE;
+
+  switch (encoding) {
+  case SILC_PKCS_FILE_BIN:
+    break;
+
+  case SILC_PKCS_FILE_BASE64:
+    data = silc_pgp_dearmor(filedata, filedata_len, &filedata_len);
+    if (!data)
+      return FALSE;
+    filedata = data;
+    break;
+  }
+
+  /* Parse PGP packets */
+  if (!silc_pgp_packet_decode(filedata, filedata_len, NULL, &list)) {
+    silc_free(data);
+    return FALSE;
+  }
+  silc_free(data);
+
+  /* Parse the private key */
+  ret = silc_pgp_private_key_decode(&list, passphrase, passphrase_len,
+                                   &privkey);
+  if (ret) {
+    if (ret_alg)
+      *ret_alg = privkey->public_key->pkcs;
+    if (ret_private_key)
+      *ret_private_key = privkey;
+  }
+
+  silc_pgp_packet_free_list(&list);
+
+  return ret;
+}
+
+/* Import OpenPGP private key */
+
+SILC_PKCS_IMPORT_PRIVATE_KEY(silc_pkcs_pgp_import_private_key)
+{
+  SilcPGPPrivateKey privkey;
+  int ret;
+
+  privkey = silc_calloc(1, sizeof(*privkey));
+  if (!privkey)
+    return 0;
+
+  ret = silc_pgp_packet_private_key_decode(key, key_len, passphrase,
+                                          passphrase_len, privkey);
+  if (ret) {
+    if (ret_alg)
+      *ret_alg = privkey->public_key->pkcs;
+    if (ret_private_key)
+      *ret_private_key = privkey;
+  } else {
+    silc_free(privkey);
+  }
+
+  return ret;
+}
+
+/* Export PGP private key file */
+
+SILC_PKCS_EXPORT_PRIVATE_KEY_FILE(silc_pkcs_pgp_export_private_key_file)
+{
+  return 0;
+}
+
+/* Export OpenPGP private key */
+
+SILC_PKCS_EXPORT_PRIVATE_KEY(silc_pkcs_pgp_export_private_key)
+{
+  return 0;
+}
+
+/* Returns key length in bits */
+
+SILC_PKCS_PRIVATE_KEY_BITLEN(silc_pkcs_pgp_private_key_bitlen)
+{
+  SilcPGPPrivateKey privkey = private_key;
+  return silc_pkcs_pgp_public_key_bitlen(pkcs, privkey->public_key);
+}
+
+/* Free private key */
+
+SILC_PKCS_PRIVATE_KEY_FREE(silc_pkcs_pgp_private_key_free)
+{
+  SilcPGPPrivateKey privkey = private_key;
+  silc_pgp_private_key_free(privkey);
+}
+
+/* Encrypt */
+
+SILC_PKCS_ENCRYPT(silc_pkcs_pgp_encrypt)
+{
+  return 0;
+}
+
+/* Decrypt */
+
+SILC_PKCS_DECRYPT(silc_pkcs_pgp_decrypt)
+{
+  return 0;
+}
+
+/* Sign */
+
+SILC_PKCS_SIGN(silc_pkcs_pgp_sign)
+{
+  return 0;
+}
+
+/* Verify */
+
+SILC_PKCS_VERIFY(silc_pkcs_pgp_verify)
+{
+  return 0;
+}
+
+/************************** OpenPGP RSA PKCS API ****************************/
+
+/* Import OpenPGP compliant RSA public key */
+
+SILC_PKCS_ALG_IMPORT_PUBLIC_KEY(silc_pgp_rsa_import_public_key)
+{
+  SilcBufferStruct alg_key;
+  RsaPublicKey *pubkey;
+  unsigned char *n, *e;
+  SilcUInt16 n_len, e_len;
+
+  if (!ret_public_key)
+    return 0;
+
+  /* Allocate RSA public key */
+  *ret_public_key = pubkey = silc_calloc(1, sizeof(*pubkey));
+  if (!pubkey)
+    return FALSE;
+
+  /* Parse OpenPGP RSA public key */
+  silc_buffer_set(&alg_key, key, key_len);
+  if (silc_buffer_unformat(&alg_key,
+                          SILC_STR_ADVANCE,
+                          SILC_STR_UINT16(&n_len),
+                          SILC_STR_END) < 0)
+    goto err;
+
+  n_len = (n_len + 7) / 8;
+  if (!n_len)
+    goto err;
+
+  if (silc_buffer_unformat(&alg_key,
+                          SILC_STR_ADVANCE,
+                          SILC_STR_DATA(&n, n_len),
+                          SILC_STR_UINT16(&e_len),
+                          SILC_STR_END) < 0)
+    goto err;
+
+  e_len = (e_len + 7) / 8;
+  if (!e_len)
+    goto err;
+
+  if (silc_buffer_unformat(&alg_key,
+                          SILC_STR_ADVANCE,
+                          SILC_STR_DATA(&e, e_len),
+                          SILC_STR_END) < 0)
+    goto err;
+
+  /* Get MP integers */
+  silc_mp_init(&pubkey->n);
+  silc_mp_init(&pubkey->e);
+  silc_mp_bin2mp(n, n_len, &pubkey->n);
+  silc_mp_bin2mp(e, e_len, &pubkey->e);
+
+  /* Set key length */
+  pubkey->bits = silc_mp_sizeinbase(&pubkey->n, 2);
+
+  return silc_buffer_headlen(&alg_key);
+
+ err:
+  silc_free(pubkey);
+  return 0;
+}
+
+/* Export OpenPGP compliant RSA public key */
+
+SILC_PKCS_ALG_EXPORT_PUBLIC_KEY(silc_pgp_rsa_export_public_key)
+{
+  RsaPublicKey *pubkey = public_key;
+  SilcBufferStruct alg_key;
+  unsigned char *n = NULL, *e = NULL, *ret;
+  SilcUInt16 n_len, e_len;
+
+  n_len = silc_mp_sizeinbase(&pubkey->n, 2);
+  e_len = silc_mp_sizeinbase(&pubkey->e, 2);
+
+  /* Encode MP integers */
+  n = silc_mp_mp2bin(&pubkey->n, 0, NULL);
+  if (!n)
+    goto err;
+  e = silc_mp_mp2bin(&pubkey->e, 0, NULL);
+  if (!e)
+    goto err;
+
+  memset(&alg_key, 0, sizeof(alg_key));
+  if (silc_buffer_format(&alg_key,
+                        SILC_STR_UINT16(n_len),
+                        SILC_STR_DATA(n, n_len),
+                        SILC_STR_UINT16(e_len),
+                        SILC_STR_DATA(e, e_len),
+                        SILC_STR_END) < 0)
+    goto err;
+
+  silc_free(n);
+  silc_free(e);
+
+  ret = silc_buffer_steal(&alg_key, ret_len);
+  return ret;
+
+ err:
+  silc_free(n);
+  silc_free(e);
+  return NULL;
+}
+
+/* Import OpenPGP compliant RSA private key */
+
+SILC_PKCS_ALG_IMPORT_PRIVATE_KEY(silc_pgp_rsa_import_private_key)
+{
+  SilcBufferStruct alg_key;
+  RsaPrivateKey *privkey;
+  unsigned char *d, *p, *q, *u;
+  SilcUInt16 d_len, p_len, q_len, u_len;
+  SilcMPInt pm1, qm1;
+
+  if (!ret_private_key)
+    return 0;
+
+  /* Allocate RSA private key */
+  *ret_private_key = privkey = silc_calloc(1, sizeof(*privkey));
+  if (!privkey)
+    goto err;
+
+  /* Parse OpenPGP RSA private key.  In OpenPGP the u is p^-1 mod q, but
+     our RSA implementation expects q^-1 mod p (PKCS#1 compliant), thus
+     we reverse p and q to make it work. */
+  silc_buffer_set(&alg_key, key, key_len);
+  if (silc_buffer_unformat(&alg_key,
+                          SILC_STR_ADVANCE,
+                          SILC_STR_UINT16(&d_len),
+                          SILC_STR_END) < 0)
+    goto err;
+
+  d_len = (d_len + 7) / 8;
+  if (!d_len)
+    goto err;
+
+  if (silc_buffer_unformat(&alg_key,
+                          SILC_STR_ADVANCE,
+                          SILC_STR_DATA(&d, d_len),
+                          SILC_STR_UINT16(&q_len),
+                          SILC_STR_END) < 0)
+    goto err;
+
+  q_len = (q_len + 7) / 8;
+  if (!q_len)
+    goto err;
+
+  if (silc_buffer_unformat(&alg_key,
+                          SILC_STR_ADVANCE,
+                          SILC_STR_DATA(&q, q_len),
+                          SILC_STR_UINT16(&p_len),
+                          SILC_STR_END) < 0)
+    goto err;
+
+  p_len = (p_len + 7) / 8;
+  if (!p_len)
+    goto err;
+
+  if (silc_buffer_unformat(&alg_key,
+                          SILC_STR_ADVANCE,
+                          SILC_STR_DATA(&p, p_len),
+                          SILC_STR_UINT16(&u_len),
+                          SILC_STR_END) < 0)
+    goto err;
+
+  u_len = (u_len + 7) / 8;
+  if (!u_len)
+    goto err;
+
+  if (silc_buffer_unformat(&alg_key,
+                          SILC_STR_ADVANCE,
+                          SILC_STR_DATA(&u, u_len),
+                          SILC_STR_END) < 0)
+    goto err;
+
+  /* Get MP integers */
+  silc_mp_init(&privkey->d);
+  silc_mp_init(&privkey->p);
+  silc_mp_init(&privkey->q);
+  silc_mp_init(&privkey->qP);
+  silc_mp_bin2mp(d, d_len, &privkey->d);
+  silc_mp_bin2mp(p, p_len, &privkey->p);
+  silc_mp_bin2mp(q, q_len, &privkey->q);
+  silc_mp_bin2mp(u, u_len, &privkey->qP);
+
+  /* Fill in missing integers and pre-compute */
+  silc_mp_init(&pm1);
+  silc_mp_init(&qm1);
+  silc_mp_init(&privkey->n);
+  silc_mp_init(&privkey->e);
+  silc_mp_init(&privkey->dP);
+  silc_mp_init(&privkey->dQ);
+  silc_mp_mul(&privkey->n, &privkey->p, &privkey->q);
+  silc_mp_sub_ui(&pm1, &privkey->p, 1);
+  silc_mp_sub_ui(&qm1, &privkey->q, 1);
+  silc_mp_mod(&privkey->dP, &privkey->d, &pm1);
+  silc_mp_mod(&privkey->dQ, &privkey->d, &qm1);
+  silc_mp_uninit(&pm1);
+  silc_mp_uninit(&qm1);
+
+  /* Set key length */
+  privkey->bits = silc_mp_sizeinbase(&privkey->n, 2);
+
+  return silc_buffer_headlen(&alg_key);
+
+ err:
+  silc_free(privkey);
+  return 0;
+}
+
+/* Export OpenPGP compliant RSA private key */
+
+SILC_PKCS_ALG_EXPORT_PRIVATE_KEY(silc_pgp_rsa_export_private_key)
+{
+  RsaPrivateKey *privkey = private_key;
+  SilcBufferStruct alg_key;
+  unsigned char *d = NULL, *p = NULL, *q = NULL, *u = NULL, *ret;
+  SilcUInt16 d_len, p_len, q_len, u_len;
+
+  /* In OpenPGP the u is p^-1 mod q, but our RSA implementation uses
+     q^-1 mod p (PKCS#1 compliant), thus we reverse p and q to make the
+     key correct. */
+  d_len = silc_mp_sizeinbase(&privkey->d, 2);
+  p_len = silc_mp_sizeinbase(&privkey->q, 2);
+  q_len = silc_mp_sizeinbase(&privkey->p, 2);
+  u_len = silc_mp_sizeinbase(&privkey->qP, 2);
+
+  /* Encode MP integers */
+  d = silc_mp_mp2bin(&privkey->d, 0, NULL);
+  if (!d)
+    goto err;
+  p = silc_mp_mp2bin(&privkey->q, 0, NULL);
+  if (!p)
+    goto err;
+  q = silc_mp_mp2bin(&privkey->p, 0, NULL);
+  if (!q)
+    goto err;
+  u = silc_mp_mp2bin(&privkey->qP, 0, NULL);
+  if (!u)
+    goto err;
+
+  memset(&alg_key, 0, sizeof(alg_key));
+  if (silc_buffer_format(&alg_key,
+                        SILC_STR_UINT16(d_len),
+                        SILC_STR_DATA(d, d_len),
+                        SILC_STR_UINT16(p_len),
+                        SILC_STR_DATA(p, p_len),
+                        SILC_STR_UINT16(q_len),
+                        SILC_STR_DATA(q, q_len),
+                        SILC_STR_UINT16(u_len),
+                        SILC_STR_DATA(u, u_len),
+                        SILC_STR_END) < 0)
+    goto err;
+
+  silc_free(d);
+  silc_free(p);
+  silc_free(q);
+  silc_free(u);
+
+  ret = silc_buffer_steal(&alg_key, ret_len);
+  return ret;
+
+ err:
+  silc_free(d);
+  silc_free(p);
+  silc_free(q);
+  silc_free(u);
+  return NULL;
+}
+
+/************************** OpenPGP DSA PKCS API ****************************/
+
+/* Import OpenPGP compliant DSA public key */
+
+SILC_PKCS_ALG_IMPORT_PUBLIC_KEY(silc_pgp_dsa_import_public_key)
+{
+  SilcBufferStruct alg_key;
+  DsaPublicKey *pubkey;
+  unsigned char *p, *q, *g, *y;
+  SilcUInt16 p_len, q_len, g_len, y_len;
+
+  if (!ret_public_key)
+    return 0;
+
+  /* Allocate DSA public key */
+  *ret_public_key = pubkey = silc_calloc(1, sizeof(*pubkey));
+  if (!pubkey)
+    return FALSE;
+
+  /* Parse OpenPGP DSA public key */
+  silc_buffer_set(&alg_key, key, key_len);
+  if (silc_buffer_unformat(&alg_key,
+                          SILC_STR_ADVANCE,
+                          SILC_STR_UINT16(&p_len),
+                          SILC_STR_END) < 0)
+    goto err;
+
+  p_len = (p_len + 7) / 8;
+  if (!p_len)
+    goto err;
+
+  if (silc_buffer_unformat(&alg_key,
+                          SILC_STR_ADVANCE,
+                          SILC_STR_DATA(&p, p_len),
+                          SILC_STR_UINT16(&q_len),
+                          SILC_STR_END) < 0)
+    goto err;
+
+  q_len = (q_len + 7) / 8;
+  if (!q_len)
+    goto err;
+
+  if (silc_buffer_unformat(&alg_key,
+                          SILC_STR_ADVANCE,
+                          SILC_STR_DATA(&q, q_len),
+                          SILC_STR_UINT16(&g_len),
+                          SILC_STR_END) < 0)
+    goto err;
+
+  g_len = (g_len + 7) / 8;
+  if (!g_len)
+    goto err;
+
+  if (silc_buffer_unformat(&alg_key,
+                          SILC_STR_ADVANCE,
+                          SILC_STR_DATA(&g, g_len),
+                          SILC_STR_UINT16(&y_len),
+                          SILC_STR_END) < 0)
+    goto err;
+
+  y_len = (y_len + 7) / 8;
+  if (!y_len)
+    goto err;
+
+  if (silc_buffer_unformat(&alg_key,
+                          SILC_STR_ADVANCE,
+                          SILC_STR_DATA(&y, y_len),
+                          SILC_STR_END) < 0)
+    goto err;
+
+  /* Get MP integers */
+  silc_mp_init(&pubkey->p);
+  silc_mp_init(&pubkey->q);
+  silc_mp_init(&pubkey->g);
+  silc_mp_init(&pubkey->y);
+  silc_mp_bin2mp(p, p_len, &pubkey->p);
+  silc_mp_bin2mp(q, q_len, &pubkey->q);
+  silc_mp_bin2mp(g, g_len, &pubkey->g);
+  silc_mp_bin2mp(y, y_len, &pubkey->y);
+
+  /* Set key length */
+  pubkey->bits = silc_mp_sizeinbase(&pubkey->p, 2);
+
+  return silc_buffer_headlen(&alg_key);
+
+ err:
+  silc_free(pubkey);
+  return 0;
+}
+
+/* Export OpenPGP compliant DSA public key */
+
+SILC_PKCS_ALG_EXPORT_PUBLIC_KEY(silc_pgp_dsa_export_public_key)
+{
+  return 0;
+}
+
+/* Import OpenPGP compliant DSA private key */
+
+SILC_PKCS_ALG_IMPORT_PRIVATE_KEY(silc_pgp_dsa_import_private_key)
+{
+  SilcBufferStruct alg_key;
+  DsaPrivateKey *privkey;
+  unsigned char *x;
+  SilcUInt16 x_len;
+
+  if (!ret_private_key)
+    return 0;
+
+  /* Allocate DSA private key */
+  *ret_private_key = privkey = silc_calloc(1, sizeof(*privkey));
+  if (!privkey)
+    goto err;
+
+  /* Parse OpenPGP DSA private key. */
+  silc_buffer_set(&alg_key, key, key_len);
+  if (silc_buffer_unformat(&alg_key,
+                          SILC_STR_ADVANCE,
+                          SILC_STR_UINT16(&x_len),
+                          SILC_STR_END) < 0)
+    goto err;
+
+  x_len = (x_len + 7) / 8;
+  if (!x_len)
+    goto err;
+
+  if (silc_buffer_unformat(&alg_key,
+                          SILC_STR_ADVANCE,
+                          SILC_STR_DATA(&x, x_len),
+                          SILC_STR_END) < 0)
+    goto err;
+
+  /* Get MP integers */
+  silc_mp_init(&privkey->x);
+  silc_mp_bin2mp(x, x_len, &privkey->x);
+
+  return silc_buffer_headlen(&alg_key);
+
+ err:
+  silc_free(privkey);
+  return 0;
+}
+
+/* Export OpenPGP compliant DSA private key */
+
+SILC_PKCS_ALG_EXPORT_PRIVATE_KEY(silc_pgp_dsa_export_private_key)
+{
+  return 0;
+}
diff --git a/lib/silcpgp/silcpgp_pkcs.h b/lib/silcpgp/silcpgp_pkcs.h
new file mode 100644 (file)
index 0000000..5b73278
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+
+  silcpgp_pkcs.h
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 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.
+
+*/
+
+#ifndef SILCPGP_PKCS_H
+#define SILCPGP_PKCS_H
+
+SILC_PKCS_GET_ALGORITHM(silc_pkcs_pgp_get_algorithm);
+SILC_PKCS_IMPORT_PUBLIC_KEY_FILE(silc_pkcs_pgp_import_public_key_file);
+SILC_PKCS_IMPORT_PUBLIC_KEY(silc_pkcs_pgp_import_public_key);
+SILC_PKCS_EXPORT_PUBLIC_KEY_FILE(silc_pkcs_pgp_export_public_key_file);
+SILC_PKCS_EXPORT_PUBLIC_KEY(silc_pkcs_pgp_export_public_key);
+SILC_PKCS_PUBLIC_KEY_BITLEN(silc_pkcs_pgp_public_key_bitlen);
+SILC_PKCS_PUBLIC_KEY_COPY(silc_pkcs_pgp_public_key_copy);
+SILC_PKCS_PUBLIC_KEY_COMPARE(silc_pkcs_pgp_public_key_compare);
+SILC_PKCS_PUBLIC_KEY_FREE(silc_pkcs_pgp_public_key_free);
+SILC_PKCS_IMPORT_PRIVATE_KEY_FILE(silc_pkcs_pgp_import_private_key_file);
+SILC_PKCS_IMPORT_PRIVATE_KEY(silc_pkcs_pgp_import_private_key);
+SILC_PKCS_EXPORT_PRIVATE_KEY_FILE(silc_pkcs_pgp_export_private_key_file);
+SILC_PKCS_EXPORT_PRIVATE_KEY(silc_pkcs_pgp_export_private_key);
+SILC_PKCS_PRIVATE_KEY_BITLEN(silc_pkcs_pgp_private_key_bitlen);
+SILC_PKCS_PRIVATE_KEY_FREE(silc_pkcs_pgp_private_key_free);
+SILC_PKCS_ENCRYPT(silc_pkcs_pgp_encrypt);
+SILC_PKCS_DECRYPT(silc_pkcs_pgp_decrypt);
+SILC_PKCS_SIGN(silc_pkcs_pgp_sign);
+SILC_PKCS_VERIFY(silc_pkcs_pgp_verify);
+
+SILC_PKCS_ALG_IMPORT_PUBLIC_KEY(silc_pgp_rsa_import_public_key);
+SILC_PKCS_ALG_EXPORT_PUBLIC_KEY(silc_pgp_rsa_export_public_key);
+SILC_PKCS_ALG_IMPORT_PRIVATE_KEY(silc_pgp_rsa_import_private_key);
+SILC_PKCS_ALG_EXPORT_PRIVATE_KEY(silc_pgp_rsa_export_private_key);
+
+SILC_PKCS_ALG_IMPORT_PUBLIC_KEY(silc_pgp_dsa_import_public_key);
+SILC_PKCS_ALG_EXPORT_PUBLIC_KEY(silc_pgp_dsa_export_public_key);
+SILC_PKCS_ALG_IMPORT_PRIVATE_KEY(silc_pgp_dsa_import_private_key);
+SILC_PKCS_ALG_EXPORT_PRIVATE_KEY(silc_pgp_dsa_export_private_key);
+
+#endif /* SILCPGP_PKCS_H */
diff --git a/lib/silcpgp/silcpgp_pubkey.c b/lib/silcpgp/silcpgp_pubkey.c
new file mode 100644 (file)
index 0000000..c33fbae
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+
+  silcpgp_pubkey.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  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);
+}
diff --git a/lib/silcpgp/silcpgp_seckey.c b/lib/silcpgp/silcpgp_seckey.c
new file mode 100644 (file)
index 0000000..acfddbc
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+
+  silcpgp_seckey.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  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"
+
+/*************************** Private Key Routines ***************************/
+
+/* Decode OpenPGP Secret Key packet */
+
+int silc_pgp_packet_private_key_decode(unsigned char *key, SilcUInt32 key_len,
+                                      const char *passphrase,
+                                      SilcUInt32 passphrase_len,
+                                      SilcPGPPrivateKey privkey)
+{
+  SilcBufferStruct keybuf;
+  SilcPGPPublicKey pubkey = NULL;
+  unsigned char *iv, *salt = NULL, *dec = NULL;
+  unsigned char *dec_key;
+  SilcUInt32 iv_len = 0;
+  SilcUInt16 pcksum;
+  SilcUInt8 s2k_usage, s2k_count;
+  SilcCipher cipher = NULL;
+  int ret, ret_len;
+
+  SILC_LOG_DEBUG(("Parsing OpenPGP private key"));
+
+  if (!key || !key_len)
+    return 0;
+  silc_buffer_set(&keybuf, key, key_len);
+
+  SILC_LOG_HEXDUMP(("OpenPGP private key"), key, key_len);
+
+  pubkey = silc_calloc(1, sizeof(*pubkey));
+  if (!pubkey) {
+    silc_free(privkey);
+    return 0;
+  }
+
+  /* Parse public key from the private key */
+  ret = silc_pgp_packet_public_key_decode(key, key_len, pubkey);
+  if (!ret) {
+    SILC_LOG_DEBUG(("Malformed private key"));
+    goto err;
+  }
+  if (!silc_buffer_pull(&keybuf, ret))
+    goto err;
+
+  if (silc_buffer_len(&keybuf) < 12)
+    goto err;
+
+  /* Decode algorithm info */
+  s2k_usage = keybuf.data[0];
+  silc_buffer_pull(&keybuf, 1);
+  switch (s2k_usage) {
+  case 0:
+    /* Plaintext private key */
+    SILC_LOG_DEBUG(("Private key is not encrypted"));
+    break;
+
+  case 254:
+  case 255:
+    /* Encrypted, string-to-key (S2K) specifier present */
+    if (silc_buffer_unformat(&keybuf,
+                            SILC_STR_ADVANCE,
+                            SILC_STR_UINT8(&privkey->cipher),
+                            SILC_STR_UINT8(&privkey->s2k_type),
+                            SILC_STR_UINT8(&privkey->s2k_hash),
+                            SILC_STR_END) < 0) {
+      SILC_LOG_DEBUG(("Malformed S2K specifier in private key"));
+      goto err;
+    }
+
+    SILC_LOG_DEBUG(("Private key S2K type %d", privkey->s2k_type));
+
+    switch (privkey->s2k_type) {
+    case SILC_PGP_S2K_SIMPLE:
+      /* Simple S2K */
+      iv_len = 0;
+      break;
+
+    case SILC_PGP_S2K_SALTED:
+      /* Salted S2K */
+      if (silc_buffer_unformat(&keybuf,
+                              SILC_STR_ADVANCE,
+                              SILC_STR_DATA(&salt, 8),
+                              SILC_STR_END) < 0) {
+       SILC_LOG_DEBUG(("Malformed S2K specifier in private key"));
+       goto err;
+      }
+      break;
+
+    case SILC_PGP_S2K_ITERATED_SALTED:
+      /* Iterated and salted S2K */
+      if (silc_buffer_unformat(&keybuf,
+                              SILC_STR_ADVANCE,
+                              SILC_STR_DATA(&salt, 8),
+                              SILC_STR_UINT8(&s2k_count),
+                              SILC_STR_END) < 0) {
+       SILC_LOG_DEBUG(("Malformed S2K specifier in private key"));
+       goto err;
+      }
+
+      /* Get the iterator octet count, formula comes from the RFC */
+      privkey->s2k_count = ((SilcUInt32)16 +
+                           (s2k_count & 15)) << ((s2k_count >> 4) + 6);
+      break;
+
+    default:
+      SILC_LOG_DEBUG(("Malformed private key"));
+      goto err;
+    }
+
+    break;
+
+  default:
+    /* Encrypted with given algorithm */
+    privkey->cipher = keybuf.data[0];
+    silc_buffer_pull(&keybuf, 1);
+    break;
+  }
+
+  ret_len = silc_buffer_headlen(&keybuf);
+
+  /* Decrypt */
+  if (privkey->cipher) {
+    cipher = silc_pgp_cipher_alloc(privkey->cipher);
+    if (!cipher)
+      goto err;
+
+    iv_len = silc_cipher_get_iv_len(cipher);
+
+    /* Get IV */
+    if (!silc_buffer_unformat(&keybuf,
+                             SILC_STR_ADVANCE,
+                             SILC_STR_DATA(&iv, iv_len),
+                             SILC_STR_END)) {
+      SILC_LOG_DEBUG(("Malformed private key, IV not present"));
+      goto err;
+    }
+    ret_len += iv_len;
+
+    SILC_LOG_HEXDUMP(("IV, iv_len %d", iv_len), iv, iv_len);
+
+    /* Generate decryption key from passphrase */
+    dec_key = silc_pgp_s2k(privkey->s2k_type, privkey->s2k_hash, passphrase,
+                          passphrase_len, silc_cipher_get_key_len(cipher) / 8,
+                          salt, privkey->s2k_count, NULL);
+    if (!dec_key)
+      goto err;
+
+    SILC_LOG_HEXDUMP(("S2K"), dec_key, silc_cipher_get_key_len(cipher) / 8);
+
+    /* Set decryption key */
+    silc_cipher_set_key(cipher, dec_key, silc_cipher_get_key_len(cipher),
+                       FALSE);
+    silc_cipher_set_iv(cipher, iv);
+
+    /* Decrypt the private key */
+    SILC_LOG_DEBUG(("Decrypting private key"));
+    dec = silc_memdup(silc_buffer_data(&keybuf), silc_buffer_len(&keybuf));
+    if (!dec)
+      goto err;
+    silc_buffer_set(&keybuf, dec, silc_buffer_len(&keybuf));
+
+    if (pubkey->version >= 4) {
+      silc_cipher_decrypt(cipher, keybuf.data, keybuf.data,
+                         silc_buffer_len(&keybuf), NULL);
+    } else {
+      /* Versions 2 and 3 */
+      /* Support may be added for these at some point. */
+      SILC_LOG_ERROR(("Version %d encrypted private keys not supported",
+                     pubkey->version));
+      goto err;
+    }
+  }
+
+  /* Verify checksum to see if decryption succeeded */
+  if (s2k_usage == 254) {
+    SilcHash sha1;
+    unsigned char cksum_hash[20], pcksum_hash[20];
+
+    if (!silc_buffer_push_tail(&keybuf, 20)) {
+      SILC_LOG_DEBUG(("Malformed private key, checksum not present"));
+      goto err;
+    }
+
+    memcpy(pcksum_hash, keybuf.tail, 20);
+
+    if (!silc_hash_alloc("sha1", &sha1))
+      goto err;
+    silc_hash_init(sha1);
+    silc_hash_update(sha1, silc_buffer_data(&keybuf),
+                    silc_buffer_len(&keybuf));
+    silc_hash_final(sha1, cksum_hash);
+    silc_hash_free(sha1);
+
+    /* Verify */
+    if (memcmp(cksum_hash, pcksum_hash, sizeof(cksum_hash))) {
+      SILC_LOG_DEBUG(("Private key checksum invalid, decryption failed"));
+      goto err;
+    }
+
+    ret_len += 20;
+  } else {
+    SilcUInt16 cksum = 0;
+    int i;
+
+    if (silc_buffer_unformat(&keybuf,
+                            SILC_STR_ADVANCE,
+                            SILC_STR_UINT16(&pcksum),
+                            SILC_STR_END) < 0) {
+      SILC_LOG_DEBUG(("Malformed private key, checksum not present"));
+      goto err;
+    }
+
+    for (i = 0; i < silc_buffer_len(&keybuf); i++)
+      cksum = (cksum + keybuf.data[i]) % 0x10000;
+
+    /* Verify */
+    if (cksum != pcksum) {
+      SILC_LOG_DEBUG(("Private key checksum invalid, decryption failed"));
+      goto err;
+    }
+
+    ret_len += 2;
+  }
+
+  /* Import the algorithm private key */
+  ret = pubkey->pkcs->import_private_key(pubkey->pkcs,
+                                        silc_buffer_data(&keybuf),
+                                        silc_buffer_len(&keybuf),
+                                        &privkey->private_key);
+  if (!ret) {
+    SILC_LOG_DEBUG(("Malformed private key"));
+    goto err;
+  }
+
+  silc_free(dec);
+
+  privkey->public_key = pubkey;
+
+  return ret_len + ret;
+
+ err:
+  if (pubkey)
+    silc_pgp_public_key_free(pubkey);
+  silc_free(dec);
+  return 0;
+}
+
+/* Decode private key from PGP packets */
+
+SilcBool silc_pgp_private_key_decode(SilcList *list,
+                                    const char *passphrase,
+                                    SilcUInt32 passphrase_len,
+                                    SilcPGPPrivateKey *ret_private_key)
+{
+  SilcPGPPrivateKey privkey, subkey;
+  unsigned char *data;
+  SilcUInt32 data_len;
+  SilcPGPPacket prv, packet;
+
+  SILC_LOG_DEBUG(("Parse OpenPGP private key"));
+
+  privkey = silc_calloc(1, sizeof(*privkey));
+  if (!privkey)
+    goto err;
+
+  /* First packet must be private key packet */
+  prv = silc_list_get(*list);
+  if (!prv)
+    goto err;
+  if (silc_pgp_packet_get_tag(prv) != SILC_PGP_PACKET_SECKEY &&
+      silc_pgp_packet_get_tag(prv) != SILC_PGP_PACKET_SECKEY_SUB)
+    goto err;
+
+  /* Parse the private key */
+  data = silc_pgp_packet_get_data(prv, &data_len);
+  if (!silc_pgp_packet_private_key_decode(data, data_len, passphrase,
+                                         passphrase_len, privkey))
+    goto err;
+
+  /* Parse any and all packets until we hit end of the packets or next
+     private 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(prv) == SILC_PGP_PACKET_SECKEY) {
+    silc_list_init(privkey->packets, struct SilcPGPPacketStruct, next);
+
+    /* Copy the raw private key packet */
+    packet = silc_pgp_packet_copy(prv);
+    if (packet)
+      silc_list_add(privkey->packets, packet);
+
+    while ((packet = silc_list_get(*list))) {
+      SILC_LOG_DEBUG(("Adding %d (%s) packet to private 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_SECKEY:
+       /* Next private key, stop decoding.  Set list pointer so that the list
+          points to the next private key. */
+       list->current = packet;
+       break;
+
+      case SILC_PGP_PACKET_SECKEY_SUB:
+       /* Parse subkeys recursively */
+       list->current = packet;
+       if (!silc_pgp_private_key_decode(list, passphrase,
+                                        passphrase_len, &subkey))
+         goto err;
+
+       if (!privkey->subkeys) {
+         privkey->subkeys = silc_dlist_init();
+         if (!privkey->subkeys)
+           goto err;
+       }
+       silc_dlist_add(privkey->subkeys, subkey);
+
+      default:
+       /* Copy packet to the private key */
+       packet = silc_pgp_packet_copy(packet);
+       if (packet)
+         silc_list_add(privkey->packets, packet);
+       break;
+      }
+    }
+  }
+
+  if (ret_private_key)
+    *ret_private_key = privkey;
+
+  return TRUE;
+
+ err:
+  silc_free(privkey);
+  return FALSE;
+}
+
+/* Free private key */
+
+void silc_pgp_private_key_free(SilcPGPPrivateKey private_key)
+{
+  SilcPGPPrivateKey p;
+  SilcPGPPacket packet;
+
+  if (private_key->public_key && private_key->public_key->pkcs)
+    private_key->public_key->pkcs->private_key_free(private_key->
+                                                   public_key->pkcs,
+                                                   private_key->private_key);
+
+  silc_pgp_public_key_free(private_key->public_key);
+
+  if (private_key->subkeys) {
+    silc_dlist_start(private_key->subkeys);
+    while ((p = silc_dlist_get(private_key->subkeys)))
+      silc_pgp_private_key_free(p);
+    silc_dlist_uninit(private_key->subkeys);
+  }
+
+  silc_list_start(private_key->packets);
+  while ((packet = silc_list_get(private_key->packets)))
+    silc_pgp_packet_free(packet);
+
+  silc_free(private_key);
+}
diff --git a/lib/silcpgp/tests/Makefile.am b/lib/silcpgp/tests/Makefile.am
new file mode 100644 (file)
index 0000000..375f1aa
--- /dev/null
@@ -0,0 +1,27 @@
+#
+#  Makefile.am
+#
+#  Author: Pekka Riikonen <priikone@silcnet.org>
+#
+#  Copyright (C) 2007 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.
+#
+
+AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
+
+bin_PROGRAMS =         test_silcpgp
+
+test_silcpgp_SOURCES = test_silcpgp.c
+
+LIBS = $(SILC_COMMON_LIBS)
+LDADD = -L.. -L../.. -lsct
+
+include $(top_srcdir)/Makefile.defines.in
diff --git a/lib/silcpgp/tests/pubkey.asc b/lib/silcpgp/tests/pubkey.asc
new file mode 100644 (file)
index 0000000..cda8717
--- /dev/null
@@ -0,0 +1,29 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.7 (GNU/Linux)
+
+mQINBEewruUBEAChMm51MHCvl0dOveNAI0MedPt+1alW7wLJ/NmuIXxb8Xt0G/TU
+ZIpn9e40Op7F2PQsxTlUWV7XFFaop0KPqikAu6oKfrWC2DP355CXsBoqto/qI+Em
+Bqu9nPXMAM4g1FlKghSmzF8vrnD2v9gvF+vlKrDJycaR68/uN8307+bfuqIWEjeo
+l9BGhB85qGo7/2OwHBxX/laxInGoTgthdgOuYjqNNjDday6unw6HC/xEPI3uVsDI
+2WKdbOGKGj5DHVCquX/wZr3Nldn3zxlZts4GenFq0fV97n9HncMr4qg5UdlE8pe2
+pSxf7V+XqShq19uH1b6g/zbhtFFxCRah7I3BJXTzeVoW1T0daY/OsluPe34sIwz9
+QQ7zV42p+k465YpcmZ0SaU3abnWfdv0cr8z7p7sjxMDkfxR+xs++eFDgv1t6CNKz
+oNoZHTZsZNwJiQPQteWvobD6+L90S0XSxI1o342lx2BR/uI25KcKUW59S2JQfnpi
+64MwcBl/8wOMoIXAiGERQrHQhWJpI6v8QL6d1JWCiA2YnAXtLnaJf3ScIQ6nE4hX
+1lNDREtVUHAvRQYrXUDn7NWYcaA0GKf3Sc8yF72299iN9r7seb5RhtufNPvQ+bYS
+M87mcEqsLAURPQR36tjBT87rg1G96UTyh9IHpKSywwEY8ZUMf4wxEOSq0QARAQAB
+tBxUZXN0IEtleSAoRm9vYmFyKSA8dGVzdEBrZXk+iQI8BBMBAgAmBQJHsK7lAhsD
+BQkQ6s+ABgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQ81qWM3L9V1qgDA//RhZi
+Y/PvOFqo6BjRWckDyUtTVM7fX+SgUjjBtmMbRDEOzl0AMRIGDC49v7U3+FBwnu39
+xUoEQThDqUK1IcSucdPBK8ce93jqE4r+TZm2zaQ0sjmVEZHQtHZ8+LYnerbWHLJ3
+xCe0dnLAlfOUfNmxhq0BR3A6UlkEYsh4e5/7j9JzgYU6ENe982kysdzs/3Y4NDtB
+V1VU2flxnFuAAiPWM61paU+X6CqQjt050rFRbK7DtgA8fE9UEk15fif2G5TknjDD
+WWTaw/1ddHxgoQYcp+cRCyy2XyFVJkjnHZGECbhjC96LV5oq4m9izBRS5hnE4ffH
+KhMrl9fLmWTQ8qyE8gt23xMCKukuuyUxy2AaJywPbh2PMcaCzaYhacJ9s+keuIRr
+9E9GoYzioxUUqm9YWXvGwDJMK58gMVh5N2beap9XaXqbanWNkeqAfJrscTVJpSf3
+655vGaIJDTEgpgBW7iJHAVhYK5c3nxhSxln2u8b7+3DPIAr7XXE+4KWWElebHwB6
+5ixougRWt0hsEY4WAoubsWTaVDWPozhHPtXCLDhbSpcTic0yFjFdU31gzHczo4av
+ZlU3DwQbTcoX9A4fOl/goYUgO3nzzv1q6opT7qB4w4Ri4cpi71CSxYg3dxt2ula/
+cAE7HHtQSXCO+XbBpbS6J3CwAFEYjlCaDUs4+0g=
+=cdNW
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/lib/silcpgp/tests/seckey.prv b/lib/silcpgp/tests/seckey.prv
new file mode 100644 (file)
index 0000000..200abd9
Binary files /dev/null and b/lib/silcpgp/tests/seckey.prv differ
diff --git a/lib/silcpgp/tests/test_silcpgp.c b/lib/silcpgp/tests/test_silcpgp.c
new file mode 100644 (file)
index 0000000..ed9c324
--- /dev/null
@@ -0,0 +1,42 @@
+/* SILC PGP library tests */
+
+#include "silccrypto.h"
+
+int main(int argc, char **argv)
+{
+  SilcBool success = FALSE;
+  SilcRng rng;
+  SilcPublicKey public_key;
+  SilcPrivateKey private_key;
+
+  if (argc > 1 && !strcmp(argv[1], "-d")) {
+    silc_log_debug(TRUE);
+    silc_log_quick(TRUE);
+    silc_log_debug_hexdump(TRUE);
+    silc_log_set_debug_string("*pgp*,*pkcs1*,*asn1*,*rsa*,*dsa*");
+  }
+
+  silc_crypto_init(NULL);
+  rng = silc_rng_alloc();
+  silc_rng_init(rng);
+
+  SILC_LOG_DEBUG(("Load private key"));
+  if (!silc_pkcs_load_private_key("seckey.prv", "foobar", 6,
+                                 SILC_PKCS_ANY, &private_key))
+    goto err;
+
+  SILC_LOG_DEBUG(("Load public key"));
+  if (!silc_pkcs_load_public_key("pubkey.asc", SILC_PKCS_ANY,  &public_key))
+    goto err;
+
+  silc_rng_free(rng);
+  silc_crypto_uninit();
+
+  success = TRUE;
+
+ err:
+  SILC_LOG_DEBUG(("Testing was %s", success ? "SUCCESS" : "FAILURE"));
+  fprintf(stderr, "Testing was %s\n", success ? "SUCCESS" : "FAILURE");
+
+  return success;
+}
index 767abbfe6f4383f88005b79e7f2c1b7f594c908d..d119b3d5d94143ecc08ad5b73369282323361593 100644 (file)
@@ -573,8 +573,9 @@ SILC_PKCS_IMPORT_PRIVATE_KEY_FILE(silc_pkcs_ssh_import_private_key_file)
   }
 
   /* Decode the private key */
-  ret = silc_pkcs_ssh_import_private_key(pkcs, alg, filedata, filedata_len,
-                                        (void *)&privkey, ret_alg);
+  ret = silc_pkcs_ssh_import_private_key(pkcs, alg, NULL, 0, filedata, 
+                                        filedata_len, (void *)&privkey,
+                                        ret_alg);
   silc_free(data);
 
   if (ret) {