1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/*
 * Copyright (C) 2016 Benjamin Fry <benjaminfry@me.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//! pointer record from parent zone to child zone for dnskey proof

use ::serialize::binary::*;
use ::error::*;
use ::rr::dnssec::{Algorithm, DigestType};

/// [RFC 4034, DNSSEC Resource Records, March 2005](https://tools.ietf.org/html/rfc4034#section-5)
///
/// ```text
/// 5.1.  DS RDATA Wire Format
///
///    The RDATA for a DS RR consists of a 2 octet Key Tag field, a 1 octet
///    Algorithm field, a 1 octet Digest Type field, and a Digest field.
///
///                         1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
///     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
///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
///    |           Key Tag             |  Algorithm    |  Digest Type  |
///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
///    /                                                               /
///    /                            Digest                             /
///    /                                                               /
///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
///
/// 5.2.  Processing of DS RRs When Validating Responses
///
///    The DS RR links the authentication chain across zone boundaries, so
///    the DS RR requires extra care in processing.  The DNSKEY RR referred
///    to in the DS RR MUST be a DNSSEC zone key.  The DNSKEY RR Flags MUST
///    have Flags bit 7 set.  If the DNSKEY flags do not indicate a DNSSEC
///    zone key, the DS RR (and the DNSKEY RR it references) MUST NOT be
///    used in the validation process.
///
/// 5.3.  The DS RR Presentation Format
///
///    The presentation format of the RDATA portion is as follows:
///
///    The Key Tag field MUST be represented as an unsigned decimal integer.
///
///    The Algorithm field MUST be represented either as an unsigned decimal
///    integer or as an algorithm mnemonic specified in Appendix A.1.
///
///    The Digest Type field MUST be represented as an unsigned decimal
///    integer.
///
///    The Digest MUST be represented as a sequence of case-insensitive
///    hexadecimal digits.  Whitespace is allowed within the hexadecimal
///    text.
/// ```
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct DS { key_tag: u16, algorithm: Algorithm, digest_type: DigestType, digest: Vec<u8> }

impl DS {
  pub fn new(key_tag: u16, algorithm: Algorithm, digest_type: DigestType, digest: Vec<u8>) -> DS {
    DS { key_tag: key_tag, algorithm: algorithm, digest_type: digest_type, digest: digest }
  }

  /// [RFC 4034, DNSSEC Resource Records, March 2005](https://tools.ietf.org/html/rfc4034#section-5.1.1)
  ///
  /// ```text
  /// 5.1.1.  The Key Tag Field
  ///
  ///    The Key Tag field lists the key tag of the DNSKEY RR referred to by
  ///    the DS record, in network byte order.
  ///
  ///    The Key Tag used by the DS RR is identical to the Key Tag used by
  ///    RRSIG RRs.  Appendix B describes how to compute a Key Tag.
  /// ```
  pub fn get_key_tag(&self) -> u16 { self.key_tag }

  /// [RFC 4034, DNSSEC Resource Records, March 2005](https://tools.ietf.org/html/rfc4034#section-5.1.1)
  ///
  /// ```text
  /// 5.1.2.  The Algorithm Field
  ///
  ///    The Algorithm field lists the algorithm number of the DNSKEY RR
  ///    referred to by the DS record.
  ///
  ///    The algorithm number used by the DS RR is identical to the algorithm
  ///    number used by RRSIG and DNSKEY RRs.  Appendix A.1 lists the
  ///    algorithm number types.
  /// ```
  pub fn get_algorithm(&self) -> &Algorithm { &self.algorithm }

  /// [RFC 4034, DNSSEC Resource Records, March 2005](https://tools.ietf.org/html/rfc4034#section-5.1.1)
  ///
  /// ```text
  /// 5.1.3.  The Digest Type Field
  ///
  ///    The DS RR refers to a DNSKEY RR by including a digest of that DNSKEY
  ///    RR.  The Digest Type field identifies the algorithm used to construct
  ///    the digest.  Appendix A.2 lists the possible digest algorithm types.
  /// ```
  pub fn get_digest_type(&self) -> DigestType { self.digest_type }

  /// [RFC 4034, DNSSEC Resource Records, March 2005](https://tools.ietf.org/html/rfc4034#section-5.1.1)
  ///
  /// ```text
  /// 5.1.4.  The Digest Field
  ///
  ///    The DS record refers to a DNSKEY RR by including a digest of that
  ///    DNSKEY RR.
  ///
  ///    The digest is calculated by concatenating the canonical form of the
  ///    fully qualified owner name of the DNSKEY RR with the DNSKEY RDATA,
  ///    and then applying the digest algorithm.
  ///
  ///      digest = digest_algorithm( DNSKEY owner name | DNSKEY RDATA);
  ///
  ///       "|" denotes concatenation
  ///
  ///      DNSKEY RDATA = Flags | Protocol | Algorithm | Public Key.
  ///
  ///    The size of the digest may vary depending on the digest algorithm and
  ///    DNSKEY RR size.  As of the time of this writing, the only defined
  ///    digest algorithm is SHA-1, which produces a 20 octet digest.
  /// ```
  pub fn get_digest(&self) -> &[u8] { &self.digest }
}

pub fn read(decoder: &mut BinDecoder, rdata_length: u16) -> DecodeResult<DS> {
  let start_idx = decoder.index();

  let key_tag: u16 = try!(decoder.read_u16());
  let algorithm: Algorithm = try!(Algorithm::read(decoder));
  let digest_type: DigestType = try!(DigestType::from_u8(try!(decoder.read_u8())));

  let left: usize = rdata_length as usize - (decoder.index() - start_idx);;
  let digest = try!(decoder.read_vec(left));

  Ok(DS::new(key_tag, algorithm, digest_type, digest))
}

pub fn emit(encoder: &mut BinEncoder, rdata: &DS) -> EncodeResult {
  try!(encoder.emit_u16(rdata.get_key_tag()));
  try!(rdata.get_algorithm().emit(encoder)); // always 3 for now
  try!(encoder.emit(rdata.get_digest_type().into()));
  try!(encoder.emit_vec(rdata.get_digest()));

  Ok(())
}

#[test]
pub fn test() {
  let rdata = DS::new(0xF00F, Algorithm::RSASHA256, DigestType::SHA256, vec![5,6,7,8]);

  let mut bytes = Vec::new();
  let mut encoder: BinEncoder = BinEncoder::new(&mut bytes);
  assert!(emit(&mut encoder, &rdata).is_ok());
  let bytes = encoder.as_bytes();

  println!("bytes: {:?}", bytes);

  let mut decoder: BinDecoder = BinDecoder::new(bytes);
  let read_rdata = read(&mut decoder, bytes.len() as u16);
  assert!(read_rdata.is_ok(), format!("error decoding: {:?}", read_rdata.unwrap_err()));
  assert_eq!(rdata, read_rdata.unwrap());
}