mirror of
https://github.com/zama-ai/tfhe-rs.git
synced 2026-01-09 14:47:56 -05:00
fix(csprng): make Seed interface less confusing wrt endianness
- From a user perspective giving the same u128 seed e.g. 1u128 should have the same behavior no matter the endianness of the system
This commit is contained in:
@@ -1,8 +1,16 @@
|
||||
use crate::generators::aes_ctr::{AES_CALLS_PER_BATCH, BYTES_PER_AES_CALL, BYTES_PER_BATCH};
|
||||
|
||||
/// Represents a key used in the AES block cipher.
|
||||
///
|
||||
/// The u128 endianness should be ignored by implementations and the u128 should be seen as a simple
|
||||
/// [u8; 16].
|
||||
///
|
||||
/// Therefore, except when loading the key from a [`Seed`](`crate::seeders::Seed`), whose bytes
|
||||
/// needs to be loaded with [u128::from_le] (to keep consistency of the loaded bytes across systems
|
||||
/// endianness), the rest of the code should use the [`AesKey`] with native endian ordering such
|
||||
/// that the internal u128 is equivalent to [u8; 16].
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct AesKey(pub u128);
|
||||
pub(crate) struct AesKey(pub(crate) u128);
|
||||
|
||||
/// A trait for AES block ciphers.
|
||||
///
|
||||
|
||||
@@ -85,7 +85,15 @@ impl<BlockCipher: AesBlockCipher> AesCtrGenerator<BlockCipher> {
|
||||
|
||||
pub(crate) fn from_seed(seed: impl Into<SeedKind>) -> Self {
|
||||
match seed.into() {
|
||||
SeedKind::Ctr(seed) => Self::new(AesKey(seed.0), None, None),
|
||||
SeedKind::Ctr(seed) => {
|
||||
// AesKey has an unspoken requirement to have bytes in an order independent of
|
||||
// platform endianness, problem is the Seed(u128) has an endianness, meaning
|
||||
// 1u128 == [1, 0, ..., 0] for little endian
|
||||
// but
|
||||
// 1u128 == [0, ..., 0, 1] for big endian
|
||||
let seed_u128 = u128::from_le(seed.0);
|
||||
Self::new(AesKey(seed_u128), None, None)
|
||||
}
|
||||
SeedKind::Xof(seed) => {
|
||||
let (key, init_index) = super::xof_init(seed);
|
||||
let last_index = TableIndex::LAST.decremented();
|
||||
|
||||
@@ -117,4 +117,9 @@ mod test {
|
||||
fn test_vector_xof_seed() {
|
||||
generator_generic_test::test_vectors_xof_seed::<NeonAesRandomGenerator>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vector_xof_seed_bytes() {
|
||||
generator_generic_test::test_vectors_xof_seed_bytes::<NeonAesRandomGenerator>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,4 +117,9 @@ mod test {
|
||||
fn test_vector_xof_seed() {
|
||||
generator_generic_test::test_vectors_xof_seed::<AesniRandomGenerator>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vector_xof_seed_bytes() {
|
||||
generator_generic_test::test_vectors_xof_seed_bytes::<AesniRandomGenerator>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,4 +125,9 @@ mod test {
|
||||
fn test_vector_xof_seed() {
|
||||
generator_generic_test::test_vectors_xof_seed::<SoftwareRandomGenerator>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vector_xof_seed_bytes() {
|
||||
generator_generic_test::test_vectors_xof_seed_bytes::<SoftwareRandomGenerator>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,8 +265,7 @@ pub mod generator_generic_test {
|
||||
70, 110, 91, 181, 229, 231, 27, 225, 185, 143, 63, 238,
|
||||
];
|
||||
|
||||
let seed_bytes: [u8; 16] = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
let seed = Seed(u128::from_ne_bytes(seed_bytes));
|
||||
let seed = Seed(1u128);
|
||||
|
||||
let mut rng = G::new(seed);
|
||||
let bytes = rng.take(N_BYTES).collect::<Vec<_>>();
|
||||
@@ -295,9 +294,41 @@ pub mod generator_generic_test {
|
||||
162, 44, 120, 67, 241, 157, 31, 162, 113, 91,
|
||||
];
|
||||
|
||||
let seed_bytes: [u8; 16] = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
let seed = u128::from_ne_bytes(seed_bytes);
|
||||
let xof_seed = XofSeed::new_u128(seed, [b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h']);
|
||||
let seed = 1u128;
|
||||
let xof_seed = XofSeed::new_u128(seed, *b"abcdefgh");
|
||||
|
||||
let mut rng = G::new(xof_seed);
|
||||
let bytes = rng.take(N_BYTES).collect::<Vec<_>>();
|
||||
assert_eq!(bytes, EXPECTED_BYTE);
|
||||
}
|
||||
|
||||
pub fn test_vectors_xof_seed_bytes<G: RandomGenerator>() {
|
||||
// Number of random bytes to generate,
|
||||
// this should be 2 batch worth of aes calls (where a batch is 8 aes)
|
||||
const N_BYTES: usize = 16 * 2 * 8;
|
||||
|
||||
const EXPECTED_BYTE: [u8; N_BYTES] = [
|
||||
21, 82, 236, 82, 18, 196, 63, 129, 54, 134, 70, 114, 199, 200, 11, 5, 52, 170, 218, 49,
|
||||
127, 45, 5, 252, 214, 82, 127, 196, 241, 83, 161, 79, 139, 183, 33, 122, 126, 177, 23,
|
||||
36, 161, 122, 7, 112, 237, 154, 195, 90, 202, 218, 64, 90, 86, 190, 139, 169, 192, 105,
|
||||
248, 220, 126, 133, 60, 124, 81, 72, 183, 238, 253, 138, 141, 144, 167, 168, 94, 19,
|
||||
172, 92, 235, 113, 185, 31, 150, 143, 165, 220, 115, 83, 180, 1, 10, 130, 140, 32, 74,
|
||||
132, 76, 22, 120, 126, 68, 154, 95, 61, 202, 79, 126, 38, 217, 181, 243, 6, 218, 75,
|
||||
232, 235, 194, 255, 254, 184, 18, 122, 51, 222, 61, 167, 175, 97, 188, 186, 217, 105,
|
||||
72, 205, 130, 3, 204, 157, 252, 27, 20, 212, 136, 70, 65, 215, 164, 130, 242, 107, 214,
|
||||
150, 211, 59, 92, 13, 148, 219, 96, 181, 5, 38, 170, 48, 218, 111, 131, 246, 102, 169,
|
||||
17, 182, 253, 41, 209, 185, 79, 245, 30, 142, 192, 127, 78, 178, 68, 223, 89, 210, 27,
|
||||
84, 164, 163, 216, 188, 190, 128, 154, 224, 160, 53, 249, 10, 250, 95, 160, 94, 28, 41,
|
||||
34, 254, 232, 137, 185, 82, 82, 192, 74, 197, 19, 46, 180, 169, 182, 216, 221, 127,
|
||||
196, 185, 156, 82, 32, 133, 97, 140, 183, 67, 37, 110, 31, 210, 197, 27, 81, 197, 132,
|
||||
136, 98, 78, 218, 252, 247, 239, 205, 21, 166, 218,
|
||||
];
|
||||
|
||||
let seed = vec![
|
||||
0, 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,
|
||||
];
|
||||
let xof_seed = XofSeed::new(seed, *b"abcdefgh");
|
||||
|
||||
let mut rng = G::new(xof_seed);
|
||||
let bytes = rng.take(N_BYTES).collect::<Vec<_>>();
|
||||
|
||||
@@ -30,7 +30,7 @@ impl XofSeed {
|
||||
pub fn new_u128(seed: u128, domain_separator: [u8; Self::DOMAIN_SEP_LEN]) -> Self {
|
||||
let mut data = vec![0u8; size_of::<u128>() + domain_separator.len()];
|
||||
data[..Self::DOMAIN_SEP_LEN].copy_from_slice(domain_separator.as_slice());
|
||||
data[Self::DOMAIN_SEP_LEN..].copy_from_slice(seed.to_ne_bytes().as_slice());
|
||||
data[Self::DOMAIN_SEP_LEN..].copy_from_slice(seed.to_le_bytes().as_slice());
|
||||
|
||||
Self { data }
|
||||
}
|
||||
@@ -134,16 +134,19 @@ mod generic_tests {
|
||||
|
||||
#[test]
|
||||
fn test_xof_seed_getters() {
|
||||
let bits = u128::from_ne_bytes([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
|
||||
let seed_bytes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
|
||||
let bits = u128::from_le_bytes(seed_bytes);
|
||||
let dsep = [b't', b'f', b'h', b'e', b'k', b's', b'p', b's'];
|
||||
let seed = XofSeed::new_u128(bits, dsep);
|
||||
|
||||
let s = u128::from_ne_bytes(seed.seed().try_into().unwrap());
|
||||
let s = u128::from_le_bytes(seed.seed().try_into().unwrap());
|
||||
assert_eq!(s, bits);
|
||||
assert_eq!(seed.domain_separator(), dsep);
|
||||
assert_eq!(seed.bit_len(), 192);
|
||||
|
||||
let collected_u128s = seed.iter_u128_blocks().collect::<Vec<_>>();
|
||||
// Those u128 are used in AES computations and are just a way to handle a [u8; 16] so those
|
||||
// are ok to check in ne_bytes
|
||||
assert_eq!(
|
||||
collected_u128s,
|
||||
vec![
|
||||
@@ -155,7 +158,7 @@ mod generic_tests {
|
||||
);
|
||||
|
||||
// To make sure both constructors yield the same results
|
||||
let seed2 = XofSeed::new(bits.to_ne_bytes().to_vec(), dsep);
|
||||
let seed2 = XofSeed::new(seed_bytes.to_vec(), dsep);
|
||||
assert_eq!(seed.data, seed2.data);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user