|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| use num_complex::Complex;
|
| use std::collections::HashMap;
|
|
|
|
|
| static PRIMES: [u64; 25] = [
|
| 2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
|
| 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
|
| 73, 79, 83, 89, 97
|
| ];
|
|
|
| const PHI_INV: f64 = 0.6180339887498948482;
|
|
|
|
|
| #[derive(Clone, Debug)]
|
| pub struct Matrix {
|
| pub data: Vec<Complex<f64>>,
|
| pub rows: usize,
|
| pub cols: usize,
|
| }
|
|
|
| impl Matrix {
|
| pub fn zeros(rows: usize, cols: usize) -> Self {
|
| Self {
|
| data: vec![Complex::new(0.0, 0.0); rows * cols],
|
| rows,
|
| cols,
|
| }
|
| }
|
|
|
| pub fn identity(n: usize) -> Self {
|
| let mut m = Self::zeros(n, n);
|
| for i in 0..n {
|
| m.data[i * n + i] = Complex::new(1.0, 0.0);
|
| }
|
| m
|
| }
|
|
|
| pub fn get(&self, i: usize, j: usize) -> Complex<f64> {
|
| self.data[i * self.cols + j]
|
| }
|
|
|
| pub fn set(&mut self, i: usize, j: usize, val: Complex<f64>) {
|
| self.data[i * self.cols + j] = val;
|
| }
|
|
|
| pub fn matmul(&self, other: &Matrix) -> Matrix {
|
| assert_eq!(self.cols, other.rows);
|
| let mut result = Matrix::zeros(self.rows, other.cols);
|
| for i in 0..self.rows {
|
| for j in 0..other.cols {
|
| let mut sum = Complex::new(0.0, 0.0);
|
| for k in 0..self.cols {
|
| sum += self.get(i, k) * other.get(k, j);
|
| }
|
| result.set(i, j, sum);
|
| }
|
| }
|
| result
|
| }
|
|
|
| pub fn scale(&self, s: Complex<f64>) -> Matrix {
|
| let mut result = self.clone();
|
| for v in result.data.iter_mut() {
|
| *v *= s;
|
| }
|
| result
|
| }
|
|
|
| pub fn sub(&self, other: &Matrix) -> Matrix {
|
| assert_eq!(self.rows, other.rows);
|
| assert_eq!(self.cols, other.cols);
|
| let mut result = self.clone();
|
| for (a, b) in result.data.iter_mut().zip(other.data.iter()) {
|
| *a -= b;
|
| }
|
| result
|
| }
|
|
|
|
|
| pub fn try_inverse(&self) -> Option<Matrix> {
|
| assert_eq!(self.rows, self.cols);
|
| let n = self.rows;
|
| let mut aug = Matrix::zeros(n, 2 * n);
|
|
|
| for i in 0..n {
|
| for j in 0..n {
|
| aug.set(i, j, self.get(i, j));
|
| }
|
| aug.set(i, n + i, Complex::new(1.0, 0.0));
|
| }
|
|
|
| for col in 0..n {
|
|
|
| let mut max_row = col;
|
| let mut max_val = aug.get(col, col).norm();
|
| for row in (col + 1)..n {
|
| let val = aug.get(row, col).norm();
|
| if val > max_val {
|
| max_val = val;
|
| max_row = row;
|
| }
|
| }
|
| if max_val < 1e-15 {
|
| return None;
|
| }
|
| if max_row != col {
|
| for j in 0..(2 * n) {
|
| let tmp = aug.get(col, j);
|
| aug.set(col, j, aug.get(max_row, j));
|
| aug.set(max_row, j, tmp);
|
| }
|
| }
|
|
|
| let pivot = aug.get(col, col);
|
| for j in 0..(2 * n) {
|
| aug.set(col, j, aug.get(col, j) / pivot);
|
| }
|
|
|
| for row in 0..n {
|
| if row == col {
|
| continue;
|
| }
|
| let factor = aug.get(row, col);
|
| for j in 0..(2 * n) {
|
| let val = aug.get(row, j) - factor * aug.get(col, j);
|
| aug.set(row, j, val);
|
| }
|
| }
|
| }
|
|
|
| let mut result = Matrix::zeros(n, n);
|
| for i in 0..n {
|
| for j in 0..n {
|
| result.set(i, j, aug.get(i, n + j));
|
| }
|
| }
|
| Some(result)
|
| }
|
| }
|
|
|
|
|
| fn p_adic_valuation(x: f64, p: u64) -> i64 {
|
| if x.abs() < 1e-15 {
|
| return 64;
|
| }
|
| let mut n = x.abs().round() as i64;
|
| if n == 0 {
|
| return 0;
|
| }
|
| let p = p as i64;
|
| let mut v: i64 = 0;
|
| while n % p == 0 {
|
| n /= p;
|
| v += 1;
|
| }
|
| v
|
| }
|
|
|
|
|
| fn is_p_adic_integral(x: f64, p: u64) -> bool {
|
| p_adic_valuation(x, p) >= 0
|
| }
|
|
|
|
|
|
|
| fn project_to_prime_subspace(h: &Matrix, p: u64) -> Matrix {
|
| let mut h_p = Matrix::zeros(h.rows, h.cols);
|
| for i in 0..h.rows {
|
| for j in 0..h.cols {
|
| let val = h.get(i, j);
|
| if is_p_adic_integral(val.re, p) {
|
| h_p.set(i, j, val);
|
| }
|
| }
|
| }
|
| h_p
|
| }
|
|
|
|
|
|
|
| fn pade13_exp(h: &Matrix, t: f64) -> Matrix {
|
| let n = h.rows;
|
|
|
| let neg_i_t = Complex::new(0.0, -t);
|
| let a = h.scale(neg_i_t);
|
|
|
|
|
| let norm: f64 = a.data.iter().map(|x| x.norm()).sum::<f64>().sqrt();
|
| let s = (norm.log2().ceil().max(0.0)) as u32;
|
| let scale = 2.0_f64.powi(-(s as i32));
|
| let a_scaled = a.scale(Complex::new(scale, 0.0));
|
|
|
|
|
|
|
| let i_mat = Matrix::identity(n);
|
| let a2 = a_scaled.matmul(&a_scaled);
|
| let a4 = a2.matmul(&a2);
|
| let a6 = a4.matmul(&a2);
|
|
|
|
|
| let b: [f64; 7] = [1.0, 0.5, 1.0/9.0, 1.0/72.0, 1.0/1008.0, 1.0/15120.0, 1.0/665280.0];
|
|
|
|
|
| let inner_u = i_mat.scale(Complex::new(b[1], 0.0))
|
| .sub(&a2.scale(Complex::new(-b[3], 0.0)))
|
| .sub(&a4.scale(Complex::new(-b[5], 0.0)));
|
| let u_part = a_scaled.matmul(&inner_u);
|
|
|
|
|
| let v_part = i_mat.scale(Complex::new(b[0], 0.0))
|
| .sub(&a2.scale(Complex::new(-b[2], 0.0)))
|
| .sub(&a4.scale(Complex::new(-b[4], 0.0)))
|
| .sub(&a6.scale(Complex::new(-b[6], 0.0)));
|
|
|
|
|
| let mut numer = v_part.clone();
|
| let mut denom = v_part;
|
| for (n_val, u_val) in numer.data.iter_mut().zip(u_part.data.iter()) {
|
| *n_val += u_val;
|
| }
|
| for (d_val, u_val) in denom.data.iter_mut().zip(u_part.data.iter()) {
|
| *d_val -= u_val;
|
| }
|
|
|
|
|
| let d_inv = denom.try_inverse().unwrap_or_else(|| Matrix::identity(n));
|
| let mut result = d_inv.matmul(&numer);
|
|
|
|
|
| for _ in 0..s {
|
| result = result.matmul(&result);
|
| }
|
|
|
| result
|
| }
|
|
|
|
|
|
|
|
|
|
|
| pub fn zeta_operator_product(
|
| s: Complex<f64>,
|
| t: f64,
|
| hamiltonian: &Matrix,
|
| ) -> Matrix {
|
| let n = hamiltonian.rows;
|
|
|
|
|
| let mut prime_spaces: HashMap<u64, Matrix> = HashMap::new();
|
| for &p in PRIMES.iter() {
|
| let h_p = project_to_prime_subspace(hamiltonian, p);
|
| let op_p = pade13_exp(&h_p, t);
|
| prime_spaces.insert(p, op_p);
|
| }
|
|
|
|
|
| let mut result = Matrix::identity(n);
|
| for (&p, op_p) in prime_spaces.iter() {
|
|
|
| let p_minus_s = Complex::new(p as f64, 0.0).powc(-s);
|
|
|
| let scaled_op = op_p.scale(p_minus_s);
|
| let factor = Matrix::identity(n).sub(&scaled_op);
|
|
|
| if let Some(factor_inv) = factor.try_inverse() {
|
| result = result.matmul(&factor_inv);
|
| }
|
| }
|
| result
|
| }
|
|
|
|
|
|
|
| pub fn spectral_invariant_delta(
|
| hamiltonian: &Matrix,
|
| tau_k: f64,
|
| ) -> f64 {
|
|
|
| let s_point = Complex::new(0.5, tau_k);
|
| let z_st = zeta_operator_product(s_point, tau_k, hamiltonian);
|
|
|
|
|
| let n = z_st.rows;
|
| let mut max_deviation: f64 = 0.0;
|
| for i in 0..n {
|
| let diag = z_st.get(i, i);
|
| let dev = (diag - Complex::new(0.0, tau_k)).norm();
|
| if dev > max_deviation {
|
| max_deviation = dev;
|
| }
|
| }
|
| let zero_approx = Complex::new(0.5, max_deviation);
|
|
|
|
|
| let mut pole_proximity = f64::MAX;
|
| for &p in PRIMES.iter() {
|
|
|
| let s_pole = Complex::new((p as f64).ln() / PHI_INV.ln(), 0.0);
|
| let dist = (s_pole - zero_approx).norm();
|
| if dist < pole_proximity {
|
| pole_proximity = dist;
|
| }
|
| }
|
|
|
| pole_proximity
|
| }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| fn frobenius_norm(m: &Matrix) -> f64 {
|
| m.data.iter().map(|x| x.norm_sqr()).sum::<f64>().sqrt()
|
| }
|
|
|
|
|
| fn matrix_power(m: &Matrix, exp: usize) -> Matrix {
|
| if exp == 0 {
|
| return Matrix::identity(m.rows);
|
| }
|
| if exp == 1 {
|
| return m.clone();
|
| }
|
| if exp % 2 == 0 {
|
| let half = matrix_power(m, exp / 2);
|
| half.matmul(&half)
|
| } else {
|
| let rest = matrix_power(m, exp - 1);
|
| m.matmul(&rest)
|
| }
|
| }
|
|
|
|
|
| fn tensor_product(a: &Matrix, b: &Matrix) -> Matrix {
|
| let (m, n) = (a.rows, a.cols);
|
| let (p, q) = (b.rows, b.cols);
|
| let mut c = Matrix::zeros(m * p, n * q);
|
| for i in 0..m {
|
| for j in 0..n {
|
| let a_ij = a.get(i, j);
|
| for k in 0..p {
|
| for l in 0..q {
|
| c.set(i * p + k, j * q + l, a_ij * b.get(k, l));
|
| }
|
| }
|
| }
|
| }
|
| c
|
| }
|
|
|
|
|
|
|
|
|
| pub fn prime_encoded_state(
|
| hamiltonian: &Matrix,
|
| depth: usize,
|
| ) -> Matrix {
|
|
|
| if depth == 0 {
|
| return Matrix::identity(hamiltonian.rows);
|
| }
|
|
|
|
|
| let mut state = Matrix::identity(1);
|
| for &p in PRIMES.iter().take(5) {
|
|
|
| let h_p = project_to_prime_subspace(hamiltonian, p);
|
|
|
|
|
| let norm_h_p = frobenius_norm(&h_p);
|
| let k_p = p_adic_valuation(norm_h_p, p).max(0) as usize;
|
|
|
|
|
|
|
| let op_p = pade13_exp(&h_p, 1.0 / (depth as f64));
|
| let prime_state = matrix_power(&op_p, k_p.min(4));
|
|
|
|
|
|
|
| if state.rows * prime_state.rows <= 64 {
|
| state = tensor_product(&state, &prime_state);
|
| }
|
| }
|
| state
|
| }
|
|
|
|
|
|
|
| pub fn mmp_multiplicity(hamiltonian: &Matrix) -> f64 {
|
| let mut current_multiplicity: f64 = 1.0;
|
| for &p in PRIMES.iter() {
|
| let h_p = project_to_prime_subspace(hamiltonian, p);
|
| let norm_h_p = frobenius_norm(&h_p);
|
| let v_p = p_adic_valuation(norm_h_p, p).max(0) as f64;
|
| current_multiplicity *= 1.0 + v_p;
|
| }
|
| current_multiplicity
|
| }
|
|
|
|
|
| pub fn mmp_bound(n: usize) -> f64 {
|
| PHI_INV.powi(n as i32)
|
| }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| fn state_to_entropy(state: &Matrix) -> Vec<u8> {
|
| state.data.iter()
|
| .map(|x| x.norm_sqr())
|
| .take(32)
|
| .map(|p| ((p * 255.0).clamp(0.0, 255.0)) as u8)
|
| .collect()
|
| }
|
|
|
|
|
| fn blake3_kdf(salt: &[u8], ikm: &[u8]) -> [u8; 32] {
|
| let mut prk = [0u8; 32];
|
|
|
| for (i, &byte) in salt.iter().chain(ikm.iter()).enumerate() {
|
| prk[i % 32] ^= byte;
|
| }
|
|
|
| for round in 0..8 {
|
| let phi_byte = ((PHI_INV * (round as f64 + 1.0) * 256.0) as u8) & 0xFF;
|
| for i in 0..32 {
|
| prk[i] = prk[i].wrapping_add(prk[(i + 13) % 32] ^ phi_byte);
|
| }
|
| }
|
| prk
|
| }
|
|
|
|
|
| fn bind_to_fixed_point(key: &mut [u8; 32], rho_star: &Matrix) {
|
| for i in 0..32 {
|
| let row = i % rho_star.rows;
|
| let col = (i * 7) % rho_star.cols;
|
| let fp_byte = ((rho_star.get(row, col).re.abs() * 255.0).clamp(0.0, 255.0)) as u8;
|
| key[i] ^= fp_byte;
|
| }
|
| }
|
|
|
|
|
|
|
|
|
| pub fn sndl_resistant_key(
|
| hamiltonian: &Matrix,
|
| pulse_entropy: &[f64],
|
| jst_fixed_point: &Matrix,
|
| depth: usize,
|
| ) -> [u8; 32] {
|
|
|
| let pqc_state = prime_encoded_state(hamiltonian, depth);
|
| let pqc_entropy = state_to_entropy(&pqc_state);
|
|
|
|
|
| let classical_entropy: Vec<u8> = pulse_entropy.iter()
|
| .map(|&x| ((x.abs() * 255.0).clamp(0.0, 255.0)) as u8)
|
| .take(32)
|
| .collect();
|
|
|
|
|
| let pqc_key = blake3_kdf(b"SNDL-PQC-SALT-v1", &pqc_entropy);
|
| let classical_key = blake3_kdf(b"SNDL-CLASSICAL-SALT-v1", &classical_entropy);
|
| let mut shared_key = [0u8; 32];
|
| for i in 0..32 {
|
| shared_key[i] = pqc_key[i] ^ classical_key[i];
|
| }
|
|
|
|
|
| bind_to_fixed_point(&mut shared_key, jst_fixed_point);
|
|
|
| shared_key
|
| }
|
|
|
|
|
| pub fn key_freshness_hash(rho: &Matrix) -> [u8; 32] {
|
| let entropy = state_to_entropy(rho);
|
| blake3_kdf(b"SNDL-FRESHNESS-v1", &entropy)
|
| }
|
|
|
|
|
| pub fn sndl_key_rotate(current_key: &[u8; 32], depth: usize) -> [u8; 32] {
|
| let rotation_factor = PHI_INV.powi(depth as i32);
|
| let rotation_bytes: Vec<u8> = (0..32)
|
| .map(|i| ((rotation_factor * (i as f64 + 1.0) * 256.0) as u8) & 0xFF)
|
| .collect();
|
| let mut new_key = [0u8; 32];
|
| for i in 0..32 {
|
| new_key[i] = current_key[i].wrapping_add(rotation_bytes[i]);
|
| }
|
| blake3_kdf(b"SNDL-ROTATE-v1", &new_key)
|
| }
|
|
|
|
|
|
|
|
|
|
|
|
|
| #[no_mangle]
|
| pub extern "C" fn sndl_generate_key(
|
| h_ptr: *const Complex<f64>,
|
| rho_ptr: *const Complex<f64>,
|
| n: i64,
|
| depth: i64,
|
| pulse_ptr: *const f64,
|
| pulse_len: i64,
|
| out_ptr: *mut u8,
|
| ) {
|
| let n = n as usize;
|
| let h_slice = unsafe { std::slice::from_raw_parts(h_ptr, n * n) };
|
| let rho_slice = unsafe { std::slice::from_raw_parts(rho_ptr, n * n) };
|
| let pulse_slice = unsafe { std::slice::from_raw_parts(pulse_ptr, pulse_len as usize) };
|
| let h = Matrix { data: h_slice.to_vec(), rows: n, cols: n };
|
| let rho = Matrix { data: rho_slice.to_vec(), rows: n, cols: n };
|
| let key = sndl_resistant_key(&h, pulse_slice, &rho, depth as usize);
|
| let out = unsafe { std::slice::from_raw_parts_mut(out_ptr, 32) };
|
| out.copy_from_slice(&key);
|
| }
|
|
|
|
|
| #[no_mangle]
|
| pub extern "C" fn sndl_freshness_hash(
|
| rho_ptr: *const Complex<f64>,
|
| n: i64,
|
| out_ptr: *mut u8,
|
| ) {
|
| let n = n as usize;
|
| let slice = unsafe { std::slice::from_raw_parts(rho_ptr, n * n) };
|
| let rho = Matrix { data: slice.to_vec(), rows: n, cols: n };
|
| let hash = key_freshness_hash(&rho);
|
| let out = unsafe { std::slice::from_raw_parts_mut(out_ptr, 32) };
|
| out.copy_from_slice(&hash);
|
| }
|
|
|
|
|
| #[no_mangle]
|
| pub extern "C" fn sndl_rotate_key(
|
| key_ptr: *const u8,
|
| depth: i64,
|
| out_ptr: *mut u8,
|
| ) {
|
| let key_slice = unsafe { std::slice::from_raw_parts(key_ptr, 32) };
|
| let mut key = [0u8; 32];
|
| key.copy_from_slice(key_slice);
|
| let rotated = sndl_key_rotate(&key, depth as usize);
|
| let out = unsafe { std::slice::from_raw_parts_mut(out_ptr, 32) };
|
| out.copy_from_slice(&rotated);
|
| }
|
|
|
|
|
|
|
| #[no_mangle]
|
| pub extern "C" fn zmos_spectral_invariant(
|
| h_ptr: *const Complex<f64>,
|
| n: i64,
|
| tau_k: f64,
|
| ) -> f64 {
|
| let n = n as usize;
|
| let slice = unsafe { std::slice::from_raw_parts(h_ptr, n * n) };
|
| let h = Matrix {
|
| data: slice.to_vec(),
|
| rows: n,
|
| cols: n,
|
| };
|
| spectral_invariant_delta(&h, tau_k)
|
| }
|
|
|
|
|
|
|
| #[no_mangle]
|
| pub extern "C" fn zmos_operator_product(
|
| h_ptr: *const Complex<f64>,
|
| n: i64,
|
| s_re: f64,
|
| s_im: f64,
|
| t: f64,
|
| out_ptr: *mut Complex<f64>,
|
| ) {
|
| let n = n as usize;
|
| let slice = unsafe { std::slice::from_raw_parts(h_ptr, n * n) };
|
| let h = Matrix {
|
| data: slice.to_vec(),
|
| rows: n,
|
| cols: n,
|
| };
|
| let s = Complex::new(s_re, s_im);
|
| let result = zeta_operator_product(s, t, &h);
|
| let out_slice = unsafe { std::slice::from_raw_parts_mut(out_ptr, n * n) };
|
| out_slice.copy_from_slice(&result.data);
|
| }
|
|
|
|
|
|
|
| #[no_mangle]
|
| pub extern "C" fn qmhes_prime_encoded_norm(
|
| h_ptr: *const Complex<f64>,
|
| n: i64,
|
| depth: i64,
|
| ) -> f64 {
|
| let n = n as usize;
|
| let slice = unsafe { std::slice::from_raw_parts(h_ptr, n * n) };
|
| let h = Matrix {
|
| data: slice.to_vec(),
|
| rows: n,
|
| cols: n,
|
| };
|
| let state = prime_encoded_state(&h, depth as usize);
|
| frobenius_norm(&state)
|
| }
|
|
|
|
|
|
|
| #[no_mangle]
|
| pub extern "C" fn qmhes_mmp_multiplicity(
|
| h_ptr: *const Complex<f64>,
|
| n: i64,
|
| ) -> f64 {
|
| let n = n as usize;
|
| let slice = unsafe { std::slice::from_raw_parts(h_ptr, n * n) };
|
| let h = Matrix {
|
| data: slice.to_vec(),
|
| rows: n,
|
| cols: n,
|
| };
|
| mmp_multiplicity(&h)
|
| }
|
|
|
|
|
| #[no_mangle]
|
| pub extern "C" fn qmhes_mmp_bound(n: i64) -> f64 {
|
| mmp_bound(n as usize)
|
| }
|
|
|
| #[cfg(test)]
|
| mod tests {
|
| use super::*;
|
|
|
| #[test]
|
| fn test_identity_euler_product() {
|
| let h = Matrix::identity(2);
|
| let s = Complex::new(2.0, 0.0);
|
| let result = zeta_operator_product(s, 0.01, &h);
|
|
|
| for val in &result.data {
|
| assert!(val.norm().is_finite());
|
| }
|
| }
|
|
|
| #[test]
|
| fn test_spectral_invariant_positive() {
|
| let h = Matrix::identity(2);
|
| let delta = spectral_invariant_delta(&h, 0.01);
|
| assert!(delta > 0.0);
|
| assert!(delta.is_finite());
|
| }
|
|
|
| #[test]
|
| fn test_pade13_identity() {
|
| let h = Matrix::zeros(2, 2);
|
| let result = pade13_exp(&h, 1.0);
|
|
|
| assert!((result.get(0, 0) - Complex::new(1.0, 0.0)).norm() < 1e-10);
|
| assert!((result.get(1, 1) - Complex::new(1.0, 0.0)).norm() < 1e-10);
|
| assert!((result.get(0, 1)).norm() < 1e-10);
|
| }
|
|
|
| #[test]
|
| fn test_prime_encoded_state_depth_zero() {
|
| let h = Matrix::identity(2);
|
| let state = prime_encoded_state(&h, 0);
|
|
|
| assert_eq!(state.rows, 2);
|
| assert!((state.get(0, 0) - Complex::new(1.0, 0.0)).norm() < 1e-10);
|
| }
|
|
|
| #[test]
|
| fn test_prime_encoded_state_depth_one() {
|
| let h = Matrix::identity(2);
|
| let state = prime_encoded_state(&h, 1);
|
|
|
| for val in &state.data {
|
| assert!(val.norm().is_finite());
|
| }
|
| }
|
|
|
| #[test]
|
| fn test_mmp_multiplicity_identity() {
|
| let h = Matrix::identity(2);
|
| let mult = mmp_multiplicity(&h);
|
| assert!(mult >= 1.0);
|
| assert!(mult.is_finite());
|
| }
|
|
|
| #[test]
|
| fn test_mmp_bound_decreases() {
|
|
|
| let b2 = mmp_bound(2);
|
| let b4 = mmp_bound(4);
|
| assert!(b4 < b2);
|
| }
|
|
|
| #[test]
|
| fn test_tensor_product_dimensions() {
|
| let a = Matrix::identity(2);
|
| let b = Matrix::identity(3);
|
| let c = tensor_product(&a, &b);
|
| assert_eq!(c.rows, 6);
|
| assert_eq!(c.cols, 6);
|
| }
|
|
|
| #[test]
|
| fn test_matrix_power_identity() {
|
| let m = Matrix::identity(3);
|
| let p = matrix_power(&m, 5);
|
|
|
| assert!((p.get(0, 0) - Complex::new(1.0, 0.0)).norm() < 1e-10);
|
| assert!((p.get(1, 0)).norm() < 1e-10);
|
| }
|
|
|
| #[test]
|
| fn test_sndl_key_generation() {
|
| let h = Matrix::identity(2);
|
| let rho = Matrix::identity(2);
|
| let pulse = vec![0.5, 0.3, 0.8, 0.1];
|
| let key = sndl_resistant_key(&h, &pulse, &rho, 1);
|
|
|
| assert_eq!(key.len(), 32);
|
| assert!(key.iter().any(|&b| b != 0));
|
| }
|
|
|
| #[test]
|
| fn test_sndl_key_deterministic() {
|
| let h = Matrix::identity(2);
|
| let rho = Matrix::identity(2);
|
| let pulse = vec![0.5, 0.3, 0.8, 0.1];
|
| let key1 = sndl_resistant_key(&h, &pulse, &rho, 1);
|
| let key2 = sndl_resistant_key(&h, &pulse, &rho, 1);
|
|
|
| assert_eq!(key1, key2);
|
| }
|
|
|
| #[test]
|
| fn test_sndl_key_different_inputs() {
|
| let h = Matrix::identity(2);
|
| let rho = Matrix::identity(2);
|
| let pulse1 = vec![0.5, 0.3, 0.8, 0.1];
|
| let pulse2 = vec![0.9, 0.1, 0.2, 0.7];
|
| let key1 = sndl_resistant_key(&h, &pulse1, &rho, 1);
|
| let key2 = sndl_resistant_key(&h, &pulse2, &rho, 1);
|
|
|
| assert_ne!(key1, key2);
|
| }
|
|
|
| #[test]
|
| fn test_sndl_freshness_hash() {
|
| let rho = Matrix::identity(2);
|
| let hash = key_freshness_hash(&rho);
|
| assert_eq!(hash.len(), 32);
|
| assert!(hash.iter().any(|&b| b != 0));
|
| }
|
|
|
| #[test]
|
| fn test_sndl_key_rotation() {
|
| let key = [42u8; 32];
|
| let rotated = sndl_key_rotate(&key, 3);
|
|
|
| assert_ne!(key, rotated);
|
|
|
| assert_eq!(rotated.len(), 32);
|
| }
|
|
|
| #[test]
|
| fn test_sndl_rotation_depth_matters() {
|
| let key = [42u8; 32];
|
| let r1 = sndl_key_rotate(&key, 1);
|
| let r2 = sndl_key_rotate(&key, 5);
|
|
|
| assert_ne!(r1, r2);
|
| }
|
|
|
| #[test]
|
| fn test_blake3_kdf_deterministic() {
|
| let result1 = blake3_kdf(b"salt", b"input");
|
| let result2 = blake3_kdf(b"salt", b"input");
|
| assert_eq!(result1, result2);
|
| }
|
|
|
| #[test]
|
| fn test_blake3_kdf_different_salts() {
|
| let r1 = blake3_kdf(b"salt1", b"input");
|
| let r2 = blake3_kdf(b"salt2", b"input");
|
| assert_ne!(r1, r2);
|
| }
|
| }
|
|
|