Skip to content
Snippets Groups Projects
Commit d41cd9d0 authored by Jan Včelák's avatar Jan Včelák :rocket:
Browse files

[dnssec] implement nsec3 hashing

parent 4449243c
Branches
Tags
1 merge request!332libdnssec
......@@ -27,6 +27,8 @@ libdnssec_la_SOURCES = \
src/keystore.h \
src/nsec/bitmap.c \
src/nsec/bitmap.h \
src/nsec/hash.c \
src/nsec/hash.h \
src/shared.h \
src/sign.c \
src/sign.h \
......
......@@ -36,6 +36,8 @@ enum dnssec_error {
DNSSEC_SIGN_INIT_ERROR,
DNSSEC_SIGN_ERROR,
DNSSEC_INVALID_SIGNATURE,
DNSSEC_INVALID_NSEC3_ALGORITHM,
DNSSEC_NSEC3_HASHING_ERROR,
};
static inline int dnssec_errno_to_error(int ecode)
......
#include <assert.h>
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>
#include <string.h>
#include "error.h"
#include "nsec/hash.h"
#include "shared.h"
#include "wire.h"
static void free_gnutls_hash(gnutls_hash_hd_t *ptr)
{
if (*ptr) {
gnutls_hash_deinit(*ptr, NULL);
}
}
#define _cleanup_digest_ _cleanup_(free_gnutls_hash)
/*!
* Compute NSEC3 hash for given data and algorithm.
*
* \see RFC 5155
*
* \todo Input data should be converted to lowercase.
*/
static int nsec3_hash(gnutls_digest_algorithm_t algorithm, unsigned iterations,
const dnssec_binary_t *salt, const dnssec_binary_t *data,
dnssec_binary_t *hash)
{
assert(salt);
assert(data);
assert(hash);
int hash_size = gnutls_hash_get_len(algorithm);
if (hash_size <= 0) {
return DNSSEC_NSEC3_HASHING_ERROR;
}
int result = dnssec_binary_resize(hash, hash_size);
if (result != DNSSEC_EOK) {
return result;
}
_cleanup_digest_ gnutls_hash_hd_t digest = NULL;
result = gnutls_hash_init(&digest, algorithm);
if (result < 0) {
return DNSSEC_NSEC3_HASHING_ERROR;
}
const uint8_t *in = data->data;
size_t in_size = data->size;
for (int i = 0; i <= iterations; i++) {
result = gnutls_hash(digest, in, in_size);
if (result < 0) {
return DNSSEC_NSEC3_HASHING_ERROR;
}
result = gnutls_hash(digest, salt->data, salt->size);
if (result < 0) {
return DNSSEC_NSEC3_HASHING_ERROR;
}
gnutls_hash_output(digest, hash->data);
in = hash->data;
in_size = hash->size;
}
return DNSSEC_EOK;
}
/*!
* Get GnuTLS digest algorithm from DNSSEC algorithm number.
*/
static gnutls_digest_algorithm_t algorithm_d2g(dnssec_nsec3_algorithm_t dnssec)
{
switch (dnssec) {
case DNSSEC_NSEC3_ALGORITHM_SHA1: return GNUTLS_DIG_SHA1;
default: return GNUTLS_DIG_UNKNOWN;
}
}
/* -- public API ----------------------------------------------------------- */
/*!
* Free NSEC3 parameters.
*/
void dnssec_nsec3_params_free(dnssec_nsec3_params_t *params)
{
if (!params) {
return;
}
dnssec_binary_free(&params->salt);
clear_struct(&params);
}
/*!
* Parse NSEC3 parameters from NSEC3PARAM RDATA.
*
* \see RFC 5155 (section 4.2)
*/
int dnssec_nsec3_params_from_rdata(dnssec_nsec3_params_t *params,
const dnssec_binary_t *rdata)
{
if (!params || !rdata || !rdata->data) {
return DNSSEC_EINVAL;
}
dnssec_nsec3_params_t new_params = { 0 };
wire_ctx_t wire;
wire_init_binary(&wire, rdata);
if (wire_available(&wire) < 5) {
return DNSSEC_MALFORMED_DATA;
}
new_params.algorithm = wire_read_u8(&wire);
new_params.flags = wire_read_u8(&wire);
new_params.iterations = wire_read_u16(&wire);
new_params.salt.size = wire_read_u8(&wire);
if (wire_available(&wire) != new_params.salt.size) {
return DNSSEC_MALFORMED_DATA;
}
new_params.salt.data = malloc(new_params.salt.size);
if (new_params.salt.data == NULL) {
return DNSSEC_ENOMEM;
}
wire_read_binary(&wire, &new_params.salt);
assert(wire_tell(&wire) == rdata->size);
*params = new_params;
return DNSSEC_EOK;
}
/*!
* Compute NSEC3 hash for given data.
*/
int dnssec_nsec3_hash(const dnssec_binary_t *data,
const dnssec_nsec3_params_t *params,
dnssec_binary_t *hash)
{
if (!data || !params || !hash) {
return DNSSEC_EINVAL;
}
gnutls_digest_algorithm_t algorithm = algorithm_d2g(params->algorithm);
if (algorithm == GNUTLS_DIG_UNKNOWN) {
return DNSSEC_INVALID_NSEC3_ALGORITHM;
}
return nsec3_hash(algorithm, params->iterations, &params->salt, data, hash);
}
/*!
* Get length of raw NSEC3 hash for a given algorithm.
*/
size_t dnssec_nsec3_hash_length(dnssec_nsec3_algorithm_t dnssec)
{
gnutls_digest_algorithm_t gnutls = algorithm_d2g(dnssec);
if (gnutls == GNUTLS_DIG_UNKNOWN) {
return 0;
}
return gnutls_hash_get_len(gnutls);
}
#pragma once
#include "binary.h"
/*!
* DNSSEC NSEC3 algorithm numbers.
*/
typedef enum dnssec_nsec_algorithm {
DNSSEC_NSEC3_ALGORITHM_UNKNOWN = 0,
DNSSEC_NSEC3_ALGORITHM_SHA1 = 1,
} dnssec_nsec3_algorithm_t;
/*!
* DNSSEC NSEC3 parameters.
*/
typedef struct dnssec_nsec3_params {
dnssec_nsec3_algorithm_t algorithm;
uint8_t flags;
uint16_t iterations;
dnssec_binary_t salt;
} dnssec_nsec3_params_t;
/*!
* Free NSEC3 parameters.
*/
void dnssec_nsec3_params_free(dnssec_nsec3_params_t *params);
/*!
* Parse NSEC3 parameters from NSEC3PARAM RDATA.
*
* \param params Output parameters.
* \param rdata NSEC3PARAM RDATA.
*
* \return Error code, KNOT_EOK if successful.
*/
int dnssec_nsec3_params_from_rdata(dnssec_nsec3_params_t *params,
const dnssec_binary_t *rdata);
/*!
* Compute NSEC3 hash for given data.
*
* \todo Input data must be converted to lowercase!
*
* \param[in] data Data to be hashed (usually domain name).
* \param[in] params NSEC3 parameters.
* \param[out] hash Computed hash (will be allocated or resized).
*
* \return Error code, KNOT_EOK if successful.
*/
int dnssec_nsec3_hash(const dnssec_binary_t *data,
const dnssec_nsec3_params_t *params,
dnssec_binary_t *hash);
/*!
* Get length of raw NSEC3 hash for a given algorithm.
*
* \param algorithm NSEC3 algorithm number.
*
* \return Length of raw NSEC3 hash, zero on error.
*/
size_t dnssec_nsec3_hash_length(dnssec_nsec3_algorithm_t algorithm);
......@@ -12,6 +12,7 @@ check_PROGRAMS = \
key_keytag \
key_pubkey \
nsec_bitmap \
nsec_hash \
sign \
sign_der \
wire
......
#include <string.h>
#include <tap/basic.h>
#include "crypto.h"
#include "error.h"
#include "nsec/hash.h"
void test_length(void)
{
ok(dnssec_nsec3_hash_length(DNSSEC_NSEC3_ALGORITHM_SHA1) == 20,
"dnssec_nsec3_hash_length() for SHA1");
}
void test_parsing(void)
{
const dnssec_binary_t rdata = { .size = 9, .data = (uint8_t []) {
0x01, // algorithm
0x00, // flags
0x00, 0x0a, // iterations
0x04, // salt length
'a', 'b', 'c', 'd' // salt
}};
dnssec_nsec3_params_t params = { 0 };
int result = dnssec_nsec3_params_from_rdata(&params, &rdata);
ok(result == DNSSEC_EOK, "dnssec_nsec3_params_from_rdata()");
ok(params.algorithm == 1, "algorithm");
ok(params.flags == 0, "flags");
ok(params.iterations == 10, "iterations");
ok(params.salt.size == 4, "salt length");
ok(params.salt.data != NULL && memcmp(params.salt.data, "abcd", 4) == 0,
"salt content");
dnssec_nsec3_params_free(&params);
ok(params.salt.data == NULL, "dnssec_nsec3_params_free()");
}
void test_hashing(void)
{
const dnssec_binary_t dname = {
.size = 13,
.data = (uint8_t *) "\x08""knot-dns""\x02""cz"
};
const dnssec_nsec3_params_t params = {
.algorithm = DNSSEC_NSEC3_ALGORITHM_SHA1,
.flags = 0,
.iterations = 7,
.salt = { .size = 14, .data = (uint8_t *) "happywithnsec3" }
};
const dnssec_binary_t expected = { .size = 20, .data = (uint8_t []) {
0x72, 0x40, 0x55, 0x83, 0x92, 0x93, 0x95, 0x28, 0xee, 0xa2,
0xcc, 0xe1, 0x13, 0xbe, 0xcd, 0x41, 0xee, 0x8a, 0x71, 0xfd
}};
dnssec_binary_t hash = { 0 };
int result = dnssec_nsec3_hash(&dname, &params, &hash);
ok(result == DNSSEC_EOK, "dnssec_nsec3_hash()");
ok(hash.size == expected.size && hash.data != NULL &&
memcmp(hash.data, expected.data, expected.size) == 0,
"valid hash");
dnssec_binary_free(&hash);
}
int main(void)
{
plan_lazy();
dnssec_crypto_init();
test_length();
test_parsing();
test_hashing();
dnssec_crypto_cleanup();
return 0;
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment