fuchsia / fuchsia / 099dfaa57f8e77a17e57e89b07604f2a4d648410 / . / src / storage / fxfs / src / ff1.rs

// 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::crypt::UnwrappedKey, | |

aes::{cipher::generic_array::GenericArray, Aes256, BlockEncrypt, NewBlockCipher}, | |

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 addtiion is replaced by modular | |

// subtracton. | |

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, crate::crypt::UnwrappedKey, rand}; | |

#[test] | |

fn test_ff1() { | |

// These values have been compared against other ff1 implementations. | |

let ff1 = Ff1::new(&UnwrappedKey::new([0; 32])); | |

assert_eq!(ff1.encrypt(1), 0x27c9468); | |

assert_eq!(ff1.encrypt(999), 0x87a92dd5); | |

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); | |

for _ in 0..100 { | |

let v = rand::random(); | |

assert_eq!(ff1.decrypt(ff1.encrypt(v)), v); | |

} | |

} | |

} |