Added support for encrypted private key files. The passphrase
authorPekka Riikonen <priikone@silcnet.org>
Sat, 2 Nov 2002 20:06:34 +0000 (20:06 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Sat, 2 Nov 2002 20:06:34 +0000 (20:06 +0000)
is now used when creating new key pair, and if necessary prompted
for when loading the key pair.

CHANGES
TODO
apps/irssi/src/silc/core/clientutil.c
apps/irssi/src/silc/core/silc-core.c
apps/silcd/server.c
apps/silcd/serverconfig.c
apps/silcd/silcd.c
lib/silccrypt/silcpkcs.c
lib/silccrypt/silcpkcs.h
lib/silcutil/silcapputil.c
lib/silcutil/silcapputil.h

diff --git a/CHANGES b/CHANGES
index 19e6d29b0ea716a356b421bd7368d7d97a2a1186..0e76816de27ef66bed62be7b8b8c016d52554af8 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,20 @@
+Sat Nov  2 21:26:56 EET 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added support for encrypted private key files.  Now
+         passphrase must be provided when new key pair is created
+         (can be empty though), and prompted when loading the
+         private key.  Added support for loading the old-style
+         non-encrypted private keys as well.  Affected files
+         lib/silccrypt/silcpkcs.[ch], Irssi SILC client and
+         SILC Server.
+
+       * Fixed silc_get_input to accept input on all terminals.
+         Affected file lib/silcutil/silcutil.c.
+
+       * Moved the Irssi SILC client key loading and other stuff
+         to be called after the arguments and UI is initialized.
+         Affected file irssi/src/silc/core/silc-core.c.  Bug #31.
+
 Sat Nov  2 12:53:09 EET 2002  Pekka Riikonen <priikone@silcnet.org>
 
        * Fixed connection closing in client library to not crash.
diff --git a/TODO b/TODO
index 2e5e405946170c4ddb51c2e87930b2987e706160..d1c34b1eb3bdbcd2a6322d0c5d93ae08d920916d 100644 (file)
--- a/TODO
+++ b/TODO
@@ -36,9 +36,6 @@ TODO for SILC Server 1.0
 TODO/bugs In SILC Libraries
 ===========================
 
- o Private key encryption to silc_pkcs_[save/load]_private_key[_data]
-   function.
-
  o WIN32 silc_net_create_connection_async does not work the same way
    than on Unix.  Do it with threads on WIN32.  The function works but
    is not actually async currently (Fix this to 0.9.x).
index cdc0fc9ee3bb9fdd9bbccc4e33dcf3ae9991210a..818444dd3e52e636ef70f1998b2999478a8e5438 100644 (file)
@@ -238,7 +238,7 @@ int silc_client_check_silc_dir()
       silc_create_key_pair(SILC_CLIENT_DEF_PKCS,
                           SILC_CLIENT_DEF_PKCS_LEN,
                           file_public_key, file_private_key, NULL,
-                          NULL, NULL, NULL, FALSE);
+                          NULL, NULL, NULL, NULL, FALSE);
       printf("Press <Enter> to continue...\n");
       getchar();
     } else {
@@ -260,7 +260,7 @@ int silc_client_check_silc_dir()
       silc_create_key_pair(SILC_CLIENT_DEF_PKCS,
                           SILC_CLIENT_DEF_PKCS_LEN,
                           file_public_key, file_private_key, NULL,
-                          NULL, NULL, NULL, FALSE);
+                          NULL, NULL, NULL, NULL, FALSE);
       printf("Press <Enter> to continue...\n");
       getchar();
     } else {
@@ -312,7 +312,7 @@ int silc_client_check_silc_dir()
       silc_create_key_pair(SILC_CLIENT_DEF_PKCS,
                           SILC_CLIENT_DEF_PKCS_LEN,
                           file_public_key, file_private_key, NULL,
-                          NULL, NULL, NULL, FALSE);
+                          NULL, NULL, NULL, NULL, FALSE);
       printf("Press <Enter> to continue...\n");
       getchar();
     } else {
@@ -335,6 +335,7 @@ int silc_client_load_keys(SilcClient client)
 {
   char pub[256], prv[256];
   struct passwd *pw;
+  bool ret;
 
   SILC_LOG_DEBUG(("Loading public and private keys"));
 
@@ -350,6 +351,13 @@ int silc_client_load_keys(SilcClient client)
   snprintf(pub, sizeof(pub) - 1, "%s/%s",
           get_irssi_dir(), SILC_CLIENT_PUBLIC_KEY_NAME);
   
-  return silc_load_key_pair(pub, prv, &client->pkcs, &client->public_key,
-                           &client->private_key);
+  /* Try loading first with "" passphrase, for those that didn't set
+     passphrase for private key, and only if that fails let it prompt
+     for passphrase. */
+  ret = silc_load_key_pair(pub, prv, "", &client->pkcs, &client->public_key,
+                          &client->private_key);
+  if (!ret)
+    ret = silc_load_key_pair(pub, prv, NULL, &client->pkcs,
+                            &client->public_key, &client->private_key);
+  return ret;
 }
index aec63667c6766ae70332e0fb9133638054f22d01..b7b9b005810043db925054af4d97e055a3801032 100644 (file)
@@ -310,11 +310,32 @@ void silc_opt_callback(poptContext con,
     silc_hash_register_default();
     silc_hmac_register_default();
     silc_create_key_pair(opt_pkcs, opt_bits, NULL, NULL, NULL,
-                        NULL, NULL, NULL, TRUE);
+                        NULL, NULL, NULL, NULL, TRUE);
     exit(0);
   }
 }
 
+static void sig_init_finished(void)
+{
+  /* Check ~/.silc directory and public and private keys */
+  if (!silc_client_check_silc_dir()) {
+    idletag = -1;
+    exit(1);
+  }
+
+  /* Load public and private key */
+  if (!silc_client_load_keys(silc_client)) {
+    idletag = -1;
+    exit(1);
+  }
+
+  /* Initialize the SILC client */
+  if (!silc_client_init(silc_client)) {
+    idletag = -1;
+    exit(1);
+  }
+}
+
 /* Init SILC. Called from src/fe-text/silc.c */
 
 void silc_core_init(void)
@@ -383,6 +404,7 @@ void silc_core_init(void)
 #endif
 
   signal_add("setup changed", (SIGNAL_FUNC) sig_setup_changed);
+  signal_add("irssi init finished", (SIGNAL_FUNC) sig_init_finished);
 
   silc_init_userinfo();
 
@@ -411,24 +433,6 @@ void silc_core_init(void)
   silc_client->hostname = silc_net_localhost();
   silc_client->realname = g_strdup(settings_get_str("real_name"));
 
-  /* Check ~/.silc directory and public and private keys */
-  if (silc_client_check_silc_dir() == FALSE) {
-    idletag = -1;
-    return;
-  }
-
-  /* Load public and private key */
-  if (silc_client_load_keys(silc_client) == FALSE) {
-    idletag = -1;
-    return;
-  }
-
-  /* Initialize the SILC client */
-  if (!silc_client_init(silc_client)) {
-    idletag = -1;
-    return;
-  }
-
   silc_log_set_callback(SILC_LOG_INFO, silc_log_misc, NULL);
   silc_log_set_callback(SILC_LOG_WARNING, silc_log_misc, NULL);
   silc_log_set_callback(SILC_LOG_ERROR, silc_log_misc, NULL);
@@ -473,6 +477,7 @@ void silc_core_deinit(void)
     signal_emit("chat protocol deinit", 1,
                chat_protocol_find("SILC"));
     signal_remove("setup changed", (SIGNAL_FUNC) sig_setup_changed);
+    signal_remove("irssi init finished", (SIGNAL_FUNC) sig_init_finished);
 
     silc_server_deinit();
     silc_channels_deinit();
index b1d06032f9436940d5b4284cd3b8ed7d88da39c2..42b5e78b039db4278ec1c94d75002d2945f82427 100644 (file)
@@ -988,8 +988,12 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
                             silc_server_connect_to_router_retry,
                             context, 0, 1, SILC_TASK_TIMEOUT,
                             SILC_TASK_PRI_NORMAL);
-    else
+    else {
       silc_server_config_unref(&sconn->conn);
+      silc_free(sconn->remote_host);
+      silc_free(sconn->backup_replace_ip);
+      silc_free(sconn);
+    }
     return;
   }
 
index 39f00afe0d912ecf5d759d054d319f245b07daff..0bd9ab57861f6b2acc6dc33489b0c366e2ed1480 100644 (file)
@@ -536,9 +536,9 @@ SILC_CONFIG_CALLBACK(fetch_serverinfo)
 
     /* try to load specified file, if fail stop config parsing */
     if (!silc_pkcs_load_private_key(file_tmp, &server_info->private_key,
-                                   SILC_PKCS_FILE_BIN))
+                                   "", 0, SILC_PKCS_FILE_BIN))
       if (!silc_pkcs_load_private_key(file_tmp, &server_info->private_key,
-                                     SILC_PKCS_FILE_PEM)) {
+                                     "", 0, SILC_PKCS_FILE_PEM)) {
        SILC_SERVER_LOG_ERROR(("Error: Could not load private key file."));
        return SILC_CONFIG_EPRINTLINE;
       }
@@ -1202,11 +1202,11 @@ static const SilcConfigTable table_routerconn[] = {
 };
 
 static const SilcConfigTable table_main[] = {
-  { "general",         SILC_CONFIG_ARG_BLOCK,  NULL,          table_general },
   { "cipher",          SILC_CONFIG_ARG_BLOCK,  fetch_cipher,  table_cipher },
   { "hash",            SILC_CONFIG_ARG_BLOCK,  fetch_hash,    table_hash },
   { "hmac",            SILC_CONFIG_ARG_BLOCK,  fetch_hmac,    table_hmac },
   { "pkcs",            SILC_CONFIG_ARG_BLOCK,  fetch_pkcs,    table_pkcs },
+  { "general",         SILC_CONFIG_ARG_BLOCK,  NULL,          table_general },
   { "serverinfo",      SILC_CONFIG_ARG_BLOCK,  fetch_serverinfo, table_serverinfo },
   { "logging",         SILC_CONFIG_ARG_BLOCK,  NULL,          table_logging },
   { "connectionparams",        SILC_CONFIG_ARG_BLOCK,  fetch_connparam, table_connparam },
index c110a649b3be9aa0b5737eacee7a7c9d71f21b0d..b0155df1edd9990043a43a63324a18cb3297812b 100644 (file)
@@ -526,7 +526,7 @@ int main(int argc, char **argv)
     silc_hash_register_default();
     silc_hmac_register_default();
     silc_create_key_pair(opt_pkcs, opt_bits, pubfile, prvfile,
-                        opt_identifier, NULL, NULL, NULL, FALSE);
+                        opt_identifier, "", NULL, NULL, NULL, FALSE);
     exit(0);
   }
 
@@ -539,12 +539,25 @@ int main(int argc, char **argv)
   if (ret == FALSE)
     goto fail;
 
+  /* Register default crypto stuff since we are going to need them 
+     in the configuration file parsing phase */
+  silc_cipher_register_default();
+  silc_pkcs_register_default();
+  silc_hash_register_default();
+  silc_hmac_register_default();
+
   /* Read configuration files */
   silcd->config = silc_server_config_alloc(silcd_config_file);
   if (silcd->config == NULL)
     goto fail;
   silcd->config_file = silcd_config_file;
 
+  /* Unregister the default crypto stuff so that configuration takes effect */
+  silc_cipher_unregister_all();
+  silc_pkcs_unregister_all();
+  silc_hash_unregister_all();
+  silc_hmac_unregister_all();
+
   /* Check for another silcd running */
   silc_server_checkpid(silcd);
 
index f3282f92f23715484881ed7fecdf93b27296c322..891e9afaeb443ec35fa566707fa3f7025f2d1154 100644 (file)
@@ -904,6 +904,7 @@ silc_pkcs_private_key_encode(SilcPrivateKey private_key, SilcUInt32 *len)
 
   ret = silc_calloc(buf->len, sizeof(*ret));
   memcpy(ret, buf->data, buf->len);
+  silc_buffer_clear(buf);
   silc_buffer_free(buf);
 
   return ret;
@@ -933,6 +934,7 @@ silc_pkcs_private_key_data_encode(unsigned char *prv, SilcUInt32 prv_len,
 
   ret = silc_calloc(buf->len, sizeof(*ret));
   memcpy(ret, buf->data, buf->len);
+  silc_buffer_clear(buf);
   silc_buffer_free(buf);
 
   return ret;
@@ -960,11 +962,15 @@ bool silc_pkcs_private_key_decode(unsigned char *data, SilcUInt32 data_len,
     silc_buffer_unformat(buf,
                         SILC_STR_UI16_NSTRING_ALLOC(&pkcs_name, &pkcs_len),
                         SILC_STR_END);
-  if (ret == -1)
+  if (ret == -1) {
+    SILC_LOG_DEBUG(("Cannot decode private key buffer"));
     goto err;
+  }
 
-  if (pkcs_len < 1 || pkcs_len > buf->truelen)
+  if (pkcs_len < 1 || pkcs_len > buf->truelen) {
+    SILC_LOG_DEBUG(("Malformed private key buffer"));
     goto err;
+  }
 
   /* See if we support this algorithm (check only if PKCS are registered). */
   if (SILC_PKCS_LIST && !silc_pkcs_is_supported(pkcs_name)) {
@@ -986,8 +992,10 @@ bool silc_pkcs_private_key_decode(unsigned char *data, SilcUInt32 data_len,
      (check only if PKCS are registered) */
   if (SILC_PKCS_LIST) {
     silc_pkcs_alloc(pkcs_name, &alg);
-    if (!alg->pkcs->set_private_key(alg->context, key_data, key_len))
+    if (!alg->pkcs->set_private_key(alg->context, key_data, key_len)) {
+      SILC_LOG_DEBUG(("Could not set private key data"));
       goto err;
+    }
     silc_pkcs_free(alg);
   }
   
@@ -998,6 +1006,7 @@ bool silc_pkcs_private_key_decode(unsigned char *data, SilcUInt32 data_len,
     (*private_key)->prv_len = key_len;
   }
 
+  silc_buffer_clear(buf);
   silc_buffer_free(buf);
   return TRUE;
 
@@ -1006,6 +1015,7 @@ bool silc_pkcs_private_key_decode(unsigned char *data, SilcUInt32 data_len,
     silc_free(pkcs_name);
   if (key_data)
     silc_free(key_data);
+  silc_buffer_clear(buf);
   silc_buffer_free(buf);
   return FALSE;
 }
@@ -1057,10 +1067,13 @@ bool silc_pkcs_save_public_key(char *filename, SilcPublicKey public_key,
 {
   unsigned char *data;
   SilcUInt32 data_len;
+  bool ret;
 
   data = silc_pkcs_public_key_encode(public_key, &data_len);
-  return silc_pkcs_save_public_key_internal(filename, data, data_len,
-                                           encoding);
+  ret = silc_pkcs_save_public_key_internal(filename, data, data_len,
+                                          encoding);
+  silc_free(data);
+  return ret;
 }
 
 /* Saves public key into file */
@@ -1073,17 +1086,123 @@ bool silc_pkcs_save_public_key_data(char *filename, unsigned char *data,
                                            encoding);
 }
 
+#define SILC_PKCS_PRIVATE_KEY_MAGIC 0x738df531
+
 /* Internal routine to save private key. */
 
 static bool silc_pkcs_save_private_key_internal(char *filename,
                                                unsigned char *data,
                                                SilcUInt32 data_len,
+                                               unsigned char *key,
+                                               SilcUInt32 key_len,
                                                SilcUInt32 encoding)
 {
-  SilcBuffer buf;
-  SilcUInt32 len;
+  SilcCipher aes;
+  SilcHash md5;
+  SilcHmac sha1hmac;
+  SilcBuffer buf, enc;
+  SilcUInt32 len, blocklen;
+  unsigned char tmp[32], keymat[32];
+  int i;
 
-  switch(encoding) {
+  memset(tmp, 0, sizeof(tmp));
+  memset(keymat, 0, sizeof(keymat));
+
+  /* Allocate the AES cipher */
+  if (!silc_cipher_alloc("aes-256-cbc", &aes)) {
+    SILC_LOG_ERROR(("Could not allocate AES cipher, probably not registered"));
+    return FALSE;
+  }
+  blocklen = silc_cipher_get_block_len(aes);
+  if (blocklen * 2 > sizeof(tmp))
+    return FALSE;
+
+  /* Allocate MD5 hash */
+  if (!silc_hash_alloc("md5", &md5)) {
+    SILC_LOG_ERROR(("Could not allocate MD5 hash, probably not registered"));
+    silc_cipher_free(aes);
+    return FALSE;
+  }
+
+  /* Allocate HMAC */
+  if (!silc_hmac_alloc("hmac-sha1-96", NULL, &sha1hmac)) {
+    SILC_LOG_ERROR(("Could not allocate SHA1 HMAC, probably not registered"));
+    silc_hash_free(md5);
+    silc_cipher_free(aes);
+    return FALSE;
+  }
+
+  /* Derive the encryption key from the provided key material.  The key
+     is 256 bits length, and derived by taking hash of the data, then 
+     re-hashing the data and the previous digest, and using the first and
+     second digest as the key. */
+  silc_hash_init(md5);
+  silc_hash_update(md5, key, key_len);
+  silc_hash_final(md5, keymat);
+  silc_hash_init(md5);
+  silc_hash_update(md5, key, key_len);
+  silc_hash_update(md5, keymat, 16);
+  silc_hash_final(md5, keymat + 16);
+
+  /* Set the key to the cipher */
+  silc_cipher_set_key(aes, keymat, sizeof(keymat) * 8);
+
+  /* Encode the buffer to be encrypted.  Add padding to it too, at least
+     block size of the cipher. */
+
+  /* Allocate buffer for encryption */
+  len = silc_hmac_len(sha1hmac);
+  enc = silc_buffer_alloc_size(data_len + 4 + 4 +
+                              (blocklen + (data_len % blocklen)) + len);
+  if (!enc) {
+    silc_hmac_free(sha1hmac);
+    silc_hash_free(md5);
+    silc_cipher_free(aes);
+    return FALSE;
+  }
+
+  /* Generate padding */
+  for (i = 0; i < blocklen + (data_len % blocklen); i++)
+    tmp[i] = silc_rng_global_get_byte_fast();
+
+  /* Put magic number */
+  SILC_PUT32_MSB(SILC_PKCS_PRIVATE_KEY_MAGIC, enc->data);
+  silc_buffer_pull(enc, 4);
+
+  /* Encode the buffer */
+  silc_buffer_format(enc,
+                    SILC_STR_UI_INT(data_len),
+                    SILC_STR_UI_XNSTRING(data, data_len),
+                    SILC_STR_UI_XNSTRING(tmp, blocklen + (data_len %
+                                                          blocklen)),
+                    SILC_STR_END);
+
+  /* Encrypt. */
+  silc_cipher_encrypt(aes, enc->data, enc->data, enc->len - len,
+                     silc_cipher_get_iv(aes));
+
+  silc_buffer_push(enc, 4);
+
+  /* Compute HMAC over the encrypted data and append the MAC to data.
+     The key is the first digest of the original key material. */
+  data_len = enc->len - len;
+  silc_hmac_init_with_key(sha1hmac, keymat, 16);
+  silc_hmac_update(sha1hmac, enc->data, data_len);
+  silc_buffer_pull(enc, data_len);
+  silc_hmac_final(sha1hmac, enc->data, NULL);
+  silc_buffer_push(enc, data_len);
+
+  /* Cleanup */
+  memset(keymat, 0, sizeof(keymat));
+  memset(tmp, 0, sizeof(tmp));
+  silc_hmac_free(sha1hmac);
+  silc_hash_free(md5);
+  silc_cipher_free(aes);
+
+  data = enc->data;
+  data_len = enc->len;
+
+  switch (encoding) {
   case SILC_PKCS_FILE_BIN:
     break;
   case SILC_PKCS_FILE_PEM:
@@ -1092,11 +1211,10 @@ static bool silc_pkcs_save_private_key_internal(char *filename,
     break;
   }
 
+  /* Encode the data and save to file */
   len = data_len + (strlen(SILC_PKCS_PRIVATE_KEYFILE_BEGIN) +
                    strlen(SILC_PKCS_PRIVATE_KEYFILE_END));
-  buf = silc_buffer_alloc(len);
-  silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
-
+  buf = silc_buffer_alloc_size(len);
   silc_buffer_format(buf,
                     SILC_STR_UI32_STRING(SILC_PKCS_PRIVATE_KEYFILE_BEGIN),
                     SILC_STR_UI_XNSTRING(data, data_len),
@@ -1105,39 +1223,38 @@ static bool silc_pkcs_save_private_key_internal(char *filename,
 
   /* Save into a file */
   if (silc_file_writefile_mode(filename, buf->data, buf->len, 0600)) {
+    silc_buffer_clear(buf);
     silc_buffer_free(buf);
+    silc_buffer_clear(enc);
+    silc_buffer_free(enc);
     return FALSE;
   }
 
+  silc_buffer_clear(buf);
   silc_buffer_free(buf);
+  silc_buffer_clear(enc);
+  silc_buffer_free(enc);
   return TRUE;
 }
 
 /* Saves private key into file. */
-/* XXX The buffer should be encrypted if passphrase is provided. */
 
 bool silc_pkcs_save_private_key(char *filename, SilcPrivateKey private_key, 
                                unsigned char *passphrase,
+                               SilcUInt32 passphrase_len,
                                SilcUInt32 encoding)
 {
   unsigned char *data;
   SilcUInt32 data_len;
+  bool ret;
 
   data = silc_pkcs_private_key_encode(private_key, &data_len);
-  return silc_pkcs_save_private_key_internal(filename, data, data_len,
-                                            encoding);
-}
-
-/* Saves private key into file. */
-/* XXX The buffer should be encrypted if passphrase is provided. */
-
-bool silc_pkcs_save_private_key_data(char *filename, unsigned char *data, 
-                                    SilcUInt32 data_len,
-                                    unsigned char *passphrase,
-                                    SilcUInt32 encoding)
-{
-  return silc_pkcs_save_private_key_internal(filename, data, data_len,
-                                            encoding);
+  ret = silc_pkcs_save_private_key_internal(filename, data, data_len,
+                                           passphrase, passphrase_len,
+                                           encoding);
+  memset(data, 0, data_len);
+  silc_free(data);
+  return ret;
 }
 
 /* Loads public key from file and allocates new public key. Returns TRUE
@@ -1198,13 +1315,19 @@ bool silc_pkcs_load_public_key(char *filename, SilcPublicKey *public_key,
 
 /* Load private key from file and allocates new private key. Returns TRUE
    if loading was successful. */
-/* XXX Should support encrypted private key files */
 
 bool silc_pkcs_load_private_key(char *filename, SilcPrivateKey *private_key,
+                               unsigned char *passphrase,
+                               SilcUInt32 passphrase_len,
                                SilcUInt32 encoding)
 {
+  SilcCipher aes;
+  SilcHash md5;
+  SilcHmac sha1hmac;
+  SilcUInt32 blocklen;
+  unsigned char tmp[32], keymat[32];
   unsigned char *cp, *old, *data, byte;
-  SilcUInt32 i, data_len, len;
+  SilcUInt32 i, data_len, len, magic, mac_len;
 
   old = data = silc_file_readfile(filename, &data_len);
   if (!data)
@@ -1225,23 +1348,137 @@ bool silc_pkcs_load_private_key(char *filename, SilcPrivateKey *private_key,
   data = cp;
 
   /* Decode private key */
-  if (private_key) {
-    len = data_len - (strlen(SILC_PKCS_PRIVATE_KEYFILE_BEGIN) +
-                     strlen(SILC_PKCS_PRIVATE_KEYFILE_END));
+  len = data_len - (strlen(SILC_PKCS_PRIVATE_KEYFILE_BEGIN) +
+                   strlen(SILC_PKCS_PRIVATE_KEYFILE_END));
 
-    switch(encoding) {
-    case SILC_PKCS_FILE_BIN:
-      break;
-    case SILC_PKCS_FILE_PEM:
-      data = silc_pem_decode(data, len, &len);
-      break;
+  switch(encoding) {
+  case SILC_PKCS_FILE_BIN:
+    break;
+  case SILC_PKCS_FILE_PEM:
+    data = silc_pem_decode(data, len, &len);
+    if (!data) {
+      memset(old, 0, data_len);
+      silc_free(old);
+      return FALSE;
     }
+    break;
+  }
 
-    if (!data || !silc_pkcs_private_key_decode(data, len, private_key)) {
+  memset(tmp, 0, sizeof(tmp));
+  memset(keymat, 0, sizeof(keymat));
+
+  /* Private key files without the specific magic number are assumed
+     to be the old-style private keys that are not encrypted. */
+  SILC_GET32_MSB(magic, data);
+  if (magic != SILC_PKCS_PRIVATE_KEY_MAGIC) {
+    /* Now decode the actual private key */
+    if (!silc_pkcs_private_key_decode(data, len, private_key)) {
       memset(old, 0, data_len);
       silc_free(old);
       return FALSE;
     }
+
+    memset(old, 0, data_len);
+    silc_free(old);
+    return TRUE;
+  }
+
+  /* Allocate the AES cipher */
+  if (!silc_cipher_alloc("aes-256-cbc", &aes)) {
+    SILC_LOG_ERROR(("Could not allocate AES cipher, probably not registered"));
+    memset(old, 0, data_len);
+    silc_free(old);
+    return FALSE;
+  }
+  blocklen = silc_cipher_get_block_len(aes);
+  if (blocklen * 2 > sizeof(tmp)) {
+    memset(old, 0, data_len);
+    silc_free(old);
+    return FALSE;
+  }
+
+  /* Allocate MD5 hash */
+  if (!silc_hash_alloc("md5", &md5)) {
+    SILC_LOG_ERROR(("Could not allocate MD5 hash, probably not registered"));
+    silc_cipher_free(aes);
+    memset(old, 0, data_len);
+    silc_free(old);
+    return FALSE;
+  }
+
+  /* Allocate HMAC */
+  if (!silc_hmac_alloc("hmac-sha1-96", NULL, &sha1hmac)) {
+    SILC_LOG_ERROR(("Could not allocate SHA1 HMAC, probably not registered"));
+    silc_hash_free(md5);
+    silc_cipher_free(aes);
+    memset(old, 0, data_len);
+    silc_free(old);
+    return FALSE;
+  }
+
+  /* Derive the decryption key from the provided key material.  The key
+     is 256 bits length, and derived by taking hash of the data, then 
+     re-hashing the data and the previous digest, and using the first and
+     second digest as the key. */
+  silc_hash_init(md5);
+  silc_hash_update(md5, passphrase, passphrase_len);
+  silc_hash_final(md5, keymat);
+  silc_hash_init(md5);
+  silc_hash_update(md5, passphrase, passphrase_len);
+  silc_hash_update(md5, keymat, 16);
+  silc_hash_final(md5, keymat + 16);
+
+  /* Set the key to the cipher */
+  silc_cipher_set_key(aes, keymat, sizeof(keymat) * 8);
+
+  /* First, verify the MAC of the private key data */
+  mac_len = silc_hmac_len(sha1hmac);
+  silc_hmac_init_with_key(sha1hmac, keymat, 16);
+  silc_hmac_update(sha1hmac, data, len - mac_len);
+  silc_hmac_final(sha1hmac, tmp, NULL);
+  if (memcmp(tmp, data + (len - mac_len), mac_len)) {
+    SILC_LOG_DEBUG(("Integrity check for private key failed"));
+    memset(keymat, 0, sizeof(keymat));
+    memset(tmp, 0, sizeof(tmp));
+    silc_hmac_free(sha1hmac);
+    silc_hash_free(md5);
+    silc_cipher_free(aes);
+    memset(old, 0, data_len);
+    silc_free(old);
+    return FALSE;
+  }
+  data += 4;
+  len -= 4;
+
+  /* Decrypt the private key buffer */
+  silc_cipher_decrypt(aes, data, data, len - mac_len, silc_cipher_get_iv(aes));
+  SILC_GET32_MSB(i, data);
+  if (i > len) {
+    SILC_LOG_DEBUG(("Bad private key length in buffer!"));
+    memset(keymat, 0, sizeof(keymat));
+    memset(tmp, 0, sizeof(tmp));
+    silc_hmac_free(sha1hmac);
+    silc_hash_free(md5);
+    silc_cipher_free(aes);
+    memset(old, 0, data_len);
+    silc_free(old);
+    return FALSE;
+  }
+  data += 4;
+  len = i;
+
+  /* Cleanup */
+  memset(keymat, 0, sizeof(keymat));
+  memset(tmp, 0, sizeof(tmp));
+  silc_hmac_free(sha1hmac);
+  silc_hash_free(md5);
+  silc_cipher_free(aes);
+
+  /* Now decode the actual private key */
+  if (!silc_pkcs_private_key_decode(data, len, private_key)) {
+    memset(old, 0, data_len);
+    silc_free(old);
+    return FALSE;
   }
 
   memset(old, 0, data_len);
index 7b704b44608341b20a170e2aa0c00b22aebfeea8..d96d9ef604721be848bac3a37ad091315433e5c3 100644 (file)
@@ -850,7 +850,8 @@ bool silc_pkcs_save_public_key(char *filename, SilcPublicKey public_key,
  *
  * DESCRIPTION
  *
- *    Saves public key into file. Returns FALSE on error.
+ *    Saves public key into file.  The public key is already encoded as
+ *    data when calling this function.  Returns FALSE on error.
  *
  ***/
 bool silc_pkcs_save_public_key_data(char *filename, unsigned char *data,
@@ -862,38 +863,23 @@ bool silc_pkcs_save_public_key_data(char *filename, unsigned char *data,
  * SYNOPSIS
  *
  *    bool silc_pkcs_save_private_key(char *filename,
- *                                   SilcPrivateKey private_key,
- *                                   unsigned char *passphrase,
- *                                   SilcUInt32 encoding);
+ *                                   SilcPrivateKey private_key,
+ *                                    unsigned char *passphrase,
+ *                                    SilcUInt32 passphrase_len,
+ *                                    SilcUInt32 encoding);
  *
  * DESCRIPTION
  *
- *    Saves private key into file. Returns FALSE on error.
+ *    Saves private key into file.  The private key is encrypted into
+ *    the file with the `passphrase' as a key.  The encryption algorithm
+ *    is AES with 256 bit key in CBC mode.  Returns FALSE on error.
  *
  ***/
 bool silc_pkcs_save_private_key(char *filename, SilcPrivateKey private_key, 
                                unsigned char *passphrase,
+                               SilcUInt32 passphrase_len,
                                SilcUInt32 encoding);
 
-/****f* silccrypt/SilcPKCSAPI/silc_pkcs_save_private_key_data
- *
- * SYNOPSIS
- *
- *    bool silc_pkcs_save_private_key_data(char *filename, unsigned char *data,
- *                                        SilcUInt32 data_len,
- *                                        unsigned char *passphrase,
- *                                        SilcUInt32 encoding);
- *
- * DESCRIPTION
- *
- *    Saves private key into file. Returns FALSE on error.
- *
- ***/
-bool silc_pkcs_save_private_key_data(char *filename, unsigned char *data, 
-                                    SilcUInt32 data_len,
-                                    unsigned char *passphrase,
-                                    SilcUInt32 encoding);
-
 /****f* silccrypt/SilcPKCSAPI/silc_pkcs_load_public_key
  *
  * SYNOPSIS
@@ -915,16 +901,21 @@ bool silc_pkcs_load_public_key(char *filename, SilcPublicKey *public_key,
  * SYNOPSIS
  *
  *    bool silc_pkcs_load_private_key(char *filename,
- *                                   SilcPrivateKey *private_key,
- *                                   SilcUInt32 encoding);
+ *                                    SilcPrivateKey *private_key,
+ *                                    unsigned char *passphrase,
+ *                                    SilcUInt32 passphrase_len,
+ *                                    SilcUInt32 encoding);
  *
  * DESCRIPTION
  *
  *    Load private key from file and allocates new private key. Returns TRUE
- *    if loading was successful.
+ *    if loading was successful. The `passphrase' is used as decryption
+ *    key of the private key file.
  *
  ***/
 bool silc_pkcs_load_private_key(char *filename, SilcPrivateKey *private_key,
+                               unsigned char *passphrase,
+                               SilcUInt32 passphrase_len,
                                SilcUInt32 encoding);
 
 #endif /* SILCPKCS_H */
index 6c0eccaaa8504eeb621c35d97817a78698b7a90e..4d05f16c4c806822b308c2a85b180d1358a996d5 100644 (file)
@@ -59,6 +59,7 @@ bool silc_create_key_pair(const char *pkcs_name,
                          const char *pub_filename,
                          const char *prv_filename,
                          const char *pub_identifier,
+                         const char *passphrase,
                          SilcPKCS *return_pkcs,
                          SilcPublicKey *return_public_key,
                          SilcPrivateKey *return_private_key,
@@ -75,6 +76,7 @@ bool silc_create_key_pair(const char *pkcs_name,
   char *prvfile = prv_filename ? strdup(prv_filename) : NULL;
   char *alg = pkcs_name ? strdup(pkcs_name) : NULL;
   char *identifier = pub_identifier ? strdup(pub_identifier) : NULL;
+  char *pass = passphrase ? strdup(passphrase) : NULL;
 
   if (interactive && (!alg || !pub_filename || !prv_filename))
     printf("\
@@ -155,7 +157,7 @@ New pair of keys will be created.  Please, answer to following questions.\n\
   if (!pkfile) {
     if (interactive) {
       memset(line, 0, sizeof(line));
-      snprintf(line, sizeof(line), "Public key filename [public_key.pub] ");
+      snprintf(line, sizeof(line), "Public key filename [public_key.pub]: ");
       pkfile = silc_get_input(line, FALSE);
     }
     if (!pkfile)
@@ -165,13 +167,21 @@ New pair of keys will be created.  Please, answer to following questions.\n\
   if (!prvfile) {
     if (interactive) {
       memset(line, 0, sizeof(line));
-      snprintf(line, sizeof(line), "Public key filename [private_key.prv] ");
+      snprintf(line, sizeof(line), "Private key filename [private_key.prv]: ");
       prvfile = silc_get_input(line, FALSE);
     }
     if (!prvfile)
       prvfile = strdup("private_key.prv");
   }
 
+  if (!pass) {
+    memset(line, 0, sizeof(line));
+    snprintf(line, sizeof(line), "Private key passphrase: ");
+    pass = silc_get_input(line, TRUE);
+    if (!pass)
+      pass = strdup("");
+  }
+
   /* Generate keys */
   silc_pkcs_alloc(alg, &pkcs);
   silc_pkcs_generate_key(pkcs, key_len_bits, rng);
@@ -192,7 +202,9 @@ New pair of keys will be created.  Please, answer to following questions.\n\
   key = silc_pkcs_get_private_key(pkcs, &key_len);
   prv_key = silc_pkcs_private_key_alloc(silc_pkcs_get_name(pkcs),
                                        key, key_len);
-  silc_pkcs_save_private_key(prvfile, prv_key, NULL, SILC_PKCS_FILE_BIN);
+  silc_pkcs_save_private_key(prvfile, prv_key,
+                            (unsigned char *)pass, strlen(pass),
+                            SILC_PKCS_FILE_BIN);
   if (return_private_key)
     *return_private_key = prv_key;
   else
@@ -217,6 +229,8 @@ New pair of keys will be created.  Please, answer to following questions.\n\
   silc_free(pkfile);
   silc_free(prvfile);
   silc_free(identifier);
+  memset(pass, 0, strlen(pass));
+  silc_free(pass);
 
   return TRUE;
 }
@@ -225,23 +239,38 @@ New pair of keys will be created.  Please, answer to following questions.\n\
 
 bool silc_load_key_pair(const char *pub_filename,
                        const char *prv_filename,
+                       const char *passphrase,
                        SilcPKCS *return_pkcs,
                        SilcPublicKey *return_public_key,
                        SilcPrivateKey *return_private_key)
 {
+  char *pass = passphrase ? strdup(passphrase) : NULL;
+
   SILC_LOG_DEBUG(("Loading public and private keys"));
 
   if (silc_pkcs_load_public_key((char *)pub_filename, return_public_key,
                                SILC_PKCS_FILE_PEM) == FALSE)
     if (silc_pkcs_load_public_key((char *)pub_filename, return_public_key,
-                                 SILC_PKCS_FILE_BIN) == FALSE)
+                                 SILC_PKCS_FILE_BIN) == FALSE) {
+      memset(pass, 0, strlen(pass));
       return FALSE;
+    }
+
+  if (!pass) {
+    pass = silc_get_input("Private key passphrase: ", TRUE);
+    if (!pass)
+      pass = strdup("");
+  }
 
   if (silc_pkcs_load_private_key((char *)prv_filename, return_private_key,
+                                (unsigned char *)pass, strlen(pass),
                                 SILC_PKCS_FILE_BIN) == FALSE)
     if (silc_pkcs_load_private_key((char *)prv_filename, return_private_key,
-                                  SILC_PKCS_FILE_PEM) == FALSE)
+                                  (unsigned char *)pass, strlen(pass),
+                                  SILC_PKCS_FILE_PEM) == FALSE) {
+      memset(pass, 0, strlen(pass));
       return FALSE;
+    }
 
   if (return_pkcs) {
     silc_pkcs_alloc((*return_public_key)->name, return_pkcs);
@@ -249,6 +278,7 @@ bool silc_load_key_pair(const char *pub_filename,
     silc_pkcs_private_key_set(*return_pkcs, *return_private_key);
   }
 
+  memset(pass, 0, strlen(pass));
   return TRUE;
 }
 
index f054fa4d2b90be617d44a78be7c32186b3090eec..827c2ff90a666a074da555cb4c2e1b70b2a8ced0 100644 (file)
@@ -44,6 +44,7 @@
  *                              const char *pub_filename,
  *                              const char *prv_filename,
  *                              const char *pub_identifier,
+ *                              const char *passphrase,
  *                              SilcPKCS *return_pkcs,
  *                              SilcPublicKey *return_public_key,
  *                              SilcPrivateKey *return_private_key,
  *    "UN=foobar, HN=hostname"), or if NULL the routine generates it
  *    automatically.
  *
+ *    The `passphrase' is the passphrase that is used to encrypt the
+ *    private key file.  It is recommended that you would protect your
+ *    private key file with a passphrase.
+ *
  *    The routine returns FALSE if error occurs during key generation.
  *    Function returns TRUE when success and returns the created SilcPKCS
  *    object, which can be used to perform public key cryptography into
@@ -84,6 +89,7 @@ bool silc_create_key_pair(const char *pkcs_name,
                          const char *pub_filename,
                          const char *prv_filename,
                          const char *pub_identifier,
+                         const char *passphrase,
                          SilcPKCS *return_pkcs,
                          SilcPublicKey *return_public_key,
                          SilcPrivateKey *return_private_key,
@@ -96,6 +102,7 @@ bool silc_create_key_pair(const char *pkcs_name,
  *    bool silc_load_key_pair(const char *pub_filename,
  *                            const char *prv_filename,
  *                            SilcPKCS *return_pkcs,
+ *                            const char *passphrase,
  *                            SilcPublicKey *return_public_key,
  *                            SilcPrivateKey *return_private_key);
  *
@@ -107,11 +114,13 @@ bool silc_create_key_pair(const char *pkcs_name,
  *    public key into `return_public_key' pointer, private key into
  *    `return_private_key' pointer and the SilcPKCS object to the
  *    `return_pkcs'.  The SilcPKCS can be used to perform public key
- *    cryptographic operations.
+ *    cryptographic operations.  The `passphrase' is the passphrase
+ *    which will be used to decrypt the private key file.
  *
  ***/
 bool silc_load_key_pair(const char *pub_filename,
                        const char *prv_filename,
+                       const char *passphrase,
                        SilcPKCS *return_pkcs,
                        SilcPublicKey *return_public_key,
                        SilcPrivateKey *return_private_key);