X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=lib%2Fsilccrypt%2Fsilcpkcs.c;h=4deddfd96f33b063277784024cd7f1861b4bb98d;hp=0a5bd31bad465cdf72faa422f10fe8f18d015517;hb=382d15d447b7a95390decfa783836ae4fe255b3d;hpb=38d89c8cd04b2b9b7e487230420bbf21d26ac722 diff --git a/lib/silccrypt/silcpkcs.c b/lib/silccrypt/silcpkcs.c index 0a5bd31b..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 - 2001 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 @@ -24,6 +23,13 @@ #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; @@ -65,6 +71,16 @@ bool silc_pkcs_register(const SilcPKCSObject *pkcs) 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; @@ -104,6 +120,8 @@ bool silc_pkcs_unregister(SilcPKCSObject *pkcs) 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); @@ -134,6 +152,24 @@ bool silc_pkcs_register_default(void) 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; +} + /* Allocates a new SilcPKCS object. The new allocated object is returned to the 'new_pkcs' argument. */ @@ -168,7 +204,6 @@ bool silc_pkcs_alloc(const unsigned char *name, SilcPKCS *new_pkcs) *new_pkcs = silc_calloc(1, sizeof(**new_pkcs)); (*new_pkcs)->pkcs = entry; (*new_pkcs)->context = silc_calloc(1, entry->context_len()); - (*new_pkcs)->get_key_len = silc_pkcs_get_key_len; return TRUE; } @@ -188,7 +223,7 @@ void silc_pkcs_free(SilcPKCS 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) { #ifndef SILC_EPOC SilcPKCSObject *entry; @@ -255,8 +290,8 @@ char *silc_pkcs_get_supported(void) /* Generate new key pair into the `pkcs' context. */ -int silc_pkcs_generate_key(SilcPKCS pkcs, SilcUInt32 bits_key_len, - SilcRng rng) +bool silc_pkcs_generate_key(SilcPKCS pkcs, SilcUInt32 bits_key_len, + SilcRng rng) { return pkcs->pkcs->init(pkcs->context, bits_key_len, rng); } @@ -268,6 +303,11 @@ SilcUInt32 silc_pkcs_get_key_len(SilcPKCS pkcs) return pkcs->key_len; } +const char *silc_pkcs_get_name(SilcPKCS pkcs) +{ + return pkcs->pkcs->name; +} + /* Returns SILC style public key */ unsigned char *silc_pkcs_get_public_key(SilcPKCS pkcs, SilcUInt32 *len) @@ -302,49 +342,57 @@ SilcUInt32 silc_pkcs_public_key_data_set(SilcPKCS pkcs, unsigned char *pk, /* 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, - SilcUInt32 prv_len) +SilcUInt32 silc_pkcs_private_key_data_set(SilcPKCS pkcs, unsigned char *prv, + SilcUInt32 prv_len) { - return pkcs->pkcs->set_private_key(pkcs->context, prv, 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 */ -int silc_pkcs_encrypt(SilcPKCS pkcs, unsigned char *src, SilcUInt32 src_len, - unsigned char *dst, SilcUInt32 *dst_len) +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 */ -int silc_pkcs_decrypt(SilcPKCS pkcs, unsigned char *src, SilcUInt32 src_len, - unsigned char *dst, SilcUInt32 *dst_len) +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 */ -int silc_pkcs_sign(SilcPKCS pkcs, unsigned char *src, SilcUInt32 src_len, - unsigned char *dst, SilcUInt32 *dst_len) +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 */ -int silc_pkcs_verify(SilcPKCS pkcs, unsigned char *signature, - SilcUInt32 signature_len, unsigned char *data, - SilcUInt32 data_len) +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); @@ -352,9 +400,9 @@ int silc_pkcs_verify(SilcPKCS pkcs, unsigned char *signature, /* Generates signature with hash. The hash is signed. */ -int silc_pkcs_sign_with_hash(SilcPKCS pkcs, SilcHash hash, - unsigned char *src, SilcUInt32 src_len, - unsigned char *dst, SilcUInt32 *dst_len) +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; @@ -374,11 +422,11 @@ int silc_pkcs_sign_with_hash(SilcPKCS pkcs, SilcHash hash, /* Verifies signature with hash. The `data' is hashed and verified against the `signature'. */ -int silc_pkcs_verify_with_hash(SilcPKCS pkcs, SilcHash hash, - unsigned char *signature, - SilcUInt32 signature_len, - unsigned char *data, - SilcUInt32 data_len) +bool silc_pkcs_verify_with_hash(SilcPKCS pkcs, SilcHash hash, + unsigned char *signature, + SilcUInt32 signature_len, + unsigned char *data, + SilcUInt32 data_len) { unsigned char hashr[32]; SilcUInt32 hash_len; @@ -560,8 +608,9 @@ void silc_pkcs_free_identifier(SilcPublicKeyIdentifier 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, +SilcPublicKey silc_pkcs_public_key_alloc(const char *name, + const char *identifier, + const unsigned char *pk, SilcUInt32 pk_len) { SilcPublicKey public_key; @@ -571,6 +620,7 @@ SilcPublicKey silc_pkcs_public_key_alloc(char *name, char *identifier, public_key->name = strdup(name); 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))) { @@ -581,7 +631,7 @@ SilcPublicKey silc_pkcs_public_key_alloc(char *name, char *identifier, } public_key->identifier = strdup(identifier); - public_key->len = 4 + 2 + strlen(name) + 2 + strlen(identifier) + pk_len; + public_key->len = 2 + strlen(name) + 2 + strlen(identifier) + pk_len; silc_free(tmp); return public_key; @@ -602,7 +652,8 @@ 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, +SilcPrivateKey silc_pkcs_private_key_alloc(const char *name, + const unsigned char *prv, SilcUInt32 prv_len) { SilcPrivateKey private_key; @@ -636,7 +687,7 @@ 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, @@ -649,7 +700,7 @@ silc_pkcs_public_key_encode(SilcPublicKey public_key, SilcUInt32 *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); @@ -669,8 +720,8 @@ silc_pkcs_public_key_data_encode(unsigned char *pk, SilcUInt32 pk_len, unsigned char *ret; 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, @@ -682,7 +733,7 @@ silc_pkcs_public_key_data_encode(unsigned char *pk, SilcUInt32 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); @@ -694,8 +745,8 @@ silc_pkcs_public_key_data_encode(unsigned char *pk, SilcUInt32 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, SilcUInt32 data_len, - SilcPublicKey *public_key) +bool silc_pkcs_public_key_decode(unsigned char *data, SilcUInt32 data_len, + SilcPublicKey *public_key) { SilcBuffer buf; SilcPKCS alg; @@ -717,7 +768,12 @@ int silc_pkcs_public_key_decode(unsigned char *data, SilcUInt32 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; } @@ -776,6 +832,7 @@ int silc_pkcs_public_key_decode(unsigned char *data, SilcUInt32 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); @@ -792,6 +849,79 @@ int silc_pkcs_public_key_decode(unsigned char *data, SilcUInt32 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. */ @@ -826,6 +956,7 @@ SilcPublicKey silc_pkcs_public_key_copy(SilcPublicKey public_key) 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; } @@ -854,6 +985,7 @@ silc_pkcs_private_key_encode(SilcPrivateKey private_key, SilcUInt32 *len) ret = silc_calloc(buf->len, sizeof(*ret)); memcpy(ret, buf->data, buf->len); + silc_buffer_clear(buf); silc_buffer_free(buf); return ret; @@ -883,16 +1015,17 @@ silc_pkcs_private_key_data_encode(unsigned char *prv, SilcUInt32 prv_len, ret = silc_calloc(buf->len, sizeof(*ret)); memcpy(ret, buf->data, buf->len); + silc_buffer_clear(buf); silc_buffer_free(buf); return ret; } -/* 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, SilcUInt32 data_len, - SilcPrivateKey *private_key) +bool silc_pkcs_private_key_decode(unsigned char *data, SilcUInt32 data_len, + SilcPrivateKey *private_key) { SilcBuffer buf; SilcPKCS alg; @@ -910,11 +1043,15 @@ int silc_pkcs_private_key_decode(unsigned char *data, SilcUInt32 data_len, silc_buffer_unformat(buf, SILC_STR_UI16_NSTRING_ALLOC(&pkcs_name, &pkcs_len), SILC_STR_END); - if (ret == -1) + if (ret == -1) { + SILC_LOG_DEBUG(("Cannot decode private key buffer")); goto err; + } - if (pkcs_len < 1 || pkcs_len > buf->truelen) + if (pkcs_len < 1 || pkcs_len > buf->truelen) { + SILC_LOG_DEBUG(("Malformed private key buffer")); goto err; + } /* See if we support this algorithm (check only if PKCS are registered). */ if (SILC_PKCS_LIST && !silc_pkcs_is_supported(pkcs_name)) { @@ -936,8 +1073,10 @@ int silc_pkcs_private_key_decode(unsigned char *data, SilcUInt32 data_len, (check only if PKCS are registered) */ if (SILC_PKCS_LIST) { silc_pkcs_alloc(pkcs_name, &alg); - if (!alg->pkcs->set_private_key(alg->context, key_data, key_len)) + if (!alg->pkcs->set_private_key(alg->context, key_data, key_len)) { + SILC_LOG_DEBUG(("Could not set private key data")); goto err; + } silc_pkcs_free(alg); } @@ -948,6 +1087,7 @@ int silc_pkcs_private_key_decode(unsigned char *data, SilcUInt32 data_len, (*private_key)->prv_len = key_len; } + silc_buffer_clear(buf); silc_buffer_free(buf); return TRUE; @@ -956,16 +1096,17 @@ int silc_pkcs_private_key_decode(unsigned char *data, SilcUInt32 data_len, silc_free(pkcs_name); if (key_data) silc_free(key_data); + silc_buffer_clear(buf); silc_buffer_free(buf); return FALSE; } /* Internal routine to save public key */ -static int silc_pkcs_save_public_key_internal(char *filename, - unsigned char *data, - SilcUInt32 data_len, - SilcUInt32 encoding) +static bool silc_pkcs_save_public_key_internal(const char *filename, + unsigned char *data, + SilcUInt32 data_len, + SilcUInt32 encoding) { SilcBuffer buf; SilcUInt32 len; @@ -1002,38 +1143,145 @@ 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, - SilcUInt32 encoding) +bool silc_pkcs_save_public_key(const char *filename, SilcPublicKey public_key, + SilcUInt32 encoding) { unsigned char *data; SilcUInt32 data_len; + bool ret; data = silc_pkcs_public_key_encode(public_key, &data_len); - return silc_pkcs_save_public_key_internal(filename, data, data_len, - encoding); + ret = silc_pkcs_save_public_key_internal(filename, data, data_len, + encoding); + silc_free(data); + return ret; } /* Saves public key into file */ -int silc_pkcs_save_public_key_data(char *filename, unsigned char *data, - SilcUInt32 data_len, - SilcUInt32 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, - SilcUInt32 data_len, - SilcUInt32 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; - SilcUInt32 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: @@ -1042,11 +1290,10 @@ static int silc_pkcs_save_private_key_internal(char *filename, break; } + /* Encode the data and save to file */ len = data_len + (strlen(SILC_PKCS_PRIVATE_KEYFILE_BEGIN) + strlen(SILC_PKCS_PRIVATE_KEYFILE_END)); - buf = silc_buffer_alloc(len); - silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf)); - + buf = silc_buffer_alloc_size(len); silc_buffer_format(buf, SILC_STR_UI32_STRING(SILC_PKCS_PRIVATE_KEYFILE_BEGIN), SILC_STR_UI_XNSTRING(data, data_len), @@ -1055,50 +1302,54 @@ static int silc_pkcs_save_private_key_internal(char *filename, /* Save into a file */ if (silc_file_writefile_mode(filename, buf->data, buf->len, 0600)) { + silc_buffer_clear(buf); silc_buffer_free(buf); + silc_buffer_clear(enc); + silc_buffer_free(enc); return FALSE; } + silc_buffer_clear(buf); silc_buffer_free(buf); + silc_buffer_clear(enc); + silc_buffer_free(enc); return TRUE; } /* Saves private key into file. */ -/* XXX The buffer should be encrypted if passphrase is provided. */ -int silc_pkcs_save_private_key(char *filename, SilcPrivateKey private_key, - unsigned char *passphrase, - SilcUInt32 encoding) +bool silc_pkcs_save_private_key(const char *filename, + SilcPrivateKey private_key, + unsigned char *passphrase, + SilcUInt32 passphrase_len, + SilcUInt32 encoding) { unsigned char *data; SilcUInt32 data_len; + bool ret; data = silc_pkcs_private_key_encode(private_key, &data_len); - return silc_pkcs_save_private_key_internal(filename, data, data_len, - encoding); -} - -/* Saves private key into file. */ -/* XXX The buffer should be encrypted if passphrase is provided. */ - -int silc_pkcs_save_private_key_data(char *filename, unsigned char *data, - SilcUInt32 data_len, - unsigned char *passphrase, - SilcUInt32 encoding) -{ - return silc_pkcs_save_private_key_internal(filename, data, data_len, - encoding); + ret = silc_pkcs_save_private_key_internal(filename, data, data_len, + passphrase, passphrase_len, + encoding); + memset(data, 0, data_len); + silc_free(data); + return ret; } /* Loads public key from file and allocates new public key. Returns TRUE - is loading was successful. */ + if loading was successful. */ -int silc_pkcs_load_public_key(char *filename, SilcPublicKey *public_key, - SilcUInt32 encoding) +bool silc_pkcs_load_public_key(const char *filename, SilcPublicKey *public_key, + SilcUInt32 encoding) { unsigned char *cp, *old, *data, byte; SilcUInt32 i, data_len, 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; @@ -1148,14 +1399,25 @@ 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, - SilcUInt32 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; - SilcUInt32 i, data_len, len; + SilcUInt32 i, data_len, len, magic, mac_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; @@ -1175,23 +1437,139 @@ int silc_pkcs_load_private_key(char *filename, SilcPrivateKey *private_key, data = cp; /* Decode private key */ - if (private_key) { - len = data_len - (strlen(SILC_PKCS_PRIVATE_KEYFILE_BEGIN) + - strlen(SILC_PKCS_PRIVATE_KEYFILE_END)); + len = data_len - (strlen(SILC_PKCS_PRIVATE_KEYFILE_BEGIN) + + strlen(SILC_PKCS_PRIVATE_KEYFILE_END)); - switch(encoding) { - case SILC_PKCS_FILE_BIN: - break; - case SILC_PKCS_FILE_PEM: - data = silc_pem_decode(data, len, &len); - break; + switch(encoding) { + case SILC_PKCS_FILE_BIN: + break; + case SILC_PKCS_FILE_PEM: + data = silc_pem_decode(data, len, &len); + if (!data) { + memset(old, 0, data_len); + silc_free(old); + return FALSE; } + break; + } + + memset(tmp, 0, sizeof(tmp)); + memset(keymat, 0, sizeof(keymat)); - if (!data || !silc_pkcs_private_key_decode(data, len, private_key)) { + /* 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);