1use aes::Aes128;
17use aes::cipher::generic_array::GenericArray;
18use aes::cipher::{BlockDecrypt, BlockEncrypt, KeyInit};
19
20pub const BLOCK_SIZE: usize = 16;
22pub const KEY_SIZE: usize = 16;
24
25type Block = [u8; BLOCK_SIZE];
27
28fn xor(a: &Block, b: &Block) -> Block {
31 let mut out = [0u8; BLOCK_SIZE];
32 for i in 0..BLOCK_SIZE {
33 out[i] = a[i] ^ b[i];
34 }
35 out
36}
37
38fn times2(block: &mut Block) {
42 let carry = block[0] >> 7;
43 for i in 0..BLOCK_SIZE - 1 {
44 block[i] = (block[i] << 1) | (block[i + 1] >> 7);
45 }
46 block[BLOCK_SIZE - 1] = (block[BLOCK_SIZE - 1] << 1) ^ (carry * 0x87);
47}
48
49fn times3(block: &mut Block) {
52 let mut doubled = *block;
53 times2(&mut doubled);
54 for i in 0..BLOCK_SIZE {
55 block[i] ^= doubled[i];
56 }
57}
58
59fn aes_encrypt(cipher: &Aes128, input: &Block) -> Block {
61 let mut buffer = GenericArray::clone_from_slice(input);
62 cipher.encrypt_block(&mut buffer);
63 let mut out = [0u8; BLOCK_SIZE];
64 out.copy_from_slice(buffer.as_slice());
65 out
66}
67
68fn aes_decrypt(cipher: &Aes128, input: &Block) -> Block {
70 let mut buffer = GenericArray::clone_from_slice(input);
71 cipher.decrypt_block(&mut buffer);
72 let mut out = [0u8; BLOCK_SIZE];
73 out.copy_from_slice(buffer.as_slice());
74 out
75}
76
77fn length_block(len: usize) -> Block {
80 let bits = u128::try_from(len).unwrap_or(u128::MAX).wrapping_mul(8);
83 bits.to_be_bytes()
84}
85
86pub fn ocb_encrypt(
96 cipher: &Aes128,
97 plain: &[u8],
98 nonce: &Block,
99 modify_on_xexstar_attack: bool,
100) -> (bool, Vec<u8>, Block) {
101 let mut authentic = true;
102 let mut delta = aes_encrypt(cipher, nonce);
103 let mut checksum = [0u8; BLOCK_SIZE];
104 let mut encrypted = Vec::with_capacity(plain.len());
105
106 let mut offset = 0;
107 let mut remaining = plain.len();
108 while remaining > BLOCK_SIZE {
109 let block = read_block(plain, offset);
110
111 let mut flip_a_bit = false;
114 if remaining - BLOCK_SIZE <= BLOCK_SIZE {
115 let mut sum = 0u8;
116 for &byte in &block[..BLOCK_SIZE - 1] {
117 sum |= byte;
118 }
119 if sum == 0 {
120 if modify_on_xexstar_attack {
121 flip_a_bit = true;
122 } else {
123 authentic = false;
124 }
125 }
126 }
127
128 times2(&mut delta);
129 let mut tmp = xor(&delta, &block);
130 if flip_a_bit {
131 tmp[0] ^= 1;
132 }
133 tmp = aes_encrypt(cipher, &tmp);
134 encrypted.extend_from_slice(&xor(&delta, &tmp));
135
136 checksum = xor(&checksum, &block);
137 if flip_a_bit {
138 checksum[0] ^= 1;
139 }
140
141 offset += BLOCK_SIZE;
142 remaining -= BLOCK_SIZE;
143 }
144
145 times2(&mut delta);
147 let pad = aes_encrypt(cipher, &xor(&length_block(remaining), &delta));
148
149 let mut tail = pad;
150 tail[..remaining].copy_from_slice(&plain[offset..offset + remaining]);
151 checksum = xor(&checksum, &tail);
152 let out_tail = xor(&pad, &tail);
153 encrypted.extend_from_slice(&out_tail[..remaining]);
154
155 times3(&mut delta);
156 let tag = aes_encrypt(cipher, &xor(&delta, &checksum));
157
158 (authentic, encrypted, tag)
159}
160
161pub fn ocb_decrypt(cipher: &Aes128, encrypted: &[u8], nonce: &Block) -> (bool, Vec<u8>, Block) {
170 let mut authentic = true;
171 let mut delta = aes_encrypt(cipher, nonce);
172 let mut checksum = [0u8; BLOCK_SIZE];
173 let mut plain = Vec::with_capacity(encrypted.len());
174
175 let mut offset = 0;
176 let mut remaining = encrypted.len();
177 while remaining > BLOCK_SIZE {
178 let block = read_block(encrypted, offset);
179 times2(&mut delta);
180 let tmp = aes_decrypt(cipher, &xor(&delta, &block));
181 let decoded = xor(&delta, &tmp);
182 plain.extend_from_slice(&decoded);
183 checksum = xor(&checksum, &decoded);
184 offset += BLOCK_SIZE;
185 remaining -= BLOCK_SIZE;
186 }
187
188 times2(&mut delta);
190 let pad = aes_encrypt(cipher, &xor(&length_block(remaining), &delta));
191
192 let mut tail = [0u8; BLOCK_SIZE];
193 tail[..remaining].copy_from_slice(&encrypted[offset..offset + remaining]);
194 tail = xor(&tail, &pad);
195 checksum = xor(&checksum, &tail);
196 plain.extend_from_slice(&tail[..remaining]);
197
198 if tail[..BLOCK_SIZE - 1] == delta[..BLOCK_SIZE - 1] {
201 authentic = false;
202 }
203
204 times3(&mut delta);
205 let tag = aes_encrypt(cipher, &xor(&delta, &checksum));
206
207 (authentic, plain, tag)
208}
209
210fn read_block(data: &[u8], offset: usize) -> Block {
213 let mut block = [0u8; BLOCK_SIZE];
214 if let Some(slice) = data.get(offset..offset + BLOCK_SIZE) {
215 block.copy_from_slice(slice);
216 }
217 block
218}
219
220pub struct CryptState {
226 cipher: Aes128,
227 encrypt_iv: Block,
228 decrypt_iv: Block,
229 decrypt_history: [u8; 256],
230 pub good: u32,
232 pub late: u32,
234 pub lost: u32,
236}
237
238impl CryptState {
239 pub fn new(raw_key: &[u8; KEY_SIZE], encrypt_iv: &Block, decrypt_iv: &Block) -> Self {
241 Self {
242 cipher: Aes128::new(GenericArray::from_slice(raw_key)),
243 encrypt_iv: *encrypt_iv,
244 decrypt_iv: *decrypt_iv,
245 decrypt_history: [0u8; 256],
246 good: 0,
247 late: 0,
248 lost: 0,
249 }
250 }
251
252 pub fn encrypt_iv(&self) -> Block {
254 self.encrypt_iv
255 }
256
257 pub fn decrypt_iv(&self) -> Block {
259 self.decrypt_iv
260 }
261
262 pub fn set_encrypt_iv(&mut self, iv: &Block) {
264 self.encrypt_iv = *iv;
265 }
266
267 pub fn set_decrypt_iv(&mut self, iv: &Block) {
269 self.decrypt_iv = *iv;
270 }
271
272 pub fn encrypt(&mut self, plain: &[u8]) -> Option<Vec<u8>> {
279 increment_iv(&mut self.encrypt_iv);
280 let (authentic, ciphertext, tag) = ocb_encrypt(&self.cipher, plain, &self.encrypt_iv, true);
281 if !authentic {
282 return None;
283 }
284 let mut packet = Vec::with_capacity(4 + ciphertext.len());
285 packet.push(self.encrypt_iv[0]);
286 packet.extend_from_slice(&tag[..3]);
287 packet.extend_from_slice(&ciphertext);
288 Some(packet)
289 }
290
291 pub fn decrypt(&mut self, source: &[u8]) -> Option<Vec<u8>> {
297 if source.len() < 4 {
298 return None;
299 }
300 let (header, ciphertext) = source.split_at(4);
301 let ivbyte = header[0];
302 let saveiv = self.decrypt_iv;
303 let mut restore = false;
304 let mut late: i32 = 0;
305 let mut lost: i32 = 0;
306
307 if self.decrypt_iv[0].wrapping_add(1) == ivbyte {
308 if ivbyte > self.decrypt_iv[0] {
310 self.decrypt_iv[0] = ivbyte;
311 } else if ivbyte < self.decrypt_iv[0] {
312 self.decrypt_iv[0] = ivbyte;
313 increment_iv_from(&mut self.decrypt_iv, 1);
314 } else {
315 return None;
316 }
317 } else {
318 let mut diff = ivbyte as i32 - self.decrypt_iv[0] as i32;
320 if diff > 128 {
321 diff -= 256;
322 } else if diff < -128 {
323 diff += 256;
324 }
325
326 if ivbyte < self.decrypt_iv[0] && diff > -30 && diff < 0 {
327 late = 1;
329 lost = -1;
330 self.decrypt_iv[0] = ivbyte;
331 restore = true;
332 } else if ivbyte > self.decrypt_iv[0] && diff > -30 && diff < 0 {
333 late = 1;
335 lost = -1;
336 self.decrypt_iv[0] = ivbyte;
337 decrement_iv_from(&mut self.decrypt_iv, 1);
338 restore = true;
339 } else if ivbyte > self.decrypt_iv[0] && diff > 0 {
340 lost = ivbyte as i32 - self.decrypt_iv[0] as i32 - 1;
342 self.decrypt_iv[0] = ivbyte;
343 } else if ivbyte < self.decrypt_iv[0] && diff > 0 {
344 lost = 256 - self.decrypt_iv[0] as i32 + ivbyte as i32 - 1;
346 self.decrypt_iv[0] = ivbyte;
347 increment_iv_from(&mut self.decrypt_iv, 1);
348 } else {
349 return None;
350 }
351
352 if self.decrypt_history[self.decrypt_iv[0] as usize] == self.decrypt_iv[1] {
353 self.decrypt_iv = saveiv;
354 return None;
355 }
356 }
357
358 let (authentic, plain, tag) = ocb_decrypt(&self.cipher, ciphertext, &self.decrypt_iv);
359 if !authentic || tag[..3] != header[1..4] {
360 self.decrypt_iv = saveiv;
361 return None;
362 }
363
364 self.decrypt_history[self.decrypt_iv[0] as usize] = self.decrypt_iv[1];
365 if restore {
366 self.decrypt_iv = saveiv;
367 }
368
369 self.good = self.good.wrapping_add(1);
370 apply_stat(&mut self.late, late);
371 apply_stat(&mut self.lost, lost);
372 Some(plain)
373 }
374}
375
376fn increment_iv(iv: &mut Block) {
379 increment_iv_from(iv, 0);
380}
381
382fn increment_iv_from(iv: &mut Block, start: usize) {
383 for byte in iv.iter_mut().skip(start) {
384 *byte = byte.wrapping_add(1);
385 if *byte != 0 {
386 break;
387 }
388 }
389}
390
391fn decrement_iv_from(iv: &mut Block, start: usize) {
394 for byte in iv.iter_mut().skip(start) {
395 let was = *byte;
396 *byte = byte.wrapping_sub(1);
397 if was != 0 {
398 break;
399 }
400 }
401}
402
403fn apply_stat(counter: &mut u32, delta: i32) {
407 if delta > 0 {
408 *counter = counter.wrapping_add(delta as u32);
409 } else if (*counter as i32) > delta.abs() {
410 *counter -= delta.unsigned_abs();
411 }
412}
413
414#[cfg(test)]
415mod tests {
416 #![allow(clippy::expect_used)]
419
420 use super::*;
421
422 const RAW_KEY: [u8; KEY_SIZE] = [
425 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
426 0x0f,
427 ];
428
429 fn cipher() -> Aes128 {
430 Aes128::new(GenericArray::from_slice(&RAW_KEY))
431 }
432
433 #[test]
435 fn ocb_test_vectors_from_krovetz_draft() {
436 let cipher = cipher();
437
438 let (authentic, ciphertext, tag) = ocb_encrypt(&cipher, &[], &RAW_KEY, true);
440 assert!(authentic);
441 assert!(ciphertext.is_empty());
442 const BLANK_TAG: Block = [
443 0xBF, 0x31, 0x08, 0x13, 0x07, 0x73, 0xAD, 0x5E, 0xC7, 0x0E, 0xC6, 0x9E, 0x78, 0x75,
444 0xA7, 0xB0,
445 ];
446 assert_eq!(tag, BLANK_TAG);
447
448 let source: Vec<u8> = (0u8..40).collect();
450 let (authentic, ciphertext, tag) = ocb_encrypt(&cipher, &source, &RAW_KEY, true);
451 assert!(authentic);
452 const LONG_TAG: Block = [
453 0x9D, 0xB0, 0xCD, 0xF8, 0x80, 0xF7, 0x3E, 0x3E, 0x10, 0xD4, 0xEB, 0x32, 0x17, 0x76,
454 0x66, 0x88,
455 ];
456 const CRYPTED: [u8; 40] = [
457 0xF7, 0x5D, 0x6B, 0xC8, 0xB4, 0xDC, 0x8D, 0x66, 0xB8, 0x36, 0xA2, 0xB0, 0x8B, 0x32,
458 0xA6, 0x36, 0x9F, 0x1C, 0xD3, 0xC5, 0x22, 0x8D, 0x79, 0xFD, 0x6C, 0x26, 0x7F, 0x5F,
459 0x6A, 0xA7, 0xB2, 0x31, 0xC7, 0xDF, 0xB9, 0xD5, 0x99, 0x51, 0xAE, 0x9C,
460 ];
461 assert_eq!(tag, LONG_TAG);
462 assert_eq!(ciphertext, CRYPTED);
463 }
464
465 #[test]
467 fn authcrypt_roundtrips_every_length_and_agrees_on_tag() {
468 let nonce: Block = [
469 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22,
470 0x11, 0x00,
471 ];
472 let cipher = cipher();
473 for len in 0u32..128 {
474 let src: Vec<u8> = (0..len).map(|i| (i + 1) as u8).collect();
475 let (enc_ok, encrypted, enc_tag) = ocb_encrypt(&cipher, &src, &nonce, true);
476 let (dec_ok, decrypted, dec_tag) = ocb_decrypt(&cipher, &encrypted, &nonce);
477 assert!(enc_ok, "encrypt authentic at len {len}");
478 assert!(dec_ok, "decrypt authentic at len {len}");
479 assert_eq!(enc_tag, dec_tag, "tags agree at len {len}");
480 assert_eq!(decrypted, src, "roundtrip at len {len}");
481 }
482 }
483
484 #[test]
486 fn xexstar_attack_is_detected() {
487 let nonce: Block = [
488 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22,
489 0x11, 0x00,
490 ];
491 let cipher = cipher();
492
493 let mut src = vec![0u8; 2 * BLOCK_SIZE];
494 src[BLOCK_SIZE - 1] = (BLOCK_SIZE * 8) as u8;
496 for b in src[BLOCK_SIZE..].iter_mut() {
498 *b = 42;
499 }
500
501 let (enc_ok, mut encrypted, mut enc_tag) = ocb_encrypt(&cipher, &src, &nonce, false);
503 let failed_encrypt = !enc_ok;
504
505 encrypted[BLOCK_SIZE - 1] ^= (BLOCK_SIZE * 8) as u8;
507 for i in 0..BLOCK_SIZE {
508 enc_tag[i] = src[BLOCK_SIZE + i] ^ encrypted[BLOCK_SIZE + i];
509 }
510
511 let (dec_ok, _plain, dec_tag) = ocb_decrypt(&cipher, &encrypted[..BLOCK_SIZE], &nonce);
513 let failed_decrypt = !dec_ok;
514
515 assert_eq!(enc_tag, dec_tag);
517 assert!(failed_encrypt);
519 assert!(failed_decrypt);
520
521 let (enc_ok, encrypted, enc_tag) = ocb_encrypt(&cipher, &src, &nonce, true);
524 let (dec_ok, decrypted, dec_tag) = ocb_decrypt(&cipher, &encrypted, &nonce);
525 assert!(enc_ok);
526 assert!(dec_ok);
527 assert_eq!(enc_tag, dec_tag);
528 assert_eq!(src[0], 0);
529 assert_eq!(decrypted[0], 1);
530 }
531
532 #[test]
534 fn tamper_with_any_bit_is_rejected() {
535 let nonce: Block = [
536 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22,
537 0x11, 0x00,
538 ];
539 let mut state = CryptState::new(&RAW_KEY, &nonce, &nonce);
540 let mut message = b"It was a funky funky town!".to_vec();
542 message.push(0);
543
544 let mut encrypted = state.encrypt(&message).expect("encrypt");
545 let body_len = message.len();
546 for i in 0..body_len * 8 {
547 encrypted[i / 8] ^= 1u8 << (i % 8);
548 assert!(
549 state.decrypt(&encrypted).is_none(),
550 "flipped bit {i} must be rejected"
551 );
552 encrypted[i / 8] ^= 1u8 << (i % 8);
553 }
554 assert_eq!(state.decrypt(&encrypted).as_deref(), Some(&message[..]));
555 }
556
557 #[test]
559 fn iv_recovery_and_replay_rejection() {
560 let mut enc = CryptState::new(&RAW_KEY, &[0u8; BLOCK_SIZE], &[0u8; BLOCK_SIZE]);
561 let forced = [0x55u8; BLOCK_SIZE];
563 enc.set_encrypt_iv(&forced);
564 let mut dec = CryptState::new(&RAW_KEY, &enc.encrypt_iv(), &enc.encrypt_iv());
565
566 let secret = b"abcdefghi\0";
567
568 let crypted = enc.encrypt(secret).expect("encrypt");
569 assert_eq!(dec.decrypt(&crypted).as_deref(), Some(&secret[..]));
570 assert!(dec.decrypt(&crypted).is_none());
572
573 let mut crypted = crypted;
575 for _ in 0..16 {
576 crypted = enc.encrypt(secret).expect("encrypt");
577 }
578 assert!(dec.decrypt(&crypted).is_some());
579
580 for _ in 0..128 {
582 dec.lost = 0;
583 let mut last = Vec::new();
584 for _ in 0..15 {
585 last = enc.encrypt(secret).expect("encrypt");
586 }
587 assert!(dec.decrypt(&last).is_some());
588 assert_eq!(dec.lost, 14);
589 }
590 assert_eq!(enc.encrypt_iv(), dec.decrypt_iv());
591
592 let mut far = Vec::new();
594 for _ in 0..257 {
595 far = enc.encrypt(secret).expect("encrypt");
596 }
597 assert!(dec.decrypt(&far).is_none());
598
599 dec.set_decrypt_iv(&enc.encrypt_iv());
601 let next = enc.encrypt(secret).expect("encrypt");
602 assert!(dec.decrypt(&next).is_some());
603 }
604
605 #[test]
607 fn reverse_recovery_within_window_then_replay_rejected() {
608 let forced = [0x55u8; BLOCK_SIZE];
609 let mut enc = CryptState::new(&RAW_KEY, &forced, &forced);
610 enc.set_encrypt_iv(&forced);
611 let mut dec = CryptState::new(&RAW_KEY, &enc.encrypt_iv(), &enc.encrypt_iv());
612
613 let secret = b"abcdefghi\0";
614
615 let mut packets = Vec::new();
617 for _ in 0..128 {
618 packets.push(enc.encrypt(secret).expect("encrypt"));
619 }
620 for i in 0..30 {
621 assert!(dec.decrypt(&packets[127 - i]).is_some(), "reverse {i}");
622 }
623 for i in 30..128 {
625 assert!(dec.decrypt(&packets[127 - i]).is_none(), "too old {i}");
626 }
627 for i in 0..30 {
629 assert!(dec.decrypt(&packets[127 - i]).is_none(), "replay {i}");
630 }
631 }
632}