X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=lib%2Fsilccrypt%2Fsilcpkcs.c;h=4deddfd96f33b063277784024cd7f1861b4bb98d;hp=74870ec8a33f0c4ee4361190d4e521efa99841dc;hb=382d15d447b7a95390decfa783836ae4fe255b3d;hpb=48d89bc2868ec49c2ab9aa74efe9ac703eee8f29 diff --git a/lib/silccrypt/silcpkcs.c b/lib/silccrypt/silcpkcs.c index 74870ec8..4deddfd9 100644 --- a/lib/silccrypt/silcpkcs.c +++ b/lib/silccrypt/silcpkcs.c @@ -1,16 +1,15 @@ /* - silcpkcs.c + silcpkcs.c - Author: Pekka Riikonen + Author: Pekka Riikonen - Copyright (C) 1997 - 2000 Pekka Riikonen + Copyright (C) 1997 - 2002 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; either version 2 of the License, or - (at your option) any later version. - + 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 @@ -22,17 +21,39 @@ #include "silcincludes.h" #include "rsa.h" +#include "pkcs1.h" + +/* The main SILC PKCS structure. */ +struct SilcPKCSStruct { + void *context; /* Algorithm internal context */ + SilcPKCSObject *pkcs; /* Algorithm implementation */ + SilcUInt32 key_len; /* Key length in bits */ +}; + +#ifndef SILC_EPOC +/* Dynamically registered list of PKCS. */ +SilcDList silc_pkcs_list = NULL; +#define SILC_PKCS_LIST silc_pkcs_list +#else +#define SILC_PKCS_LIST TRUE +#endif /* SILC_EPOC */ -/* List of all PKCS's in SILC. PKCS's don't support SIM's thus - only static declarations are possible. XXX: I hope this to change - real soon. */ -SilcPKCSObject silc_pkcs_list[] = +/* Static list of PKCS for silc_pkcs_register_default(). */ +const SilcPKCSObject silc_default_pkcs[] = { - { "rsa", &silc_rsa_data_context, + /* RSA with PKCS #1 (Uses directly routines from Raw RSA operations) */ + { "rsa", + silc_rsa_init, silc_rsa_clear_keys, silc_rsa_get_public_key, + silc_rsa_get_private_key, silc_rsa_set_public_key, + silc_rsa_set_private_key, silc_rsa_context_len, + silc_pkcs1_encrypt, silc_pkcs1_decrypt, + silc_pkcs1_sign, silc_pkcs1_verify }, + + /* Raw RSA operations */ + { "rsa-raw", silc_rsa_init, silc_rsa_clear_keys, silc_rsa_get_public_key, silc_rsa_get_private_key, silc_rsa_set_public_key, silc_rsa_set_private_key, silc_rsa_context_len, - silc_rsa_data_context_len, silc_rsa_set_arg, silc_rsa_encrypt, silc_rsa_decrypt, silc_rsa_sign, silc_rsa_verify }, @@ -40,133 +61,387 @@ SilcPKCSObject silc_pkcs_list[] = NULL, NULL, NULL, NULL, NULL, NULL } }; -/* Allocates a new SilcPKCS object. The new allocated object is returned - to the 'new_pkcs' argument. This function also initializes the data - context structure. Function returns 1 on success and 0 on error. +/* Register a new PKCS into SILC. This is used at the initialization of + the SILC. */ -*/ -int silc_pkcs_alloc(const unsigned char *name, SilcPKCS *new_pkcs) +bool silc_pkcs_register(const SilcPKCSObject *pkcs) +{ +#ifndef SILC_EPOC + SilcPKCSObject *new; + + SILC_LOG_DEBUG(("Registering new PKCS `%s'", pkcs->name)); + + /* Check if exists already */ + if (silc_pkcs_list) { + SilcPKCSObject *entry; + silc_dlist_start(silc_pkcs_list); + while ((entry = silc_dlist_get(silc_pkcs_list)) != SILC_LIST_END) { + if (!strcmp(entry->name, pkcs->name)) + return FALSE; + } + } + + new = silc_calloc(1, sizeof(*new)); + new->name = strdup(pkcs->name); + new->init = pkcs->init; + new->clear_keys = pkcs->clear_keys; + new->get_public_key = pkcs->get_public_key; + new->get_private_key = pkcs->get_private_key; + new->set_public_key = pkcs->set_public_key; + new->set_private_key = pkcs->set_private_key; + new->context_len = pkcs->context_len; + new->encrypt = pkcs->encrypt; + new->decrypt = pkcs->decrypt; + new->sign = pkcs->sign; + new->verify = pkcs->verify; + + /* Add to list */ + if (silc_pkcs_list == NULL) + silc_pkcs_list = silc_dlist_init(); + silc_dlist_add(silc_pkcs_list, new); + +#endif /* SILC_EPOC */ + return TRUE; +} + +/* Unregister a PKCS from the SILC. */ + +bool silc_pkcs_unregister(SilcPKCSObject *pkcs) +{ +#ifndef SILC_EPOC + SilcPKCSObject *entry; + + SILC_LOG_DEBUG(("Unregistering PKCS")); + + if (!silc_pkcs_list) + return FALSE; + + silc_dlist_start(silc_pkcs_list); + while ((entry = silc_dlist_get(silc_pkcs_list)) != SILC_LIST_END) { + if (pkcs == SILC_ALL_PKCS || entry == pkcs) { + silc_dlist_del(silc_pkcs_list, entry); + silc_free(entry->name); + silc_free(entry); + + if (silc_dlist_count(silc_pkcs_list) == 0) { + silc_dlist_uninit(silc_pkcs_list); + silc_pkcs_list = NULL; + } + + return TRUE; + } + } + +#endif /* SILC_EPOC */ + return FALSE; +} + +/* Function that registers all the default PKCS (all builtin PKCS). + The application may use this to register the default PKCS if specific + PKCS in any specific order is not wanted. */ + +bool silc_pkcs_register_default(void) { +#ifndef SILC_EPOC int i; - SILC_LOG_DEBUG(("Allocating new PKCS object")); + for (i = 0; silc_default_pkcs[i].name; i++) + silc_pkcs_register(&(silc_default_pkcs[i])); - for (i = 0; silc_pkcs_list[i].name; i++) { - if (!strcmp(silc_pkcs_list[i].name, name)) +#endif /* SILC_EPOC */ + return TRUE; +} + +bool silc_pkcs_unregister_all(void) +{ +#ifndef SILC_EPOC + SilcPKCSObject *entry; + + if (!silc_pkcs_list) + return FALSE; + + silc_dlist_start(silc_pkcs_list); + while ((entry = silc_dlist_get(silc_pkcs_list)) != SILC_LIST_END) { + silc_pkcs_unregister(entry); + if (!silc_pkcs_list) break; } +#endif /* SILC_EPOC */ + return TRUE; +} - if (silc_pkcs_list[i].name == NULL) - return FALSE; +/* Allocates a new SilcPKCS object. The new allocated object is returned + to the 'new_pkcs' argument. */ - *new_pkcs = silc_calloc(1, sizeof(**new_pkcs)); +bool silc_pkcs_alloc(const unsigned char *name, SilcPKCS *new_pkcs) +{ + SilcPKCSObject *entry = NULL; - /* Set the pointers */ - (*new_pkcs)->pkcs = &silc_pkcs_list[i]; - (*new_pkcs)->pkcs->data_context = - silc_calloc(1, (*new_pkcs)->pkcs->data_context_len()); - (*new_pkcs)->context = silc_calloc(1, (*new_pkcs)->pkcs->context_len()); - (*new_pkcs)->get_key_len = silc_pkcs_get_key_len; + SILC_LOG_DEBUG(("Allocating new PKCS object")); - return TRUE; +#ifndef SILC_EPOC + if (silc_pkcs_list) { + silc_dlist_start(silc_pkcs_list); + while ((entry = silc_dlist_get(silc_pkcs_list)) != SILC_LIST_END) { + if (!strcmp(entry->name, name)) + break; + } + } +#else + { + /* On EPOC which don't have globals we check our constant hash list. */ + int i; + for (i = 0; silc_default_pkcs[i].name; i++) { + if (!strcmp(silc_default_pkcs[i].name, name)) { + entry = (SilcPKCSObject *)&(silc_default_pkcs[i]); + break; + } + } + } +#endif /* SILC_EPOC */ + + if (entry) { + *new_pkcs = silc_calloc(1, sizeof(**new_pkcs)); + (*new_pkcs)->pkcs = entry; + (*new_pkcs)->context = silc_calloc(1, entry->context_len()); + return TRUE; + } + + return FALSE; } /* Free's the PKCS object */ void silc_pkcs_free(SilcPKCS pkcs) { - if (pkcs) + if (pkcs) { + pkcs->pkcs->clear_keys(pkcs->context); silc_free(pkcs->context); + } + silc_free(pkcs); } /* Return TRUE if PKCS algorithm `name' is supported. */ -int silc_pkcs_is_supported(const unsigned char *name) +bool silc_pkcs_is_supported(const unsigned char *name) { - int i; - - for (i = 0; silc_pkcs_list[i].name; i++) { - if (!strcmp(silc_pkcs_list[i].name, name)) - return TRUE; +#ifndef SILC_EPOC + SilcPKCSObject *entry; + + if (silc_pkcs_list) { + silc_dlist_start(silc_pkcs_list); + while ((entry = silc_dlist_get(silc_pkcs_list)) != SILC_LIST_END) { + if (!strcmp(entry->name, name)) + return TRUE; + } } - +#else + { + int i; + for (i = 0; silc_default_pkcs[i].name; i++) + if (!strcmp(silc_default_pkcs[i].name, name)) + return TRUE; + } +#endif /* SILC_EPOC */ return FALSE; } /* Returns comma separated list of supported PKCS algorithms */ -char *silc_pkcs_get_supported() +char *silc_pkcs_get_supported(void) { + SilcPKCSObject *entry; char *list = NULL; - int i, len; - - len = 0; - for (i = 0; silc_pkcs_list[i].name; i++) { - len += strlen(silc_pkcs_list[i].name); - list = silc_realloc(list, len + 1); - - memcpy(list + (len - strlen(silc_pkcs_list[i].name)), - silc_pkcs_list[i].name, strlen(silc_pkcs_list[i].name)); - memcpy(list + len, ",", 1); - len++; + int len = 0; + +#ifndef SILC_EPOC + if (silc_pkcs_list) { + silc_dlist_start(silc_pkcs_list); + while ((entry = silc_dlist_get(silc_pkcs_list)) != SILC_LIST_END) { + len += strlen(entry->name); + list = silc_realloc(list, len + 1); + + memcpy(list + (len - strlen(entry->name)), + entry->name, strlen(entry->name)); + memcpy(list + len, ",", 1); + len++; + } } +#else + { + int i; + for (i = 0; silc_default_pkcs[i].name; i++) { + entry = (SilcPKCSObject *)&(silc_default_pkcs[i]); + len += strlen(entry->name); + list = silc_realloc(list, len + 1); + + memcpy(list + (len - strlen(entry->name)), + entry->name, strlen(entry->name)); + memcpy(list + len, ",", 1); + len++; + } + } +#endif /* SILC_EPOC */ list[len - 1] = 0; return list; } +/* Generate new key pair into the `pkcs' context. */ + +bool silc_pkcs_generate_key(SilcPKCS pkcs, SilcUInt32 bits_key_len, + SilcRng rng) +{ + return pkcs->pkcs->init(pkcs->context, bits_key_len, rng); +} + /* Returns the length of the key */ -unsigned int silc_pkcs_get_key_len(SilcPKCS self) +SilcUInt32 silc_pkcs_get_key_len(SilcPKCS pkcs) +{ + return pkcs->key_len; +} + +const char *silc_pkcs_get_name(SilcPKCS pkcs) { - return self->key_len; + return pkcs->pkcs->name; } /* Returns SILC style public key */ -unsigned char *silc_pkcs_get_public_key(SilcPKCS pkcs, unsigned int *len) +unsigned char *silc_pkcs_get_public_key(SilcPKCS pkcs, SilcUInt32 *len) { return pkcs->pkcs->get_public_key(pkcs->context, len); } /* Returns SILC style private key */ -unsigned char *silc_pkcs_get_private_key(SilcPKCS pkcs, unsigned int *len) +unsigned char *silc_pkcs_get_private_key(SilcPKCS pkcs, SilcUInt32 *len) { return pkcs->pkcs->get_private_key(pkcs->context, len); } /* Sets public key from SilcPublicKey. */ -int silc_pkcs_public_key_set(SilcPKCS pkcs, SilcPublicKey public_key) +SilcUInt32 silc_pkcs_public_key_set(SilcPKCS pkcs, SilcPublicKey public_key) { - return pkcs->pkcs->set_public_key(pkcs->context, public_key->pk, - public_key->pk_len); + pkcs->key_len = pkcs->pkcs->set_public_key(pkcs->context, public_key->pk, + public_key->pk_len); + return pkcs->key_len; } /* Sets public key from data. */ -int silc_pkcs_public_key_data_set(SilcPKCS pkcs, unsigned char *pk, - unsigned int pk_len) +SilcUInt32 silc_pkcs_public_key_data_set(SilcPKCS pkcs, unsigned char *pk, + SilcUInt32 pk_len) { - return pkcs->pkcs->set_public_key(pkcs->context, pk, pk_len); + pkcs->key_len = pkcs->pkcs->set_public_key(pkcs->context, pk, pk_len); + return pkcs->key_len; } /* Sets private key from SilcPrivateKey. */ -int silc_pkcs_private_key_set(SilcPKCS pkcs, SilcPrivateKey private_key) +SilcUInt32 silc_pkcs_private_key_set(SilcPKCS pkcs, SilcPrivateKey private_key) { - return pkcs->pkcs->set_private_key(pkcs->context, private_key->prv, - private_key->prv_len); + SilcUInt32 key_len; + key_len = pkcs->pkcs->set_private_key(pkcs->context, private_key->prv, + private_key->prv_len); + if (!pkcs->key_len) + pkcs->key_len = key_len; + return pkcs->key_len; } /* Sets private key from data. */ -int silc_pkcs_private_key_data_set(SilcPKCS pkcs, unsigned char *prv, - unsigned int prv_len) +SilcUInt32 silc_pkcs_private_key_data_set(SilcPKCS pkcs, unsigned char *prv, + SilcUInt32 prv_len) +{ + SilcUInt32 key_len; + key_len = pkcs->pkcs->set_private_key(pkcs->context, prv, prv_len); + if (!pkcs->key_len) + pkcs->key_len = key_len; + return pkcs->key_len; +} + +/* Encrypts */ + +bool silc_pkcs_encrypt(SilcPKCS pkcs, unsigned char *src, SilcUInt32 src_len, + unsigned char *dst, SilcUInt32 *dst_len) +{ + return pkcs->pkcs->encrypt(pkcs->context, src, src_len, dst, dst_len); +} + +/* Decrypts */ + +bool silc_pkcs_decrypt(SilcPKCS pkcs, unsigned char *src, SilcUInt32 src_len, + unsigned char *dst, SilcUInt32 *dst_len) +{ + return pkcs->pkcs->decrypt(pkcs->context, src, src_len, dst, dst_len); +} + +/* Generates signature */ + +bool silc_pkcs_sign(SilcPKCS pkcs, unsigned char *src, SilcUInt32 src_len, + unsigned char *dst, SilcUInt32 *dst_len) +{ + return pkcs->pkcs->sign(pkcs->context, src, src_len, dst, dst_len); +} + +/* Verifies signature */ + +bool silc_pkcs_verify(SilcPKCS pkcs, unsigned char *signature, + SilcUInt32 signature_len, unsigned char *data, + SilcUInt32 data_len) +{ + return pkcs->pkcs->verify(pkcs->context, signature, signature_len, + data, data_len); +} + +/* Generates signature with hash. The hash is signed. */ + +bool silc_pkcs_sign_with_hash(SilcPKCS pkcs, SilcHash hash, + unsigned char *src, SilcUInt32 src_len, + unsigned char *dst, SilcUInt32 *dst_len) +{ + unsigned char hashr[32]; + SilcUInt32 hash_len; + int ret; + + silc_hash_make(hash, src, src_len, hashr); + hash_len = silc_hash_len(hash); + + SILC_LOG_HEXDUMP(("Hash"), hashr, hash_len); + + ret = pkcs->pkcs->sign(pkcs->context, hashr, hash_len, dst, dst_len); + memset(hashr, 0, sizeof(hashr)); + + return ret; +} + +/* Verifies signature with hash. The `data' is hashed and verified against + the `signature'. */ + +bool silc_pkcs_verify_with_hash(SilcPKCS pkcs, SilcHash hash, + unsigned char *signature, + SilcUInt32 signature_len, + unsigned char *data, + SilcUInt32 data_len) { - return pkcs->pkcs->set_private_key(pkcs->context, prv, prv_len); + unsigned char hashr[32]; + SilcUInt32 hash_len; + int ret; + + silc_hash_make(hash, data, data_len, hashr); + hash_len = silc_hash_len(hash); + + SILC_LOG_HEXDUMP(("Hash"), hashr, hash_len); + + ret = pkcs->pkcs->verify(pkcs->context, signature, signature_len, + hashr, hash_len); + memset(hashr, 0, sizeof(hashr)); + + return ret; } /* Encodes and returns SILC public key identifier. If some of the @@ -178,7 +453,7 @@ char *silc_pkcs_encode_identifier(char *username, char *host, char *realname, { SilcBuffer buf; char *identifier; - unsigned int len, tlen = 0; + SilcUInt32 len, tlen = 0; if (!username || !host) return NULL; @@ -264,23 +539,101 @@ char *silc_pkcs_encode_identifier(char *username, char *host, char *realname, return identifier; } +/* Decodes the provided `identifier' and returns allocated context for + the identifier. */ + +SilcPublicKeyIdentifier silc_pkcs_decode_identifier(char *identifier) +{ + SilcPublicKeyIdentifier ident; + char *cp, *item; + int len; + + ident = silc_calloc(1, sizeof(*ident)); + + cp = identifier; + while (cp) { + len = strcspn(cp, ","); + if (len - 1 >= 0 && cp[len - 1] == '\\') { + while (cp) { + cp += len + 1; + len = strcspn(cp, ",") + len; + if (len - 1 >= 0 && cp[len - 1] != '\\') + break; + } + } + + item = silc_calloc(len + 1, sizeof(char)); + memcpy(item, cp, len); + + if (strstr(item, "UN=")) + ident->username = strdup(item + strcspn(cp, "=") + 1); + else if (strstr(item, "HN=")) + ident->host = strdup(item + strcspn(cp, "=") + 1); + else if (strstr(item, "RN=")) + ident->realname = strdup(item + strcspn(cp, "=") + 1); + else if (strstr(item, "E=")) + ident->email = strdup(item + strcspn(cp, "=") + 1); + else if (strstr(item, "O=")) + ident->org = strdup(item + strcspn(cp, "=") + 1); + else if (strstr(item, "C=")) + ident->country = strdup(item + strcspn(cp, "=") + 1); + + cp += len; + if (strlen(cp) == 0) + cp = NULL; + else + cp += 1; + + if (item) + silc_free(item); + } + + return ident; +} + +/* Free's decoded public key identifier context. Call this to free the + context returned by the silc_pkcs_decode_identifier. */ + +void silc_pkcs_free_identifier(SilcPublicKeyIdentifier identifier) +{ + silc_free(identifier->username); + silc_free(identifier->host); + silc_free(identifier->realname); + silc_free(identifier->email); + silc_free(identifier->org); + silc_free(identifier->country); + silc_free(identifier); +} + /* Allocates SILC style public key formed from sent arguments. All data is duplicated. */ -SilcPublicKey silc_pkcs_public_key_alloc(char *name, char *identifier, - unsigned char *pk, - unsigned int pk_len) +SilcPublicKey silc_pkcs_public_key_alloc(const char *name, + const char *identifier, + const unsigned char *pk, + SilcUInt32 pk_len) { SilcPublicKey public_key; + char *tmp = NULL; public_key = silc_calloc(1, sizeof(*public_key)); - public_key->len = 4 + 2 + strlen(name) + 2 + strlen(identifier) + pk_len; public_key->name = strdup(name); - public_key->identifier = strdup(identifier); public_key->pk_len = pk_len; public_key->pk = silc_calloc(pk_len, sizeof(*public_key->pk)); + public_key->pk_type = SILC_SKE_PK_TYPE_SILC; memcpy(public_key->pk, pk, pk_len); + if (!silc_utf8_valid(identifier, strlen(identifier))) { + int len = silc_utf8_encoded_len(identifier, strlen(identifier), 0); + tmp = silc_calloc(len + 1, sizeof(*tmp)); + silc_utf8_encode(identifier, strlen(identifier), 0, tmp, len); + identifier = tmp; + } + + public_key->identifier = strdup(identifier); + public_key->len = 2 + strlen(name) + 2 + strlen(identifier) + pk_len; + silc_free(tmp); + return public_key; } @@ -299,8 +652,9 @@ void silc_pkcs_public_key_free(SilcPublicKey public_key) /* Allocates SILC private key formed from sent arguments. All data is duplicated. */ -SilcPrivateKey silc_pkcs_private_key_alloc(char *name, unsigned char *prv, - unsigned int prv_len) +SilcPrivateKey silc_pkcs_private_key_alloc(const char *name, + const unsigned char *prv, + SilcUInt32 prv_len) { SilcPrivateKey private_key; @@ -328,12 +682,12 @@ void silc_pkcs_private_key_free(SilcPrivateKey private_key) data. */ unsigned char * -silc_pkcs_public_key_encode(SilcPublicKey public_key, unsigned int *len) +silc_pkcs_public_key_encode(SilcPublicKey public_key, SilcUInt32 *len) { SilcBuffer buf; unsigned char *ret; - buf = silc_buffer_alloc(public_key->len); + buf = silc_buffer_alloc(public_key->len + 4); silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf)); silc_buffer_format(buf, @@ -346,7 +700,7 @@ silc_pkcs_public_key_encode(SilcPublicKey public_key, unsigned int *len) public_key->pk_len), SILC_STR_END); if (len) - *len = public_key->len; + *len = public_key->len + 4; ret = silc_calloc(buf->len, sizeof(*ret)); memcpy(ret, buf->data, buf->len); @@ -358,16 +712,16 @@ silc_pkcs_public_key_encode(SilcPublicKey public_key, unsigned int *len) /* Encodes SILC style public key. Returns the encoded data. */ unsigned char * -silc_pkcs_public_key_data_encode(unsigned char *pk, unsigned int pk_len, +silc_pkcs_public_key_data_encode(unsigned char *pk, SilcUInt32 pk_len, char *pkcs, char *identifier, - unsigned int *len) + SilcUInt32 *len) { SilcBuffer buf; unsigned char *ret; - unsigned int totlen; + SilcUInt32 totlen; - totlen = 4 + 2 + strlen(pkcs) + 2 + strlen(identifier) + pk_len; - buf = silc_buffer_alloc(totlen); + totlen = 2 + strlen(pkcs) + 2 + strlen(identifier) + pk_len; + buf = silc_buffer_alloc(totlen + 4); silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf)); silc_buffer_format(buf, @@ -379,7 +733,7 @@ silc_pkcs_public_key_data_encode(unsigned char *pk, unsigned int pk_len, SILC_STR_UI_XNSTRING(pk, pk_len), SILC_STR_END); if (len) - *len = totlen; + *len = totlen + 4; ret = silc_calloc(buf->len, sizeof(*ret)); memcpy(ret, buf->data, buf->len); @@ -391,13 +745,13 @@ silc_pkcs_public_key_data_encode(unsigned char *pk, unsigned int pk_len, /* Decodes SILC style public key. Returns TRUE if the decoding was successful. Allocates new public key as well. */ -int silc_pkcs_public_key_decode(unsigned char *data, unsigned int data_len, - SilcPublicKey *public_key) +bool silc_pkcs_public_key_decode(unsigned char *data, SilcUInt32 data_len, + SilcPublicKey *public_key) { SilcBuffer buf; SilcPKCS alg; - unsigned short pkcs_len, identifier_len; - unsigned int totlen, key_len; + SilcUInt16 pkcs_len, identifier_len; + SilcUInt32 totlen, key_len; unsigned char *pkcs_name = NULL, *ident = NULL, *key_data = NULL; int ret; @@ -414,7 +768,12 @@ int silc_pkcs_public_key_decode(unsigned char *data, unsigned int data_len, return FALSE; } - if (totlen != data_len) { +#if 1 /* Backwards support, remove! */ + if (totlen == data_len) + totlen -= 4; +#endif + + if (totlen + 4 != data_len) { silc_buffer_free(buf); return FALSE; } @@ -433,14 +792,19 @@ int silc_pkcs_public_key_decode(unsigned char *data, unsigned int data_len, pkcs_len + identifier_len > totlen) goto err; - /* See if we support this algorithm */ - if (!silc_pkcs_is_supported(pkcs_name)) + /* 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; + } /* Protocol says that at least UN and HN must be provided as identifier, check for these. */ - if (!strstr(ident, "UN=") && !strstr(ident, "HN=")) + if (!strstr(ident, "UN=") && !strstr(ident, "HN=")) { + SILC_LOG_DEBUG(("The public does not have the required UN= and HN= " + "identifiers")); goto err; + } /* Get key data. We assume that rest of the buffer is key data. */ silc_buffer_pull(buf, 2 + pkcs_len + 2 + identifier_len); @@ -452,11 +816,14 @@ int silc_pkcs_public_key_decode(unsigned char *data, unsigned int data_len, 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. */ - silc_pkcs_alloc(pkcs_name, &alg); - if (!alg->pkcs->set_public_key(alg->context, key_data, key_len)) - goto err; - silc_pkcs_free(alg); + 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_public_key(alg->context, key_data, key_len)) + goto err; + silc_pkcs_free(alg); + } if (public_key) { *public_key = silc_calloc(1, sizeof(**public_key)); @@ -465,6 +832,7 @@ int silc_pkcs_public_key_decode(unsigned char *data, unsigned int data_len, (*public_key)->identifier = ident; (*public_key)->pk = key_data; (*public_key)->pk_len = key_len; + (*public_key)->pk_type = SILC_SKE_PK_TYPE_SILC; } silc_buffer_free(buf); @@ -481,14 +849,126 @@ int silc_pkcs_public_key_decode(unsigned char *data, unsigned int data_len, return FALSE; } +/* Encodes Public Key Payload for transmitting public keys and certificates. */ + +SilcBuffer silc_pkcs_public_key_payload_encode(SilcPublicKey public_key) +{ + SilcBuffer buffer; + unsigned char *pk; + SilcUInt32 pk_len; + + if (!public_key) + return NULL; + + pk = silc_pkcs_public_key_encode(public_key, &pk_len); + if (!pk) + return NULL; + + buffer = silc_buffer_alloc_size(4 + pk_len); + if (!buffer) { + silc_free(pk); + return NULL; + } + + silc_buffer_format(buffer, + SILC_STR_UI_SHORT(pk_len), + SILC_STR_UI_SHORT(public_key->pk_type), + SILC_STR_UI_XNSTRING(pk, pk_len), + 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, unsigned int *len) +silc_pkcs_private_key_encode(SilcPrivateKey private_key, SilcUInt32 *len) { SilcBuffer buf; unsigned char *ret; - unsigned int totlen; + SilcUInt32 totlen; totlen = 2 + strlen(private_key->name) + private_key->prv_len; buf = silc_buffer_alloc(totlen); @@ -505,6 +985,7 @@ silc_pkcs_private_key_encode(SilcPrivateKey private_key, unsigned int *len) ret = silc_calloc(buf->len, sizeof(*ret)); memcpy(ret, buf->data, buf->len); + silc_buffer_clear(buf); silc_buffer_free(buf); return ret; @@ -513,12 +994,12 @@ silc_pkcs_private_key_encode(SilcPrivateKey private_key, unsigned int *len) /* Encodes SILC private key. Returns the encoded data. */ unsigned char * -silc_pkcs_private_key_data_encode(unsigned char *prv, unsigned int prv_len, - char *pkcs, unsigned int *len) +silc_pkcs_private_key_data_encode(unsigned char *prv, SilcUInt32 prv_len, + char *pkcs, SilcUInt32 *len) { SilcBuffer buf; unsigned char *ret; - unsigned int totlen; + SilcUInt32 totlen; totlen = 2 + strlen(pkcs) + prv_len; buf = silc_buffer_alloc(totlen); @@ -534,21 +1015,22 @@ silc_pkcs_private_key_data_encode(unsigned char *prv, unsigned int 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; } -/* Decodes SILC style public key. Returns TRUE if the decoding was +/* Decodes SILC style private key. Returns TRUE if the decoding was successful. Allocates new private key as well. */ -int silc_pkcs_private_key_decode(unsigned char *data, unsigned int data_len, - SilcPrivateKey *private_key) +bool silc_pkcs_private_key_decode(unsigned char *data, SilcUInt32 data_len, + SilcPrivateKey *private_key) { SilcBuffer buf; SilcPKCS alg; - unsigned short pkcs_len; - unsigned int key_len; + SilcUInt16 pkcs_len; + SilcUInt32 key_len; unsigned char *pkcs_name = NULL, *key_data = NULL; int ret; @@ -561,15 +1043,21 @@ int silc_pkcs_private_key_decode(unsigned char *data, unsigned int 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 */ - if (!silc_pkcs_is_supported(pkcs_name)) + /* 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); @@ -581,11 +1069,16 @@ int silc_pkcs_private_key_decode(unsigned char *data, unsigned int data_len, 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. */ - silc_pkcs_alloc(pkcs_name, &alg); - if (!alg->pkcs->set_private_key(alg->context, key_data, key_len)) - goto err; - silc_pkcs_free(alg); + 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)); @@ -594,6 +1087,7 @@ int silc_pkcs_private_key_decode(unsigned char *data, unsigned int data_len, (*private_key)->prv_len = key_len; } + silc_buffer_clear(buf); silc_buffer_free(buf); return TRUE; @@ -602,25 +1096,26 @@ int silc_pkcs_private_key_decode(unsigned char *data, unsigned int data_len, silc_free(pkcs_name); if (key_data) silc_free(key_data); + silc_buffer_clear(buf); silc_buffer_free(buf); return FALSE; } /* Internal routine to save public key */ -static int silc_pkcs_save_public_key_internal(char *filename, - unsigned char *data, - unsigned int data_len, - unsigned int encoding) +static bool silc_pkcs_save_public_key_internal(const char *filename, + unsigned char *data, + SilcUInt32 data_len, + SilcUInt32 encoding) { SilcBuffer buf; - unsigned int len; + SilcUInt32 len; switch(encoding) { case SILC_PKCS_FILE_BIN: break; case SILC_PKCS_FILE_PEM: - data = silc_encode_pem_file(data, data_len); + data = silc_pem_encode_file(data, data_len); data_len = strlen(data); break; } @@ -637,7 +1132,7 @@ static int silc_pkcs_save_public_key_internal(char *filename, SILC_STR_END); /* Save into file */ - if (silc_file_write(filename, buf->data, buf->len)) { + if (silc_file_writefile(filename, buf->data, buf->len)) { silc_buffer_free(buf); return FALSE; } @@ -648,51 +1143,157 @@ static int silc_pkcs_save_public_key_internal(char *filename, /* Saves public key into file */ -int silc_pkcs_save_public_key(char *filename, SilcPublicKey public_key, - unsigned int encoding) +bool silc_pkcs_save_public_key(const char *filename, SilcPublicKey public_key, + SilcUInt32 encoding) { unsigned char *data; - unsigned int data_len; + 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 */ -int silc_pkcs_save_public_key_data(char *filename, unsigned char *data, - unsigned int data_len, - unsigned int encoding) +bool silc_pkcs_save_public_key_data(const char *filename, unsigned char *data, + SilcUInt32 data_len, SilcUInt32 encoding) { return silc_pkcs_save_public_key_internal(filename, data, data_len, encoding); } +#define SILC_PKCS_PRIVATE_KEY_MAGIC 0x738df531 + /* Internal routine to save private key. */ -static int silc_pkcs_save_private_key_internal(char *filename, - unsigned char *data, - unsigned int data_len, - unsigned int encoding) +static bool silc_pkcs_save_private_key_internal(const char *filename, + unsigned char *data, + SilcUInt32 data_len, + unsigned char *key, + SilcUInt32 key_len, + SilcUInt32 encoding) { - SilcBuffer buf; - unsigned int len; + SilcCipher aes; + SilcHash sha1; + SilcHmac sha1hmac; + SilcBuffer buf, enc; + SilcUInt32 len, blocklen, padlen; + unsigned char tmp[32], keymat[64]; + 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 SHA1 hash */ + if (!silc_hash_alloc("sha1", &sha1)) { + SILC_LOG_ERROR(("Could not allocate SHA1 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(sha1); + 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(sha1); + silc_hash_update(sha1, key, key_len); + silc_hash_final(sha1, keymat); + silc_hash_init(sha1); + silc_hash_update(sha1, key, key_len); + silc_hash_update(sha1, keymat, 16); + silc_hash_final(sha1, keymat + 16); + + /* Set the key to the cipher */ + silc_cipher_set_key(aes, keymat, 256); + + /* 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); + padlen = 16 + (16 - ((data_len + 4) % blocklen)); + enc = silc_buffer_alloc_size(4 + 4 + data_len + padlen + len); + if (!enc) { + silc_hmac_free(sha1hmac); + silc_hash_free(sha1); + silc_cipher_free(aes); + return FALSE; + } + + /* Generate padding */ + for (i = 0; i < padlen; 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, padlen), + 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(sha1); + silc_cipher_free(aes); + + data = enc->data; + data_len = enc->len; + + switch (encoding) { case SILC_PKCS_FILE_BIN: break; case SILC_PKCS_FILE_PEM: - data = silc_encode_pem_file(data, data_len); + data = silc_pem_encode_file(data, data_len); data_len = strlen(data); 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), @@ -700,52 +1301,56 @@ static int silc_pkcs_save_private_key_internal(char *filename, SILC_STR_END); /* Save into a file */ - if (silc_file_write_mode(filename, buf->data, buf->len, 0600)) { + 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. */ -int silc_pkcs_save_private_key(char *filename, SilcPrivateKey private_key, - unsigned char *passphrase, - unsigned int encoding) +bool silc_pkcs_save_private_key(const char *filename, + SilcPrivateKey private_key, + unsigned char *passphrase, + SilcUInt32 passphrase_len, + SilcUInt32 encoding) { unsigned char *data; - unsigned int data_len; + 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. */ - -int silc_pkcs_save_private_key_data(char *filename, unsigned char *data, - unsigned int data_len, - unsigned char *passphrase, - unsigned int 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 - is loading was successful. */ + if loading was successful. */ -int silc_pkcs_load_public_key(char *filename, SilcPublicKey *public_key, - unsigned int encoding) +bool silc_pkcs_load_public_key(const char *filename, SilcPublicKey *public_key, + SilcUInt32 encoding) { unsigned char *cp, *old, *data, byte; - unsigned int i, data_len, len; + SilcUInt32 i, data_len, len; - old = data = silc_file_read(filename, &data_len); + SILC_LOG_DEBUG(("Loading public key `%s' with %s encoding", filename, + encoding == SILC_PKCS_FILE_PEM ? "Base64" : + encoding == SILC_PKCS_FILE_BIN ? "Binary" : "Unkonwn")); + + old = data = silc_file_readfile(filename, &data_len); if (!data) return FALSE; @@ -758,6 +1363,7 @@ int silc_pkcs_load_public_key(char *filename, SilcPublicKey *public_key, if (byte != SILC_PKCS_PUBLIC_KEYFILE_BEGIN[i]) { memset(old, 0, data_len); silc_free(old); + return FALSE; } } data = cp; @@ -771,7 +1377,11 @@ int silc_pkcs_load_public_key(char *filename, SilcPublicKey *public_key, case SILC_PKCS_FILE_BIN: break; case SILC_PKCS_FILE_PEM: - data = silc_decode_pem(data, len, &len); + data = silc_pem_decode(data, len, &len); + memset(old, 0, data_len); + silc_free(old); + old = data; + data_len = len; break; } @@ -789,15 +1399,26 @@ int 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 */ -int silc_pkcs_load_private_key(char *filename, SilcPrivateKey *private_key, - unsigned int encoding) +bool silc_pkcs_load_private_key(const char *filename, + SilcPrivateKey *private_key, + unsigned char *passphrase, + SilcUInt32 passphrase_len, + SilcUInt32 encoding) { + SilcCipher aes; + SilcHash sha1; + SilcHmac sha1hmac; + SilcUInt32 blocklen; + unsigned char tmp[32], keymat[64]; unsigned char *cp, *old, *data, byte; - unsigned int i, data_len, len; + SilcUInt32 i, data_len, len, magic, mac_len; - old = data = silc_file_read(filename, &data_len); + SILC_LOG_DEBUG(("Loading private key `%s' with %s encoding", filename, + encoding == SILC_PKCS_FILE_PEM ? "Base64" : + encoding == SILC_PKCS_FILE_BIN ? "Binary" : "Unkonwn")); + + old = data = silc_file_readfile(filename, &data_len); if (!data) return FALSE; @@ -810,28 +1431,145 @@ int silc_pkcs_load_private_key(char *filename, SilcPrivateKey *private_key, if (byte != SILC_PKCS_PRIVATE_KEYFILE_BEGIN[i]) { memset(old, 0, data_len); silc_free(old); + return FALSE; } } 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_decode_pem(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) { + SILC_LOG_DEBUG(("Private key does not have correct 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 SHA1 hash */ + if (!silc_hash_alloc("sha1", &sha1)) { + SILC_LOG_ERROR(("Could not allocate SHA1 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(sha1); + 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(sha1); + silc_hash_update(sha1, passphrase, passphrase_len); + silc_hash_final(sha1, keymat); + silc_hash_init(sha1); + silc_hash_update(sha1, passphrase, passphrase_len); + silc_hash_update(sha1, keymat, 16); + silc_hash_final(sha1, keymat + 16); + + /* Set the key to the cipher */ + silc_cipher_set_key(aes, keymat, 256); + + /* 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(sha1); + 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, NULL); + 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(sha1); + 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(sha1); + 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);