1#[cfg(feature = "__dnssec")]
17use alloc::boxed::Box;
18#[cfg(feature = "__dnssec")]
19use alloc::string::ToString;
20use alloc::sync::Arc;
21use alloc::vec::Vec;
22#[cfg(feature = "__dnssec")]
23use core::ops::Range;
24
25#[cfg(feature = "__dnssec")]
26use tracing::debug;
27
28#[cfg(feature = "__dnssec")]
29use crate::dnssec::DnsSecError;
30
31use super::rdata::tsig::TsigAlgorithm;
32#[cfg(feature = "__dnssec")]
33use super::rdata::tsig::{
34 TSIG, TsigError, make_tsig_record, message_tbs, signed_bitmessage_to_buf,
35};
36#[cfg(feature = "__dnssec")]
37use crate::error::{ProtoError, ProtoResult};
38#[cfg(feature = "__dnssec")]
39use crate::op::DnsResponse;
40use crate::op::{Message, OpCode};
41#[cfg(feature = "__dnssec")]
42use crate::rr::Record;
43use crate::rr::{Name, RecordType};
44#[cfg(feature = "__dnssec")]
45use crate::serialize::binary::BinEncoder;
46
47pub struct TSigResponseContext {
49 #[cfg(feature = "__dnssec")]
50 request_id: u16,
51 #[cfg(feature = "__dnssec")]
52 time: u64,
53 #[cfg(feature = "__dnssec")]
54 kind: TsigResponseKind,
55}
56
57#[cfg(feature = "__dnssec")]
58impl TSigResponseContext {
59 pub fn new(
62 request_id: u16,
63 time: u64,
64 signer: TSigner,
65 request_mac: Vec<u8>,
66 error: Option<TsigError>,
67 ) -> Self {
68 Self {
69 request_id,
70 time,
71 kind: TsigResponseKind::Signed {
72 signer,
73 request_mac,
74 error,
75 },
76 }
77 }
78
79 pub fn bad_signature(request_id: u16, time: u64, signer: TSigner) -> Self {
81 Self {
82 request_id,
83 time,
84 kind: TsigResponseKind::BadSignature { signer },
85 }
86 }
87
88 pub fn unknown_key(request_id: u16, time: u64, key_name: Name) -> Self {
90 Self {
91 request_id,
92 time,
93 kind: TsigResponseKind::UnknownKey { key_name },
94 }
95 }
96
97 #[cfg(feature = "__dnssec")]
99 pub fn sign(self, response: &[u8]) -> Result<Box<Record<TSIG>>, ProtoError> {
100 match self.kind {
101 TsigResponseKind::Signed {
102 signer,
103 request_mac,
104 error,
105 } => {
106 debug_assert!(!matches!(
108 error,
109 Some(TsigError::BadSig | TsigError::BadKey)
110 ));
111
112 let mut stub_tsig = TSIG::stub(self.request_id, self.time, &signer);
113 if let Some(err) = error {
114 stub_tsig.error = Some(err);
115 }
116
117 let tbs_tsig_encoded =
118 signer.encode_response_tbs(&request_mac, response, &stub_tsig)?;
119 let resp_tsig = stub_tsig.set_mac(
120 signer
121 .sign(&tbs_tsig_encoded)
122 .map_err(|e| ProtoError::from(e.to_string()))?,
123 );
124
125 Ok(Box::new(make_tsig_record(
126 signer.signer_name().clone(),
127 resp_tsig,
128 )))
129 }
130 TsigResponseKind::BadSignature { signer } => {
131 let mut stub_tsig = TSIG::stub(self.request_id, self.time, &signer);
132 stub_tsig.error = Some(TsigError::BadSig);
133 Ok(Box::new(make_tsig_record(
134 signer.signer_name().clone(),
135 stub_tsig,
136 )))
137 }
138 TsigResponseKind::UnknownKey { key_name } => {
139 Ok(Box::new(make_tsig_record(
149 key_name.clone(),
150 TSIG::new(
151 TsigAlgorithm::HmacSha256,
152 self.time,
153 300,
154 Vec::new(),
155 self.request_id,
156 Some(TsigError::BadKey),
157 Vec::new(),
158 ),
159 )))
160 }
161 }
162 }
163}
164
165#[cfg(feature = "__dnssec")]
167enum TsigResponseKind {
168 Signed {
170 signer: TSigner,
171 request_mac: Vec<u8>,
172 error: Option<TsigError>,
173 },
174 BadSignature { signer: TSigner },
176 UnknownKey { key_name: Name },
178}
179
180#[derive(Clone)]
182pub struct TSigner(Arc<TSignerInner>);
183
184struct TSignerInner {
185 key: Vec<u8>, algorithm: TsigAlgorithm,
187 signer_name: Name,
188 fudge: u16,
189}
190
191impl TSigner {
192 #[cfg(feature = "__dnssec")]
201 pub fn new(
202 key: Vec<u8>,
203 algorithm: TsigAlgorithm,
204 mut signer_name: Name,
205 fudge: u16,
206 ) -> Result<Self, DnsSecError> {
207 if !algorithm.supported() {
208 return Err(DnsSecError::TsigUnsupportedMacAlgorithm(algorithm));
209 }
210
211 signer_name.set_fqdn(true);
212 Ok(Self(Arc::new(TSignerInner {
213 key,
214 algorithm,
215 signer_name,
216 fudge,
217 })))
218 }
219
220 pub fn key(&self) -> &[u8] {
222 &self.0.key
223 }
224
225 pub fn algorithm(&self) -> &TsigAlgorithm {
227 &self.0.algorithm
228 }
229
230 pub fn signer_name(&self) -> &Name {
232 &self.0.signer_name
233 }
234
235 pub fn fudge(&self) -> u16 {
240 self.0.fudge
241 }
242
243 #[cfg(feature = "__dnssec")]
245 pub fn sign(&self, tbs: &[u8]) -> Result<Vec<u8>, DnsSecError> {
246 self.0.algorithm.mac_data(&self.0.key, tbs)
247 }
248
249 #[cfg(feature = "__dnssec")]
251 pub fn verify(&self, tbv: &[u8], tag: &[u8]) -> Result<(), DnsSecError> {
252 self.0.algorithm.verify_mac(&self.0.key, tbv, tag)
253 }
254
255 pub fn should_sign_message(&self, message: &Message) -> bool {
257 [OpCode::Update, OpCode::Notify].contains(&message.op_code)
258 || message
259 .queries
260 .iter()
261 .any(|q| [RecordType::AXFR, RecordType::IXFR].contains(&q.query_type()))
262 }
263
264 #[cfg(feature = "__dnssec")]
287 pub fn verify_message_byte(
288 &self,
289 message: &[u8],
290 previous_hash: Option<&[u8]>,
291 first_message: bool,
292 ) -> Result<(Vec<u8>, u64, Range<u64>), DnsSecError> {
293 let (tbv, record) = signed_bitmessage_to_buf(message, previous_hash, first_message)?;
294 let tsig = record.data;
295
296 if record.name != self.0.signer_name || tsig.algorithm != self.0.algorithm {
299 return Err(DnsSecError::TsigWrongKey);
300 }
301
302 if tsig.mac.len() < tsig.algorithm.output_len()? {
309 return Err(DnsSecError::from(
310 "Please file an issue with https://github.com/hickory-dns/hickory-dns to support truncated HMACs with TSIG",
311 ));
312 }
313 self.verify(&tbv, &tsig.mac)?;
314
315 Ok((
322 tsig.mac.to_vec(),
323 tsig.time,
324 Range {
325 start: tsig.time - tsig.fudge as u64,
326 end: tsig.time + tsig.fudge as u64,
327 },
328 ))
329 }
330
331 #[cfg(feature = "__dnssec")]
341 pub fn encode_response_tbs(
342 &self,
343 previous_mac: &[u8],
344 encoded_response: &[u8],
345 stub_tsig: &TSIG,
346 ) -> Result<Vec<u8>, ProtoError> {
347 let mut tbs_buf = Vec::with_capacity(
351 previous_mac.len() + size_of::<u16>() + encoded_response.len() + 128,
352 );
353 let mut encoder = BinEncoder::new(&mut tbs_buf);
354
355 debug_assert!(previous_mac.len() <= u16::MAX as usize); encoder.emit_u16(previous_mac.len() as u16)?;
357 encoder.emit_vec(previous_mac)?;
358 encoder.emit_vec(encoded_response)?;
359 stub_tsig.emit_tsig_for_mac(&mut encoder, self.signer_name())?;
360
361 Ok(tbs_buf)
362 }
363
364 #[cfg(feature = "__dnssec")]
366 pub fn sign_message(
367 &self,
368 message: &Message,
369 current_time: u64,
370 ) -> ProtoResult<(Box<Record<TSIG>>, Option<TSigVerifier>)> {
371 debug!("signing message: {:?}", message);
372
373 let pre_tsig = TSIG::stub(message.id, current_time, self);
374 let signature = self
375 .sign(&message_tbs(message, &pre_tsig, &self.0.signer_name)?)
376 .map_err(|err| ProtoError::from(err.to_string()))?;
377 let tsig = make_tsig_record(
378 self.0.signer_name.clone(),
379 pre_tsig.set_mac(signature.clone()),
380 );
381
382 let verifier = TSigVerifier {
383 signer: self.clone(),
384 previous_signature: signature,
385 remote_time: 0,
386 request_time: current_time,
387 };
388
389 Ok((Box::new(tsig), Some(verifier)))
390 }
391}
392
393#[cfg(feature = "__dnssec")]
399pub struct TSigVerifier {
400 signer: TSigner,
401 previous_signature: Vec<u8>,
402 remote_time: u64,
403 request_time: u64,
404}
405
406#[cfg(feature = "__dnssec")]
407impl TSigVerifier {
408 pub fn verify(&mut self, response_bytes: &[u8]) -> ProtoResult<DnsResponse> {
423 let (last_sig, rt, range) = self
424 .signer
425 .verify_message_byte(
426 response_bytes,
427 Some(&self.previous_signature),
428 self.remote_time == 0,
429 )
430 .map_err(|err| ProtoError::from(err.to_string()))?;
431
432 if rt >= self.remote_time && range.contains(&self.request_time) {
433 self.previous_signature = last_sig;
434 self.remote_time = rt;
435 DnsResponse::from_buffer(response_bytes.to_vec())
436 } else {
437 Err(ProtoError::from("tsig validation error: outdated response"))
438 }
439 }
440}
441
442#[cfg(test)]
443#[cfg(feature = "__dnssec")]
444mod tests {
445 #![allow(clippy::dbg_macro, clippy::print_stdout)]
446
447 use crate::op::{Message, Query};
448 use crate::rr::Name;
449 use crate::serialize::binary::BinEncodable;
450
451 use super::*;
452 fn assert_send_and_sync<T: Send + Sync>() {}
453
454 #[test]
455 fn test_send_and_sync() {
456 assert_send_and_sync::<TSigner>();
457 }
458
459 #[test]
460 fn test_sign_and_verify_message_tsig() {
461 let time_begin = 1609459200u64;
462 let fudge = 300u64;
463 let origin: Name = Name::parse("example.com.", None).unwrap();
464 let key_name: Name = Name::from_ascii("key_name.").unwrap();
465 let mut question = Message::query();
466 let mut query: Query = Query::new();
467 query.set_name(origin);
468 question.add_query(query);
469
470 let sig_key = b"some_key".to_vec();
471 let signer =
472 TSigner::new(sig_key, TsigAlgorithm::HmacSha512, key_name, fudge as u16).unwrap();
473
474 assert!(question.signature().is_none());
475 question
476 .finalize(&signer, time_begin)
477 .expect("should have signed");
478 assert!(question.signature().is_some());
479
480 let (_, _, validity_range) = signer
481 .verify_message_byte(&question.to_bytes().unwrap(), None, true)
482 .unwrap();
483 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))); }
488
489 fn get_message_and_signer() -> (Message, TSigner) {
491 let time_begin = 1609459200u64;
492 let fudge = 300u64;
493 let origin: Name = Name::parse("example.com.", None).unwrap();
494 let key_name: Name = Name::from_ascii("key_name.").unwrap();
495 let mut question = Message::query();
496 let mut query: Query = Query::new();
497 query.set_name(origin);
498 question.add_query(query);
499
500 let sig_key = b"some_key".to_vec();
501 let signer =
502 TSigner::new(sig_key, TsigAlgorithm::HmacSha512, key_name, fudge as u16).unwrap();
503
504 assert!(question.signature().is_none());
505 question
506 .finalize(&signer, time_begin)
507 .expect("should have signed");
508 assert!(question.signature().is_some());
509
510 assert!(
512 signer
513 .verify_message_byte(&question.to_bytes().unwrap(), None, true)
514 .is_ok()
515 );
516
517 (question, signer)
518 }
519
520 #[test]
521 fn test_sign_and_verify_message_tsig_reject_keyname() {
522 let (mut question, signer) = get_message_and_signer();
523
524 let other_name = Name::from_ascii("other_name.").unwrap();
525 let Some(mut signature) = question.take_signature() else {
526 panic!("should have TSIG signed");
527 };
528 signature.name = other_name;
529 question.set_signature(signature);
530
531 assert!(
532 signer
533 .verify_message_byte(&question.to_bytes().unwrap(), None, true)
534 .is_err()
535 );
536 }
537
538 #[test]
539 fn test_sign_and_verify_message_tsig_reject_invalid_mac() {
540 let (mut question, signer) = get_message_and_signer();
541
542 let mut query: Query = Query::new();
543 let origin: Name = Name::parse("example.net.", None).unwrap();
544 query.set_name(origin);
545 question.add_query(query);
546
547 assert!(
548 signer
549 .verify_message_byte(&question.to_bytes().unwrap(), None, true)
550 .is_err()
551 );
552 }
553}