1 /*  Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2 
3     This program is free software: you can redistribute it and/or modify
4     it under the terms of the GNU General Public License as published by
5     the Free Software Foundation, either version 3 of the License, or
6     (at your option) any later version.
7 
8     This program is distributed in the hope that it will be useful,
9     but WITHOUT ANY WARRANTY; without even the implied warranty of
10     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11     GNU General Public License for more details.
12 
13     You should have received a copy of the GNU General Public License
14     along with this program.  If not, see <https://www.gnu.org/licenses/>.
15  */
16 
17 #include <assert.h>
18 #include <gnutls/gnutls.h>
19 #include <gnutls/crypto.h>
20 #include <string.h>
21 
22 #include "libdnssec/error.h"
23 #include "libdnssec/nsec.h"
24 #include "libdnssec/shared/shared.h"
25 
26 /*!
27  * Compute NSEC3 hash for given data and algorithm.
28  *
29  * \see RFC 5155
30  *
31  * \todo Input data should be converted to lowercase.
32  */
nsec3_hash(gnutls_digest_algorithm_t algorithm,int iterations,const dnssec_binary_t * salt,const dnssec_binary_t * data,dnssec_binary_t * hash)33 static int nsec3_hash(gnutls_digest_algorithm_t algorithm, int iterations,
34 		      const dnssec_binary_t *salt, const dnssec_binary_t *data,
35 		      dnssec_binary_t *hash)
36 {
37 	assert(salt);
38 	assert(data);
39 	assert(hash);
40 
41 	int hash_size = gnutls_hash_get_len(algorithm);
42 	if (hash_size <= 0) {
43 		return DNSSEC_NSEC3_HASHING_ERROR;
44 	}
45 
46 	int result = dnssec_binary_resize(hash, hash_size);
47 	if (result != DNSSEC_EOK) {
48 		return result;
49 	}
50 
51 	_cleanup_hash_ gnutls_hash_hd_t digest = NULL;
52 	result = gnutls_hash_init(&digest, algorithm);
53 	if (result < 0) {
54 		return DNSSEC_NSEC3_HASHING_ERROR;
55 	}
56 
57 	const uint8_t *in = data->data;
58 	size_t in_size = data->size;
59 
60 	for (int i = 0; i <= iterations; i++) {
61 		result = gnutls_hash(digest, in, in_size);
62 		if (result < 0) {
63 			return DNSSEC_NSEC3_HASHING_ERROR;
64 		}
65 
66 		result = gnutls_hash(digest, salt->data, salt->size);
67 		if (result < 0) {
68 			return DNSSEC_NSEC3_HASHING_ERROR;
69 		}
70 
71 		gnutls_hash_output(digest, hash->data);
72 
73 		in = hash->data;
74 		in_size = hash->size;
75 	}
76 
77 	return DNSSEC_EOK;
78 }
79 
80 /*!
81  * Get GnuTLS digest algorithm from DNSSEC algorithm number.
82  */
algorithm_d2g(dnssec_nsec3_algorithm_t dnssec)83 static gnutls_digest_algorithm_t algorithm_d2g(dnssec_nsec3_algorithm_t dnssec)
84 {
85 	switch (dnssec) {
86 	case DNSSEC_NSEC3_ALGORITHM_SHA1: return GNUTLS_DIG_SHA1;
87 	default:                          return GNUTLS_DIG_UNKNOWN;
88 	}
89 }
90 
91 /* -- public API ----------------------------------------------------------- */
92 
93 /*!
94  * Compute NSEC3 hash for given data.
95  */
96 _public_
dnssec_nsec3_hash(const dnssec_binary_t * data,const dnssec_nsec3_params_t * params,dnssec_binary_t * hash)97 int dnssec_nsec3_hash(const dnssec_binary_t *data,
98 		      const dnssec_nsec3_params_t *params,
99 		      dnssec_binary_t *hash)
100 {
101 	if (!data || !params || !hash) {
102 		return DNSSEC_EINVAL;
103 	}
104 
105 	gnutls_digest_algorithm_t algorithm = algorithm_d2g(params->algorithm);
106 	if (algorithm == GNUTLS_DIG_UNKNOWN) {
107 		return DNSSEC_INVALID_NSEC3_ALGORITHM;
108 	}
109 
110 	return nsec3_hash(algorithm, params->iterations, &params->salt, data, hash);
111 }
112 
113 /*!
114  * Get length of raw NSEC3 hash for a given algorithm.
115  */
116 _public_
dnssec_nsec3_hash_length(dnssec_nsec3_algorithm_t algorithm)117 size_t dnssec_nsec3_hash_length(dnssec_nsec3_algorithm_t algorithm)
118 {
119 	gnutls_digest_algorithm_t gnutls = algorithm_d2g(algorithm);
120 	if (gnutls == GNUTLS_DIG_UNKNOWN) {
121 		return 0;
122 	}
123 
124 	return gnutls_hash_get_len(gnutls);
125 }
126