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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
/*
 * 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.
 */

//! public key record data for signing zone records

use ::serialize::binary::*;
use ::error::*;
use ::rr::dnssec::Algorithm;

/// [RFC 4034, DNSSEC Resource Records, March 2005](https://tools.ietf.org/html/rfc4034#section-2)
///
/// ```text
/// 2.  The DNSKEY Resource Record
///
///    DNSSEC uses public key cryptography to sign and authenticate DNS
///    resource record sets (RRsets).  The public keys are stored in DNSKEY
///    resource records and are used in the DNSSEC authentication process
///    described in [RFC4035]: A zone signs its authoritative RRsets by
///    using a private key and stores the corresponding public key in a
///    DNSKEY RR.  A resolver can then use the public key to validate
///    signatures covering the RRsets in the zone, and thus to authenticate
///    them.
///
///    The DNSKEY RR is not intended as a record for storing arbitrary
///    public keys and MUST NOT be used to store certificates or public keys
///    that do not directly relate to the DNS infrastructure.
///
///    The Type value for the DNSKEY RR type is 48.
///
///    The DNSKEY RR is class independent.
///
///    The DNSKEY RR has no special TTL requirements.
///
/// 2.1.  DNSKEY RDATA Wire Format
///
///    The RDATA for a DNSKEY RR consists of a 2 octet Flags Field, a 1
///    octet Protocol Field, a 1 octet Algorithm Field, and the Public Key
///    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
///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
///    |              Flags            |    Protocol   |   Algorithm   |
///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
///    /                                                               /
///    /                            Public Key                         /
///    /                                                               /
///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
///
/// 2.1.5.  Notes on DNSKEY RDATA Design
///
///    Although the Protocol Field always has value 3, it is retained for
///    backward compatibility with early versions of the KEY record.
///
/// ```
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct DNSKEY { zone_key: bool, secure_entry_point: bool, revoke: bool, algorithm: Algorithm,
                    public_key: Vec<u8> }

impl DNSKEY {
  pub fn new(zone_key: bool, secure_entry_point: bool, revoke: bool, algorithm: Algorithm,
    public_key: Vec<u8>) -> DNSKEY {
      DNSKEY { zone_key: zone_key, secure_entry_point: secure_entry_point, revoke: revoke,
        algorithm: algorithm,
        public_key: public_key }
  }

  /// [RFC 4034, DNSSEC Resource Records, March 2005](https://tools.ietf.org/html/rfc4034#section-2.1.1)
  ///
  /// ```text
  /// 2.1.1.  The Flags Field
  ///
  ///    Bit 7 of the Flags field is the Zone Key flag.  If bit 7 has value 1,
  ///    then the DNSKEY record holds a DNS zone key, and the DNSKEY RR's
  ///    owner name MUST be the name of a zone.  If bit 7 has value 0, then
  ///    the DNSKEY record holds some other type of DNS public key and MUST
  ///    NOT be used to verify RRSIGs that cover RRsets.
  ///
  ///
  ///    Bits 0-6 and 8-14 are reserved: these bits MUST have value 0 upon
  ///    creation of the DNSKEY RR and MUST be ignored upon receipt.
  /// ```
  pub fn is_zone_key(&self) -> bool { self.zone_key }

  /// [RFC 4034, DNSSEC Resource Records, March 2005](https://tools.ietf.org/html/rfc4034#section-2.1.1)
  ///
  /// ```text
  /// 2.1.1.  The Flags Field
  ///
  ///    Bit 15 of the Flags field is the Secure Entry Point flag, described
  ///    in [RFC3757].  If bit 15 has value 1, then the DNSKEY record holds a
  ///    key intended for use as a secure entry point.  This flag is only
  ///    intended to be a hint to zone signing or debugging software as to the
  ///    intended use of this DNSKEY record; validators MUST NOT alter their
  ///    behavior during the signature validation process in any way based on
  ///    the setting of this bit.  This also means that a DNSKEY RR with the
  ///    SEP bit set would also need the Zone Key flag set in order to be able
  ///    to generate signatures legally.  A DNSKEY RR with the SEP set and the
  ///    Zone Key flag not set MUST NOT be used to verify RRSIGs that cover
  ///    RRsets.
  /// ```
  pub fn is_secure_entry_point(&self) -> bool { self.secure_entry_point }

  /// [RFC 5011, Trust Anchor Update, September 2007](https://tools.ietf.org/html/rfc5011#section-3)
  ///
  /// ```text
  /// RFC 5011                  Trust Anchor Update             September 2007
  ///
  /// 7.  IANA Considerations
  ///
  ///   The IANA has assigned a bit in the DNSKEY flags field (see Section 7
  ///   of [RFC4034]) for the REVOKE bit (8).
  /// ```
  pub fn is_revoke(&self) -> bool { self.revoke }

  /// [RFC 4034, DNSSEC Resource Records, March 2005](https://tools.ietf.org/html/rfc4034#section-2.1.3)
  ///
  /// ```text
  /// 2.1.3.  The Algorithm Field
  ///
  ///    The Algorithm field identifies the public key's cryptographic
  ///    algorithm and determines the format of the Public Key field.  A list
  ///    of DNSSEC algorithm types can be found in Appendix A.1
  /// ```
  pub fn get_algorithm(&self) -> &Algorithm { &self.algorithm }

  /// [RFC 4034, DNSSEC Resource Records, March 2005](https://tools.ietf.org/html/rfc4034#section-2.1.4)
  ///
  /// ```text
  /// 2.1.4.  The Public Key Field
  ///
  ///    The Public Key Field holds the public key material.  The format
  ///    depends on the algorithm of the key being stored and is described in
  ///    separate documents.
  /// ```
  pub fn get_public_key(&self) -> &[u8] { &self.public_key }
}

pub fn read(decoder: &mut BinDecoder, rdata_length: u16) -> DecodeResult<DNSKEY> {
  let flags: u16 = try!(decoder.read_u16());

  //    Bits 0-6 and 8-14 are reserved: these bits MUST have value 0 upon
  //    creation of the DNSKEY RR and MUST be ignored upon receipt.
  let zone_key: bool = flags & 0b0000_0001_0000_0000 == 0b0000_0001_0000_0000;
  let secure_entry_point: bool = flags & 0b0000_0000_0000_0001 == 0b0000_0000_0000_00001;
  let revoke: bool = flags & 0b0000_0000_1000_0000 == 0b0000_0000_1000_0000;
  let protocol: u8 = try!(decoder.read_u8());

  // RFC 4034                DNSSEC Resource Records               March 2005
  //
  // 2.1.2.  The Protocol Field
  //
  //    The Protocol Field MUST have value 3, and the DNSKEY RR MUST be
  //    treated as invalid during signature verification if it is found to be
  //    some value other than 3.
  //
  // protocol is defined to only be '3' right now
  if protocol != 3 { return Err(DecodeError::DnsKeyProtocolNot3(protocol)) }

  let algorithm: Algorithm = try!(Algorithm::read(decoder));

  // the public key is the left-over bytes minus 4 for the first fields
  // TODO: decode the key here?
  let public_key: Vec<u8> = try!(decoder.read_vec((rdata_length - 4) as usize));

  Ok(DNSKEY::new(zone_key, secure_entry_point, revoke, algorithm, public_key))
}

pub fn emit(encoder: &mut BinEncoder, rdata: &DNSKEY) -> EncodeResult {
  let mut flags: u16 = 0;
  if rdata.is_zone_key() { flags |= 0b0000_0001_0000_0000 }
  if rdata.is_secure_entry_point() { flags |= 0b0000_0000_0000_0001 }
  if rdata.is_revoke() { flags |= 0b0000_0000_1000_0000 }
  try!(encoder.emit_u16(flags));
  try!(encoder.emit(3)); // always 3 for now
  try!(rdata.get_algorithm().emit(encoder));
  try!(encoder.emit_vec(rdata.get_public_key()));

  Ok(())
}

// / 2.2.  The DNSKEY RR Presentation Format
// /
// /    The presentation format of the RDATA portion is as follows:
// /
// /    The Flag field MUST be represented as an unsigned decimal integer.
// /    Given the currently defined flags, the possible values are: 0, 256,
// /    and 257.
// /
// /    The Protocol Field MUST be represented as an unsigned decimal integer
// /    with a value of 3.
// /
// /    The Algorithm field MUST be represented either as an unsigned decimal
// /    integer or as an algorithm mnemonic as specified in Appendix A.1.
// /
// /    The Public Key field MUST be represented as a Base64 encoding of the
// /    Public Key.  Whitespace is allowed within the Base64 text.  For a
// /    definition of Base64 encoding, see [RFC3548].
// /
// / TODO: to_string()

#[test]
pub fn test() {
  let rdata = DNSKEY::new(true, true, false, Algorithm::RSASHA256, vec![0,1,2,3,4,5,6,7]);

  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());
}