hctr2_rs/
hctr2fp.rs

1#![allow(deprecated)]
2//! HCTR2+FP (Format-Preserving) variant of HCTR2.
3//!
4//! While standard HCTR2 operates on arbitrary bytes, HCTR2+FP ensures that ciphertext
5//! consists only of digits in a specified radix (e.g., decimal digits 0-9 for radix-10).
6//!
7//! Use cases:
8//! - Encrypting credit card numbers (decimal)
9//! - Encrypting alphanumeric identifiers (hexadecimal or custom radix)
10//! - Systems requiring format-preserving encryption
11//!
12//! Security properties:
13//! - Ciphertext length equals plaintext length
14//! - All ciphertext digits are in range [0, radix)
15//! - Requires unique (key, tweak) pairs for security
16//! - No authentication - consider AEAD if integrity protection is needed
17//! - Minimum message length depends on radix (e.g., 39 digits for decimal)
18
19#[allow(deprecated)]
20use aes::cipher::{Array, BlockCipherDecrypt, BlockCipherEncrypt, KeyInit};
21use aes::{Aes128, Aes256};
22use polyval::{Polyval, universal_hash::UniversalHash};
23
24use crate::common::{BLOCK_LENGTH, Direction, Error, absorb, xor_block, xor_blocks_3};
25use crate::hctr2::AesCipher;
26
27// Keep the old error type as an alias for backwards compatibility
28#[allow(non_camel_case_types)]
29#[deprecated(note = "Use common::Error instead")]
30pub type Hctr2FpError = Error;
31
32/// Check if a number is a power of two.
33pub const fn is_power_of_two(n: u16) -> bool {
34    n > 0 && (n & (n - 1)) == 0
35}
36
37/// Compute bits per digit for power-of-2 radix.
38pub const fn bits_per_digit(radix: u16) -> u32 {
39    radix.trailing_zeros()
40}
41
42/// Compute the minimum number of base-radix digits needed to represent 128 bits.
43/// This is: ceil(128 / log2(radix)) = smallest k where radix^k >= 2^128
44pub const fn first_block_length(radix: u16) -> usize {
45    assert!(radix >= 2 && radix <= 256);
46
47    if radix == 256 {
48        return 16;
49    }
50
51    if is_power_of_two(radix) {
52        let bpd = bits_per_digit(radix);
53        return 128_u32.div_ceil(bpd) as usize;
54    }
55
56    let mut k: usize = 1;
57    let mut capacity: u128 = radix as u128;
58
59    loop {
60        k += 1;
61        if let Some(next) = capacity.checked_mul(radix as u128) {
62            capacity = next;
63        } else {
64            return k;
65        }
66    }
67}
68
69/// Encode a 128-bit value as base-radix digits (little-endian).
70pub fn encode_base_radix(value: u128, radix: u16, output: &mut [u8]) {
71    debug_assert!((2..=256).contains(&radix));
72    let min_len = first_block_length(radix);
73    debug_assert!(output.len() >= min_len);
74
75    if radix == 256 {
76        output[..16].copy_from_slice(&value.to_le_bytes());
77        return;
78    }
79
80    if is_power_of_two(radix) {
81        let bpd = bits_per_digit(radix);
82        let mask: u128 = ((1u128) << bpd) - 1;
83        let mut bits = value;
84
85        for digit in output.iter_mut() {
86            *digit = (bits & mask) as u8;
87            bits >>= bpd;
88        }
89        return;
90    }
91
92    let mut remaining = value;
93    for digit in output.iter_mut() {
94        *digit = (remaining % radix as u128) as u8;
95        remaining /= radix as u128;
96    }
97}
98
99/// Decode base-radix digits (little-endian) to a 128-bit value.
100pub fn decode_base_radix(digits: &[u8], radix: u16) -> Result<u128, Error> {
101    debug_assert!((2..=256).contains(&radix));
102
103    if radix == 256 {
104        if digits.len() < 16 {
105            return Err(Error::InputTooShort);
106        }
107        return Ok(u128::from_le_bytes(digits[..16].try_into().unwrap()));
108    }
109
110    for &d in digits {
111        if d >= radix as u8 {
112            return Err(Error::InvalidDigit);
113        }
114    }
115
116    if is_power_of_two(radix) {
117        let bpd = bits_per_digit(radix);
118        let mut value: u128 = 0;
119
120        for (i, &digit) in digits.iter().enumerate() {
121            let shift = (i as u32) * bpd;
122            if shift < 128 {
123                value |= (digit as u128) << shift;
124            }
125        }
126        return Ok(value);
127    }
128
129    let mut value: u128 = 0;
130    for &digit in digits.iter().rev() {
131        value = value
132            .wrapping_mul(radix as u128)
133            .wrapping_add(digit as u128);
134    }
135
136    Ok(value)
137}
138
139/// Generic HCTR2+FP cipher.
140pub struct Hctr2Fp<Aes: AesCipher, const RADIX: u16> {
141    ks_enc: Aes,
142    ks_dec: Aes::Dec,
143    h: [u8; BLOCK_LENGTH],
144    l: [u8; BLOCK_LENGTH],
145}
146
147#[allow(non_camel_case_types)]
148/// HCTR2+FP with AES-128 and decimal (radix-10) format preservation.
149pub type Hctr2Fp_128_Decimal = Hctr2Fp<Aes128, 10>;
150
151#[allow(non_camel_case_types)]
152/// HCTR2+FP with AES-256 and decimal (radix-10) format preservation.
153pub type Hctr2Fp_256_Decimal = Hctr2Fp<Aes256, 10>;
154
155#[allow(non_camel_case_types)]
156/// HCTR2+FP with AES-128 and hexadecimal (radix-16) format preservation.
157pub type Hctr2Fp_128_Hex = Hctr2Fp<Aes128, 16>;
158
159#[allow(non_camel_case_types)]
160/// HCTR2+FP with AES-256 and hexadecimal (radix-16) format preservation.
161pub type Hctr2Fp_256_Hex = Hctr2Fp<Aes256, 16>;
162
163#[allow(non_camel_case_types)]
164/// HCTR2+FP with AES-128 and base-64 (radix-64) format preservation.
165pub type Hctr2Fp_128_Base64 = Hctr2Fp<Aes128, 64>;
166
167#[allow(non_camel_case_types)]
168/// HCTR2+FP with AES-256 and base-64 (radix-64) format preservation.
169pub type Hctr2Fp_256_Base64 = Hctr2Fp<Aes256, 64>;
170
171impl<Aes: AesCipher, const RADIX: u16> Hctr2Fp<Aes, RADIX> {
172    /// First block length in digits (radix-dependent).
173    pub const FIRST_BLOCK_LENGTH: usize = first_block_length(RADIX);
174
175    /// Minimum message length in digits (same as first_block_length).
176    pub const MIN_MESSAGE_LENGTH: usize = Self::FIRST_BLOCK_LENGTH;
177
178    /// AES block length in bytes (always 16).
179    pub const BLOCK_LENGTH: usize = BLOCK_LENGTH;
180
181    /// Initialize HCTR2+FP cipher state from an encryption key.
182    pub fn new(key: &[u8]) -> Self {
183        debug_assert_eq!(key.len(), Aes::KEY_LEN);
184
185        let ks_enc = Aes::new(Array::from_slice(key));
186        let ks_dec = Aes::new_dec(key);
187
188        let mut h_block = Array::clone_from_slice(&[0u8; 16]);
189        let mut l_block = Array::clone_from_slice(&{
190            let mut b = [0u8; 16];
191            b[0] = 1;
192            b
193        });
194        ks_enc.encrypt_block(&mut h_block);
195        ks_enc.encrypt_block(&mut l_block);
196
197        let h: [u8; 16] = h_block.as_slice().try_into().unwrap();
198        let l: [u8; 16] = l_block.as_slice().try_into().unwrap();
199        Self {
200            ks_enc,
201            ks_dec,
202            h,
203            l,
204        }
205    }
206
207    /// Encrypt plaintext digits to ciphertext digits using HCTR2+FP.
208    ///
209    /// All input digits must be in range [0, RADIX). Output will also be in this range.
210    pub fn encrypt(
211        &self,
212        plaintext: &[u8],
213        tweak: &[u8],
214        ciphertext: &mut [u8],
215    ) -> Result<(), Error> {
216        self.hctr2fp(plaintext, tweak, ciphertext, Direction::Encrypt)
217    }
218
219    /// Decrypt ciphertext digits to plaintext digits using HCTR2+FP.
220    pub fn decrypt(
221        &self,
222        ciphertext: &[u8],
223        tweak: &[u8],
224        plaintext: &mut [u8],
225    ) -> Result<(), Error> {
226        self.hctr2fp(ciphertext, tweak, plaintext, Direction::Decrypt)
227    }
228
229    fn hctr2fp(
230        &self,
231        src: &[u8],
232        tweak: &[u8],
233        dst: &mut [u8],
234        direction: Direction,
235    ) -> Result<(), Error> {
236        debug_assert_eq!(dst.len(), src.len());
237
238        let first_block_len = Self::FIRST_BLOCK_LENGTH;
239        if src.len() < first_block_len {
240            return Err(Error::InputTooShort);
241        }
242
243        for &digit in src {
244            if digit >= RADIX as u8 {
245                return Err(Error::InvalidDigit);
246            }
247        }
248
249        let first_part = &src[..first_block_len];
250        let tail = &src[first_block_len..];
251
252        let mut block_bytes = [0u8; BLOCK_LENGTH];
253        let tweak_len_bits = tweak.len() * 8;
254        let tweak_len_bytes: u128 = if tail.len() % BLOCK_LENGTH == 0 {
255            (2 * tweak_len_bits + 2) as u128
256        } else {
257            (2 * tweak_len_bits + 3) as u128
258        };
259        block_bytes.copy_from_slice(&tweak_len_bytes.to_le_bytes());
260
261        let mut poly = Polyval::new(Array::from_slice(&self.h));
262        poly.update(&[Array::clone_from_slice(&block_bytes)]);
263
264        let full_tweak_blocks = tweak.len() / BLOCK_LENGTH;
265        for i in 0..full_tweak_blocks {
266            let block = Array::clone_from_slice(&tweak[i * BLOCK_LENGTH..(i + 1) * BLOCK_LENGTH]);
267            poly.update(&[block]);
268        }
269        let tweak_remainder = tweak.len() % BLOCK_LENGTH;
270        if tweak_remainder > 0 {
271            let mut padded_tweak = [0u8; BLOCK_LENGTH];
272            padded_tweak[..tweak_remainder]
273                .copy_from_slice(&tweak[full_tweak_blocks * BLOCK_LENGTH..]);
274            poly.update(&[Array::clone_from_slice(&padded_tweak)]);
275        }
276
277        let poly_after_tweak = poly.clone();
278
279        match direction {
280            Direction::Encrypt => {
281                let hh = absorb(&mut poly, tail);
282                let m_bits = decode_base_radix(first_part, RADIX)?;
283                let mut mm: [u8; BLOCK_LENGTH] = m_bits.to_le_bytes();
284                xor_block(&mut mm, &hh);
285
286                let mut uu_block = Array::clone_from_slice(&mm);
287                self.ks_enc.encrypt_block(&mut uu_block);
288                let uu: [u8; BLOCK_LENGTH] = uu_block.as_slice().try_into().unwrap();
289
290                let s = xor_blocks_3(&mm, &uu, &self.l);
291                fp_xctr::<Aes, RADIX>(
292                    &self.ks_enc,
293                    &mut dst[first_block_len..],
294                    tail,
295                    &s,
296                    Direction::Encrypt,
297                );
298
299                let mut poly = poly_after_tweak;
300                let hh2 = absorb(&mut poly, &dst[first_block_len..]);
301                let mut u_bytes = uu;
302                xor_block(&mut u_bytes, &hh2);
303                encode_base_radix(
304                    u128::from_le_bytes(u_bytes),
305                    RADIX,
306                    &mut dst[..first_block_len],
307                );
308            }
309            Direction::Decrypt => {
310                let hh2 = absorb(&mut poly, tail);
311                let u_bits = decode_base_radix(first_part, RADIX)?;
312                let mut uu: [u8; BLOCK_LENGTH] = u_bits.to_le_bytes();
313                xor_block(&mut uu, &hh2);
314
315                let mut mm_block = Array::clone_from_slice(&uu);
316                self.ks_dec.decrypt_block(&mut mm_block);
317                let mm: [u8; BLOCK_LENGTH] = mm_block.as_slice().try_into().unwrap();
318
319                let s = xor_blocks_3(&mm, &uu, &self.l);
320                fp_xctr::<Aes, RADIX>(
321                    &self.ks_enc,
322                    &mut dst[first_block_len..],
323                    tail,
324                    &s,
325                    Direction::Decrypt,
326                );
327
328                let mut poly = poly_after_tweak;
329                let hh = absorb(&mut poly, &dst[first_block_len..]);
330                let mut m_bytes = mm;
331                xor_block(&mut m_bytes, &hh);
332                encode_base_radix(
333                    u128::from_le_bytes(m_bytes),
334                    RADIX,
335                    &mut dst[..first_block_len],
336                );
337            }
338        }
339
340        Ok(())
341    }
342}
343
344fn fp_xctr<Aes: BlockCipherEncrypt, const RADIX: u16>(
345    ks_enc: &Aes,
346    dst: &mut [u8],
347    src: &[u8],
348    seed: &[u8; BLOCK_LENGTH],
349    dir: Direction,
350) {
351    debug_assert_eq!(dst.len(), src.len());
352
353    let mut counter: u64 = 1;
354    let mut i = 0;
355
356    if is_power_of_two(RADIX) {
357        let bpd = bits_per_digit(RADIX);
358        let digits_per_block = 128 / bpd as usize;
359        let mask: u128 = (RADIX as u128) - 1;
360
361        let mut block = [0u8; BLOCK_LENGTH];
362
363        while i + digits_per_block <= src.len() {
364            block[..8].copy_from_slice(&counter.to_le_bytes());
365            block[8..].fill(0);
366            for j in 0..BLOCK_LENGTH {
367                block[j] ^= seed[j];
368            }
369            let mut ga_block = Array::clone_from_slice(&block);
370            ks_enc.encrypt_block(&mut ga_block);
371            let mut ks_bytes = [0u8; 16];
372            ks_bytes.copy_from_slice(ga_block.as_slice());
373            let keystream = u128::from_le_bytes(ks_bytes);
374
375            let mut ks = keystream;
376            for j in 0..digits_per_block {
377                let ks_digit = (ks & mask) as u8;
378                let adjustment = match dir {
379                    Direction::Encrypt => ks_digit,
380                    Direction::Decrypt => {
381                        (RADIX as u8).wrapping_sub(ks_digit) & ((RADIX as u8) - 1)
382                    }
383                };
384                dst[i + j] = ((src[i + j] as u16 + adjustment as u16) & (RADIX - 1)) as u8;
385                ks >>= bpd;
386            }
387
388            counter += 1;
389            i += digits_per_block;
390        }
391
392        if i < src.len() {
393            block[..8].copy_from_slice(&counter.to_le_bytes());
394            block[8..].fill(0);
395            for j in 0..BLOCK_LENGTH {
396                block[j] ^= seed[j];
397            }
398            let mut ga_block = Array::clone_from_slice(&block);
399            ks_enc.encrypt_block(&mut ga_block);
400            let mut ks_bytes = [0u8; 16];
401            ks_bytes.copy_from_slice(ga_block.as_slice());
402            let keystream = u128::from_le_bytes(ks_bytes);
403
404            let mut ks = keystream;
405            while i < src.len() {
406                let ks_digit = (ks & mask) as u8;
407                let adjustment = match dir {
408                    Direction::Encrypt => ks_digit,
409                    Direction::Decrypt => {
410                        (RADIX as u8).wrapping_sub(ks_digit) & ((RADIX as u8) - 1)
411                    }
412                };
413                dst[i] = ((src[i] as u16 + adjustment as u16) & (RADIX - 1)) as u8;
414                ks >>= bpd;
415                i += 1;
416            }
417        }
418
419        return;
420    }
421
422    let mut block = [0u8; BLOCK_LENGTH];
423
424    while i < src.len() {
425        block[..8].copy_from_slice(&counter.to_le_bytes());
426        block[8..].fill(0);
427        for j in 0..BLOCK_LENGTH {
428            block[j] ^= seed[j];
429        }
430
431        let mut ga_block = Array::clone_from_slice(&block);
432        ks_enc.encrypt_block(&mut ga_block);
433        let mut ks_bytes = [0u8; 16];
434        ks_bytes.copy_from_slice(ga_block.as_slice());
435        let keystream = u128::from_le_bytes(ks_bytes);
436
437        let ks_digit = (keystream % (RADIX as u128)) as u8;
438        match dir {
439            Direction::Encrypt => {
440                dst[i] = ((src[i] as u16 + ks_digit as u16) % RADIX) as u8;
441            }
442            Direction::Decrypt => {
443                dst[i] = ((src[i] as u16 + RADIX - ks_digit as u16) % RADIX) as u8;
444            }
445        }
446
447        counter += 1;
448        i += 1;
449    }
450}
451
452#[cfg(test)]
453mod tests {
454    use super::*;
455
456    #[test]
457    fn test_first_block_length() {
458        assert_eq!(first_block_length(2), 128);
459        assert_eq!(first_block_length(10), 39);
460        assert_eq!(first_block_length(16), 32);
461        assert_eq!(first_block_length(64), 22);
462        assert_eq!(first_block_length(256), 16);
463    }
464
465    #[test]
466    fn test_encode_decode_decimal() {
467        let value: u128 = 12345678901234567890;
468        let mut digits = [0u8; 39];
469        encode_base_radix(value, 10, &mut digits);
470
471        let decoded = decode_base_radix(&digits, 10).unwrap();
472        assert_eq!(value, decoded);
473    }
474
475    #[test]
476    fn test_encode_decode_hex() {
477        let value: u128 = 0xDEADBEEFCAFEBABE_u128 << 64 | 0x123456789ABCDEF0_u128;
478        let mut digits = [0u8; 32];
479        encode_base_radix(value, 16, &mut digits);
480
481        let decoded = decode_base_radix(&digits, 16).unwrap();
482        assert_eq!(value, decoded);
483    }
484
485    #[test]
486    fn test_encode_decode_base64() {
487        let value: u128 = u128::MAX / 2;
488        let mut digits = [0u8; 22];
489        encode_base_radix(value, 64, &mut digits);
490
491        let decoded = decode_base_radix(&digits, 64).unwrap();
492        assert_eq!(value, decoded);
493    }
494
495    #[test]
496    fn test_hctr2fp_decimal_roundtrip() {
497        let key = [0u8; 16];
498        let cipher = Hctr2Fp_128_Decimal::new(&key);
499
500        let mut plaintext = vec![0u8; 40];
501        for i in 0..38 {
502            plaintext[i] = (i % 10) as u8;
503        }
504        plaintext[38] = 2;
505        plaintext[39] = 5;
506
507        let mut ciphertext = vec![0u8; plaintext.len()];
508        let mut decrypted = vec![0u8; plaintext.len()];
509
510        cipher
511            .encrypt(&plaintext, b"tweak", &mut ciphertext)
512            .unwrap();
513
514        for &d in &ciphertext {
515            assert!(d < 10);
516        }
517
518        cipher
519            .decrypt(&ciphertext, b"tweak", &mut decrypted)
520            .unwrap();
521        assert_eq!(plaintext, decrypted);
522    }
523
524    #[test]
525    fn test_hctr2fp_hex_roundtrip() {
526        let key = [0u8; 16];
527        let cipher = Hctr2Fp_128_Hex::new(&key);
528
529        let plaintext: Vec<u8> = (0..33).map(|i| (i % 16) as u8).collect();
530        let mut ciphertext = vec![0u8; plaintext.len()];
531        let mut decrypted = vec![0u8; plaintext.len()];
532
533        cipher
534            .encrypt(&plaintext, b"tweak", &mut ciphertext)
535            .unwrap();
536
537        for &d in &ciphertext {
538            assert!(d < 16);
539        }
540
541        cipher
542            .decrypt(&ciphertext, b"tweak", &mut decrypted)
543            .unwrap();
544        assert_eq!(plaintext, decrypted);
545    }
546
547    #[test]
548    fn test_hctr2fp_decimal_nonzero_key() {
549        let key: [u8; 16] = core::array::from_fn(|i| (i + 1) as u8);
550        let cipher = Hctr2Fp_128_Decimal::new(&key);
551
552        let mut plaintext = vec![0u8; 40];
553        for i in 0..38 {
554            plaintext[i] = (i % 10) as u8;
555        }
556        plaintext[38] = 1;
557        plaintext[39] = 7;
558
559        let mut ciphertext = vec![0u8; plaintext.len()];
560        let mut decrypted = vec![0u8; plaintext.len()];
561
562        cipher
563            .encrypt(&plaintext, b"tweak", &mut ciphertext)
564            .unwrap();
565        assert_ne!(plaintext, ciphertext);
566
567        cipher
568            .decrypt(&ciphertext, b"tweak", &mut decrypted)
569            .unwrap();
570        assert_eq!(plaintext, decrypted);
571    }
572
573    #[test]
574    fn test_hctr2fp_decimal_minimum_length() {
575        let key = [0u8; 16];
576        let cipher = Hctr2Fp_128_Decimal::new(&key);
577
578        let mut plaintext = [5u8; 39];
579        plaintext[38] = 2;
580
581        let mut ciphertext = [0u8; 39];
582        let mut decrypted = [0u8; 39];
583
584        cipher.encrypt(&plaintext, b"", &mut ciphertext).unwrap();
585        cipher.decrypt(&ciphertext, b"", &mut decrypted).unwrap();
586
587        assert_eq!(plaintext.as_slice(), decrypted.as_slice());
588    }
589
590    #[test]
591    fn test_hctr2fp_decimal_too_short() {
592        let key = [0u8; 16];
593        let cipher = Hctr2Fp_128_Decimal::new(&key);
594
595        let plaintext = [5u8; 38]; // One too short
596        let mut ciphertext = [0u8; 38];
597
598        assert_eq!(
599            cipher.encrypt(&plaintext, b"", &mut ciphertext),
600            Err(Error::InputTooShort)
601        );
602    }
603
604    #[test]
605    fn test_hctr2fp_decimal_invalid_digit() {
606        let key = [0u8; 16];
607        let cipher = Hctr2Fp_128_Decimal::new(&key);
608
609        let mut plaintext = [5u8; 40];
610        plaintext[0] = 10; // Invalid digit for decimal
611        let mut ciphertext = [0u8; 40];
612
613        assert_eq!(
614            cipher.encrypt(&plaintext, b"", &mut ciphertext),
615            Err(Error::InvalidDigit)
616        );
617    }
618
619    #[test]
620    fn test_hctr2fp_different_tweaks() {
621        let key = [0u8; 16];
622        let cipher = Hctr2Fp_128_Decimal::new(&key);
623
624        let plaintext = [5u8; 40];
625        let mut ciphertext1 = [0u8; 40];
626        let mut ciphertext2 = [0u8; 40];
627
628        cipher
629            .encrypt(&plaintext, b"tweak1", &mut ciphertext1)
630            .unwrap();
631        cipher
632            .encrypt(&plaintext, b"tweak2", &mut ciphertext2)
633            .unwrap();
634
635        assert_ne!(ciphertext1, ciphertext2);
636    }
637
638    #[test]
639    fn test_hctr2fp_256_decimal_roundtrip() {
640        let key = [0u8; 32];
641        let cipher = Hctr2Fp_256_Decimal::new(&key);
642
643        let mut plaintext = vec![0u8; 50];
644        plaintext[0] = 5;
645        plaintext[1] = 7;
646        plaintext[2] = 9;
647        plaintext[38] = 3;
648        for i in 39..50 {
649            plaintext[i] = ((i - 39) % 10) as u8;
650        }
651
652        let mut ciphertext = vec![0u8; plaintext.len()];
653        let mut decrypted = vec![0u8; plaintext.len()];
654
655        cipher
656            .encrypt(&plaintext, b"tweak", &mut ciphertext)
657            .unwrap();
658        cipher
659            .decrypt(&ciphertext, b"tweak", &mut decrypted)
660            .unwrap();
661
662        assert_eq!(plaintext, decrypted);
663    }
664
665    #[test]
666    fn test_first_block_encode_decode_consistency() {
667        let value: u128 = 0x123456789ABCDEF0_u128 << 64 | 0xFEDCBA9876543210_u128;
668
669        let mut digits = [0u8; 39];
670        encode_base_radix(value, 10, &mut digits);
671
672        for &d in &digits {
673            assert!(d < 10, "digit {} out of range", d);
674        }
675
676        let decoded = decode_base_radix(&digits, 10).unwrap();
677        assert_eq!(value, decoded, "encode/decode roundtrip failed");
678    }
679
680    #[test]
681    fn test_xor_and_encode_decode() {
682        let a: u128 = 0x123456789ABCDEF0_u128 << 64 | 0xFEDCBA9876543210_u128;
683        let b: u128 = 0xFFFFFFFFFFFFFFFF_u128 << 64 | 0xFFFFFFFFFFFFFFFF_u128;
684
685        let c = a ^ b;
686
687        let mut digits = [0u8; 39];
688        encode_base_radix(c, 10, &mut digits);
689
690        let decoded = decode_base_radix(&digits, 10).unwrap();
691        assert_eq!(c, decoded);
692    }
693
694    #[test]
695    fn test_max_u128_encode_decode() {
696        let value = u128::MAX;
697
698        let mut digits = [0u8; 39];
699        encode_base_radix(value, 10, &mut digits);
700
701        assert!(digits[38] <= 3, "39th digit {} is too large", digits[38]);
702
703        let decoded = decode_base_radix(&digits, 10).unwrap();
704        assert_eq!(value, decoded);
705    }
706
707    #[test]
708    fn test_aes_encrypt_decrypt_roundtrip() {
709        #[allow(deprecated)]
710        use aes::cipher::{Array, BlockCipherDecrypt, BlockCipherEncrypt, KeyInit};
711        use aes::{Aes128, Aes128Dec};
712
713        let key = [0u8; 16];
714        let ks_enc = Aes128::new(Array::from_slice(&key));
715        let ks_dec = Aes128Dec::new(Array::from_slice(&key));
716
717        let plaintext = [1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
718
719        let mut block = Array::clone_from_slice(&plaintext);
720        ks_enc.encrypt_block(&mut block);
721        let ciphertext: [u8; 16] = block.as_slice().try_into().unwrap();
722
723        let mut block2 = Array::clone_from_slice(&ciphertext);
724        ks_dec.decrypt_block(&mut block2);
725        let decrypted: [u8; 16] = block2.as_slice().try_into().unwrap();
726
727        assert_eq!(plaintext, decrypted);
728    }
729
730    #[test]
731    fn test_hctr2fp_decimal_debug() {
732        let key = [0u8; 16];
733        let cipher = Hctr2Fp_128_Decimal::new(&key);
734
735        let plaintext = [0u8; 39];
736        let mut ciphertext = [0u8; 39];
737        let mut decrypted = [0u8; 39];
738
739        cipher.encrypt(&plaintext, b"", &mut ciphertext).unwrap();
740
741        for &d in &ciphertext {
742            assert!(d < 10, "ciphertext digit {} >= 10", d);
743        }
744
745        cipher.decrypt(&ciphertext, b"", &mut decrypted).unwrap();
746
747        assert_eq!(plaintext.as_slice(), decrypted.as_slice());
748    }
749
750    #[test]
751    fn test_hctr2fp_decimal_nonzero_plain_empty_tweak() {
752        let key = [0u8; 16];
753        let cipher = Hctr2Fp_128_Decimal::new(&key);
754
755        let mut plaintext = vec![0u8; 40];
756        for i in 0..38 {
757            plaintext[i] = ((i + 1) % 10) as u8;
758        }
759        plaintext[38] = 2;
760        plaintext[39] = 8;
761
762        let mut ciphertext = vec![0u8; 40];
763        let mut decrypted = vec![0u8; 40];
764
765        cipher.encrypt(&plaintext, b"", &mut ciphertext).unwrap();
766        cipher.decrypt(&ciphertext, b"", &mut decrypted).unwrap();
767
768        assert_eq!(plaintext, decrypted);
769    }
770
771    #[test]
772    fn test_hctr2fp_decimal_zeros_plain_with_tweak() {
773        let key = [0u8; 16];
774        let cipher = Hctr2Fp_128_Decimal::new(&key);
775
776        let plaintext = [0u8; 40];
777        let mut ciphertext = [0u8; 40];
778        let mut decrypted = [0u8; 40];
779
780        cipher
781            .encrypt(&plaintext, b"tweak", &mut ciphertext)
782            .unwrap();
783        cipher
784            .decrypt(&ciphertext, b"tweak", &mut decrypted)
785            .unwrap();
786
787        assert_eq!(plaintext.as_slice(), decrypted.as_slice());
788    }
789
790    #[test]
791    fn test_decode_specific_pattern() {
792        let mut digits = [0u8; 39];
793        digits[0] = 1;
794        digits[1] = 2;
795        digits[2] = 3;
796        digits[38] = 3;
797
798        let value = decode_base_radix(&digits, 10).unwrap();
799
800        let mut reencoded = [0u8; 39];
801        encode_base_radix(value, 10, &mut reencoded);
802
803        assert_eq!(digits.as_slice(), reencoded.as_slice());
804    }
805
806    #[test]
807    fn test_hctr2fp_decimal_single_digit_1() {
808        let key = [0u8; 16];
809        let cipher = Hctr2Fp_128_Decimal::new(&key);
810
811        let plaintext = [1u8; 40];
812        let mut ciphertext = [0u8; 40];
813        let mut decrypted = [0u8; 40];
814
815        cipher.encrypt(&plaintext, b"", &mut ciphertext).unwrap();
816        cipher.decrypt(&ciphertext, b"", &mut decrypted).unwrap();
817
818        assert_eq!(plaintext.as_slice(), decrypted.as_slice());
819    }
820}