Merge branch 'topic/mm-fixes' of git://208.110.73.182/silc into silc.1.1.branch
[silc.git] / lib / silccrypt / silcpk.c
index b0fd72835fcab0a660545a286e0b0117e8193d2e..e4a000788c79f71fdae9da4e7516470d498d612f 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2006 Pekka Riikonen
+  Copyright (C) 1997 - 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
@@ -25,7 +25,6 @@
 /* Generate new SILC key pair. */
 
 SilcBool silc_pkcs_silc_generate_key(const char *algorithm,
-                                    const char *scheme,
                                     SilcUInt32 bits_key_len,
                                     const char *identifier,
                                     SilcRng rng,
@@ -36,6 +35,7 @@ SilcBool silc_pkcs_silc_generate_key(const char *algorithm,
   SilcSILCPrivateKey privkey;
   const SilcPKCSAlgorithm *alg;
   const SilcPKCSObject *pkcs;
+  SilcUInt32 version;
 
   SILC_LOG_DEBUG(("Generating SILC %s key pair with key length %d bits",
                  algorithm, bits_key_len));
@@ -47,21 +47,28 @@ SilcBool silc_pkcs_silc_generate_key(const char *algorithm,
   if (!pkcs)
     return FALSE;
 
-  alg = silc_pkcs_find_algorithm(algorithm, scheme);
-  if (!alg)
-    return FALSE;
-
   /* Allocate SILC public key */
   pubkey = silc_calloc(1, sizeof(*pubkey));
   if (!pubkey)
     return FALSE;
-  pubkey->pkcs = alg;
 
   /* Decode identifier */
-  if (!silc_pkcs_silc_decode_identifier(identifier, &pubkey->identifier)) {
+  if (!silc_pkcs_silc_decode_identifier(identifier, &pubkey->identifier))
+    return FALSE;
+
+  if (pubkey->identifier.version && atoi(pubkey->identifier.version) >= 2)
+    version = 2;
+  else
+    version = 1;
+
+  /* Allocate algorithm */
+  alg = silc_pkcs_find_algorithm(algorithm, (version == 1 ? "pkcs1-no-oid" :
+                                            "pkcs1"));
+  if (!alg) {
     silc_free(pubkey);
     return FALSE;
   }
+  pubkey->pkcs = alg;
 
   /* Allocate SILC private key */
   privkey = silc_calloc(1, sizeof(*privkey));
@@ -117,7 +124,7 @@ SilcBool silc_pkcs_silc_decode_identifier(const char *identifier,
   int len;
 
   /* Protocol says that at least UN and HN must be provided as identifier */
-  if (!strstr(identifier, "UN=") && !strstr(identifier, "HN=")) {
+  if (!strstr(identifier, "UN=") || !strstr(identifier, "HN=")) {
     SILC_LOG_DEBUG(("The public does not have the required UN= and HN= "
                    "identifiers"));
     return FALSE;
@@ -169,6 +176,8 @@ SilcBool silc_pkcs_silc_decode_identifier(const char *identifier,
       ident->org = strdup(item + strcspn(cp, "=") + 1);
     else if (strstr(item, "C="))
       ident->country = strdup(item + strcspn(cp, "=") + 1);
+    else if (strstr(item, "V="))
+      ident->version = strdup(item + strcspn(cp, "=") + 1);
 
     cp += len;
     if (strlen(cp) < 1)
@@ -189,100 +198,102 @@ SilcBool silc_pkcs_silc_decode_identifier(const char *identifier,
 
 char *silc_pkcs_silc_encode_identifier(char *username, char *host,
                                       char *realname, char *email,
-                                      char *org, char *country)
+                                      char *org, char *country,
+                                      char *version)
 {
-  SilcBuffer buf;
+  SilcBufferStruct buf;
   char *identifier;
-  SilcUInt32 len, tlen = 0;
 
   if (!username || !host)
     return NULL;
-
-  len = (username ? strlen(username) : 0) +
-       (host     ? strlen(host)     : 0) +
-       (realname ? strlen(realname) : 0) +
-       (email    ? strlen(email)    : 0) +
-       (org      ? strlen(org)      : 0) +
-       (country  ? strlen(country)  : 0);
-
-  if (len < 3)
+  if (strlen(username) < 1 || strlen(host) < 1)
     return NULL;
 
-  len += 3 + 5 + 5 + 4 + 4 + 4;
-  buf = silc_buffer_alloc(len);
-  if (!buf)
-    return NULL;
-  silc_buffer_pull_tail(buf, len);
+  memset(&buf, 0, sizeof(buf));
 
-  if (username) {
-    silc_buffer_format(buf,
+  if (username)
+    silc_buffer_format(&buf,
+                      SILC_STR_ADVANCE,
                       SILC_STR_UI32_STRING("UN="),
                       SILC_STR_UI32_STRING(username),
                       SILC_STR_END);
-    silc_buffer_pull(buf, 3 + strlen(username));
-    tlen = 3 + strlen(username);
-  }
 
-  if (host) {
-    silc_buffer_format(buf,
+  if (host)
+    silc_buffer_format(&buf,
+                      SILC_STR_ADVANCE,
                       SILC_STR_UI32_STRING(", "),
                       SILC_STR_UI32_STRING("HN="),
                       SILC_STR_UI32_STRING(host),
                       SILC_STR_END);
-    silc_buffer_pull(buf, 5 + strlen(host));
-    tlen += 5 + strlen(host);
-  }
 
-  if (realname) {
-    silc_buffer_format(buf,
+  if (realname)
+    silc_buffer_format(&buf,
+                      SILC_STR_ADVANCE,
                       SILC_STR_UI32_STRING(", "),
                       SILC_STR_UI32_STRING("RN="),
                       SILC_STR_UI32_STRING(realname),
                       SILC_STR_END);
-    silc_buffer_pull(buf, 5 + strlen(realname));
-    tlen += 5 + strlen(realname);
-  }
 
-  if (email) {
-    silc_buffer_format(buf,
+  if (email)
+    silc_buffer_format(&buf,
+                      SILC_STR_ADVANCE,
                       SILC_STR_UI32_STRING(", "),
                       SILC_STR_UI32_STRING("E="),
                       SILC_STR_UI32_STRING(email),
                       SILC_STR_END);
-    silc_buffer_pull(buf, 4 + strlen(email));
-    tlen += 4 + strlen(email);
-  }
 
-  if (org) {
-    silc_buffer_format(buf,
+  if (org)
+    silc_buffer_format(&buf,
+                      SILC_STR_ADVANCE,
                       SILC_STR_UI32_STRING(", "),
                       SILC_STR_UI32_STRING("O="),
                       SILC_STR_UI32_STRING(org),
                       SILC_STR_END);
-    silc_buffer_pull(buf, 4 + strlen(org));
-    tlen += 4 + strlen(org);
-  }
 
-  if (country) {
-    silc_buffer_format(buf,
+  if (country)
+    silc_buffer_format(&buf,
+                      SILC_STR_ADVANCE,
                       SILC_STR_UI32_STRING(", "),
                       SILC_STR_UI32_STRING("C="),
                       SILC_STR_UI32_STRING(country),
                       SILC_STR_END);
-    silc_buffer_pull(buf, 4 + strlen(country));
-    tlen += 4 + strlen(country);
+
+  if (version) {
+    if (strlen(version) > 1 || !isdigit(version[0])) {
+      silc_buffer_purge(&buf);
+      return NULL;
+    }
+    silc_buffer_format(&buf,
+                      SILC_STR_ADVANCE,
+                      SILC_STR_UI32_STRING(", "),
+                      SILC_STR_UI32_STRING("V="),
+                      SILC_STR_UI32_STRING(version),
+                      SILC_STR_END);
   }
 
-  silc_buffer_push(buf, buf->data - buf->head);
-  identifier = silc_calloc(tlen + 1, sizeof(*identifier));
-  if (!identifier)
-    return NULL;
-  memcpy(identifier, buf->data, tlen);
-  silc_buffer_free(buf);
+  silc_buffer_format(&buf, SILC_STR_UI_CHAR(0), SILC_STR_END);
 
+  identifier = silc_buffer_steal(&buf, NULL);
   return identifier;
 }
 
+/* Return SILC public key version */
+
+int silc_pkcs_silc_public_key_version(SilcPublicKey public_key)
+{
+  SilcSILCPublicKey silc_pubkey;
+
+  if (silc_pkcs_get_type(public_key) != SILC_PKCS_SILC)
+    return -1;
+
+  silc_pubkey = public_key->public_key;
+
+  /* If version identifire is not present it is version 1. */
+  if (!silc_pubkey->identifier.version)
+    return 1;
+
+  return atoi(silc_pubkey->identifier.version);
+}
 
 /*************************** Public key routines *****************************/
 
@@ -303,7 +314,7 @@ SilcBool silc_pkcs_silc_import_public_key_file(unsigned char *filedata,
 {
   SilcUInt32 i, len;
   unsigned char *data = NULL;
-  SilcBool ret;
+  int ret;
 
   SILC_LOG_DEBUG(("Parsing SILC public key file"));
 
@@ -312,11 +323,15 @@ SilcBool silc_pkcs_silc_import_public_key_file(unsigned char *filedata,
 
   /* Check start of file and remove header from the data. */
   len = strlen(SILC_PKCS_PUBLIC_KEYFILE_BEGIN);
-  if (filedata_len < len + strlen(SILC_PKCS_PUBLIC_KEYFILE_END))
+  if (filedata_len < len + strlen(SILC_PKCS_PUBLIC_KEYFILE_END)) {
+    SILC_LOG_ERROR(("Malformed SILC public key header"));
     return FALSE;
+  }
   for (i = 0; i < len; i++) {
-    if (*filedata != SILC_PKCS_PUBLIC_KEYFILE_BEGIN[i])
+    if (*filedata != SILC_PKCS_PUBLIC_KEYFILE_BEGIN[i]) {
+      SILC_LOG_ERROR(("Malformed SILC public key header"));
       return FALSE;
+    }
     filedata++;
   }
   filedata_len -= (strlen(SILC_PKCS_PUBLIC_KEYFILE_BEGIN) +
@@ -327,7 +342,7 @@ SilcBool silc_pkcs_silc_import_public_key_file(unsigned char *filedata,
     break;
 
   case SILC_PKCS_FILE_BASE64:
-    data = silc_pem_decode(filedata, filedata_len, &filedata_len);
+    data = silc_base64_decode(filedata, filedata_len, &filedata_len);
     if (!data)
       return FALSE;
     filedata = data;
@@ -338,14 +353,14 @@ SilcBool silc_pkcs_silc_import_public_key_file(unsigned char *filedata,
                                         ret_public_key);
   silc_free(data);
 
-  return ret;
+  return ret ? TRUE : FALSE;
 }
 
 /* Imports SILC protocol style public key */
 
-SilcBool silc_pkcs_silc_import_public_key(unsigned char *key,
-                                         SilcUInt32 key_len,
-                                         void **ret_public_key)
+int silc_pkcs_silc_import_public_key(unsigned char *key,
+                                    SilcUInt32 key_len,
+                                    void **ret_public_key)
 {
   const SilcPKCSAlgorithm *pkcs;
   SilcBufferStruct buf, alg_key;
@@ -359,12 +374,13 @@ SilcBool silc_pkcs_silc_import_public_key(unsigned char *key,
   SILC_LOG_DEBUG(("Parsing SILC public key"));
 
   if (!ret_public_key)
-    return FALSE;
+    return 0;
 
   silc_buffer_set(&buf, key, key_len);
 
   /* Get length */
   ret = silc_buffer_unformat(&buf,
+                            SILC_STR_ADVANCE,
                             SILC_STR_UI_INT(&totlen),
                             SILC_STR_END);
   if (ret == -1)
@@ -380,7 +396,7 @@ SilcBool silc_pkcs_silc_import_public_key(unsigned char *key,
   /* Get algorithm name and identifier */
   ret =
     silc_buffer_unformat(&buf,
-                        SILC_STR_OFFSET(4),
+                        SILC_STR_ADVANCE,
                         SILC_STR_UI16_NSTRING_ALLOC(&pkcs_name, &pkcs_len),
                         SILC_STR_UI16_NSTRING_ALLOC(&ident, &identifier_len),
                         SILC_STR_END);
@@ -392,11 +408,9 @@ SilcBool silc_pkcs_silc_import_public_key(unsigned char *key,
     goto err;
 
   /* Get key data */
-  silc_buffer_pull(&buf, 4 + 2 + pkcs_len + 2 + identifier_len);
   keydata_len = silc_buffer_len(&buf);
   ret = silc_buffer_unformat(&buf,
-                            SILC_STR_UI_XNSTRING(&key_data,
-                                                 keydata_len),
+                            SILC_STR_DATA(&key_data, keydata_len),
                             SILC_STR_END);
   if (ret == -1)
     goto err;
@@ -414,15 +428,27 @@ SilcBool silc_pkcs_silc_import_public_key(unsigned char *key,
   if (!asn1)
     goto err;
 
+  SILC_LOG_DEBUG(("Public key version %s",
+                 (!silc_pubkey->identifier.version ? "1" :
+                  silc_pubkey->identifier.version)));
+
   if (!strcmp(pkcs_name, "rsa")) {
     /* Parse the SILC RSA public key */
     SilcUInt32 e_len, n_len;
     SilcMPInt n, e;
 
-    /* Get PKCS object */
-    pkcs = silc_pkcs_find_algorithm(pkcs_name, "pkcs1-no-oid");
+    /* Get PKCS object.  Different PKCS #1 scheme is used with different
+       versions. */
+    if (!silc_pubkey->identifier.version ||
+       atoi(silc_pubkey->identifier.version) <= 1) {
+      /* Version 1 */
+      pkcs = silc_pkcs_find_algorithm(pkcs_name, "pkcs1-no-oid");
+    } else {
+      /* Version 2 and newer */
+      pkcs = silc_pkcs_find_algorithm(pkcs_name, "pkcs1");
+    }
     if (!pkcs) {
-      SILC_LOG_DEBUG(("Unsupported PKCS algorithm"));
+      SILC_LOG_DEBUG(("Unsupported PKCS algorithm: rsa"));
       goto err;
     }
     silc_pubkey->pkcs = pkcs;
@@ -471,10 +497,9 @@ SilcBool silc_pkcs_silc_import_public_key(unsigned char *key,
   }
 
   /* Import PKCS algorithm public key */
-  if (pkcs->import_public_key)
-    if (!pkcs->import_public_key(alg_key.data, silc_buffer_len(&alg_key),
-                                &silc_pubkey->public_key))
-      goto err;
+  if (!pkcs->import_public_key(alg_key.data, silc_buffer_len(&alg_key),
+                              &silc_pubkey->public_key))
+    goto err;
 
   silc_free(pkcs_name);
   silc_free(ident);
@@ -482,7 +507,7 @@ SilcBool silc_pkcs_silc_import_public_key(unsigned char *key,
 
   *ret_public_key = silc_pubkey;
 
-  return TRUE;
+  return key_len;
 
  err:
   silc_free(pkcs_name);
@@ -490,7 +515,7 @@ SilcBool silc_pkcs_silc_import_public_key(unsigned char *key,
   silc_free(silc_pubkey);
   if (asn1)
     silc_asn1_free(asn1);
-  return FALSE;
+  return 0;
 }
 
 /* Exports public key as SILC protocol style public key file */
@@ -516,7 +541,7 @@ silc_pkcs_silc_export_public_key_file(void *public_key,
     break;
 
   case SILC_PKCS_FILE_BASE64:
-    data = silc_pem_encode_file(key, key_len);
+    data = silc_base64_encode_file(key, key_len);
     if (!data)
       return NULL;
     silc_free(key);
@@ -570,8 +595,10 @@ unsigned char *silc_pkcs_silc_export_public_key(void *public_key,
   /* Export PKCS algorithm public key */
   if (pkcs->export_public_key)
     pk = pkcs->export_public_key(silc_pubkey->public_key, &pk_len);
-  if (!pk)
+  if (!pk) {
+    SILC_LOG_ERROR(("Error exporting PKCS algorithm key"));
     return NULL;
+  }
   silc_buffer_set(&alg_key, pk, pk_len);
 
   /* Encode identifier */
@@ -581,9 +608,12 @@ unsigned char *silc_pkcs_silc_export_public_key(void *public_key,
                                     silc_pubkey->identifier.realname,
                                     silc_pubkey->identifier.email,
                                     silc_pubkey->identifier.org,
-                                    silc_pubkey->identifier.country);
-  if (!identifier)
+                                    silc_pubkey->identifier.country,
+                                    silc_pubkey->identifier.version);
+  if (!identifier) {
+    SILC_LOG_ERROR(("Error encoding SILC public key identifier"));
     goto err;
+  }
 
   asn1 = silc_asn1_alloc();
   if (!asn1)
@@ -632,7 +662,7 @@ unsigned char *silc_pkcs_silc_export_public_key(void *public_key,
     goto err;
 
   } else {
-    SILC_LOG_DEBUG(("Unsupported PKCS algorithm"));
+    SILC_LOG_ERROR(("Unsupported PKCS algorithm: %s", pkcs->name));
     goto err;
   }
 
@@ -655,6 +685,7 @@ unsigned char *silc_pkcs_silc_export_public_key(void *public_key,
   silc_buffer_free(buf);
   silc_free(key);
   silc_free(identifier);
+  silc_buffer_purge(&alg_key);
   silc_asn1_free(asn1);
 
   return ret;
@@ -683,6 +714,7 @@ SilcUInt32 silc_pkcs_silc_public_key_bitlen(void *public_key)
 void *silc_pkcs_silc_public_key_copy(void *public_key)
 {
   SilcSILCPublicKey silc_pubkey = public_key, new_pubkey;
+  SilcPublicKeyIdentifier ident = &silc_pubkey->identifier;
 
   new_pubkey = silc_calloc(1, sizeof(*new_pubkey));
   if (!new_pubkey)
@@ -696,6 +728,28 @@ void *silc_pkcs_silc_public_key_copy(void *public_key)
     return NULL;
   }
 
+  if (ident->username)
+    new_pubkey->identifier.username =
+      silc_memdup(ident->username, strlen(ident->username));
+  if (ident->host)
+    new_pubkey->identifier.host =
+      silc_memdup(ident->host, strlen(ident->host));
+  if (ident->realname)
+    new_pubkey->identifier.realname =
+      silc_memdup(ident->realname, strlen(ident->realname));
+  if (ident->email)
+    new_pubkey->identifier.email =
+      silc_memdup(ident->email, strlen(ident->email));
+  if (ident->org)
+    new_pubkey->identifier.org =
+      silc_memdup(ident->org, strlen(ident->org));
+  if (ident->country)
+    new_pubkey->identifier.country =
+      silc_memdup(ident->country, strlen(ident->country));
+  if (ident->version)
+    new_pubkey->identifier.version =
+      silc_memdup(ident->version, strlen(ident->version));
+
   return new_pubkey;
 }
 
@@ -744,6 +798,12 @@ SilcBool silc_pkcs_silc_public_key_compare(void *key1, void *key2)
        strcmp(k1->identifier.country, k2->identifier.country)))
     return FALSE;
 
+  if ((k1->identifier.version && !k2->identifier.version) ||
+      (!k1->identifier.version && k2->identifier.version) ||
+      (k1->identifier.version && k2->identifier.version &&
+       strcmp(k1->identifier.version, k2->identifier.version)))
+    return FALSE;
+
   return k1->pkcs->public_key_compare(k1->public_key, k2->public_key);
 }
 
@@ -761,6 +821,7 @@ void silc_pkcs_silc_public_key_free(void *public_key)
   silc_free(silc_pubkey->identifier.email);
   silc_free(silc_pubkey->identifier.org);
   silc_free(silc_pubkey->identifier.country);
+  silc_free(silc_pubkey->identifier.version);
   silc_free(silc_pubkey);
 }
 
@@ -785,17 +846,21 @@ SilcBool silc_pkcs_silc_import_private_key_file(unsigned char *filedata,
   SilcUInt32 blocklen;
   unsigned char tmp[32], keymat[64], *data = NULL;
   SilcUInt32 i, len, magic, mac_len;
-  SilcBool ret;
+  int ret;
 
   SILC_LOG_DEBUG(("Parsing SILC private key file"));
 
   /* Check start of file and remove header from the data. */
   len = strlen(SILC_PKCS_PRIVATE_KEYFILE_BEGIN);
-  if (filedata_len < len + strlen(SILC_PKCS_PRIVATE_KEYFILE_END))
+  if (filedata_len < len + strlen(SILC_PKCS_PRIVATE_KEYFILE_END)) {
+    SILC_LOG_ERROR(("Malformed SILC private key header"));
     return FALSE;
+  }
   for (i = 0; i < len; i++) {
-    if (*filedata != SILC_PKCS_PRIVATE_KEYFILE_BEGIN[i])
+    if (*filedata != SILC_PKCS_PRIVATE_KEYFILE_BEGIN[i]) {
+      SILC_LOG_ERROR(("Malformed SILC private key header"));
       return FALSE;
+    }
     filedata++;
   }
 
@@ -807,7 +872,7 @@ SilcBool silc_pkcs_silc_import_private_key_file(unsigned char *filedata,
     break;
 
   case SILC_PKCS_FILE_BASE64:
-    data = silc_pem_decode(filedata, filedata_len, &len);
+    data = silc_base64_decode(filedata, filedata_len, &len);
     if (!data)
       return FALSE;
     filedata = data;
@@ -863,7 +928,7 @@ SilcBool silc_pkcs_silc_import_private_key_file(unsigned char *filedata,
   silc_hash_final(sha1, keymat + 16);
 
   /* Set the key to the cipher */
-  silc_cipher_set_key(aes, keymat, 256);
+  silc_cipher_set_key(aes, keymat, 256, FALSE);
 
   /* First, verify the MAC of the private key data */
   mac_len = silc_hmac_len(sha1hmac);
@@ -909,17 +974,18 @@ SilcBool silc_pkcs_silc_import_private_key_file(unsigned char *filedata,
 
   silc_free(data);
 
-  return ret;
+  return ret ? TRUE : FALSE;
 }
 
 /* Private key version */
 #define SILC_PRIVATE_KEY_VERSION_1 0x82171273
+#define SILC_PRIVATE_KEY_VERSION_2 0xf911a3d1
 
 /* Imports SILC implementation style private key */
 
-SilcBool silc_pkcs_silc_import_private_key(unsigned char *key,
-                                          SilcUInt32 key_len,
-                                          void **ret_private_key)
+int silc_pkcs_silc_import_private_key(unsigned char *key,
+                                     SilcUInt32 key_len,
+                                     void **ret_private_key)
 {
   SilcBufferStruct buf;
   const SilcPKCSAlgorithm *pkcs;
@@ -934,7 +1000,7 @@ SilcBool silc_pkcs_silc_import_private_key(unsigned char *key,
   SILC_LOG_DEBUG(("Parsing SILC private key"));
 
   if (!ret_private_key)
-    return FALSE;
+    return 0;
 
   silc_buffer_set(&buf, key, key_len);
 
@@ -978,17 +1044,8 @@ SilcBool silc_pkcs_silc_import_private_key(unsigned char *key,
     unsigned char *tmp;
     SilcUInt32 len, ver;
 
-    /* Get PKCS object */
-    pkcs = silc_pkcs_find_algorithm(pkcs_name, "pkcs1-no-oid");
-    if (!pkcs) {
-      SILC_LOG_DEBUG(("Unsupported PKCS algorithm"));
-      goto err;
-    }
-    silc_privkey->pkcs = pkcs;
-
     if (keydata_len < 4)
       goto err;
-
     silc_buffer_set(&k, key_data, keydata_len);
 
     /* Get version.  Key without the version is old style private key
@@ -999,8 +1056,10 @@ SilcBool silc_pkcs_silc_import_private_key(unsigned char *key,
       goto err;
     silc_buffer_pull(&k, 4);
 
-    if (ver != SILC_PRIVATE_KEY_VERSION_1) {
+    if (ver != SILC_PRIVATE_KEY_VERSION_1 &&
+       ver != SILC_PRIVATE_KEY_VERSION_2) {
       len = ver;
+      ver = 0;
     } else {
       if (silc_buffer_unformat(&k,
                               SILC_STR_UI_INT(&len),
@@ -1009,9 +1068,28 @@ SilcBool silc_pkcs_silc_import_private_key(unsigned char *key,
       silc_buffer_pull(&k, 4);
     }
 
+    /* Get PKCS object.  Different PKCS #1 scheme is used with different
+       versions. */
+    if (ver == 0 || ver == SILC_PRIVATE_KEY_VERSION_1) {
+      /* Version 0 and 1 */
+      pkcs = silc_pkcs_find_algorithm(pkcs_name, "pkcs1-no-oid");
+    } else {
+      /* Version 2 and newer */
+      pkcs = silc_pkcs_find_algorithm(pkcs_name, "pkcs1");
+    }
+    if (!pkcs) {
+      SILC_LOG_DEBUG(("Unsupported PKCS algorithm"));
+      goto err;
+    }
+    silc_privkey->pkcs = pkcs;
+
+    SILC_LOG_DEBUG(("Private key version %s",
+                   (ver == SILC_PRIVATE_KEY_VERSION_1 ? "1" :
+                    ver == SILC_PRIVATE_KEY_VERSION_2 ? "2" : "0")));
+
     /* Get e */
     if (silc_buffer_unformat(&k,
-                            SILC_STR_UI_XNSTRING(&tmp, len),
+                            SILC_STR_DATA(&tmp, len),
                             SILC_STR_END) < 0)
       goto err;
     silc_mp_init(&e);
@@ -1025,7 +1103,7 @@ SilcBool silc_pkcs_silc_import_private_key(unsigned char *key,
       goto err;
     silc_buffer_pull(&k, 4);
     if (silc_buffer_unformat(&k,
-                            SILC_STR_UI_XNSTRING(&tmp, len),
+                            SILC_STR_DATA(&tmp, len),
                             SILC_STR_END) < 0)
       goto err;
     silc_mp_init(&n);
@@ -1039,7 +1117,7 @@ SilcBool silc_pkcs_silc_import_private_key(unsigned char *key,
       goto err;
     silc_buffer_pull(&k, 4);
     if (silc_buffer_unformat(&k,
-                            SILC_STR_UI_XNSTRING(&tmp, len),
+                            SILC_STR_DATA(&tmp, len),
                             SILC_STR_END) < 0)
       goto err;
     silc_mp_init(&d);
@@ -1053,7 +1131,7 @@ SilcBool silc_pkcs_silc_import_private_key(unsigned char *key,
       goto err;
     silc_buffer_pull(&k, 4);
     if (silc_buffer_unformat(&k,
-                            SILC_STR_UI_XNSTRING(&tmp, len),
+                            SILC_STR_DATA(&tmp, len),
                             SILC_STR_END) < 0)
       goto err;
     silc_mp_init(&dp);
@@ -1067,14 +1145,14 @@ SilcBool silc_pkcs_silc_import_private_key(unsigned char *key,
       goto err;
     silc_buffer_pull(&k, 4);
     if (silc_buffer_unformat(&k,
-                            SILC_STR_UI_XNSTRING(&tmp, len),
+                            SILC_STR_DATA(&tmp, len),
                             SILC_STR_END) < 0)
       goto err;
     silc_mp_init(&dq);
     silc_mp_bin2mp(tmp, len, &dq);
     silc_buffer_pull(&k, len);
 
-    if (ver != SILC_PRIVATE_KEY_VERSION_1) {
+    if (ver == 0) {
       /* Old version */
 
       /* Get pQ len */
@@ -1106,7 +1184,7 @@ SilcBool silc_pkcs_silc_import_private_key(unsigned char *key,
        goto err;
       silc_buffer_pull(&k, 4);
       if (silc_buffer_unformat(&k,
-                              SILC_STR_UI_XNSTRING(&tmp, len),
+                              SILC_STR_DATA(&tmp, len),
                               SILC_STR_END) < 0)
        goto err;
       silc_mp_init(&qp);
@@ -1121,7 +1199,7 @@ SilcBool silc_pkcs_silc_import_private_key(unsigned char *key,
       goto err;
     silc_buffer_pull(&k, 4);
     if (silc_buffer_unformat(&k,
-                            SILC_STR_UI_XNSTRING(&tmp, len),
+                            SILC_STR_DATA(&tmp, len),
                             SILC_STR_END) < 0)
       goto err;
     silc_mp_init(&p);
@@ -1135,14 +1213,14 @@ SilcBool silc_pkcs_silc_import_private_key(unsigned char *key,
       goto err;
     silc_buffer_pull(&k, 4);
     if (silc_buffer_unformat(&k,
-                            SILC_STR_UI_XNSTRING(&tmp, len),
+                            SILC_STR_DATA(&tmp, len),
                             SILC_STR_END) < 0)
       goto err;
     silc_mp_init(&q);
     silc_mp_bin2mp(tmp, len, &q);
     silc_buffer_pull(&k, len);
 
-    if (ver != SILC_PRIVATE_KEY_VERSION_1) {
+    if (ver == 0) {
       /* Old version.  Compute to new version */
       SILC_LOG_DEBUG(("Old version private key"));
       silc_mp_init(&qp);
@@ -1185,24 +1263,24 @@ SilcBool silc_pkcs_silc_import_private_key(unsigned char *key,
   }
 
   /* Import PKCS algorithm private key */
-  if (pkcs->import_private_key)
-    if (!pkcs->import_private_key(alg_key.data, silc_buffer_len(&alg_key),
-                                 &silc_privkey->private_key))
-      goto err;
+  if (!pkcs->import_private_key(alg_key.data, silc_buffer_len(&alg_key),
+                               &silc_privkey->private_key))
+    goto err;
 
   silc_free(pkcs_name);
   silc_asn1_free(asn1);
 
   *ret_private_key = silc_privkey;
 
-  return TRUE;
+  return key_len;
 
  err:
   silc_free(pkcs_name);
   silc_free(silc_privkey);
   if (asn1)
     silc_asn1_free(asn1);
-  return FALSE;
+  SILC_LOG_ERROR(("Malformed SILC private key "));
+  return 0;
 }
 
 /* Exports private key as SILC implementation style private key file */
@@ -1275,7 +1353,7 @@ silc_pkcs_silc_export_private_key_file(void *private_key,
   silc_hash_final(sha1, keymat + 16);
 
   /* Set the key to the cipher */
-  silc_cipher_set_key(aes, keymat, 256);
+  silc_cipher_set_key(aes, keymat, 256, TRUE);
 
   /* Encode the buffer to be encrypted.  Add padding to it too, at least
      block size of the cipher. */
@@ -1334,7 +1412,7 @@ silc_pkcs_silc_export_private_key_file(void *private_key,
     break;
 
   case SILC_PKCS_FILE_BASE64:
-    data = silc_pem_encode_file(enc->data, silc_buffer_len(enc));
+    data = silc_base64_encode_file(enc->data, silc_buffer_len(enc));
     if (!data) {
       silc_buffer_clear(enc);
       silc_buffer_free(enc);
@@ -1529,7 +1607,8 @@ SilcBool silc_pkcs_silc_encrypt(void *public_key,
                                SilcUInt32 src_len,
                                unsigned char *dst,
                                SilcUInt32 dst_size,
-                               SilcUInt32 *ret_dst_len)
+                               SilcUInt32 *ret_dst_len,
+                               SilcRng rng)
 {
   SilcSILCPublicKey silc_pubkey = public_key;
 
@@ -1538,7 +1617,7 @@ SilcBool silc_pkcs_silc_encrypt(void *public_key,
 
   return silc_pubkey->pkcs->encrypt(silc_pubkey->public_key,
                                    src, src_len,
-                                   dst, dst_size, ret_dst_len);
+                                   dst, dst_size, ret_dst_len, rng);
 }
 
 /* Decrypts as specified in SILC protocol specification */
@@ -1568,6 +1647,7 @@ SilcBool silc_pkcs_silc_sign(void *private_key,
                             unsigned char *signature,
                             SilcUInt32 signature_size,
                             SilcUInt32 *ret_signature_len,
+                            SilcBool compute_hash,
                             SilcHash hash)
 {
   SilcSILCPrivateKey silc_privkey = private_key;
@@ -1578,7 +1658,7 @@ SilcBool silc_pkcs_silc_sign(void *private_key,
   return silc_privkey->pkcs->sign(silc_privkey->private_key,
                                  src, src_len,
                                  signature, signature_size,
-                                 ret_signature_len, hash);
+                                 ret_signature_len, compute_hash, hash);
 }
 
 /* Verifies as specified in SILC protocol specification */