ezk_stun_types/attributes/
integrity.rs1use super::{Attribute, ATTRIBUTE_HEADER_LEN};
2use crate::builder::MessageBuilder;
3use crate::header::STUN_HEADER_LENGTH;
4use crate::parse::{AttrSpan, Message};
5use crate::Error;
6use hmac::digest::core_api::BlockSizeUser;
7use hmac::digest::{Digest, Update};
8use hmac::{Mac, SimpleHmac};
9use sha1::Sha1;
10use sha2::Sha256;
11use std::convert::TryFrom;
12
13pub fn long_term_password_md5(username: &str, realm: &str, password: &str) -> Vec<u8> {
14 md5::compute(format!("{}:{}:{}", username, realm, password).as_bytes()).to_vec()
15}
16
17pub fn long_term_password_sha256(username: &str, realm: &str, password: &str) -> Vec<u8> {
18 Sha256::digest(format!("{}:{}:{}", username, realm, password).as_bytes()).to_vec()
19}
20
21pub struct MessageIntegrityKey(SimpleHmac<Sha1>);
22
23impl MessageIntegrityKey {
24 pub fn new(key: impl AsRef<[u8]>) -> Self {
25 Self(SimpleHmac::new_from_slice(key.as_ref()).expect("any key length is valid"))
26 }
27}
28
29pub struct MessageIntegrity;
31
32impl Attribute<'_> for MessageIntegrity {
33 type Context = MessageIntegrityKey;
34 const TYPE: u16 = 0x0008;
35
36 fn decode(ctx: Self::Context, msg: &mut Message, attr: AttrSpan) -> Result<Self, Error> {
37 message_integrity_decode(ctx.0, msg, attr)?;
38
39 Ok(Self)
40 }
41
42 fn encode(&self, ctx: Self::Context, builder: &mut MessageBuilder) {
43 message_integrity_encode(ctx.0, builder)
44 }
45
46 fn encode_len(&self) -> Result<u16, Error> {
47 Ok(u16::try_from(Sha1::output_size())?)
48 }
49}
50
51pub struct MessageIntegritySha256Key(SimpleHmac<Sha256>);
52
53impl MessageIntegritySha256Key {
54 pub fn new(key: impl AsRef<[u8]>) -> Self {
55 Self(SimpleHmac::new_from_slice(key.as_ref()).expect("any key length is valid"))
56 }
57}
58
59pub struct MessageIntegritySha256;
61
62impl Attribute<'_> for MessageIntegritySha256 {
63 type Context = MessageIntegritySha256Key;
64 const TYPE: u16 = 0x001C;
65
66 fn decode(ctx: Self::Context, msg: &mut Message, attr: AttrSpan) -> Result<Self, Error> {
67 message_integrity_decode(ctx.0, msg, attr)?;
68
69 Ok(Self)
70 }
71
72 fn encode(&self, ctx: Self::Context, builder: &mut MessageBuilder) {
73 message_integrity_encode(ctx.0, builder)
74 }
75
76 fn encode_len(&self) -> Result<u16, Error> {
77 Ok(u16::try_from(dbg!(Sha256::output_size()))?)
78 }
79}
80
81fn message_integrity_decode<D>(
82 mut hmac: SimpleHmac<D>,
83 msg: &mut Message,
84 attr: AttrSpan,
85) -> Result<(), Error>
86where
87 D: Digest + BlockSizeUser,
88{
89 msg.with_msg_len(
96 u16::try_from(attr.padding_end - STUN_HEADER_LENGTH)?,
97 |msg| {
98 let received_digest = attr.get_value(msg.buffer());
100
101 let message = &msg.buffer()[..attr.begin - ATTRIBUTE_HEADER_LEN];
103
104 Update::update(&mut hmac, message);
106 let calculated_digest = hmac.finalize().into_bytes();
107
108 if calculated_digest.as_slice() != received_digest {
110 return Err(Error::InvalidData("failed to verify message integrity"));
111 }
112
113 Ok(())
114 },
115 )
116}
117
118fn message_integrity_encode<D>(mut hmac: SimpleHmac<D>, builder: &mut MessageBuilder)
119where
120 D: Digest + BlockSizeUser,
121{
122 let message_length_with_integrity_attribute =
124 (builder.buffer().len() + <D as Digest>::output_size()) - STUN_HEADER_LENGTH;
125
126 builder.set_len(
127 message_length_with_integrity_attribute
128 .try_into()
129 .expect("stun messages must fit withing 65535 bytes"),
130 );
131
132 let data = builder.buffer();
134 let data = &data[..data.len() - ATTRIBUTE_HEADER_LEN];
135 Update::update(&mut hmac, data);
136 let digest = hmac.finalize().into_bytes();
137
138 builder.buffer().extend_from_slice(&digest);
139}
140
141#[cfg(test)]
142mod test {
143 use super::{
144 MessageIntegrity, MessageIntegrityKey, MessageIntegritySha256, MessageIntegritySha256Key,
145 };
146 use crate::attributes::Software;
147 use crate::builder::MessageBuilder;
148 use crate::header::{Class, Method};
149 use crate::parse::Message;
150 use crate::TransactionId;
151
152 #[test]
153 fn selftest_sha1() {
154 let password = "abc123";
155
156 let mut message =
157 MessageBuilder::new(Class::Request, Method::Binding, TransactionId::new([0; 12]));
158
159 message.add_attr(Software::new("ezk-stun"));
160 message.add_attr_with(MessageIntegrity, MessageIntegrityKey::new(password));
161
162 let bytes = message.finish();
163 let bytes = Vec::from(&bytes[..]);
164
165 let mut msg = Message::parse(bytes).unwrap();
166
167 msg.attribute_with::<MessageIntegrity>(MessageIntegrityKey::new(password))
168 .unwrap()
169 .unwrap();
170 }
171
172 #[test]
173 fn selftest_sha256() {
174 let password = "abc123";
175
176 let mut message =
177 MessageBuilder::new(Class::Request, Method::Binding, TransactionId::new([0; 12]));
178
179 message.add_attr(Software::new("ezk-stun"));
180 message.add_attr_with(
181 MessageIntegritySha256,
182 MessageIntegritySha256Key::new(password),
183 );
184
185 let bytes = message.finish();
186 let bytes = Vec::from(&bytes[..]);
187
188 let mut msg = Message::parse(bytes).unwrap();
189
190 msg.attribute_with::<MessageIntegritySha256>(MessageIntegritySha256Key::new(password))
191 .unwrap()
192 .unwrap();
193 }
194}