rrset-sign.c 11 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*  Copyright (C) 2013 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>

    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 3 of the License, or
    (at your option) any later version.

    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
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
21

22
#include "dnssec/error.h"
23
#include "dnssec/kasp.h"
24 25
#include "dnssec/key.h"
#include "dnssec/sign.h"
26
#include "knot/dnssec/rrset-sign.h"
27
#include "libknot/descriptor.h"
28
#include "libknot/dnssec/key.h"
29
#include "libknot/libknot.h"
30
#include "libknot/packet/rrset-wire.h"
31
#include "libknot/packet/wire.h"
32
#include "libknot/rrset.h"
33
#include "libknot/rrtype/rrsig.h"
34
#include "libknot/packet/rrset-wire.h"
35 36 37

#define RRSIG_RDATA_SIGNER_OFFSET 18

38
/*- Creating of RRSIGs -------------------------------------------------------*/
39 40

/*!
41
 * \brief Get size of RRSIG RDATA for a given key without signature.
42
 */
43
static size_t rrsig_rdata_header_size(const dnssec_key_t *key)
44
{
45 46 47
	if (!key) {
		return 0;
	}
48 49 50 51 52 53 54 55 56 57 58 59 60

	size_t size;

	// static part

	size = sizeof(uint16_t)		// type covered
	     + sizeof(uint8_t)		// algorithm
	     + sizeof(uint8_t)		// labels
	     + sizeof(uint32_t)		// original TTL
	     + sizeof(uint32_t)		// signature expiration
	     + sizeof(uint32_t)		// signature inception
	     + sizeof(uint16_t);	// key tag (footprint)

61 62
	assert(size == RRSIG_RDATA_SIGNER_OFFSET);

63 64
	// variable part

65 66 67
	const uint8_t *signer = dnssec_key_get_dname(key);
	assert(signer);
	size += knot_dname_size(signer);
68 69 70 71 72

	return size;
}

/*!
73
 * \brief Write RRSIG RDATA except signature.
74 75 76 77 78 79 80 81 82
 *
 * \note This can be also used for SIG(0) if proper parameters are supplied.
 *
 * \param rdata         Pointer to RDATA.
 * \param key           Key used for signing.
 * \param covered_type  Type of the covered RR.
 * \param owner_labels  Number of labels covered by the signature.
 * \param sig_incepted  Timestamp of signature inception.
 * \param sig_expires   Timestamp of signature expiration.
83
 */
84 85 86 87
static int rrsig_write_rdata(uint8_t *rdata, const dnssec_key_t *key,
                            uint16_t covered_type, uint8_t owner_labels,
                            uint32_t owner_ttl,  uint32_t sig_incepted,
                            uint32_t sig_expires)
88
{
89 90
	if (!rdata || !key || sig_incepted >= sig_expires) {
		return KNOT_EINVAL;
91 92
	}

93 94 95 96
	uint8_t algorithm = dnssec_key_get_algorithm(key);
	uint16_t keytag = dnssec_key_get_keytag(key);
	const uint8_t *signer = dnssec_key_get_dname(key);

97 98
	uint8_t *w = rdata;

99
	wire_write_u16(w, covered_type);	// type covered
100
	w += sizeof(uint16_t);
101
	*w = algorithm;				// algorithm
102 103 104
	w += sizeof(uint8_t);
	*w = owner_labels;			// labels
	w += sizeof(uint8_t);
Daniel Salzman's avatar
Daniel Salzman committed
105
	wire_write_u32(w, owner_ttl);		// original TTL
106
	w += sizeof(uint32_t);
Daniel Salzman's avatar
Daniel Salzman committed
107
	wire_write_u32(w, sig_expires);		// signature expiration
108
	w += sizeof(uint32_t);
109
	wire_write_u32(w, sig_incepted);	// signature inception
110
	w += sizeof(uint32_t);
111
	wire_write_u16(w, keytag);		// key fingerprint
112 113 114
	w += sizeof(uint16_t);

	assert(w == rdata + RRSIG_RDATA_SIGNER_OFFSET);
115
	assert(signer);
116
	memcpy(w, signer, knot_dname_size(signer)); // signer
117 118

	return KNOT_EOK;
119 120 121 122 123 124 125
}

/*- Computation of signatures ------------------------------------------------*/

/*!
 * \brief Add RRSIG RDATA without signature to signing context.
 *
126 127
 * Requires signer name in RDATA in canonical form.
 *
128 129 130 131 132
 * \param ctx   Signing context.
 * \param rdata Pointer to RRSIG RDATA.
 *
 * \return Error code, KNOT_EOK if successful.
 */
133
static int sign_ctx_add_self(dnssec_sign_ctx_t *ctx, const uint8_t *rdata)
134 135 136 137
{
	assert(ctx);
	assert(rdata);

138
	int result;
139

140
	// static header
141

142
	dnssec_binary_t header = { 0 };
143
	header.data = (uint8_t *)rdata;
144
	header.size = RRSIG_RDATA_SIGNER_OFFSET;
145

146 147
	result = dnssec_sign_add(ctx, &header);
	if (result != DNSSEC_EOK) {
148
		return result;
149
	}
150

151 152 153 154 155 156 157 158 159
	// signer name

	const uint8_t *rdata_signer = rdata + RRSIG_RDATA_SIGNER_OFFSET;
	dnssec_binary_t signer = { 0 };
	signer.data = knot_dname_copy(rdata_signer, NULL);
	signer.size = knot_dname_size(signer.data);

	result = dnssec_sign_add(ctx, &signer);
	free(signer.data);
160 161

	return result;
162 163 164 165 166
}

/*!
 * \brief Add covered RRs to signing context.
 *
167 168
 * Requires all DNAMEs in canonical form and all RRs ordered canonically.
 *
169 170 171 172 173
 * \param ctx      Signing context.
 * \param covered  Covered RRs.
 *
 * \return Error code, KNOT_EOK if successful.
 */
174
static int sign_ctx_add_records(dnssec_sign_ctx_t *ctx, const knot_rrset_t *covered)
175
{
176
	// huge block of rrsets can be optionally created
177
	uint8_t *rrwf = malloc(KNOT_WIRE_MAX_PKTSIZE);
178 179 180 181
	if (!rrwf) {
		return KNOT_ENOMEM;
	}

182
	int written = knot_rrset_to_wire(covered, rrwf, KNOT_WIRE_MAX_PKTSIZE, NULL);
183
	if (written < 0) {
184
		free(rrwf);
185
		return written;
186 187
	}

188
	dnssec_binary_t rrset_wire = { 0 };
189
	rrset_wire.size = written;
190
	rrset_wire.data = rrwf;
191
	int result = dnssec_sign_add(ctx, &rrset_wire);
192 193
	free(rrwf);

194
	return result;
195 196 197 198 199 200 201 202
}

/*!
 * \brief Add all data covered by signature into signing context.
 *
 * RFC 4034: The signature covers RRSIG RDATA field (excluding the signature)
 * and all matching RR records, which are ordered canonically.
 *
203 204
 * Requires all DNAMEs in canonical form and all RRs ordered canonically.
 *
205 206 207 208 209 210
 * \param ctx          Signing context.
 * \param rrsig_rdata  RRSIG RDATA with populated fields except signature.
 * \param covered      Covered RRs.
 *
 * \return Error code, KNOT_EOK if successful.
 */
211
static int sign_ctx_add_data(dnssec_sign_ctx_t *ctx,
212 213 214 215 216 217 218 219 220 221 222
                             const uint8_t *rrsig_rdata,
                             const knot_rrset_t *covered)
{
	int result = sign_ctx_add_self(ctx, rrsig_rdata);
	if (result != KNOT_EOK) {
		return result;
	}

	return sign_ctx_add_records(ctx, covered);
}

223
/*!
224
 * \brief Create RRSIG RDATA.
225 226
 *
 * \param[in]  rrsigs        RR set with RRSIGS.
227
 * \param[in]  ctx           DNSSEC signing context.
228 229 230 231 232 233 234
 * \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.
 *
 * \return Error code, KNOT_EOK if succesful.
 */
235 236 237 238
static int rrsigs_create_rdata(knot_rrset_t *rrsigs, dnssec_sign_ctx_t *ctx,
			       const knot_rrset_t *covered,
			       const dnssec_key_t *key,
			       uint32_t sig_incepted, uint32_t sig_expires)
239 240 241 242 243 244
{
	assert(rrsigs);
	assert(rrsigs->type == KNOT_RRTYPE_RRSIG);
	assert(!knot_rrset_empty(covered));
	assert(key);

245
	size_t header_size = rrsig_rdata_header_size(key);
246
	assert(header_size != 0);
247 248 249 250 251 252

	uint8_t owner_labels = knot_dname_labels(covered->owner, NULL);
	if (knot_dname_is_wildcard(covered->owner)) {
		owner_labels -= 1;
	}

253
	uint8_t header[header_size];
254
	const knot_rdata_t *covered_data = knot_rdataset_at(&covered->rrs, 0);
255 256 257
	int res = rrsig_write_rdata(header, key, covered->type, owner_labels,
	                            knot_rdata_ttl(covered_data),
	                            sig_incepted, sig_expires);
258 259
	assert(res == KNOT_EOK);

260
	res = dnssec_sign_init(ctx);
261 262 263 264
	if (res != KNOT_EOK) {
		return res;
	}

265
	res = sign_ctx_add_data(ctx, header, covered);
266 267 268 269
	if (res != KNOT_EOK) {
		return res;
	}

270 271 272
	dnssec_binary_t signature = { 0 };
	res = dnssec_sign_write(ctx, &signature);
	if (res != DNSSEC_EOK) {
273
		return res;
274
	}
275 276 277 278 279 280 281 282
	assert(signature.size > 0);

	size_t rrsig_size = header_size + signature.size;
	uint8_t rrsig[rrsig_size];
	memcpy(rrsig, header, header_size);
	memcpy(rrsig + header_size, signature.data, signature.size);

	dnssec_binary_free(&signature);
283

284
	return knot_rrset_add_rdata(rrsigs, rrsig, rrsig_size,
285
	                            knot_rdata_ttl(covered_data), NULL);
286 287
}

288
int knot_sign_rrset(knot_rrset_t *rrsigs, const knot_rrset_t *covered,
289 290
               const dnssec_key_t *key, dnssec_sign_ctx_t *sign_ctx,
               const kdnssec_ctx_t *dnssec_ctx)
291
{
292
	if (knot_rrset_empty(covered) || !key || !sign_ctx || !dnssec_ctx ||
293
	    rrsigs->type != KNOT_RRTYPE_RRSIG ||
294
	    !knot_dname_is_equal(rrsigs->owner, covered->owner)
295 296 297 298
	) {
		return KNOT_EINVAL;
	}

299 300
	uint32_t sig_incept = dnssec_ctx->now;
	uint32_t sig_expire = sig_incept + dnssec_ctx->policy->rrsig_lifetime;
301

302 303
	return rrsigs_create_rdata(rrsigs, sign_ctx, covered, key, sig_incept,
	                           sig_expire);
304 305
}

306
int knot_synth_rrsig(uint16_t type, const knot_rdataset_t *rrsig_rrs,
307
                knot_rdataset_t *out_sig, mm_ctx_t *mm)
308 309 310 311 312 313 314 315 316 317
{
	if (rrsig_rrs == NULL) {
		return KNOT_ENOENT;
	}

	if (out_sig == NULL || out_sig->rr_count > 0) {
		return KNOT_EINVAL;
	}

	for (int i = 0; i < rrsig_rrs->rr_count; ++i) {
318
		if (type == knot_rrsig_type_covered(rrsig_rrs, i)) {
319 320
			const knot_rdata_t *rr_to_copy = knot_rdataset_at(rrsig_rrs, i);
			int ret = knot_rdataset_add(out_sig, rr_to_copy, mm);
321
			if (ret != KNOT_EOK) {
322
				knot_rdataset_clear(out_sig, mm);
323 324 325 326 327 328 329 330
				return ret;
			}
		}
	}

	return out_sig->rr_count > 0 ? KNOT_EOK : KNOT_ENOENT;
}

331 332 333 334 335 336 337 338 339 340 341 342
/*- Verification of signatures -----------------------------------------------*/

/*!
 * \brief Check if the signature is expired.
 *
 * \param rrsigs  RR set with RRSIGs.
 * \param pos     Number of RR in the RR set.
 * \param policy  DNSSEC policy.
 *
 * \return Signature is expired or should be replaced soon.
 */
static bool is_expired_signature(const knot_rrset_t *rrsigs, size_t pos,
343
                                 uint32_t now, uint32_t refresh_before)
344
{
345
	assert(!knot_rrset_empty(rrsigs));
346 347
	assert(rrsigs->type == KNOT_RRTYPE_RRSIG);

348 349
	uint32_t expire_at = knot_rrsig_sig_expiration(&rrsigs->rrs, pos);
	uint32_t expire_in = expire_at > now ? expire_at - now : 0;
350

351
	return expire_in <= refresh_before;
352 353
}

354 355 356 357 358
int knot_check_signature(const knot_rrset_t *covered,
                    const knot_rrset_t *rrsigs, size_t pos,
                    const dnssec_key_t *key,
                    dnssec_sign_ctx_t *sign_ctx,
                    const kdnssec_ctx_t *dnssec_ctx)
359
{
360 361
	if (knot_rrset_empty(covered) || knot_rrset_empty(rrsigs) || !key ||
	    !sign_ctx || !dnssec_ctx) {
362 363 364
		return KNOT_EINVAL;
	}

365 366
	if (is_expired_signature(rrsigs, pos, dnssec_ctx->now,
	                         dnssec_ctx->policy->rrsig_refresh_before)) {
367
		return DNSSEC_INVALID_SIGNATURE;
368 369 370 371
	}

	// identify fields in the signature being validated

372 373
	const knot_rdata_t *rr_data = knot_rdataset_at(&rrsigs->rrs, pos);
	uint8_t *rdata = knot_rdata_data(rr_data);
374
	if (!rdata) {
375 376 377
		return KNOT_EINVAL;
	}

378 379 380
	dnssec_binary_t signature = { 0 };
	knot_rrsig_signature(&rrsigs->rrs, pos, &signature.data, &signature.size);
	if (!signature.data) {
381 382 383 384 385
		return KNOT_EINVAL;
	}

	// perform the validation

386
	int result = dnssec_sign_init(sign_ctx);
387 388 389 390
	if (result != KNOT_EOK) {
		return result;
	}

391
	result = sign_ctx_add_data(sign_ctx, rdata, covered);
392 393 394 395
	if (result != KNOT_EOK) {
		return result;
	}

396
	return dnssec_sign_verify(sign_ctx, &signature);
397
}