#![cfg(feature = "tsig")]
#![cfg_attr(docsrs, doc(cfg(feature = "tsig")))]
mod interop;
use crate::base::header::HeaderSection;
use crate::base::iana::{Class, Rcode, TsigRcode};
use crate::base::message::Message;
use crate::base::message_builder::{AdditionalBuilder, MessageBuilder};
use crate::base::name::{Dname, Label, ParsedDname, ToDname, ToLabelIter};
use crate::base::octets::{
OctetsBuilder, OctetsRef, OctetsVec, ParseError, ShortBuf,
};
use crate::base::record::Record;
use crate::rdata::rfc2845::{Time48, Tsig};
use bytes::{Bytes, BytesMut};
use ring::{constant_time, hkdf::KeyType, hmac, rand};
use std::collections::HashMap;
use std::{cmp, error, fmt, hash, mem, str};
#[derive(Debug)]
pub struct Key {
key: hmac::Key,
name: Dname<OctetsVec>,
min_mac_len: usize,
signing_len: usize,
}
impl Key {
pub fn new(
algorithm: Algorithm,
key: &[u8],
name: Dname<OctetsVec>,
min_mac_len: Option<usize>,
signing_len: Option<usize>,
) -> Result<Self, NewKeyError> {
let (min_mac_len, signing_len) =
Self::calculate_bounds(algorithm, min_mac_len, signing_len)?;
Ok(Key {
key: hmac::Key::new(algorithm.into_hmac_algorithm(), key),
name,
min_mac_len,
signing_len,
})
}
pub fn generate(
algorithm: Algorithm,
rng: &dyn rand::SecureRandom,
name: Dname<OctetsVec>,
min_mac_len: Option<usize>,
signing_len: Option<usize>,
) -> Result<(Self, Bytes), GenerateKeyError> {
let (min_mac_len, signing_len) =
Self::calculate_bounds(algorithm, min_mac_len, signing_len)?;
let algorithm = algorithm.into_hmac_algorithm();
let key_len = algorithm.len();
let mut bytes = BytesMut::with_capacity(key_len);
bytes.resize(key_len, 0);
rng.fill(&mut bytes)?;
let key = Key {
key: hmac::Key::new(algorithm, &bytes),
name,
min_mac_len,
signing_len,
};
Ok((key, bytes.freeze()))
}
fn calculate_bounds(
algorithm: Algorithm,
min_mac_len: Option<usize>,
signing_len: Option<usize>,
) -> Result<(usize, usize), NewKeyError> {
let min_mac_len = match min_mac_len {
Some(len) => {
if !algorithm.within_len_bounds(len) {
return Err(NewKeyError::BadMinMacLen);
}
len
}
None => algorithm.native_len(),
};
let signing_len = match signing_len {
Some(len) => {
if !algorithm.within_len_bounds(len) {
return Err(NewKeyError::BadSigningLen);
}
len
}
None => algorithm.native_len(),
};
Ok((min_mac_len, signing_len))
}
fn signing_context(&self) -> hmac::Context {
hmac::Context::with_key(&self.key)
}
fn signature_slice<'a>(&self, signature: &'a hmac::Tag) -> &'a [u8] {
&signature.as_ref()[..self.signing_len]
}
}
impl Key {
pub fn algorithm(&self) -> Algorithm {
Algorithm::from_hmac_algorithm(self.key.algorithm())
}
pub fn name(&self) -> &Dname<OctetsVec> {
&self.name
}
pub fn native_len(&self) -> usize {
self.key.algorithm().len()
}
pub fn min_mac_len(&self) -> usize {
self.min_mac_len
}
pub fn signing_len(&self) -> usize {
self.signing_len
}
fn check_tsig<Octets>(
&self,
tsig: &MessageTsig<Octets>,
) -> Result<(), ValidationError>
where
for<'o> &'o Octets: OctetsRef,
{
if *tsig.owner() != self.name
|| *tsig.data().algorithm() != self.algorithm().to_dname()
{
Err(ValidationError::BadKey)
} else {
Ok(())
}
}
fn compare_signatures(
&self,
expected: &hmac::Tag,
provided: &[u8],
) -> Result<(), ValidationError> {
if provided.len() < self.min_mac_len {
return Err(ValidationError::BadTrunc);
}
let expected = if provided.len() < expected.as_ref().len() {
&expected.as_ref()[..provided.len()]
} else {
expected.as_ref()
};
constant_time::verify_slices_are_equal(expected, provided)
.map_err(|_| ValidationError::BadSig)
}
fn complete_message<Target: OctetsBuilder + AsRef<[u8]> + AsMut<[u8]>>(
&self,
message: &mut AdditionalBuilder<Target>,
variables: &Variables,
mac: &[u8],
) -> Result<(), ShortBuf> {
let id = message.header().id();
variables.push_tsig(self, mac, id, message)
}
}
impl AsRef<Key> for Key {
fn as_ref(&self) -> &Self {
self
}
}
pub trait KeyStore {
type Key: AsRef<Key>;
fn get_key<N: ToDname>(
&self,
name: &N,
algorithm: Algorithm,
) -> Option<Self::Key>;
}
impl<K: AsRef<Key> + Clone> KeyStore for K {
type Key = Self;
fn get_key<N: ToDname>(
&self,
name: &N,
algorithm: Algorithm,
) -> Option<Self::Key> {
if self.as_ref().name() == name
&& self.as_ref().algorithm() == algorithm
{
Some(self.clone())
} else {
None
}
}
}
impl<K, S> KeyStore for HashMap<(Dname<OctetsVec>, Algorithm), K, S>
where
K: AsRef<Key> + Clone,
S: hash::BuildHasher,
{
type Key = K;
fn get_key<N: ToDname>(
&self,
name: &N,
algorithm: Algorithm,
) -> Option<Self::Key> {
let name = name.to_dname::<OctetsVec>().unwrap();
self.get(&(name, algorithm)).cloned()
}
}
#[derive(Clone, Debug)]
pub struct ClientTransaction<K> {
context: SigningContext<K>,
}
impl<K: AsRef<Key>> ClientTransaction<K> {
pub fn request<Target: OctetsBuilder + AsRef<[u8]> + AsMut<[u8]>>(
key: K,
message: &mut AdditionalBuilder<Target>,
) -> Result<Self, ShortBuf> {
Self::request_with_fudge(key, message, 300)
}
pub fn request_with_fudge<Target>(
key: K,
message: &mut AdditionalBuilder<Target>,
fudge: u16,
) -> Result<Self, ShortBuf>
where
Target: OctetsBuilder + AsRef<[u8]> + AsMut<[u8]>,
{
let variables =
Variables::new(Time48::now(), fudge, TsigRcode::NoError, None);
let (mut context, mac) = SigningContext::request(
key,
message.as_slice(),
None,
&variables,
);
let mac = context.key().signature_slice(&mac);
context.apply_signature(mac);
context.key().complete_message(message, &variables, mac)?;
Ok(ClientTransaction { context })
}
pub fn answer<Octets>(
&self,
message: &mut Message<Octets>,
) -> Result<(), ValidationError>
where
Octets: AsRef<[u8]> + AsMut<[u8]>,
for<'a> &'a Octets: OctetsRef,
{
let tsig = match self.context.get_answer_tsig(message)? {
Some(some) => some,
None => return Err(ValidationError::ServerUnsigned),
};
let mut header = message.header_section();
header.header_mut().set_id(tsig.data().original_id());
header.counts_mut().dec_arcount();
let signature = self.context.answer(
header.as_slice(),
Some(
&message.as_slice()
[mem::size_of::<HeaderSection>()..tsig.start],
),
&tsig.variables(),
);
self.context
.key()
.compare_signatures(&signature, tsig.data().mac().as_ref())?;
self.context.check_answer_time(message, &tsig)?;
remove_tsig(tsig.into_original_id(), message);
Ok(())
}
pub fn key(&self) -> &Key {
self.context.key()
}
}
#[derive(Clone, Debug)]
pub struct ServerTransaction<K> {
context: SigningContext<K>,
}
impl<K: AsRef<Key>> ServerTransaction<K> {
pub fn request<Store, Octets>(
store: &Store,
message: &mut Message<Octets>,
) -> Result<Option<Self>, ServerError<K>>
where
Store: KeyStore<Key = K>,
Octets: AsRef<[u8]> + AsMut<[u8]>,
for<'o> &'o Octets: OctetsRef,
{
SigningContext::server_request(store, message).map(|context| {
context.map(|context| ServerTransaction { context })
})
}
pub fn answer<Target: OctetsBuilder + AsRef<[u8]> + AsMut<[u8]>>(
self,
message: &mut AdditionalBuilder<Target>,
) -> Result<(), ShortBuf> {
self.answer_with_fudge(message, 300)
}
pub fn answer_with_fudge<Target>(
self,
message: &mut AdditionalBuilder<Target>,
fudge: u16,
) -> Result<(), ShortBuf>
where
Target: OctetsBuilder + AsRef<[u8]> + AsMut<[u8]>,
{
let variables =
Variables::new(Time48::now(), fudge, TsigRcode::NoError, None);
let (mac, key) =
self.context
.final_answer(message.as_slice(), None, &variables);
let mac = key.as_ref().signature_slice(&mac);
key.as_ref().complete_message(message, &variables, mac)
}
pub fn key(&self) -> &Key {
self.context.key()
}
}
#[derive(Clone, Debug)]
pub struct ClientSequence<K> {
context: SigningContext<K>,
first: bool,
unsigned: usize,
}
impl<K: AsRef<Key>> ClientSequence<K> {
pub fn request<Target: OctetsBuilder + AsRef<[u8]> + AsMut<[u8]>>(
key: K,
message: &mut AdditionalBuilder<Target>,
) -> Result<Self, ShortBuf> {
Self::request_with_fudge(key, message, 300)
}
pub fn request_with_fudge<Target>(
key: K,
message: &mut AdditionalBuilder<Target>,
fudge: u16,
) -> Result<Self, ShortBuf>
where
Target: OctetsBuilder + AsRef<[u8]> + AsMut<[u8]>,
{
let variables =
Variables::new(Time48::now(), fudge, TsigRcode::NoError, None);
let (mut context, mac) = SigningContext::request(
key,
message.as_slice(),
None,
&variables,
);
let mac = context.key().signature_slice(&mac);
context.apply_signature(mac);
context.key().complete_message(message, &variables, mac)?;
Ok(ClientSequence {
context,
first: true,
unsigned: 0,
})
}
pub fn answer<Octets>(
&mut self,
message: &mut Message<Octets>,
) -> Result<(), ValidationError>
where
Octets: AsRef<[u8]> + AsMut<[u8]>,
for<'a> &'a Octets: OctetsRef,
{
if self.first {
self.answer_first(message)
} else {
self.answer_subsequent(message)
}
}
pub fn done(self) -> Result<(), ValidationError> {
if self.unsigned != 0 {
Err(ValidationError::TooManyUnsigned)
} else {
Ok(())
}
}
fn answer_first<Octets>(
&mut self,
message: &mut Message<Octets>,
) -> Result<(), ValidationError>
where
Octets: AsRef<[u8]> + AsMut<[u8]>,
for<'a> &'a Octets: OctetsRef,
{
let tsig = match self.context.get_answer_tsig(message)? {
Some(some) => some,
None => return Err(ValidationError::ServerUnsigned),
};
let mut header = message.header_section();
header.header_mut().set_id(tsig.data().original_id());
header.counts_mut().dec_arcount();
let signature = self.context.first_answer(
header.as_slice(),
Some(
&message.as_slice()
[mem::size_of::<HeaderSection>()..tsig.start],
),
&tsig.variables(),
);
self.context
.key()
.compare_signatures(&signature, tsig.data().mac().as_ref())?;
self.context.apply_signature(tsig.data().mac().as_ref());
self.context.check_answer_time(message, &tsig)?;
self.first = false;
remove_tsig(tsig.into_original_id(), message);
Ok(())
}
fn answer_subsequent<Octets>(
&mut self,
message: &mut Message<Octets>,
) -> Result<(), ValidationError>
where
Octets: AsRef<[u8]> + AsMut<[u8]>,
for<'a> &'a Octets: OctetsRef,
{
let tsig = match self.context.get_answer_tsig(message)? {
Some(tsig) => tsig,
None => {
if self.unsigned < 99 {
self.context.unsigned_subsequent(message.as_slice());
self.unsigned += 1;
return Ok(());
} else {
return Err(ValidationError::TooManyUnsigned);
}
}
};
let mut header = message.header_section();
header.header_mut().set_id(tsig.data().original_id());
header.counts_mut().dec_arcount();
let signature = self.context.signed_subsequent(
header.as_slice(),
Some(
&message.as_slice()
[mem::size_of::<HeaderSection>()..tsig.start],
),
&tsig.variables(),
);
self.context
.key()
.compare_signatures(&signature, tsig.data().mac().as_ref())?;
self.context.apply_signature(tsig.data().mac().as_ref());
self.context.check_answer_time(message, &tsig)?;
self.unsigned = 0;
remove_tsig(tsig.into_original_id(), message);
Ok(())
}
pub fn key(&self) -> &Key {
self.context.key()
}
}
#[derive(Clone, Debug)]
pub struct ServerSequence<K> {
context: SigningContext<K>,
first: bool,
}
impl<K: AsRef<Key>> ServerSequence<K> {
pub fn request<Store, Octets>(
store: &Store,
message: &mut Message<Octets>,
) -> Result<Option<Self>, ServerError<K>>
where
Store: KeyStore<Key = K>,
Octets: AsRef<[u8]> + AsMut<[u8]>,
for<'o> &'o Octets: OctetsRef,
{
SigningContext::server_request(store, message).map(|context| {
context.map(|context| ServerSequence {
context,
first: false,
})
})
}
pub fn answer<Target: OctetsBuilder + AsRef<[u8]> + AsMut<[u8]>>(
&mut self,
message: &mut AdditionalBuilder<Target>,
) -> Result<(), ShortBuf> {
self.answer_with_fudge(message, 300)
}
pub fn answer_with_fudge<Target>(
&mut self,
message: &mut AdditionalBuilder<Target>,
fudge: u16,
) -> Result<(), ShortBuf>
where
Target: OctetsBuilder + AsRef<[u8]> + AsMut<[u8]>,
{
let variables =
Variables::new(Time48::now(), fudge, TsigRcode::NoError, None);
let mac = if self.first {
self.first = false;
self.context
.first_answer(message.as_slice(), None, &variables)
} else {
self.context.signed_subsequent(
message.as_slice(),
None,
&variables,
)
};
let mac = self.key().signature_slice(&mac);
self.key().complete_message(message, &variables, mac)
}
pub fn key(&self) -> &Key {
self.context.key()
}
}
#[derive(Clone, Debug)]
struct SigningContext<K> {
context: hmac::Context,
key: K,
}
impl<K: AsRef<Key>> SigningContext<K> {
fn server_request<Store, Octets>(
store: &Store,
message: &mut Message<Octets>,
) -> Result<Option<Self>, ServerError<Store::Key>>
where
Store: KeyStore<Key = K>,
Octets: AsRef<[u8]> + AsMut<[u8]>,
for<'a> &'a Octets: OctetsRef,
{
let tsig = match MessageTsig::from_message(message) {
Some(tsig) => tsig,
None => return Ok(None),
};
let algorithm = match Algorithm::from_dname(tsig.data().algorithm()) {
Some(algorithm) => algorithm,
None => return Err(ServerError::unsigned(TsigRcode::BadKey)),
};
let key = match store.get_key(tsig.owner(), algorithm) {
Some(key) => key,
None => return Err(ServerError::unsigned(TsigRcode::BadKey)),
};
let variables = tsig.variables();
let mut header = message.header_section();
header.header_mut().set_id(tsig.data().original_id());
header.counts_mut().dec_arcount();
let (mut context, signature) = Self::request(
key,
header.as_slice(),
Some(
&message.as_slice()
[mem::size_of::<HeaderSection>()..tsig.start],
),
&variables,
);
let res = context
.key
.as_ref()
.compare_signatures(&signature, tsig.data().mac().as_ref());
if let Err(err) = res {
return Err(ServerError::unsigned(match err {
ValidationError::BadTrunc => TsigRcode::BadTrunc,
ValidationError::BadKey => TsigRcode::BadKey,
_ => TsigRcode::FormErr,
}));
}
context.apply_signature(tsig.data().mac().as_ref());
if !tsig.data().is_valid_now() {
return Err(ServerError::signed(
context,
Variables::new(
variables.time_signed,
variables.fudge,
TsigRcode::BadTime,
Some(Time48::now()),
),
));
}
remove_tsig(tsig.into_original_id(), message);
Ok(Some(context))
}
fn get_answer_tsig<'a, Octets>(
&self,
message: &'a Message<Octets>,
) -> Result<Option<MessageTsig<'a, Octets>>, ValidationError>
where
Octets: AsRef<[u8]>,
for<'o> &'o Octets: OctetsRef,
{
let tsig = match MessageTsig::from_message(message) {
Some(tsig) => tsig,
None => return Ok(None),
};
if message.header().rcode() == Rcode::NotAuth {
if tsig.data().error() == TsigRcode::BadKey {
return Err(ValidationError::ServerBadKey);
}
if tsig.data().error() == TsigRcode::BadSig {
return Err(ValidationError::ServerBadSig);
}
}
self.key().check_tsig(&tsig)?;
Ok(Some(tsig))
}
fn check_answer_time<'a, Octets>(
&self,
message: &'a Message<Octets>,
tsig: &MessageTsig<'a, Octets>,
) -> Result<(), ValidationError>
where
Octets: AsRef<[u8]>,
for<'o> &'o Octets: OctetsRef,
{
if message.header().rcode() == Rcode::NotAuth
&& tsig.data().error() == TsigRcode::BadTime
{
let server = match tsig.data().other_time() {
Some(time) => time,
None => return Err(ValidationError::FormErr),
};
return Err(ValidationError::ServerBadTime {
client: tsig.data().time_signed(),
server,
});
}
if !tsig.data().is_valid_now() {
return Err(ValidationError::BadTime);
}
Ok(())
}
}
impl<K: AsRef<Key>> SigningContext<K> {
fn new(key: K) -> Self {
SigningContext {
context: key.as_ref().signing_context(),
key,
}
}
fn key(&self) -> &Key {
self.key.as_ref()
}
fn apply_signature(&mut self, data: &[u8]) {
self.context.update(&(data.len() as u16).to_be_bytes());
self.context.update(data);
}
fn request(
key: K,
first: &[u8],
second: Option<&[u8]>,
variables: &Variables,
) -> (Self, hmac::Tag) {
let mut context = key.as_ref().signing_context();
context.update(first);
if let Some(second) = second {
context.update(second)
}
variables.sign(key.as_ref(), &mut context);
let signature = context.sign();
(Self::new(key), signature)
}
fn answer(
&self,
first: &[u8],
second: Option<&[u8]>,
variables: &Variables,
) -> hmac::Tag {
let mut context = self.context.clone();
context.update(first);
if let Some(second) = second {
context.update(second)
}
variables.sign(self.key.as_ref(), &mut context);
context.sign()
}
fn final_answer(
mut self,
first: &[u8],
second: Option<&[u8]>,
variables: &Variables,
) -> (hmac::Tag, K) {
self.context.update(first);
if let Some(second) = second {
self.context.update(second)
}
variables.sign(self.key.as_ref(), &mut self.context);
(self.context.sign(), self.key)
}
fn first_answer(
&mut self,
first: &[u8],
second: Option<&[u8]>,
variables: &Variables,
) -> hmac::Tag {
let mut context = self.key().signing_context();
mem::swap(&mut self.context, &mut context);
context.update(first);
if let Some(second) = second {
context.update(second)
}
variables.sign(self.key.as_ref(), &mut context);
context.sign()
}
fn unsigned_subsequent(&mut self, message: &[u8]) {
self.context.update(message)
}
fn signed_subsequent(
&mut self,
first: &[u8],
second: Option<&[u8]>,
variables: &Variables,
) -> hmac::Tag {
let mut context = self.key().signing_context();
mem::swap(&mut self.context, &mut context);
context.update(first);
if let Some(second) = second {
context.update(second)
}
variables.sign_timers(&mut context);
context.sign()
}
}
struct MessageTsig<'a, Octets>
where
for<'o> &'o Octets: OctetsRef,
{
record: Record<
ParsedDname<&'a Octets>,
Tsig<<&'a Octets as OctetsRef>::Range, ParsedDname<&'a Octets>>,
>,
start: usize,
}
impl<'a, Octets> MessageTsig<'a, Octets>
where
for<'o> &'o Octets: OctetsRef,
{
fn from_message(msg: &'a Message<Octets>) -> Option<Self>
where
Octets: AsRef<[u8]>,
for<'o> &'o Octets: OctetsRef,
{
let mut section = msg.additional().ok()?;
let mut start = section.pos();
let mut record = section.next()?;
loop {
record = match section.next() {
Some(record) => record,
None => break,
};
start = section.pos();
}
record
.ok()?
.into_record::<Tsig<_, _>>()
.ok()?
.map(|record| MessageTsig { record, start })
}
fn variables(&self) -> Variables {
Variables::new(
self.record.data().time_signed(),
self.record.data().fudge(),
self.record.data().error(),
self.record.data().other_time(),
)
}
fn into_original_id(self) -> u16 {
self.record.data().original_id()
}
}
impl<'a, Octets> std::ops::Deref for MessageTsig<'a, Octets>
where
for<'o> &'o Octets: OctetsRef,
{
type Target = Record<
ParsedDname<&'a Octets>,
Tsig<<&'a Octets as OctetsRef>::Range, ParsedDname<&'a Octets>>,
>;
fn deref(&self) -> &Self::Target {
&self.record
}
}
#[derive(Clone, Debug)]
struct Variables {
time_signed: Time48,
fudge: u16,
error: TsigRcode,
other: Option<Time48>,
}
impl Variables {
fn new(
time_signed: Time48,
fudge: u16,
error: TsigRcode,
other: Option<Time48>,
) -> Self {
Variables {
time_signed,
fudge,
error,
other,
}
}
fn push_tsig<Target: OctetsBuilder + AsMut<[u8]>>(
&self,
key: &Key,
hmac: &[u8],
original_id: u16,
builder: &mut AdditionalBuilder<Target>,
) -> Result<(), ShortBuf> {
let other = self.other.map(Time48::into_octets);
let other = match other {
Some(ref time) => time.as_ref(),
None => b"",
};
builder.push((
key.name.clone(),
Class::Any,
0,
Tsig::new(
key.algorithm().to_dname(),
self.time_signed,
self.fudge,
hmac,
original_id,
self.error,
other,
),
))
}
fn sign(&self, key: &Key, context: &mut hmac::Context) {
for label in key.name.iter_labels().map(Label::to_canonical) {
context.update(label.as_wire_slice());
}
context.update(&Class::Any.to_int().to_be_bytes());
context.update(&0u32.to_be_bytes());
context.update(key.algorithm().into_wire_slice());
context.update(&self.time_signed.into_octets());
context.update(&self.fudge.to_be_bytes());
context.update(&self.error.to_int().to_be_bytes());
if self.other.is_some() {
context.update(&6u16.to_be_bytes());
} else {
context.update(&0u16.to_be_bytes());
}
if let Some(time) = self.other {
context.update(&u64::from(time).to_be_bytes());
}
}
fn sign_timers(&self, context: &mut hmac::Context) {
context.update(&self.time_signed.into_octets());
context.update(&self.fudge.to_be_bytes());
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Algorithm {
Sha1,
Sha256,
Sha384,
Sha512,
}
impl Algorithm {
pub fn from_dname<N: ToDname>(name: &N) -> Option<Self> {
let mut labels = name.iter_labels();
let first = match labels.next() {
Some(label) => label,
None => return None,
};
match labels.next() {
Some(label) if label.is_root() => {}
_ => return None,
}
match first.as_slice() {
b"hmac-sha1" => Some(Algorithm::Sha1),
b"hmac-sha256" => Some(Algorithm::Sha256),
b"hmac-sha384" => Some(Algorithm::Sha384),
b"hmac-sha512" => Some(Algorithm::Sha512),
_ => None,
}
}
fn from_hmac_algorithm(alg: hmac::Algorithm) -> Self {
if alg == hmac::HMAC_SHA1_FOR_LEGACY_USE_ONLY {
Algorithm::Sha1
} else if alg == hmac::HMAC_SHA256 {
Algorithm::Sha256
} else if alg == hmac::HMAC_SHA384 {
Algorithm::Sha384
} else if alg == hmac::HMAC_SHA512 {
Algorithm::Sha512
} else {
panic!("Unknown TSIG key algorithm.")
}
}
fn into_hmac_algorithm(self) -> hmac::Algorithm {
match self {
Algorithm::Sha1 => hmac::HMAC_SHA1_FOR_LEGACY_USE_ONLY,
Algorithm::Sha256 => hmac::HMAC_SHA256,
Algorithm::Sha384 => hmac::HMAC_SHA384,
Algorithm::Sha512 => hmac::HMAC_SHA512,
}
}
fn into_wire_slice(self) -> &'static [u8] {
match self {
Algorithm::Sha1 => b"\x09hmac-sha1\0",
Algorithm::Sha256 => b"\x0Bhmac-sha256\0",
Algorithm::Sha384 => b"\x0Bhmac-sha384\0",
Algorithm::Sha512 => b"\x0Bhmac-sha512\0",
}
}
pub fn to_dname(self) -> Dname<&'static [u8]> {
unsafe { Dname::from_octets_unchecked(self.into_wire_slice()) }
}
pub fn native_len(self) -> usize {
self.into_hmac_algorithm().len()
}
pub fn within_len_bounds(self, len: usize) -> bool {
len >= cmp::max(10, self.native_len() / 2) && len <= self.native_len()
}
}
impl str::FromStr for Algorithm {
type Err = AlgorithmError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"hmac-sha1" => Ok(Algorithm::Sha1),
"hmac-sha256" => Ok(Algorithm::Sha256),
"hmac-sha384" => Ok(Algorithm::Sha384),
"hmac-sha512" => Ok(Algorithm::Sha512),
_ => Err(AlgorithmError),
}
}
}
impl fmt::Display for Algorithm {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match *self {
Algorithm::Sha1 => "hmac-sha1",
Algorithm::Sha256 => "hmac-sha256",
Algorithm::Sha384 => "hmac-sha384",
Algorithm::Sha512 => "hmac-sha512",
}
)
}
}
fn remove_tsig<Octets>(original_id: u16, message: &mut Message<Octets>)
where
Octets: AsRef<[u8]> + AsMut<[u8]>,
for<'o> &'o Octets: OctetsRef,
{
message.header_mut().set_id(original_id);
message.remove_last_additional();
}
#[derive(Clone)]
pub struct ServerError<K>(ServerErrorInner<K>);
#[derive(Clone)]
enum ServerErrorInner<K> {
Unsigned { error: TsigRcode },
Signed {
context: SigningContext<K>,
variables: Variables,
},
}
impl<K> ServerError<K> {
fn unsigned(error: TsigRcode) -> Self {
ServerError(ServerErrorInner::Unsigned { error })
}
fn signed(context: SigningContext<K>, variables: Variables) -> Self {
ServerError(ServerErrorInner::Signed { context, variables })
}
pub fn error(&self) -> TsigRcode {
match self.0 {
ServerErrorInner::Unsigned { error } => error,
ServerErrorInner::Signed { ref variables, .. } => variables.error,
}
}
}
impl<K: AsRef<Key>> ServerError<K> {
pub fn build_message<Octets, Target>(
self,
msg: &Message<Octets>,
builder: MessageBuilder<Target>,
) -> Result<AdditionalBuilder<Target>, ShortBuf>
where
Octets: AsRef<[u8]>,
for<'a> &'a Octets: OctetsRef,
Target: OctetsBuilder + AsRef<[u8]> + AsMut<[u8]>,
{
let builder = builder.start_answer(msg, Rcode::NotAuth)?;
let mut builder = builder.additional();
match self.0 {
ServerErrorInner::Unsigned { error } => {
let tsig = {
MessageTsig::from_message(msg)
.expect("missing or malformed TSIG record")
};
builder.push((
tsig.owner(),
tsig.class(),
tsig.ttl(),
Tsig::new(
tsig.data().algorithm(),
tsig.data().time_signed(),
tsig.data().fudge(),
b"",
msg.header().id(),
error,
b"",
),
))?;
}
ServerErrorInner::Signed { context, variables } => {
let (mac, key) = context.final_answer(
builder.as_slice(),
None,
&variables,
);
let mac = key.as_ref().signature_slice(&mac);
key.as_ref().complete_message(
&mut builder,
&variables,
mac,
)?;
}
}
Ok(builder)
}
}
impl<K> fmt::Debug for ServerError<K> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("ServerError").field(&self.0).finish()
}
}
impl<K> fmt::Debug for ServerErrorInner<K> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ServerErrorInner::Unsigned { error } => {
f.debug_struct("Unsigned").field("error", &error).finish()
}
ServerErrorInner::Signed { ref variables, .. } => f
.debug_struct("Signed")
.field("variables", variables)
.finish(),
}
}
}
impl<K> fmt::Display for ServerError<K> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.error().fmt(f)
}
}
impl<K> error::Error for ServerError<K> {}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum NewKeyError {
BadMinMacLen,
BadSigningLen,
}
impl fmt::Display for NewKeyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
NewKeyError::BadMinMacLen => {
f.write_str("minimum signature length out of bounds")
}
NewKeyError::BadSigningLen => {
f.write_str("created signature length out of bounds")
}
}
}
}
impl error::Error for NewKeyError {}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum GenerateKeyError {
BadMinMacLen,
BadSigningLen,
GenerationFailed,
}
impl From<NewKeyError> for GenerateKeyError {
fn from(err: NewKeyError) -> Self {
match err {
NewKeyError::BadMinMacLen => GenerateKeyError::BadMinMacLen,
NewKeyError::BadSigningLen => GenerateKeyError::BadSigningLen,
}
}
}
impl From<ring::error::Unspecified> for GenerateKeyError {
fn from(_: ring::error::Unspecified) -> Self {
GenerateKeyError::GenerationFailed
}
}
impl fmt::Display for GenerateKeyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
GenerateKeyError::BadMinMacLen => {
f.write_str("minimum signature length out of bounds")
}
GenerateKeyError::BadSigningLen => {
f.write_str("created signature length out of bounds")
}
GenerateKeyError::GenerationFailed => {
f.write_str("generating key failed")
}
}
}
}
impl error::Error for GenerateKeyError {}
#[derive(Clone, Copy, Debug)]
pub struct AlgorithmError;
impl fmt::Display for AlgorithmError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("invalid algorithm")
}
}
impl error::Error for AlgorithmError {}
#[derive(Clone, Copy, Debug)]
pub enum ValidationError {
BadAlg,
BadOther,
BadSig,
BadTrunc,
BadKey,
BadTime,
FormErr,
ServerUnsigned,
ServerBadKey,
ServerBadSig,
ServerBadTime { client: Time48, server: Time48 },
TooManyUnsigned,
}
impl From<ParseError> for ValidationError {
fn from(_: ParseError) -> Self {
ValidationError::FormErr
}
}
impl fmt::Display for ValidationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ValidationError::BadAlg => f.write_str("unknown algorithm"),
ValidationError::BadOther => {
f.write_str("bad content of 'other' field")
}
ValidationError::BadSig => f.write_str("bad signature"),
ValidationError::BadTrunc => f.write_str("short signature"),
ValidationError::BadKey => f.write_str("unknown key"),
ValidationError::BadTime => f.write_str("bad time"),
ValidationError::FormErr => f.write_str("format error"),
ValidationError::ServerUnsigned => f.write_str("unsigned answer"),
ValidationError::ServerBadKey => {
f.write_str("unknown key on server")
}
ValidationError::ServerBadSig => {
f.write_str("server failed to verify MAC")
}
ValidationError::ServerBadTime { .. } => {
f.write_str("server reported bad time")
}
ValidationError::TooManyUnsigned => {
f.write_str("too many unsigned messages")
}
}
}
}
impl error::Error for ValidationError {}