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 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
use crate::{Point3D, RotationAxis};
use rand::{RngCore, SeedableRng};
use rand_xoshiro::Xoshiro256PlusPlus;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
/// A hasher that uses a 3D point and a rotation axis to encrypt and decrypt data.
///
/// The `Spha256` struct provides methods for encrypting and decrypting data based on a deterministic algorithm. It utilizes a pseudo-random number generator (PRNG) seeded with a hash of its parameters.
///
/// # Examples
///
/// ```
/// use spatial_hasher::{Point3D, RotationAxis, Spha256};
/// let point = Point3D { x: 1.0, y: 2.0, z: 3.0 };
/// let axis = RotationAxis { x: 0.0, y: 1.0, z: 0.0 };
/// let hasher = Spha256::new(point, axis, 10, 0.1);
/// ```
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Spha256 {
/// The starting point in 3D space.
point: Point3D,
/// The rotation axis used in the hashing process.
rotation_axis: RotationAxis,
/// The number of iterations for the transformation.
iterations: u32,
/// The strength of the transformation.
strength: f64,
}
impl Spha256 {
/// Creates a new `Spha256` instance with the specified parameters.
///
/// # Arguments
///
/// * `point` - A `Point3D` specifying the starting point in 3D space.
/// * `rotation_axis` - A `RotationAxis` specifying the axis of rotation.
/// * `iterations` - The number of iterations to perform in the hashing process.
/// * `strength` - A floating-point value representing the strength of the transformation.
///
/// # Returns
///
/// A new `Spha256` instance configured with the provided parameters.
///
/// # Examples
///
/// ```
/// use spatial_hasher::{Point3D, RotationAxis, Spha256};
/// let point = Point3D { x: 1.0, y: 2.0, z: 3.0 };
/// let axis = RotationAxis { x: 0.0, y: 1.0, z: 0.0 };
/// let hasher = Spha256::new(point, axis, 10, 0.1);
/// ```
pub fn new(
point: Point3D,
rotation_axis: RotationAxis,
iterations: u32,
strength: f64,
) -> Self {
Spha256 {
point,
rotation_axis,
iterations,
strength,
}
}
/// Generates a 256-bit seed for the pseudo-random number generator (PRNG) by hashing the hasher's parameters.
///
/// This function uses the SHA-256 hash function to create a seed based on the bit representations of the `point`, `rotation_axis`, `iterations`, and `strength` fields. The seed is used to initialize the PRNG in the [`encrypt`](#method.encrypt) and [`decrypt`](#method.decrypt) methods.
///
/// # Returns
///
/// A 32-byte array representing the seed for the PRNG.
fn generate_seed(&self) -> [u8; 32] {
let mut hasher = Sha256::new();
// Hash the bit representations of the floating-point numbers
hasher.update(self.point.x.to_bits().to_ne_bytes());
hasher.update(self.point.y.to_bits().to_ne_bytes());
hasher.update(self.point.z.to_bits().to_ne_bytes());
hasher.update(self.rotation_axis.x.to_bits().to_ne_bytes());
hasher.update(self.rotation_axis.y.to_bits().to_ne_bytes());
hasher.update(self.rotation_axis.z.to_bits().to_ne_bytes());
hasher.update(self.iterations.to_ne_bytes());
hasher.update(self.strength.to_bits().to_ne_bytes());
let result = hasher.finalize();
let mut seed = [0u8; 32];
seed.copy_from_slice(&result[..32]);
seed
}
/// Encrypts the provided data using the hasher's parameters.
///
/// This method encrypts the input data by XORing each byte with a byte generated from a pseudo-random number generator (PRNG). The PRNG is seeded using the [`generate_seed`](#method.generate_seed) method, ensuring that the encryption is deterministic based on the hasher's parameters.
///
/// # Arguments
///
/// * `data` - A slice of bytes representing the data to encrypt.
///
/// # Returns
///
/// A `Vec<u8>` containing the encrypted data.
///
/// # Examples
///
/// ```
/// use spatial_hasher::{Spha256, Point3D, RotationAxis};
/// let point = Point3D { x: 1.0, y: 2.0, z: 3.0 };
/// let axis = RotationAxis { x: 0.0, y: 1.0, z: 0.0 };
/// let hasher = Spha256::new(point, axis, 10, 0.1);
///
/// let data = b"Secret Message";
/// let encrypted = hasher.encrypt(data);
/// ```
pub fn encrypt(&self, data: &[u8]) -> Vec<u8> {
let seed = self.generate_seed();
let mut rng = Xoshiro256PlusPlus::from_seed(seed);
data.iter()
.map(|&byte| {
let rand_byte = rng.next_u32() as u8;
byte ^ rand_byte
})
.collect()
}
/// Decrypts the provided data using the hasher's parameters.
///
/// The decryption process is identical to the encryption process since the XOR operation is symmetric. The method uses the same PRNG sequence as in [`encrypt`](#method.encrypt), ensuring that the original data is recovered when the same parameters are used.
///
/// # Arguments
///
/// * `encrypted` - A slice of bytes representing the encrypted data.
///
/// # Returns
///
/// A `Vec<u8>` containing the decrypted data.
///
/// # Examples
///
/// ```
/// use spatial_hasher::{Spha256, Point3D, RotationAxis};
/// let point = Point3D { x: 1.0, y: 2.0, z: 3.0 };
/// let axis = RotationAxis { x: 0.0, y: 1.0, z: 0.0 };
/// let hasher = Spha256::new(point, axis, 10, 0.1);
///
/// let encrypted = hasher.encrypt(b"Secret Message");
/// let decrypted = hasher.decrypt(&encrypted);
/// assert_eq!(decrypted, b"Secret Message");
/// ```
pub fn decrypt(&self, encrypted: &[u8]) -> Vec<u8> {
// Decryption is the same as encryption in XOR cipher
self.encrypt(encrypted)
}
}