Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "horizon"
version = "0.9.4"
version = "0.9.5"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand All @@ -13,13 +13,13 @@ subtle = "2.6.1"
secrecy = { version = "0.8.0", features = ["alloc"] }
once_cell = "*"
hkdf = "0.13.0-rc.0"
hmac = "0.13.0-rc.0"
sha2 = "0.11.0-rc.0"
zeroize = "1.8.1"
dashmap = { version = "*", features = ["rayon", "inline"] }
ring = "0.17.14"
blake3 = "1.8.2"
rand = "0.8.5"
poly1305 = "0.8.0"

[profile.release]
lto = "fat"
Expand Down
22 changes: 11 additions & 11 deletions src/cipher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -643,20 +643,20 @@ pub fn encrypt3_final(
println!(" Bit shift: {:?}", start_shift.elapsed());
}

let hmac_key = derive_hmac_key_final(key1, key2, &run_salt);
let poly_key = derive_poly1305_key_final(key1, key2, &run_salt);

let mut header = Vec::with_capacity(SALT_LEN + 2);
header.extend_from_slice(&run_salt);
header.push(VERSION);
header.push(ALG_ID);

let hmac_tag = compute_hmac(&hmac_key, &header, &body);
let poly_tag = compute_poly1305(&poly_key, &header, &body);

let total_len = header.len() + body.len() + hmac_tag.len();
let total_len = header.len() + body.len() + poly_tag.len();
let mut output = Vec::with_capacity(total_len);
output.extend_from_slice(&header);
output.extend_from_slice(&body);
output.extend_from_slice(&hmac_tag);
output.extend_from_slice(&poly_tag);

Ok(output)
}
Expand All @@ -668,7 +668,7 @@ pub fn decrypt3_final(
key2: &Secret<Vec<u8>>,
round_keys: &[Vec<u8>],
) -> Result<Vec<u8>, Box<dyn Error>> {
if encrypted_data.len() < SALT_LEN + 2 + 32 {
if encrypted_data.len() < SALT_LEN + 2 + 16 {
return Err("Data too short".into());
}

Expand All @@ -682,14 +682,14 @@ pub fn decrypt3_final(
return Err("Invalid version or algorithm ID".into());
}

let split_point = encrypted_data.len() - 32;
let split_point = encrypted_data.len() - 16;
let header = &encrypted_data[0..SALT_LEN + 2];
let body = &encrypted_data[SALT_LEN + 2..split_point];
let hmac_tag = &encrypted_data[split_point..];
let poly_tag = encrypted_data[split_point..].try_into().map_err(|_| "Invalid Poly1305 tag length")?;

let hmac_key = derive_hmac_key_final(key1, key2, &run_salt);
if !verify_hmac(&hmac_key, header, body, hmac_tag) {
return Err("HMAC verification failed".into());
let poly_key = derive_poly1305_key_final(key1, key2, &run_salt);
if !verify_poly1305(&poly_key, header, body, &poly_tag) {
return Err("Poly1305 verification failed".into());
}

let mut plaintext = body.to_vec();
Expand Down Expand Up @@ -718,4 +718,4 @@ pub fn decrypt3_final(
}

Ok(plaintext)
}
}
103 changes: 34 additions & 69 deletions src/crypto.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
use crate::types::*;
use argon2::{Argon2, Params};

Check failure on line 2 in src/crypto.rs

View workflow job for this annotation

GitHub Actions / Clippy

unused imports: `Argon2` and `Params`

Check warning on line 2 in src/crypto.rs

View workflow job for this annotation

GitHub Actions / Test

unused imports: `Argon2` and `Params`

Check warning on line 2 in src/crypto.rs

View workflow job for this annotation

GitHub Actions / Test

unused imports: `Argon2` and `Params`

Check failure on line 2 in src/crypto.rs

View workflow job for this annotation

GitHub Actions / Clippy

unused imports: `Argon2` and `Params`
use blake3::Hasher;
use hkdf::Hkdf;
use hmac::{Hmac, KeyInit, Mac};
use poly1305::{Key, UniversalKey};

Check failure on line 5 in src/crypto.rs

View workflow job for this annotation

GitHub Actions / Clippy

unused import: `Key`

Check failure on line 5 in src/crypto.rs

View workflow job for this annotation

GitHub Actions / Clippy

unresolved import `poly1305::UniversalKey`

Check warning on line 5 in src/crypto.rs

View workflow job for this annotation

GitHub Actions / Test

unused import: `Key`

Check failure on line 5 in src/crypto.rs

View workflow job for this annotation

GitHub Actions / Test

unresolved import `poly1305::UniversalKey`

Check warning on line 5 in src/crypto.rs

View workflow job for this annotation

GitHub Actions / Test

unused import: `Key`

Check failure on line 5 in src/crypto.rs

View workflow job for this annotation

GitHub Actions / Test

unresolved import `poly1305::UniversalKey`

Check failure on line 5 in src/crypto.rs

View workflow job for this annotation

GitHub Actions / Clippy

unused import: `Key`

Check failure on line 5 in src/crypto.rs

View workflow job for this annotation

GitHub Actions / Clippy

unresolved import `poly1305::UniversalKey`
use ring::rand::{SecureRandom, SystemRandom};
use secrecy::{ExposeSecret, Secret};
use sha2::Sha256;
use subtle::ConstantTimeEq;
use zeroize::Zeroize;

type HmacSha256 = Hmac<Sha256>;

fn gf256_mul(mut a: u8, mut b: u8) -> u8 {
let mut res = 0u8;
while b != 0 {
Expand Down Expand Up @@ -319,122 +316,90 @@
(xor_key, rot_key)
}

/// Derives the final HMAC key for message authentication
/// Derives the final Poly1305 key for message authentication
///
/// Creates a 32-byte HMAC key from the provided secret keys and salt,
/// Creates a 32-byte Poly1305 key from the provided secret keys and salt,
/// used for authenticating the encrypted data and preventing tampering.
///
/// # Arguments
///
/// * `key1` - First secret key for HMAC key derivation
/// * `key2` - Second secret key for HMAC key derivation
/// * `key1` - First secret key for Poly1305 key derivation
/// * `key2` - Second secret key for Poly1305 key derivation
/// * `salt` - Salt value for key derivation
///
/// # Returns
///
/// A 32-byte HMAC key for message authentication
/// A 32-byte Poly1305 key for message authentication
///
/// # Security
///
/// Intermediate key material is securely zeroed after use.
pub fn derive_hmac_key_final(key1: &Secret<Vec<u8>>, key2: &Secret<Vec<u8>>, salt: &[u8]) -> Vec<u8> {
pub fn derive_poly1305_key_final(key1: &Secret<Vec<u8>>, key2: &Secret<Vec<u8>>, salt: &[u8]) -> [u8; 32] {
let k1 = key1.expose_secret();
let k2 = key2.expose_secret();
let mut ikm = Vec::with_capacity(k1.len() + k2.len());
ikm.extend_from_slice(k1);
ikm.extend_from_slice(k2);
let hk = Hkdf::<Sha256>::new(Some(salt), &ikm);
let mut hmac_key = vec![0u8; 32];
hk.expand(b"hmac_final_v1", &mut hmac_key)
.expect("hkdf hmac final");
let mut poly_key = [0u8; 32];
hk.expand(b"poly1305_final_v1", &mut poly_key)
.expect("hkdf poly1305 final");
ikm.zeroize();
hmac_key
}

/// Generates a strong cryptographic key using Argon2 password hashing
///
/// Derives a high-entropy secret key from seed material using Argon2id,
/// a memory-hard password hashing function resistant to brute-force attacks.
/// The result is further processed with HKDF for key expansion.
///
/// # Arguments
///
/// * `seed` - Source entropy for key generation
/// * `salt` - Salt value to prevent rainbow table attacks
///
/// # Returns
///
/// A Secret-wrapped vector containing KEY_LENGTH bytes of key material
///
/// # Security
///
/// Uses Argon2id with secure parameters (8192 KB memory, 2 iterations, 4 parallelism).
/// Intermediate outputs are securely cleared from memory.
pub fn gene3_with_salt(seed: &[u8], salt: &[u8]) -> Secret<Vec<u8>> {
let params = Params::new(8192, 2, 4, None).expect("params");
let argon2 = Argon2::new(argon2::Algorithm::Argon2id, argon2::Version::V0x13, params);
let mut out = vec![0u8; 64];
argon2
.hash_password_into(seed, salt, &mut out)
.expect("argon2");
let hk = Hkdf::<Sha256>::new(Some(salt), &out);
let mut okm = vec![0u8; KEY_LENGTH];
hk.expand(b"key_expand_v1", &mut okm).expect("hkdf expand");
out.zeroize();
Secret::new(okm)
poly_key
}

/// Computes HMAC-SHA256 authentication tag for message integrity
/// Computes Poly1305 authentication tag for message integrity
///
/// Generates a cryptographic message authentication code over the provided
/// header and ciphertext data using HMAC with SHA-256.
/// header and ciphertext data using Poly1305.
///
/// # Arguments
///
/// * `hmac_key` - Secret key for HMAC computation
/// * `poly_key` - Secret key for Poly1305 computation
/// * `header` - Header data to authenticate
/// * `ciphertext` - Encrypted data to authenticate
///
/// # Returns
///
/// HMAC tag as a vector of bytes
/// Poly1305 tag as a 16-byte array
///
/// # Panics
///
/// Panics if the HMAC key length is invalid
pub fn compute_hmac(hmac_key: &[u8], header: &[u8], ciphertext: &[u8]) -> Vec<u8> {
let mut mac = HmacSha256::new_from_slice(hmac_key).expect("hmac key");
mac.update(header);
mac.update(ciphertext);
mac.finalize().into_bytes().to_vec()
/// Panics if the Poly1305 key length is invalid
pub fn compute_poly1305(poly_key: &[u8; 32], header: &[u8], ciphertext: &[u8]) -> [u8; 16] {
let key = UniversalKey::from_slice(poly_key);
let mut poly = poly1305::Poly1305::new(key);

Check failure on line 371 in src/crypto.rs

View workflow job for this annotation

GitHub Actions / Clippy

no function or associated item named `new` found for struct `poly1305::Poly1305` in the current scope

Check failure on line 371 in src/crypto.rs

View workflow job for this annotation

GitHub Actions / Test

no function or associated item named `new` found for struct `Poly1305` in the current scope

Check failure on line 371 in src/crypto.rs

View workflow job for this annotation

GitHub Actions / Test

no function or associated item named `new` found for struct `Poly1305` in the current scope

Check failure on line 371 in src/crypto.rs

View workflow job for this annotation

GitHub Actions / Clippy

no function or associated item named `new` found for struct `poly1305::Poly1305` in the current scope
poly.update(header);
poly.update(ciphertext);
poly.finalize().into_bytes()
}

/// Verifies HMAC-SHA256 authentication tag in constant time
/// Verifies Poly1305 authentication tag in constant time
///
/// Validates the integrity and authenticity of data by recomputing the HMAC
/// Validates the integrity and authenticity of data by recomputing the Poly1305
/// and comparing it against the provided tag using constant-time comparison
/// to prevent timing attacks.
///
/// # Arguments
///
/// * `hmac_key` - Secret key for HMAC verification
/// * `poly_key` - Secret key for Poly1305 verification
/// * `header` - Header data to verify
/// * `ciphertext` - Encrypted data to verify
/// * `tag` - Expected HMAC tag to validate against
/// * `tag` - Expected Poly1305 tag to validate against
///
/// # Returns
///
/// `true` if the HMAC is valid, `false` otherwise
/// `true` if the Poly1305 is valid, `false` otherwise
///
/// # Security
///
/// Uses constant-time comparison to prevent timing side-channel attacks.
pub fn verify_hmac(hmac_key: &[u8], header: &[u8], ciphertext: &[u8], tag: &[u8]) -> bool {
let mut mac = HmacSha256::new_from_slice(hmac_key).expect("hmac key");
mac.update(header);
mac.update(ciphertext);
let computed_tag = mac.finalize().into_bytes();
computed_tag.ct_eq(tag).into()
pub fn verify_poly1305(poly_key: &[u8; 32], header: &[u8], ciphertext: &[u8], tag: &[u8; 16]) -> bool {
let key = UniversalKey::from_slice(poly_key);
let mut poly = poly1305::Poly1305::new(key);

Check failure on line 399 in src/crypto.rs

View workflow job for this annotation

GitHub Actions / Clippy

no function or associated item named `new` found for struct `poly1305::Poly1305` in the current scope

Check failure on line 399 in src/crypto.rs

View workflow job for this annotation

GitHub Actions / Test

no function or associated item named `new` found for struct `Poly1305` in the current scope

Check failure on line 399 in src/crypto.rs

View workflow job for this annotation

GitHub Actions / Test

no function or associated item named `new` found for struct `Poly1305` in the current scope

Check failure on line 399 in src/crypto.rs

View workflow job for this annotation

GitHub Actions / Clippy

no function or associated item named `new` found for struct `poly1305::Poly1305` in the current scope
poly.update(header);
poly.update(ciphertext);
poly.verify(tag).is_ok()
}

/// Builds a character substitution table for encryption operations
Expand Down Expand Up @@ -487,4 +452,4 @@
/// Uses non-linear polynomial transformations and key-dependent mixing for cryptographic strength.
pub fn perm256_from_key(key: &[u8; 2048]) -> [u8; 256] {
generate_custom_sbox(key)
}
}
Loading