fxfs_crypto/
ff1.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::UnwrappedKey;
6use aes::Aes256;
7use aes::cipher::generic_array::GenericArray;
8use aes::cipher::{BlockEncrypt, KeyInit};
9use byteorder::{BigEndian, ByteOrder};
10
11// This is a heavily specialized version of ff1 encryption as described in:
12// https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38G.pdf.  This implementation
13// encrypts and decrypts a u32 without a tweak. It uses radix 2.
14#[derive(Clone)]
15pub struct Ff1 {
16    initial_block: [u8; 16],
17    cipher: Aes256,
18}
19
20impl Ff1 {
21    pub fn new(key: &UnwrappedKey) -> Self {
22        let cipher = Aes256::new(GenericArray::from_slice(key));
23        // See step 5 from the specification. radix = 2, u = 16, n = 32.
24        let mut initial_block = [1, 2, 1, 0, 0, 2, 10, 16, 0, 0, 0, 32, 0, 0, 0, 0];
25        cipher.encrypt_block(GenericArray::from_mut_slice(&mut initial_block));
26        // initial_block is now PRF(P).
27        Self { initial_block, cipher }
28    }
29
30    pub fn encrypt(&self, data: u32) -> u32 {
31        // This makes assumptions that the endianness will never change, which is probably true.
32        let mut a = (data >> 16) as u16;
33        let mut b = data as u16;
34
35        // ff1 uses 10 rounds.
36        for i in 0..10 {
37            // initial_block is PRF(P), so now we compute PRF(P || Q).
38            let mut block = self.initial_block.clone();
39            // xor block with Q before encrypting.
40            block[13] ^= i;
41            block[14] ^= (b >> 8) as u8;
42            block[15] ^= b as u8;
43            self.cipher.encrypt_block(GenericArray::from_mut_slice(&mut block));
44            // block is now R.
45
46            // b = 2, d = 8, so S is the first 8 bytes of block.
47            let c = a.wrapping_add(BigEndian::read_u16(&block[6..8]));
48            a = b;
49            b = c;
50        }
51        (a as u32) << 16 | b as u32
52    }
53
54    // This differs from encrypt in three ways (see specification): the order of the indices is
55    // reversed, the roles of A and B are swapped and modular addition is replaced by modular
56    // subtraction.
57    pub fn decrypt(&self, data: u32) -> u32 {
58        let mut a = (data >> 16) as u16;
59        let mut b = data as u16;
60
61        for i in (0..10).rev() {
62            // initial_block is PRF(P), so now we compute PRF(P || Q).
63            let mut block = self.initial_block.clone();
64            // xor block with Q before encrypting.
65            block[13] ^= i;
66            block[14] ^= (a >> 8) as u8;
67            block[15] ^= a as u8;
68            self.cipher.encrypt_block(GenericArray::from_mut_slice(&mut block));
69            // block is now R.
70
71            // b = 2, d = 8, so S is the first 8 bytes of block.
72            let c = b.wrapping_sub(BigEndian::read_u16(&block[6..8]));
73            b = a;
74            a = c;
75        }
76        (a as u32) << 16 | b as u32
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use super::Ff1;
83    use crate::UnwrappedKey;
84
85    #[test]
86    fn test_ff1() {
87        // These values have been compared against the implementation in the Rust binary-ff1 crate.
88        // The rust binary-ff1 crate will produce the same results if the bits in each byte are
89        // reversed on the input and likewise on the output.
90        let ff1 = Ff1::new(&UnwrappedKey::new(vec![0; 32]));
91        assert_eq!(ff1.encrypt(1), 0x27c9468);
92        assert_eq!(ff1.encrypt(999), 0x87a92dd5);
93        assert_eq!(ff1.encrypt(11471928), 0x70c679b1);
94        assert_eq!(ff1.encrypt(318689559), 0xdec5199a);
95
96        let ff1 = Ff1::new(&UnwrappedKey::new(vec![
97            1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
98            0, 1, 2,
99        ]));
100        assert_eq!(ff1.encrypt(1), 0x92fac14e);
101        assert_eq!(ff1.encrypt(999), 0x6d2cd513);
102        assert_eq!(ff1.encrypt(11471928), 0xdb672d05);
103        assert_eq!(ff1.encrypt(318689559), 0x66d58b7e);
104
105        let ff1 = Ff1::new(&UnwrappedKey::new(vec![
106            0xf8, 0x24, 0x6b, 0x2c, 0x38, 0x39, 0xfa, 0x6d, 0x98, 0xe8, 0x56, 0x17, 0x0c, 0xdd,
107            0xf4, 0xf1, 0x1b, 0xa5, 0xa6, 0xcb, 0x07, 0x06, 0x58, 0x4c, 0x2a, 0x63, 0x9d, 0x32,
108            0x22, 0x80, 0xe6, 0xf1,
109        ]));
110        assert_eq!(ff1.encrypt(1), 0xfc049c96);
111        assert_eq!(ff1.encrypt(999), 0xbe10a02a);
112        assert_eq!(ff1.encrypt(11471928), 0xe1290afd);
113        assert_eq!(ff1.encrypt(318689559), 0xf4fcf414);
114
115        for _ in 0..100 {
116            let v = rand::random();
117            assert_eq!(ff1.decrypt(ff1.encrypt(v)), v);
118        }
119    }
120}