diff --git a/src/libknot/dnssec/config.h b/src/libknot/dnssec/config.h index 41eeaef0808c3c3001d382bc020b0ccaf3412330..79cab3427cac9807a647d3777946bd8f807eccea 100644 --- a/src/libknot/dnssec/config.h +++ b/src/libknot/dnssec/config.h @@ -28,6 +28,11 @@ #define _KNOT_DNSSEC_CONFIG_H_ #include <openssl/opensslconf.h> +#include <openssl/opensslv.h> + +#ifndef OPENSSL_VERSION_NUMBER +#error "OpenSSL version is not defined." +#endif // ECDSA support requires OpenSSL version >= 1.0.1 #if !defined(OPENSSL_NO_ECDSA) && OPENSSL_VERSION_NUMBER >= 0x10001000 @@ -36,6 +41,12 @@ #undef KNOT_ENABLE_ECDSA #endif +#if !defined(OPENSSL_NO_GOST) + #define KNOT_ENABLE_GOST 1 +#else + #undef KNOT_ENABLE_GOST +#endif + #endif // _KNOT_DNSSEC_CONFIG_H_ /*! @} */ diff --git a/src/libknot/dnssec/crypto.c b/src/libknot/dnssec/crypto.c index 6faa12a05f6f2e835bcb28216146cd5b97836452..c8c5a3444e61e2d80f84525bb02db6723d96064e 100644 --- a/src/libknot/dnssec/crypto.c +++ b/src/libknot/dnssec/crypto.c @@ -16,13 +16,17 @@ #include <assert.h> #include <openssl/crypto.h> +#include <openssl/engine.h> #include <openssl/err.h> #include <openssl/evp.h> #include <pthread.h> #include "libknot/common.h" +#include "libknot/dnssec/config.h" #include "libknot/dnssec/crypto.h" +/*- thread safety -----------------------------------------------------------*/ + /*! * \brief Mutexes to be used by OpenSSL. */ @@ -107,6 +111,45 @@ static void openssl_threadid_cb(CRYPTO_THREADID *openssl_id) CRYPTO_THREADID_set_pointer(openssl_id, (void *)id); } +/*- pluggable engines -------------------------------------------------------*/ + +#if KNOT_ENABLE_GOST + +static ENGINE *gost_engine = NULL; + +static void init_gost_engine(void) +{ + assert(gost_engine == NULL); + +#ifndef OPENSSL_NO_STATIC_ENGINE + ENGINE_load_gost(); +#else + ENGINE_load_dynamic(); +#endif + + gost_engine = ENGINE_by_id("gost"); + if (!gost_engine) { + return; + } + + ENGINE_init(gost_engine); + ENGINE_register_pkey_asn1_meths(gost_engine); + ENGINE_ctrl_cmd_string(gost_engine, "CRYPT_PARAMS", + "id-Gost28147-89-CryptoPro-A-ParamSet", 0); +} + +static void deinit_gost_engine(void) +{ + assert(gost_engine); + + ENGINE_finish(gost_engine); + ENGINE_free(gost_engine); + + gost_engine = NULL; +} + +#endif + /*- public API --------------------------------------------------------------*/ void knot_crypto_init(void) @@ -116,6 +159,8 @@ void knot_crypto_init(void) void knot_crypto_cleanup(void) { + knot_crypto_unload_engines(); + EVP_cleanup(); CRYPTO_cleanup_all_ex_data(); ERR_free_strings(); @@ -145,3 +190,21 @@ void knot_crypto_cleanup_threads(void) openssl_mutexes_destroy(); } } + +void knot_crypto_load_engines(void) +{ +#if KNOT_ENABLE_GOST + if (!gost_engine) { + init_gost_engine(); + } +#endif +} + +void knot_crypto_unload_engines(void) +{ +#if KNOT_ENABLE_GOST + if (gost_engine) { + deinit_gost_engine(); + } +#endif +} diff --git a/src/libknot/dnssec/crypto.h b/src/libknot/dnssec/crypto.h index 4273c5aeb59bbc70226d1fa5f275f6a2e0322ff4..22e5046a14f240bf8253f66ce59d5ec071bad64b 100644 --- a/src/libknot/dnssec/crypto.h +++ b/src/libknot/dnssec/crypto.h @@ -54,6 +54,16 @@ void knot_crypto_init_threads(void); */ void knot_crypto_cleanup_threads(void); +/*! + * \brief Load pluggable crypto engines. + */ +void knot_crypto_load_engines(void); + +/*! + * \brief Unload pluggable crypto engines. + */ +void knot_crypto_unload_engines(void); + #endif // _KNOT_DNSSEC_CRYPTO_H_ /*! @} */ diff --git a/src/libknot/dnssec/key.c b/src/libknot/dnssec/key.c index a5c9a9fa727675019e9ec3d9d868305a33c2b392..918d7be538f99e1cf29b316ca6d3789bbc6cabc7 100644 --- a/src/libknot/dnssec/key.c +++ b/src/libknot/dnssec/key.c @@ -295,6 +295,7 @@ static const struct key_parameter key_parameters[] = { { "Private_value(x)",key_offset(private_value), key_param_base64 }, { "Public_value(y)", key_offset(public_value), key_param_base64 }, { "PrivateKey", key_offset(private_key), key_param_base64 }, + { "GostAsn1", key_offset(private_key), key_param_base64 }, { "Publish", key_offset(time_publish), key_param_time }, { "Activate", key_offset(time_activate), key_param_time }, { "Inactive", key_offset(time_inactive), key_param_time }, diff --git a/src/libknot/dnssec/key.h b/src/libknot/dnssec/key.h index 4dcb52cdc938cb1f253505b1c14cf972d70e474e..789e3ad9a15cf41ef8acd9db0cf4a377d5aabf24 100644 --- a/src/libknot/dnssec/key.h +++ b/src/libknot/dnssec/key.h @@ -66,7 +66,7 @@ typedef struct { knot_binary_t private_value; knot_binary_t public_value; - // EC + // EC/GOST knot_binary_t private_key; // key lifetime diff --git a/src/libknot/dnssec/rrset-sign.c b/src/libknot/dnssec/rrset-sign.c index a06ea7817981ddaf6ae8cfc6cfd428d17f65b4d5..75995fc01abedc8802fd483b62a62570c4fbd9b5 100644 --- a/src/libknot/dnssec/rrset-sign.c +++ b/src/libknot/dnssec/rrset-sign.c @@ -106,29 +106,35 @@ int knot_rrsig_write_rdata(uint8_t *rdata, const knot_dnssec_key_t *key, /*! * \brief Create RRSIG RDATA (all fields except signature are filled). * - * \param rrsigs RR set with RRSIGS. - * \param covered RR covered by the signature. - * \param key Key used for signing. - * \param sig_incepted Timestamp of signature inception. - * \param sig_expires Timestamp of signature expiration. + * \param[in] rrsigs RR set with RRSIGS. + * \param[in] covered RR covered by the signature. + * \param[in] key Key used for signing. + * \param[in] sig_incepted Timestamp of signature inception. + * \param[in] sig_expires Timestamp of signature expiration. + * \param[out] rdata Created RDATA. + * \param[out] rdata_size Size of created RDATA. + * + * \return Error code, KNOT_EOK if succesful. */ -static uint8_t *rrsigs_create_rdata(knot_rrset_t *rrsigs, - const knot_rrset_t *covered, - const knot_dnssec_key_t *key, - uint32_t sig_incepted, - uint32_t sig_expires) +static int rrsigs_create_rdata(knot_rrset_t *rrsigs, + const knot_rrset_t *covered, + const knot_dnssec_key_t *key, + uint32_t sig_incepted, uint32_t sig_expires, + uint8_t **rdata, size_t *rdata_size) { assert(rrsigs); assert(rrsigs->type == KNOT_RRTYPE_RRSIG); assert(covered); assert(key); + assert(rdata); + assert(rdata_size); - size_t rdata_size = knot_rrsig_rdata_size(key); - assert(rdata_size != 0); + size_t size = knot_rrsig_rdata_size(key); + assert(size != 0); - uint8_t *rdata = knot_rrset_create_rdata(rrsigs, rdata_size); - if (!rdata) { - return NULL; + uint8_t *result = knot_rrset_create_rdata(rrsigs, size); + if (!result) { + return KNOT_ENOMEM; } uint8_t owner_labels = knot_dname_labels(covered->owner, NULL); @@ -136,12 +142,16 @@ static uint8_t *rrsigs_create_rdata(knot_rrset_t *rrsigs, owner_labels -= 1; } - int res = knot_rrsig_write_rdata(rdata, key, covered->type, owner_labels, - covered->ttl, sig_incepted, sig_expires); + int res = knot_rrsig_write_rdata(result, key, covered->type, owner_labels, + covered->ttl, sig_incepted, sig_expires); + assert(res == KNOT_EOK); + UNUSED(res); + *rdata = result; + *rdata_size = size; - return rdata; + return KNOT_EOK; } /*- Computation of signatures ------------------------------------------------*/ @@ -280,13 +290,16 @@ int knot_sign_rrset(knot_rrset_t *rrsigs, const knot_rrset_t *covered, uint32_t sig_incept = policy->now; uint32_t sig_expire = sig_incept + policy->sign_lifetime; - uint8_t *rdata = rrsigs_create_rdata(rrsigs, covered, key, - sig_incept, sig_expire); - if (!rdata) { - return KNOT_ENOMEM; + uint8_t *rdata = NULL; + size_t rdata_size = 0; + + int result = rrsigs_create_rdata(rrsigs, covered, key, sig_incept, + sig_expire, &rdata, &rdata_size); + if (result != KNOT_EOK) { + return result; } - int result = knot_dnssec_sign_new(sign_ctx); + result = knot_dnssec_sign_new(sign_ctx); if (result != KNOT_EOK) { return result; } @@ -296,10 +309,11 @@ int knot_sign_rrset(knot_rrset_t *rrsigs, const knot_rrset_t *covered, return result; } - uint8_t *rdata_signature = rdata + RRSIG_RDATA_SIGNER_OFFSET - + knot_dname_size(key->name); + size_t signature_offset = RRSIG_RDATA_SIGNER_OFFSET + knot_dname_size(key->name); + uint8_t *signature = rdata + signature_offset; + size_t signature_size = rdata_size - signature_offset; - return knot_dnssec_sign_write(sign_ctx, rdata_signature); + return knot_dnssec_sign_write(sign_ctx, signature, signature_size); } /*- Verification of signatures -----------------------------------------------*/ diff --git a/src/libknot/dnssec/sig0.c b/src/libknot/dnssec/sig0.c index b8f255c94d5ac599d4cdb9e8a58cc29e85ab2fb4..65827b12d684d6567c4b15a087c0e16e582c31a5 100644 --- a/src/libknot/dnssec/sig0.c +++ b/src/libknot/dnssec/sig0.c @@ -105,8 +105,9 @@ static int sig0_write_signature(uint8_t* wire, size_t request_size, assert(key->data); knot_dnssec_sign_context_t *ctx = knot_dnssec_sign_init(key); - if (!ctx) + if (!ctx) { return KNOT_ENOMEM; + } size_t signature_size = knot_dnssec_sign_size(key); size_t sig_rr_header_size = 11; // owner (== root), type, class, TTL @@ -117,7 +118,7 @@ static int sig0_write_signature(uint8_t* wire, size_t request_size, knot_dnssec_sign_add(ctx, sig_rdata, sig_rdata_size - signature_size); knot_dnssec_sign_add(ctx, wire, request_size); - int result = knot_dnssec_sign_write(ctx, signature); + int result = knot_dnssec_sign_write(ctx, signature, signature_size); knot_dnssec_sign_free(ctx); diff --git a/src/libknot/dnssec/sign.c b/src/libknot/dnssec/sign.c index 8f9a681baa69564c11c5994437488b81526d34a0..1d432267c1e1f407cbe0cebb87e92498ec326d2a 100644 --- a/src/libknot/dnssec/sign.c +++ b/src/libknot/dnssec/sign.c @@ -17,20 +17,25 @@ #include <config.h> #include <assert.h> #include <openssl/dsa.h> -#include <openssl/opensslconf.h> #include <openssl/evp.h> +#include <openssl/opensslconf.h> #include <openssl/rsa.h> +#include <pthread.h> #include "common/descriptor.h" #include "common/errcode.h" #include "libknot/common.h" #include "libknot/consts.h" #include "libknot/dnssec/config.h" +#include "libknot/dnssec/crypto.h" #include "libknot/dnssec/key.h" #include "libknot/dnssec/sign.h" #ifdef KNOT_ENABLE_ECDSA #include <openssl/ecdsa.h> #endif +#ifdef KNOT_ENABLE_GOST +#include <openssl/x509.h> +#endif #define DNSKEY_RDATA_PUBKEY_OFFSET 4 @@ -53,6 +58,8 @@ struct knot_dnssec_sign_context { * \brief Algorithm implementation specific functions. */ struct algorithm_functions { + //! \brief Callback: function called before creating any keys/contexts + int (*algorithm_init)(void); //! \brief Callback: create private key from key parameters. int (*create_pkey)(const knot_key_params_t *, EVP_PKEY *); //! \brief Callback: get signature size in bytes. @@ -60,7 +67,7 @@ struct algorithm_functions { //! \brief Callback: cover supplied data with the signature. int (*sign_add)(const knot_dnssec_sign_context_t *, const uint8_t *, size_t); //! \brief Callback: finish the signing and write out the signature. - int (*sign_write)(const knot_dnssec_sign_context_t *, uint8_t *); + int (*sign_write)(const knot_dnssec_sign_context_t *, uint8_t *, size_t); //! \brief Callback: finish the signing and validate the signature. int (*sign_verify)(const knot_dnssec_sign_context_t *, const uint8_t *, size_t); }; @@ -75,6 +82,14 @@ static BIGNUM *binary_to_bn(const knot_binary_t *bin) /*- Algorithm independent ----------------------------------------------------*/ +/*! + * \brief Initialize algorithm. + */ +static int any_algorithm_init(void) +{ + return KNOT_EOK; +} + /*! * \brief Get size of the resulting signature. * @@ -112,45 +127,114 @@ static int any_sign_add(const knot_dnssec_sign_context_t *context, } /*! - * \brief Finish the signing and get the RAW signature. - * - * Caller should free the memory returned via signature parameter. + * \brief Finish the signing and write the signature while checking boundaries. * - * \param context DNSSEC signature context. - * \param signature Pointer to signature (output). - * \param signature_size Signature size (output). + * \param context DNSSEC signing context. + * \param signature Pointer to signature to be written. + * \param max_size Maximal size of the signature. + * \param size Actual size of written signature. * * \return Error code, KNOT_EOK if successful. */ -static int any_sign_write(const knot_dnssec_sign_context_t *context, - uint8_t **signature, size_t *signature_size) +static int sign_safe_write(const knot_dnssec_sign_context_t *context, + uint8_t *signature, size_t max_size, size_t *size) { assert(context); assert(signature); - assert(signature_size); + assert(size); - size_t max_size = (size_t)EVP_PKEY_size(context->key->data->private_key); - uint8_t *output = calloc(1, max_size); - if (!output) { - return KNOT_ENOMEM; + EVP_MD_CTX *digest_ctx = context->digest_context; + EVP_PKEY *private_key = context->key->data->private_key; + + // check target size + + unsigned int max_write = 0; + int result = EVP_SignFinal(digest_ctx, NULL, &max_write, private_key); + if (!result) { + return KNOT_DNSSEC_ESIGN; + } + + if (max_write > max_size) { + return KNOT_DNSSEC_EUNEXPECTED_SIGNATURE_SIZE; } - unsigned int actual_size; - int result = EVP_SignFinal(context->digest_context, output, - &actual_size, context->key->data->private_key); + // write signature + + unsigned int written = 0; + result = EVP_SignFinal(digest_ctx, signature, &written, private_key); if (!result) { - free(output); return KNOT_DNSSEC_ESIGN; } - assert(actual_size <= max_size); + assert(written <= max_write); + *size = written; + + return KNOT_EOK; +} + +/*! + * \brief Allocate space for signature, finish signature, and write it. + * + * \param context DNSSEC signing context. + * \param signature Pointer to allocated signature. + * \param size Size of the written signature. + * + * \return Error code, KNOT_EOK if successful. + */ +static int sign_alloc_and_write(const knot_dnssec_sign_context_t *context, + uint8_t **signature, size_t *size) +{ + assert(context); + assert(signature); + assert(size); + + size_t buffer_size = EVP_PKEY_size(context->key->data->private_key); + uint8_t *buffer = malloc(buffer_size); + if (!buffer) { + return KNOT_ENOMEM; + } + + size_t written = 0; + int result = sign_safe_write(context, buffer, buffer_size, &written); + if (result != KNOT_EOK) { + free(buffer); + return result; + } + + assert(written <= buffer_size); - *signature = output; - *signature_size = actual_size; + *signature = buffer; + *size = written; return KNOT_EOK; } +/*! + * \brief Finish the signing and write out the signature. + * + * \note Expects algorithm whose signature size is constant. + * + * \param context DNSSEC signing context. + * \param signature Pointer to memory where the signature will be written. + * \param signature_size Expected size of the signature. + * + * \return Error code, KNOT_EOK if successful. + */ +static int any_sign_write(const knot_dnssec_sign_context_t *context, + uint8_t *signature, size_t signature_size) +{ + assert(context); + assert(signature); + + size_t written_size = 0; + int result = sign_safe_write(context, signature, + signature_size, &written_size); + + assert(written_size == signature_size); + + return result; +} + /*! * \brief Verify the DNSSEC signature for supplied data. * @@ -183,6 +267,32 @@ static int any_sign_verify(const knot_dnssec_sign_context_t *context, }; } +/*! + * \brief Get pointer to and size of public key in DNSKEY RDATA. + * + * \param[in] rdata DNSKEY RDATA. + * \param[out] pubkey Public key. + * \param[out] pubkey_size Size of public key. + * + * \return Success. + */ +static bool any_dnskey_get_pubkey(const knot_binary_t *rdata, + const uint8_t **pubkey, size_t *pubkey_size) +{ + assert(rdata); + assert(pubkey); + assert(pubkey_size); + + if (rdata->size <= DNSKEY_RDATA_PUBKEY_OFFSET) { + return false; + } + + *pubkey = rdata->data + DNSKEY_RDATA_PUBKEY_OFFSET; + *pubkey_size = rdata->size - DNSKEY_RDATA_PUBKEY_OFFSET; + + return true; +} + /*- RSA specific -------------------------------------------------------------*/ /*! @@ -225,41 +335,6 @@ static int rsa_create_pkey(const knot_key_params_t *params, EVP_PKEY *key) return KNOT_EOK; } -/*! - * \brief Finish the signing and write out the RSA signature. - * - * \param context DNSSEC signing context. - * \param signature Pointer to memory where the signature will be written. - * - * \return Error code, KNOT_EOK if successful. - */ -static int rsa_sign_write(const knot_dnssec_sign_context_t *context, - uint8_t *signature) -{ - assert(context); - assert(signature); - - int result; - uint8_t *raw_signature; - size_t raw_signature_size; - const knot_dnssec_key_t *key = context->key; - - result = any_sign_write(context, &raw_signature, &raw_signature_size); - if (result != KNOT_EOK) { - return result; - } - - if (raw_signature_size != key->data->functions->sign_size(key)) { - free(raw_signature); - return KNOT_DNSSEC_EUNEXPECTED_SIGNATURE_SIZE; - } - - memcpy(signature, raw_signature, raw_signature_size); - free(raw_signature); - - return KNOT_EOK; -} - /*- DSA specific -------------------------------------------------------------*/ /*! @@ -303,19 +378,24 @@ static size_t dsa_sign_size(const knot_dnssec_key_t *key) /*! * \brief Finish the signing and write out the DSA signature. - * \see rsa_sign_write + * \see any_sign_write */ static int dsa_sign_write(const knot_dnssec_sign_context_t *context, - uint8_t *signature) + uint8_t *signature, size_t signature_size) { assert(context); assert(signature); - int result; - uint8_t *raw_signature; - size_t raw_signature_size; + size_t output_size = dsa_sign_size(context->key); + if (output_size != signature_size) { + return KNOT_DNSSEC_EUNEXPECTED_SIGNATURE_SIZE; + } + + // create raw signature - result = any_sign_write(context, &raw_signature, &raw_signature_size); + uint8_t *raw_signature = NULL; + size_t raw_size = 0; + int result = sign_alloc_and_write(context, &raw_signature, &raw_size); if (result != KNOT_EOK) { return result; } @@ -329,7 +409,7 @@ static int dsa_sign_write(const knot_dnssec_sign_context_t *context, } const uint8_t *decode_scan = raw_signature; - if (!d2i_DSA_SIG(&decoded, &decode_scan, (long)raw_signature_size)) { + if (!d2i_DSA_SIG(&decoded, &decode_scan, (long)raw_size)) { DSA_SIG_free(decoded); free(raw_signature); return KNOT_DNSSEC_EDECODE_RAW_SIGNATURE; @@ -344,7 +424,7 @@ static int dsa_sign_write(const knot_dnssec_sign_context_t *context, uint8_t *signature_r = signature + 21 - BN_num_bytes(decoded->r); uint8_t *signature_s = signature + 41 - BN_num_bytes(decoded->s); - memset(signature, '\0', dsa_sign_size(context->key)); + memset(signature, '\0', output_size); *signature_t = 0x00; //! \todo Take from public key. Only recommended. BN_bn2bin(decoded->r, signature_r); BN_bn2bin(decoded->s, signature_s); @@ -419,20 +499,19 @@ static int ecdsa_set_public_key(const knot_binary_t *rdata, EC_KEY *ec_key) assert(rdata); assert(ec_key); - if (rdata->size <= DNSKEY_RDATA_PUBKEY_OFFSET) { + const uint8_t *pubkey_data = NULL; + size_t pubkey_size = 0; + if (!any_dnskey_get_pubkey(rdata, &pubkey_data, &pubkey_size)) { return KNOT_EINVAL; } - uint8_t *pubkey_data = rdata->data + DNSKEY_RDATA_PUBKEY_OFFSET; - size_t pubkey_size = rdata->size - DNSKEY_RDATA_PUBKEY_OFFSET; - if (pubkey_size % 2 != 0) { return KNOT_EINVAL; } size_t param_size = pubkey_size / 2; - uint8_t *x_ptr = pubkey_data; - uint8_t *y_ptr = pubkey_data + param_size; + const uint8_t *x_ptr = pubkey_data; + const uint8_t *y_ptr = pubkey_data + param_size; BIGNUM *x = BN_bin2bn(x_ptr, param_size, NULL); BIGNUM *y = BN_bin2bn(y_ptr, param_size, NULL); @@ -531,16 +610,21 @@ static size_t ecdsa_sign_size(const knot_dnssec_key_t *key) * \see rsa_sign_write */ static int ecdsa_sign_write(const knot_dnssec_sign_context_t *context, - uint8_t *signature) + uint8_t *signature, size_t signature_size) { assert(context); assert(signature); - int result; - uint8_t *raw_signature; - size_t raw_signature_size; + size_t output_size = ecdsa_sign_size(context->key); + if (output_size != signature_size) { + return KNOT_DNSSEC_EUNEXPECTED_SIGNATURE_SIZE; + } - result = any_sign_write(context, &raw_signature, &raw_signature_size); + // create raw signature + + uint8_t *raw_signature = NULL; + size_t raw_size = 0; + int result = sign_alloc_and_write(context, &raw_signature, &raw_size); if (result != KNOT_EOK) { return result; } @@ -554,7 +638,7 @@ static int ecdsa_sign_write(const knot_dnssec_sign_context_t *context, } const uint8_t *decode_scan = raw_signature; - if (!d2i_ECDSA_SIG(&decoded, &decode_scan, (long)raw_signature_size)) { + if (!d2i_ECDSA_SIG(&decoded, &decode_scan, (long)raw_size)) { ECDSA_SIG_free(decoded); free(raw_signature); return KNOT_DNSSEC_EDECODE_RAW_SIGNATURE; @@ -567,10 +651,9 @@ static int ecdsa_sign_write(const knot_dnssec_sign_context_t *context, uint8_t *signature_r; uint8_t *signature_s; - size_t signature_size = ecdsa_sign_size(context->key); - size_t param_size = signature_size / 2; + size_t param_size = output_size / 2; - memset(signature, '\0', signature_size); + memset(signature, '\0', output_size); signature_r = signature + param_size - BN_num_bytes(decoded->r); signature_s = signature + 2 * param_size - BN_num_bytes(decoded->s); @@ -637,17 +720,136 @@ static int ecdsa_sign_verify(const knot_dnssec_sign_context_t *context, #endif +/*- GOST specific ------------------------------------------------------------*/ + +#ifdef KNOT_ENABLE_GOST + +static pthread_once_t gost_init_control = PTHREAD_ONCE_INIT; + +static void gost_algorithm_init_once(void) +{ + knot_crypto_load_engines(); +} + +/*! + * \brief Initialize GOST algorithm. + * \see any_algorithm_init + */ +static int gost_algorithm_init(void) +{ + pthread_once(&gost_init_control, gost_algorithm_init_once); + + return KNOT_EOK; +} + +// future feature (disabled now) +#if 0 +/*! + * Prefix for GOST public keys allowing to load them using X.509 API functions. + * + * GOST keys in X.509 PKI start with prefix which identifies key algorithm. + * GOST public keys in DNS do not contain this prefix. We have to add it as + * OpenSSL supports GOST using dynamic engine and has no GOST specific API. + * + * RFC 5933 (GOST in DNSSEC, specifies this prefix), RFC 4491 (GOST in X.509) + */ +static const uint8_t gost_x509_pubkey_prefix[] = { + 0x30, 0x63, 0x30, 0x1c, 0x06, 0x06, 0x2a, 0x85, 0x03, 0x02, + 0x02, 0x13, 0x30, 0x12, 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, + 0x02, 0x23, 0x01, 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, + 0x1e, 0x01, 0x03, 0x43, 0x00, 0x04, 0x40 +}; + +#define GOST_DNSSEC_PUBKEY_SIZE 64 +#define GOST_X509_PUBKEY_PREFIX_SIZE sizeof(gost_x509_pubkey_prefix) +#define GOST_X509_PUBKEY_SIZE (GOST_X509_PUBKEY_PREFIX_SIZE + GOST_DNSSEC_PUBKEY_SIZE) + +static int gost_get_public_key(const knot_binary_t *dnskey_rdata, EVP_PKEY **key) +{ + assert(dnskey_rdata); + assert(key); + + const uint8_t *pubkey_data = NULL; + size_t pubkey_size = 0; + if (!any_dnskey_get_pubkey(dnskey_rdata, &pubkey_data, &pubkey_size)) { + return KNOT_EINVAL; + } + + if (pubkey_size != GOST_DNSSEC_PUBKEY_SIZE) { + return KNOT_DNSSEC_EINVALID_KEY; + } + + // construct X.509 public key + + uint8_t x509_pubkey[GOST_X509_PUBKEY_SIZE] = { '\0' }; + uint8_t *prefix = x509_pubkey; + uint8_t *keydata = x509_pubkey + GOST_X509_PUBKEY_PREFIX_SIZE; + + memcpy(prefix, gost_x509_pubkey_prefix, GOST_X509_PUBKEY_PREFIX_SIZE); + memcpy(keydata, pubkey_data, pubkey_size); + + // construct EVP_PKEY + + const unsigned char **parse = (const unsigned char **)&prefix; + EVP_PKEY *result = d2i_PUBKEY(NULL, parse, GOST_X509_PUBKEY_SIZE); + if (!result) { + return KNOT_DNSSEC_EINVALID_KEY; + } + + *key = result; + + return KNOT_EOK; +} +#endif + +static int gost_set_private_key(const knot_binary_t *private, EVP_PKEY **key) +{ + assert(private); + assert(key && *key); + + const unsigned char *parse = private->data; + EVP_PKEY *result = d2i_PrivateKey(NID_id_GostR3410_2001, key, + &parse, private->size); + + if (result != *key) { + return KNOT_DNSSEC_EINVALID_KEY; + } + + return KNOT_EOK; +} + +/*! + * \brief Create GOST private key from key parameters. + * \see rsa_create_pkey + */ +static int gost_create_pkey(const knot_key_params_t *params, EVP_PKEY * key) +{ + assert(params); + assert(key); + + int result = gost_set_private_key(¶ms->private_key, &key); + if (result != KNOT_EOK) { + return result; + } + + return KNOT_EOK; +} + +#endif + /*- Algorithm specifications -------------------------------------------------*/ static const algorithm_functions_t rsa_functions = { + any_algorithm_init, rsa_create_pkey, any_sign_size, any_sign_add, - rsa_sign_write, + any_sign_write, any_sign_verify }; static const algorithm_functions_t dsa_functions = { + any_algorithm_init, dsa_create_pkey, dsa_sign_size, any_sign_add, @@ -657,6 +859,7 @@ static const algorithm_functions_t dsa_functions = { #ifdef KNOT_ENABLE_ECDSA static const algorithm_functions_t ecdsa_functions = { + any_algorithm_init, ecdsa_create_pkey, ecdsa_sign_size, any_sign_add, @@ -665,6 +868,17 @@ static const algorithm_functions_t ecdsa_functions = { }; #endif +#ifdef KNOT_ENABLE_GOST +static const algorithm_functions_t gost_functions = { + gost_algorithm_init, + gost_create_pkey, + any_sign_size, + any_sign_add, + any_sign_write, + any_sign_verify +}; +#endif + /*! * \brief Get implementation specific callbacks for a given algorithm. * @@ -684,10 +898,14 @@ static const algorithm_functions_t *get_implementation(int algorithm) case KNOT_DNSSEC_ALG_DSA: case KNOT_DNSSEC_ALG_DSA_NSEC3_SHA1: return &dsa_functions; +#ifdef KNOT_ENABLE_ECDSA case KNOT_DNSSEC_ALG_ECDSAP256SHA256: case KNOT_DNSSEC_ALG_ECDSAP384SHA384: -#ifdef KNOT_ENABLE_ECDSA return &ecdsa_functions; +#endif +#ifdef KNOT_ENABLE_GOST + case KNOT_DNSSEC_ALG_ECC_GOST: + return &gost_functions; #endif default: return NULL; @@ -720,6 +938,10 @@ static const EVP_MD *get_digest_type(knot_dnssec_algorithm_t algorithm) return EVP_sha384(); case KNOT_DNSSEC_ALG_RSASHA512: return EVP_sha512(); +#if KNOT_ENABLE_GOST + case KNOT_DNSSEC_ALG_ECC_GOST: + return EVP_get_digestbyname(SN_id_GostR3411_94); +#endif default: return NULL; } @@ -811,6 +1033,21 @@ static int destroy_digest_context(EVP_MD_CTX **context) return KNOT_EOK; } +/*! + * \brief Initialize algorithm. + * + * \param functions Algorithm specific callbacks. + * + * \return Error code, KNOT_EOK if successful. + */ +static int init_algorithm(const algorithm_functions_t *functions) +{ + assert(functions); + assert(functions->algorithm_init); + + return functions->algorithm_init(); +} + /*! * \brief Free algorithm data. * @@ -827,6 +1064,8 @@ static int clean_algorithm_data(knot_dnssec_key_data_t *data) data->private_key = NULL; } + memset(data, '\0', sizeof(*data)); + return KNOT_EOK; } @@ -844,17 +1083,25 @@ static int init_algorithm_data(const knot_key_params_t *params, assert(params); assert(data); - data->functions = get_implementation(params->algorithm); - if (!data->functions) { + knot_dnssec_key_data_t result = { '\0' }; + + result.functions = get_implementation(params->algorithm); + if (!result.functions) { return KNOT_DNSSEC_ENOTSUP; } - int result = create_pkey(params, data->functions, &data->private_key); - if (result != KNOT_EOK) { - clean_algorithm_data(data); - return result; + int error = init_algorithm(result.functions); + if (error != KNOT_EOK) { + return error; + } + + error = create_pkey(params, result.functions, &result.private_key); + if (error != KNOT_EOK) { + return error; } + *data = result; + return KNOT_EOK; } @@ -1011,13 +1258,15 @@ int knot_dnssec_sign_add(knot_dnssec_sign_context_t *context, /** * \brief Write down the DNSSEC signature for supplied data. */ -int knot_dnssec_sign_write(knot_dnssec_sign_context_t *context, uint8_t *signature) +int knot_dnssec_sign_write(knot_dnssec_sign_context_t *context, + uint8_t *signature, size_t signature_size) { - if (!context || !context->key || !signature) { + if (!context || !context->key || !signature || signature_size == 0) { return KNOT_EINVAL; } - return context->key->data->functions->sign_write(context, signature); + return context->key->data->functions->sign_write(context, signature, + signature_size); } /** diff --git a/src/libknot/dnssec/sign.h b/src/libknot/dnssec/sign.h index a4f321c991fd324fb8dbf298df30551e8ef85232..319f08700781642cdbcfbb68b1cca13396382ded 100644 --- a/src/libknot/dnssec/sign.h +++ b/src/libknot/dnssec/sign.h @@ -132,13 +132,14 @@ int knot_dnssec_sign_add(knot_dnssec_sign_context_t *context, /** * \brief Write down the DNSSEC signature for supplied data. * - * \param context DNSSEC signing context. - * \param signature Pointer to signature to be written. + * \param context DNSSEC signing context. + * \param signature Pointer to signature to be written. + * \param signature_size Allocated size for the signature. * * \return Error code, KNOT_EOK if successful. */ int knot_dnssec_sign_write(knot_dnssec_sign_context_t *context, - uint8_t *signature); + uint8_t *signature, size_t signature_size); /** * \brief Verify the DNSSEC signature for supplied data. diff --git a/src/libknot/updates/xfr-in.c b/src/libknot/updates/xfr-in.c index da8a0246126b8a3323816bc82a44cf1dc749f0aa..f61d4c2d6ebd8439f4a5e0efde09d9dc9772f8b4 100644 --- a/src/libknot/updates/xfr-in.c +++ b/src/libknot/updates/xfr-in.c @@ -1508,14 +1508,6 @@ static knot_node_t *xfrin_add_new_node(knot_zone_contents_t *contents, return NULL; } - /*! - * \note It is not needed to set the previous node, we will do this - * in adjusting after the transfer. - */ - - assert(contents->zone != NULL); - knot_node_set_zone(node, contents->zone); - return node; } @@ -1964,10 +1956,10 @@ static void xfrin_cleanup_failed_update(knot_zone_contents_t *old_contents, if (old_contents != NULL) { // cleanup old zone tree - reset pointers to new node to NULL - knot_zone_tree_apply(old_contents, xfrin_cleanup_old_nodes, + knot_zone_tree_apply(old_contents->nodes, xfrin_cleanup_old_nodes, NULL); - knot_zone_tree_apply(old_contents, xfrin_cleanup_old_nodes, + knot_zone_tree_apply(old_contents->nsec3_nodes, xfrin_cleanup_old_nodes, NULL); } } diff --git a/src/libknot/zone/node.c b/src/libknot/zone/node.c index 062ffcadb6105f9a0fcfdb1d23db8bf008aa5461..2bbb7068a99ef0e8b00189432bbb624bc3a56105 100644 --- a/src/libknot/zone/node.c +++ b/src/libknot/zone/node.c @@ -488,28 +488,6 @@ void knot_node_set_new_node(knot_node_t *node, /*----------------------------------------------------------------------------*/ -void knot_node_set_zone(knot_node_t *node, const knot_zone_t *zone) -{ - if (node == NULL) { - return; - } - - node->zone = zone; -} - -/*----------------------------------------------------------------------------*/ - -const knot_zone_t *knot_node_zone(const knot_node_t *node) -{ - if (node == NULL) { - return NULL; - } - - return node->zone; -} - -/*----------------------------------------------------------------------------*/ - void knot_node_update_ref(knot_node_t **ref) { if (*ref != NULL && (*ref)->new_node != NULL) { diff --git a/src/libknot/zone/node.h b/src/libknot/zone/node.h index 0fdb731c53f61aefde7aebd5bb912e039ca477ab..bbf81b826b9f24b38143d624289ceb5dcc7abaee 100644 --- a/src/libknot/zone/node.h +++ b/src/libknot/zone/node.h @@ -70,8 +70,6 @@ struct knot_node { */ struct knot_node *nsec3_node; - const struct knot_zone *zone; - struct knot_node *new_node; unsigned int children; @@ -331,10 +329,6 @@ knot_node_t *knot_node_get_new_node(const knot_node_t *node); void knot_node_set_new_node(knot_node_t *node, knot_node_t *new_node); -void knot_node_set_zone(knot_node_t *node, const struct knot_zone *zone); - -const struct knot_zone *knot_node_zone(const knot_node_t *node); - void knot_node_update_ref(knot_node_t **ref); void knot_node_update_refs(knot_node_t *node); diff --git a/src/libknot/zone/zone-contents.c b/src/libknot/zone/zone-contents.c index 058db8d5c0899cf85c1d94e9ec0508e6ce25043e..f8f1a8d8b885c319cc92d892f8c0320bc3d6b2aa 100644 --- a/src/libknot/zone/zone-contents.c +++ b/src/libknot/zone/zone-contents.c @@ -350,7 +350,6 @@ knot_zone_contents_t *knot_zone_contents_new(knot_node_t *apex, contents->apex = apex; contents->zone = zone; - knot_node_set_zone(apex, contents->zone); contents->node_count = 1; dbg_zone_verb("Creating tree for normal nodes.\n"); @@ -510,8 +509,6 @@ dbg_zone_exec_detail( return ret; } - knot_node_set_zone(node, zone->zone); - ++zone->node_count; if (!create_parents) { @@ -557,7 +554,6 @@ dbg_zone_exec_detail( /* Update node pointers. */ knot_node_set_parent(node, next_node); - knot_node_set_zone(next_node, zone->zone); if (knot_dname_is_wildcard(knot_node_owner(node))) { knot_node_set_wildcard_child(next_node, node); } @@ -790,9 +786,6 @@ int knot_zone_contents_add_nsec3_node(knot_zone_contents_t *zone, // set the apex as the parent of the node knot_node_set_parent(node, zone->apex); - // set the zone to the node - knot_node_set_zone(node, zone->zone); - // cannot be wildcard child, so nothing to be done return KNOT_EOK; @@ -1587,7 +1580,9 @@ void knot_zone_contents_free(knot_zone_contents_t **contents) } // free the zone tree, but only the structure + dbg_zone("Destroying zone tree.\n"); knot_zone_tree_free(&(*contents)->nodes); + dbg_zone("Destroying NSEC3 zone tree.\n"); knot_zone_tree_free(&(*contents)->nsec3_nodes); knot_nsec3_params_free(&(*contents)->nsec3_params); @@ -1619,19 +1614,9 @@ void knot_zone_contents_deep_free(knot_zone_contents_t **contents) (*contents)->nodes, knot_zone_contents_destroy_node_rrsets_from_tree, (void*)1); - - // free the zone tree, but only the structure - // (nodes are already destroyed) - dbg_zone("Destroying zone tree.\n"); - knot_zone_tree_free(&(*contents)->nodes); - dbg_zone("Destroying NSEC3 zone tree.\n"); - knot_zone_tree_free(&(*contents)->nsec3_nodes); - - knot_nsec3_params_free(&(*contents)->nsec3_params); } - free((*contents)); - *contents = NULL; + knot_zone_contents_free(contents); } /*----------------------------------------------------------------------------*/ diff --git a/tests/dnssec_sign.c b/tests/dnssec_sign.c index aade0d47224451e3ae70ca0bb1f5921c9a3a8fd8..c53976e0501cef3b48cd061736a569bbfca78b5d 100644 --- a/tests/dnssec_sign.c +++ b/tests/dnssec_sign.c @@ -24,12 +24,6 @@ #include "libknot/dnssec/crypto.h" #include "libknot/dnssec/sign.h" -#ifdef KNOT_ENABLE_ECDSA -static const int ecdsa_supported = 1; -#else -static const int ecdsa_supported = 0; -#endif - static void test_algorithm(const char *alg, const knot_key_params_t *kp) { int result; @@ -64,7 +58,7 @@ static void test_algorithm(const char *alg, const knot_key_params_t *kp) result = knot_dnssec_sign_add(ctx, (uint8_t *)"dns", 3); is_int(KNOT_EOK, result, "%s: add data C", alg); - result = knot_dnssec_sign_write(ctx, sig); + result = knot_dnssec_sign_write(ctx, sig, sig_size); is_int(KNOT_EOK, result, "%s: write signature", alg); result = knot_dnssec_sign_new(ctx); @@ -94,7 +88,7 @@ static void test_algorithm(const char *alg, const knot_key_params_t *kp) int main(int argc, char *argv[]) { - plan(42); + plan(4 * 14); knot_key_params_t kp = { 0 }; @@ -129,17 +123,29 @@ int main(int argc, char *argv[]) // ECDSA - if (!ecdsa_supported) { - skip_block(14, "ECDSA: not supported on this system"); - } else { - kp.name = knot_dname_from_str("example.com"); - kp.algorithm = 13; - knot_binary_from_base64("1N/PvpB8jZcvv+zr3Q987RKK1cBxDKULzEc5F/nnpSg=", &kp.private_key); - knot_binary_from_base64("AAAAAH3t6EfkvHK5fQMGslhWcCfMF6Q3oNbol2f19DGAb8r49ZX7iQ12sFIyrs2CiwDxFR9Y7fF2zOZ005VV1LA3m1Q=", &kp.rdata); +#ifdef KNOT_ENABLE_ECDSA + kp.name = knot_dname_from_str("example.com"); + kp.algorithm = 13; + knot_binary_from_base64("1N/PvpB8jZcvv+zr3Q987RKK1cBxDKULzEc5F/nnpSg=", &kp.private_key); + knot_binary_from_base64("AAAAAH3t6EfkvHK5fQMGslhWcCfMF6Q3oNbol2f19DGAb8r49ZX7iQ12sFIyrs2CiwDxFR9Y7fF2zOZ005VV1LA3m1Q=", &kp.rdata); - test_algorithm("ECDSA", &kp); - knot_free_key_params(&kp); - } + test_algorithm("ECDSA", &kp); + knot_free_key_params(&kp); +#else + skip_block(14, "ECDSA: not supported on this system"); +#endif + +#if KNOT_ENABLE_GOST + kp.name = knot_dname_from_str("example.com"); + kp.algorithm = 12; + knot_binary_from_base64("MEUCAQAwHAYGKoUDAgITMBIGByqFAwICIwEGByqFAwICHgEEIgIgN2CMRL538HmFM9+GHYM54rEDYO+tLDV3q7AtK1nZ4iA=", &kp.private_key); + knot_binary_from_base64("eHh4eOJ4YHvlasoDRc4ZnvRzldoTUgwWSW0bPv7r9xJ074Dn8KzM4yU9fJgTwIT1TsaHmejYopDnVdjxZyrKNra8Keo=", &kp.rdata); + + test_algorithm("GOST", &kp); + knot_free_key_params(&kp); +#else + skip_block(14, "GOST: not supported on this system"); +#endif knot_crypto_cleanup();