tdns_cli/
tsig.rs

1/// An implementation of RFC 2845 for `trust-dns`.
2use 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    // All SHA2-based algorithms are defined in RFC 4635
72    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, // This is a actually a 48-bit value
161    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, // This is a actually a 48-bit value
172        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)?; // Other data is of length 0
215        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(); // TODO: initial capacity?
227    let mut encoder = BinEncoder::new(&mut encoded);
228    let fudge = 300; // FIXME: fudge hardcoded
229                     // See RFC 2845, section 3.4. The "whole and complete message" in wire
230                     // format, before adding the TSIG RR.
231    msg.emit(&mut encoder)?;
232    //  3.4.2. TSIG Variables
233    //
234    // Source       Field Name       Notes
235    // -----------------------------------------------------------------------
236    // TSIG RR      NAME             Key name, in canonical wire format
237    // TSIG RR      CLASS            (Always ANY in the current specification)
238    // TSIG RR      TTL              (Always 0 in the current specification)
239    // TSIG RDATA   Algorithm Name   in canonical wire format
240    // TSIG RDATA   Time Signed      in network byte order
241    // TSIG RDATA   Fudge            in network byte order
242    // TSIG RDATA   Error            in network byte order
243    // TSIG RDATA   Other Len        in network byte order
244    // TSIG RDATA   Other Data       exactly as transmitted
245    encoder.set_canonical_names(true);
246    key.name.emit(&mut encoder)?;
247    rr::DNSClass::ANY.emit(&mut encoder)?;
248    encoder.emit_u32(0)?; // TTL
249    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)?; // Other data is of length 0
255    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}