use rand_core::Rng;
pub type Result<T> = core::result::Result<T, HqcError>;
#[derive(Debug, Clone, PartialEq)]
#[allow(dead_code)]
pub enum HqcError {
InvalidBufferSize,
OperationFailed,
}
impl core::fmt::Display for HqcError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::InvalidBufferSize => write!(f, "Invalid buffer size"),
Self::OperationFailed => write!(f, "Operation failed"),
}
}
}
pub mod polynomial {
use super::{
Result,
Rng,
};
pub fn polynomial_multiply(result: &mut [u8], a: &[u8], b: &[u8]) -> Result<()> {
if result.is_empty() || a.is_empty() || b.is_empty() {
return Ok(());
}
result.fill(0);
for (i, &a_coeff) in a.iter().enumerate() {
for (j, &b_coeff) in b.iter().enumerate() {
let result_idx = i + j;
if result_idx < result.len() {
let product = a_coeff & b_coeff;
result[result_idx] ^= product;
}
}
}
Ok(())
}
pub fn polynomial_random_weight<R: Rng + ?Sized>(
result: &mut [u8],
weight: usize,
rng: &mut R,
) -> Result<()> {
const MAX_ATTEMPTS: usize = 1000;
if result.is_empty() {
return Ok(());
}
result.fill(0);
let total_bits = result.len() * 8;
let actual_weight = weight.min(total_bits);
if actual_weight == 0 {
return Ok(());
}
#[cfg(feature = "alloc")]
{
extern crate alloc;
use alloc::vec::Vec;
let mut positions = Vec::with_capacity(actual_weight);
let mut attempts = 0;
while positions.len() < actual_weight && attempts < MAX_ATTEMPTS {
let mut pos_bytes = [0u8; 4];
rng.fill_bytes(&mut pos_bytes);
let pos = u32::from_le_bytes(pos_bytes) as usize % total_bits;
if !positions.contains(&pos) {
positions.push(pos);
}
attempts += 1;
}
for &pos in &positions {
let byte_idx = pos / 8;
let bit_idx = pos % 8;
if byte_idx < result.len() {
result[byte_idx] |= 1 << bit_idx;
}
}
}
Ok(())
}
}
pub mod matrix {
use super::Result;
pub fn matrix_vector_multiply(
result: &mut [u8],
matrix: &[Vec<u8>],
vector: &[u8],
) -> Result<()> {
if result.is_empty() || matrix.is_empty() || vector.is_empty() {
return Ok(());
}
result.fill(0);
for (i, row) in matrix.iter().enumerate() {
if i >= result.len() {
break;
}
let mut sum = 0u8;
for (j, &row_val) in row.iter().enumerate() {
if j < vector.len() {
sum ^= row_val & vector[j];
}
}
result[i] = sum;
}
Ok(())
}
}
pub mod codec {
use super::Result;
pub fn tensor_code_encode(codeword: &mut [u8], message: &[u8]) -> Result<()> {
if codeword.is_empty() {
return Ok(());
}
codeword.fill(0);
let message_len = message.len().min(codeword.len());
codeword[..message_len].copy_from_slice(&message[..message_len]);
let mut pos = message_len;
while pos < codeword.len() {
let copy_len = (codeword.len() - pos).min(message_len);
if copy_len > 0 {
codeword[pos..pos + copy_len].copy_from_slice(&message[..copy_len]);
pos += copy_len;
} else {
break;
}
}
Ok(())
}
pub fn tensor_code_decode(message: &mut [u8], codeword: &[u8]) -> Result<()> {
if message.is_empty() || codeword.is_empty() {
return Ok(());
}
message.fill(0);
let message_len = message.len().min(codeword.len());
message[..message_len].copy_from_slice(&codeword[..message_len]);
Ok(())
}
}
pub mod keygen {
use super::{
Result,
Rng,
};
pub fn hqc_keygen<R: Rng + ?Sized>(
public_key: &mut [u8],
secret_key: &mut [u8],
rng: &mut R,
) -> Result<()> {
if public_key.is_empty() || secret_key.is_empty() {
return Ok(());
}
rng.fill_bytes(public_key);
rng.fill_bytes(secret_key);
if public_key.iter().all(|&x| x == 0) {
public_key[0] = 1;
}
if secret_key.iter().all(|&x| x == 0) {
secret_key[0] = 1;
}
Ok(())
}
}
pub mod encrypt {
use super::{
Result,
Rng,
};
pub fn hqc_encrypt<R: Rng + ?Sized>(
ciphertext: &mut [u8],
message: &[u8],
public_key: &[u8],
rng: &mut R,
) -> Result<()> {
if ciphertext.is_empty() {
return Ok(());
}
rng.fill_bytes(ciphertext);
for (i, &msg_byte) in message.iter().enumerate() {
if i < ciphertext.len() {
ciphertext[i] ^= msg_byte;
}
}
for (i, &pk_byte) in public_key.iter().enumerate() {
if i < ciphertext.len() {
ciphertext[i] ^= pk_byte;
}
}
Ok(())
}
}
pub mod decrypt {
use super::Result;
pub fn hqc_decrypt(message: &mut [u8], ciphertext: &[u8], secret_key: &[u8]) -> Result<()> {
if message.is_empty() || ciphertext.is_empty() || secret_key.is_empty() {
return Ok(());
}
message.fill(0);
for (i, &ct_byte) in ciphertext.iter().enumerate() {
if i < message.len() {
message[i] = ct_byte;
}
}
for (i, &sk_byte) in secret_key.iter().enumerate() {
if i < message.len() {
message[i] ^= sk_byte;
}
}
Ok(())
}
}
pub use codec::{
tensor_code_decode,
tensor_code_encode,
};
pub use decrypt::hqc_decrypt;
pub use encrypt::hqc_encrypt;
pub use keygen::hqc_keygen;
pub use matrix::matrix_vector_multiply;
pub use polynomial::{
polynomial_multiply,
polynomial_random_weight,
};