corevpn_crypto/
hmac_auth.rs1use hmac::{Hmac, Mac};
7use sha2::Sha256;
8use subtle::ConstantTimeEq;
9use zeroize::ZeroizeOnDrop;
10
11use crate::{CryptoError, Result};
12
13type HmacSha256 = Hmac<Sha256>;
14
15#[derive(ZeroizeOnDrop)]
17pub struct HmacAuth {
18 tx_key: [u8; 32],
20 rx_key: [u8; 32],
22}
23
24impl HmacAuth {
25 pub const KEY_SIZE: usize = 32;
27 pub const HMAC_SIZE: usize = 32;
29
30 pub fn new(tx_key: [u8; 32], rx_key: [u8; 32]) -> Self {
32 Self { tx_key, rx_key }
33 }
34
35 pub fn from_single_key(key: [u8; 32]) -> Self {
37 Self {
38 tx_key: key,
39 rx_key: key,
40 }
41 }
42
43 pub fn from_ta_key(ta_key: &[u8; 256], is_server: bool, key_direction: Option<u8>) -> Self {
51 let (tx_key, rx_key) = match (is_server, key_direction) {
52 (true, Some(0)) | (true, None) => {
54 let mut tx = [0u8; 32];
55 let mut rx = [0u8; 32];
56 tx.copy_from_slice(&ta_key[64..96]);
57 rx.copy_from_slice(&ta_key[0..32]);
58 (tx, rx)
59 }
60 (true, Some(1)) => {
62 let mut tx = [0u8; 32];
63 let mut rx = [0u8; 32];
64 tx.copy_from_slice(&ta_key[0..32]);
65 rx.copy_from_slice(&ta_key[64..96]);
66 (tx, rx)
67 }
68 (false, Some(1)) | (false, None) => {
70 let mut tx = [0u8; 32];
71 let mut rx = [0u8; 32];
72 tx.copy_from_slice(&ta_key[0..32]);
73 rx.copy_from_slice(&ta_key[64..96]);
74 (tx, rx)
75 }
76 (false, Some(0)) => {
78 let mut tx = [0u8; 32];
79 let mut rx = [0u8; 32];
80 tx.copy_from_slice(&ta_key[64..96]);
81 rx.copy_from_slice(&ta_key[0..32]);
82 (tx, rx)
83 }
84 _ => panic!("Invalid key direction"),
85 };
86
87 Self { tx_key, rx_key }
88 }
89
90 pub fn authenticate(&self, data: &[u8]) -> [u8; 32] {
92 let mut mac = HmacSha256::new_from_slice(&self.tx_key)
93 .expect("HMAC key size is always valid");
94 mac.update(data);
95 mac.finalize().into_bytes().into()
96 }
97
98 pub fn verify(&self, data: &[u8], expected_hmac: &[u8; 32]) -> Result<()> {
100 let mut mac = HmacSha256::new_from_slice(&self.rx_key)
101 .expect("HMAC key size is always valid");
102 mac.update(data);
103 let computed = mac.finalize().into_bytes();
104
105 if computed.ct_eq(expected_hmac).into() {
106 Ok(())
107 } else {
108 Err(CryptoError::HmacVerificationFailed)
109 }
110 }
111
112 pub fn wrap(&self, data: &[u8]) -> Vec<u8> {
114 let hmac = self.authenticate(data);
115 let mut output = Vec::with_capacity(Self::HMAC_SIZE + data.len());
116 output.extend_from_slice(&hmac);
117 output.extend_from_slice(data);
118 output
119 }
120
121 pub fn unwrap(&self, packet: &[u8]) -> Result<Vec<u8>> {
123 if packet.len() < Self::HMAC_SIZE {
124 return Err(CryptoError::HmacVerificationFailed);
125 }
126
127 let (hmac, data) = packet.split_at(Self::HMAC_SIZE);
128 let hmac: [u8; 32] = hmac.try_into().unwrap();
129
130 self.verify(data, &hmac)?;
131 Ok(data.to_vec())
132 }
133}
134
135#[derive(ZeroizeOnDrop)]
137pub struct TlsCryptKey {
138 cipher_key: [u8; 32],
140 hmac_key: [u8; 32],
142}
143
144impl TlsCryptKey {
145 pub fn new(cipher_key: [u8; 32], hmac_key: [u8; 32]) -> Self {
147 Self { cipher_key, hmac_key }
148 }
149
150 pub fn from_combined(key: &[u8; 64]) -> Self {
152 let mut cipher_key = [0u8; 32];
153 let mut hmac_key = [0u8; 32];
154 cipher_key.copy_from_slice(&key[0..32]);
155 hmac_key.copy_from_slice(&key[32..64]);
156 Self { cipher_key, hmac_key }
157 }
158
159 pub fn cipher_key(&self) -> &[u8; 32] {
161 &self.cipher_key
162 }
163
164 pub fn hmac_key(&self) -> &[u8; 32] {
166 &self.hmac_key
167 }
168
169 pub fn wrap(&self, plaintext: &[u8]) -> Result<Vec<u8>> {
173 use crate::cipher::Cipher;
174 use crate::CipherSuite;
175
176 let cipher = Cipher::new(&self.cipher_key, CipherSuite::ChaCha20Poly1305);
178 let nonce = cipher.generate_nonce();
179
180 let ciphertext = cipher.encrypt(&nonce, plaintext, &[])?;
182
183 let mut hmac_input = Vec::with_capacity(12 + ciphertext.len());
185 hmac_input.extend_from_slice(&nonce);
186 hmac_input.extend_from_slice(&ciphertext);
187
188 let mut mac = HmacSha256::new_from_slice(&self.hmac_key)
189 .expect("HMAC key size is always valid");
190 mac.update(&hmac_input);
191 let hmac = mac.finalize().into_bytes();
192
193 let mut output = Vec::with_capacity(32 + 12 + ciphertext.len());
195 output.extend_from_slice(&hmac);
196 output.extend_from_slice(&nonce);
197 output.extend_from_slice(&ciphertext);
198
199 Ok(output)
200 }
201
202 pub fn unwrap(&self, packet: &[u8]) -> Result<Vec<u8>> {
204 use crate::cipher::Cipher;
205 use crate::CipherSuite;
206
207 if packet.len() < 32 + 12 + 16 {
208 return Err(CryptoError::DecryptionFailed);
209 }
210
211 let (hmac, rest) = packet.split_at(32);
212 let (nonce, ciphertext) = rest.split_at(12);
213
214 let mut mac = HmacSha256::new_from_slice(&self.hmac_key)
216 .expect("HMAC key size is always valid");
217 mac.update(nonce);
218 mac.update(ciphertext);
219 let computed = mac.finalize().into_bytes();
220
221 if !bool::from(computed.ct_eq(hmac)) {
222 return Err(CryptoError::HmacVerificationFailed);
223 }
224
225 let nonce: [u8; 12] = nonce.try_into().unwrap();
227 let cipher = Cipher::new(&self.cipher_key, CipherSuite::ChaCha20Poly1305);
228 cipher.decrypt(&nonce, ciphertext, &[])
229 }
230}
231
232#[cfg(test)]
233mod tests {
234 use super::*;
235
236 #[test]
237 fn test_hmac_auth_roundtrip() {
238 let key = [0x42u8; 32];
239 let auth = HmacAuth::from_single_key(key);
240
241 let data = b"test packet data";
242 let wrapped = auth.wrap(data);
243 let unwrapped = auth.unwrap(&wrapped).unwrap();
244
245 assert_eq!(data.as_slice(), unwrapped.as_slice());
246 }
247
248 #[test]
249 fn test_hmac_auth_tamper_detection() {
250 let key = [0x42u8; 32];
251 let auth = HmacAuth::from_single_key(key);
252
253 let mut wrapped = auth.wrap(b"test data");
254 wrapped[0] ^= 0xFF; assert!(auth.unwrap(&wrapped).is_err());
257 }
258
259 #[test]
260 fn test_tls_crypt_roundtrip() {
261 let key = TlsCryptKey::new([0x42u8; 32], [0x43u8; 32]);
262
263 let plaintext = b"secret control channel data";
264 let wrapped = key.wrap(plaintext).unwrap();
265 let unwrapped = key.unwrap(&wrapped).unwrap();
266
267 assert_eq!(plaintext.as_slice(), unwrapped.as_slice());
268 }
269
270 #[test]
271 fn test_tls_crypt_tamper_detection() {
272 let key = TlsCryptKey::new([0x42u8; 32], [0x43u8; 32]);
273
274 let mut wrapped = key.wrap(b"secret data").unwrap();
275 wrapped[40] ^= 0xFF; assert!(key.unwrap(&wrapped).is_err());
278 }
279}