hickory_proto/dnssec/rdata/sig.rs
1// Copyright 2015-2023 Benjamin Fry <benjaminfry@me.com>
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// https://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// https://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! signature record for signing queries, updates, and responses
9use alloc::vec::Vec;
10use core::fmt;
11
12#[cfg(feature = "serde")]
13use serde::{Deserialize, Serialize};
14use time::OffsetDateTime;
15
16use super::DNSSECRData;
17use crate::{
18 dnssec::{Algorithm, DnssecSigner},
19 error::{ProtoError, ProtoResult},
20 rr::{Name, RData, RecordData, RecordDataDecodable, RecordSet, RecordType, SerialNumber},
21 serialize::binary::{
22 BinDecodable, BinDecoder, BinEncodable, BinEncoder, DecodeError, RDataEncoding, Restrict,
23 RestrictedMath,
24 },
25};
26
27/// [RFC 2535](https://tools.ietf.org/html/rfc2535#section-4), Domain Name System Security Extensions, March 1999
28///
29/// NOTE: RFC 2535 was obsoleted with 4034+, with the exception of the
30/// usage for UPDATE, which is what this implementation is for.
31///
32/// ```text
33/// 4.1 SIG RDATA Format
34///
35/// The RDATA portion of a SIG RR is as shown below. The integrity of
36/// the RDATA information is protected by the signature field.
37///
38/// 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
39/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
40/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
41/// | type covered | algorithm | labels |
42/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
43/// | original TTL |
44/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
45/// | signature expiration |
46/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
47/// | signature inception |
48/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
49/// | key tag | |
50/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ signer's name +
51/// | /
52/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-/
53/// / /
54/// / signature /
55/// / /
56/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
57///
58/// ```
59/// [RFC 2931](https://tools.ietf.org/html/rfc2931), DNS Request and Transaction Signatures, September 2000
60///
61/// NOTE: 2931 updates SIG0 to clarify certain particulars...
62///
63/// ```text
64/// RFC 2931 DNS SIG(0) September 2000
65///
66/// 3. The SIG(0) Resource Record
67///
68/// The structure of and type number of SIG resource records (RRs) is
69/// given in [RFC 2535] Section 4.1. However all of Section 4.1.8.1 and
70/// the parts of Sections 4.2 and 4.3 related to SIG(0) should be
71/// considered replaced by the material below. Any conflict between [RFC
72/// 2535] and this document concerning SIG(0) RRs should be resolved in
73/// favor of this document.
74///
75/// For all transaction SIG(0)s, the signer field MUST be a name of the
76/// originating host and there MUST be a KEY RR at that name with the
77/// public key corresponding to the private key used to calculate the
78/// signature. (The host domain name used may be the inverse IP address
79/// mapping name for an IP address of the host if the relevant KEY is
80/// stored there.)
81///
82/// For all SIG(0) RRs, the owner name, class, TTL, and original TTL, are
83/// meaningless. The TTL fields SHOULD be zero and the CLASS field
84/// SHOULD be ANY. To conserve space, the owner name SHOULD be root (a
85/// single zero octet). When SIG(0) authentication on a response is
86/// desired, that SIG RR MUST be considered the highest priority of any
87/// additional information for inclusion in the response. If the SIG(0)
88/// RR cannot be added without causing the message to be truncated, the
89/// server MUST alter the response so that a SIG(0) can be included.
90/// This response consists of only the question and a SIG(0) record, and
91/// has the TC bit set and RCODE 0 (NOERROR). The client should at this
92/// point retry the request using TCP.
93///
94/// 3.1 Calculating Request and Transaction SIGs
95///
96/// A DNS request may be optionally signed by including one SIG(0)s at
97/// the end of the query additional information section. Such a SIG is
98/// identified by having a "type covered" field of zero. It signs the
99/// preceding DNS request message including DNS header but not including
100/// the UDP/IP header and before the request RR counts have been adjusted
101/// for the inclusions of the request SIG(0).
102///
103/// It is calculated by using a "data" (see [RFC 2535], Section 4.1.8) of
104/// (1) the SIG's RDATA section entirely omitting (not just zeroing) the
105/// signature subfield itself, (2) the DNS query messages, including DNS
106/// header, but not the UDP/IP header and before the reply RR counts have
107/// been adjusted for the inclusion of the SIG(0). That is
108///
109/// data = RDATA | request - SIG(0)
110///
111/// where "|" is concatenation and RDATA is the RDATA of the SIG(0) being
112/// calculated less the signature itself.
113///
114/// Similarly, a SIG(0) can be used to secure a response and the request
115/// that produced it. Such transaction signatures are calculated by
116/// using a "data" of (1) the SIG's RDATA section omitting the signature
117/// itself, (2) the entire DNS query message that produced this response,
118/// including the query's DNS header but not its UDP/IP header, and (3)
119/// the entire DNS response message, including DNS header but not the
120/// UDP/IP header and before the response RR counts have been adjusted
121/// for the inclusion of the SIG(0).
122///
123/// That is
124///
125/// data = RDATA | full query | response - SIG(0)
126///
127/// where "|" is concatenation and RDATA is the RDATA of the SIG(0) being
128/// calculated less the signature itself.
129///
130/// Verification of a response SIG(0) (which is signed by the server host
131/// key, not the zone key) by the requesting resolver shows that the
132/// query and response were not tampered with in transit, that the
133/// response corresponds to the intended query, and that the response
134/// comes from the queried server.
135///
136/// In the case of a DNS message via TCP, a SIG(0) on the first data
137/// packet is calculated with "data" as above and for each subsequent
138/// packet, it is calculated as follows:
139///
140/// data = RDATA | DNS payload - SIG(0) | previous packet
141///
142/// where "|" is concatenations, RDATA is as above, and previous packet
143/// is the previous DNS payload including DNS header and the SIG(0) but
144/// not the TCP/IP header. Support of SIG(0) for TCP is OPTIONAL. As an
145/// alternative, TSIG may be used after, if necessary, setting up a key
146/// with TKEY [RFC 2930].
147///
148/// Except where needed to authenticate an update, TKEY, or similar
149/// privileged request, servers are not required to check a request
150/// SIG(0).
151///
152/// Note: requests and responses can either have a single TSIG or one
153/// SIG(0) but not both a TSIG and a SIG(0).
154///
155/// 3.2 Processing Responses and SIG(0) RRs
156///
157/// If a SIG RR is at the end of the additional information section of a
158/// response and has a type covered of zero, it is a transaction
159/// signature covering the response and the query that produced the
160/// response. For TKEY responses, it MUST be checked and the message
161/// rejected if the checks fail unless otherwise specified for the TKEY
162/// mode in use. For all other responses, it MAY be checked and the
163/// message rejected if the checks fail.
164///
165/// If a response's SIG(0) check succeed, such a transaction
166/// authentication SIG does NOT directly authenticate the validity any
167/// data-RRs in the message. However, it authenticates that they were
168/// sent by the queried server and have not been diddled. (Only a proper
169/// SIG(0) RR signed by the zone or a key tracing its authority to the
170/// zone or to static resolver configuration can directly authenticate
171///
172/// data-RRs, depending on resolver policy.) If a resolver or server does
173/// not implement transaction and/or request SIGs, it MUST ignore them
174/// without error where they are optional and treat them as failing where
175/// they are required.
176///
177/// 3.3 SIG(0) Lifetime and Expiration
178///
179/// The inception and expiration times in SIG(0)s are for the purpose of
180/// resisting replay attacks. They should be set to form a time bracket
181/// such that messages outside that bracket can be ignored. In IP
182/// networks, this time bracket should not normally extend further than 5
183/// minutes into the past and 5 minutes into the future.
184/// ```
185#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
186#[derive(Debug, PartialEq, Eq, Hash, Clone)]
187pub struct SIG {
188 pub(crate) input: SigInput,
189 pub(crate) sig: Vec<u8>,
190}
191
192impl SIG {
193 /// [RFC 2535](https://tools.ietf.org/html/rfc2535#section-4.1.8), Domain Name System Security Extensions, March 1999
194 ///
195 /// ```text
196 /// 4.1.8 Signature Field
197 ///
198 /// The actual signature portion of the SIG RR binds the other RDATA
199 /// fields to the RRset of the "type covered" RRs with that owner name
200 /// and class. This covered RRset is thereby authenticated. To
201 /// accomplish this, a data sequence is constructed as follows:
202 ///
203 /// data = RDATA | RR(s)...
204 ///
205 /// where "|" is concatenation,
206 ///
207 /// RDATA is the wire format of all the RDATA fields in the SIG RR itself
208 /// (including the canonical form of the signer's name) before but not
209 /// including the signature, and
210 ///
211 /// RR(s) is the RRset of the RR(s) of the type covered with the same
212 /// owner name and class as the SIG RR in canonical form and order as
213 /// defined in Section 8.
214 ///
215 /// How this data sequence is processed into the signature is algorithm
216 /// dependent. These algorithm dependent formats and procedures are
217 /// described in separate documents (Section 3.2).
218 ///
219 /// SIGs SHOULD NOT be included in a zone for any "meta-type" such as
220 /// ANY, AXFR, etc. (but see section 5.6.2 with regard to IXFR).
221 /// ```
222 pub fn sig(&self) -> &[u8] {
223 &self.sig
224 }
225
226 /// The input data used to create the signature.
227 pub fn input(&self) -> &SigInput {
228 &self.input
229 }
230}
231
232impl BinEncodable for SIG {
233 /// [RFC 4034](https://tools.ietf.org/html/rfc4034#section-6), DNSSEC Resource Records, March 2005
234 ///
235 /// This is accurate for all currently known name records.
236 ///
237 /// ```text
238 /// 6.2. Canonical RR Form
239 ///
240 /// For the purposes of DNS security, the canonical form of an RR is the
241 /// wire format of the RR where:
242 ///
243 /// ...
244 ///
245 /// 3. if the type of the RR is NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR,
246 /// HINFO, MINFO, MX, HINFO, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX,
247 /// SRV, DNAME, A6, RRSIG, or (rfc6840 removes NSEC), all uppercase
248 /// US-ASCII letters in the DNS names contained within the RDATA are replaced
249 /// by the corresponding lowercase US-ASCII letters;
250 /// ```
251 fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
252 let mut encoder = encoder.with_rdata_behavior(RDataEncoding::Canonical);
253 self.input.emit(&mut encoder)?;
254 encoder.emit_vec(&self.sig)?;
255 Ok(())
256 }
257}
258
259impl<'r> RecordDataDecodable<'r> for SIG {
260 fn read_data(decoder: &mut BinDecoder<'r>, length: Restrict<u16>) -> Result<Self, DecodeError> {
261 let start_idx = decoder.index();
262
263 // TODO should we verify here? or elsewhere...
264 let type_covered = RecordType::read(decoder)?;
265 let algorithm = Algorithm::read(decoder)?;
266 let num_labels = decoder.read_u8()?.unverified(/*technically valid as any u8*/);
267 let original_ttl = decoder.read_u32()?.unverified(/*valid as any u32*/);
268 let sig_expiration = SerialNumber(
269 decoder.read_u32()?.unverified(/*valid as any u32, in practice should be in the future*/),
270 );
271 let sig_inception = SerialNumber(
272 decoder.read_u32()?.unverified(/*valid as any u32, in practice should be before expiration*/),
273 );
274 let key_tag = decoder.read_u16()?.unverified(/*valid as any u16*/);
275 let signer_name = Name::read(decoder)?;
276
277 let input = SigInput {
278 type_covered,
279 algorithm,
280 num_labels,
281 original_ttl,
282 sig_expiration,
283 sig_inception,
284 key_tag,
285 signer_name,
286 };
287
288 // read the signature, this will vary buy key size
289 let sig_len = length
290 .map(|u| u as usize)
291 .checked_sub(decoder.index() - start_idx)
292 .map_err(|len| DecodeError::IncorrectRDataLengthRead { read: decoder.index() - start_idx, len })?
293 .unverified(/*used only as length safely*/);
294 let sig = decoder
295 .read_vec(sig_len)?
296 .unverified(/*will fail in usage if invalid*/);
297 Ok(Self { input, sig })
298 }
299}
300
301impl RecordData for SIG {
302 fn try_borrow(data: &RData) -> Option<&Self> {
303 match data {
304 RData::DNSSEC(DNSSECRData::SIG(csync)) => Some(csync),
305 _ => None,
306 }
307 }
308
309 fn record_type(&self) -> RecordType {
310 RecordType::SIG
311 }
312
313 fn into_rdata(self) -> RData {
314 RData::DNSSEC(DNSSECRData::SIG(self))
315 }
316}
317
318/// [RFC 2535](https://tools.ietf.org/html/rfc2535#section-7.2), Domain Name System Security Extensions, March 1999
319///
320/// ```text
321/// 7.2 Presentation of SIG RRs
322///
323/// A data SIG RR may be represented as a single logical line in a zone
324/// data file [RFC 1033] but there are some special considerations as
325/// described below. (It does not make sense to include a transaction or
326/// request authenticating SIG RR in a file as they are a transient
327/// authentication that covers data including an ephemeral transaction
328/// number and so must be calculated in real time.)
329///
330/// There is no particular problem with the signer, covered type, and
331/// times. The time fields appears in the form YYYYMMDDHHMMSS where YYYY
332/// is the year, the first MM is the month number (01-12), DD is the day
333/// of the month (01-31), HH is the hour in 24 hours notation (00-23),
334/// the second MM is the minute (00-59), and SS is the second (00-59).
335///
336/// The original TTL field appears as an unsigned integer.
337///
338/// If the original TTL, which applies to the type signed, is the same as
339/// the TTL of the SIG RR itself, it may be omitted. The date field
340/// which follows it is larger than the maximum possible TTL so there is
341/// no ambiguity.
342///
343/// The "labels" field appears as an unsigned integer.
344///
345/// The key tag appears as an unsigned number.
346///
347/// However, the signature itself can be very long. It is the last data
348/// field and is represented in base 64 (see Appendix A) and may be
349/// divided up into any number of white space separated substrings, down
350/// to single base 64 digits, which are concatenated to obtain the full
351/// signature. These substrings can be split between lines using the
352/// standard parenthesis.
353///
354/// foo.nil. SIG NXT 1 2 ( ;type-cov=NXT, alg=1, labels=2
355/// 19970102030405 ;signature expiration
356/// 19961211100908 ;signature inception
357/// 2143 ;key identifier
358/// foo.nil. ;signer
359/// AIYADP8d3zYNyQwW2EM4wXVFdslEJcUx/fxkfBeH1El4ixPFhpfHFElxbvKoWmvjDTCm
360/// fiYy2X+8XpFjwICHc398kzWsTMKlxovpz2FnCTM= ;signature (640 bits)
361/// ```
362impl fmt::Display for SIG {
363 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
364 write!(
365 f,
366 "{ty_covered} {alg} {num_labels} {original_ttl} {expire} {inception} {tag} {signer} {sig}",
367 ty_covered = self.input.type_covered,
368 alg = self.input.algorithm,
369 num_labels = self.input.num_labels,
370 original_ttl = self.input.original_ttl,
371 expire = self.input.sig_expiration.0,
372 inception = self.input.sig_inception.0,
373 tag = self.input.key_tag,
374 signer = self.input.signer_name,
375 sig = data_encoding::BASE64.encode(&self.sig)
376 )
377 }
378}
379
380/// Input for a SIG or RRSIG record signature.
381#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
382#[derive(Debug, PartialEq, Eq, Hash, Clone)]
383pub struct SigInput {
384 /// `RecordType` which this signature covers, should be NULL for SIG(0).
385 pub type_covered: RecordType,
386 /// `Algorithm` used to generate the `signature`.
387 pub algorithm: Algorithm,
388 /// Number of labels in the name, should be less 1 for *.name labels, see
389 /// `Name::num_labels()`.
390 pub num_labels: u8,
391 /// * TTL for the RRSet stored in the zone, should be 0 for SIG(0).
392 pub original_ttl: u32,
393 /// Timestamp at which this signature is no longer valid
394 ///
395 /// Very important to keep this low, < +5 minutes to limit replay attacks.
396 pub sig_expiration: SerialNumber,
397 /// * Timestamp when this signature was generated.
398 pub sig_inception: SerialNumber,
399 /// * See the key_tag generation in `rr::dnssec::Signer::key_tag()`.
400 pub key_tag: u16,
401 /// * Domain name of the server which was used to generate the signature.
402 pub signer_name: Name,
403}
404
405impl SigInput {
406 /// Create a new `SigInput` from the given input parameters.
407 pub fn from_rrset(
408 rr_set: &RecordSet,
409 expiration: OffsetDateTime,
410 inception: OffsetDateTime,
411 signer: &DnssecSigner,
412 ) -> Result<Self, ProtoError> {
413 Ok(Self {
414 type_covered: rr_set.record_type(),
415 algorithm: signer.key().algorithm(),
416 num_labels: rr_set.name().num_labels(),
417 original_ttl: rr_set.ttl(),
418 sig_expiration: SerialNumber(expiration.unix_timestamp() as u32),
419 sig_inception: SerialNumber(inception.unix_timestamp() as u32),
420 key_tag: signer.calculate_key_tag()?,
421 signer_name: signer.signer_name().clone(),
422 })
423 }
424}
425
426impl BinEncodable for SigInput {
427 fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
428 let mut encoder = encoder.with_rdata_behavior(RDataEncoding::Canonical);
429 // specifically for outputting the RData for an RRSIG, with signer_name in canonical form
430 self.type_covered.emit(&mut encoder)?;
431 self.algorithm.emit(&mut encoder)?;
432 encoder.emit(self.num_labels)?;
433 encoder.emit_u32(self.original_ttl)?;
434 encoder.emit_u32(self.sig_expiration.0)?;
435 encoder.emit_u32(self.sig_inception.0)?;
436 encoder.emit_u16(self.key_tag)?;
437 self.signer_name.emit(&mut encoder)?;
438 Ok(())
439 }
440}
441
442#[cfg(test)]
443mod tests {
444 #![allow(clippy::dbg_macro, clippy::print_stdout)]
445
446 use std::println;
447
448 use super::*;
449
450 #[test]
451 fn test() {
452 use core::str::FromStr;
453
454 let input = SigInput {
455 type_covered: RecordType::NULL,
456 algorithm: Algorithm::RSASHA256,
457 num_labels: 0,
458 original_ttl: 0,
459 sig_expiration: SerialNumber(2),
460 sig_inception: SerialNumber(1),
461 key_tag: 5,
462 signer_name: Name::from_str("www.example.com.").unwrap(),
463 };
464 let rdata = SIG {
465 input,
466 sig: vec![
467 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
468 23, 24, 25, 26, 27, 28, 29, 29, 31,
469 ], // 32 bytes for SHA256
470 };
471
472 let mut bytes = Vec::new();
473 let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
474 assert!(rdata.emit(&mut encoder).is_ok());
475 let bytes = encoder.into_bytes();
476
477 println!("bytes: {bytes:?}");
478
479 let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
480 let restrict = Restrict::new(bytes.len() as u16);
481 let read_rdata = SIG::read_data(&mut decoder, restrict).expect("Decoding error");
482 assert_eq!(rdata, read_rdata);
483 }
484}