shield_core/
identity.rs

1//! Identity and SSO without public-key cryptography.
2//!
3//! Provides identity management, session tokens, and service tokens.
4
5// Token timestamps and indices use intentional truncation for compact encoding
6#![allow(clippy::cast_possible_truncation)]
7
8use base64::Engine;
9use ring::hmac;
10use ring::rand::{SecureRandom, SystemRandom};
11use std::collections::HashMap;
12use std::num::NonZeroU32;
13use std::time::{SystemTime, UNIX_EPOCH};
14use subtle::ConstantTimeEq;
15
16use crate::error::{Result, ShieldError};
17
18/// Generate keystream using SHA256.
19fn generate_keystream(key: &[u8], nonce: &[u8], length: usize) -> Vec<u8> {
20    let mut keystream = Vec::with_capacity(length.div_ceil(32) * 32);
21    let num_blocks = length.div_ceil(32);
22
23    for i in 0..num_blocks {
24        let counter = (i as u32).to_le_bytes();
25        let mut data = Vec::with_capacity(key.len() + nonce.len() + 4);
26        data.extend_from_slice(key);
27        data.extend_from_slice(nonce);
28        data.extend_from_slice(&counter);
29
30        let hash = ring::digest::digest(&ring::digest::SHA256, &data);
31        keystream.extend_from_slice(hash.as_ref());
32    }
33
34    keystream.truncate(length);
35    keystream
36}
37
38/// User identity.
39#[derive(Clone)]
40pub struct Identity {
41    pub user_id: String,
42    pub display_name: String,
43    pub verification_key: [u8; 32],
44    pub attributes: HashMap<String, String>,
45    pub created_at: u64,
46}
47
48/// Session information.
49#[derive(Clone)]
50pub struct Session {
51    pub user_id: String,
52    pub permissions: Vec<String>,
53    pub expires_at: Option<u64>,
54    pub attributes: HashMap<String, String>,
55}
56
57impl Session {
58    /// Check if session is expired.
59    #[must_use] 
60    pub fn is_expired(&self) -> bool {
61        match self.expires_at {
62            None => false,
63            Some(expires) => {
64                let now = SystemTime::now()
65                    .duration_since(UNIX_EPOCH)
66                    .unwrap()
67                    .as_secs();
68                now > expires
69            }
70        }
71    }
72
73    /// Check if session has permission.
74    #[must_use] 
75    pub fn has_permission(&self, permission: &str) -> bool {
76        self.permissions.contains(&permission.to_string())
77    }
78}
79
80/// Stored user data.
81struct UserData {
82    password_hash: [u8; 32],
83    salt: [u8; 16],
84    identity: Identity,
85}
86
87/// Identity provider for managing users and sessions.
88pub struct IdentityProvider {
89    master_key: [u8; 32],
90    token_ttl: u64,
91    users: HashMap<String, UserData>,
92}
93
94impl IdentityProvider {
95    const ITERATIONS: u32 = 100_000;
96
97    /// Create new identity provider.
98    #[must_use] 
99    pub fn new(master_key: [u8; 32], token_ttl: u64) -> Self {
100        Self {
101            master_key,
102            token_ttl: if token_ttl == 0 { 3600 } else { token_ttl },
103            users: HashMap::new(),
104        }
105    }
106
107    /// Derive key for specific purpose.
108    fn derive_key(&self, purpose: &str) -> [u8; 32] {
109        let mut data = Vec::with_capacity(32 + purpose.len());
110        data.extend_from_slice(&self.master_key);
111        data.extend_from_slice(purpose.as_bytes());
112        let hash = ring::digest::digest(&ring::digest::SHA256, &data);
113        let mut key = [0u8; 32];
114        key.copy_from_slice(hash.as_ref());
115        key
116    }
117
118    /// Register a new user.
119    pub fn register(
120        &mut self,
121        user_id: &str,
122        password: &str,
123        display_name: Option<&str>,
124        attributes: HashMap<String, String>,
125    ) -> Result<Identity> {
126        if self.users.contains_key(user_id) {
127            return Err(ShieldError::UserExists(user_id.to_string()));
128        }
129
130        let rng = SystemRandom::new();
131        let mut salt = [0u8; 16];
132        rng.fill(&mut salt).map_err(|_| ShieldError::RandomFailed)?;
133
134        let mut password_hash = [0u8; 32];
135        ring::pbkdf2::derive(
136            ring::pbkdf2::PBKDF2_HMAC_SHA256,
137            NonZeroU32::new(Self::ITERATIONS).unwrap(),
138            &salt,
139            password.as_bytes(),
140            &mut password_hash,
141        );
142
143        // Generate verification key
144        let verify_key = self.derive_key("verify");
145        let mut vk_data = Vec::with_capacity(32 + user_id.len());
146        vk_data.extend_from_slice(&verify_key);
147        vk_data.extend_from_slice(user_id.as_bytes());
148        let vk_hash = ring::digest::digest(&ring::digest::SHA256, &vk_data);
149        let mut verification_key = [0u8; 32];
150        verification_key.copy_from_slice(vk_hash.as_ref());
151
152        let now = SystemTime::now()
153            .duration_since(UNIX_EPOCH)
154            .unwrap()
155            .as_secs();
156
157        let identity = Identity {
158            user_id: user_id.to_string(),
159            display_name: display_name.unwrap_or(user_id).to_string(),
160            verification_key,
161            attributes,  // Consume owned value, no clone needed
162            created_at: now,
163        };
164
165        self.users.insert(
166            user_id.to_string(),
167            UserData {
168                password_hash,
169                salt,
170                identity: identity.clone(),
171            },
172        );
173
174        Ok(identity)
175    }
176
177    /// Authenticate user and return session token.
178    #[must_use]
179    pub fn authenticate(
180        &self,
181        user_id: &str,
182        password: &str,
183        permissions: &[String],
184        ttl: Option<u64>,
185    ) -> Option<String> {
186        let user = self.users.get(user_id)?;
187
188        let mut password_hash = [0u8; 32];
189        ring::pbkdf2::derive(
190            ring::pbkdf2::PBKDF2_HMAC_SHA256,
191            NonZeroU32::new(Self::ITERATIONS).unwrap(),
192            &user.salt,
193            password.as_bytes(),
194            &mut password_hash,
195        );
196
197        if password_hash.ct_eq(&user.password_hash).unwrap_u8() != 1 {
198            return None;
199        }
200
201        Some(self.create_token(user_id, permissions, ttl.unwrap_or(self.token_ttl)))
202    }
203
204    /// Create session token.
205    fn create_token(&self, user_id: &str, permissions: &[String], ttl: u64) -> String {
206        let rng = SystemRandom::new();
207        let mut nonce = [0u8; 16];
208        rng.fill(&mut nonce).unwrap();
209
210        let now = SystemTime::now()
211            .duration_since(UNIX_EPOCH)
212            .unwrap()
213            .as_secs();
214        let expires_at = now + ttl;
215
216        // Serialize token data
217        let user_id_bytes = user_id.as_bytes();
218        let perms_json = serde_json::to_string(permissions).unwrap();
219        let perms_bytes = perms_json.as_bytes();
220
221        let mut token_data = Vec::new();
222        token_data.extend_from_slice(&(user_id_bytes.len() as u16).to_le_bytes());
223        token_data.extend_from_slice(user_id_bytes);
224        token_data.extend_from_slice(&(perms_bytes.len() as u16).to_le_bytes());
225        token_data.extend_from_slice(perms_bytes);
226        token_data.extend_from_slice(&expires_at.to_le_bytes());
227
228        // Encrypt
229        let key = self.derive_key("session");
230        let keystream = generate_keystream(&key, &nonce, token_data.len());
231        let encrypted: Vec<u8> = token_data
232            .iter()
233            .zip(keystream.iter())
234            .map(|(p, k)| p ^ k)
235            .collect();
236
237        // MAC
238        let hmac_key = hmac::Key::new(hmac::HMAC_SHA256, &key);
239        let mut hmac_data = Vec::with_capacity(16 + encrypted.len());
240        hmac_data.extend_from_slice(&nonce);
241        hmac_data.extend_from_slice(&encrypted);
242        let tag = hmac::sign(&hmac_key, &hmac_data);
243
244        let mut result = Vec::with_capacity(16 + encrypted.len() + 16);
245        result.extend_from_slice(&nonce);
246        result.extend_from_slice(&encrypted);
247        result.extend_from_slice(&tag.as_ref()[..16]);
248
249        base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(&result)
250    }
251
252    /// Validate session token.
253    #[must_use] 
254    pub fn validate_token(&self, token: &str) -> Option<Session> {
255        let data = base64::engine::general_purpose::URL_SAFE_NO_PAD
256            .decode(token)
257            .ok()?;
258
259        if data.len() < 34 {
260            return None;
261        }
262
263        let nonce = &data[..16];
264        let encrypted = &data[16..data.len() - 16];
265        let mac = &data[data.len() - 16..];
266
267        let key = self.derive_key("session");
268
269        // Verify MAC
270        let hmac_key = hmac::Key::new(hmac::HMAC_SHA256, &key);
271        let mut hmac_data = Vec::with_capacity(16 + encrypted.len());
272        hmac_data.extend_from_slice(nonce);
273        hmac_data.extend_from_slice(encrypted);
274        let expected_tag = hmac::sign(&hmac_key, &hmac_data);
275
276        if mac.ct_eq(&expected_tag.as_ref()[..16]).unwrap_u8() != 1 {
277            return None;
278        }
279
280        // Decrypt
281        let keystream = generate_keystream(&key, nonce, encrypted.len());
282        let token_data: Vec<u8> = encrypted
283            .iter()
284            .zip(keystream.iter())
285            .map(|(c, k)| c ^ k)
286            .collect();
287
288        // Parse
289        let user_id_len = u16::from_le_bytes([token_data[0], token_data[1]]) as usize;
290        let user_id = String::from_utf8(token_data[2..2 + user_id_len].to_vec()).ok()?;
291
292        let offset = 2 + user_id_len;
293        let perms_len = u16::from_le_bytes([token_data[offset], token_data[offset + 1]]) as usize;
294        let perms_json =
295            String::from_utf8(token_data[offset + 2..offset + 2 + perms_len].to_vec()).ok()?;
296        let permissions: Vec<String> = serde_json::from_str(&perms_json).ok()?;
297
298        let exp_offset = offset + 2 + perms_len;
299        let expires_at = u64::from_le_bytes(token_data[exp_offset..exp_offset + 8].try_into().ok()?);
300
301        let session = Session {
302            user_id,
303            permissions,
304            expires_at: Some(expires_at),
305            attributes: HashMap::new(),
306        };
307
308        if session.is_expired() {
309            return None;
310        }
311
312        Some(session)
313    }
314
315    /// Create service-specific token.
316    #[must_use]
317    pub fn create_service_token(
318        &self,
319        session_token: &str,
320        service: &str,
321        permissions: &[String],
322        ttl: u64,
323    ) -> Option<String> {
324        let session = self.validate_token(session_token)?;
325
326        let rng = SystemRandom::new();
327        let mut nonce = [0u8; 16];
328        rng.fill(&mut nonce).ok()?;
329
330        let now = SystemTime::now()
331            .duration_since(UNIX_EPOCH)
332            .unwrap()
333            .as_secs();
334        let expires_at = now + ttl;
335
336        // Serialize
337        let user_id_bytes = session.user_id.as_bytes();
338        let service_bytes = service.as_bytes();
339        let perms_json = serde_json::to_string(permissions).unwrap();
340        let perms_bytes = perms_json.as_bytes();
341
342        let mut token_data = Vec::new();
343        token_data.extend_from_slice(&(user_id_bytes.len() as u16).to_le_bytes());
344        token_data.extend_from_slice(user_id_bytes);
345        token_data.extend_from_slice(&(service_bytes.len() as u16).to_le_bytes());
346        token_data.extend_from_slice(service_bytes);
347        token_data.extend_from_slice(&(perms_bytes.len() as u16).to_le_bytes());
348        token_data.extend_from_slice(perms_bytes);
349        token_data.extend_from_slice(&expires_at.to_le_bytes());
350
351        // Encrypt with service-specific key
352        let key = self.derive_key(&format!("service:{service}"));
353        let keystream = generate_keystream(&key, &nonce, token_data.len());
354        let encrypted: Vec<u8> = token_data
355            .iter()
356            .zip(keystream.iter())
357            .map(|(p, k)| p ^ k)
358            .collect();
359
360        let hmac_key = hmac::Key::new(hmac::HMAC_SHA256, &key);
361        let mut hmac_data = Vec::with_capacity(16 + encrypted.len());
362        hmac_data.extend_from_slice(&nonce);
363        hmac_data.extend_from_slice(&encrypted);
364        let tag = hmac::sign(&hmac_key, &hmac_data);
365
366        let mut result = Vec::with_capacity(16 + encrypted.len() + 16);
367        result.extend_from_slice(&nonce);
368        result.extend_from_slice(&encrypted);
369        result.extend_from_slice(&tag.as_ref()[..16]);
370
371        Some(base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(&result))
372    }
373
374    /// Validate service token.
375    #[must_use] 
376    pub fn validate_service_token(&self, token: &str, service: &str) -> Option<Session> {
377        let data = base64::engine::general_purpose::URL_SAFE_NO_PAD
378            .decode(token)
379            .ok()?;
380
381        if data.len() < 34 {
382            return None;
383        }
384
385        let nonce = &data[..16];
386        let encrypted = &data[16..data.len() - 16];
387        let mac = &data[data.len() - 16..];
388
389        let key = self.derive_key(&format!("service:{service}"));
390
391        // Verify MAC
392        let hmac_key = hmac::Key::new(hmac::HMAC_SHA256, &key);
393        let mut hmac_data = Vec::with_capacity(16 + encrypted.len());
394        hmac_data.extend_from_slice(nonce);
395        hmac_data.extend_from_slice(encrypted);
396        let expected_tag = hmac::sign(&hmac_key, &hmac_data);
397
398        if mac.ct_eq(&expected_tag.as_ref()[..16]).unwrap_u8() != 1 {
399            return None;
400        }
401
402        // Decrypt
403        let keystream = generate_keystream(&key, nonce, encrypted.len());
404        let token_data: Vec<u8> = encrypted
405            .iter()
406            .zip(keystream.iter())
407            .map(|(c, k)| c ^ k)
408            .collect();
409
410        // Parse
411        let mut offset = 0;
412        let user_id_len = u16::from_le_bytes([token_data[offset], token_data[offset + 1]]) as usize;
413        offset += 2;
414        let user_id = String::from_utf8(token_data[offset..offset + user_id_len].to_vec()).ok()?;
415        offset += user_id_len;
416
417        let service_len = u16::from_le_bytes([token_data[offset], token_data[offset + 1]]) as usize;
418        offset += 2;
419        let token_service =
420            String::from_utf8(token_data[offset..offset + service_len].to_vec()).ok()?;
421        offset += service_len;
422
423        if token_service != service {
424            return None;
425        }
426
427        let perms_len = u16::from_le_bytes([token_data[offset], token_data[offset + 1]]) as usize;
428        offset += 2;
429        let perms_json =
430            String::from_utf8(token_data[offset..offset + perms_len].to_vec()).ok()?;
431        let permissions: Vec<String> = serde_json::from_str(&perms_json).ok()?;
432        offset += perms_len;
433
434        let expires_at = u64::from_le_bytes(token_data[offset..offset + 8].try_into().ok()?);
435
436        let session = Session {
437            user_id,
438            permissions,
439            expires_at: Some(expires_at),
440            attributes: HashMap::new(),
441        };
442
443        if session.is_expired() {
444            return None;
445        }
446
447        Some(session)
448    }
449
450    /// Refresh session token.
451    #[must_use] 
452    pub fn refresh_token(&self, token: &str) -> Option<String> {
453        let session = self.validate_token(token)?;
454        Some(self.create_token(&session.user_id, &session.permissions, self.token_ttl))
455    }
456
457    /// Get user identity.
458    #[must_use] 
459    pub fn get_identity(&self, user_id: &str) -> Option<&Identity> {
460        self.users.get(user_id).map(|u| &u.identity)
461    }
462
463    /// Revoke user.
464    pub fn revoke_user(&mut self, user_id: &str) {
465        self.users.remove(user_id);
466    }
467}
468
469/// Secure session with automatic key rotation.
470pub struct SecureSession {
471    master_key: [u8; 32],
472    rotation_interval: u64,
473    max_old_keys: usize,
474    key_version: u32,
475    keys: HashMap<u32, [u8; 32]>,
476    last_rotation: u64,
477}
478
479impl SecureSession {
480    /// Create new secure session.
481    #[must_use] 
482    pub fn new(master_key: [u8; 32], rotation_interval: u64, max_old_keys: usize) -> Self {
483        let now = SystemTime::now()
484            .duration_since(UNIX_EPOCH)
485            .unwrap()
486            .as_secs();
487
488        let key = Self::derive_session_key(&master_key, 1);
489        let mut keys = HashMap::new();
490        keys.insert(1, key);
491
492        Self {
493            master_key,
494            rotation_interval: if rotation_interval == 0 {
495                3600
496            } else {
497                rotation_interval
498            },
499            max_old_keys: if max_old_keys == 0 { 3 } else { max_old_keys },
500            key_version: 1,
501            keys,
502            last_rotation: now,
503        }
504    }
505
506    fn derive_session_key(master_key: &[u8; 32], version: u32) -> [u8; 32] {
507        let mut data = Vec::with_capacity(32 + 16);
508        data.extend_from_slice(master_key);
509        data.extend_from_slice(format!("session:{version}").as_bytes());
510        let hash = ring::digest::digest(&ring::digest::SHA256, &data);
511        let mut key = [0u8; 32];
512        key.copy_from_slice(hash.as_ref());
513        key
514    }
515
516    fn maybe_rotate(&mut self) {
517        let now = SystemTime::now()
518            .duration_since(UNIX_EPOCH)
519            .unwrap()
520            .as_secs();
521
522        if now - self.last_rotation >= self.rotation_interval {
523            self.key_version += 1;
524            let new_key = Self::derive_session_key(&self.master_key, self.key_version);
525            self.keys.insert(self.key_version, new_key);
526            self.last_rotation = now;
527
528            // Prune old keys
529            let mut versions: Vec<u32> = self.keys.keys().copied().collect();
530            versions.sort_by(|a, b| b.cmp(a));
531            for v in versions.into_iter().skip(self.max_old_keys + 1) {
532                self.keys.remove(&v);
533            }
534        }
535    }
536
537    /// Encrypt session data.
538    pub fn encrypt(&mut self, data: &[u8]) -> Result<Vec<u8>> {
539        self.maybe_rotate();
540
541        let key = self.keys.get(&self.key_version).unwrap();
542        let rng = SystemRandom::new();
543        let mut nonce = [0u8; 16];
544        rng.fill(&mut nonce).map_err(|_| ShieldError::RandomFailed)?;
545
546        let keystream = generate_keystream(key, &nonce, data.len());
547        let ciphertext: Vec<u8> = data.iter().zip(keystream.iter()).map(|(p, k)| p ^ k).collect();
548
549        let version_bytes = self.key_version.to_le_bytes();
550
551        let hmac_key = hmac::Key::new(hmac::HMAC_SHA256, key);
552        let mut hmac_data = Vec::with_capacity(4 + 16 + ciphertext.len());
553        hmac_data.extend_from_slice(&version_bytes);
554        hmac_data.extend_from_slice(&nonce);
555        hmac_data.extend_from_slice(&ciphertext);
556        let tag = hmac::sign(&hmac_key, &hmac_data);
557
558        let mut result = Vec::with_capacity(4 + 16 + ciphertext.len() + 16);
559        result.extend_from_slice(&version_bytes);
560        result.extend_from_slice(&nonce);
561        result.extend_from_slice(&ciphertext);
562        result.extend_from_slice(&tag.as_ref()[..16]);
563
564        Ok(result)
565    }
566
567    /// Decrypt session data.
568    pub fn decrypt(&mut self, encrypted: &[u8]) -> Option<Vec<u8>> {
569        self.maybe_rotate();
570
571        if encrypted.len() < 36 {
572            return None;
573        }
574
575        let version = u32::from_le_bytes(encrypted[..4].try_into().ok()?);
576        let nonce = &encrypted[4..20];
577        let ciphertext = &encrypted[20..encrypted.len() - 16];
578        let mac = &encrypted[encrypted.len() - 16..];
579
580        let key = self.keys.get(&version)?;
581
582        // Verify MAC
583        let hmac_key = hmac::Key::new(hmac::HMAC_SHA256, key);
584        let expected_tag = hmac::sign(&hmac_key, &encrypted[..encrypted.len() - 16]);
585
586        if mac.ct_eq(&expected_tag.as_ref()[..16]).unwrap_u8() != 1 {
587            return None;
588        }
589
590        let keystream = generate_keystream(key, nonce, ciphertext.len());
591        let plaintext: Vec<u8> = ciphertext
592            .iter()
593            .zip(keystream.iter())
594            .map(|(c, k)| c ^ k)
595            .collect();
596
597        Some(plaintext)
598    }
599
600    /// Get current key version (for testing).
601    #[must_use] 
602    pub fn key_version(&self) -> u32 {
603        self.key_version
604    }
605}
606
607#[cfg(test)]
608mod tests {
609    use super::*;
610
611    #[test]
612    fn test_register_user() {
613        let mut provider = IdentityProvider::new([0u8; 32], 3600);
614        let identity = provider
615            .register("alice", "password123", Some("Alice Smith"), HashMap::new())
616            .unwrap();
617
618        assert_eq!(identity.user_id, "alice");
619        assert_eq!(identity.display_name, "Alice Smith");
620    }
621
622    #[test]
623    fn test_register_duplicate() {
624        let mut provider = IdentityProvider::new([0u8; 32], 3600);
625        provider
626            .register("alice", "password", None, HashMap::new())
627            .unwrap();
628        assert!(provider
629            .register("alice", "password2", None, HashMap::new())
630            .is_err());
631    }
632
633    #[test]
634    fn test_authenticate() {
635        let mut provider = IdentityProvider::new([0u8; 32], 3600);
636        provider
637            .register("alice", "password123", None, HashMap::new())
638            .unwrap();
639
640        let token = provider.authenticate("alice", "password123", &[], None);
641        assert!(token.is_some());
642    }
643
644    #[test]
645    fn test_authenticate_wrong_password() {
646        let mut provider = IdentityProvider::new([0u8; 32], 3600);
647        provider
648            .register("alice", "password123", None, HashMap::new())
649            .unwrap();
650
651        let token = provider.authenticate("alice", "wrongpassword", &[], None);
652        assert!(token.is_none());
653    }
654
655    #[test]
656    fn test_validate_token() {
657        let mut provider = IdentityProvider::new([0u8; 32], 3600);
658        provider
659            .register("alice", "password", None, HashMap::new())
660            .unwrap();
661        let token = provider
662            .authenticate("alice", "password", &[], None)
663            .unwrap();
664
665        let session = provider.validate_token(&token);
666        assert!(session.is_some());
667        assert_eq!(session.unwrap().user_id, "alice");
668    }
669
670    #[test]
671    fn test_service_token() {
672        let mut provider = IdentityProvider::new([0u8; 32], 3600);
673        provider
674            .register("alice", "password", None, HashMap::new())
675            .unwrap();
676        let session_token = provider
677            .authenticate("alice", "password", &[], None)
678            .unwrap();
679
680        let service_token = provider
681            .create_service_token(&session_token, "api.example.com", &["read".to_string()], 300)
682            .unwrap();
683
684        let session = provider.validate_service_token(&service_token, "api.example.com");
685        assert!(session.is_some());
686        assert_eq!(session.as_ref().unwrap().user_id, "alice");
687        assert!(session.unwrap().has_permission("read"));
688    }
689
690    #[test]
691    fn test_service_token_wrong_service() {
692        let mut provider = IdentityProvider::new([0u8; 32], 3600);
693        provider
694            .register("alice", "password", None, HashMap::new())
695            .unwrap();
696        let session_token = provider
697            .authenticate("alice", "password", &[], None)
698            .unwrap();
699        let service_token = provider
700            .create_service_token(&session_token, "api.example.com", &[], 300)
701            .unwrap();
702
703        let session = provider.validate_service_token(&service_token, "other.example.com");
704        assert!(session.is_none());
705    }
706
707    #[test]
708    fn test_secure_session() {
709        let mut session = SecureSession::new([0u8; 32], 3600, 3);
710        let plaintext = b"session data";
711        let encrypted = session.encrypt(plaintext).unwrap();
712        let decrypted = session.decrypt(&encrypted).unwrap();
713        assert_eq!(plaintext.as_slice(), decrypted.as_slice());
714    }
715
716    #[test]
717    fn test_secure_session_tampered() {
718        let mut session = SecureSession::new([0u8; 32], 3600, 3);
719        let mut encrypted = session.encrypt(b"data").unwrap();
720        encrypted[20] ^= 0xFF;
721        assert!(session.decrypt(&encrypted).is_none());
722    }
723}