fxfs_crypto/
ff1.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use crate::UnwrappedKey;
use aes::cipher::generic_array::GenericArray;
use aes::cipher::{BlockEncrypt, KeyInit};
use aes::Aes256;
use byteorder::{BigEndian, ByteOrder};

// This is a heavily specialized version of ff1 encryption as described in:
// https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38G.pdf.  This implementation
// encrypts and decrypts a u32 without a tweak. It uses radix 2.
pub struct Ff1 {
    initial_block: [u8; 16],
    cipher: Aes256,
}

impl Ff1 {
    pub fn new(key: &UnwrappedKey) -> Self {
        let cipher = Aes256::new(GenericArray::from_slice(key.key()));
        // See step 5 from the specification. radix = 2, u = 16, n = 32.
        let mut initial_block = [1, 2, 1, 0, 0, 2, 10, 16, 0, 0, 0, 32, 0, 0, 0, 0];
        cipher.encrypt_block(GenericArray::from_mut_slice(&mut initial_block));
        // initial_block is now PRF(P).
        Self { initial_block, cipher }
    }

    pub fn encrypt(&self, data: u32) -> u32 {
        // This makes assumptions that the endianness will never change, which is probably true.
        let mut a = (data >> 16) as u16;
        let mut b = data as u16;

        // ff1 uses 10 rounds.
        for i in 0..10 {
            // initial_block is PRF(P), so now we compute PRF(P || Q).
            let mut block = self.initial_block.clone();
            // xor block with Q before encrypting.
            block[13] ^= i;
            block[14] ^= (b >> 8) as u8;
            block[15] ^= b as u8;
            self.cipher.encrypt_block(GenericArray::from_mut_slice(&mut block));
            // block is now R.

            // b = 2, d = 8, so S is the first 8 bytes of block.
            let c = a.wrapping_add(BigEndian::read_u16(&block[6..8]));
            a = b;
            b = c;
        }
        (a as u32) << 16 | b as u32
    }

    // This differs from encrypt in three ways (see specification): the order of the indices is
    // reversed, the roles of A and B are swapped and modular addition is replaced by modular
    // subtraction.
    pub fn decrypt(&self, data: u32) -> u32 {
        let mut a = (data >> 16) as u16;
        let mut b = data as u16;

        for i in (0..10).rev() {
            // initial_block is PRF(P), so now we compute PRF(P || Q).
            let mut block = self.initial_block.clone();
            // xor block with Q before encrypting.
            block[13] ^= i;
            block[14] ^= (a >> 8) as u8;
            block[15] ^= a as u8;
            self.cipher.encrypt_block(GenericArray::from_mut_slice(&mut block));
            // block is now R.

            // b = 2, d = 8, so S is the first 8 bytes of block.
            let c = b.wrapping_sub(BigEndian::read_u16(&block[6..8]));
            b = a;
            a = c;
        }
        (a as u32) << 16 | b as u32
    }
}

#[cfg(test)]
mod tests {
    use super::Ff1;
    use crate::UnwrappedKey;

    #[test]
    fn test_ff1() {
        // These values have been compared against the implementation in the Rust binary-ff1 crate.
        // The rust binary-ff1 crate will produce the same results if the bits in each byte are
        // reversed on the input and likewise on the output.
        let ff1 = Ff1::new(&UnwrappedKey::new([0; 32]));
        assert_eq!(ff1.encrypt(1), 0x27c9468);
        assert_eq!(ff1.encrypt(999), 0x87a92dd5);
        assert_eq!(ff1.encrypt(11471928), 0x70c679b1);
        assert_eq!(ff1.encrypt(318689559), 0xdec5199a);

        let ff1 = Ff1::new(&UnwrappedKey::new([
            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,
            0, 1, 2,
        ]));
        assert_eq!(ff1.encrypt(1), 0x92fac14e);
        assert_eq!(ff1.encrypt(999), 0x6d2cd513);
        assert_eq!(ff1.encrypt(11471928), 0xdb672d05);
        assert_eq!(ff1.encrypt(318689559), 0x66d58b7e);

        let ff1 = Ff1::new(&UnwrappedKey::new([
            0xf8, 0x24, 0x6b, 0x2c, 0x38, 0x39, 0xfa, 0x6d, 0x98, 0xe8, 0x56, 0x17, 0x0c, 0xdd,
            0xf4, 0xf1, 0x1b, 0xa5, 0xa6, 0xcb, 0x07, 0x06, 0x58, 0x4c, 0x2a, 0x63, 0x9d, 0x32,
            0x22, 0x80, 0xe6, 0xf1,
        ]));
        assert_eq!(ff1.encrypt(1), 0xfc049c96);
        assert_eq!(ff1.encrypt(999), 0xbe10a02a);
        assert_eq!(ff1.encrypt(11471928), 0xe1290afd);
        assert_eq!(ff1.encrypt(318689559), 0xf4fcf414);

        for _ in 0..100 {
            let v = rand::random();
            assert_eq!(ff1.decrypt(ff1.encrypt(v)), v);
        }
    }
}