hashing and pkey signing working

This commit is contained in:
Benjamin Fry 2015-11-15 20:57:59 -08:00
parent 889f7993bc
commit f0792d77a0
17 changed files with 684 additions and 24 deletions

1
.coveralls.yml Normal file
View File

@ -0,0 +1 @@
repo_token: 0fXi81cx3Khi8g0Q0mGqsABePoEdJfMcf

View File

@ -6,3 +6,9 @@ rust:
matrix:
allow_failure:
- rust: nightly
after_success: |
sudo apt-get install libcurl4-openssl-dev libelf-dev libdw-dev &&
wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz &&
tar xzf master.tar.gz && mkdir kcov-master/build && cd kcov-master/build && cmake .. && make &&
sudo make install && cd ../.. &&
kcov --coveralls-id=$TRAVIS_JOB_ID --exclude-pattern=/.cargo target/kcov target/debug/trust-dns-*

73
Cargo.lock generated
View File

@ -1,11 +1,12 @@
[root]
name = "trust-dns"
version = "0.4.0"
version = "0.4.1"
dependencies = [
"chrono 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
"docopt 0.6.73 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"openssl 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -32,6 +33,11 @@ name = "bitflags"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bitflags"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bytes"
version = "0.2.11"
@ -64,6 +70,15 @@ dependencies = [
"strsim 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "gcc"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"advapi32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "kernel32-sys"
version = "0.1.4"
@ -73,11 +88,24 @@ dependencies = [
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "lazy_static"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libressl-pnacl-sys"
version = "2.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"pnacl-build-helper 1.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "log"
version = "0.3.2"
@ -126,6 +154,41 @@ dependencies = [
"rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "openssl"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"openssl-sys 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "openssl-sys"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"gcc 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libressl-pnacl-sys 2.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "pkg-config"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "pnacl-build-helper"
version = "1.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"tempdir 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.3.11"
@ -166,6 +229,14 @@ name = "strsim"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "tempdir"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "time"
version = "0.1.32"

View File

@ -1,6 +1,6 @@
[package]
name = "trust-dns"
version = "0.4.0"
version = "0.4.1"
authors = ["Benjamin Fry <benjaminfry@me.com>"]
# A short blurb about the package. This is not rendered in any format when
@ -68,3 +68,4 @@ docopt = "^0.6.72"
mio = "^0.4.3"
toml = "^0.1.22"
chrono = "^0.2.16"
openssl = "^0.6.7"

View File

@ -1,4 +1,7 @@
# trust-dns [![Build Status](https://travis-ci.org/bluejekyll/trust-dns.svg?branch=master)](https://travis-ci.org/bluejekyll/trust-dns)
# trust-dns
[![Build Status](https://travis-ci.org/bluejekyll/trust-dns.svg?branch=master)](https://travis-ci.org/bluejekyll/trust-dns)
[![Coverage Status](https://coveralls.io/repos/bluejekyll/trust-dns/badge.svg?branch=master&service=github)](https://coveralls.io/github/bluejekyll/trust-dns?branch=master)
[![](http://meritbadge.herokuapp.com/trust-dns)](https://crates.io/crates/trust-dns)
A Rust based DNS client and server, built to be safe and secure from the
ground up.
@ -19,28 +22,46 @@ WARNING!!! Under active development!
The client now supports timeouts (thanks mio!). Currently hardcoded to 5 seconds,
I'll make this configurable if people ask for that, but this allows me to move on.
The server code is complete, the daemon currently only supports IPv4. Master file
parsing is complete and supported. There is currently no forking option, and
the server is not yet threaded.
The server code is complete, the daemon supports IPv4 and IPv6, UDP and TCP.
There currently is no way to limit TCP and AXFR operations, so it is still not
recommended to put into production as TCP can be used to DOS the service.
Master file parsing is complete and supported. There is currently no forking
option, and the server is not yet threaded.
## RFC's implemented
### Basic operations
- [RFC 1035](https://tools.ietf.org/html/rfc1035): Base DNS spec (partial, caching not yet supported)
- [RFC 3596](https://tools.ietf.org/html/rfc3596): IPv6
- [RFC 2782](https://tools.ietf.org/html/rfc2782): Service location
### Update operations
- [RFC 2136](https://tools.ietf.org/html/rfc2136): Dynamic Update
## RFC's in progress or not yet implemented
### Basic operations
- [RFC 2308](https://tools.ietf.org/html/rfc2308): Negative Caching of DNS Queries
- [RFC 2317](https://tools.ietf.org/html/rfc2317): Classless IN-ADDR.ARPA delegation
- [RFC 6891](https://tools.ietf.org/html/rfc6891): Extension Mechanisms for DNS
### Update operations
- [RFC 1995](https://tools.ietf.org/html/rfc1995): Incremental Zone Transfer
- [RFC 1996](https://tools.ietf.org/html/rfc1996): Notify slaves of update
- [RFC 2782](https://tools.ietf.org/html/rfc2782): Service location
- [RFC 3007](https://tools.ietf.org/html/rfc3007): Secure Dynamic Update
- [RFC 6891](https://tools.ietf.org/html/rfc6891): Extension Mechanisms for DNS
- [RFC 4034](https://tools.ietf.org/html/rfc4034): DNSSEC Resource Records
- [DNSCrypt](https://dnscrypt.org): Trusted DNS queries
- [Update Leases](https://tools.ietf.org/html/draft-sekar-dns-ul-01): Dynamic DNS Update Leases
- [Long-Lived Queries](http://tools.ietf.org/html/draft-sekar-dns-llq-01): Notify with bells
### Secure DNS operations
- [RFC 3007](https://tools.ietf.org/html/rfc3007): Secure Dynamic Update
- [RFC 4034](https://tools.ietf.org/html/rfc4034): DNSSEC Resource Records
- [RFC 4035](https://tools.ietf.org/html/rfc4035): Protocol Modifications for DNSSEC
- [RFC 4509](https://tools.ietf.org/html/rfc4509): SHA-256 in DNSSEC Delegation Signer
- [RFC 5155](https://tools.ietf.org/html/rfc5155): DNSSEC Hashed Authenticated Denial of Existence
- [RFC 5702](https://tools.ietf.org/html/rfc5702): SHA-2 Algorithms with RSA in DNSKEY and RRSIG for DNSSEC
- [RFC 6840](https://tools.ietf.org/html/rfc6840): Clarifications and Implementation Notes for DNSSEC
- [RFC 6944](https://tools.ietf.org/html/rfc6944): DNSKEY Algorithm Implementation Status
- [DNSCrypt](https://dnscrypt.org): Trusted DNS queries
# Usage
This assumes that you have [Rust](https://www.rust-lang.org) stable installed. These
@ -94,6 +115,6 @@ so this should allow it to work with most internal loads.
- Why are you building another DNS server?
Because I've gotten tired of seeing the security advisories out there for BIND.
Because of all the security advisories out there for BIND.
Using Rust semantics it should be possible to develop a high performance and
safe DNS Server that is more resilient to attacks.

View File

@ -248,7 +248,9 @@ impl Authority {
* if (local option)
* return (REFUSED)
*/
fn authorize(&self) -> UpdateResult<()> {
fn authorize(&self/*, update_message: &UpdateMessage*/) -> UpdateResult<()> {
//
if !self.allow_update {
warn!("update attempted on non-updatable Authority: {}", self.origin);
Err(ResponseCode::Refused)

View File

@ -25,6 +25,7 @@ pub enum DecodeError {
UnknownDnsClassStr(String),
UnknownRecordTypeValue(u16),
UnknownRecordTypeStr(String),
UnknownAlgorithmTypeValue(u8),
NoRecordDataType,
NoRecordDataLength,
EOF,
@ -38,6 +39,7 @@ impl fmt::Display for DecodeError {
DecodeError::UnknownDnsClassStr(ref val) => write!(f, "DnsClass string unknown: {}", val),
DecodeError::UnknownRecordTypeValue(ref val) => write!(f, "RecordType value unknown: {}", val),
DecodeError::UnknownRecordTypeStr(ref val) => write!(f, "RecordType string unknown: {}", val),
DecodeError::UnknownAlgorithmTypeValue(ref val) => write!(f, "AlgorithmType value unknown: {}", val),
DecodeError::NoRecordDataType => write!(f, "There was no record data type specified"),
DecodeError::NoRecordDataLength => write!(f, "There was no record data length specified"),
DecodeError::EOF => write!(f, "End of input reached before next read could complete"),
@ -53,6 +55,7 @@ impl Error for DecodeError {
DecodeError::UnknownDnsClassStr(..) => "DnsClass string unknown",
DecodeError::UnknownRecordTypeValue(..) => "RecordType value unknown",
DecodeError::UnknownRecordTypeStr(..) => "RecordType string unknown",
DecodeError::UnknownAlgorithmTypeValue(..) => "AlgorithmType value unknown",
DecodeError::NoRecordDataType => "RecordType unspecified",
DecodeError::NoRecordDataLength => "RecordData length unspecified",
DecodeError::EOF => "End of input",

View File

@ -18,6 +18,7 @@ extern crate mio;
extern crate toml;
extern crate rustc_serialize;
extern crate chrono;
extern crate openssl;
pub mod logger;
pub mod rr;

229
src/rr/dnssec/algorithm.rs Normal file
View File

@ -0,0 +1,229 @@
/*
* Copyright (C) 2015 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.
*/
use std::convert::Into;
use openssl::crypto::pkey;
use openssl::crypto::hash;
use ::serialize::binary::*;
use ::error::*;
// RFC 6944 DNSSEC DNSKEY Algorithm Status April 2013
//
// 2.2. Algorithm Implementation Status Assignment Rationale
//
// RSASHA1 has an implementation status of Must Implement, consistent
// with [RFC4034]. RSAMD5 has an implementation status of Must Not
// Implement because of known weaknesses in MD5.
//
// The status of RSASHA1-NSEC3-SHA1 is set to Recommended to Implement
// as many deployments use NSEC3. The status of RSA/SHA-256 and RSA/
// SHA-512 are also set to Recommended to Implement as major deployments
// (such as the root zone) use these algorithms [ROOTDPS]. It is
// believed that RSA/SHA-256 or RSA/SHA-512 algorithms will replace
// older algorithms (e.g., RSA/SHA-1) that have a perceived weakness.
//
// Likewise, ECDSA with the two identified curves (ECDSAP256SHA256 and
// ECDSAP384SHA384) is an algorithm that may see widespread use due to
// the perceived similar level of security offered with smaller key size
// compared to the key sizes of algorithms such as RSA. Therefore,
// ECDSAP256SHA256 and ECDSAP384SHA384 are Recommended to Implement.
//
// All other algorithms used in DNSSEC specified without an
// implementation status are currently set to Optional.
//
// 2.3. DNSSEC Implementation Status Table
//
// The DNSSEC algorithm implementation status table is listed below.
// Only the algorithms already specified for use with DNSSEC at the time
// of writing are listed.
//
// +------------+------------+-------------------+-------------------+
// | Must | Must Not | Recommended | Optional |
// | Implement | Implement | to Implement | |
// +------------+------------+-------------------+-------------------+
// | | | | |
// | RSASHA1 | RSAMD5 | RSASHA256 | Any |
// | | | RSASHA1-NSEC3 | registered |
// | | | -SHA1 | algorithm |
// | | | RSASHA512 | not listed in |
// | | | ECDSAP256SHA256 | this table |
// | | | ECDSAP384SHA384 | |
// +------------+------------+-------------------+-------------------+
//
// This table does not list the Reserved values in the IANA registry
// table or the values for INDIRECT (252), PRIVATE (253), and PRIVATEOID
// (254). These values may relate to more than one algorithm and are
// therefore up to the implementer's discretion. As noted, any
// algorithm not listed in the table is Optional. As of this writing,
// the Optional algorithms are DSASHA1, DH, DSA-NSEC3-SHA1, and GOST-
// ECC, but in general, anything not explicitly listed is Optional.
//
// 2.4. Specifying New Algorithms and Updating the Status of Existing
// Entries
//
// [RFC6014] establishes a parallel procedure for adding a registry
// entry for a new algorithm other than a standards track document.
// Because any algorithm not listed in the foregoing table is Optional,
// algorithms entered into the registry using the [RFC6014] procedure
// are automatically Optional.
//
// It has turned out to be useful for implementations to refer to a
// single document that specifies the implementation status of every
// algorithm. Accordingly, when a new algorithm is to be registered
// with a status other than Optional, this document shall be made
// obsolete by a new document that adds the new algorithm to the table
// in Section 2.3. Similarly, if the status of any algorithm in the
// table in Section 2.3 changes, a new document shall make this document
// obsolete; that document shall include a replacement of the table in
// Section 2.3. This way, the goal of having one authoritative document
// to specify all the status values is achieved.
//
// This document cannot be updated, only made obsolete and replaced by a
// successor document.
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub enum Algorithm {
/// DO NOT USE, SHA1 is a compromised hashing function, it is here for backward compatability
RSASHA1,
RSASHA256,
/// DO NOT USE, SHA1 is a compromised hashing function, it is here for backward compatability
RSASHA1_NSEC3_SHA1,
RSASHA512,
// ECDSAP256SHA256, // not yet supported
// ECDSAP384SHA384,
}
impl Algorithm {
pub fn get_hash_type(self) -> hash::Type {
match self {
Algorithm::RSASHA1 | Algorithm::RSASHA1_NSEC3_SHA1 => hash::Type::SHA1,
Algorithm::RSASHA256 => hash::Type::SHA256,
Algorithm::RSASHA512 => hash::Type::SHA512,
// Algorithm::ECDSAP256SHA256 => hash::Type::SHA256,
// Algorithm::ECDSAP384SHA384 => hash::Type::SHA384,
}
}
fn hash(&self, data: &[u8]) -> Vec<u8> {
hash::hash(self.get_hash_type(), data)
}
pub fn sign(&self, private_key: &pkey::PKey, data: &[u8]) -> Vec<u8> {
if !private_key.can(pkey::Role::Sign) { panic!("This key cannot be used for signing") }
// calculate the hash...
let hash = self.hash(data);
// then sign and return
private_key.sign(&hash)
}
pub fn verify(&self, public_key: &pkey::PKey, data: &[u8], signature: &[u8]) -> bool {
if !public_key.can(pkey::Role::Verify) { panic!("This key cannot be used to verify signature") }
// calculate the hash on the local data
let hash = self.hash(data);
// verify the remotely sent signature
public_key.verify(&hash, signature)
}
/// http://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml
pub fn from_u8(value: u8) -> DecodeResult<Self> {
match value {
5 => Ok(Algorithm::RSASHA1),
7 => Ok(Algorithm::RSASHA1_NSEC3_SHA1),
8 => Ok(Algorithm::RSASHA256),
10 => Ok(Algorithm::RSASHA512),
// 13 => Algorithm::ECDSAP256SHA256,
// 14 => Algorithm::ECDSAP384SHA384,
_ => Err(DecodeError::UnknownAlgorithmTypeValue(value)),
}
}
}
impl BinSerializable<Algorithm> for Algorithm {
// http://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml
fn read(decoder: &mut BinDecoder) -> DecodeResult<Algorithm> {
let algorithm_id = try!(decoder.read_u8());
Algorithm::from_u8(algorithm_id)
}
fn emit(&self, encoder: &mut BinEncoder) -> EncodeResult {
encoder.emit(u8::from(*self))
}
}
impl From<&'static str> for Algorithm {
fn from(s: &'static str) -> Algorithm {
match s {
"RSASHA1" => Algorithm::RSASHA1,
"RSASHA256" => Algorithm::RSASHA256,
"RSASHA1-NSEC3-SHA1" => Algorithm::RSASHA1_NSEC3_SHA1,
"RSASHA512" => Algorithm::RSASHA512,
// "ECDSAP256SHA256" => Algorithm::ECDSAP256SHA256,
// "ECDSAP384SHA384" => Algorithm::ECDSAP384SHA384,
_ => panic!("unrecognized string {}", s),
}
}
}
impl From<Algorithm> for &'static str {
fn from(a: Algorithm) -> &'static str {
match a {
Algorithm::RSASHA1 => "RSASHA1",
Algorithm::RSASHA256 => "RSASHA256",
Algorithm::RSASHA1_NSEC3_SHA1 => "RSASHA1-NSEC3-SHA1",
Algorithm::RSASHA512 => "RSASHA512",
// ECDSAP256SHA256 => "ECDSAP256SHA256",
// ECDSAP384SHA384 => "ECDSAP384SHA384",
}
}
}
impl From<Algorithm> for u8 {
fn from(a: Algorithm) -> u8 {
match a {
Algorithm::RSASHA1 => 5,
Algorithm::RSASHA256 => 7,
Algorithm::RSASHA1_NSEC3_SHA1 => 8,
Algorithm::RSASHA512 => 10,
// ECDSAP256SHA256 => 13,
// ECDSAP384SHA384 => 14,
}
}
}
#[cfg(test)]
mod test {
use super::Algorithm;
use openssl::crypto::pkey;
#[test]
fn test_hashing() {
let bytes = b"www.example.com";
let mut pkey = pkey::PKey::new();
pkey.gen(2048);
for algorithm in &[Algorithm::RSASHA1,
Algorithm::RSASHA256,
Algorithm::RSASHA1_NSEC3_SHA1,
Algorithm::RSASHA512] {
let sig = algorithm.sign(&pkey, bytes);
assert!(algorithm.verify(&pkey, bytes, &sig));
}
}
}

18
src/rr/dnssec/mod.rs Normal file
View File

@ -0,0 +1,18 @@
/*
* Copyright (C) 2015 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.
*/
mod algorithm;
pub use self::algorithm::Algorithm;

View File

@ -19,6 +19,7 @@ pub mod resource;
pub mod record_data;
pub mod domain;
mod rdata;
mod dnssec;
pub use self::record_type::RecordType;
pub use self::resource::Record;

View File

@ -17,13 +17,14 @@
// TODO: these should each be it's own struct, it would make parsing and decoding a little cleaner
// and also a little more ergonomic when accessing.
// each of these module's has the parser for that rdata embedded, to keep the file sizes down...
pub mod a;
pub mod aaaa;
pub mod cname;
pub mod mx;
pub mod null;
pub mod ns;
pub mod ptr;
pub mod sig;
pub mod soa;
pub mod srv;
pub mod txt;
pub mod a;
pub mod aaaa;

259
src/rr/rdata/sig.rs Normal file
View File

@ -0,0 +1,259 @@
/*
* Copyright (C) 2015 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.
*/
use ::serialize::txt::*;
use ::serialize::binary::*;
use ::error::*;
use ::rr::record_data::RData;
use ::rr::domain::Name;
use ::rr::dnssec::Algorithm;
// RFC 2535 & 2931 DNS Security Extensions March 1999
//
// NOTE: RFC 2535 was obsoleted with 4034+, with the exception of the
// usage for UPDATE, which is what this implementation is for.
//
// 4.1 SIG RDATA Format
//
// The RDATA portion of a SIG RR is as shown below. The integrity of
// the RDATA information is protected by the signature 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | type covered | algorithm | labels |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | original TTL |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | signature expiration |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | signature inception |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | key tag | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ signer's name +
// | /
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-/
// / /
// / signature /
// / /
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// 4.1.1 Type Covered Field
//
// The "type covered" is the type of the other RRs covered by this SIG.
//
// 4.1.2 Algorithm Number Field
//
// This octet is as described in section 3.2.
//
// 4.1.3 Labels Field
//
// The "labels" octet is an unsigned count of how many labels there are
// in the original SIG RR owner name not counting the null label for
// root and not counting any initial "*" for a wildcard. If a secured
// retrieval is the result of wild card substitution, it is necessary
// for the resolver to use the original form of the name in verifying
// the digital signature. This field makes it easy to determine the
// original form.
//
// If, on retrieval, the RR appears to have a longer name than indicated
// by "labels", the resolver can tell it is the result of wildcard
// substitution. If the RR owner name appears to be shorter than the
// labels count, the SIG RR must be considered corrupt and ignored. The
// maximum number of labels allowed in the current DNS is 127 but the
// entire octet is reserved and would be required should DNS names ever
// be expanded to 255 labels. The following table gives some examples.
// The value of "labels" is at the top, the retrieved owner name on the
// left, and the table entry is the name to use in signature
// verification except that "bad" means the RR is corrupt.
//
// labels= | 0 | 1 | 2 | 3 | 4 |
// --------+-----+------+--------+----------+----------+
// .| . | bad | bad | bad | bad |
// d.| *. | d. | bad | bad | bad |
// c.d.| *. | *.d. | c.d. | bad | bad |
// b.c.d.| *. | *.d. | *.c.d. | b.c.d. | bad |
// a.b.c.d.| *. | *.d. | *.c.d. | *.b.c.d. | a.b.c.d. |
//
// 4.1.4 Original TTL Field
//
// The "original TTL" field is included in the RDATA portion to avoid
// (1) authentication problems that caching servers would otherwise
// cause by decrementing the real TTL field and (2) security problems
// that unscrupulous servers could otherwise cause by manipulating the
// real TTL field. This original TTL is protected by the signature
// while the current TTL field is not.
//
// NOTE: The "original TTL" must be restored into the covered RRs when
// the signature is verified (see Section 8). This generaly implies
// that all RRs for a particular type, name, and class, that is, all the
// RRs in any particular RRset, must have the same TTL to start with.
//
// 4.1.5 Signature Expiration and Inception Fields
//
// The SIG is valid from the "signature inception" time until the
// "signature expiration" time. Both are unsigned numbers of seconds
// since the start of 1 January 1970, GMT, ignoring leap seconds. (See
// also Section 4.4.) Ring arithmetic is used as for DNS SOA serial
// numbers [RFC 1982] which means that these times can never be more
// than about 68 years in the past or the future. This means that these
// times are ambiguous modulo ~136.09 years. However there is no
// security flaw because keys are required to be changed to new random
// keys by [RFC 2541] at least every five years. This means that the
// probability that the same key is in use N*136.09 years later should
// be the same as the probability that a random guess will work.
//
// A SIG RR may have an expiration time numerically less than the
// inception time if the expiration time is near the 32 bit wrap around
// point and/or the signature is long lived.
//
// (To prevent misordering of network requests to update a zone
// dynamically, monotonically increasing "signature inception" times may
// be necessary.)
//
// A secure zone must be considered changed for SOA serial number
// purposes not only when its data is updated but also when new SIG RRs
// are inserted (ie, the zone or any part of it is re-signed).
//
// 4.1.6 Key Tag Field
//
// The "key Tag" is a two octet quantity that is used to efficiently
// select between multiple keys which may be applicable and thus check
// that a public key about to be used for the computationally expensive
// effort to check the signature is possibly valid. For algorithm 1
// (MD5/RSA) as defined in [RFC 2537], it is the next to the bottom two
// octets of the public key modulus needed to decode the signature
// field. That is to say, the most significant 16 of the least
// significant 24 bits of the modulus in network (big endian) order. For
// all other algorithms, including private algorithms, it is calculated
// as a simple checksum of the KEY RR as described in Appendix C.
//
// 4.1.7 Signer's Name Field
//
// The "signer's name" field is the domain name of the signer generating
// the SIG RR. This is the owner name of the public KEY RR that can be
// used to verify the signature. It is frequently the zone which
// contained the RRset being authenticated. Which signers should be
// authorized to sign what is a significant resolver policy question as
// discussed in Section 6. The signer's name may be compressed with
// standard DNS name compression when being transmitted over the
// network.
//
// 4.1.8 Signature Field
//
// The actual signature portion of the SIG RR binds the other RDATA
// fields to the RRset of the "type covered" RRs with that owner name
// and class. This covered RRset is thereby authenticated. To
// accomplish this, a data sequence is constructed as follows:
//
// data = RDATA | RR(s)...
//
// where "|" is concatenation,
//
// RDATA is the wire format of all the RDATA fields in the SIG RR itself
// (including the canonical form of the signer's name) before but not
// including the signature, and
//
// RR(s) is the RRset of the RR(s) of the type covered with the same
// owner name and class as the SIG RR in canonical form and order as
// defined in Section 8.
//
// How this data sequence is processed into the signature is algorithm
// dependent. These algorithm dependent formats and procedures are
// described in separate documents (Section 3.2).
//
// SIGs SHOULD NOT be included in a zone for any "meta-type" such as
// ANY, AXFR, etc. (but see section 5.6.2 with regard to IXFR).
//
// SIG { type_covered: u16, algorithm: SecAlgorithm, num_labels: u8, original_ttl: u32,
// sig_expiration: u32, sig_inception: u32, key_tag: u16, signer_name: Name, sig: Vec<u8> }
pub fn read(decoder: &mut BinDecoder) -> DecodeResult<RData> {
// TODO should we verify here? or elsewhere...
let type_covered = try!(decoder.read_u16());
let algorithm = try!(Algorithm::read(decoder));
let num_labels = try!(decoder.read_u8());
let original_ttl = try!(decoder.read_u32());
let sig_expiration = try!(decoder.read_u32());
let sig_inception = try!(decoder.read_u32());
let key_tag = try!(decoder.read_u16());
let signer_name = try!(Name::read(decoder));
let sig = try!(decoder.read_vec(algorithm.get_hash_type().md_len()));
Ok(RData::SIG {
type_covered: type_covered,
algorithm: algorithm,
num_labels: num_labels,
original_ttl: original_ttl,
sig_expiration: sig_expiration,
sig_inception: sig_expiration,
key_tag: key_tag,
signer_name: signer_name,
sig: sig,
})
}
pub fn emit(encoder: &mut BinEncoder, sig: &RData) -> EncodeResult {
if let RData::SIG { type_covered, algorithm, num_labels, original_ttl, sig_expiration, sig_inception, key_tag, ref signer_name, ref sig } = *sig {
try!(encoder.emit_u16(type_covered));
try!(algorithm.emit(encoder));
try!(encoder.emit(num_labels));
try!(encoder.emit_u32(original_ttl));
try!(encoder.emit_u32(sig_expiration));
try!(encoder.emit_u32(sig_inception));
try!(encoder.emit_u16(key_tag));
try!(signer_name.emit(encoder));
try!(encoder.emit_vec(sig));
Ok(())
} else {
panic!("wrong type here {:?}", sig);
}
}
// VENERA Action\.domains (
// 20 ; SERIAL
// 7200 ; REFRESH
// 600 ; RETRY
// 3600000; EXPIRE
// 60) ; MINIMUM
// pub fn parse(tokens: &Vec<Token>, origin: Option<&Name>) -> ParseResult<RData> {
// let mut token = tokens.iter();
//
// let mname: Name = try!(token.next().ok_or(ParseError::MissingToken("mname".to_string())).and_then(|t| if let &Token::CharData(ref s) = t {Name::parse(s, origin)} else {Err(ParseError::UnexpectedToken(t.clone()))} ));
// let rname: Name = try!(token.next().ok_or(ParseError::MissingToken("rname".to_string())).and_then(|t| if let &Token::CharData(ref s) = t {Name::parse(s, origin)} else {Err(ParseError::UnexpectedToken(t.clone()))} ));
// let mut list = try!(token.next().ok_or(ParseError::MissingToken("List".to_string())).and_then(|t| if let &Token::List(ref v) = t {Ok(v)} else {Err(ParseError::UnexpectedToken(t.clone()))} )).iter();
//
// let serial: u32 = try!(list.next().ok_or(ParseError::MissingToken("serial".to_string())).and_then(|s| Ok(try!(s.parse()))));
// let refresh: i32 = try!(list.next().ok_or(ParseError::MissingToken("refresh".to_string())).and_then(|s| Ok(try!(s.parse()))));
// let retry: i32 = try!(list.next().ok_or(ParseError::MissingToken("retry".to_string())).and_then(|s| Ok(try!(s.parse()))));
// let expire: i32 = try!(list.next().ok_or(ParseError::MissingToken("expire".to_string())).and_then(|s| Ok(try!(s.parse()))));
// let minimum: u32 = try!(list.next().ok_or(ParseError::MissingToken("minimum".to_string())).and_then(|s| Ok(try!(s.parse()))));
//
//
// // let serial: u32 = try!(token.next().ok_or(ParseError::MissingToken("serial".to_string())).and_then(|t| if let &Token::CharData(ref s) = t {Ok(try!(s.parse()))} else {Err(ParseError::UnexpectedToken(t.clone()))} ));
// // let refresh: i32 = try!(token.next().ok_or(ParseError::MissingToken("refresh".to_string())).and_then(|t| if let &Token::CharData(ref s) = t {Ok(try!(s.parse()))} else {Err(ParseError::UnexpectedToken(t.clone()))} ));
// // let retry: i32 = try!(token.next().ok_or(ParseError::MissingToken("retry".to_string())).and_then(|t| if let &Token::CharData(ref s) = t {Ok(try!(s.parse()))} else {Err(ParseError::UnexpectedToken(t.clone()))} ));
// // let expire: i32 = try!(token.next().ok_or(ParseError::MissingToken("expire".to_string())).and_then(|t| if let &Token::CharData(ref s) = t {Ok(try!(s.parse()))} else {Err(ParseError::UnexpectedToken(t.clone()))} ));
// // let minimum: u32 = try!(token.next().ok_or(ParseError::MissingToken("minimum".to_string())).and_then(|t| if let &Token::CharData(ref s) = t {Ok(try!(s.parse()))} else {Err(ParseError::UnexpectedToken(t.clone()))} ));
//
// Ok(RData::SOA{
// mname: mname,
// rname: rname,
// serial: serial,
// refresh: refresh,
// retry: retry,
// expire: expire,
// minimum: minimum,
// })
// }

View File

@ -19,6 +19,7 @@ use std::convert::From;
use ::error::*;
use ::serialize::binary::*;
use ::serialize::txt::*;
use ::rr::dnssec::Algorithm;
use super::domain::Name;
use super::record_type::RecordType;
use super::rdata;
@ -174,6 +175,30 @@ pub enum RData {
// description of the IN-ADDR.ARPA domain for an example.
PTR { ptrdname: Name },
// RFC 2535 & 2931 DNS Security Extensions March 1999
//
// 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | type covered | algorithm | labels |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | original TTL |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | signature expiration |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | signature inception |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | key tag | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ signer's name +
// | /
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-/
// / /
// / signature /
// / /
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
SIG { type_covered: u16, algorithm: Algorithm, num_labels: u8, original_ttl: u32,
sig_expiration: u32, sig_inception: u32, key_tag: u16, signer_name: Name, sig: Vec<u8> },
// 3.3.13. SOA RDATA format
//
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

View File

@ -35,8 +35,8 @@ pub enum RecordType {
// DHCID, // 49 RFC 4701 DHCP identifier
// DLV, // 32769 RFC 4431 DNSSEC Lookaside Validation record
// DNAME, // 39 RFC 2672 Delegation Name
// DNSKEY, // 48 RFC 4034 DNS Key record
// DS, // 43 RFC 4034 Delegation signer
DNSKEY, // 48 RFC 4034 DNS Key record: RSASHA256 and RSASHA512, RFC5702
DS, // 43 RFC 4034 Delegation signer: RSASHA256 and RSASHA512, RFC5702
// HIP, // 55 RFC 5205 Host Identity Protocol
// IPSECKEY, // 45 RFC 4025 IPsec Key
// KEY, // 25 RFC 2535[3] and RFC 2930[4] Key record
@ -47,12 +47,12 @@ pub enum RecordType {
NS, // 2 RFC 1035[1] Name server record
NULL, // 0 RFC 1035[1] Null server record, for testing
// NSEC, // 47 RFC 4034 Next-Secure record
// NSEC3, // 50 RFC 5155 NSEC record version 3
NSEC3, // 50 RFC 5155 NSEC record version 3
// NSEC3PARAM, // 51 RFC 5155 NSEC3 parameters
PTR, // 12 RFC 1035[1] Pointer record
// RRSIG, // 46 RFC 4034 DNSSEC signature
RRSIG, // 46 RFC 4034 DNSSEC signature: RSASHA256 and RSASHA512, RFC5702
// RP, // 17 RFC 1183 Responsible person
// SIG, // 24 RFC 2535 Signature
SIG, // 24 RFC 2535 (2931) Signature, to support 2137 Update
SOA, // 6 RFC 1035[1] and RFC 2308[9] Start of [a zone of] authority record
SRV, // 33 RFC 2782 Service locator
// SSHFP, // 44 RFC 4255 SSH Public Key Fingerprint

View File

@ -84,15 +84,26 @@ impl<'a> BinDecoder<'a> {
let length: u8 = try!(self.pop());
// TODO once Drain stabalizes on Vec, this should be replaced...
let mut label_vec: Vec<u8> = Vec::with_capacity(length as usize);
for _ in 0..length as usize {
label_vec.push(try!(self.pop()))
}
let mut label_vec: Vec<u8> = try!(self.read_vec(length as usize));
// translate bytes to string, then lowercase...
Ok(try!(String::from_utf8(label_vec)).to_lowercase())
}
pub fn read_vec(&mut self, len: usize) -> DecodeResult<Vec<u8>> {
// TODO once Drain stabalizes on Vec, this should be replaced...
let mut vec: Vec<u8> = Vec::with_capacity(len);
for _ in 0..len as usize {
vec.push(try!(self.pop()))
}
Ok(vec)
}
pub fn read_u8(&mut self) -> DecodeResult<u8> {
self.pop()
}
/// parses the next 2 bytes into u16. This performs a byte-by-byte manipulation, there
/// which means endianness is implicitly handled (i.e. no network to little endian (intel), issues)
///

View File

@ -147,4 +147,14 @@ impl<'a> BinEncoder<'a> {
Ok(())
}
pub fn emit_vec(&mut self, data: &[u8]) -> EncodeResult {
self.buffer.reserve(data.len());
for i in data {
try!(self.emit(*i));
}
Ok(())
}
}