1#[cfg(test)]
2mod integrity_test;
3
4use crate::attributes::*;
5use crate::message::*;
6use crypto::{CryptoError, HashAlgorithm, HmacAlgorithm, RTCCrypto, SecretVec};
7use shared::error::*;
8use std::fmt;
9
10pub(crate) const CREDENTIALS_SEP: &str = ":";
12
13#[derive(Clone)]
20pub struct MessageIntegrity<'a> {
24 key: SecretVec,
25 crypto: &'a dyn RTCCrypto,
26}
27
28fn crypto_error(error: CryptoError) -> Error {
29 Error::Crypto(error.to_string())
30}
31
32impl<'a> fmt::Display for MessageIntegrity<'a> {
33 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34 write!(
35 f,
36 "MESSAGE-INTEGRITY key: [REDACTED; {} bytes]",
37 self.key.len()
38 )
39 }
40}
41
42impl<'a> Setter for MessageIntegrity<'a> {
43 fn add_to(&self, m: &mut Message) -> Result<()> {
47 for a in &m.attributes.0 {
48 if a.typ == ATTR_FINGERPRINT {
51 return Err(Error::ErrFingerprintBeforeIntegrity);
52 }
53 }
54 let length = m.length;
58 m.length += (MESSAGE_INTEGRITY_SIZE + ATTRIBUTE_HEADER_SIZE) as u32;
60 m.write_length(); let mut value = [0_u8; MESSAGE_INTEGRITY_SIZE];
62 let result = self
65 .crypto
66 .new_hmac(HmacAlgorithm::Sha1, self.key.as_ref())
67 .and_then(|mut mac| mac.sign(&[&m.raw], &mut value));
68 m.length = length; m.write_length();
70 result.map_err(crypto_error)?;
71
72 m.add(ATTR_MESSAGE_INTEGRITY, &value);
73
74 Ok(())
75 }
76}
77
78pub(crate) const MESSAGE_INTEGRITY_SIZE: usize = 20;
79
80impl<'a> MessageIntegrity<'a> {
81 #[must_use]
83 pub fn new_raw_integrity_with_provider(
84 key: impl Into<Vec<u8>>,
85 crypto: &'a dyn RTCCrypto,
86 ) -> Self {
87 Self {
88 key: SecretVec::new(key.into()),
89 crypto,
90 }
91 }
92
93 #[must_use]
95 pub fn new_short_term_integrity_with_provider(
96 password: String,
97 crypto: &'a dyn RTCCrypto,
98 ) -> Self {
99 Self::new_raw_integrity_with_provider(password.into_bytes(), crypto)
100 }
101
102 pub fn new_long_term_integrity_with_provider(
104 username: String,
105 realm: String,
106 password: String,
107 crypto: &'a dyn RTCCrypto,
108 ) -> Result<Self> {
109 let key = MessageIntegrity::long_term_integrity_key(username, realm, password, crypto)?;
110 Ok(Self::new_raw_integrity_with_provider(key, crypto))
111 }
112
113 pub fn long_term_integrity_key(
115 username: String,
116 realm: String,
117 password: String,
118 crypto: &'a dyn RTCCrypto,
119 ) -> Result<Vec<u8>> {
120 let credentials = [username, realm, password].join(CREDENTIALS_SEP);
121 let key = crypto
122 .hash(HashAlgorithm::Md5, credentials.as_bytes())
123 .map_err(crypto_error)?;
124 if key.len() != 16 {
125 return Err(Error::Crypto(format!(
126 "provider returned an invalid MD5 digest length: {}",
127 key.len()
128 )));
129 }
130 Ok(key)
131 }
132
133 pub fn check(m: &mut Message, key: &[u8], crypto: &dyn RTCCrypto) -> Result<()> {
137 let v = m.get(ATTR_MESSAGE_INTEGRITY)?;
138
139 let length = m.length as usize;
143 let mut after_integrity = false;
144 let mut size_reduced = 0;
145
146 for a in &m.attributes.0 {
147 if after_integrity {
148 size_reduced += nearest_padded_value_length(a.length as usize);
149 size_reduced += ATTRIBUTE_HEADER_SIZE;
150 }
151 if a.typ == ATTR_MESSAGE_INTEGRITY {
152 after_integrity = true;
153 }
154 }
155 m.length -= size_reduced as u32;
156 m.write_length();
157 let start_of_hmac = MESSAGE_HEADER_SIZE + m.length as usize
159 - (ATTRIBUTE_HEADER_SIZE + MESSAGE_INTEGRITY_SIZE);
160 let b = &m.raw[..start_of_hmac]; let result = crypto
162 .new_hmac(HmacAlgorithm::Sha1, key)
163 .and_then(|mut mac| mac.verify(&[b], &v));
164 m.length = length as u32;
165 m.write_length(); match result {
167 Ok(()) => Ok(()),
168 Err(CryptoError::AuthenticationFailed | CryptoError::InvalidTagLength { .. }) => {
169 Err(Error::ErrIntegrityMismatch)
170 }
171 Err(error) => Err(crypto_error(error)),
172 }
173 }
174}