DNSKEY added to zone
This commit is contained in:
parent
aab326f9fd
commit
64c1020ba4
@ -6,6 +6,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
### Added
|
||||
- Documentation on all modules, and many standard RFC types
|
||||
- Authority zone signing now complete, still need to load/save private keys
|
||||
- DNSKEYs auto inserted for added private keys
|
||||
- New mocked network client tests, to verify zone signing
|
||||
|
||||
### Fixed
|
||||
- Added loop on TCP accept requests
|
||||
|
@ -82,6 +82,13 @@ impl Authority {
|
||||
}
|
||||
|
||||
pub fn add_secure_key(&mut self, signer: Signer) {
|
||||
// also add the key to the zone
|
||||
let zone_ttl = self.get_soa(false).map_or(0, |soa| if let &RData::SOA(ref soa_rdata) = soa.get_rdata() { soa_rdata.get_minimum() } else { 0 });
|
||||
let dnskey = signer.to_dnskey(self.origin.clone(), zone_ttl);
|
||||
|
||||
// TODO: also generate the CDS and CDNSKEY
|
||||
let serial = self.get_serial();
|
||||
self.upsert(dnskey, serial);
|
||||
self.secure_keys.push(signer);
|
||||
}
|
||||
|
||||
@ -90,6 +97,11 @@ impl Authority {
|
||||
self.allow_update = allow_update;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn get_secure_keys(&self) -> &[Signer] {
|
||||
&self.secure_keys
|
||||
}
|
||||
|
||||
pub fn get_origin(&self) -> &Name {
|
||||
&self.origin
|
||||
}
|
||||
@ -726,8 +738,12 @@ impl Authority {
|
||||
.map_or(0, |soa_rec| if let &RData::SOA(ref soa) = soa_rec.get_rdata() { soa.get_minimum() }
|
||||
else { panic!("SOA has no RDATA: {}", self.origin) } );
|
||||
|
||||
for rr_set in self.records.iter_mut().filter_map(|(_, rr_set)| { rr_set.get_rrsigs().is_empty();
|
||||
Some(rr_set) } ) {
|
||||
for rr_set in self.records.iter_mut().filter_map(|(_, rr_set)| {
|
||||
// do not sign zone DNSKEY's that's the job of the parent zone
|
||||
if rr_set.get_record_type() == RecordType::DNSKEY { return None }
|
||||
rr_set.get_rrsigs().is_empty();
|
||||
Some(rr_set) } ) {
|
||||
|
||||
debug!("signing rr_set: {}", rr_set.get_name());
|
||||
rr_set.clear_rrsigs();
|
||||
let rrsig_temp = Record::with(rr_set.get_name().clone(), RecordType::RRSIG, zone_ttl);
|
||||
@ -842,6 +858,21 @@ pub mod authority_tests {
|
||||
return records;
|
||||
}
|
||||
|
||||
pub fn create_secure_example() -> Authority {
|
||||
use openssl::crypto::pkey::PKey;
|
||||
use ::rr::dnssec::{Algorithm, Signer};
|
||||
|
||||
let mut authority: Authority = create_example();
|
||||
let mut pkey = PKey::new();
|
||||
pkey.gen(512);
|
||||
let signer = Signer::new(Algorithm::RSASHA256, pkey, authority.get_origin().clone(), u32::max_value(), 0);
|
||||
|
||||
authority.add_secure_key(signer);
|
||||
authority.sign_zone();
|
||||
|
||||
authority
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_authority() {
|
||||
let authority: Authority = create_example();
|
||||
@ -1050,22 +1081,17 @@ pub mod authority_tests {
|
||||
|
||||
#[test]
|
||||
fn test_zone_signing() {
|
||||
use openssl::crypto::pkey::PKey;
|
||||
use ::rr::dnssec::{Algorithm, Signer};
|
||||
use ::rr::{RData};
|
||||
|
||||
let mut authority: Authority = create_example();
|
||||
let mut pkey = PKey::new();
|
||||
pkey.gen(512);
|
||||
let signer = Signer::new(Algorithm::RSASHA256, pkey, Name::root(), u32::max_value(), 0);
|
||||
|
||||
authority.add_secure_key(signer);
|
||||
authority.sign_zone();
|
||||
let authority: Authority = create_secure_example();
|
||||
|
||||
let results = authority.lookup(&authority.get_origin(), RecordType::AXFR, true).expect("AXFR broken");
|
||||
|
||||
assert!(results.iter().any(|r| r.get_rr_type() == RecordType::DNSKEY), "must contain a DNSKEY");
|
||||
|
||||
for record in results.iter() {
|
||||
if record.get_rr_type() == RecordType::RRSIG { continue }
|
||||
if record.get_rr_type() == RecordType::DNSKEY { continue }
|
||||
|
||||
// validate all records have associated RRSIGs after signing
|
||||
assert!(results.iter().any(|r| r.get_rr_type() == RecordType::RRSIG &&
|
||||
|
@ -217,14 +217,15 @@ impl Catalog {
|
||||
if let Some(ref_authority) = self.find_auth_recurse(query.get_name()) {
|
||||
let authority = &ref_authority.read().unwrap(); // poison errors should panic
|
||||
debug!("found authority: {:?}", authority.get_origin());
|
||||
let is_dnssec = request.get_edns().map_or(false, |edns|edns.is_dnssec_ok());
|
||||
|
||||
if let Some(records) = Self::search(&*authority, query) {
|
||||
if let Some(records) = Self::search(&*authority, query, is_dnssec) {
|
||||
response.response_code(ResponseCode::NoError);
|
||||
response.authoritative(true);
|
||||
response.add_all_answers(&records);
|
||||
|
||||
// get the NS records
|
||||
let ns = authority.get_ns(false);
|
||||
let ns = authority.get_ns(is_dnssec);
|
||||
if ns.is_none() { warn!("there are no NS records for: {:?}", authority.get_origin()); }
|
||||
else {
|
||||
response.add_all_name_servers(&ns.unwrap());
|
||||
@ -233,7 +234,7 @@ impl Catalog {
|
||||
// in the not found case it's standard to return the SOA in the authority section
|
||||
response.response_code(ResponseCode::NXDomain);
|
||||
|
||||
let soa = authority.get_soa(false);
|
||||
let soa = authority.get_soa(is_dnssec);
|
||||
if soa.is_none() { warn!("there is no SOA record for: {:?}", authority.get_origin()); }
|
||||
else {
|
||||
response.add_name_server(soa.unwrap().clone());
|
||||
@ -251,7 +252,7 @@ impl Catalog {
|
||||
}
|
||||
|
||||
// TODO: move this to Authority
|
||||
pub fn search<'a>(authority: &'a Authority, query: &Query) -> Option<Vec<&'a Record>> {
|
||||
pub fn search<'a>(authority: &'a Authority, query: &Query, is_secure: bool) -> Option<Vec<&'a Record>> {
|
||||
let record_type: RecordType = query.get_query_type();
|
||||
|
||||
// if this is an AXFR zone transfer, verify that this is either the slave or master
|
||||
@ -266,7 +267,7 @@ impl Catalog {
|
||||
|
||||
// it would be better to stream this back, rather than packaging everything up in an array
|
||||
// though for UDP it would still need to be bundled
|
||||
let mut query_result: Option<Vec<_>> = authority.lookup(query.get_name(), record_type, false);
|
||||
let mut query_result: Option<Vec<_>> = authority.lookup(query.get_name(), record_type, is_secure);
|
||||
|
||||
if RecordType::AXFR == record_type {
|
||||
if let Some(soa) = authority.get_soa(false) {
|
||||
@ -300,14 +301,16 @@ impl Catalog {
|
||||
|
||||
#[cfg(test)]
|
||||
mod catalog_tests {
|
||||
use std::net::*;
|
||||
use std::collections::*;
|
||||
|
||||
use ::authority::{Authority, ZoneType};
|
||||
use ::authority::authority_tests::create_example;
|
||||
use super::*;
|
||||
use ::op::*;
|
||||
use ::rr::*;
|
||||
use ::rr::rdata::SOA;
|
||||
use ::op::*;
|
||||
use std::net::*;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_catalog_search() {
|
||||
@ -317,7 +320,7 @@ mod catalog_tests {
|
||||
let mut query: Query = Query::new();
|
||||
query.name(origin.clone());
|
||||
|
||||
if let Some(result) = Catalog::search(&example, &query) {
|
||||
if let Some(result) = Catalog::search(&example, &query, false) {
|
||||
assert_eq!(result.first().unwrap().get_rr_type(), RecordType::A);
|
||||
assert_eq!(result.first().unwrap().get_dns_class(), DNSClass::IN);
|
||||
assert_eq!(result.first().unwrap().get_rdata(), &RData::A(Ipv4Addr::new(93,184,216,34)));
|
||||
@ -335,7 +338,7 @@ mod catalog_tests {
|
||||
let mut query: Query = Query::new();
|
||||
query.name(www_name.clone());
|
||||
|
||||
if let Some(result) = Catalog::search(&example, &query) {
|
||||
if let Some(result) = Catalog::search(&example, &query, false) {
|
||||
assert_eq!(result.first().unwrap().get_rr_type(), RecordType::A);
|
||||
assert_eq!(result.first().unwrap().get_dns_class(), DNSClass::IN);
|
||||
assert_eq!(result.first().unwrap().get_rdata(), &RData::A(Ipv4Addr::new(93,184,216,34)));
|
||||
|
@ -33,12 +33,21 @@ use ::client::ClientConnection;
|
||||
pub struct Client<C: ClientConnection> {
|
||||
client_connection: RefCell<C>,
|
||||
next_id: Cell<u16>,
|
||||
trust_anchor: TrustAnchor,
|
||||
}
|
||||
|
||||
impl<C: ClientConnection> Client<C> {
|
||||
/// name_server to connect to with default port 53
|
||||
pub fn new(client_connection: C) -> Client<C> {
|
||||
Client{ client_connection: RefCell::new(client_connection), next_id: Cell::new(1037) }
|
||||
Client{ client_connection: RefCell::new(client_connection),
|
||||
next_id: Cell::new(1037),
|
||||
trust_anchor: TrustAnchor::default() }
|
||||
}
|
||||
|
||||
pub fn with_trust_anchor(client_connection: C, trust_anchor: TrustAnchor) -> Client<C> {
|
||||
Client{ client_connection: RefCell::new(client_connection),
|
||||
next_id: Cell::new(1037),
|
||||
trust_anchor: trust_anchor }
|
||||
}
|
||||
|
||||
/// When the resolver receives an answer via the normal DNS lookup process, it then checks to
|
||||
@ -86,7 +95,7 @@ impl<C: ClientConnection> Client<C> {
|
||||
// 'com. DS' is signed by '. DNSKEY' which produces 'com. RRSIG', all are in the same zone, '.'
|
||||
// the '.' DNSKEY is signed by the well known root certificate.
|
||||
// TODO fix rrsigs clone()
|
||||
let proof = try!(self.recursive_query_verify(&name, rrset, rrsigs.clone(), query_type, query_class));
|
||||
let proof = try!(self.recursive_query_verify(&name, rrset, rrsigs.clone(), rrset_type, query_class));
|
||||
|
||||
// TODO return this, also make a prettier print
|
||||
debug!("proved existance through: {:?}", proof);
|
||||
@ -198,11 +207,9 @@ impl<C: ClientConnection> Client<C> {
|
||||
fn verify_dnskey(&self, dnskey: &Record) -> ClientResult<Vec<Record>> {
|
||||
let name: &domain::Name = dnskey.get_name();
|
||||
|
||||
if dnskey.get_name().is_root() {
|
||||
if let &RData::DNSKEY(ref rdata) = dnskey.get_rdata() {
|
||||
if TrustAnchor::new().contains(rdata.get_public_key()) {
|
||||
return Ok(vec![dnskey.clone()])
|
||||
}
|
||||
if let &RData::DNSKEY(ref rdata) = dnskey.get_rdata() {
|
||||
if self.trust_anchor.contains(rdata.get_public_key()) {
|
||||
return Ok(vec![dnskey.clone()])
|
||||
}
|
||||
}
|
||||
|
||||
@ -302,7 +309,7 @@ impl<C: ClientConnection> Client<C> {
|
||||
// corresponding RRSIG RR, a validator MUST ignore the settings of the
|
||||
// NSEC and RRSIG bits in an NSEC RR.
|
||||
fn verify_nsec(&self, query_name: &domain::Name, query_type: RecordType,
|
||||
query_class: DNSClass, nsecs: Vec<&Record>) -> ClientResult<()> {
|
||||
_: DNSClass, nsecs: Vec<&Record>) -> ClientResult<()> {
|
||||
debug!("verifying nsec");
|
||||
|
||||
// first look for a record with the same name
|
||||
@ -403,7 +410,7 @@ impl<C: ClientConnection> Client<C> {
|
||||
// equivalent algorithm) proves that X does not exist by proving that an
|
||||
// ancestor of X is its closest encloser.
|
||||
fn verify_nsec3(&self, query_name: &domain::Name, query_type: RecordType,
|
||||
query_class: DNSClass, soa: Option<&Record>,
|
||||
_: DNSClass, soa: Option<&Record>,
|
||||
nsec3s: Vec<&Record>) -> ClientResult<()> {
|
||||
// the search name is the one to look for
|
||||
let zone_name = try!(soa.ok_or(ClientError::NoSOARecord(query_name.clone()))).get_name();
|
||||
@ -529,23 +536,40 @@ impl<C: ClientConnection> Client<C> {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "ftest")]
|
||||
mod test {
|
||||
use std::net::*;
|
||||
|
||||
use ::rr::{DNSClass, RecordType, domain, RData};
|
||||
use ::authority::Catalog;
|
||||
use ::authority::authority_tests::{create_example, create_secure_example};
|
||||
use ::client::{Client, ClientConnection, TestClientConnection};
|
||||
#[cfg(feature = "ftest")]
|
||||
use ::op::ResponseCode;
|
||||
use ::udp::UdpClientConnection;
|
||||
use ::rr::{DNSClass, RecordType, domain, RData};
|
||||
use ::rr::dnssec::TrustAnchor;
|
||||
#[cfg(feature = "ftest")]
|
||||
use ::tcp::TcpClientConnection;
|
||||
use super::Client;
|
||||
use super::super::ClientConnection;
|
||||
#[cfg(feature = "ftest")]
|
||||
use ::udp::UdpClientConnection;
|
||||
|
||||
#[test]
|
||||
fn test_query_nonet() {
|
||||
let authority = create_example();
|
||||
let mut catalog = Catalog::new();
|
||||
catalog.upsert(authority.get_origin().clone(), authority);
|
||||
|
||||
let client = Client::new(TestClientConnection::new(&catalog));
|
||||
|
||||
test_query(client);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "ftest")]
|
||||
fn test_query_udp() {
|
||||
let addr: SocketAddr = ("8.8.8.8",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
let conn = UdpClientConnection::new(addr).unwrap();
|
||||
test_query(conn);
|
||||
let client = Client::new(conn);
|
||||
|
||||
test_query(client);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -553,16 +577,14 @@ mod test {
|
||||
fn test_query_tcp() {
|
||||
let addr: SocketAddr = ("8.8.8.8",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
let conn = TcpClientConnection::new(addr).unwrap();
|
||||
test_query(conn);
|
||||
let client = Client::new(conn);
|
||||
|
||||
test_query(client);
|
||||
}
|
||||
|
||||
|
||||
// TODO: this should be flagged with cfg as a functional test.
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "ftest")]
|
||||
fn test_query<C: ClientConnection>(conn: C) {
|
||||
fn test_query<C: ClientConnection>(client: Client<C>) {
|
||||
let name = domain::Name::with_labels(vec!["www".to_string(), "example".to_string(), "com".to_string()]);
|
||||
let client = Client::new(conn);
|
||||
|
||||
let response = client.query(&name, DNSClass::IN, RecordType::A);
|
||||
assert!(response.is_ok(), "query failed: {}", response.unwrap_err());
|
||||
@ -583,12 +605,36 @@ mod test {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_secure_query_example_nonet() {
|
||||
use ::client::client_connection::test::TestClientConnection;
|
||||
|
||||
let authority = create_secure_example();
|
||||
|
||||
let public_key = {
|
||||
let signers = authority.get_secure_keys();
|
||||
signers.first().expect("expected a key in the authority").get_public_key()
|
||||
};
|
||||
|
||||
let mut catalog = Catalog::new();
|
||||
catalog.upsert(authority.get_origin().clone(), authority);
|
||||
|
||||
let mut trust_anchor = TrustAnchor::new();
|
||||
trust_anchor.insert_trust_anchor(public_key);
|
||||
|
||||
let client = Client::with_trust_anchor(TestClientConnection::new(&catalog), trust_anchor);
|
||||
|
||||
test_secure_query_example(client);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "ftest")]
|
||||
fn test_secure_query_example_udp() {
|
||||
let addr: SocketAddr = ("8.8.8.8",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
let conn = UdpClientConnection::new(addr).unwrap();
|
||||
test_secure_query_example(conn);
|
||||
let client = Client::new(conn);
|
||||
|
||||
test_secure_query_example(client);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -596,21 +642,22 @@ mod test {
|
||||
fn test_secure_query_example_tcp() {
|
||||
let addr: SocketAddr = ("8.8.8.8",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
let conn = TcpClientConnection::new(addr).unwrap();
|
||||
test_secure_query_example(conn);
|
||||
let client = Client::new(conn);
|
||||
|
||||
test_secure_query_example(client);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "ftest")]
|
||||
fn test_secure_query_example<C: ClientConnection>(conn: C) {
|
||||
fn test_secure_query_example<C: ClientConnection>(client: Client<C>) {
|
||||
let name = domain::Name::with_labels(vec!["www".to_string(), "example".to_string(), "com".to_string()]);
|
||||
let client = Client::new(conn);
|
||||
|
||||
let response = client.secure_query(&name, DNSClass::IN, RecordType::A);
|
||||
assert!(response.is_ok(), "query failed: {}", response.unwrap_err());
|
||||
|
||||
assert!(response.is_ok(), "query for {} failed: {}", name, response.unwrap_err());
|
||||
|
||||
let response = response.unwrap();
|
||||
|
||||
println!("response records: {:?}", response);
|
||||
assert!(response.get_edns().expect("edns not here").is_dnssec_ok());
|
||||
|
||||
let record = &response.get_answers()[0];
|
||||
assert_eq!(record.get_name(), &name);
|
||||
|
@ -20,3 +20,46 @@ pub trait ClientConnection: Sized+Debug {
|
||||
fn send(&mut self, bytes: Vec<u8>) -> ClientResult<Vec<u8>>;
|
||||
// TODO: split send and read...
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use std::fmt;
|
||||
use super::*;
|
||||
use ::op::Message;
|
||||
use ::authority::Catalog;
|
||||
use ::serialize::binary::{BinDecoder, BinEncoder, BinSerializable};
|
||||
use ::error::*;
|
||||
|
||||
pub struct TestClientConnection<'a> {
|
||||
catalog: &'a Catalog
|
||||
}
|
||||
|
||||
impl<'a> TestClientConnection<'a> {
|
||||
pub fn new(catalog: &'a Catalog) -> TestClientConnection<'a> {
|
||||
TestClientConnection { catalog: catalog }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ClientConnection for TestClientConnection<'a> {
|
||||
fn send(&mut self, bytes: Vec<u8>) -> ClientResult<Vec<u8>> {
|
||||
let mut decoder = BinDecoder::new(&bytes);
|
||||
|
||||
let message = try!(Message::read(&mut decoder));
|
||||
let response = self.catalog.handle_request(&message);
|
||||
|
||||
let mut buf = Vec::with_capacity(512);
|
||||
{
|
||||
let mut encoder = BinEncoder::new(&mut buf);
|
||||
try!(response.emit(&mut encoder));
|
||||
}
|
||||
|
||||
Ok(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Debug for TestClientConnection<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "TestClientConnection catalog")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,3 +22,5 @@ mod client_connection;
|
||||
|
||||
pub use self::client::Client;
|
||||
pub use self::client_connection::ClientConnection;
|
||||
#[cfg(test)]
|
||||
pub use self::client_connection::test::TestClientConnection;
|
||||
|
@ -13,14 +13,19 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
//! signer is a structure for performing many of the signing processes of the DNSSec specification
|
||||
|
||||
use openssl::crypto::pkey::{PKey, Role};
|
||||
|
||||
use ::op::Message;
|
||||
use ::rr::dnssec::{Algorithm, DigestType};
|
||||
use ::rr::{DNSClass, Name, Record, RecordType, RData};
|
||||
use ::serialize::binary::{BinEncoder, BinSerializable};
|
||||
use ::rr::rdata::sig;
|
||||
use ::rr::rdata::{sig, DNSKEY};
|
||||
|
||||
|
||||
/// Use for performing signing and validation of DNSSec based components.
|
||||
pub struct Signer {
|
||||
algorithm: Algorithm,
|
||||
pkey: PKey,
|
||||
@ -30,10 +35,12 @@ pub struct Signer {
|
||||
}
|
||||
|
||||
impl Signer {
|
||||
/// Version of Signer for verifying RRSIGs and SIG0 records.
|
||||
pub fn new_verifier(algorithm: Algorithm, pkey: PKey, signer_name: Name) -> Self {
|
||||
Signer{ algorithm: algorithm, pkey: pkey, signer_name: signer_name, expiration: 0, inception: 0 }
|
||||
}
|
||||
|
||||
/// Version of Signer for signing RRSIGs and SIG0 records.
|
||||
pub fn new(algorithm: Algorithm, pkey: PKey, signer_name: Name, expiration: u32, inception: u32) -> Self {
|
||||
Signer{ algorithm: algorithm, pkey: pkey, signer_name: signer_name, expiration: expiration, inception: inception }
|
||||
}
|
||||
@ -42,54 +49,77 @@ impl Signer {
|
||||
pub fn get_signer_name(&self) -> &Name { &self.signer_name }
|
||||
pub fn get_expiration(&self) -> u32 { self.expiration }
|
||||
pub fn get_inception(&self) -> u32 { self.inception }
|
||||
pub fn get_pkey(&self) -> &PKey { &self.pkey }
|
||||
|
||||
// RFC 2535 DNS Security Extensions March 1999
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// Appendix C: Key Tag Calculation
|
||||
//
|
||||
// The key tag field in the SIG RR is just a means of more efficiently
|
||||
// selecting the correct KEY RR to use when there is more than one KEY
|
||||
// RR candidate available, for example, in verifying a signature. It is
|
||||
// possible for more than one candidate key to have the same tag, in
|
||||
// which case each must be tried until one works or all fail. The
|
||||
// following reference implementation of how to calculate the Key Tag,
|
||||
// for all algorithms other than algorithm 1, is in ANSI C. It is coded
|
||||
// for clarity, not efficiency. (See section 4.1.6 for how to determine
|
||||
// the Key Tag of an algorithm 1 key.)
|
||||
//
|
||||
// /* assumes int is at least 16 bits
|
||||
// first byte of the key tag is the most significant byte of return
|
||||
// value
|
||||
// second byte of the key tag is the least significant byte of
|
||||
// return value
|
||||
// */
|
||||
//
|
||||
// int keytag (
|
||||
//
|
||||
// unsigned char key[], /* the RDATA part of the KEY RR */
|
||||
// unsigned int keysize, /* the RDLENGTH */
|
||||
// )
|
||||
// {
|
||||
// long int ac; /* assumed to be 32 bits or larger */
|
||||
//
|
||||
// for ( ac = 0, i = 0; i < keysize; ++i )
|
||||
// ac += (i&1) ? key[i] : key[i]<<8;
|
||||
// ac += (ac>>16) & 0xFFFF;
|
||||
// return ac & 0xFFFF;
|
||||
// }
|
||||
pub fn get_public_key(&self) -> Vec<u8> {
|
||||
self.algorithm.public_key_to_vec(&self.pkey)
|
||||
}
|
||||
|
||||
/// Creates a Record that represents the public key for this Signer
|
||||
pub fn to_dnskey(&self, name: Name, ttl: u32) -> Record {
|
||||
let mut record = Record::with(name.clone(), RecordType::DNSKEY, ttl);
|
||||
record.rdata(RData::DNSKEY(
|
||||
DNSKEY::new(true, true, false,
|
||||
self.algorithm,
|
||||
self.get_public_key())
|
||||
));
|
||||
|
||||
record
|
||||
}
|
||||
|
||||
/// The key tag is calculated as a hash to more quickly lookup a DNSKEY.
|
||||
///
|
||||
/// [RFC 1035, DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION, November 1987](https://tools.ietf.org/html/rfc1035)
|
||||
///
|
||||
/// ```text
|
||||
/// RFC 2535 DNS Security Extensions March 1999
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// Appendix C: Key Tag Calculation
|
||||
///
|
||||
/// The key tag field in the SIG RR is just a means of more efficiently
|
||||
/// selecting the correct KEY RR to use when there is more than one KEY
|
||||
/// RR candidate available, for example, in verifying a signature. It is
|
||||
/// possible for more than one candidate key to have the same tag, in
|
||||
/// which case each must be tried until one works or all fail. The
|
||||
/// following reference implementation of how to calculate the Key Tag,
|
||||
/// for all algorithms other than algorithm 1, is in ANSI C. It is coded
|
||||
/// for clarity, not efficiency. (See section 4.1.6 for how to determine
|
||||
/// the Key Tag of an algorithm 1 key.)
|
||||
///
|
||||
/// /* assumes int is at least 16 bits
|
||||
/// first byte of the key tag is the most significant byte of return
|
||||
/// value
|
||||
/// second byte of the key tag is the least significant byte of
|
||||
/// return value
|
||||
/// */
|
||||
///
|
||||
/// int keytag (
|
||||
///
|
||||
/// unsigned char key[], /* the RDATA part of the KEY RR */
|
||||
/// unsigned int keysize, /* the RDLENGTH */
|
||||
/// )
|
||||
/// {
|
||||
/// long int ac; /* assumed to be 32 bits or larger */
|
||||
///
|
||||
/// for ( ac = 0, i = 0; i < keysize; ++i )
|
||||
/// ac += (i&1) ? key[i] : key[i]<<8;
|
||||
/// ac += (ac>>16) & 0xFFFF;
|
||||
/// return ac & 0xFFFF;
|
||||
/// }
|
||||
/// ```
|
||||
pub fn calculate_key_tag(&self) -> u16 {
|
||||
let mut ac: usize = 0;
|
||||
|
||||
@ -119,6 +149,7 @@ impl Signer {
|
||||
DigestType::from(self.algorithm).hash(&buf)
|
||||
}
|
||||
|
||||
/// ```text
|
||||
/// 4.1.8.1 Calculating Transaction and Request SIGs
|
||||
///
|
||||
/// A response message from a security aware server may optionally
|
||||
@ -155,6 +186,7 @@ impl Signer {
|
||||
///
|
||||
/// Except where needed to authenticate an update or similar privileged
|
||||
/// request, servers are not required to check request SIGs.
|
||||
/// ```
|
||||
/// ---
|
||||
///
|
||||
/// NOTE: In classic RFC style, this is unclear, it implies that each SIG record is not included in
|
||||
|
@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
use std::io::Cursor;
|
||||
use std::default::Default;
|
||||
|
||||
use openssl::crypto::pkey::{PKey, Role};
|
||||
|
||||
@ -21,12 +22,14 @@ use ::rr::dnssec::Algorithm;
|
||||
|
||||
const ROOT_ANCHOR: &'static str = include_str!("Kjqmt7v.pem");
|
||||
|
||||
// TODO: these should also store some information, or more specifically, metadata from the signed
|
||||
// public certificate.
|
||||
pub struct TrustAnchor {
|
||||
pkey: Vec<u8>
|
||||
pkeys: Vec<Vec<u8>>
|
||||
}
|
||||
|
||||
impl TrustAnchor {
|
||||
pub fn new() -> TrustAnchor {
|
||||
impl Default for TrustAnchor {
|
||||
fn default() -> TrustAnchor {
|
||||
let mut cursor = Cursor::new(ROOT_ANCHOR);
|
||||
let pkey = PKey::public_key_from_pem(&mut cursor).expect("Error parsing Kjqmt7v.pem");
|
||||
assert!(pkey.can(Role::Verify));
|
||||
@ -34,10 +37,23 @@ impl TrustAnchor {
|
||||
|
||||
let alg = Algorithm::RSASHA256;
|
||||
|
||||
TrustAnchor{ pkey: alg.public_key_to_vec(&pkey) }
|
||||
TrustAnchor{ pkeys: vec![alg.public_key_to_vec(&pkey)] }
|
||||
}
|
||||
}
|
||||
|
||||
impl TrustAnchor {
|
||||
pub fn new() -> TrustAnchor {
|
||||
TrustAnchor { pkeys: vec![] }
|
||||
}
|
||||
|
||||
pub fn contains(&self, other_key: &[u8]) -> bool {
|
||||
self.pkey == other_key
|
||||
self.pkeys.iter().any(|k|other_key == k as &[u8])
|
||||
}
|
||||
|
||||
/// inserts the trust_anchor to the trusted chain
|
||||
pub fn insert_trust_anchor(&mut self, public_key: Vec<u8>) {
|
||||
if !self.contains(&public_key) {
|
||||
self.pkeys.push(public_key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user