stun_rs/attributes/stun/
fingerprint.rs

1use crate::attributes::{stunt_attribute, DecodeAttributeValue, EncodeAttributeValue};
2use crate::common::check_buffer_boundaries;
3use crate::context::{AttributeDecoderContext, AttributeEncoderContext};
4use crate::error::{StunError, StunErrorType};
5use crate::{Decode, DecoderContext};
6use byteorder::{BigEndian, ByteOrder};
7
8const FINGERPRINT: u16 = 0x8028;
9const FINGERPRINT_SIZE: usize = 4;
10const FINGERPRINT_XOR_VALUE: u32 = 0x5354_554e;
11
12/// The encodable fingerprint is computed as the CRC-32 of the STUN
13/// message up to (but excluding) the FINGERPRINT attribute itself,
14/// Xor'd with the 32-bit value `0x5354554e`.
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct EncodableFingerprint {}
17
18/// The decodable fingerprint contains the CRC-32 value extracted from the STUN message
19#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct DecodableFingerprint(u32);
21
22impl DecodableFingerprint {
23    fn validate(&self, input: &[u8]) -> bool {
24        let crc32 = crc::Crc::<u32>::new(&crc::CRC_32_ISO_HDLC).checksum(input);
25        self.0 == crc32
26    }
27}
28
29impl crate::Decode<'_> for DecodableFingerprint {
30    fn decode(buffer: &[u8]) -> Result<(Self, usize), StunError> {
31        let (value, _) = u32::decode(buffer)?;
32        let crc32 = value ^ FINGERPRINT_XOR_VALUE;
33        Ok((DecodableFingerprint(crc32), FINGERPRINT_SIZE))
34    }
35}
36
37/// The [`Fingerprint`] attribute MAY be present in all STUN messages.
38/// When present, the [`Fingerprint`] attribute MUST be the last attribute in
39/// the message and thus will appear after
40/// [`MessageIntegrity`](crate::attributes::stun::MessageIntegrity) and
41/// [`MessageIntegritySha256`](crate::attributes::stun::MessageIntegritySha256).
42///
43/// # Examples
44///```rust
45/// # use stun_rs::attributes::{AttributeType, StunAttributeType};
46/// # use stun_rs::attributes::stun::Fingerprint;
47/// let attr = Fingerprint::default();
48/// assert_eq!(attr.attribute_type(), AttributeType::from(0x8028));
49///```
50#[derive(Debug, Clone, PartialEq, Eq)]
51pub enum Fingerprint {
52    /// The encodable [`Fingerprint`] attribute,
53    Encodable(EncodableFingerprint),
54    /// The decoded [`Fingerprint`] attribute value.
55    Decodable(DecodableFingerprint),
56}
57
58impl Default for Fingerprint {
59    fn default() -> Self {
60        Fingerprint::Encodable(EncodableFingerprint {})
61    }
62}
63
64impl From<&[u8; FINGERPRINT_SIZE]> for Fingerprint {
65    fn from(val: &[u8; FINGERPRINT_SIZE]) -> Self {
66        let (attr, _) =
67            DecodableFingerprint::decode(val).expect("Could not decode Fingerprint attribute");
68        Fingerprint::Decodable(attr)
69    }
70}
71
72impl From<[u8; FINGERPRINT_SIZE]> for Fingerprint {
73    fn from(val: [u8; FINGERPRINT_SIZE]) -> Self {
74        Fingerprint::from(&val)
75    }
76}
77
78impl EncodeAttributeValue for Fingerprint {
79    fn encode(&self, mut ctx: AttributeEncoderContext) -> Result<usize, StunError> {
80        match self {
81            Fingerprint::Encodable(_) => {
82                let raw_value = ctx.raw_value_mut();
83                check_buffer_boundaries(raw_value, FINGERPRINT_SIZE)?;
84                raw_value[0..FINGERPRINT_SIZE]
85                    .iter_mut()
86                    .for_each(|v| *v = 0);
87                Ok(FINGERPRINT_SIZE)
88            }
89            _ => Err(StunError::new(
90                StunErrorType::InvalidParam,
91                "Not encodable attribute",
92            )),
93        }
94    }
95
96    fn post_encode(&self, mut ctx: AttributeEncoderContext) -> Result<(), StunError> {
97        match self {
98            Fingerprint::Encodable(_) => {
99                check_buffer_boundaries(ctx.raw_value(), FINGERPRINT_SIZE)?;
100                let crc32 = crc::Crc::<u32>::new(&crc::CRC_32_ISO_HDLC)
101                    .checksum(ctx.encoded_message())
102                    ^ FINGERPRINT_XOR_VALUE;
103                BigEndian::write_u32(ctx.raw_value_mut(), crc32);
104                Ok(())
105            }
106            _ => Err(StunError::new(
107                StunErrorType::InvalidParam,
108                "Not encodable attribute",
109            )),
110        }
111    }
112}
113
114impl DecodeAttributeValue for Fingerprint {
115    fn decode(ctx: AttributeDecoderContext) -> Result<(Self, usize), crate::StunError> {
116        let (val, size) = DecodableFingerprint::decode(ctx.raw_value())?;
117        Ok((Fingerprint::Decodable(val), size))
118    }
119}
120
121impl crate::attributes::Verifiable for Fingerprint {
122    fn verify(&self, input: &[u8], _cxt: &DecoderContext) -> bool {
123        self.validate(input)
124    }
125}
126
127impl crate::attributes::AsVerifiable for Fingerprint {
128    fn as_verifiable_ref(&self) -> Option<&dyn crate::attributes::Verifiable> {
129        Some(self)
130    }
131}
132
133impl Fingerprint {
134    /// Validates the input value with the CRC-32 attribute value
135    /// # Arguments:
136    /// * `input`- the STUN message up to (but excluding) the FINGERPRINT attribute itself.
137    /// # Returns:
138    /// true if `input` does not match the calculated CRC-32 value.
139    pub fn validate(&self, input: &[u8]) -> bool {
140        match self {
141            Fingerprint::Decodable(attr) => attr.validate(input),
142            _ => false,
143        }
144    }
145}
146stunt_attribute!(Fingerprint, FINGERPRINT);
147
148#[cfg(test)]
149mod tests {
150    use super::*;
151    use crate::attributes::EncodeAttributeValue;
152    use crate::StunAttribute;
153
154    #[test]
155    fn encode_fingerprint() {
156        let input = &stun_vectors::SAMPLE_IPV4_RESPONSE[..72];
157        let mut output: [u8; 4] = [0xff; 4];
158        let ctx = AttributeEncoderContext::new(None, input, &mut output);
159
160        let fingerprint = Fingerprint::default();
161        let size = fingerprint
162            .encode(ctx)
163            .expect("Could not encode Fingerprint");
164
165        assert_eq!(size, FINGERPRINT_SIZE);
166        // Expect dummy value
167        output.iter().for_each(|x| assert_eq!(*x, 0x00));
168
169        let ctx = AttributeEncoderContext::new(None, input, &mut output[..size]);
170        // Encode
171        fingerprint
172            .post_encode(ctx)
173            .expect("Could not encode Fingerprint");
174
175        assert_eq!(output, [0xc0, 0x7d, 0x4c, 0x96]);
176
177        // Decodable fingerprint can not be encoded
178        let fingerprint = Fingerprint::from([0xc0, 0x7d, 0x4c, 0x96]);
179        let ctx = AttributeEncoderContext::new(None, input, &mut output);
180        let error = fingerprint
181            .encode(ctx)
182            .expect_err("Expected error to encode a decodable fingerprint");
183        assert_eq!(error, StunErrorType::InvalidParam);
184
185        let ctx = AttributeEncoderContext::new(None, input, &mut output);
186        let error = fingerprint
187            .post_encode(ctx)
188            .expect_err("Expected error to encode a decodable fingerprint");
189        assert_eq!(error, StunErrorType::InvalidParam);
190    }
191
192    #[test]
193    fn validate_fingerprint() {
194        let input = crate::get_input_text::<Fingerprint>(&stun_vectors::SAMPLE_IPV4_RESPONSE)
195            .expect("Can not get input buffer");
196
197        let fingerprint = Fingerprint::from([0xc0, 0x7d, 0x4c, 0x96]);
198        let _val = format!("{:?}", fingerprint);
199
200        assert!(fingerprint.validate(&input));
201
202        let fingerprint = Fingerprint::default();
203        assert!(!fingerprint.validate(&input));
204    }
205
206    #[test]
207    fn fingerprint_stunt_attribute() {
208        let attr = StunAttribute::Fingerprint(Fingerprint::default());
209        assert!(attr.is_fingerprint());
210        assert!(attr.as_fingerprint().is_ok());
211        assert!(attr.as_unknown().is_err());
212
213        assert!(!attr.attribute_type().is_comprehension_required());
214        assert!(attr.attribute_type().is_comprehension_optional());
215
216        let dbg_fmt = format!("{:?}", attr);
217        assert_eq!("Fingerprint(Encodable(EncodableFingerprint))", dbg_fmt);
218    }
219}