Created SILC GIT repository.
[silc.git] / lib / silcssh / silcssh_pkcs.c
diff --git a/lib/silcssh/silcssh_pkcs.c b/lib/silcssh/silcssh_pkcs.c
deleted file mode 100644 (file)
index d2b2a23..0000000
+++ /dev/null
@@ -1,1265 +0,0 @@
-/*
-
-  silcssh_pkcs.c
-
-  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.
-
-*/
-
-#include "silc.h"
-#include "rsa.h"
-#include "dsa.h"
-#include "silcssh_pkcs.h"
-
-/************************** Types and definitions ***************************/
-
-/* RFC 4716 public key file markers */
-#define SILC_SSH_PUBLIC_KEY_BEGIN "---- BEGIN SSH2 PUBLIC KEY ----"
-#define SILC_SSH_PUBLIC_KEY_END "---- END SSH2 PUBLIC KEY ----"
-
-/* OpenSSH private key file markers */
-#define SILC_SSH_RSA_BEGIN "-----BEGIN RSA PRIVATE KEY-----"
-#define SILC_SSH_RSA_END "-----END RSA PRIVATE KEY-----"
-#define SILC_SSH_DSA_BEGIN "-----BEGIN DSA PRIVATE KEY-----"
-#define SILC_SSH_DSA_END "-----END DSA PRIVATE KEY-----"
-
-/****************************** SSH2 PKCS API *******************************/
-
-/* Get algorithm context */
-
-SILC_PKCS_GET_ALGORITHM(silc_pkcs_ssh_get_algorithm)
-{
-  SilcSshPublicKey pubkey = public_key;
-  return pubkey->pkcs;
-}
-
-/* Import public key file */
-
-SILC_PKCS_IMPORT_PUBLIC_KEY_FILE(silc_pkcs_ssh_import_public_key_file)
-{
-  SilcSshPublicKey pubkey;
-  SilcBufferStruct keybuf, line;
-  SilcHashTable fields;
-  unsigned char *data;
-  SilcSshKeyType type;
-  int ret;
-
-  SILC_LOG_DEBUG(("Parsing SSH2 public key file"));
-
-  if (!ret_public_key)
-    return FALSE;
-  if (encoding == SILC_PKCS_FILE_BIN)
-    return FALSE;
-
-  silc_buffer_set(&keybuf, filedata, filedata_len);
-
-  /* Check for RFC 4716 style public key markers */
-  if (!silc_ssh_parse_line(&keybuf, &line, TRUE)) {
-    SILC_LOG_DEBUG(("Malformed SSH2 public key markers"));
-    return FALSE;
-  }
-  if ((silc_buffer_len(&keybuf) < (strlen(SILC_SSH_PUBLIC_KEY_BEGIN) +
-                                  strlen(SILC_SSH_PUBLIC_KEY_END))) ||
-      strncmp(silc_buffer_data(&line), SILC_SSH_PUBLIC_KEY_BEGIN,
-             silc_buffer_len(&line))) {
-    /* We assume the key is OpenSSH style public key. */
-    type = SILC_SSH_KEY_OPENSSH;
-    silc_buffer_set(&keybuf, filedata, filedata_len);
-
-    /* Get subject name from the end of the file */
-    if (!silc_buffer_strchr(&keybuf, ' ', FALSE)) {
-      SILC_LOG_DEBUG(("Malformed SSH2 public key"));
-      return FALSE;
-    }
-    if (!silc_buffer_pull(&keybuf, 1)) {
-      SILC_LOG_DEBUG(("Malformed SSH2 public key"));
-      return FALSE;
-    }
-    if (!silc_buffer_len(&keybuf)) {
-      SILC_LOG_DEBUG(("Malformed SSH2 public key"));
-      return FALSE;
-    }
-
-    /* Add subject name to public key headers */
-    fields = silc_ssh_allocate_fields();
-    if (!fields)
-      return FALSE;
-    silc_hash_table_add(fields, strdup("Subject"),
-                       silc_memdup(silc_buffer_data(&keybuf),
-                                   silc_buffer_len(&keybuf)));
-
-    filedata_len = silc_buffer_headlen(&keybuf) - 1;
-    SILC_LOG_DEBUG(("Add Subject header to public key"));
-
-    /* Skip algorithm name */
-    silc_buffer_start(&keybuf);
-    if (!silc_buffer_strchr(&keybuf, ' ', TRUE)) {
-      SILC_LOG_DEBUG(("Malformed SSH2 public key"));
-      silc_hash_table_free(fields);
-      return FALSE;
-    }
-    if (!silc_buffer_pull(&keybuf, 1)) {
-      SILC_LOG_DEBUG(("Malformed SSH2 public key"));
-      silc_hash_table_free(fields);
-      return FALSE;
-    }
-    if (silc_buffer_len(&keybuf) < filedata_len) {
-      SILC_LOG_DEBUG(("Malformed SSH2 public key"));
-      silc_hash_table_free(fields);
-      return FALSE;
-    }
-
-    filedata = silc_buffer_data(&keybuf);
-    SILC_LOG_DEBUG(("Public key is OpenSSH public key"));
-
-  } else {
-    /* RFC 4716 style public key */
-    type = SILC_SSH_KEY_SSH2;
-
-    filedata = silc_buffer_data(&keybuf);
-    filedata_len = silc_buffer_len(&keybuf) - strlen(SILC_SSH_PUBLIC_KEY_END);
-    silc_buffer_set(&keybuf, filedata, filedata_len);
-
-    /* Parse public key headers */
-    fields = silc_ssh_parse_headers(&keybuf);
-    if (!fields)
-      return FALSE;
-
-    filedata = silc_buffer_data(&keybuf);
-    filedata_len = silc_buffer_len(&keybuf);
-
-    SILC_LOG_DEBUG(("Public key is standard SSH2 public key"));
-  }
-
-  /* Decode */
-  data = silc_base64_decode(NULL, filedata, filedata_len, &filedata_len);
-  if (!data) {
-    silc_hash_table_free(fields);
-    return FALSE;
-  }
-  filedata = data;
-
-  /* Decode the public key */
-  ret = silc_pkcs_ssh_import_public_key(pkcs, NULL, filedata, filedata_len,
-                                       (void *)&pubkey, ret_alg);
-  silc_free(data);
-
-  if (ret) {
-    pubkey->fields = fields;
-    pubkey->type = type;
-    *ret_public_key = pubkey;
-    SILC_LOG_DEBUG(("SSH2 public key file imported successfully"));
-    return TRUE;
-  }
-
-  silc_hash_table_free(fields);
-  return FALSE;
-}
-
-/* Import public key */
-
-SILC_PKCS_IMPORT_PUBLIC_KEY(silc_pkcs_ssh_import_public_key)
-{
-  SilcSshPublicKey pubkey;
-  int ret;
-
-  ret = silc_ssh_public_key_decode(key, key_len, &pubkey);
-  if (ret) {
-    if (ret_alg)
-      *ret_alg = pubkey->pkcs;
-    if (ret_public_key)
-      *ret_public_key = pubkey;
-  }
-
-  return ret;
-}
-
-/* Export public key file */
-
-SILC_PKCS_EXPORT_PUBLIC_KEY_FILE(silc_pkcs_ssh_export_public_key_file)
-{
-  SilcSshPublicKey pubkey = public_key;
-  SilcHashTableList htl;
-  SilcBufferStruct buf, fields;
-  unsigned char *key, *data;
-  SilcUInt32 key_len;
-  char *field, *value, tmp[1024], tmp2[1024 + 24];
-  int i, j, c;
-
-  SILC_LOG_DEBUG(("Encoding %s public key file",
-                 pubkey->type == SILC_SSH_KEY_SSH2 ? "SSH2" : "OpenSSH"));
-
-  /* Export key */
-  key = silc_pkcs_ssh_export_public_key(pkcs, stack, pubkey, &key_len);
-  if (!key)
-    return NULL;
-
-  /* Base64 encode the key data */
-  if (pubkey->type == SILC_SSH_KEY_SSH2)
-    data = silc_base64_encode_file(stack, key, key_len);
-  else
-    data = silc_base64_encode(stack, key, key_len);
-  if (!data)
-    return NULL;
-  silc_sfree(stack, key);
-  key = data;
-  key_len = strlen(data);
-
-  memset(&buf, 0, sizeof(buf));
-  memset(&fields, 0, sizeof(fields));
-  memset(tmp, 0, sizeof(tmp));
-  memset(tmp2, 0, sizeof(tmp2));
-
-  switch (pubkey->type) {
-  case SILC_SSH_KEY_SSH2:
-    /* RFC 4716 style public key file */
-
-    if (pubkey->fields) {
-      /* Encode public key headers */
-      silc_hash_table_list(pubkey->fields, &htl);
-      while (silc_hash_table_get(&htl, (void *)&field, (void *)&value)) {
-       /* Wrap lines with over 72 characters */
-       silc_snprintf(tmp, sizeof(tmp), "%s: %s", field, value);
-       for (i = 0, j = 0, c = 1; i < strlen(tmp); i++, c++) {
-         if (c == 72) {
-           tmp2[j++] = '\\';
-           tmp2[j++] = '\n';
-           i--;
-           c = 0;
-           continue;
-         }
-
-         tmp2[j++] = tmp[i];
-       }
-       tmp2[j++] = '\n';
-
-       if (silc_buffer_sstrformat(stack, &fields, tmp2, SILC_STRFMT_END) < 0) {
-         silc_buffer_spurge(stack, &fields);
-         silc_sfree(stack, key);
-         return NULL;
-       }
-
-       memset(tmp2, 0, sizeof(tmp2));
-      }
-      silc_hash_table_list_reset(&htl);
-    }
-
-    /* Encode the file */
-    if (silc_buffer_sformat(stack, &buf,
-                           SILC_STR_UI32_STRING(SILC_SSH_PUBLIC_KEY_BEGIN),
-                           SILC_STR_UI32_STRING("\n"),
-                           SILC_STR_UI_XNSTRING(silc_buffer_data(&fields),
-                                                silc_buffer_len(&fields)),
-                           SILC_STR_UI_XNSTRING(key, key_len),
-                           SILC_STR_UI32_STRING("\n"),
-                           SILC_STR_UI32_STRING(SILC_SSH_PUBLIC_KEY_END),
-                           SILC_STR_UI32_STRING("\n"),
-                           SILC_STR_END) < 0) {
-      silc_buffer_spurge(stack, &fields);
-      silc_sfree(stack, key);
-      return NULL;
-    }
-
-    break;
-
-  case SILC_SSH_KEY_OPENSSH:
-    /* OpenSSH style public key file */
-
-    if (!strcmp(pubkey->pkcs->name, "rsa"))
-      silc_snprintf(tmp, sizeof(tmp), "ssh-rsa ");
-    else if (!strcmp(pubkey->pkcs->name, "dsa"))
-      silc_snprintf(tmp, sizeof(tmp), "ssh-dss ");
-
-    /* Get subject */
-    value = (char *)silc_ssh_public_key_get_field(pubkey, "Subject");
-
-    /* Encode the file */
-    if (silc_buffer_sformat(stack, &buf,
-                           SILC_STR_UI32_STRING(tmp),
-                           SILC_STR_UI_XNSTRING(key, key_len),
-                           SILC_STR_UI32_STRING(" "),
-                           SILC_STR_UI32_STRING(value),
-                           SILC_STR_UI32_STRING("\n"),
-                           SILC_STR_END) < 0) {
-      silc_buffer_spurge(stack, &buf);
-      silc_sfree(stack, key);
-      return NULL;
-    }
-
-    break;
-
-  default:
-    silc_sfree(stack, key);
-    return NULL;
-    break;
-  }
-
-  silc_sfree(stack, key);
-  key = silc_buffer_steal(&buf, ret_len);
-
-  silc_buffer_spurge(stack, &fields);
-
-  return key;
-}
-
-/* Export public key */
-
-SILC_PKCS_EXPORT_PUBLIC_KEY(silc_pkcs_ssh_export_public_key)
-{
-  return silc_ssh_public_key_encode(stack, public_key, ret_len);
-}
-
-/* Return key length in bits */
-
-SILC_PKCS_PUBLIC_KEY_BITLEN(silc_pkcs_ssh_public_key_bitlen)
-{
-  SilcSshPublicKey pubkey = public_key;
-  return pubkey->pkcs->public_key_bitlen(pubkey->pkcs, pubkey->public_key);
-}
-
-/* Copy public key */
-
-SILC_PKCS_PUBLIC_KEY_COPY(silc_pkcs_ssh_public_key_copy)
-{
-  SilcSshPublicKey pubkey = public_key, new_pubkey;
-  SilcHashTableList htl;
-  char *field, *value;
-
-  new_pubkey = silc_calloc(1, sizeof(*new_pubkey));
-  if (!new_pubkey)
-    return NULL;
-  new_pubkey->pkcs = pubkey->pkcs;
-  new_pubkey->type = pubkey->type;
-
-  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;
-  }
-
-  if (pubkey->fields) {
-    new_pubkey->fields = silc_ssh_allocate_fields();
-    if (!new_pubkey->fields) {
-      pubkey->pkcs->public_key_free(pubkey->pkcs, pubkey->public_key);
-      silc_free(new_pubkey);
-      return NULL;
-    }
-
-    silc_hash_table_list(pubkey->fields, &htl);
-    while (silc_hash_table_get(&htl, (void *)&field, (void *)&value))
-      silc_hash_table_add(new_pubkey->fields, strdup(field), strdup(value));
-    silc_hash_table_list_reset(&htl);
-  }
-
-  return new_pubkey;
-}
-
-/* Compare two public keys */
-
-SILC_PKCS_PUBLIC_KEY_COMPARE(silc_pkcs_ssh_public_key_compare)
-{
-  SilcSshPublicKey k1 = key1, k2 = key2;
-  SilcHashTableList htl;
-  char *field, *value, *value2;
-
-  if (strcmp(k1->pkcs->name, k2->pkcs->name))
-    return FALSE;
-
-  if (k1->fields && !k2->fields)
-    return FALSE;
-  if (!k1->fields && k2->fields)
-    return FALSE;
-
-  if (k1->fields && k2->fields) {
-    if (silc_hash_table_count(k1->fields) != silc_hash_table_count(k2->fields))
-      return FALSE;
-
-    silc_hash_table_list(k1->fields, &htl);
-    while (silc_hash_table_get(&htl, (void *)&field, (void *)&value)) {
-      value2 = (char *)silc_ssh_public_key_get_field(k2, field);
-      if (!value2)
-       return FALSE;
-      if (strcmp(value, value2))
-       return FALSE;
-    }
-    silc_hash_table_list_reset(&htl);
-  }
-
-  return k1->pkcs->public_key_compare(k1->pkcs, k1->public_key,
-                                     k2->public_key);
-}
-
-/* Free public key */
-
-SILC_PKCS_PUBLIC_KEY_FREE(silc_pkcs_ssh_public_key_free)
-{
-  silc_ssh_public_key_free(public_key);
-}
-
-/* Import private key file.  Supports only OpenSSH (OpenSSL to be exact)
-   private key files. */
-
-SILC_PKCS_IMPORT_PRIVATE_KEY_FILE(silc_pkcs_ssh_import_private_key_file)
-{
-  const SilcPKCSAlgorithm *alg;
-  SilcSshPrivateKey privkey = NULL;
-  SilcHashTable fields;
-  SilcBufferStruct keybuf, line;
-  unsigned char *data, iv[8], key[32];
-  SilcSshKeyType type;
-  char *proctype, *dekinfo;
-  SilcCipher des;
-  SilcHash md5;
-  int ret;
-
-  SILC_LOG_DEBUG(("Parsing SSH2 private key file"));
-
-  if (!ret_private_key)
-    return FALSE;
-  if (encoding == SILC_PKCS_FILE_BIN)
-    return FALSE;
-
-  silc_buffer_set(&keybuf, filedata, filedata_len);
-
-  /* Check for private key markers */
-  if (!silc_ssh_parse_line(&keybuf, &line, TRUE)) {
-    SILC_LOG_DEBUG(("Malformed SSH2 private key markers"));
-    return FALSE;
-  }
-  if ((silc_buffer_len(&keybuf) < (strlen(SILC_SSH_RSA_BEGIN) +
-                                  strlen(SILC_SSH_RSA_END))) ||
-      (strncmp(silc_buffer_data(&line), SILC_SSH_RSA_BEGIN,
-              silc_buffer_len(&line)) &&
-       strncmp(silc_buffer_data(&line), SILC_SSH_DSA_BEGIN,
-              silc_buffer_len(&line)))) {
-    SILC_LOG_DEBUG(("Malformed SSH2 private key markers"));
-    return FALSE;
-  }
-
-  /* Get PKCS algorithm */
-  if (!strncmp(silc_buffer_data(&line), SILC_SSH_RSA_BEGIN,
-              silc_buffer_len(&line))) {
-    alg = silc_pkcs_find_algorithm("rsa", "ssh");
-    if (!alg) {
-      SILC_LOG_ERROR(("Unsupported PKCS algorithm rsa/ssh"));
-      return FALSE;
-    }
-  } else if (!strncmp(silc_buffer_data(&line), SILC_SSH_DSA_BEGIN,
-                     silc_buffer_len(&line))) {
-    alg = silc_pkcs_find_algorithm("dsa", "ssh");
-    if (!alg) {
-      SILC_LOG_ERROR(("Unsupported PKCS algorithm dsa/ssh"));
-      return FALSE;
-    }
-  } else
-    return FALSE;
-
-  type = SILC_SSH_KEY_OPENSSH;
-  filedata = silc_buffer_data(&keybuf);
-
-  /* Skip end marker */
-  if (!silc_buffer_strchr(&keybuf, '-', FALSE)) {
-    SILC_LOG_DEBUG(("Malformed SSH2 private key markers"));
-    return FALSE;
-  }
-  filedata_len = silc_buffer_data(&keybuf) - filedata;
-  silc_buffer_set(&keybuf, filedata, filedata_len);
-
-  /* Parse private key headers.  They define how the private key has been
-     encrypted. */
-  fields = silc_ssh_parse_headers(&keybuf);
-  if (!fields)
-    return FALSE;
-
-  /* Skip empty line after headers */
-  if (silc_hash_table_count(fields) > 0)
-    silc_ssh_parse_line(&keybuf, NULL, TRUE);
-
-  filedata = silc_buffer_data(&keybuf);
-  filedata_len = silc_buffer_len(&keybuf);
-
-  /* Decode */
-  data = silc_base64_decode(NULL, filedata, filedata_len, &filedata_len);
-  if (!data) {
-    SILC_LOG_DEBUG(("Malformed SSH2 private key"));
-    goto err;
-  }
-  filedata = data;
-
-  SILC_LOG_DEBUG(("Private key is %s", (silc_hash_table_count(fields) ?
-                                       "encrypted" : "not encrypted")));
-
-  if (silc_hash_table_count(fields) > 0 && passphrase) {
-    /* Decrypt */
-
-    /* Get encryption info */
-    if (!silc_hash_table_find(fields, "Proc-Type", NULL, (void *)&proctype)) {
-      SILC_LOG_ERROR(("Malformed SSH2 private key"));
-      goto err;
-    }
-    if (strcmp(proctype, "4,ENCRYPTED")) {
-      SILC_LOG_ERROR(("Malformed SSH2 private key"));
-      goto err;
-    }
-
-    /* OpenSSH uses 3DES-EDE only */
-    if (!silc_hash_table_find(fields, "DEK-Info", NULL, (void *)&dekinfo)) {
-      SILC_LOG_ERROR(("Malformed SSH2 private key"));
-      goto err;
-    }
-    if (strncmp(dekinfo, "DES-EDE3-CBC", strlen("DES-EDE3-CBC"))) {
-      SILC_LOG_ERROR(("Unsupported SSH2 private key cipher '%s'", dekinfo));
-      goto err;
-    }
-
-    /* Allocate cipher */
-    if (!silc_cipher_alloc("3des-168-cbc", &des)) {
-      SILC_LOG_ERROR(("Unsupported algorithm 3des-168-cbc"));
-      goto err;
-    }
-
-    /* Allocate hash */
-    if (!silc_hash_alloc("md5", &md5)) {
-      SILC_LOG_ERROR(("Unsupported hash algorithm md5"));
-      goto err;
-    }
-
-    /* Get IV from private key file */
-    dekinfo = strchr(dekinfo, ',');
-    if (!dekinfo || strlen(dekinfo) < 16) {
-      SILC_LOG_ERROR(("Malformed SSH2 private key"));
-      goto err;
-    }
-    dekinfo++;
-    silc_hex2data(dekinfo, iv, sizeof(iv), NULL);
-
-    /* Generate key from passphrase and IV as salt.  The passphrase is
-       hashed with the IV, then rehashed with the previous hash, passphrase
-       and the IV to produce the final key, which is the concatenation of
-       the two hashes. */
-    silc_hash_init(md5);
-    silc_hash_update(md5, passphrase, passphrase_len);
-    silc_hash_update(md5, iv, 8);
-    silc_hash_final(md5, key);
-    silc_hash_init(md5);
-    silc_hash_update(md5, key, 16);
-    silc_hash_update(md5, passphrase, passphrase_len);
-    silc_hash_update(md5, iv, 8);
-    silc_hash_final(md5, key + 16);
-
-    /* Decrypt */
-    silc_cipher_set_key(des, key, 192, FALSE);
-    if (!silc_cipher_decrypt(des, filedata, filedata, filedata_len, iv)) {
-      SILC_LOG_ERROR(("Malformed SSH2 private key"));
-      silc_cipher_free(des);
-      silc_hash_free(md5);
-      goto err;
-    }
-
-    silc_cipher_free(des);
-    silc_hash_free(md5);
-  }
-
-  /* Decode the private key */
-  ret = silc_pkcs_ssh_import_private_key(pkcs, alg, filedata, filedata_len,
-                                        (void *)&privkey, ret_alg);
-  silc_free(data);
-
-  if (ret) {
-    privkey->fields = fields;
-    privkey->type = type;
-    *ret_private_key = privkey;
-    SILC_LOG_DEBUG(("SSH2 private key file imported successfully"));
-    return TRUE;
-  }
-
- err:
-  if (fields)
-    silc_hash_table_free(fields);
-  return FALSE;
-}
-
-/* Import private key.  The key format for RSA is PKCS#1 compliant and for
-   DSA is equivalent to our DSA implementation, so we just simply call the
-   algorithm specific import function to do the magic. */
-
-SILC_PKCS_IMPORT_PRIVATE_KEY(silc_pkcs_ssh_import_private_key)
-{
-  SilcSshPrivateKey privkey;
-  int ret;
-
-  if (!ret_private_key || !alg)
-    return 0;
-
-  /* Allocate SSH private key context */
-  privkey = silc_calloc(1, sizeof(*privkey));
-  if (!privkey)
-    return 0;
-
-  /* Import PKCS algorithm private key */
-  ret = alg->import_private_key(alg, key, key_len, &privkey->private_key);
-  if (!ret) {
-    silc_free(privkey);
-    return 0;
-  }
-
-  privkey->pkcs = alg;
-  privkey->type = SILC_SSH_KEY_OPENSSH;
-
-  *ret_private_key = privkey;
-  if (ret_alg)
-    *ret_alg = alg;
-
-  return ret;
-}
-
-/* Export private key file */
-
-SILC_PKCS_EXPORT_PRIVATE_KEY_FILE(silc_pkcs_ssh_export_private_key_file)
-{
-  SilcSshPrivateKey privkey = private_key;
-  const SilcPKCSAlgorithm *alg = privkey->pkcs;
-  SilcBufferStruct buf;
-  unsigned char *key, *keyenc, ivdata[8], iv[16 + 1], enc[32];
-  SilcUInt32 key_len, pad_len;
-  SilcCipher des = NULL;
-  SilcHash md5 = NULL;
-
-  SILC_LOG_DEBUG(("Encode SSH2 private key file"));
-
-  /* Export the private key */
-  key = silc_pkcs_ssh_export_private_key(pkcs, stack, private_key, &key_len);
-  if (!key)
-    return NULL;
-
-  memset(&buf, 0, sizeof(buf));
-  if (!strcmp(alg->name, "rsa")) {
-    if (silc_buffer_sformat(stack, &buf,
-                           SILC_STR_ADVANCE,
-                           SILC_STR_UI32_STRING(SILC_SSH_RSA_BEGIN),
-                           SILC_STR_UI32_STRING("\n"),
-                           SILC_STR_END) < 0)
-      goto err;
-  } else if (!strcmp(alg->name, "dsa")) {
-    if (silc_buffer_sformat(stack, &buf,
-                           SILC_STR_ADVANCE,
-                           SILC_STR_UI32_STRING(SILC_SSH_DSA_BEGIN),
-                           SILC_STR_UI32_STRING("\n"),
-                           SILC_STR_END) < 0)
-      goto err;
-  } else
-    goto err;
-
-  if (passphrase && strlen(passphrase) > 0) {
-    /* Encrypt the key */
-
-    /* Allocate cipher */
-    if (!silc_cipher_alloc("3des-168-cbc", &des)) {
-      SILC_LOG_ERROR(("Unsupported algorithm 3des-168-cbc"));
-      goto err;
-    }
-
-    /* Allocate hash */
-    if (!silc_hash_alloc("md5", &md5)) {
-      SILC_LOG_ERROR(("Unsupported hash algorithm md5"));
-      goto err;
-    }
-
-    /* Generate IV */
-    silc_rng_get_rn_data(rng, sizeof(ivdata), ivdata, sizeof(ivdata));
-    silc_data2hex(ivdata, sizeof(ivdata), iv, sizeof(iv));
-
-    /* Encode header */
-    if (silc_buffer_sformat(stack, &buf,
-                           SILC_STR_ADVANCE,
-                           SILC_STR_UI32_STRING("Proc-Type: 4,ENCRYPTED\n"),
-                           SILC_STR_UI32_STRING("DEK-Info: DES-EDE3-CBC,"),
-                           SILC_STR_UI32_STRING(iv),
-                           SILC_STR_UI32_STRING("\n\n"),
-                           SILC_STR_END) < 0)
-      goto err;
-
-    /* Generate key from passphrase and IV as salt.  The passphrase is
-       hashed with the IV, then rehashed with the previous hash, passphrase
-       and the IV to produce the final key, which is the concatenation of
-       the two hashes. */
-    silc_hash_init(md5);
-    silc_hash_update(md5, passphrase, passphrase_len);
-    silc_hash_update(md5, ivdata, 8);
-    silc_hash_final(md5, enc);
-    silc_hash_init(md5);
-    silc_hash_update(md5, enc, 16);
-    silc_hash_update(md5, passphrase, passphrase_len);
-    silc_hash_update(md5, ivdata, 8);
-    silc_hash_final(md5, enc + 16);
-
-    /* Pad */
-    pad_len = (-key_len) % 8;
-    if (pad_len) {
-      keyenc = silc_smalloc(stack, (key_len + pad_len) * sizeof(*keyenc));
-      if (!keyenc)
-       goto err;
-      memset(keyenc + key_len, 'F', pad_len);
-      memcpy(keyenc, key, key_len);
-    } else {
-      keyenc = silc_smemdup(stack, key, key_len);
-      if (!keyenc)
-       goto err;
-    }
-
-    /* Encrypt */
-    silc_cipher_set_key(des, enc, 192, TRUE);
-    silc_cipher_encrypt(des, keyenc, keyenc, key_len + pad_len, ivdata);
-
-    silc_sfree(stack, key);
-    key = keyenc;
-    key_len += pad_len;
-
-    silc_cipher_free(des);
-    silc_hash_free(md5);
-  }
-
-  /* Base64 encode */
-  keyenc = silc_base64_encode_file(stack, key, key_len);
-  if (!keyenc)
-    goto err;
-
-  silc_sfree(stack, key);
-  key = keyenc;
-  key_len = strlen(keyenc);
-
-  /* Encode rest of the public key */
-  if (!strcmp(alg->name, "rsa")) {
-    if (silc_buffer_sformat(stack, &buf,
-                           SILC_STR_ADVANCE,
-                           SILC_STR_DATA(key, key_len),
-                           SILC_STR_UI32_STRING("\n"),
-                           SILC_STR_UI32_STRING(SILC_SSH_RSA_END),
-                           SILC_STR_UI32_STRING("\n"),
-                           SILC_STR_END) < 0)
-      goto err;
-  } else if (!strcmp(alg->name, "dsa")) {
-    if (silc_buffer_sformat(stack, &buf,
-                           SILC_STR_ADVANCE,
-                           SILC_STR_DATA(key, key_len),
-                           SILC_STR_UI32_STRING("\n"),
-                           SILC_STR_UI32_STRING(SILC_SSH_DSA_END),
-                           SILC_STR_UI32_STRING("\n"),
-                           SILC_STR_END) < 0)
-      goto err;
-  }
-
-  silc_sfree(stack, key);
-  key = silc_buffer_steal(&buf, ret_len);
-  return key;
-
- err:
-  if (des)
-    silc_cipher_free(des);
-  if (md5)
-    silc_hash_free(md5);
-  silc_sfree(stack, key);
-  return NULL;
-}
-
-/* Export private key */
-
-SILC_PKCS_EXPORT_PRIVATE_KEY(silc_pkcs_ssh_export_private_key)
-{
-  SilcSshPrivateKey privkey = private_key;
-  const SilcPKCSAlgorithm *alg = privkey->pkcs;
-
-  SILC_LOG_DEBUG(("Encode SSH2 private key"));
-
-  /* Export PKCS algorithm private key */
-  if (alg->export_private_key)
-    return alg->export_private_key(alg, stack,
-                                  privkey->private_key, ret_len);
-  return NULL;
-}
-
-/* Return key length in bits */
-
-SILC_PKCS_PRIVATE_KEY_BITLEN(silc_pkcs_ssh_private_key_bitlen)
-{
-  SilcSshPrivateKey privkey = private_key;
-  return privkey->pkcs->private_key_bitlen(privkey->pkcs,
-                                          privkey->private_key);
-}
-
-/* Free private key */
-
-SILC_PKCS_PRIVATE_KEY_FREE(silc_pkcs_ssh_private_key_free)
-{
-  SilcSshPrivateKey privkey = private_key;
-
-  privkey->pkcs->private_key_free(privkey->pkcs,
-                                 privkey->private_key);
-
-  if (privkey->fields)
-    silc_hash_table_free(privkey->fields);
-  silc_free(privkey);
-}
-
-/* Encrypt */
-
-SILC_PKCS_ENCRYPT(silc_pkcs_ssh_encrypt)
-{
-  SilcSshPublicKey pubkey = public_key;
-
-  if (!pubkey->pkcs->encrypt) {
-    encrypt_cb(FALSE, NULL, 0, context);
-    return NULL;
-  }
-
-  return pubkey->pkcs->encrypt(pubkey->pkcs, pubkey->public_key,
-                              src, src_len, rng, encrypt_cb, context);
-}
-
-/* Decrypt */
-
-SILC_PKCS_DECRYPT(silc_pkcs_ssh_decrypt)
-{
-  SilcSshPrivateKey privkey = private_key;
-
-  if (!privkey->pkcs->decrypt) {
-    decrypt_cb(FALSE, NULL, 0, context);
-    return NULL;
-  }
-
-  return privkey->pkcs->decrypt(privkey->pkcs, privkey->private_key,
-                               src, src_len, decrypt_cb, context);
-}
-
-/* Sign context */
-typedef struct {
-  SilcStack stack;
-  SilcSshPrivateKey privkey;
-  SilcPKCSSignCb sign_cb;
-  void *context;
-} *SilcSshSign;
-
-/* Sign callback.  This formats the signature into SSH2 protocool compliant
-   format. */
-
-static void silc_pkcs_ssh_sign_cb(SilcBool success,
-                                 const unsigned char *signature,
-                                 SilcUInt32 signature_len,
-                                 void *context)
-{
-  SilcSshSign sign = context;
-  SilcStack stack = sign->stack;
-  unsigned char rbuf[20], sbuf[20];
-  SilcBufferStruct sig;
-  SilcAsn1 asn1;
-  SilcMPInt r, s;
-
-  memset(&sig, 0, sizeof(sig));
-
-  /* Format the signature.  RSA is easy because PKCS#1 is already in
-     correct format.  For DSA the returned signature is in PKIX compliant
-     format and we have to reformat it for SSH2. */
-  if (!strcmp(sign->privkey->pkcs->name, "dsa")) {
-    asn1 = silc_asn1_alloc(stack);
-    if (!asn1) {
-      sign->sign_cb(FALSE, NULL, 0, sign->context);
-      silc_sfree(stack, sign);
-      silc_stack_free(stack);
-      return;
-    }
-
-    /* Decode the signature */
-    silc_buffer_set(&sig, (unsigned char *)signature, signature_len);
-    if (!silc_asn1_decode(asn1, &sig,
-                         SILC_ASN1_SEQUENCE,
-                           SILC_ASN1_INT(&r),
-                           SILC_ASN1_INT(&s),
-                         SILC_ASN1_END, SILC_ASN1_END)) {
-      sign->sign_cb(FALSE, NULL, 0, sign->context);
-      silc_asn1_free(asn1);
-      silc_sfree(stack, sign);
-      silc_stack_free(stack);
-      return;
-    }
-
-    /* Encode the integers */
-    memset(rbuf, 0, sizeof(rbuf));
-    memset(sbuf, 0, sizeof(sbuf));
-    silc_mp_mp2bin_noalloc(&r, rbuf, sizeof(rbuf));
-    silc_mp_mp2bin_noalloc(&s, sbuf, sizeof(sbuf));
-
-    silc_asn1_free(asn1);
-
-    /* Encode SSH2 DSS signature */
-    if (silc_buffer_sformat(stack, &sig,
-                           SILC_STR_UI_INT(7),
-                           SILC_STR_UI32_STRING("ssh-dss"),
-                           SILC_STR_UI_INT(sizeof(rbuf) + sizeof(sbuf)),
-                           SILC_STR_DATA(rbuf, sizeof(rbuf)),
-                           SILC_STR_DATA(sbuf, sizeof(sbuf)),
-                           SILC_STR_END) < 0) {
-      sign->sign_cb(FALSE, NULL, 0, sign->context);
-      silc_sfree(stack, sign);
-      silc_stack_free(stack);
-      return;
-    }
-  } else {
-    /* Encode SSH2 RSA signature */
-    if (silc_buffer_sformat(stack, &sig,
-                           SILC_STR_UI_INT(7),
-                           SILC_STR_UI32_STRING("ssh-rsa"),
-                           SILC_STR_UI_INT(signature_len),
-                           SILC_STR_DATA(signature, signature_len),
-                           SILC_STR_END) < 0) {
-      sign->sign_cb(FALSE, NULL, 0, sign->context);
-      silc_sfree(stack, sign);
-      silc_stack_free(stack);
-      return;
-    }
-  }
-
-  /* Deliver result */
-  sign->sign_cb(TRUE, silc_buffer_data(&sig), silc_buffer_len(&sig),
-               sign->context);
-
-  silc_buffer_spurge(stack, &sig);
-  silc_sfree(stack, sign);
-  silc_stack_free(stack);
-}
-
-/* Sign */
-
-SILC_PKCS_SIGN(silc_pkcs_ssh_sign)
-{
-  SilcSshPrivateKey privkey = private_key;
-  SilcStack stack;
-  SilcSshSign sign;
-
-  if (!privkey->pkcs->sign) {
-    sign_cb(FALSE, NULL, 0, context);
-    return NULL;
-  }
-
-  stack = silc_stack_alloc(0, silc_crypto_stack());
-
-  sign = silc_scalloc(stack, 1, sizeof(*sign));
-  if (!sign) {
-    sign_cb(FALSE, NULL, 0, context);
-    silc_stack_free(stack);
-    return NULL;
-  }
-
-  sign->stack = stack;
-  sign->privkey = privkey;
-  sign->sign_cb = sign_cb;
-  sign->context = context;
-
-  /* Sign.  The callback will format it to SSH2 compliant format. */
-  return privkey->pkcs->sign(privkey->pkcs, privkey->private_key,
-                            src, src_len,
-                            compute_hash, hash, rng,
-                            silc_pkcs_ssh_sign_cb, sign);
-}
-
-/* Verify */
-
-SILC_PKCS_VERIFY(silc_pkcs_ssh_verify)
-{
-  SilcSshPublicKey pubkey = public_key;
-  SilcAsyncOperation op;
-  SilcBufferStruct sig, r, s;
-  unsigned char *signame;
-  SilcStack stack = NULL;
-  SilcAsn1 asn1;
-
-  if (!pubkey->pkcs->verify) {
-    verify_cb(FALSE, context);
-    return NULL;
-  }
-
-  /* Decode the SSH2 protocol style signature encoding. */
-  silc_buffer_set(&sig, signature, signature_len);
-  if (silc_buffer_unformat(&sig,
-                          SILC_STR_UI32_STRING_ALLOC(&signame),
-                          SILC_STR_UI32_NSTRING(&signature, &signature_len),
-                          SILC_STR_END) < 0) {
-    verify_cb(FALSE, context);
-    return NULL;
-  }
-  memset(&sig, 0, sizeof(sig));
-
-  /* DSS signature must be formatted to PKIX compliant format since our
-     implementation expects that. */
-  if (!strcmp(signame, "ssh-dss")) {
-    /* The integers must be 160 bits each */
-    if (signature_len != 40) {
-      verify_cb(FALSE, context);
-      silc_free(signame);
-      return NULL;
-    }
-
-    silc_buffer_set(&r, signature, 20);
-    silc_buffer_set(&s, signature + 20, 20);
-
-    stack = silc_stack_alloc(0, silc_crypto_stack());
-
-    asn1 = silc_asn1_alloc(stack);
-    if (!asn1) {
-      verify_cb(FALSE, context);
-      silc_free(signame);
-      silc_stack_free(stack);
-      return NULL;
-    }
-
-    /* Encode signature to PKIX compliant format. */
-    if (!silc_asn1_encode(asn1, &sig,
-                         SILC_ASN1_OPTS(SILC_ASN1_ALLOC),
-                         SILC_ASN1_SEQUENCE,
-                           SILC_ASN1_ANY_PRIMITIVE(SILC_ASN1_TAG_INTEGER, &r),
-                           SILC_ASN1_ANY_PRIMITIVE(SILC_ASN1_TAG_INTEGER, &s),
-                         SILC_ASN1_END, SILC_ASN1_END)) {
-      verify_cb(FALSE, context);
-      silc_free(signame);
-      silc_asn1_free(asn1);
-      silc_stack_free(stack);
-      return NULL;
-    }
-
-    signature = silc_buffer_steal(&sig, &signature_len);
-
-    silc_asn1_free(asn1);
-  }
-
-  /* Verify */
-  op = pubkey->pkcs->verify(pubkey->pkcs, pubkey->public_key,
-                           signature, signature_len,
-                           data, data_len, hash, rng,
-                           verify_cb, context);
-
-  silc_free(signame);
-  silc_buffer_spurge(stack, &sig);
-  silc_stack_free(stack);
-
-  return op;
-}
-
-/************************** SSH2 PKCS RSA Alg API ***************************/
-
-/* The SSH2 RSA PKCS Algorithm API.  We implement here only the necessary
-   parts of the API and the common code is used from PKCS#1 Algorithm API
-   in silccrypt/silcpkcs1.c.  Basically everything else is PKCS#1 except
-   the format of the public key. */
-
-/* Import RSA public key.  Both RFC 4716 and OpenSSH have same format. */
-
-SILC_PKCS_ALG_IMPORT_PUBLIC_KEY(silc_ssh_rsa_import_public_key)
-{
-  SilcBufferStruct alg_key;
-  RsaPublicKey *pubkey;
-  unsigned char *n, *e;
-  SilcUInt32 n_len, e_len;
-  int ret;
-
-  SILC_LOG_DEBUG(("Import public key"));
-
-  if (!ret_public_key)
-    return 0;
-
-  /* Allocate RSA public key */
-  *ret_public_key = pubkey = silc_calloc(1, sizeof(*pubkey));
-  if (!pubkey)
-    return 0;
-
-  /* Parse SSH2 RSA public key */
-  silc_buffer_set(&alg_key, key, key_len);
-  ret = silc_buffer_unformat(&alg_key,
-                            SILC_STR_UI32_NSTRING(&e, &e_len),
-                            SILC_STR_UI32_NSTRING(&n, &n_len),
-                            SILC_STR_END);
-  if (ret < 0)
-    goto err;
-  if (!n_len || !e_len)
-    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) + 7) / 8) * 8;
-
-  return ret;
-
- err:
-  silc_free(pubkey);
-  return 0;
-}
-
-/* Export RSA public key.  Both RFC 4716 and OpenSSH have same format. */
-
-SILC_PKCS_ALG_EXPORT_PUBLIC_KEY(silc_ssh_rsa_export_public_key)
-{
-  RsaPublicKey *pubkey = public_key;
-  SilcBufferStruct alg_key;
-  unsigned char *n = NULL, *e = NULL, *ret;
-  SilcUInt32 n_len, e_len;
-
-  SILC_LOG_DEBUG(("Export public key"));
-
-  /* Encode MP integers */
-  n = silc_mp_mp2bin(&pubkey->n, 0, &n_len);
-  if (!n)
-    goto err;
-  e = silc_mp_mp2bin(&pubkey->e, 0, &e_len);
-  if (!e)
-    goto err;
-
-  /* Encode SSH2 RSA public key */
-  memset(&alg_key, 0, sizeof(alg_key));
-  if (silc_buffer_sformat(stack, &alg_key,
-                         SILC_STR_UI_INT(e_len),
-                         SILC_STR_DATA(e, e_len),
-                         SILC_STR_UI_INT(n_len),
-                         SILC_STR_DATA(n, n_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;
-}
-
-/************************** SSH2 PKCS DSA Alg API ***************************/
-
-/* The SSH2 DSA PKCS Algorithm API.  We implement here only the necessary
-   parts of the API and the common code is used from DSS Algorithm API
-   in silccrypt/dsa.c. */
-
-/* Import DSA public key.  Both RFC 4716 and OpenSSH have same format. */
-
-SILC_PKCS_ALG_IMPORT_PUBLIC_KEY(silc_ssh_dsa_import_public_key)
-{
-  SilcBufferStruct alg_key;
-  DsaPublicKey *pubkey;
-  unsigned char *p, *q, *g, *y;
-  SilcUInt32 p_len, q_len, g_len, y_len;
-  int ret;
-
-  SILC_LOG_DEBUG(("Import public key"));
-
-  if (!ret_public_key)
-    return 0;
-
-  /* Allocate DSA public key */
-  *ret_public_key = pubkey = silc_calloc(1, sizeof(*pubkey));
-  if (!pubkey)
-    return 0;
-
-  /* Parse SSH2 DSA public key */
-  silc_buffer_set(&alg_key, key, key_len);
-  ret = silc_buffer_unformat(&alg_key,
-                            SILC_STR_UI32_NSTRING(&p, &p_len),
-                            SILC_STR_UI32_NSTRING(&q, &q_len),
-                            SILC_STR_UI32_NSTRING(&g, &g_len),
-                            SILC_STR_UI32_NSTRING(&y, &y_len),
-                            SILC_STR_END);
-  if (ret < 0)
-    goto err;
-  if (!p_len || !q_len || !g_len || !y_len)
-    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) + 7) / 8) * 8;
-
-  return ret;
-
- err:
-  silc_free(pubkey);
-  return 0;
-}
-
-/* Export DSA public key.  Both RFC 4716 and OpenSSH have same format. */
-
-SILC_PKCS_ALG_EXPORT_PUBLIC_KEY(silc_ssh_dsa_export_public_key)
-{
-  DsaPublicKey *pubkey = public_key;
-  SilcBufferStruct alg_key;
-  unsigned char *p = NULL, *q = NULL, *g = NULL, *y = NULL, *ret;
-  SilcUInt32 p_len, q_len, g_len, y_len;
-
-  SILC_LOG_DEBUG(("Export public key"));
-
-  /* Encode MP integers */
-  p = silc_mp_mp2bin(&pubkey->p, 0, &p_len);
-  if (!p)
-    goto err;
-  q = silc_mp_mp2bin(&pubkey->q, 0, &q_len);
-  if (!q)
-    goto err;
-  g = silc_mp_mp2bin(&pubkey->g, 0, &g_len);
-  if (!g)
-    goto err;
-  y = silc_mp_mp2bin(&pubkey->y, 0, &y_len);
-  if (!y)
-    goto err;
-
-  /* Encode SSH2 DSA public key */
-  memset(&alg_key, 0, sizeof(alg_key));
-  if (silc_buffer_sformat(stack, &alg_key,
-                         SILC_STR_UI_INT(p_len),
-                         SILC_STR_DATA(p, p_len),
-                         SILC_STR_UI_INT(q_len),
-                         SILC_STR_DATA(q, q_len),
-                         SILC_STR_UI_INT(g_len),
-                         SILC_STR_DATA(g, g_len),
-                         SILC_STR_UI_INT(y_len),
-                         SILC_STR_DATA(y, y_len),
-                         SILC_STR_END) < 0)
-    goto err;
-
-  silc_free(p);
-  silc_free(q);
-  silc_free(g);
-  silc_free(y);
-
-  ret = silc_buffer_steal(&alg_key, ret_len);
-  return ret;
-
- err:
-  silc_free(p);
-  silc_free(q);
-  silc_free(g);
-  silc_free(y);
-  return NULL;
-}