1use std::{
3 convert::{TryFrom, TryInto},
4 fmt,
5 time::{SystemTime, SystemTimeError},
6};
7
8use crypto_mac::InvalidKeyLength;
9use hmac::{Hmac, Mac};
10use once_cell::sync::Lazy;
11use trust_dns::{
12 op,
13 proto::error::{ProtoError, ProtoResult},
14 rr,
15 serialize::binary::{BinEncodable, BinEncoder},
16};
17
18#[derive(Debug)]
19pub enum Error {
20 Proto(ProtoError),
21 InvalidKeyLength(InvalidKeyLength),
22 SystemTime(SystemTimeError),
23}
24
25impl fmt::Display for Error {
26 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
27 match self {
28 Error::Proto(e) => write!(f, "{}", e),
29 Error::InvalidKeyLength(e) => write!(f, "{}", e),
30 Error::SystemTime(e) => write!(f, "{}", e),
31 }
32 }
33}
34
35impl std::error::Error for Error {}
36
37impl From<ProtoError> for Error {
38 fn from(e: ProtoError) -> Self {
39 Error::Proto(e)
40 }
41}
42
43impl From<InvalidKeyLength> for Error {
44 fn from(e: InvalidKeyLength) -> Self {
45 Error::InvalidKeyLength(e)
46 }
47}
48
49impl From<SystemTimeError> for Error {
50 fn from(e: SystemTimeError) -> Self {
51 Error::SystemTime(e)
52 }
53}
54
55#[derive(Debug, Copy, Clone)]
56pub enum Algorithm {
57 HmacSha224,
58 HmacSha256,
59 HmacSha384,
60 HmacSha512,
61}
62
63struct AlgoNames {
64 sha224: rr::Name,
65 sha256: rr::Name,
66 sha384: rr::Name,
67 sha512: rr::Name,
68}
69
70static ALGO_NAMES: Lazy<AlgoNames> = Lazy::new(|| AlgoNames {
71 sha224: rr::Name::from_ascii("hmac-sha224").unwrap(),
73 sha256: rr::Name::from_ascii("hmac-sha256").unwrap(),
74 sha384: rr::Name::from_ascii("hmac-sha384").unwrap(),
75 sha512: rr::Name::from_ascii("hmac-sha512").unwrap(),
76});
77
78impl Algorithm {
79 pub fn as_name(self) -> &'static rr::Name {
80 let names = Lazy::force(&ALGO_NAMES);
81 use Algorithm::*;
82 match self {
83 HmacSha224 => &names.sha224,
84 HmacSha256 => &names.sha256,
85 HmacSha384 => &names.sha384,
86 HmacSha512 => &names.sha512,
87 }
88 }
89 pub fn from_name(name: &rr::Name) -> Result<Algorithm, UnknownAlgorithm> {
90 let names = Lazy::force(&ALGO_NAMES);
91 use Algorithm::*;
92 for (algo_name, algo) in &[
93 (&names.sha224, HmacSha224),
94 (&names.sha256, HmacSha256),
95 (&names.sha384, HmacSha384),
96 (&names.sha512, HmacSha512),
97 ] {
98 if name == *algo_name {
99 return Ok(*algo);
100 }
101 }
102 Err(UnknownAlgorithm)
103 }
104}
105
106#[derive(Debug)]
107pub struct UnknownAlgorithm;
108
109impl fmt::Display for UnknownAlgorithm {
110 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
111 write!(f, "unknown algorithm")
112 }
113}
114
115impl std::error::Error for UnknownAlgorithm {}
116
117#[derive(Debug, Clone)]
118pub struct Key {
119 name: rr::Name,
120 algorithm: Algorithm,
121 secret: Vec<u8>,
122}
123
124impl Key {
125 pub fn new<T>(name: rr::Name, algorithm: Algorithm, secret: T) -> Self
126 where
127 T: Into<Vec<u8>>,
128 {
129 Key {
130 name,
131 algorithm,
132 secret: secret.into(),
133 }
134 }
135}
136
137pub fn add_signature(msg: &mut op::Message, key: &Key) -> Result<(), Error> {
138 let unix_time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
139 let record = create_signature(&msg, unix_time.as_secs(), key)?;
140 msg.add_additional(record);
141 Ok(())
142}
143
144fn create_signature(msg: &op::Message, time_signed: u64, key: &Key) -> Result<rr::Record, Error> {
145 use Algorithm::*;
146 let tsig = match key.algorithm {
147 HmacSha224 => create_tsig::<Hmac<sha2::Sha224>>(&msg, time_signed, &key)?,
148 HmacSha256 => create_tsig::<Hmac<sha2::Sha256>>(&msg, time_signed, &key)?,
149 HmacSha384 => create_tsig::<Hmac<sha2::Sha384>>(&msg, time_signed, &key)?,
150 HmacSha512 => create_tsig::<Hmac<sha2::Sha512>>(&msg, time_signed, &key)?,
151 };
152 let mut record = rr::Record::from_rdata(key.name.clone(), 0, tsig.try_into()?);
153 record.set_dns_class(rr::DNSClass::ANY);
154 Ok(record)
155}
156
157#[derive(Debug)]
158struct TSIG {
159 algorithm_name: rr::Name,
160 time_signed: u64, fudge: u16,
162 mac: Vec<u8>,
163 original_id: u16,
164 error: op::ResponseCode,
165 other_data: Vec<u8>,
166}
167
168impl TSIG {
169 fn new(
170 algorithm_name: rr::Name,
171 time_signed: u64, fudge: u16,
173 mac: Vec<u8>,
174 original_id: u16,
175 error: op::ResponseCode,
176 other_data: Vec<u8>,
177 ) -> Self {
178 TSIG {
179 algorithm_name,
180 time_signed,
181 fudge,
182 mac,
183 original_id,
184 error,
185 other_data,
186 }
187 }
188}
189
190impl TryFrom<TSIG> for rr::RData {
191 type Error = Error;
192
193 fn try_from(tsig: TSIG) -> Result<Self, Self::Error> {
194 let mut encoded = Vec::new();
195 let mut encoder = BinEncoder::new(&mut encoded);
196 encoder.set_canonical_names(true);
197 tsig.emit(&mut encoder)?;
198 Ok(rr::RData::Unknown {
199 code: 250,
200 rdata: rr::rdata::null::NULL::with(encoded),
201 })
202 }
203}
204
205impl BinEncodable for TSIG {
206 fn emit(&self, encoder: &mut BinEncoder) -> ProtoResult<()> {
207 self.algorithm_name.emit(encoder)?;
208 emit_u48(encoder, self.time_signed)?;
209 encoder.emit_u16(self.fudge)?;
210 encoder.emit_u16(self.mac.len() as u16)?;
211 encoder.emit_vec(&self.mac)?;
212 encoder.emit_u16(self.original_id)?;
213 encoder.emit_u16(self.error.into())?;
214 encoder.emit_u16(0)?; Ok(())
216 }
217}
218
219fn emit_u48(encoder: &mut BinEncoder, n: u64) -> ProtoResult<()> {
220 encoder.emit_u16((n >> 32) as u16)?;
221 encoder.emit_u32(n as u32)?;
222 Ok(())
223}
224
225fn create_tsig<T: Mac>(msg: &op::Message, time_signed: u64, key: &Key) -> Result<TSIG, Error> {
226 let mut encoded = Vec::new(); let mut encoder = BinEncoder::new(&mut encoded);
228 let fudge = 300; msg.emit(&mut encoder)?;
232 encoder.set_canonical_names(true);
246 key.name.emit(&mut encoder)?;
247 rr::DNSClass::ANY.emit(&mut encoder)?;
248 encoder.emit_u32(0)?; key.algorithm.as_name().emit(&mut encoder)?;
250 emit_u48(&mut encoder, time_signed)?;
251 encoder.emit_u16(fudge)?;
252 let rcode = op::ResponseCode::NoError;
253 encoder.emit_u16(rcode.into())?;
254 encoder.emit_u16(0)?; let hmac = {
256 let mut mac = T::new_varkey(&key.secret)?;
257 mac.input(&encoded);
258 mac.result().code().to_vec()
259 };
260 Ok(TSIG::new(
261 key.algorithm.as_name().clone(),
262 time_signed,
263 fudge,
264 hmac,
265 msg.id(),
266 rcode,
267 Vec::new(),
268 ))
269}