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