1use super::aes::Aes;
64use crate::BlockCipher;
65
66const BLOCK: usize = 16;
67const TAG_LEN: usize = 16;
68
69#[inline]
80fn double(s: &[u8; BLOCK]) -> [u8; BLOCK] {
81 let msb = s[0] >> 7; let mut out = [0u8; BLOCK];
83 let mut carry = 0u8;
84 for i in (0..BLOCK).rev() {
85 let cur = s[i];
86 out[i] = (cur << 1) | carry;
87 carry = cur >> 7;
88 }
89 out[BLOCK - 1] ^= msb.wrapping_mul(0x87);
91 out
92}
93
94#[inline]
96fn xor_block(dst: &mut [u8; BLOCK], src: &[u8; BLOCK]) {
97 for i in 0..BLOCK {
98 dst[i] ^= src[i];
99 }
100}
101
102#[inline]
104fn xor_blocks(a: &[u8; BLOCK], b: &[u8; BLOCK]) -> [u8; BLOCK] {
105 let mut out = *a;
106 xor_block(&mut out, b);
107 out
108}
109
110#[inline]
113fn ntz(i: usize) -> u32 {
114 i.trailing_zeros()
115}
116
117pub struct Ocb {
128 aes: Aes,
129 l_star: [u8; BLOCK],
130 l_dollar: [u8; BLOCK],
131 l: Vec<[u8; BLOCK]>,
133}
134
135impl Ocb {
136 pub fn new(key: &[u8]) -> Option<Self> {
139 if !matches!(key.len(), 16 | 24 | 32) {
140 return None;
141 }
142 let aes = <Aes as BlockCipher>::new(key);
143
144 let mut l_star = [0u8; BLOCK];
146 aes.encrypt_block(&mut l_star);
147 let l_dollar = double(&l_star);
149 let l0 = double(&l_dollar);
150
151 Some(Self {
152 aes,
153 l_star,
154 l_dollar,
155 l: vec![l0],
156 })
157 }
158
159 fn ensure_l(&mut self, i: usize) {
162 while self.l.len() <= i {
163 let last = *self.l.last().unwrap();
164 self.l.push(double(&last));
165 }
166 }
167
168 fn init_offset(&self, nonce: &[u8]) -> [u8; BLOCK] {
173 debug_assert!((1..=15).contains(&nonce.len()));
174
175 let mut nb = [0u8; BLOCK];
178 nb[BLOCK - 1 - nonce.len()] = 0x01;
182 nb[BLOCK - nonce.len()..].copy_from_slice(nonce);
183
184 let bottom = (nb[BLOCK - 1] & 0x3f) as usize;
186
187 let mut ktop_in = nb;
189 ktop_in[BLOCK - 1] &= 0xc0;
190 let mut ktop = ktop_in;
191 self.aes.encrypt_block(&mut ktop);
192
193 let mut stretch = [0u8; BLOCK + 8];
195 stretch[..BLOCK].copy_from_slice(&ktop);
196 for i in 0..8 {
197 stretch[BLOCK + i] = ktop[i] ^ ktop[i + 1];
198 }
199
200 let mut offset = [0u8; BLOCK];
204 let byte_shift = bottom / 8;
205 let bit_shift = (bottom % 8) as u32;
206 if bit_shift == 0 {
207 offset.copy_from_slice(&stretch[byte_shift..byte_shift + BLOCK]);
208 } else {
209 for i in 0..BLOCK {
210 let hi = (stretch[byte_shift + i] as u32) << bit_shift;
211 let lo = (stretch[byte_shift + i + 1] as u32) >> (8 - bit_shift);
212 offset[i] = (hi | lo) as u8;
213 }
214 }
215 offset
216 }
217
218 fn hash_aad(&mut self, aad: &[u8]) -> [u8; BLOCK] {
224 let mut offset = [0u8; BLOCK];
225 let mut sum = [0u8; BLOCK];
226
227 let full = aad.len() / BLOCK;
228 for i in 0..full {
229 self.ensure_l(ntz(i + 1) as usize);
230 xor_block(&mut offset, &self.l[ntz(i + 1) as usize]);
231
232 let mut blk = [0u8; BLOCK];
233 blk.copy_from_slice(&aad[i * BLOCK..i * BLOCK + BLOCK]);
234 let mut enc = xor_blocks(&blk, &offset);
235 self.aes.encrypt_block(&mut enc);
236 xor_block(&mut sum, &enc);
237 }
238
239 let rem = aad.len() % BLOCK;
241 if rem != 0 {
242 xor_block(&mut offset, &self.l_star);
243 let mut blk = [0u8; BLOCK];
244 blk[..rem].copy_from_slice(&aad[full * BLOCK..]);
245 blk[rem] = 0x80; let mut enc = xor_blocks(&blk, &offset);
247 self.aes.encrypt_block(&mut enc);
248 xor_block(&mut sum, &enc);
249 }
250
251 sum
252 }
253
254 pub fn encrypt(&mut self, nonce: &[u8], aad: &[u8], plaintext: &[u8]) -> Option<Vec<u8>> {
259 if !(1..=15).contains(&nonce.len()) {
260 return None;
261 }
262
263 let mut offset = self.init_offset(nonce);
264 let mut checksum = [0u8; BLOCK];
265 let mut out = Vec::with_capacity(plaintext.len() + TAG_LEN);
266
267 let full = plaintext.len() / BLOCK;
268 for i in 0..full {
269 self.ensure_l(ntz(i + 1) as usize);
270 xor_block(&mut offset, &self.l[ntz(i + 1) as usize]);
271
272 let mut blk = [0u8; BLOCK];
273 blk.copy_from_slice(&plaintext[i * BLOCK..i * BLOCK + BLOCK]);
274 xor_block(&mut checksum, &blk);
276 let mut enc = xor_blocks(&blk, &offset);
278 self.aes.encrypt_block(&mut enc);
279 xor_block(&mut enc, &offset);
280 out.extend_from_slice(&enc);
281 }
282
283 let rem = plaintext.len() % BLOCK;
285 if rem != 0 {
286 xor_block(&mut offset, &self.l_star);
287 let mut pad = offset;
289 self.aes.encrypt_block(&mut pad);
290 let p_last = &plaintext[full * BLOCK..];
292 for i in 0..rem {
293 out.push(p_last[i] ^ pad[i]);
294 }
295 let mut cs_block = [0u8; BLOCK];
297 cs_block[..rem].copy_from_slice(p_last);
298 cs_block[rem] = 0x80;
299 xor_block(&mut checksum, &cs_block);
300 }
301
302 let mut tag_in = xor_blocks(&checksum, &offset);
304 xor_block(&mut tag_in, &self.l_dollar);
305 let mut tag = tag_in;
306 self.aes.encrypt_block(&mut tag);
307 let aad_hash = self.hash_aad(aad);
308 xor_block(&mut tag, &aad_hash);
309
310 out.extend_from_slice(&tag);
311 Some(out)
312 }
313
314 pub fn decrypt(&mut self, nonce: &[u8], aad: &[u8], input: &[u8]) -> Option<Vec<u8>> {
321 if !(1..=15).contains(&nonce.len()) {
322 return None;
323 }
324 if input.len() < TAG_LEN {
325 return None;
326 }
327 let (ciphertext, recv_tag) = input.split_at(input.len() - TAG_LEN);
328
329 let mut offset = self.init_offset(nonce);
330 let mut checksum = [0u8; BLOCK];
331 let mut plaintext = Vec::with_capacity(ciphertext.len());
332
333 let full = ciphertext.len() / BLOCK;
334 for i in 0..full {
335 self.ensure_l(ntz(i + 1) as usize);
336 xor_block(&mut offset, &self.l[ntz(i + 1) as usize]);
337
338 let mut blk = [0u8; BLOCK];
339 blk.copy_from_slice(&ciphertext[i * BLOCK..i * BLOCK + BLOCK]);
340 let mut dec = xor_blocks(&blk, &offset);
342 self.aes.decrypt_block(&mut dec);
343 xor_block(&mut dec, &offset);
344 xor_block(&mut checksum, &dec);
345 plaintext.extend_from_slice(&dec);
346 }
347
348 let rem = ciphertext.len() % BLOCK;
350 if rem != 0 {
351 xor_block(&mut offset, &self.l_star);
352 let mut pad = offset;
353 self.aes.encrypt_block(&mut pad); let c_last = &ciphertext[full * BLOCK..];
355 let mut p_last = [0u8; BLOCK];
356 for i in 0..rem {
357 p_last[i] = c_last[i] ^ pad[i];
358 plaintext.push(p_last[i]);
359 }
360 let mut cs_block = [0u8; BLOCK];
361 cs_block[..rem].copy_from_slice(&p_last[..rem]);
362 cs_block[rem] = 0x80;
363 xor_block(&mut checksum, &cs_block);
364 }
365
366 let mut tag_in = xor_blocks(&checksum, &offset);
368 xor_block(&mut tag_in, &self.l_dollar);
369 let mut tag = tag_in;
370 self.aes.encrypt_block(&mut tag);
371 let aad_hash = self.hash_aad(aad);
372 xor_block(&mut tag, &aad_hash);
373
374 if silentops::ct_eq(&tag, recv_tag) == 1 {
377 Some(plaintext)
378 } else {
379 None
380 }
381 }
382}
383
384#[cfg(test)]
389mod tests {
390 use super::*;
391
392 fn hex(s: &str) -> Vec<u8> {
393 let s: String = s.chars().filter(|c| !c.is_whitespace()).collect();
394 assert!(s.len() % 2 == 0);
395 (0..s.len())
396 .step_by(2)
397 .map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
398 .collect()
399 }
400
401 const KEY128: &str = "000102030405060708090A0B0C0D0E0F";
405
406 fn key128() -> Vec<u8> {
407 hex(KEY128)
408 }
409
410 fn check_case(nonce_hex: &str, aad_hex: &str, pt_hex: &str, expected_hex: &str) {
414 let key = key128();
415 let nonce = hex(nonce_hex);
416 let aad = hex(aad_hex);
417 let pt = hex(pt_hex);
418 let expected = hex(expected_hex);
419
420 let mut ocb = Ocb::new(&key).unwrap();
421 let out = ocb.encrypt(&nonce, &aad, &pt).unwrap();
422 assert_eq!(out, expected, "OCB encrypt mismatch (nonce {nonce_hex})");
423
424 let mut ocb2 = Ocb::new(&key).unwrap();
425 let back = ocb2.decrypt(&nonce, &aad, &out).unwrap();
426 assert_eq!(back, pt, "OCB decrypt mismatch (nonce {nonce_hex})");
427 }
428
429 #[test]
431 fn rfc7253_case1_empty() {
432 check_case("BBAA99887766554433221100", "", "", "785407BFFFC8AD9EDCC5520AC9111EE6");
433 }
434
435 #[test]
437 fn rfc7253_case2_one_block() {
438 check_case(
439 "BBAA99887766554433221101",
440 "0001020304050607",
441 "0001020304050607",
442 "6820B3657B6F615A5725BDA0D3B4EB3A257C9AF1F8F03009",
443 );
444 }
445
446 #[test]
448 fn rfc7253_case3_aad_only() {
449 check_case(
450 "BBAA99887766554433221102",
451 "0001020304050607",
452 "",
453 "81017F8203F081277152FADE694A0A00",
454 );
455 }
456
457 #[test]
459 fn rfc7253_case4_pt_no_aad() {
460 check_case(
461 "BBAA99887766554433221103",
462 "",
463 "0001020304050607",
464 "45DD69F8F5AAE72414054CD1F35D82760B2CD00D2F99BFA9",
465 );
466 }
467
468 #[test]
470 fn rfc7253_case5_full_block() {
471 check_case(
472 "BBAA99887766554433221104",
473 "000102030405060708090A0B0C0D0E0F",
474 "000102030405060708090A0B0C0D0E0F",
475 "571D535B60B277188BE5147170A9A22C3AD7A4FF3835B8C5701C1CCEC8FC3358",
476 );
477 }
478
479 #[test]
481 fn rfc7253_case7_pt_no_aad_full() {
482 check_case(
483 "BBAA99887766554433221106",
484 "",
485 "000102030405060708090A0B0C0D0E0F",
486 "5CE88EC2E0692706A915C00AEB8B2396F40E1C743F52436BDF06D8FA1ECA343D",
487 );
488 }
489
490 #[test]
493 fn rfc7253_case10_three_blocks() {
494 check_case(
495 "BBAA99887766554433221109",
496 "",
497 "000102030405060708090A0B0C0D0E0F1011121314151617",
498 "221BD0DE7FA6FE993ECCD769460A0AF2D6CDED0C395B1C3CE725F32494B9F914D85C0B1EB38357FF",
499 );
500 }
501
502 #[test]
505 fn rfc7253_case14_partial_block() {
506 check_case(
507 "BBAA9988776655443322110D",
508 "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627",
509 "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627",
510 "D5CA91748410C1751FF8A2F618255B68A0A12E093FF454606E59F9C1D0DDC54B65E8628E568BAD7AED07BA06A4A69483A7035490C5769E60",
511 );
512 }
513
514 #[test]
516 fn rfc7253_case16_partial_no_aad() {
517 check_case(
518 "BBAA9988776655443322110F",
519 "",
520 "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627",
521 "4412923493C57D5DE0D700F753CCE0D1D2D95060122E9F15A5DDBFC5787E50B5CC55EE507BCB084E479AD363AC366B95A98CA5F3000B1479",
522 );
523 }
524
525 #[test]
528 fn ocb_rejects_tampered_tag() {
529 let key = key128();
530 let nonce = hex("BBAA99887766554433221103");
531 let pt = hex("000102030405060708090A0B0C0D0E0F");
532
533 let mut ocb = Ocb::new(&key).unwrap();
534 let mut out = ocb.encrypt(&nonce, b"", &pt).unwrap();
535 let last = out.len() - 1;
536 out[last] ^= 0x01;
537
538 let mut ocb2 = Ocb::new(&key).unwrap();
539 assert!(ocb2.decrypt(&nonce, b"", &out).is_none());
540 }
541
542 #[test]
544 fn ocb_rejects_tampered_ciphertext() {
545 let key = key128();
546 let nonce = hex("BBAA99887766554433221103");
547 let pt = hex("000102030405060708090A0B0C0D0E0F");
548
549 let mut ocb = Ocb::new(&key).unwrap();
550 let mut out = ocb.encrypt(&nonce, b"", &pt).unwrap();
551 out[0] ^= 0x01;
552
553 let mut ocb2 = Ocb::new(&key).unwrap();
554 assert!(ocb2.decrypt(&nonce, b"", &out).is_none());
555 }
556
557 #[test]
559 fn ocb_rejects_modified_aad() {
560 let key = key128();
561 let nonce = hex("BBAA99887766554433221101");
562 let pt = hex("0001020304050607");
563
564 let mut ocb = Ocb::new(&key).unwrap();
565 let out = ocb.encrypt(&nonce, &hex("0001020304050607"), &pt).unwrap();
566
567 let mut ocb2 = Ocb::new(&key).unwrap();
568 assert!(ocb2.decrypt(&nonce, &hex("0001020304050608"), &out).is_none());
569 }
570
571 #[test]
575 fn ocb_aes192_aes256_roundtrip() {
576 for klen in [24usize, 32] {
577 let key: Vec<u8> = (0..klen).map(|i| i as u8).collect();
578 let nonce = hex("BBAA99887766554433221103");
579 let aad = b"associated data";
580 let pt = b"a message that spans more than a couple of AES blocks for good measure!!";
581
582 let mut ocb = Ocb::new(&key).unwrap();
583 let out = ocb.encrypt(&nonce, aad, pt).unwrap();
584 let mut ocb2 = Ocb::new(&key).unwrap();
585 let back = ocb2.decrypt(&nonce, aad, &out).unwrap();
586 assert_eq!(back.as_slice(), pt.as_slice(), "AES-{} OCB round-trip", klen * 8);
587 }
588 }
589
590 fn num2str96(v: u32) -> [u8; 12] {
611 let mut n = [0u8; 12];
612 n[8..].copy_from_slice(&v.to_be_bytes());
613 n
614 }
615
616 fn ocb_summary_kat(keylen: usize) -> Vec<u8> {
620 let mut key = vec![0u8; keylen];
621 *key.last_mut().unwrap() = 128u8; let mut c: Vec<u8> = Vec::new();
624 for i in 0u32..128 {
625 let s = vec![0u8; i as usize];
627
628 let mut ocb = Ocb::new(&key).unwrap();
629 let n = num2str96(3 * i + 1);
630 c.extend_from_slice(&ocb.encrypt(&n, &s, &s).unwrap());
631
632 let mut ocb = Ocb::new(&key).unwrap();
633 let n = num2str96(3 * i + 2);
634 c.extend_from_slice(&ocb.encrypt(&n, &[], &s).unwrap());
635
636 let mut ocb = Ocb::new(&key).unwrap();
637 let n = num2str96(3 * i + 3);
638 c.extend_from_slice(&ocb.encrypt(&n, &s, &[]).unwrap());
639 }
640
641 let mut ocb = Ocb::new(&key).unwrap();
643 let n = num2str96(385);
644 ocb.encrypt(&n, &c, &[]).unwrap()
645 }
646
647 #[test]
648 fn rfc7253_appendix_a_summary_aes128() {
649 assert_eq!(ocb_summary_kat(16), hex("67E944D23256C5E0B6C61FA22FDF1EA2"));
650 }
651
652 #[test]
653 fn rfc7253_appendix_a_summary_aes192() {
654 assert_eq!(ocb_summary_kat(24), hex("F673F2C3E7174AAE7BAE986CA9F29E17"));
655 }
656
657 #[test]
658 fn rfc7253_appendix_a_summary_aes256() {
659 assert_eq!(ocb_summary_kat(32), hex("D90EB8E9C977C88B79DD793D7FFA161C"));
660 }
661
662 #[test]
667 fn ocb_aes256_large_message_high_ntz() {
668 let key: Vec<u8> = (0..32u8).collect();
669 let nonce = hex("BBAA99887766554433221103");
670 let aad = b"high-ntz associated data";
671 let pt: Vec<u8> = (0..(200 * 16 + 7)).map(|i| (i * 7 + 3) as u8).collect();
673
674 let mut ocb = Ocb::new(&key).unwrap();
675 let out = ocb.encrypt(&nonce, aad, &pt).unwrap();
676 assert_eq!(out.len(), pt.len() + TAG_LEN);
677 let mut ocb2 = Ocb::new(&key).unwrap();
678 let back = ocb2.decrypt(&nonce, aad, &out).unwrap();
679 assert_eq!(back, pt, "AES-256 OCB high-ntz round-trip");
680 }
681
682 #[test]
684 fn ocb_rejects_bad_params() {
685 assert!(Ocb::new(&[0u8; 17]).is_none());
686 let mut ocb = Ocb::new(&key128()).unwrap();
687 assert!(ocb.encrypt(&[], b"", b"").is_none()); assert!(ocb.encrypt(&[0u8; 16], b"", b"").is_none()); assert!(ocb.decrypt(&hex("BBAA99887766554433221100"), b"", &[0u8; 8]).is_none()); }
691}