use qfall_math::{error::MathError, integer::Z, integer_mod_q::MatZq, traits::MatrixDimensions};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct SISHash {
key: MatZq, }
impl SISHash {
pub fn key_gen(n: impl Into<Z>, m: impl Into<Z>, q: impl Into<Z>) -> Result<Self, MathError> {
let n: Z = n.into();
let m: Z = m.into();
let q: Z = q.into();
if n < Z::ONE {
return Err(MathError::InvalidIntegerInput(String::from(
"n must be chosen bigger than 0.",
)));
}
let mat_a = MatZq::sample_uniform(&n, &m, q);
Ok(Self { key: mat_a })
}
pub fn check_security(&self) -> Result<(), MathError> {
let n: Z = self.key.get_num_rows().into();
let m: Z = self.key.get_num_columns().into();
let q: Z = self.key.get_mod().into();
let m_bar = (&n * q.log(2).unwrap()).ceil();
if m < m_bar {
return Err(MathError::InvalidIntegerInput(String::from(
"m was chosen smaller than n log q, but it must be larger to satisfy the pigeonhole principle.",
)));
}
if q <= m_bar.sqrt().ceil() {
return Err(MathError::InvalidIntegerInput(String::from(
"q was chosen smaller than ⌈sqrt(n log q)⌉, but it must be larger to satisfy the pigeonhole principle.",
)));
}
Ok(())
}
pub fn hash(&self, value: &MatZq) -> MatZq {
if !value.is_column_vector() {
panic!("The hashed value has to be a column vector!");
}
&self.key * value
}
}
#[cfg(test)]
mod test_gen {
use super::{SISHash, Z};
use qfall_math::traits::MatrixDimensions;
#[test]
fn invalid_n() {
let res_0 = SISHash::key_gen(0, 2, 2);
let res_1 = SISHash::key_gen(-1, 2, 2);
let res_2 = SISHash::key_gen(i64::MIN, 2, 2);
assert!(res_0.is_err());
assert!(res_1.is_err());
assert!(res_2.is_err());
}
#[test]
fn insecure_m() {
let res_0 = SISHash::key_gen(1, 1, 4).unwrap();
let res_1 = SISHash::key_gen(2, 2, 2).unwrap();
let res_2 = SISHash::key_gen(4, 5, i64::MAX).unwrap();
assert!(res_0.check_security().is_err());
assert!(res_1.check_security().is_err());
assert!(res_2.check_security().is_err());
}
#[test]
fn insecure_q() {
let res_0 = SISHash::key_gen(10, 50, 6).unwrap();
let res_1 = SISHash::key_gen(5, 50, 4).unwrap();
assert!(res_0.check_security().is_err());
assert!(res_1.check_security().is_err());
}
#[test]
fn working_example() {
let hash = SISHash::key_gen(5, 18, 11).unwrap();
assert!(hash.check_security().is_ok());
assert_eq!(5, hash.key.get_num_rows());
assert_eq!(18, hash.key.get_num_columns());
assert_eq!(Z::from(11), Z::from(hash.key.get_mod()));
}
#[test]
fn availability() {
let _ = SISHash::key_gen(4i8, 4i8, 4i8);
let _ = SISHash::key_gen(4i8, 4i16, 4i32);
let _ = SISHash::key_gen(4u8, 4i64, 4u16);
let _ = SISHash::key_gen(4u64, 4u32, 4);
let _ = SISHash::key_gen(Z::ONE, 4i64, 4u16);
let _ = SISHash::key_gen(Z::ONE, Z::from(2), Z::from(2));
}
}
#[cfg(test)]
mod test_hash {
use super::{MatZq, SISHash, Z};
use qfall_math::traits::MatrixDimensions;
#[should_panic]
#[test]
fn not_column_vec() {
let hash = SISHash::key_gen(1, 3, 7).unwrap();
let value = MatZq::new(1, 3, 7);
hash.hash(&value);
}
#[should_panic]
#[test]
fn mismatching_dimensions() {
let hash = SISHash::key_gen(1, 3, 7).unwrap();
let value = MatZq::new(4, 1, 7);
hash.hash(&value);
}
#[should_panic]
#[test]
fn mismatching_moduli() {
let hash = SISHash::key_gen(1, 3, 7).unwrap();
let value = MatZq::new(3, 1, 8);
hash.hash(&value);
}
#[test]
fn working_example() {
let hash = SISHash::key_gen(5, 18, 11).unwrap();
let value = MatZq::new(18, 1, 11);
let res = hash.hash(&value);
assert_eq!(5, res.get_num_rows());
assert_eq!(1, res.get_num_columns());
assert_eq!(Z::from(11), Z::from(res.get_mod()));
}
}