+ SILC_STR_END);
+
+ silc_free(pk);
+ return buffer;
+}
+
+/* Decode Public Key Payload and decodes the public key inside it to
+ to `payload'. */
+
+bool silc_pkcs_public_key_payload_decode(unsigned char *data,
+ SilcUInt32 data_len,
+ SilcPublicKey *public_key)
+{
+ SilcBufferStruct buf;
+ SilcUInt16 pk_len, pk_type;
+ unsigned char *pk;
+ int ret;
+
+ if (!public_key)
+ return FALSE;
+
+ silc_buffer_set(&buf, data, data_len);
+ ret = silc_buffer_unformat(&buf,
+ SILC_STR_UI_SHORT(&pk_len),
+ SILC_STR_UI_SHORT(&pk_type),
+ SILC_STR_END);
+ if (ret < 0 || pk_len > data_len - 4)
+ return FALSE;
+
+ /* For now we support only SILC public keys */
+ if (pk_type != SILC_SKE_PK_TYPE_SILC)
+ return FALSE;
+
+ silc_buffer_pull(&buf, 4);
+ ret = silc_buffer_unformat(&buf,
+ SILC_STR_UI_XNSTRING(&pk, pk_len),
+ SILC_STR_END);
+ silc_buffer_push(&buf, 4);
+ if (ret < 0)
+ return FALSE;
+
+ if (!silc_pkcs_public_key_decode(pk, pk_len, public_key))
+ return FALSE;
+ (*public_key)->pk_type = SILC_SKE_PK_TYPE_SILC;
+
+ return TRUE;
+}
+
+/* Compares two public keys and returns TRUE if they are same key, and
+ FALSE if they are not same. */
+
+bool silc_pkcs_public_key_compare(SilcPublicKey key1, SilcPublicKey key2)
+{
+ if (key1 == key2)
+ return TRUE;
+
+ if (key1->len == key2->len &&
+ key1->name && key2->name && key1->identifier && key2->identifier &&
+ !strcmp(key1->name, key2->name) &&
+ !strcmp(key1->identifier, key2->identifier) &&
+ !memcmp(key1->pk, key2->pk, key1->pk_len) &&
+ key1->pk_len == key2->pk_len)
+ return TRUE;
+
+ return FALSE;
+}
+
+/* Copies the public key indicated by `public_key' and returns new allocated
+ public key which is indentical to the `public_key'. */
+
+SilcPublicKey silc_pkcs_public_key_copy(SilcPublicKey public_key)
+{
+ SilcPublicKey key = silc_calloc(1, sizeof(*key));
+ if (!key)
+ return NULL;
+
+ key->len = public_key->len;
+ key->name = silc_memdup(public_key->name, strlen(public_key->name));
+ key->identifier = silc_memdup(public_key->identifier,
+ strlen(public_key->identifier));
+ key->pk = silc_memdup(public_key->pk, public_key->pk_len);
+ key->pk_len = public_key->pk_len;
+ key->pk_type = public_key->pk_type;
+
+ return key;
+}
+
+/* Encodes SILC private key from SilcPrivateKey. Returns the encoded data. */
+
+unsigned char *
+silc_pkcs_private_key_encode(SilcPrivateKey private_key, SilcUInt32 *len)
+{
+ return silc_pkcs_private_key_data_encode(private_key->prv,
+ private_key->prv_len,
+ private_key->name, len);
+}
+
+/* Encodes SILC private key. Returns the encoded data. */
+
+unsigned char *
+silc_pkcs_private_key_data_encode(unsigned char *prv, SilcUInt32 prv_len,
+ char *pkcs, SilcUInt32 *len)
+{
+ SilcBuffer buf;
+ unsigned char *ret;
+ SilcUInt32 totlen;
+
+ totlen = 2 + strlen(pkcs) + prv_len;
+ buf = silc_buffer_alloc_size(totlen);
+ if (!buf)
+ return NULL;
+
+ silc_buffer_format(buf,
+ SILC_STR_UI_SHORT(strlen(pkcs)),
+ SILC_STR_UI32_STRING(pkcs),
+ SILC_STR_UI_XNSTRING(prv, prv_len),
+ SILC_STR_END);
+
+ ret = silc_buffer_steal(buf, len);
+ silc_buffer_free(buf);
+ return ret;
+}
+
+/* Decodes SILC style private key. Returns TRUE if the decoding was
+ successful. Allocates new private key as well. */
+
+bool silc_pkcs_private_key_decode(unsigned char *data, SilcUInt32 data_len,
+ SilcPrivateKey *private_key)
+{
+ SilcBufferStruct buf;
+ SilcPKCS alg;
+ SilcUInt16 pkcs_len;
+ SilcUInt32 key_len;
+ unsigned char *pkcs_name = NULL, *key_data = NULL;
+ int ret;
+
+ silc_buffer_set(&buf, data, data_len);
+
+ /* Get algorithm name and identifier */
+ ret =
+ silc_buffer_unformat(&buf,
+ SILC_STR_UI16_NSTRING_ALLOC(&pkcs_name, &pkcs_len),
+ SILC_STR_END);
+ if (ret == -1) {
+ SILC_LOG_DEBUG(("Cannot decode private key buffer"));
+ goto err;
+ }
+
+ 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)) {
+ SILC_LOG_DEBUG(("Unknown PKCS `%s'", pkcs_name));
+ goto err;
+ }
+
+ /* Get key data. We assume that rest of the buffer is key data. */
+ silc_buffer_pull(&buf, 2 + pkcs_len);
+ key_len = buf.len;
+ ret = silc_buffer_unformat(&buf,
+ SILC_STR_UI_XNSTRING_ALLOC(&key_data, key_len),
+ SILC_STR_END);
+ if (ret == -1)
+ goto err;
+
+ /* Try to set the key. If this fails the key must be malformed. This
+ code assumes that the PKCS routine checks the format of the key.
+ (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)) {
+ SILC_LOG_DEBUG(("Could not set private key data"));
+ goto err;
+ }
+ silc_pkcs_free(alg);
+ }
+
+ if (private_key) {
+ *private_key = silc_calloc(1, sizeof(**private_key));
+ (*private_key)->name = pkcs_name;
+ (*private_key)->prv = key_data;
+ (*private_key)->prv_len = key_len;
+ }
+
+ return TRUE;
+
+ err:
+ silc_free(pkcs_name);
+ silc_free(key_data);
+ return FALSE;
+}
+
+/* Internal routine to save public key */
+
+static bool silc_pkcs_save_public_key_internal(const char *filename,
+ unsigned char *data,
+ SilcUInt32 data_len,
+ SilcUInt32 encoding)
+{
+ SilcBuffer buf;
+ SilcUInt32 len;
+ unsigned char *tmp = NULL;
+
+ switch(encoding) {
+ case SILC_PKCS_FILE_BIN:
+ break;
+ case SILC_PKCS_FILE_PEM:
+ tmp = data = silc_pem_encode_file(data, data_len);
+ data_len = strlen(data);
+ break;
+ }
+
+ len = data_len + (strlen(SILC_PKCS_PUBLIC_KEYFILE_BEGIN) +
+ strlen(SILC_PKCS_PUBLIC_KEYFILE_END));
+ buf = silc_buffer_alloc_size(len);
+ if (!buf) {
+ silc_free(tmp);
+ return FALSE;
+ }
+
+ silc_buffer_format(buf,
+ SILC_STR_UI32_STRING(SILC_PKCS_PUBLIC_KEYFILE_BEGIN),
+ SILC_STR_UI_XNSTRING(data, data_len),