use tracing::debug;
#[cfg(feature = "dnssec")]
use std::time::Duration;
use crate::op::{Message, MessageFinalizer, MessageVerifier};
use crate::proto::error::{ProtoErrorKind, ProtoResult};
use crate::rr::Record;
#[cfg(feature = "dnssec")]
use {
crate::error::DnsSecResult,
crate::proto::rr::dnssec::{tbs, TBS},
crate::rr::dnssec::{Algorithm, KeyPair, Private},
crate::rr::rdata::{DNSSECRData, DNSKEY, KEY, SIG},
crate::rr::{DNSClass, Name, RData, RecordType},
crate::serialize::binary::BinEncoder,
};
#[cfg(feature = "dnssec")]
#[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))]
pub struct SigSigner {
key_rdata: RData,
key: KeyPair<Private>,
algorithm: Algorithm,
signer_name: Name,
sig_duration: Duration,
is_zone_signing_key: bool,
}
#[cfg(not(feature = "dnssec"))]
#[allow(missing_copy_implementations)]
pub struct SigSigner;
#[deprecated(note = "renamed to SigSigner")]
pub type Signer = SigSigner;
#[cfg(feature = "dnssec")]
#[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))]
impl SigSigner {
pub fn dnssec(
key_rdata: DNSKEY,
key: KeyPair<Private>,
signer_name: Name,
sig_duration: Duration,
) -> Self {
let algorithm = key_rdata.algorithm();
let is_zone_signing_key = key_rdata.zone_key();
Self {
key_rdata: key_rdata.into(),
key,
algorithm,
signer_name,
sig_duration,
is_zone_signing_key,
}
}
pub fn sig0(key_rdata: KEY, key: KeyPair<Private>, signer_name: Name) -> Self {
let algorithm = key_rdata.algorithm();
Self {
key_rdata: key_rdata.into(),
key,
algorithm,
signer_name,
sig_duration: Duration::new(0, 0),
is_zone_signing_key: false,
}
}
#[deprecated(note = "use SIG0 or DNSSec constructors")]
pub fn new(
algorithm: Algorithm,
key: KeyPair<Private>,
signer_name: Name,
sig_duration: Duration,
is_zone_signing_key: bool,
_: bool,
) -> Self {
let dnskey = key
.to_dnskey(algorithm)
.expect("something went wrong, use one of the SIG0 or DNSSec constructors");
Self {
key_rdata: dnskey.into(),
key,
algorithm,
signer_name,
sig_duration,
is_zone_signing_key,
}
}
pub fn key(&self) -> &KeyPair<Private> {
&self.key
}
pub fn sig_duration(&self) -> Duration {
self.sig_duration
}
pub fn is_zone_signing_key(&self) -> bool {
self.is_zone_signing_key
}
pub fn sign(&self, tbs: &TBS) -> ProtoResult<Vec<u8>> {
self.key
.sign(self.algorithm, tbs)
.map_err(|e| ProtoErrorKind::Msg(format!("signing error: {}", e)).into())
}
pub fn algorithm(&self) -> Algorithm {
self.algorithm
}
pub fn signer_name(&self) -> &Name {
&self.signer_name
}
pub fn calculate_key_tag(&self) -> ProtoResult<u16> {
let mut bytes: Vec<u8> = Vec::with_capacity(512);
{
let mut e = BinEncoder::new(&mut bytes);
self.key_rdata.emit(&mut e)?;
}
Ok(DNSKEY::calculate_key_tag_internal(&bytes))
}
pub fn sign_message(&self, message: &Message, pre_sig0: &SIG) -> ProtoResult<Vec<u8>> {
tbs::message_tbs(message, pre_sig0).and_then(|tbs| self.sign(&tbs))
}
pub fn to_dnskey(&self) -> DnsSecResult<DNSKEY> {
self.key
.to_public_bytes()
.map(|bytes| DNSKEY::new(self.is_zone_signing_key, true, false, self.algorithm, bytes))
}
pub fn test_key(&self) -> DnsSecResult<()> {
Ok(())
}
}
impl MessageFinalizer for SigSigner {
#[cfg(feature = "dnssec")]
fn finalize_message(
&self,
message: &Message,
current_time: u32,
) -> ProtoResult<(Vec<Record>, Option<MessageVerifier>)> {
debug!("signing message: {:?}", message);
let key_tag: u16 = self.calculate_key_tag()?;
let mut sig0 = Record::new();
sig0.set_ttl(0);
sig0.set_dns_class(DNSClass::ANY);
sig0.set_name(Name::root());
let num_labels = sig0.name().num_labels();
let expiration_time: u32 = current_time + (5 * 60);
sig0.set_rr_type(RecordType::SIG);
let pre_sig0 = SIG::new(
RecordType::ZERO,
self.algorithm(),
num_labels,
0,
expiration_time,
current_time,
key_tag,
self.signer_name().clone(),
Vec::new(),
);
let signature: Vec<u8> = self.sign_message(message, &pre_sig0)?;
sig0.set_data(Some(RData::DNSSEC(DNSSECRData::SIG(
pre_sig0.set_sig(signature),
))));
Ok((vec![sig0], None))
}
#[cfg(not(feature = "dnssec"))]
fn finalize_message(
&self,
_: &Message,
_: u32,
) -> ProtoResult<(Vec<Record>, Option<MessageVerifier>)> {
Err(
ProtoErrorKind::Message("the ring or openssl feature must be enabled for signing")
.into(),
)
}
}
#[cfg(test)]
#[cfg(feature = "openssl")]
mod tests {
#![allow(clippy::dbg_macro, clippy::print_stdout)]
use openssl::bn::BigNum;
use openssl::pkey::Private;
use openssl::rsa::Rsa;
use crate::op::{Message, Query};
use crate::rr::dnssec::*;
use crate::rr::rdata::key::KeyUsage;
use crate::rr::rdata::{DNSSECRData, SIG};
use crate::rr::{DNSClass, Name, Record, RecordType};
use super::*;
fn assert_send_and_sync<T: Send + Sync>() {}
#[test]
fn test_send_and_sync() {
assert_send_and_sync::<SigSigner>();
}
fn pre_sig0(signer: &SigSigner, inception_time: u32, expiration_time: u32) -> SIG {
SIG::new(
RecordType::ZERO,
signer.algorithm(),
0,
0,
expiration_time,
inception_time,
signer.calculate_key_tag().unwrap(),
signer.signer_name().clone(),
Vec::new(),
)
}
#[test]
fn test_sign_and_verify_message_sig0() {
let origin: Name = Name::parse("example.com.", None).unwrap();
let mut question: Message = Message::new();
let mut query: Query = Query::new();
query.set_name(origin);
question.add_query(query);
let rsa = Rsa::generate(2048).unwrap();
let key = KeyPair::from_rsa(rsa).unwrap();
let sig0key = key.to_sig0key(Algorithm::RSASHA256).unwrap();
let signer = SigSigner::sig0(sig0key.clone(), key, Name::root());
let pre_sig0 = pre_sig0(&signer, 0, 300);
let sig = signer.sign_message(&question, &pre_sig0).unwrap();
println!("sig: {:?}", sig);
assert!(!sig.is_empty());
assert!(sig0key.verify_message(&question, &sig, &pre_sig0).is_ok());
assert!(question.sig0().is_empty());
question.finalize(&signer, 0).expect("should have signed");
assert!(!question.sig0().is_empty());
let sig = signer.sign_message(&question, &pre_sig0);
println!("sig after sign: {:?}", sig);
if let Some(RData::DNSSEC(DNSSECRData::SIG(ref sig))) = question.sig0()[0].data() {
assert!(sig0key.verify_message(&question, sig.sig(), sig).is_ok());
}
}
#[test]
#[allow(deprecated)]
fn test_sign_and_verify_rrset() {
let rsa = Rsa::generate(2048).unwrap();
let key = KeyPair::from_rsa(rsa).unwrap();
let sig0key = key
.to_sig0key_with_usage(Algorithm::RSASHA256, KeyUsage::Zone)
.unwrap();
let signer = SigSigner::sig0(sig0key, key, Name::root());
let origin: Name = Name::parse("example.com.", None).unwrap();
let rrsig = Record::new()
.set_name(origin.clone())
.set_ttl(86400)
.set_rr_type(RecordType::NS)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::DNSSEC(DNSSECRData::SIG(SIG::new(
RecordType::NS,
Algorithm::RSASHA256,
origin.num_labels(),
86400,
5,
0,
signer.calculate_key_tag().unwrap(),
origin.clone(),
vec![],
)))))
.clone();
let rrset = vec![
Record::new()
.set_name(origin.clone())
.set_ttl(86400)
.set_rr_type(RecordType::NS)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::NS(
Name::parse("a.iana-servers.net.", None).unwrap(),
)))
.clone(),
Record::new()
.set_name(origin)
.set_ttl(86400)
.set_rr_type(RecordType::NS)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::NS(
Name::parse("b.iana-servers.net.", None).unwrap(),
)))
.clone(),
];
let tbs = tbs::rrset_tbs_with_rrsig(&rrsig, &rrset).unwrap();
let sig = signer.sign(&tbs).unwrap();
let pub_key = signer.key().to_public_bytes().unwrap();
let pub_key = PublicKeyEnum::from_public_bytes(&pub_key, Algorithm::RSASHA256).unwrap();
assert!(pub_key
.verify(Algorithm::RSASHA256, tbs.as_ref(), &sig)
.is_ok());
}
fn get_rsa_from_vec(params: &[u32]) -> Result<Rsa<Private>, openssl::error::ErrorStack> {
Rsa::from_private_components(
BigNum::from_u32(params[0]).unwrap(), BigNum::from_u32(params[1]).unwrap(), BigNum::from_u32(params[2]).unwrap(), BigNum::from_u32(params[3]).unwrap(), BigNum::from_u32(params[4]).unwrap(), BigNum::from_u32(params[5]).unwrap(), BigNum::from_u32(params[6]).unwrap(), BigNum::from_u32(params[7]).unwrap(), )
}
#[test]
#[allow(deprecated)]
#[allow(clippy::unreadable_literal)]
fn test_calculate_key_tag() {
let test_vectors = vec![
(vec![33, 3, 21, 11, 3, 1, 1, 1], 9739),
(
vec![
0xc2fedb69, 0x10001, 0x6ebb9209, 0xf743, 0xc9e3, 0xd07f, 0x6275, 0x1095,
],
42354,
),
];
for &(ref input_data, exp_result) in test_vectors.iter() {
let rsa = get_rsa_from_vec(input_data).unwrap();
let rsa_pem = rsa.private_key_to_pem().unwrap();
println!("pkey:\n{}", String::from_utf8(rsa_pem).unwrap());
let key = KeyPair::from_rsa(rsa).unwrap();
let sig0key = key
.to_sig0key_with_usage(Algorithm::RSASHA256, KeyUsage::Zone)
.unwrap();
let signer = SigSigner::sig0(sig0key, key, Name::root());
let key_tag = signer.calculate_key_tag().unwrap();
assert_eq!(key_tag, exp_result);
}
}
#[test]
#[allow(deprecated)]
fn test_calculate_key_tag_pem() {
let x = "-----BEGIN RSA PRIVATE KEY-----
MC0CAQACBQC+L6pNAgMBAAECBQCYj0ZNAgMA9CsCAwDHZwICeEUCAnE/AgMA3u0=
-----END RSA PRIVATE KEY-----
";
let rsa = Rsa::private_key_from_pem(x.as_bytes()).unwrap();
let rsa_pem = rsa.private_key_to_pem().unwrap();
println!("pkey:\n{}", String::from_utf8(rsa_pem).unwrap());
let key = KeyPair::from_rsa(rsa).unwrap();
let sig0key = key
.to_sig0key_with_usage(Algorithm::RSASHA256, KeyUsage::Zone)
.unwrap();
let signer = SigSigner::sig0(sig0key, key, Name::root());
let key_tag = signer.calculate_key_tag().unwrap();
assert_eq!(key_tag, 28551);
}
#[cfg(feature = "openssl")]
#[allow(clippy::module_inception)]
#[cfg(test)]
mod tests {
use openssl::rsa::Rsa;
use crate::rr::dnssec::tbs::*;
use crate::rr::dnssec::*;
use crate::rr::rdata::{DNSSECRData, SIG};
use crate::rr::*;
#[test]
fn test_rrset_tbs() {
let rsa = Rsa::generate(2048).unwrap();
let key = KeyPair::from_rsa(rsa).unwrap();
let sig0key = key.to_sig0key(Algorithm::RSASHA256).unwrap();
let signer = SigSigner::sig0(sig0key, key, Name::root());
let origin: Name = Name::parse("example.com.", None).unwrap();
let rrsig = Record::new()
.set_name(origin.clone())
.set_ttl(86400)
.set_rr_type(RecordType::NS)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::DNSSEC(DNSSECRData::SIG(SIG::new(
RecordType::NS,
Algorithm::RSASHA256,
origin.num_labels(),
86400,
5,
0,
signer.calculate_key_tag().unwrap(),
origin.clone(),
vec![],
)))))
.clone();
let rrset = vec![
Record::new()
.set_name(origin.clone())
.set_ttl(86400)
.set_rr_type(RecordType::NS)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::NS(
Name::parse("a.iana-servers.net.", None).unwrap(),
)))
.clone(),
Record::new()
.set_name(origin.clone())
.set_ttl(86400)
.set_rr_type(RecordType::NS)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::NS(
Name::parse("b.iana-servers.net.", None).unwrap(),
)))
.clone(),
];
let tbs = rrset_tbs_with_rrsig(&rrsig, &rrset).unwrap();
assert!(!tbs.as_ref().is_empty());
let rrset = vec![
Record::new()
.set_name(origin.clone())
.set_ttl(86400)
.set_rr_type(RecordType::CNAME)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::CNAME(
Name::parse("a.iana-servers.net.", None).unwrap(),
)))
.clone(), Record::new()
.set_name(Name::parse("www.example.com.", None).unwrap())
.set_ttl(86400)
.set_rr_type(RecordType::NS)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::NS(
Name::parse("a.iana-servers.net.", None).unwrap(),
)))
.clone(), Record::new()
.set_name(origin.clone())
.set_ttl(86400)
.set_rr_type(RecordType::NS)
.set_dns_class(DNSClass::CH)
.set_data(Some(RData::NS(
Name::parse("a.iana-servers.net.", None).unwrap(),
)))
.clone(), Record::new()
.set_name(origin.clone())
.set_ttl(86400)
.set_rr_type(RecordType::NS)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::NS(
Name::parse("a.iana-servers.net.", None).unwrap(),
)))
.clone(),
Record::new()
.set_name(origin)
.set_ttl(86400)
.set_rr_type(RecordType::NS)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::NS(
Name::parse("b.iana-servers.net.", None).unwrap(),
)))
.clone(),
];
let filtered_tbs = rrset_tbs_with_rrsig(&rrsig, &rrset).unwrap();
assert!(!filtered_tbs.as_ref().is_empty());
assert_eq!(tbs.as_ref(), filtered_tbs.as_ref());
}
}
}