1use alloc::boxed::Box;
17use alloc::string::ToString;
18use alloc::sync::Arc;
19use alloc::vec::Vec;
20use core::ops::Range;
21
22use tracing::debug;
23
24use super::rdata::DNSSECRData;
25use super::rdata::tsig::{
26 TSIG, TsigAlgorithm, make_tsig_record, message_tbs, signed_bitmessage_to_buf,
27};
28use super::{DnsSecError, DnsSecErrorKind};
29use crate::error::{ProtoError, ProtoResult};
30use crate::op::{Message, MessageFinalizer, MessageVerifier};
31use crate::rr::{Name, RData, Record};
32use crate::xfer::DnsResponse;
33
34#[derive(Clone)]
36pub struct TSigner(Arc<TSignerInner>);
37
38struct TSignerInner {
39 key: Vec<u8>, algorithm: TsigAlgorithm,
41 signer_name: Name,
42 fudge: u16,
43}
44
45impl TSigner {
46 pub fn new(
55 key: Vec<u8>,
56 algorithm: TsigAlgorithm,
57 mut signer_name: Name,
58 fudge: u16,
59 ) -> Result<Self, DnsSecError> {
60 signer_name.set_fqdn(true);
61 if algorithm.supported() {
62 Ok(Self(Arc::new(TSignerInner {
63 key,
64 algorithm,
65 signer_name,
66 fudge,
67 })))
68 } else {
69 Err(DnsSecErrorKind::TsigUnsupportedMacAlgorithm(algorithm).into())
70 }
71 }
72
73 pub fn key(&self) -> &[u8] {
75 &self.0.key
76 }
77
78 pub fn algorithm(&self) -> &TsigAlgorithm {
80 &self.0.algorithm
81 }
82
83 pub fn signer_name(&self) -> &Name {
85 &self.0.signer_name
86 }
87
88 pub fn fudge(&self) -> u16 {
93 self.0.fudge
94 }
95
96 pub fn sign(&self, tbs: &[u8]) -> Result<Vec<u8>, DnsSecError> {
98 self.0.algorithm.mac_data(&self.0.key, tbs)
99 }
100
101 pub fn sign_message(&self, message: &Message, pre_tsig: &TSIG) -> Result<Vec<u8>, DnsSecError> {
103 self.sign(&message_tbs(None, message, pre_tsig, &self.0.signer_name)?)
104 }
105
106 pub fn verify(&self, tbv: &[u8], tag: &[u8]) -> Result<(), DnsSecError> {
108 self.0.algorithm.verify_mac(&self.0.key, tbv, tag)
109 }
110
111 pub fn verify_message_byte(
129 &self,
130 previous_hash: Option<&[u8]>,
131 message: &[u8],
132 first_message: bool,
133 ) -> Result<(Vec<u8>, Range<u64>, u64), DnsSecError> {
134 let (tbv, record) = signed_bitmessage_to_buf(previous_hash, message, first_message)?;
135 let tsig = if let RData::DNSSEC(DNSSECRData::TSIG(tsig)) = record.data() {
136 tsig
137 } else {
138 unreachable!("tsig::signed_message_to_buff always returns a TSIG record")
139 };
140
141 if record.name() != &self.0.signer_name || tsig.algorithm() != &self.0.algorithm {
144 return Err(DnsSecErrorKind::TsigWrongKey.into());
145 }
146
147 if tsig.mac().len() < tsig.algorithm().output_len()? {
152 return Err(DnsSecError::from(
153 "Please file an issue with https://github.com/hickory-dns/hickory-dns to support truncated HMACs with TSIG",
154 ));
155 }
156
157 let mac = tsig.mac();
159 self.verify(&tbv, mac)?;
160
161 Ok((
176 tsig.mac().to_vec(),
177 Range {
178 start: tsig.time() - tsig.fudge() as u64,
179 end: tsig.time() + tsig.fudge() as u64,
180 },
181 tsig.time(),
182 ))
183 }
184}
185
186impl MessageFinalizer for TSigner {
187 fn finalize_message(
188 &self,
189 message: &Message,
190 current_time: u32,
191 ) -> ProtoResult<(Vec<Record>, Option<MessageVerifier>)> {
192 debug!("signing message: {:?}", message);
193 let current_time = current_time as u64;
194
195 let pre_tsig = TSIG::new(
196 self.0.algorithm.clone(),
197 current_time,
198 self.0.fudge,
199 Vec::new(),
200 message.id(),
201 0,
202 Vec::new(),
203 );
204 let mut signature: Vec<u8> = self
205 .sign_message(message, &pre_tsig)
206 .map_err(|err| ProtoError::from(err.to_string()))?;
207 let tsig = make_tsig_record(
208 self.0.signer_name.clone(),
209 pre_tsig.set_mac(signature.clone()),
210 );
211 let self2 = self.clone();
212 let mut remote_time = 0;
213 let verifier = move |dns_response: &[u8]| {
214 let (last_sig, range, rt) = self2
215 .verify_message_byte(Some(signature.as_ref()), dns_response, remote_time == 0)
216 .map_err(|err| ProtoError::from(err.to_string()))?;
217 if rt >= remote_time && range.contains(¤t_time)
218 {
220 signature = last_sig;
221 remote_time = rt;
222 DnsResponse::from_buffer(dns_response.to_vec())
223 } else {
224 Err(ProtoError::from("tsig validation error: outdated response"))
225 }
226 };
227 Ok((vec![tsig], Some(Box::new(verifier))))
228 }
229}
230
231#[cfg(test)]
232mod tests {
233 #![allow(clippy::dbg_macro, clippy::print_stdout)]
234
235 use crate::op::{Message, Query};
236 use crate::rr::Name;
237 use crate::serialize::binary::BinEncodable;
238
239 use super::*;
240 fn assert_send_and_sync<T: Send + Sync>() {}
241
242 #[test]
243 fn test_send_and_sync() {
244 assert_send_and_sync::<TSigner>();
245 }
246
247 #[test]
248 fn test_sign_and_verify_message_tsig() {
249 let time_begin = 1609459200u64;
250 let fudge = 300u64;
251 let origin: Name = Name::parse("example.com.", None).unwrap();
252 let key_name: Name = Name::from_ascii("key_name.").unwrap();
253 let mut question: Message = Message::new();
254 let mut query: Query = Query::new();
255 query.set_name(origin);
256 question.add_query(query);
257
258 let sig_key = b"some_key".to_vec();
259 let signer =
260 TSigner::new(sig_key, TsigAlgorithm::HmacSha512, key_name, fudge as u16).unwrap();
261
262 assert!(question.signature().is_empty());
263 question
264 .finalize(&signer, time_begin as u32)
265 .expect("should have signed");
266 assert!(!question.signature().is_empty());
267
268 let (_, validity_range, _) = signer
269 .verify_message_byte(None, &question.to_bytes().unwrap(), true)
270 .unwrap();
271 assert!(validity_range.contains(&(time_begin + fudge / 2))); assert!(validity_range.contains(&(time_begin - fudge / 2))); assert!(!validity_range.contains(&(time_begin + fudge * 2))); assert!(!validity_range.contains(&(time_begin - fudge * 2))); }
276
277 fn get_message_and_signer() -> (Message, TSigner) {
279 let time_begin = 1609459200u64;
280 let fudge = 300u64;
281 let origin: Name = Name::parse("example.com.", None).unwrap();
282 let key_name: Name = Name::from_ascii("key_name.").unwrap();
283 let mut question: Message = Message::new();
284 let mut query: Query = Query::new();
285 query.set_name(origin);
286 question.add_query(query);
287
288 let sig_key = b"some_key".to_vec();
289 let signer =
290 TSigner::new(sig_key, TsigAlgorithm::HmacSha512, key_name, fudge as u16).unwrap();
291
292 assert!(question.signature().is_empty());
293 question
294 .finalize(&signer, time_begin as u32)
295 .expect("should have signed");
296 assert!(!question.signature().is_empty());
297
298 assert!(
300 signer
301 .verify_message_byte(None, &question.to_bytes().unwrap(), true)
302 .is_ok()
303 );
304
305 (question, signer)
306 }
307
308 #[test]
309 fn test_sign_and_verify_message_tsig_reject_keyname() {
310 let (mut question, signer) = get_message_and_signer();
311
312 let other_name: Name = Name::from_ascii("other_name.").unwrap();
313 let mut signature = question.take_signature().remove(0);
314 signature.set_name(other_name);
315 question.add_tsig(signature);
316
317 assert!(
318 signer
319 .verify_message_byte(None, &question.to_bytes().unwrap(), true)
320 .is_err()
321 );
322 }
323
324 #[test]
325 fn test_sign_and_verify_message_tsig_reject_invalid_mac() {
326 let (mut question, signer) = get_message_and_signer();
327
328 let mut query: Query = Query::new();
329 let origin: Name = Name::parse("example.net.", None).unwrap();
330 query.set_name(origin);
331 question.add_query(query);
332
333 assert!(
334 signer
335 .verify_message_byte(None, &question.to_bytes().unwrap(), true)
336 .is_err()
337 );
338 }
339}