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, ¶ms->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