#![cfg(feature = "tsig")]
#![cfg_attr(docsrs, doc(cfg(feature = "tsig")))]
use core::{cmp, fmt, mem, str};
#[cfg(feature = "std")]
use std::collections::HashMap;
#[cfg(feature = "std")]
use std::sync::Arc;
use bytes::{Bytes, BytesMut};
use constant_time_eq::constant_time_eq;
use octseq::octets::Octets;
use ring::{hkdf::KeyType, hmac, rand};
use crate::base::header::HeaderSection;
use crate::base::iana::{Class, Rcode, TsigRcode};
use crate::base::message::Message;
use crate::base::message_builder::{
AdditionalBuilder, MessageBuilder, PushError,
};
use crate::base::name::{Label, Name, ParsedName, ToLabelIter, ToName};
use crate::base::record::Record;
use crate::base::wire::{Composer, ParseError};
use crate::rdata::tsig::{Time48, Tsig};
pub type KeyName = Name<octseq::array::Array<255>>;
#[derive(Clone, Debug)]
pub struct Key {
key: hmac::Key,
name: KeyName,
min_mac_len: usize,
signing_len: usize,
}
impl Key {
pub fn new(
algorithm: Algorithm,
key: &[u8],
name: KeyName,
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: KeyName,
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) -> &KeyName {
&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
}
pub fn compose_len(&self) -> u16 {
let rdata_len = self.algorithm().to_name().compose_len() + 6 + 2 + 2 + self.signing_len() as u16
+ 2 + 2 + 2;
let rr_len = self.name().compose_len()
+ 2 + 2 + 4 + 2 + rdata_len;
rr_len
}
fn check_tsig<Octs: Octets + ?Sized>(
&self,
tsig: &MessageTsig<'_, Octs>,
) -> Result<(), ValidationError> {
if *tsig.record.owner() != self.name
|| *tsig.record.data().algorithm() != self.algorithm().to_name()
{
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()
};
if !constant_time_eq(expected, provided) {
return Err(ValidationError::BadSig);
}
Ok(())
}
fn complete_message<Target: Composer>(
&self,
message: &mut AdditionalBuilder<Target>,
variables: &Variables,
mac: &[u8],
) -> Result<(), PushError> {
let id = message.header().id();
variables.push_tsig(self, mac, id, message)
}
}
impl AsRef<Key> for Key {
fn as_ref(&self) -> &Self {
self
}
}
#[cfg(feature = "std")]
impl fmt::Display for Key {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.name.fmt(f)
}
}
pub trait KeyStore {
type Key: AsRef<Key>;
fn get_key<N: ToName>(
&self,
name: &N,
algorithm: Algorithm,
) -> Option<Self::Key>;
}
impl<K: AsRef<Key> + Clone> KeyStore for K {
type Key = Self;
fn get_key<N: ToName>(
&self,
name: &N,
algorithm: Algorithm,
) -> Option<Self::Key> {
if self.as_ref().name() == name
&& self.as_ref().algorithm() == algorithm
{
Some(self.clone())
} else {
None
}
}
}
#[cfg(feature = "std")]
impl<K, S> KeyStore for HashMap<(KeyName, Algorithm), K, S>
where
K: AsRef<Key> + Clone,
S: core::hash::BuildHasher,
{
type Key = K;
fn get_key<N: ToName>(
&self,
name: &N,
algorithm: Algorithm,
) -> Option<Self::Key> {
let name = name.try_to_name().ok()?;
self.get(&(name, algorithm)).cloned()
}
}
#[cfg(feature = "std")]
impl KeyStore for Arc<HashMap<(KeyName, Algorithm), Key>> {
type Key = Key;
fn get_key<N: ToName>(
&self,
name: &N,
algorithm: Algorithm,
) -> Option<Self::Key> {
if let Ok(name) = name.try_to_name() {
self.get(&(name, algorithm)).cloned()
} else {
None
}
}
}
#[derive(Clone, Debug)]
pub struct ClientTransaction<K> {
context: SigningContext<K>,
}
impl<K: AsRef<Key>> ClientTransaction<K> {
pub fn request<Target: Composer>(
key: K,
message: &mut AdditionalBuilder<Target>,
now: Time48,
) -> Result<Self, PushError> {
Self::request_with_fudge(key, message, now, 300)
}
pub fn request_with_fudge<Target>(
key: K,
message: &mut AdditionalBuilder<Target>,
now: Time48,
fudge: u16,
) -> Result<Self, PushError>
where
Target: Composer,
{
let variables = Variables::new(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<Octs: Octets + AsMut<[u8]> + ?Sized>(
&self,
message: &mut Message<Octs>,
now: Time48,
) -> Result<(), ValidationError> {
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.record.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.record.data().mac().as_ref(),
)?;
self.context.check_answer_time(message, &tsig, now)?;
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> {
#[allow(clippy::result_large_err)]
pub fn request<Store, Octs>(
store: &Store,
message: &mut Message<Octs>,
now: Time48,
) -> Result<Option<Self>, ServerError<K>>
where
Store: KeyStore<Key = K>,
Octs: Octets + AsMut<[u8]> + ?Sized,
{
SigningContext::server_request(store, message, now).map(|context| {
context.map(|context| ServerTransaction { context })
})
}
pub fn answer<Target: Composer>(
self,
message: &mut AdditionalBuilder<Target>,
now: Time48,
) -> Result<(), PushError> {
self.answer_with_fudge(message, now, 300)
}
pub fn answer_with_fudge<Target>(
self,
message: &mut AdditionalBuilder<Target>,
now: Time48,
fudge: u16,
) -> Result<(), PushError>
where
Target: Composer,
{
let variables = Variables::new(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()
}
pub fn wrapped_key(&self) -> &K {
self.context.wrapped_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: Composer>(
key: K,
message: &mut AdditionalBuilder<Target>,
now: Time48,
) -> Result<Self, PushError> {
Self::request_with_fudge(key, message, now, 300)
}
pub fn request_with_fudge<Target>(
key: K,
message: &mut AdditionalBuilder<Target>,
now: Time48,
fudge: u16,
) -> Result<Self, PushError>
where
Target: Composer,
{
let variables = Variables::new(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<Octs>(
&mut self,
message: &mut Message<Octs>,
now: Time48,
) -> Result<(), ValidationError>
where
Octs: Octets + AsMut<[u8]> + ?Sized,
{
if self.first {
self.answer_first(message, now)
} else {
self.answer_subsequent(message, now)
}
}
pub fn done(self) -> Result<(), ValidationError> {
if self.unsigned != 0 {
Err(ValidationError::TooManyUnsigned)
} else {
Ok(())
}
}
fn answer_first<Octs>(
&mut self,
message: &mut Message<Octs>,
now: Time48,
) -> Result<(), ValidationError>
where
Octs: Octets + AsMut<[u8]> + ?Sized,
{
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.record.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.record.data().mac().as_ref(),
)?;
self.context
.apply_signature(tsig.record.data().mac().as_ref());
self.context.check_answer_time(message, &tsig, now)?;
self.first = false;
remove_tsig(tsig.into_original_id(), message);
Ok(())
}
fn answer_subsequent<Octs>(
&mut self,
message: &mut Message<Octs>,
now: Time48,
) -> Result<(), ValidationError>
where
Octs: Octets + AsMut<[u8]> + ?Sized,
{
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.record.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.record.data().mac().as_ref(),
)?;
self.context
.apply_signature(tsig.record.data().mac().as_ref());
self.context.check_answer_time(message, &tsig, now)?;
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> {
#[allow(clippy::result_large_err)]
pub fn request<Store, Octs>(
store: &Store,
message: &mut Message<Octs>,
now: Time48,
) -> Result<Option<Self>, ServerError<K>>
where
Store: KeyStore<Key = K>,
Octs: Octets + AsMut<[u8]> + ?Sized,
{
SigningContext::server_request(store, message, now).map(|context| {
context.map(|context| ServerSequence {
context,
first: true,
})
})
}
pub fn answer<Target: Composer>(
&mut self,
message: &mut AdditionalBuilder<Target>,
now: Time48,
) -> Result<(), PushError> {
self.answer_with_fudge(message, now, 300)
}
pub fn answer_with_fudge<Target>(
&mut self,
message: &mut AdditionalBuilder<Target>,
now: Time48,
fudge: u16,
) -> Result<(), PushError>
where
Target: Composer,
{
let variables = Variables::new(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,
)
};
self.context.apply_signature(mac.as_ref());
let mac = self.key().signature_slice(&mac);
self.key().complete_message(message, &variables, mac)
}
pub fn key(&self) -> &Key {
self.context.key()
}
pub fn wrapped_key(&self) -> &K {
self.context.wrapped_key()
}
}
impl<K> From<ServerTransaction<K>> for ServerSequence<K> {
fn from(txn: ServerTransaction<K>) -> Self {
Self {
context: txn.context,
first: true,
}
}
}
#[derive(Clone, Debug)]
struct SigningContext<K> {
context: hmac::Context,
key: K,
}
impl<K: AsRef<Key>> SigningContext<K> {
#[allow(clippy::result_large_err)]
fn server_request<Store, Octs>(
store: &Store,
message: &mut Message<Octs>,
now: Time48,
) -> Result<Option<Self>, ServerError<Store::Key>>
where
Store: KeyStore<Key = K>,
Octs: Octets + AsMut<[u8]> + ?Sized,
{
let tsig = match MessageTsig::from_message(message) {
Ok(tsig) => tsig,
Err(TsigError::Position) => {
return Err(ServerError::unsigned(TsigRcode::FORMERR));
}
Err(TsigError::Invalid) => {
return Err(ServerError::unsigned(TsigRcode::FORMERR))
}
Err(TsigError::ParseError) => {
return Err(ServerError::unsigned(TsigRcode::FORMERR))
}
Err(TsigError::Missing) => return Ok(None),
};
let algorithm =
match Algorithm::from_name(tsig.record.data().algorithm()) {
Some(algorithm) => algorithm,
None => return Err(ServerError::unsigned(TsigRcode::BADKEY)),
};
let key = match store.get_key(tsig.record.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.record.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.record.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.record.data().mac().as_ref());
if !tsig.record.data().is_valid_at(now) {
return Err(ServerError::signed(
context,
Variables::new(
variables.time_signed,
variables.fudge,
TsigRcode::BADTIME,
Some(now),
),
));
}
remove_tsig(tsig.into_original_id(), message);
Ok(Some(context))
}
fn get_answer_tsig<'a, Octs>(
&self,
message: &'a Message<Octs>,
) -> Result<Option<MessageTsig<'a, Octs>>, ValidationError>
where
Octs: Octets + ?Sized,
{
let tsig = match MessageTsig::from_message(message) {
Ok(tsig) => tsig,
Err(TsigError::Position) => return Err(ValidationError::FormErr),
Err(TsigError::Invalid) => return Err(ValidationError::FormErr),
Err(TsigError::ParseError) => {
return Err(ValidationError::FormErr)
}
Err(TsigError::Missing) => return Ok(None),
};
if message.header().rcode() == Rcode::NOTAUTH {
if tsig.record.data().error() == TsigRcode::BADKEY {
return Err(ValidationError::ServerBadKey);
}
if tsig.record.data().error() == TsigRcode::BADSIG {
return Err(ValidationError::ServerBadSig);
}
}
self.key().check_tsig(&tsig)?;
Ok(Some(tsig))
}
fn check_answer_time<'a, Octs>(
&self,
message: &'a Message<Octs>,
tsig: &MessageTsig<'a, Octs>,
now: Time48,
) -> Result<(), ValidationError>
where
Octs: Octets + ?Sized,
{
if message.header().rcode() == Rcode::NOTAUTH
&& tsig.record.data().error() == TsigRcode::BADTIME
{
let server = match tsig.record.data().other_time() {
Some(time) => time,
None => return Err(ValidationError::FormErr),
};
return Err(ValidationError::ServerBadTime {
client: tsig.record.data().time_signed(),
server,
});
}
if !tsig.record.data().is_valid_at(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 wrapped_key(&self) -> &K {
&self.key
}
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, Octs: Octets + ?Sized + 'a> {
#[allow(clippy::type_complexity)]
record: Record<
ParsedName<Octs::Range<'a>>,
Tsig<Octs::Range<'a>, ParsedName<Octs::Range<'a>>>,
>,
start: usize,
}
impl<'a, Octs: Octets + ?Sized> MessageTsig<'a, Octs> {
fn from_message(msg: &'a Message<Octs>) -> Result<Self, TsigError> {
let mut section =
msg.additional().map_err(|_| TsigError::ParseError)?;
loop {
let start = section.pos();
let Some(record) = section.next() else {
return Err(TsigError::Missing);
};
let record = record
.map_err(|_| TsigError::ParseError)?
.into_record::<Tsig<_, _>>()
.map_err(|_| TsigError::Invalid)?;
if let Some(record) = record {
if section.next().is_some() {
return Err(TsigError::Position);
}
return Ok(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()
}
}
#[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: Composer>(
&self,
key: &Key,
hmac: &[u8],
original_id: u16,
builder: &mut AdditionalBuilder<Target>,
) -> Result<(), PushError> {
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_name(),
self.time_signed,
self.fudge,
hmac,
original_id,
self.error,
other,
)
.expect("long MAC"),
))
}
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_name<N: ToName>(name: &N) -> Option<Self> {
let mut labels = name.iter_labels();
let first = labels.next()?;
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_name(self) -> Name<&'static [u8]> {
unsafe { Name::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<Octs>(original_id: u16, message: &mut Message<Octs>)
where
Octs: Octets + AsMut<[u8]> + ?Sized,
{
message.header_mut().set_id(original_id);
message.remove_last_additional();
}
#[derive(Clone)]
pub struct ServerError<K>(ServerErrorInner<K>);
#[allow(clippy::large_enum_variant)]
#[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<Octs, Target>(
self,
msg: &Message<Octs>,
builder: MessageBuilder<Target>,
) -> Result<AdditionalBuilder<Target>, PushError>
where
Octs: Octets + ?Sized,
Target: Composer,
{
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.record.owner(),
tsig.record.class(),
tsig.record.ttl(),
Tsig::new(
tsig.record.data().algorithm(),
tsig.record.data().time_signed(),
tsig.record.data().fudge(),
b"",
msg.header().id(),
error,
b"",
)
.expect("long record data"),
))?;
}
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)
}
}
#[cfg(feature = "std")]
impl<K> std::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")
}
}
}
}
#[cfg(feature = "std")]
impl std::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")
}
}
}
}
#[cfg(feature = "std")]
impl std::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")
}
}
#[cfg(feature = "std")]
impl std::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")
}
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for ValidationError {}
#[derive(Debug)]
enum TsigError {
Invalid,
Position,
Missing,
ParseError,
}