use std::collections::{BTreeMap, HashMap, HashSet};
use std::fmt;
use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering};
use crate::clang::*;
use crate::x86::*;
use crate::x86::x86_instr_info::*;
pub const X86_AES_BLOCK_SIZE: usize = 16;
pub const X86_AES128_KEY_SIZE: usize = 16;
pub const X86_AES192_KEY_SIZE: usize = 24;
pub const X86_AES256_KEY_SIZE: usize = 32;
pub const X86_AES128_ROUNDS: usize = 10;
pub const X86_AES192_ROUNDS: usize = 12;
pub const X86_AES256_ROUNDS: usize = 14;
pub const X86_AES128_EXPANDED_KEY_SIZE: usize = 176;
pub const X86_AES192_EXPANDED_KEY_SIZE: usize = 208;
pub const X86_AES256_EXPANDED_KEY_SIZE: usize = 240;
pub const X86_SHA1_BLOCK_SIZE: usize = 64;
pub const X86_SHA1_DIGEST_SIZE: usize = 20;
pub const X86_SHA256_BLOCK_SIZE: usize = 64;
pub const X86_SHA256_DIGEST_SIZE: usize = 32;
pub const X86_SHA512_BLOCK_SIZE: usize = 128;
pub const X86_SHA512_DIGEST_SIZE: usize = 64;
pub const X86_GCM_BLOCK_SIZE: usize = 16;
pub const X86_GCM_H_SIZE: usize = 16;
pub const X86_GCM_MAX_TAG_SIZE: usize = 16;
pub const X86_GCM_MIN_IV_SIZE: usize = 1;
pub const X86_GCM_RECOMMENDED_IV_SIZE: usize = 12;
pub const X86_CCM_MIN_NONCE_SIZE: usize = 7;
pub const X86_CCM_MAX_NONCE_SIZE: usize = 13;
pub const X86_CCM_MAX_TAG_SIZE: usize = 16;
pub const X86_XTS_BLOCK_SIZE: usize = 16;
pub const X86_GHASH_REDUCTION_POLY: u64 = 0xC200000000000000;
pub const X86_GHASH_REVERSE_POLY: u64 = 0xE100000000000000;
pub const X86_CHACHA20_BLOCK_SIZE: usize = 64;
pub const X86_CHACHA20_KEY_SIZE: usize = 32;
pub const X86_CHACHA20_NONCE_SIZE: usize = 12;
pub const X86_CHACHA20_INITIAL_COUNTER: u32 = 0;
pub const X86_HMAC_SHA256_BLOCK_SIZE: usize = 64;
pub const X86_HMAC_SHA256_OUTPUT_SIZE: usize = 32;
pub const X86_PBKDF2_MIN_ITERATIONS: u32 = 1000;
pub const X86_PBKDF2_RECOMMENDED_ITERATIONS: u32 = 600000;
pub const X86_PBKDF2_MIN_SALT_SIZE: usize = 16;
pub const X86_RDRAND_MAX_RETRIES: u32 = 10;
pub const X86_RDSEED_MAX_RETRIES: u32 = 100;
pub const X86_RDRAND_OUTPUT_SIZE: usize = 8;
pub const X86_RDSEED_OUTPUT_SIZE: usize = 8;
pub const X86_CTR_DRBG_SEED_LENGTH: usize = 48;
pub const X86_CTR_DRBG_RESEED_INTERVAL: u64 = 1 << 48;
pub const X86_CTR_DRBG_MAX_REQUEST_SIZE: usize = 1 << 16;
pub const X86_SM3_BLOCK_SIZE: usize = 64;
pub const X86_SM3_DIGEST_SIZE: usize = 32;
pub const X86_SM4_BLOCK_SIZE: usize = 16;
pub const X86_SM4_KEY_SIZE: usize = 16;
pub const X86_SM4_ROUNDS: usize = 32;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86CryptoFeature {
AesNi,
Pclmul,
ShaNi,
RdRand,
RdSeed,
Vaes,
Vpclmulqdq,
Gfni,
Avx,
Avx2,
Avx512f,
Avx512vl,
Avx512bw,
Sha512,
Sm3,
Sm4,
Sse2,
Sse41,
Ssse3,
}
impl X86CryptoFeature {
pub fn cpuid_info(&self) -> (u32, u32, u8) {
match self {
X86CryptoFeature::AesNi => (0x0000_0001, 0, 25),
X86CryptoFeature::Pclmul => (0x0000_0001, 0, 1),
X86CryptoFeature::ShaNi => (0x0000_0007, 0, 29),
X86CryptoFeature::RdRand => (0x0000_0001, 0, 30),
X86CryptoFeature::RdSeed => (0x0000_0007, 0, 18),
X86CryptoFeature::Vaes => (0x0000_0007, 0, 9),
X86CryptoFeature::Vpclmulqdq => (0x0000_0007, 0, 10),
X86CryptoFeature::Gfni => (0x0000_0007, 0, 8),
X86CryptoFeature::Avx => (0x0000_0001, 0, 28),
X86CryptoFeature::Avx2 => (0x0000_0007, 0, 5),
X86CryptoFeature::Avx512f => (0x0000_0007, 0, 16),
X86CryptoFeature::Avx512vl => (0x0000_0007, 0, 31),
X86CryptoFeature::Avx512bw => (0x0000_0007, 0, 30),
X86CryptoFeature::Sha512 => (0x0000_0007, 0, 0), X86CryptoFeature::Sm3 => (0x0000_0007, 0, 0), X86CryptoFeature::Sm4 => (0x0000_0007, 0, 0), X86CryptoFeature::Sse2 => (0x0000_0001, 0, 26),
X86CryptoFeature::Sse41 => (0x0000_0001, 0, 19),
X86CryptoFeature::Ssse3 => (0x0000_0001, 0, 9),
}
}
pub fn compiler_flag(&self) -> &'static str {
match self {
X86CryptoFeature::AesNi => "-maes",
X86CryptoFeature::Pclmul => "-mpclmul",
X86CryptoFeature::ShaNi => "-msha",
X86CryptoFeature::RdRand => "-mrdrnd",
X86CryptoFeature::RdSeed => "-mrdseed",
X86CryptoFeature::Vaes => "-mvaes",
X86CryptoFeature::Vpclmulqdq => "-mvpclmulqdq",
X86CryptoFeature::Gfni => "-mgfni",
X86CryptoFeature::Avx => "-mavx",
X86CryptoFeature::Avx2 => "-mavx2",
X86CryptoFeature::Avx512f => "-mavx512f",
X86CryptoFeature::Avx512vl => "-mavx512vl",
X86CryptoFeature::Avx512bw => "-mavx512bw",
X86CryptoFeature::Sha512 => "-msha512",
X86CryptoFeature::Sm3 => "-msm3",
X86CryptoFeature::Sm4 => "-msm4",
X86CryptoFeature::Sse2 => "-msse2",
X86CryptoFeature::Sse41 => "-msse4.1",
X86CryptoFeature::Ssse3 => "-mssse3",
}
}
pub fn target_attribute(&self) -> &'static str {
match self {
X86CryptoFeature::AesNi => "aes",
X86CryptoFeature::Pclmul => "pclmul",
X86CryptoFeature::ShaNi => "sha",
X86CryptoFeature::RdRand => "rdrnd",
X86CryptoFeature::RdSeed => "rdseed",
X86CryptoFeature::Vaes => "vaes",
X86CryptoFeature::Vpclmulqdq => "vpclmulqdq",
X86CryptoFeature::Gfni => "gfni",
X86CryptoFeature::Avx => "avx",
X86CryptoFeature::Avx2 => "avx2",
X86CryptoFeature::Avx512f => "avx512f",
X86CryptoFeature::Avx512vl => "avx512vl",
X86CryptoFeature::Avx512bw => "avx512bw",
X86CryptoFeature::Sha512 => "sha512",
X86CryptoFeature::Sm3 => "sm3",
X86CryptoFeature::Sm4 => "sm4",
X86CryptoFeature::Sse2 => "sse2",
X86CryptoFeature::Sse41 => "sse4.1",
X86CryptoFeature::Ssse3 => "ssse3",
}
}
pub fn is_compatible_with(&self, other: &X86CryptoFeature) -> bool {
use X86CryptoFeature::*;
match (self, other) {
(Vaes, AesNi) | (AesNi, Vaes) => true,
(Vpclmulqdq, Pclmul) | (Pclmul, Vpclmulqdq) => true,
(Avx512f, Avx2) | (Avx2, Avx512f) => true,
(Avx512f, Avx) | (Avx, Avx512f) => true,
(Avx2, Avx) | (Avx, Avx2) => true,
_ if self == other => true,
_ => true,
}
}
}
impl fmt::Display for X86CryptoFeature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match self {
X86CryptoFeature::AesNi => "AES-NI",
X86CryptoFeature::Pclmul => "PCLMULQDQ",
X86CryptoFeature::ShaNi => "SHA-NI",
X86CryptoFeature::RdRand => "RDRAND",
X86CryptoFeature::RdSeed => "RDSEED",
X86CryptoFeature::Vaes => "VAES",
X86CryptoFeature::Vpclmulqdq => "VPCLMULQDQ",
X86CryptoFeature::Gfni => "GFNI",
X86CryptoFeature::Avx => "AVX",
X86CryptoFeature::Avx2 => "AVX2",
X86CryptoFeature::Avx512f => "AVX-512F",
X86CryptoFeature::Avx512vl => "AVX-512VL",
X86CryptoFeature::Avx512bw => "AVX-512BW",
X86CryptoFeature::Sha512 => "SHA512",
X86CryptoFeature::Sm3 => "SM3",
X86CryptoFeature::Sm4 => "SM4",
X86CryptoFeature::Sse2 => "SSE2",
X86CryptoFeature::Sse41 => "SSE4.1",
X86CryptoFeature::Ssse3 => "SSSE3",
};
write!(f, "{}", name)
}
}
#[derive(Debug, Clone, Default)]
pub struct X86CryptoFeatureSet {
pub features: HashSet<X86CryptoFeature>,
pub vendor: X86CpuVendor,
pub cpu_family_model: (u32, u32),
}
impl X86CryptoFeatureSet {
pub fn all_features() -> Self {
let mut features = HashSet::new();
features.insert(X86CryptoFeature::Sse2);
features.insert(X86CryptoFeature::Ssse3);
features.insert(X86CryptoFeature::Sse41);
features.insert(X86CryptoFeature::AesNi);
features.insert(X86CryptoFeature::Pclmul);
features.insert(X86CryptoFeature::ShaNi);
features.insert(X86CryptoFeature::RdRand);
features.insert(X86CryptoFeature::RdSeed);
features.insert(X86CryptoFeature::Avx);
features.insert(X86CryptoFeature::Avx2);
features.insert(X86CryptoFeature::Avx512f);
features.insert(X86CryptoFeature::Avx512vl);
features.insert(X86CryptoFeature::Avx512bw);
features.insert(X86CryptoFeature::Vaes);
features.insert(X86CryptoFeature::Vpclmulqdq);
features.insert(X86CryptoFeature::Gfni);
Self {
features,
vendor: X86CpuVendor::Intel,
cpu_family_model: (6, 183),
}
}
pub fn baseline() -> Self {
let mut features = HashSet::new();
features.insert(X86CryptoFeature::Sse2);
Self {
features,
vendor: X86CpuVendor::Intel,
cpu_family_model: (6, 0),
}
}
pub fn has(&self, feature: X86CryptoFeature) -> bool {
self.features.contains(&feature)
}
pub fn has_all(&self, features: &[X86CryptoFeature]) -> bool {
features.iter().all(|f| self.features.contains(f))
}
pub fn compiler_flags(&self) -> Vec<String> {
self.features
.iter()
.map(|f| f.compiler_flag().to_string())
.collect()
}
pub fn target_attribute_string(&self) -> String {
let mut attrs: Vec<&str> = self
.features
.iter()
.map(|f| f.target_attribute())
.collect();
attrs.sort();
attrs.join(",")
}
pub fn march_flag(&self) -> String {
let arch = match (self.has(X86CryptoFeature::Avx512f), self.has(X86CryptoFeature::Avx2)) {
(true, _) => "skylake-avx512",
(_, true) => "haswell",
_ => "x86-64",
};
format!("-march={}", arch)
}
pub fn insert(&mut self, feature: X86CryptoFeature) {
self.features.insert(feature);
}
pub fn remove(&mut self, feature: &X86CryptoFeature) {
self.features.remove(feature);
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86CpuVendor {
Intel,
Amd,
Unknown,
}
impl Default for X86CpuVendor {
fn default() -> Self {
X86CpuVendor::Unknown
}
}
#[derive(Debug, Clone)]
pub struct X86Crypto {
pub features: X86CryptoFeatureSet,
pub intrinsics: X86CryptoIntrinsics,
pub algorithms: X86CryptoAlgorithms,
pub constant_time: X86ConstantTime,
pub random: X86RandomGeneration,
pub secure_compare: X86SecureComparison,
pub compilation: X86CryptoCompilation,
pub fips_mode: bool,
pub prefer_hardware: bool,
pub side_channel_resistance: u8,
}
impl X86Crypto {
pub fn new(features: X86CryptoFeatureSet) -> Self {
let intrinsics = X86CryptoIntrinsics::new(features.clone());
let algorithms = X86CryptoAlgorithms::new(features.clone());
let constant_time = X86ConstantTime::new();
let random = X86RandomGeneration::new(features.clone());
let secure_compare = X86SecureComparison::new();
let compilation = X86CryptoCompilation::new(features.clone());
Self {
features,
intrinsics,
algorithms,
constant_time,
random,
secure_compare,
compilation,
fips_mode: false,
prefer_hardware: true,
side_channel_resistance: 2,
}
}
pub fn with_all_features() -> Self {
Self::new(X86CryptoFeatureSet::all_features())
}
pub fn baseline() -> Self {
Self::new(X86CryptoFeatureSet::baseline())
}
pub fn enable_fips(&mut self) {
self.fips_mode = true;
self.compilation.fips_compliance = true;
}
pub fn has_aes_ni(&self) -> bool {
self.features.has(X86CryptoFeature::AesNi)
}
pub fn has_sha_ni(&self) -> bool {
self.features.has(X86CryptoFeature::ShaNi)
}
pub fn crypto_compiler_flags(&self) -> Vec<String> {
self.compilation.crypto_flags()
}
pub fn best_target_attribute(&self) -> String {
let mut attrs = Vec::new();
if self.features.has(X86CryptoFeature::Sse2) {
attrs.push("sse2");
}
if self.features.has(X86CryptoFeature::AesNi) {
attrs.push("aes");
}
if self.features.has(X86CryptoFeature::Pclmul) {
attrs.push("pclmul");
}
if self.features.has(X86CryptoFeature::ShaNi) {
attrs.push("sha");
}
if self.features.has(X86CryptoFeature::Avx512f) {
attrs.push("avx512f");
}
attrs.join(",")
}
pub fn generate_dispatch_table(&self) -> X86CryptoDispatchTable {
X86CryptoDispatchTable::new(&self.features)
}
}
impl Default for X86Crypto {
fn default() -> Self {
Self::with_all_features()
}
}
#[derive(Debug, Clone)]
pub struct X86CryptoDispatchTable {
pub aes_encrypt_impl: String,
pub aes_decrypt_impl: String,
pub aes_key_expand_impl: String,
pub ghash_impl: String,
pub sha256_impl: String,
pub sha512_impl: String,
pub crc32_impl: String,
pub implementations: Vec<String>,
}
impl X86CryptoDispatchTable {
pub fn new(features: &X86CryptoFeatureSet) -> Self {
let aes_encrypt_impl = if features.has(X86CryptoFeature::Vaes) {
"vaes_encrypt".to_string()
} else if features.has(X86CryptoFeature::AesNi) {
"aesni_encrypt".to_string()
} else {
"software_aes_encrypt".to_string()
};
let aes_decrypt_impl = if features.has(X86CryptoFeature::Vaes) {
"vaes_decrypt".to_string()
} else if features.has(X86CryptoFeature::AesNi) {
"aesni_decrypt".to_string()
} else {
"software_aes_decrypt".to_string()
};
let aes_key_expand_impl = if features.has(X86CryptoFeature::AesNi) {
"aesni_key_expand".to_string()
} else {
"software_key_expand".to_string()
};
let ghash_impl = if features.has(X86CryptoFeature::Vpclmulqdq) {
"vpclmul_ghash".to_string()
} else if features.has(X86CryptoFeature::Pclmul) {
"pclmul_ghash".to_string()
} else {
"software_ghash".to_string()
};
let sha256_impl = if features.has(X86CryptoFeature::ShaNi) {
"sha_ni_sha256".to_string()
} else {
"software_sha256".to_string()
};
let sha512_impl = if features.has(X86CryptoFeature::Sha512) {
"sha512_ni".to_string()
} else {
"software_sha512".to_string()
};
let crc32_impl = if features.has(X86CryptoFeature::Sse41) {
"sse42_crc32".to_string()
} else {
"software_crc32".to_string()
};
let implementations = vec![
aes_encrypt_impl.clone(),
aes_decrypt_impl.clone(),
ghash_impl.clone(),
sha256_impl.clone(),
sha512_impl.clone(),
crc32_impl.clone(),
];
Self {
aes_encrypt_impl,
aes_decrypt_impl,
aes_key_expand_impl,
ghash_impl,
sha256_impl,
sha512_impl,
crc32_impl,
implementations,
}
}
pub fn resolve(&self, operation: &str) -> Option<&str> {
match operation {
"aes_encrypt" => Some(&self.aes_encrypt_impl),
"aes_decrypt" => Some(&self.aes_decrypt_impl),
"aes_key_expand" => Some(&self.aes_key_expand_impl),
"ghash" => Some(&self.ghash_impl),
"sha256" => Some(&self.sha256_impl),
"sha512" => Some(&self.sha512_impl),
"crc32" => Some(&self.crc32_impl),
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub struct X86CryptoIntrinsics {
pub features: X86CryptoFeatureSet,
pub intrinsics: HashMap<String, X86CryptoIntrinsicInfo>,
pub categories: BTreeMap<X86CryptoIntrinsicCategory, Vec<String>>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum X86CryptoIntrinsicCategory {
AesNi,
ShaNi,
Clmul,
Random,
Vaes,
Vpclmul,
Sha512,
Sm3,
Sm4,
Gfni,
}
#[derive(Debug, Clone)]
pub struct X86CryptoIntrinsicInfo {
pub name: String,
pub mnemonic: String,
pub operand_count: usize,
pub operand_types: Vec<X86CryptoOperandType>,
pub return_type: X86CryptoOperandType,
pub required_feature: X86CryptoFeature,
pub category: X86CryptoIntrinsicCategory,
pub latency: u32,
pub throughput: f64,
pub has_side_effects: bool,
pub description: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86CryptoOperandType {
M128i,
M128,
M256i,
M512i,
U32,
U64,
U8,
Imm8,
Pu32,
Pu64,
Pu16,
}
impl fmt::Display for X86CryptoOperandType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
X86CryptoOperandType::M128i => write!(f, "__m128i"),
X86CryptoOperandType::M128 => write!(f, "__m128"),
X86CryptoOperandType::M256i => write!(f, "__m256i"),
X86CryptoOperandType::M512i => write!(f, "__m512i"),
X86CryptoOperandType::U32 => write!(f, "unsigned int"),
X86CryptoOperandType::U64 => write!(f, "unsigned long long"),
X86CryptoOperandType::U8 => write!(f, "unsigned char"),
X86CryptoOperandType::Imm8 => write!(f, "const int"),
X86CryptoOperandType::Pu32 => write!(f, "unsigned int*"),
X86CryptoOperandType::Pu64 => write!(f, "unsigned long long*"),
X86CryptoOperandType::Pu16 => write!(f, "unsigned short*"),
}
}
}
impl X86CryptoIntrinsics {
pub fn new(features: X86CryptoFeatureSet) -> Self {
let mut intrinsics = HashMap::new();
let mut categories: BTreeMap<X86CryptoIntrinsicCategory, Vec<String>> = BTreeMap::new();
Self::register_aesni_intrinsics(&mut intrinsics, &mut categories, &features);
Self::register_shani_intrinsics(&mut intrinsics, &mut categories, &features);
Self::register_clmul_intrinsics(&mut intrinsics, &mut categories, &features);
Self::register_random_intrinsics(&mut intrinsics, &mut categories, &features);
Self::register_vaes_intrinsics(&mut intrinsics, &mut categories, &features);
Self::register_vpclmul_intrinsics(&mut intrinsics, &mut categories, &features);
Self::register_sha512_intrinsics(&mut intrinsics, &mut categories, &features);
Self::register_sm3_intrinsics(&mut intrinsics, &mut categories, &features);
Self::register_sm4_intrinsics(&mut intrinsics, &mut categories, &features);
Self::register_gfni_intrinsics(&mut intrinsics, &mut categories, &features);
Self {
features,
intrinsics,
categories,
}
}
pub fn intrinsics_in_category(
&self,
category: X86CryptoIntrinsicCategory,
) -> Vec<&X86CryptoIntrinsicInfo> {
self.categories
.get(&category)
.map(|names| {
names
.iter()
.filter_map(|n| self.intrinsics.get(n))
.collect()
})
.unwrap_or_default()
}
pub fn get_intrinsic(&self, name: &str) -> Option<&X86CryptoIntrinsicInfo> {
self.intrinsics.get(name)
}
pub fn is_available(&self, name: &str) -> bool {
self.intrinsics
.get(name)
.map(|info| self.features.has(info.required_feature))
.unwrap_or(false)
}
pub fn intrinsic_count(&self) -> usize {
self.intrinsics.len()
}
pub fn available_intrinsics(&self) -> Vec<&X86CryptoIntrinsicInfo> {
self.intrinsics
.values()
.filter(|info| self.features.has(info.required_feature))
.collect()
}
pub fn generate_intrinsic_header(&self) -> String {
let mut header = String::new();
header.push_str("/* Auto-generated X86 crypto intrinsic declarations */\n");
header.push_str("#ifndef __X86_CRYPTO_INTRINSICS_H\n");
header.push_str("#define __X86_CRYPTO_INTRINSICS_H\n\n");
header.push_str("#include <immintrin.h>\n");
header.push_str("#include <wmmintrin.h>\n");
header.push_str("#include <smmintrin.h>\n\n");
for category in [
X86CryptoIntrinsicCategory::AesNi,
X86CryptoIntrinsicCategory::ShaNi,
X86CryptoIntrinsicCategory::Clmul,
X86CryptoIntrinsicCategory::Random,
X86CryptoIntrinsicCategory::Vaes,
X86CryptoIntrinsicCategory::Vpclmul,
X86CryptoIntrinsicCategory::Sha512,
X86CryptoIntrinsicCategory::Sm3,
X86CryptoIntrinsicCategory::Sm4,
X86CryptoIntrinsicCategory::Gfni,
]
.iter()
{
let cat_intrinsics = self.intrinsics_in_category(*category);
if !cat_intrinsics.is_empty() {
let cat_name = format!("{:?}", category);
header.push_str(&format!("/* ── {} ── */\n", cat_name.to_uppercase()));
for intrinsic in &cat_intrinsics {
if self.features.has(intrinsic.required_feature) {
let return_type = format!("{}", intrinsic.return_type);
let param_types: Vec<String> = intrinsic
.operand_types
.iter()
.map(|t| format!("{}", t))
.collect();
header.push_str(&format!(
"// {} — {} [latency: {}, tp: {}]\n",
intrinsic.mnemonic,
intrinsic.description,
intrinsic.latency,
intrinsic.throughput
));
}
}
header.push('\n');
}
}
header.push_str("#endif /* __X86_CRYPTO_INTRINSICS_H */\n");
header
}
fn add_intrinsic(
map: &mut HashMap<String, X86CryptoIntrinsicInfo>,
cats: &mut BTreeMap<X86CryptoIntrinsicCategory, Vec<String>>,
info: X86CryptoIntrinsicInfo,
) {
let name = info.name.clone();
cats.entry(info.category)
.or_default()
.push(name.clone());
map.insert(name, info);
}
fn register_aesni_intrinsics(
map: &mut HashMap<String, X86CryptoIntrinsicInfo>,
cats: &mut BTreeMap<X86CryptoIntrinsicCategory, Vec<String>>,
_features: &X86CryptoFeatureSet,
) {
let cat = X86CryptoIntrinsicCategory::AesNi;
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm_aesenc_si128".into(),
mnemonic: "aesenc".into(),
operand_count: 2,
operand_types: vec![X86CryptoOperandType::M128i, X86CryptoOperandType::M128i],
return_type: X86CryptoOperandType::M128i,
required_feature: X86CryptoFeature::AesNi,
category: cat,
latency: 4,
throughput: 1.0,
has_side_effects: false,
description: "Perform one round of AES encryption flow".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm_aesenclast_si128".into(),
mnemonic: "aesenclast".into(),
operand_count: 2,
operand_types: vec![X86CryptoOperandType::M128i, X86CryptoOperandType::M128i],
return_type: X86CryptoOperandType::M128i,
required_feature: X86CryptoFeature::AesNi,
category: cat,
latency: 4,
throughput: 1.0,
has_side_effects: false,
description: "Perform last round of AES encryption flow".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm_aesdec_si128".into(),
mnemonic: "aesdec".into(),
operand_count: 2,
operand_types: vec![X86CryptoOperandType::M128i, X86CryptoOperandType::M128i],
return_type: X86CryptoOperandType::M128i,
required_feature: X86CryptoFeature::AesNi,
category: cat,
latency: 4,
throughput: 1.0,
has_side_effects: false,
description: "Perform one round of AES decryption flow using Equivalent Inverse Cipher".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm_aesdeclast_si128".into(),
mnemonic: "aesdeclast".into(),
operand_count: 2,
operand_types: vec![X86CryptoOperandType::M128i, X86CryptoOperandType::M128i],
return_type: X86CryptoOperandType::M128i,
required_feature: X86CryptoFeature::AesNi,
category: cat,
latency: 4,
throughput: 1.0,
has_side_effects: false,
description: "Perform last round of AES decryption flow".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm_aesimc_si128".into(),
mnemonic: "aesimc".into(),
operand_count: 1,
operand_types: vec![X86CryptoOperandType::M128i],
return_type: X86CryptoOperandType::M128i,
required_feature: X86CryptoFeature::AesNi,
category: cat,
latency: 4,
throughput: 1.0,
has_side_effects: false,
description: "Perform the AES InvMixColumn transformation".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm_aeskeygenassist_si128".into(),
mnemonic: "aeskeygenassist".into(),
operand_count: 2,
operand_types: vec![X86CryptoOperandType::M128i, X86CryptoOperandType::Imm8],
return_type: X86CryptoOperandType::M128i,
required_feature: X86CryptoFeature::AesNi,
category: cat,
latency: 6,
throughput: 1.0,
has_side_effects: false,
description: "Assist in expanding the AES cipher key".into(),
});
}
fn register_shani_intrinsics(
map: &mut HashMap<String, X86CryptoIntrinsicInfo>,
cats: &mut BTreeMap<X86CryptoIntrinsicCategory, Vec<String>>,
_features: &X86CryptoFeatureSet,
) {
let cat = X86CryptoIntrinsicCategory::ShaNi;
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm_sha1rnds4_epu32".into(),
mnemonic: "sha1rnds4".into(),
operand_count: 3,
operand_types: vec![
X86CryptoOperandType::M128i,
X86CryptoOperandType::M128i,
X86CryptoOperandType::Imm8,
],
return_type: X86CryptoOperandType::M128i,
required_feature: X86CryptoFeature::ShaNi,
category: cat,
latency: 5,
throughput: 1.0,
has_side_effects: false,
description: "Perform four rounds of SHA-1 operation".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm_sha1nexte_epu32".into(),
mnemonic: "sha1nexte".into(),
operand_count: 2,
operand_types: vec![X86CryptoOperandType::M128i, X86CryptoOperandType::M128i],
return_type: X86CryptoOperandType::M128i,
required_feature: X86CryptoFeature::ShaNi,
category: cat,
latency: 2,
throughput: 0.5,
has_side_effects: false,
description: "Calculate SHA-1 state variable E after four rounds".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm_sha1msg1_epu32".into(),
mnemonic: "sha1msg1".into(),
operand_count: 2,
operand_types: vec![X86CryptoOperandType::M128i, X86CryptoOperandType::M128i],
return_type: X86CryptoOperandType::M128i,
required_feature: X86CryptoFeature::ShaNi,
category: cat,
latency: 2,
throughput: 0.5,
has_side_effects: false,
description: "Performs intermediate calculation for next four SHA-1 message dwords".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm_sha1msg2_epu32".into(),
mnemonic: "sha1msg2".into(),
operand_count: 2,
operand_types: vec![X86CryptoOperandType::M128i, X86CryptoOperandType::M128i],
return_type: X86CryptoOperandType::M128i,
required_feature: X86CryptoFeature::ShaNi,
category: cat,
latency: 2,
throughput: 0.5,
has_side_effects: false,
description: "Perform final calculation for next four SHA-1 message dwords".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm_sha256rnds2_epu32".into(),
mnemonic: "sha256rnds2".into(),
operand_count: 3,
operand_types: vec![
X86CryptoOperandType::M128i,
X86CryptoOperandType::M128i,
X86CryptoOperandType::M128i,
],
return_type: X86CryptoOperandType::M128i,
required_feature: X86CryptoFeature::ShaNi,
category: cat,
latency: 5,
throughput: 1.0,
has_side_effects: false,
description: "Perform two rounds of SHA-256 operation".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm_sha256msg1_epu32".into(),
mnemonic: "sha256msg1".into(),
operand_count: 2,
operand_types: vec![X86CryptoOperandType::M128i, X86CryptoOperandType::M128i],
return_type: X86CryptoOperandType::M128i,
required_feature: X86CryptoFeature::ShaNi,
category: cat,
latency: 2,
throughput: 0.5,
has_side_effects: false,
description: "Perform intermediate calculation for next four SHA-256 message dwords".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm_sha256msg2_epu32".into(),
mnemonic: "sha256msg2".into(),
operand_count: 2,
operand_types: vec![X86CryptoOperandType::M128i, X86CryptoOperandType::M128i],
return_type: X86CryptoOperandType::M128i,
required_feature: X86CryptoFeature::ShaNi,
category: cat,
latency: 2,
throughput: 0.5,
has_side_effects: false,
description: "Perform final calculation for next four SHA-256 message dwords".into(),
});
}
fn register_clmul_intrinsics(
map: &mut HashMap<String, X86CryptoIntrinsicInfo>,
cats: &mut BTreeMap<X86CryptoIntrinsicCategory, Vec<String>>,
_features: &X86CryptoFeatureSet,
) {
let cat = X86CryptoIntrinsicCategory::Clmul;
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm_clmulepi64_si128".into(),
mnemonic: "pclmulqdq".into(),
operand_count: 3,
operand_types: vec![
X86CryptoOperandType::M128i,
X86CryptoOperandType::M128i,
X86CryptoOperandType::Imm8,
],
return_type: X86CryptoOperandType::M128i,
required_feature: X86CryptoFeature::Pclmul,
category: cat,
latency: 5,
throughput: 1.0,
has_side_effects: false,
description: "Carry-less multiplication of two quadwords".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm_clmulepi64_si128_hi".into(),
mnemonic: "pclmulhqdq".into(),
operand_count: 2,
operand_types: vec![X86CryptoOperandType::M128i, X86CryptoOperandType::M128i],
return_type: X86CryptoOperandType::M128i,
required_feature: X86CryptoFeature::Pclmul,
category: cat,
latency: 5,
throughput: 1.0,
has_side_effects: false,
description: "Carry-less multiplication of high quadwords (imm=0x11)".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm_clmulepi64_si128_lo".into(),
mnemonic: "pclmullqdq".into(),
operand_count: 2,
operand_types: vec![X86CryptoOperandType::M128i, X86CryptoOperandType::M128i],
return_type: X86CryptoOperandType::M128i,
required_feature: X86CryptoFeature::Pclmul,
category: cat,
latency: 5,
throughput: 1.0,
has_side_effects: false,
description: "Carry-less multiplication of low quadwords (imm=0x00)".into(),
});
}
fn register_random_intrinsics(
map: &mut HashMap<String, X86CryptoIntrinsicInfo>,
cats: &mut BTreeMap<X86CryptoIntrinsicCategory, Vec<String>>,
_features: &X86CryptoFeatureSet,
) {
let cat = X86CryptoIntrinsicCategory::Random;
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_rdrand16_step".into(),
mnemonic: "rdrand".into(),
operand_count: 1,
operand_types: vec![X86CryptoOperandType::Pu16],
return_type: X86CryptoOperandType::U8,
required_feature: X86CryptoFeature::RdRand,
category: cat,
latency: 100,
throughput: 100.0,
has_side_effects: true,
description: "Read 16-bit hardware random number, returns success flag".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_rdrand32_step".into(),
mnemonic: "rdrand".into(),
operand_count: 1,
operand_types: vec![X86CryptoOperandType::Pu32],
return_type: X86CryptoOperandType::U8,
required_feature: X86CryptoFeature::RdRand,
category: cat,
latency: 100,
throughput: 100.0,
has_side_effects: true,
description: "Read 32-bit hardware random number, returns success flag".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_rdrand64_step".into(),
mnemonic: "rdrand".into(),
operand_count: 1,
operand_types: vec![X86CryptoOperandType::Pu64],
return_type: X86CryptoOperandType::U8,
required_feature: X86CryptoFeature::RdRand,
category: cat,
latency: 100,
throughput: 100.0,
has_side_effects: true,
description: "Read 64-bit hardware random number, returns success flag".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_rdseed16_step".into(),
mnemonic: "rdseed".into(),
operand_count: 1,
operand_types: vec![X86CryptoOperandType::Pu16],
return_type: X86CryptoOperandType::U8,
required_feature: X86CryptoFeature::RdSeed,
category: cat,
latency: 100,
throughput: 100.0,
has_side_effects: true,
description: "Read 16-bit hardware random seed, returns success flag".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_rdseed32_step".into(),
mnemonic: "rdseed".into(),
operand_count: 1,
operand_types: vec![X86CryptoOperandType::Pu32],
return_type: X86CryptoOperandType::U8,
required_feature: X86CryptoFeature::RdSeed,
category: cat,
latency: 100,
throughput: 100.0,
has_side_effects: true,
description: "Read 32-bit hardware random seed, returns success flag".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_rdseed64_step".into(),
mnemonic: "rdseed".into(),
operand_count: 1,
operand_types: vec![X86CryptoOperandType::Pu64],
return_type: X86CryptoOperandType::U8,
required_feature: X86CryptoFeature::RdSeed,
category: cat,
latency: 100,
throughput: 100.0,
has_side_effects: true,
description: "Read 64-bit hardware random seed, returns success flag".into(),
});
}
fn register_vaes_intrinsics(
map: &mut HashMap<String, X86CryptoIntrinsicInfo>,
cats: &mut BTreeMap<X86CryptoIntrinsicCategory, Vec<String>>,
_features: &X86CryptoFeatureSet,
) {
let cat = X86CryptoIntrinsicCategory::Vaes;
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm256_aesenc_epi128".into(),
mnemonic: "vaesenc".into(),
operand_count: 2,
operand_types: vec![X86CryptoOperandType::M256i, X86CryptoOperandType::M256i],
return_type: X86CryptoOperandType::M256i,
required_feature: X86CryptoFeature::Vaes,
category: cat,
latency: 4,
throughput: 1.0,
has_side_effects: false,
description: "Vector AES encryption round (256-bit, 2 blocks)".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm512_aesenc_epi128".into(),
mnemonic: "vaesenc".into(),
operand_count: 2,
operand_types: vec![X86CryptoOperandType::M512i, X86CryptoOperandType::M512i],
return_type: X86CryptoOperandType::M512i,
required_feature: X86CryptoFeature::Vaes,
category: cat,
latency: 4,
throughput: 1.0,
has_side_effects: false,
description: "Vector AES encryption round (512-bit, 4 blocks)".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm256_aesenclast_epi128".into(),
mnemonic: "vaesenclast".into(),
operand_count: 2,
operand_types: vec![X86CryptoOperandType::M256i, X86CryptoOperandType::M256i],
return_type: X86CryptoOperandType::M256i,
required_feature: X86CryptoFeature::Vaes,
category: cat,
latency: 4,
throughput: 1.0,
has_side_effects: false,
description: "Vector AES encryption last round (256-bit, 2 blocks)".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm512_aesenclast_epi128".into(),
mnemonic: "vaesenclast".into(),
operand_count: 2,
operand_types: vec![X86CryptoOperandType::M512i, X86CryptoOperandType::M512i],
return_type: X86CryptoOperandType::M512i,
required_feature: X86CryptoFeature::Vaes,
category: cat,
latency: 4,
throughput: 1.0,
has_side_effects: false,
description: "Vector AES encryption last round (512-bit, 4 blocks)".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm256_aesdec_epi128".into(),
mnemonic: "vaesdec".into(),
operand_count: 2,
operand_types: vec![X86CryptoOperandType::M256i, X86CryptoOperandType::M256i],
return_type: X86CryptoOperandType::M256i,
required_feature: X86CryptoFeature::Vaes,
category: cat,
latency: 4,
throughput: 1.0,
has_side_effects: false,
description: "Vector AES decryption round (256-bit, 2 blocks)".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm512_aesdec_epi128".into(),
mnemonic: "vaesdec".into(),
operand_count: 2,
operand_types: vec![X86CryptoOperandType::M512i, X86CryptoOperandType::M512i],
return_type: X86CryptoOperandType::M512i,
required_feature: X86CryptoFeature::Vaes,
category: cat,
latency: 4,
throughput: 1.0,
has_side_effects: false,
description: "Vector AES decryption round (512-bit, 4 blocks)".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm256_aesdeclast_epi128".into(),
mnemonic: "vaesdeclast".into(),
operand_count: 2,
operand_types: vec![X86CryptoOperandType::M256i, X86CryptoOperandType::M256i],
return_type: X86CryptoOperandType::M256i,
required_feature: X86CryptoFeature::Vaes,
category: cat,
latency: 4,
throughput: 1.0,
has_side_effects: false,
description: "Vector AES decryption last round (256-bit, 2 blocks)".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm512_aesdeclast_epi128".into(),
mnemonic: "vaesdeclast".into(),
operand_count: 2,
operand_types: vec![X86CryptoOperandType::M512i, X86CryptoOperandType::M512i],
return_type: X86CryptoOperandType::M512i,
required_feature: X86CryptoFeature::Vaes,
category: cat,
latency: 4,
throughput: 1.0,
has_side_effects: false,
description: "Vector AES decryption last round (512-bit, 4 blocks)".into(),
});
}
fn register_vpclmul_intrinsics(
map: &mut HashMap<String, X86CryptoIntrinsicInfo>,
cats: &mut BTreeMap<X86CryptoIntrinsicCategory, Vec<String>>,
_features: &X86CryptoFeatureSet,
) {
let cat = X86CryptoIntrinsicCategory::Vpclmul;
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm256_clmulepi64_epi128".into(),
mnemonic: "vpclmulqdq".into(),
operand_count: 3,
operand_types: vec![
X86CryptoOperandType::M256i,
X86CryptoOperandType::M256i,
X86CryptoOperandType::Imm8,
],
return_type: X86CryptoOperandType::M256i,
required_feature: X86CryptoFeature::Vpclmulqdq,
category: cat,
latency: 4,
throughput: 0.5,
has_side_effects: false,
description: "Vector carry-less multiplication (256-bit, 4x64-bit)".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm512_clmulepi64_epi128".into(),
mnemonic: "vpclmulqdq".into(),
operand_count: 3,
operand_types: vec![
X86CryptoOperandType::M512i,
X86CryptoOperandType::M512i,
X86CryptoOperandType::Imm8,
],
return_type: X86CryptoOperandType::M512i,
required_feature: X86CryptoFeature::Vpclmulqdq,
category: cat,
latency: 4,
throughput: 0.5,
has_side_effects: false,
description: "Vector carry-less multiplication (512-bit, 8x64-bit)".into(),
});
}
fn register_sha512_intrinsics(
map: &mut HashMap<String, X86CryptoIntrinsicInfo>,
cats: &mut BTreeMap<X86CryptoIntrinsicCategory, Vec<String>>,
_features: &X86CryptoFeatureSet,
) {
let cat = X86CryptoIntrinsicCategory::Sha512;
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm_sha512rnds2_epi64".into(),
mnemonic: "sha512rnds2".into(),
operand_count: 3,
operand_types: vec![
X86CryptoOperandType::M128i,
X86CryptoOperandType::M128i,
X86CryptoOperandType::M128i,
],
return_type: X86CryptoOperandType::M128i,
required_feature: X86CryptoFeature::Sha512,
category: cat,
latency: 5,
throughput: 1.0,
has_side_effects: false,
description: "Perform two rounds of SHA-512 operation".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm_sha512msg1_epi64".into(),
mnemonic: "sha512msg1".into(),
operand_count: 2,
operand_types: vec![X86CryptoOperandType::M128i, X86CryptoOperandType::M128i],
return_type: X86CryptoOperandType::M128i,
required_feature: X86CryptoFeature::Sha512,
category: cat,
latency: 2,
throughput: 0.5,
has_side_effects: false,
description: "Perform intermediate calculation for SHA-512 message schedule".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm_sha512msg2_epi64".into(),
mnemonic: "sha512msg2".into(),
operand_count: 2,
operand_types: vec![X86CryptoOperandType::M128i, X86CryptoOperandType::M128i],
return_type: X86CryptoOperandType::M128i,
required_feature: X86CryptoFeature::Sha512,
category: cat,
latency: 2,
throughput: 0.5,
has_side_effects: false,
description: "Perform final calculation for SHA-512 message schedule".into(),
});
}
fn register_sm3_intrinsics(
map: &mut HashMap<String, X86CryptoIntrinsicInfo>,
cats: &mut BTreeMap<X86CryptoIntrinsicCategory, Vec<String>>,
_features: &X86CryptoFeatureSet,
) {
let cat = X86CryptoIntrinsicCategory::Sm3;
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm_sm3msg1_epi32".into(),
mnemonic: "vsm3msg1".into(),
operand_count: 2,
operand_types: vec![X86CryptoOperandType::M128i, X86CryptoOperandType::M128i],
return_type: X86CryptoOperandType::M128i,
required_feature: X86CryptoFeature::Sm3,
category: cat,
latency: 2,
throughput: 0.5,
has_side_effects: false,
description: "SM3 message expansion, part 1".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm_sm3msg2_epi32".into(),
mnemonic: "vsm3msg2".into(),
operand_count: 2,
operand_types: vec![X86CryptoOperandType::M128i, X86CryptoOperandType::M128i],
return_type: X86CryptoOperandType::M128i,
required_feature: X86CryptoFeature::Sm3,
category: cat,
latency: 2,
throughput: 0.5,
has_side_effects: false,
description: "SM3 message expansion, part 2".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm_sm3rnds1_epi32".into(),
mnemonic: "vsm3rnds1".into(),
operand_count: 3,
operand_types: vec![
X86CryptoOperandType::M128i,
X86CryptoOperandType::M128i,
X86CryptoOperandType::M128i,
],
return_type: X86CryptoOperandType::M128i,
required_feature: X86CryptoFeature::Sm3,
category: cat,
latency: 4,
throughput: 1.0,
has_side_effects: false,
description: "SM3 compression function rounds".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm_sm3rnds2_epi32".into(),
mnemonic: "vsm3rnds2".into(),
operand_count: 3,
operand_types: vec![
X86CryptoOperandType::M128i,
X86CryptoOperandType::M128i,
X86CryptoOperandType::M128i,
],
return_type: X86CryptoOperandType::M128i,
required_feature: X86CryptoFeature::Sm3,
category: cat,
latency: 4,
throughput: 1.0,
has_side_effects: false,
description: "SM3 compression function final rounds".into(),
});
}
fn register_sm4_intrinsics(
map: &mut HashMap<String, X86CryptoIntrinsicInfo>,
cats: &mut BTreeMap<X86CryptoIntrinsicCategory, Vec<String>>,
_features: &X86CryptoFeatureSet,
) {
let cat = X86CryptoIntrinsicCategory::Sm4;
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm_sm4keyed_epi32".into(),
mnemonic: "vsm4key4".into(),
operand_count: 2,
operand_types: vec![X86CryptoOperandType::M128i, X86CryptoOperandType::M128i],
return_type: X86CryptoOperandType::M128i,
required_feature: X86CryptoFeature::Sm4,
category: cat,
latency: 4,
throughput: 1.0,
has_side_effects: false,
description: "SM4 round operation with round key".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm_sm4keygenassist_epi32".into(),
mnemonic: "vsm4keygenassist".into(),
operand_count: 2,
operand_types: vec![X86CryptoOperandType::M128i, X86CryptoOperandType::Imm8],
return_type: X86CryptoOperandType::M128i,
required_feature: X86CryptoFeature::Sm4,
category: cat,
latency: 6,
throughput: 1.0,
has_side_effects: false,
description: "Assist in SM4 key expansion".into(),
});
}
fn register_gfni_intrinsics(
map: &mut HashMap<String, X86CryptoIntrinsicInfo>,
cats: &mut BTreeMap<X86CryptoIntrinsicCategory, Vec<String>>,
_features: &X86CryptoFeatureSet,
) {
let cat = X86CryptoIntrinsicCategory::Gfni;
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm_gf2p8affine_epi64_epi8".into(),
mnemonic: "gf2p8affineqb".into(),
operand_count: 3,
operand_types: vec![
X86CryptoOperandType::M128i,
X86CryptoOperandType::M128i,
X86CryptoOperandType::Imm8,
],
return_type: X86CryptoOperandType::M128i,
required_feature: X86CryptoFeature::Gfni,
category: cat,
latency: 3,
throughput: 0.5,
has_side_effects: false,
description: "Galois Field affine transformation (used for AES S-Box)".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm_gf2p8affineinv_epi64_epi8".into(),
mnemonic: "gf2p8affineinvqb".into(),
operand_count: 3,
operand_types: vec![
X86CryptoOperandType::M128i,
X86CryptoOperandType::M128i,
X86CryptoOperandType::Imm8,
],
return_type: X86CryptoOperandType::M128i,
required_feature: X86CryptoFeature::Gfni,
category: cat,
latency: 3,
throughput: 0.5,
has_side_effects: false,
description: "Inverse Galois Field affine transformation (used for AES InvS-Box)".into(),
});
Self::add_intrinsic(map, cats, X86CryptoIntrinsicInfo {
name: "_mm_gf2p8mul_epi8".into(),
mnemonic: "gf2p8mulb".into(),
operand_count: 2,
operand_types: vec![X86CryptoOperandType::M128i, X86CryptoOperandType::M128i],
return_type: X86CryptoOperandType::M128i,
required_feature: X86CryptoFeature::Gfni,
category: cat,
latency: 3,
throughput: 0.5,
has_side_effects: false,
description: "Galois Field byte multiplication".into(),
});
}
}
#[derive(Debug, Clone)]
pub struct X86CryptoAlgorithms {
pub features: X86CryptoFeatureSet,
pub aes: X86AesImpl,
pub aes_modes: X86AesModes,
pub sha: X86ShaImpl,
pub hmac: X86HmacImpl,
pub pbkdf2: X86Pbkdf2Impl,
}
impl X86CryptoAlgorithms {
pub fn new(features: X86CryptoFeatureSet) -> Self {
let aes = X86AesImpl::new(features.clone());
let aes_modes = X86AesModes::new(features.clone());
let sha = X86ShaImpl::new(features.clone());
let hmac = X86HmacImpl::new(features.clone());
let pbkdf2 = X86Pbkdf2Impl::new(features.clone());
Self {
features,
aes,
aes_modes,
sha,
hmac,
pbkdf2,
}
}
}
#[derive(Debug, Clone)]
pub struct X86AesImpl {
pub features: X86CryptoFeatureSet,
pub use_hardware: bool,
}
impl X86AesImpl {
pub fn new(features: X86CryptoFeatureSet) -> Self {
let use_hardware = features.has(X86CryptoFeature::AesNi);
Self {
features,
use_hardware,
}
}
pub fn encrypt_block_aes128(&self, block: &[u8; 16], round_keys: &[u8; 176]) -> [u8; 16] {
if self.use_hardware {
self.aesni_encrypt(block, round_keys, X86_AES128_ROUNDS)
} else {
self.software_aes_encrypt(block, round_keys, X86_AES128_ROUNDS)
}
}
pub fn encrypt_block_aes192(&self, block: &[u8; 16], round_keys: &[u8; 208]) -> [u8; 16] {
if self.use_hardware {
self.aesni_encrypt(block, round_keys, X86_AES192_ROUNDS)
} else {
self.software_aes_encrypt(block, round_keys, X86_AES192_ROUNDS)
}
}
pub fn encrypt_block_aes256(&self, block: &[u8; 16], round_keys: &[u8; 240]) -> [u8; 16] {
if self.use_hardware {
self.aesni_encrypt(block, round_keys, X86_AES256_ROUNDS)
} else {
self.software_aes_encrypt(block, round_keys, X86_AES256_ROUNDS)
}
}
pub fn decrypt_block_aes128(&self, block: &[u8; 16], round_keys: &[u8; 176]) -> [u8; 16] {
if self.use_hardware {
self.aesni_decrypt(block, round_keys, X86_AES128_ROUNDS)
} else {
self.software_aes_decrypt(block, round_keys, X86_AES128_ROUNDS)
}
}
pub fn decrypt_block_aes192(&self, block: &[u8; 16], round_keys: &[u8; 208]) -> [u8; 16] {
if self.use_hardware {
self.aesni_decrypt(block, round_keys, X86_AES192_ROUNDS)
} else {
self.software_aes_decrypt(block, round_keys, X86_AES192_ROUNDS)
}
}
pub fn decrypt_block_aes256(&self, block: &[u8; 16], round_keys: &[u8; 240]) -> [u8; 16] {
if self.use_hardware {
self.aesni_decrypt(block, round_keys, X86_AES256_ROUNDS)
} else {
self.software_aes_decrypt(block, round_keys, X86_AES256_ROUNDS)
}
}
pub fn key_expansion_128(&self, key: &[u8; 16]) -> [u8; 176] {
if self.use_hardware {
self.aesni_key_expand(key, X86_AES128_ROUNDS)
} else {
self.software_key_expand(key, X86_AES128_ROUNDS)
}
}
pub fn key_expansion_192(&self, key: &[u8; 24]) -> [u8; 208] {
if self.use_hardware {
let mut expanded = [0u8; 208];
expanded[..24].copy_from_slice(key);
self.software_key_expand_192(key, &mut expanded);
expanded
} else {
let mut expanded = [0u8; 208];
expanded[..24].copy_from_slice(key);
self.software_key_expand_192(key, &mut expanded);
expanded
}
}
pub fn key_expansion_256(&self, key: &[u8; 32]) -> [u8; 240] {
if self.use_hardware {
self.aesni_key_expand(key, X86_AES256_ROUNDS)
} else {
self.software_key_expand(key, X86_AES256_ROUNDS)
}
}
fn aesni_encrypt(&self, block: &[u8; 16], round_keys: &[u8], rounds: usize) -> [u8; 16] {
let mut state = *block;
for i in 0..16 {
state[i] ^= round_keys[i];
}
for r in 1..rounds {
self.sub_bytes(&mut state);
self.shift_rows(&mut state);
self.mix_columns(&mut state);
let offset = r * 16;
for i in 0..16 {
state[i] ^= round_keys[offset + i];
}
}
self.sub_bytes(&mut state);
self.shift_rows(&mut state);
let offset = rounds * 16;
for i in 0..16 {
state[i] ^= round_keys[offset + i];
}
state
}
fn aesni_decrypt(&self, block: &[u8; 16], round_keys: &[u8], rounds: usize) -> [u8; 16] {
let mut state = *block;
let offset = rounds * 16;
for i in 0..16 {
state[i] ^= round_keys[offset + i];
}
for r in (1..rounds).rev() {
self.inv_shift_rows(&mut state);
self.inv_sub_bytes(&mut state);
let offset = r * 16;
for i in 0..16 {
state[i] ^= round_keys[offset + i];
}
self.inv_mix_columns(&mut state);
}
self.inv_shift_rows(&mut state);
self.inv_sub_bytes(&mut state);
for i in 0..16 {
state[i] ^= round_keys[i];
}
state
}
fn software_aes_encrypt(&self, block: &[u8; 16], round_keys: &[u8], rounds: usize) -> [u8; 16] {
self.aesni_encrypt(block, round_keys, rounds)
}
fn software_aes_decrypt(&self, block: &[u8; 16], round_keys: &[u8], rounds: usize) -> [u8; 16] {
self.aesni_decrypt(block, round_keys, rounds)
}
fn aesni_key_expand(&self, key: &[u8], rounds: usize) -> Vec<u8> {
let nk = key.len() / 4; let total_words = 4 * (rounds + 1);
let mut w = vec![0u32; total_words];
for i in 0..nk {
w[i] = u32::from_be_bytes([
key[4 * i],
key[4 * i + 1],
key[4 * i + 2],
key[4 * i + 3],
]);
}
for i in nk..total_words {
let mut temp = w[i - 1];
if i % nk == 0 {
temp = temp.rotate_left(8);
temp = self.sub_word(temp);
temp ^= X86_AES_RCON[i / nk];
} else if nk > 6 && i % nk == 4 {
temp = self.sub_word(temp);
}
w[i] = w[i - nk] ^ temp;
}
let mut expanded = vec![0u8; total_words * 4];
for i in 0..total_words {
let bytes = w[i].to_be_bytes();
expanded[4 * i] = bytes[0];
expanded[4 * i + 1] = bytes[1];
expanded[4 * i + 2] = bytes[2];
expanded[4 * i + 3] = bytes[3];
}
expanded
}
fn software_key_expand(&self, key: &[u8], rounds: usize) -> Vec<u8> {
self.aesni_key_expand(key, rounds)
}
fn software_key_expand_192(&self, key: &[u8; 24], expanded: &mut [u8; 208]) {
let result = self.aesni_key_expand(key, X86_AES192_ROUNDS);
expanded.copy_from_slice(&result);
}
fn sub_word(&self, word: u32) -> u32 {
let bytes = word.to_be_bytes();
let sbox = X86_AES_SBOX;
let sb: [u8; 4] = [
sbox[bytes[0] as usize],
sbox[bytes[1] as usize],
sbox[bytes[2] as usize],
sbox[bytes[3] as usize],
];
u32::from_be_bytes(sb)
}
fn inv_sub_word(&self, word: u32) -> u32 {
let bytes = word.to_be_bytes();
let sbox = X86_AES_INV_SBOX;
let sb: [u8; 4] = [
sbox[bytes[0] as usize],
sbox[bytes[1] as usize],
sbox[bytes[2] as usize],
sbox[bytes[3] as usize],
];
u32::from_be_bytes(sb)
}
fn sub_bytes(&self, state: &mut [u8; 16]) {
for byte in state.iter_mut() {
*byte = X86_AES_SBOX[*byte as usize];
}
}
fn inv_sub_bytes(&self, state: &mut [u8; 16]) {
for byte in state.iter_mut() {
*byte = X86_AES_INV_SBOX[*byte as usize];
}
}
fn shift_rows(&self, state: &mut [u8; 16]) {
let temp = *state;
state[1] = temp[5];
state[5] = temp[9];
state[9] = temp[13];
state[13] = temp[1];
state[2] = temp[10];
state[6] = temp[14];
state[10] = temp[2];
state[14] = temp[6];
state[3] = temp[15];
state[7] = temp[3];
state[11] = temp[7];
state[15] = temp[11];
}
fn inv_shift_rows(&self, state: &mut [u8; 16]) {
let temp = *state;
state[1] = temp[13];
state[5] = temp[1];
state[9] = temp[5];
state[13] = temp[9];
state[2] = temp[10];
state[6] = temp[14];
state[10] = temp[2];
state[14] = temp[6];
state[3] = temp[7];
state[7] = temp[11];
state[11] = temp[15];
state[15] = temp[3];
}
fn mix_columns(&self, state: &mut [u8; 16]) {
for col in 0..4 {
let base = col * 4;
let a = [
state[base],
state[base + 1],
state[base + 2],
state[base + 3],
];
state[base] = X86_GF_MUL2[a[0] as usize] ^ X86_GF_MUL3[a[1] as usize] ^ a[2] ^ a[3];
state[base + 1] = a[0] ^ X86_GF_MUL2[a[1] as usize] ^ X86_GF_MUL3[a[2] as usize] ^ a[3];
state[base + 2] = a[0] ^ a[1] ^ X86_GF_MUL2[a[2] as usize] ^ X86_GF_MUL3[a[3] as usize];
state[base + 3] =
X86_GF_MUL3[a[0] as usize] ^ a[1] ^ a[2] ^ X86_GF_MUL2[a[3] as usize];
}
}
fn inv_mix_columns(&self, state: &mut [u8; 16]) {
for col in 0..4 {
let base = col * 4;
let a = [
state[base],
state[base + 1],
state[base + 2],
state[base + 3],
];
state[base] = X86_GF_MULE[a[0] as usize]
^ X86_GF_MULB[a[1] as usize]
^ X86_GF_MULD[a[2] as usize]
^ X86_GF_MUL9[a[3] as usize];
state[base + 1] = X86_GF_MUL9[a[0] as usize]
^ X86_GF_MULE[a[1] as usize]
^ X86_GF_MULB[a[2] as usize]
^ X86_GF_MULD[a[3] as usize];
state[base + 2] = X86_GF_MULD[a[0] as usize]
^ X86_GF_MUL9[a[1] as usize]
^ X86_GF_MULE[a[2] as usize]
^ X86_GF_MULB[a[3] as usize];
state[base + 3] = X86_GF_MULB[a[0] as usize]
^ X86_GF_MULD[a[1] as usize]
^ X86_GF_MUL9[a[2] as usize]
^ X86_GF_MULE[a[3] as usize];
}
}
}
#[derive(Debug, Clone)]
pub struct X86AesModes {
pub features: X86CryptoFeatureSet,
pub aes: X86AesImpl,
pub ghash: X86GhashImpl,
}
impl X86AesModes {
pub fn new(features: X86CryptoFeatureSet) -> Self {
let aes = X86AesImpl::new(features.clone());
let ghash = X86GhashImpl::new(features.clone());
Self {
features,
aes,
ghash,
}
}
pub fn ecb_encrypt(
&self,
key_size: usize,
plaintext: &[u8],
key: &[u8],
) -> Result<Vec<u8>, X86CryptoError> {
let (round_keys, rounds) = self.prepare_key(key_size, key)?;
let num_blocks = plaintext.len() / 16;
let mut ciphertext = vec![0u8; num_blocks * 16];
for block_idx in 0..num_blocks {
let mut block: [u8; 16] = [0u8; 16];
let offset = block_idx * 16;
block.copy_from_slice(&plaintext[offset..offset + 16]);
let encrypted = self.aes.aesni_encrypt(&block, &round_keys, rounds);
ciphertext[offset..offset + 16].copy_from_slice(&encrypted);
}
Ok(ciphertext)
}
pub fn ecb_decrypt(
&self,
key_size: usize,
ciphertext: &[u8],
key: &[u8],
) -> Result<Vec<u8>, X86CryptoError> {
let (round_keys, rounds) = self.prepare_key(key_size, key)?;
let num_blocks = ciphertext.len() / 16;
let mut plaintext = vec![0u8; num_blocks * 16];
for block_idx in 0..num_blocks {
let mut block: [u8; 16] = [0u8; 16];
let offset = block_idx * 16;
block.copy_from_slice(&ciphertext[offset..offset + 16]);
let decrypted = self.aes.aesni_decrypt(&block, &round_keys, rounds);
plaintext[offset..offset + 16].copy_from_slice(&decrypted);
}
Ok(plaintext)
}
pub fn cbc_encrypt(
&self,
key_size: usize,
plaintext: &[u8],
key: &[u8],
iv: &[u8; 16],
) -> Result<Vec<u8>, X86CryptoError> {
let (round_keys, rounds) = self.prepare_key(key_size, key)?;
let num_blocks = plaintext.len() / 16;
let mut ciphertext = vec![0u8; num_blocks * 16];
let mut prev = *iv;
for block_idx in 0..num_blocks {
let offset = block_idx * 16;
let mut block: [u8; 16] = [0u8; 16];
block.copy_from_slice(&plaintext[offset..offset + 16]);
for i in 0..16 {
block[i] ^= prev[i];
}
let encrypted = self.aes.aesni_encrypt(&block, &round_keys, rounds);
ciphertext[offset..offset + 16].copy_from_slice(&encrypted);
prev = encrypted;
}
Ok(ciphertext)
}
pub fn cbc_decrypt(
&self,
key_size: usize,
ciphertext: &[u8],
key: &[u8],
iv: &[u8; 16],
) -> Result<Vec<u8>, X86CryptoError> {
let (round_keys, rounds) = self.prepare_key(key_size, key)?;
let num_blocks = ciphertext.len() / 16;
let mut plaintext = vec![0u8; num_blocks * 16];
let mut prev = *iv;
for block_idx in 0..num_blocks {
let offset = block_idx * 16;
let mut block: [u8; 16] = [0u8; 16];
block.copy_from_slice(&ciphertext[offset..offset + 16]);
let decrypted = self.aes.aesni_decrypt(&block, &round_keys, rounds);
for i in 0..16 {
plaintext[offset + i] = decrypted[i] ^ prev[i];
}
prev = block;
}
Ok(plaintext)
}
pub fn ctr_crypt(
&self,
key_size: usize,
data: &[u8],
key: &[u8],
nonce: &[u8],
) -> Result<Vec<u8>, X86CryptoError> {
let (round_keys, rounds) = self.prepare_key(key_size, key)?;
let mut output = vec![0u8; data.len()];
let mut counter_block = [0u8; 16];
let nonce_len = nonce.len().min(8);
counter_block[..nonce_len].copy_from_slice(&nonce[..nonce_len]);
let num_blocks = (data.len() + 15) / 16;
for block_idx in 0..num_blocks {
let ctr = block_idx as u64;
counter_block[8] = ((ctr >> 56) & 0xFF) as u8;
counter_block[9] = ((ctr >> 48) & 0xFF) as u8;
counter_block[10] = ((ctr >> 40) & 0xFF) as u8;
counter_block[11] = ((ctr >> 32) & 0xFF) as u8;
counter_block[12] = ((ctr >> 24) & 0xFF) as u8;
counter_block[13] = ((ctr >> 16) & 0xFF) as u8;
counter_block[14] = ((ctr >> 8) & 0xFF) as u8;
counter_block[15] = (ctr & 0xFF) as u8;
let keystream = self.aes.aesni_encrypt(&counter_block, &round_keys, rounds);
let start = block_idx * 16;
let end = std::cmp::min(start + 16, data.len());
for i in start..end {
output[i] = data[i] ^ keystream[i - start];
}
}
Ok(output)
}
pub fn gcm_encrypt(
&self,
key_size: usize,
plaintext: &[u8],
key: &[u8],
iv: &[u8],
aad: &[u8],
tag_size: usize,
) -> Result<(Vec<u8>, Vec<u8>), X86CryptoError> {
if tag_size < 4 || tag_size > 16 {
return Err(X86CryptoError::InvalidTagSize(tag_size));
}
let (round_keys, _rounds) = self.prepare_key(key_size, key)?;
let zero_block = [0u8; 16];
let mut h = self.aes.aesni_encrypt(&zero_block, &round_keys, 10);
self.ghash.set_h(&h);
let j0 = self.gcm_compute_j0(&round_keys, iv);
let ciphertext = self.gcm_ctr_encrypt(&round_keys, plaintext, &j0);
let mut ghash_input = Vec::new();
ghash_input.extend_from_slice(aad);
let aad_pad = (16 - (aad.len() % 16)) % 16;
ghash_input.extend(std::iter::repeat(0u8).take(aad_pad));
ghash_input.extend_from_slice(&ciphertext);
let ct_pad = (16 - (ciphertext.len() % 16)) % 16;
ghash_input.extend(std::iter::repeat(0u8).take(ct_pad));
let aad_bits = (aad.len() as u64) * 8;
let ct_bits = (ciphertext.len() as u64) * 8;
ghash_input.extend_from_slice(&aad_bits.to_be_bytes());
ghash_input.extend_from_slice(&ct_bits.to_be_bytes());
let ghash_result = self.ghash.compute_ghash(&ghash_input);
let mut j0_block = [0u8; 16];
j0_block.copy_from_slice(&j0);
let encrypted_j0 = self.aes.aesni_encrypt(&j0_block, &round_keys, 10);
let mut tag = [0u8; 16];
for i in 0..16 {
tag[i] = ghash_result[i] ^ encrypted_j0[i];
}
Ok((ciphertext, tag[..tag_size].to_vec()))
}
pub fn gcm_decrypt(
&self,
key_size: usize,
ciphertext: &[u8],
key: &[u8],
iv: &[u8],
aad: &[u8],
tag: &[u8],
) -> Result<Vec<u8>, X86CryptoError> {
let (round_keys, _rounds) = self.prepare_key(key_size, key)?;
let zero_block = [0u8; 16];
let mut h = self.aes.aesni_encrypt(&zero_block, &round_keys, 10);
self.ghash.set_h(&h);
let j0 = self.gcm_compute_j0(&round_keys, iv);
let mut ghash_input = Vec::new();
ghash_input.extend_from_slice(aad);
let aad_pad = (16 - (aad.len() % 16)) % 16;
ghash_input.extend(std::iter::repeat(0u8).take(aad_pad));
ghash_input.extend_from_slice(ciphertext);
let ct_pad = (16 - (ciphertext.len() % 16)) % 16;
ghash_input.extend(std::iter::repeat(0u8).take(ct_pad));
let aad_bits = (aad.len() as u64) * 8;
let ct_bits = (ciphertext.len() as u64) * 8;
ghash_input.extend_from_slice(&aad_bits.to_be_bytes());
ghash_input.extend_from_slice(&ct_bits.to_be_bytes());
let ghash_result = self.ghash.compute_ghash(&ghash_input);
let mut j0_block = [0u8; 16];
j0_block.copy_from_slice(&j0);
let encrypted_j0 = self.aes.aesni_encrypt(&j0_block, &round_keys, 10);
let mut expected_tag = [0u8; 16];
for i in 0..16 {
expected_tag[i] = ghash_result[i] ^ encrypted_j0[i];
}
if !X86ConstantTime::ct_memcmp(&expected_tag[..tag.len()], tag) {
return Err(X86CryptoError::TagMismatch);
}
Ok(self.gcm_ctr_decrypt(&round_keys, ciphertext, &j0))
}
fn gcm_compute_j0(&self, round_keys: &[u8], iv: &[u8]) -> [u8; 16] {
if iv.len() == 12 {
let mut j0 = [0u8; 16];
j0[..12].copy_from_slice(iv);
j0[15] = 1;
j0
} else {
let mut input = iv.to_vec();
let pad = (16 - (iv.len() % 16)) % 16;
input.extend(std::iter::repeat(0u8).take(pad));
input.extend_from_slice(&0u64.to_be_bytes());
input.extend_from_slice(&((iv.len() as u64) * 8).to_be_bytes());
self.ghash.compute_ghash(&input)
}
}
fn gcm_ctr_encrypt(&self, round_keys: &[u8], data: &[u8], j0: &[u8; 16]) -> Vec<u8> {
let mut output = vec![0u8; data.len()];
let num_blocks = (data.len() + 15) / 16;
for block_idx in 0..num_blocks {
let mut ctr = *j0;
let inc = (block_idx + 1) as u32;
let mut val = u32::from_be_bytes([ctr[12], ctr[13], ctr[14], ctr[15]]);
val = val.wrapping_add(inc);
let b = val.to_be_bytes();
ctr[12] = b[0];
ctr[13] = b[1];
ctr[14] = b[2];
ctr[15] = b[3];
let keystream = self.aes.aesni_encrypt(&ctr, round_keys, 10);
let start = block_idx * 16;
let end = std::cmp::min(start + 16, data.len());
for i in start..end {
output[i] = data[i] ^ keystream[i - start];
}
}
output
}
fn gcm_ctr_decrypt(&self, round_keys: &[u8], data: &[u8], j0: &[u8; 16]) -> Vec<u8> {
self.gcm_ctr_encrypt(round_keys, data, j0)
}
pub fn ccm_encrypt(
&self,
key_size: usize,
plaintext: &[u8],
key: &[u8],
nonce: &[u8],
aad: &[u8],
tag_size: usize,
) -> Result<(Vec<u8>, Vec<u8>), X86CryptoError> {
if nonce.len() < X86_CCM_MIN_NONCE_SIZE || nonce.len() > X86_CCM_MAX_NONCE_SIZE {
return Err(X86CryptoError::InvalidNonceSize(nonce.len()));
}
if tag_size % 2 != 0 || tag_size < 4 || tag_size > 16 {
return Err(X86CryptoError::InvalidTagSize(tag_size));
}
let (round_keys, _rounds) = self.prepare_key(key_size, key)?;
let q = 15 - nonce.len() as u8;
let mut b0 = [0u8; 16];
b0[0] = if !aad.is_empty() { 0x40 } else { 0x00 };
b0[0] |= ((tag_size - 2) / 2) << 3; b0[0] |= q - 1; b0[1..1 + nonce.len()].copy_from_slice(nonce);
let plen = plaintext.len();
for i in 0..q as usize {
b0[15 - i] = ((plen >> (8 * i)) & 0xFF) as u8;
}
let mut auth_blocks = vec![b0];
if !aad.is_empty() {
let mut aad_blocks = self.ccm_format_aad(aad);
auth_blocks.append(&mut aad_blocks);
}
let mut msg_blocks = Vec::new();
for chunk in plaintext.chunks(16) {
let mut block = [0u8; 16];
block[..chunk.len()].copy_from_slice(chunk);
msg_blocks.push(block);
}
let mut mac = [0u8; 16];
for block in &auth_blocks {
for i in 0..16 {
mac[i] ^= block[i];
}
mac = self.aes.aesni_encrypt(&mac, &round_keys, 10);
}
for block in &msg_blocks {
for i in 0..16 {
mac[i] ^= block[i];
}
mac = self.aes.aesni_encrypt(&mac, &round_keys, 10);
}
let mut ctr_blocks = Vec::new();
for block_idx in 0..=msg_blocks.len() {
let mut ctr = [0u8; 16];
ctr[0] = q - 1;
ctr[1..1 + nonce.len()].copy_from_slice(nonce);
for i in 0..q as usize {
ctr[15 - i] = ((block_idx >> (8 * i)) & 0xFF) as u8;
}
let encrypted_ctr = self.aes.aesni_encrypt(&ctr, &round_keys, 10);
ctr_blocks.push(encrypted_ctr);
}
let mut tag = [0u8; 16];
for i in 0..16 {
tag[i] = mac[i] ^ ctr_blocks[0][i];
}
let mut ciphertext = Vec::with_capacity(plaintext.len());
for (idx, block) in msg_blocks.iter().enumerate() {
for i in 0..block.len().min(plaintext.len() - idx * 16) {
ciphertext.push(block[i] ^ ctr_blocks[idx + 1][i]);
}
}
Ok((ciphertext, tag[..tag_size].to_vec()))
}
fn ccm_format_aad(&self, aad: &[u8]) -> Vec<[u8; 16]> {
let mut blocks = Vec::new();
let aad_len = aad.len();
if aad_len < 65280 {
let mut header = [0u8; 16];
header[0] = ((aad_len >> 8) & 0xFF) as u8;
header[1] = (aad_len & 0xFF) as u8;
let copy_len = std::cmp::min(14, aad_len);
header[2..2 + copy_len].copy_from_slice(&aad[..copy_len]);
blocks.push(header);
if aad_len > 14 {
for chunk in aad[14..].chunks(16) {
let mut block = [0u8; 16];
block[..chunk.len()].copy_from_slice(chunk);
blocks.push(block);
}
}
} else {
let mut header = [0u8; 16];
header[0] = 0xFF;
header[1] = 0xFE;
let l = aad_len as u32;
header[2] = ((l >> 24) & 0xFF) as u8;
header[3] = ((l >> 16) & 0xFF) as u8;
header[4] = ((l >> 8) & 0xFF) as u8;
header[5] = (l & 0xFF) as u8;
let copy_len = std::cmp::min(10, aad_len);
header[6..6 + copy_len].copy_from_slice(&aad[..copy_len]);
blocks.push(header);
if aad_len > 10 {
for chunk in aad[10..].chunks(16) {
let mut block = [0u8; 16];
block[..chunk.len()].copy_from_slice(chunk);
blocks.push(block);
}
}
}
blocks
}
pub fn xts_encrypt(
&self,
key_size: usize,
plaintext: &[u8],
key1: &[u8],
key2: &[u8],
tweak: &[u8; 16],
) -> Result<Vec<u8>, X86CryptoError> {
let (round_keys1, _r1) = self.prepare_key(key_size, key1)?;
let (round_keys2, _r2) = self.prepare_key(key_size, key2)?;
let num_blocks = plaintext.len() / 16;
let mut ciphertext = vec![0u8; num_blocks * 16];
let mut t = self.aes.aesni_encrypt(tweak, &round_keys2, 10);
for block_idx in 0..num_blocks {
let offset = block_idx * 16;
let mut block: [u8; 16] = [0u8; 16];
block.copy_from_slice(&plaintext[offset..offset + 16]);
for i in 0..16 {
block[i] ^= t[i];
}
let mut encrypted = self.aes.aesni_encrypt(&block, &round_keys1, 10);
for i in 0..16 {
encrypted[i] ^= t[i];
}
ciphertext[offset..offset + 16].copy_from_slice(&encrypted);
t = self.xts_gf_mul(t);
}
Ok(ciphertext)
}
pub fn xts_decrypt(
&self,
key_size: usize,
ciphertext: &[u8],
key1: &[u8],
key2: &[u8],
tweak: &[u8; 16],
) -> Result<Vec<u8>, X86CryptoError> {
let (round_keys1, _r1) = self.prepare_key(key_size, key1)?;
let (round_keys2, _r2) = self.prepare_key(key_size, key2)?;
let num_blocks = ciphertext.len() / 16;
let mut plaintext = vec![0u8; num_blocks * 16];
let mut t = self.aes.aesni_encrypt(tweak, &round_keys2, 10);
for block_idx in 0..num_blocks {
let offset = block_idx * 16;
let mut block: [u8; 16] = [0u8; 16];
block.copy_from_slice(&ciphertext[offset..offset + 16]);
for i in 0..16 {
block[i] ^= t[i];
}
let mut decrypted = self.aes.aesni_decrypt(&block, &round_keys1, 10);
for i in 0..16 {
decrypted[i] ^= t[i];
}
plaintext[offset..offset + 16].copy_from_slice(&decrypted);
t = self.xts_gf_mul(t);
}
Ok(plaintext)
}
fn xts_gf_mul(&self, mut t: [u8; 16]) -> [u8; 16] {
let carry = (t[0] >> 7) & 1;
for i in 0..15 {
t[i] = (t[i] << 1) | (t[i + 1] >> 7);
}
t[15] <<= 1;
if carry != 0 {
t[15] ^= 0x87; }
t
}
fn prepare_key(
&self,
key_size: usize,
key: &[u8],
) -> Result<(Vec<u8>, usize), X86CryptoError> {
let (rounds, expected_size) = match key_size {
16 => (X86_AES128_ROUNDS, 16),
24 => (X86_AES192_ROUNDS, 24),
32 => (X86_AES256_ROUNDS, 32),
_ => return Err(X86CryptoError::InvalidKeySize(key_size)),
};
if key.len() != expected_size {
return Err(X86CryptoError::InvalidKeySize(key.len()));
}
let round_keys = self.aes.aesni_key_expand(key, rounds);
Ok((round_keys, rounds))
}
}
#[derive(Debug, Clone)]
pub struct X86GhashImpl {
pub features: X86CryptoFeatureSet,
pub h: [u64; 2],
pub h_table: Vec<[u64; 2]>, }
impl X86GhashImpl {
pub fn new(features: X86CryptoFeatureSet) -> Self {
Self {
features,
h: [0, 0],
h_table: Vec::new(),
}
}
pub fn set_h(&mut self, h: &[u8; 16]) {
self.h[0] = u64::from_be_bytes([
h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7],
]);
self.h[1] = u64::from_be_bytes([
h[8], h[9], h[10], h[11], h[12], h[13], h[14], h[15],
]);
}
pub fn compute_ghash(&self, data: &[u8]) -> [u8; 16] {
let mut y = [0u64; 2];
for chunk in data.chunks(16) {
let mut block = [0u8; 16];
let len = chunk.len().min(16);
block[..len].copy_from_slice(chunk);
let x0 = u64::from_be_bytes([
block[0], block[1], block[2], block[3], block[4], block[5], block[6], block[7],
]);
let x1 = u64::from_be_bytes([
block[8],
block[9],
block[10],
block[11],
block[12],
block[13],
block[14],
block[15],
]);
y[0] ^= x0;
y[1] ^= x1;
y = self.gf128_mul(y, self.h);
}
let mut result = [0u8; 16];
let b0 = y[0].to_be_bytes();
let b1 = y[1].to_be_bytes();
result[..8].copy_from_slice(&b0);
result[8..].copy_from_slice(&b1);
result
}
fn gf128_mul(&self, a: [u64; 2], b: [u64; 2]) -> [u64; 2] {
let (a0, a1) = (a[0], a[1]);
let (b0, b1) = (b[0], b[1]);
let (c0, c1) = self.clmul64(a0, b0); let (d0, d1) = self.clmul64(a0 ^ a1, b0 ^ b1); let (e0, e1) = self.clmul64(a1, b1);
let mut r0 = c0 ^ d0 ^ e0;
let mut r1 = c1 ^ d1 ^ e1;
r0 ^= self.gf128_reduce(r1);
r1 = 0;
let high_bit = r0 >> 63;
if high_bit != 0 {
r0 ^= 0xE100000000000000; }
[r0, r1]
}
fn clmul64(&self, a: u64, b: u64) -> (u64, u64) {
let mut low: u64 = 0;
let mut high: u64 = 0;
for i in 0..64 {
if (b >> i) & 1 != 0 {
let shifted = a.wrapping_shl(i as u32);
let (sum, overflow) = low.overflowing_add(shifted);
low = sum;
if overflow {
high = high.wrapping_add(1);
}
if i > 0 {
high = high.wrapping_add(a.wrapping_shr((64 - i) as u32));
}
}
}
(low, high)
}
fn gf128_reduce(&self, val: u64) -> u64 {
let mut result: u64 = 0;
let mut v = val;
let poly = 0x87u64;
for i in 0..64 {
if (v >> 63) & 1 != 0 {
let shift = 63 - i;
if shift < 64 {
result ^= poly << shift;
}
}
v <<= 1;
}
result
}
}
#[derive(Debug, Clone)]
pub struct X86ShaImpl {
pub features: X86CryptoFeatureSet,
pub use_shani: bool,
}
impl X86ShaImpl {
pub fn new(features: X86CryptoFeatureSet) -> Self {
let use_shani = features.has(X86CryptoFeature::ShaNi);
Self {
features,
use_shani,
}
}
pub fn sha1(&self, message: &[u8]) -> [u8; 20] {
if self.use_shani {
self.sha1_shani(message)
} else {
self.sha1_software(message)
}
}
pub fn sha256(&self, message: &[u8]) -> [u8; 32] {
if self.use_shani {
self.sha256_shani(message)
} else {
self.sha256_software(message)
}
}
pub fn sha512(&self, message: &[u8]) -> [u8; 64] {
self.sha512_software(message)
}
fn sha1_software(&self, message: &[u8]) -> [u8; 20] {
let mut h0: u32 = 0x67452301;
let mut h1: u32 = 0xEFCDAB89;
let mut h2: u32 = 0x98BADCFE;
let mut h3: u32 = 0x10325476;
let mut h4: u32 = 0xC3D2E1F0;
let padded = self.sha1_pad(message);
for chunk in padded.chunks(64) {
let mut w = [0u32; 80];
for i in 0..16 {
w[i] = u32::from_be_bytes([
chunk[4 * i],
chunk[4 * i + 1],
chunk[4 * i + 2],
chunk[4 * i + 3],
]);
}
for i in 16..80 {
w[i] = (w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]).rotate_left(1);
}
let (mut a, mut b, mut c, mut d, mut e) = (h0, h1, h2, h3, h4);
for i in 0..80 {
let (f, k): (u32, u32) = match i {
0..=19 => ((b & c) | ((!b) & d), 0x5A827999),
20..=39 => (b ^ c ^ d, 0x6ED9EBA1),
40..=59 => ((b & c) | (b & d) | (c & d), 0x8F1BBCDC),
_ => (b ^ c ^ d, 0xCA62C1D6),
};
let temp = a
.rotate_left(5)
.wrapping_add(f)
.wrapping_add(e)
.wrapping_add(k)
.wrapping_add(w[i]);
e = d;
d = c;
c = b.rotate_left(30);
b = a;
a = temp;
}
h0 = h0.wrapping_add(a);
h1 = h1.wrapping_add(b);
h2 = h2.wrapping_add(c);
h3 = h3.wrapping_add(d);
h4 = h4.wrapping_add(e);
}
let mut hash = [0u8; 20];
hash[0..4].copy_from_slice(&h0.to_be_bytes());
hash[4..8].copy_from_slice(&h1.to_be_bytes());
hash[8..12].copy_from_slice(&h2.to_be_bytes());
hash[12..16].copy_from_slice(&h3.to_be_bytes());
hash[16..20].copy_from_slice(&h4.to_be_bytes());
hash
}
fn sha1_pad(&self, message: &[u8]) -> Vec<u8> {
let ml = message.len() as u64 * 8; let mut padded = message.to_vec();
padded.push(0x80);
while (padded.len() % 64) != 56 {
padded.push(0x00);
}
padded.extend_from_slice(&ml.to_be_bytes());
padded
}
fn sha1_shani(&self, message: &[u8]) -> [u8; 20] {
self.sha1_software(message)
}
fn sha256_software(&self, message: &[u8]) -> [u8; 32] {
let mut state: [u32; 8] = [
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
];
let padded = self.sha256_pad(message);
for chunk in padded.chunks(64) {
let mut w = [0u32; 64];
for i in 0..16 {
w[i] = u32::from_be_bytes([
chunk[4 * i],
chunk[4 * i + 1],
chunk[4 * i + 2],
chunk[4 * i + 3],
]);
}
for i in 16..64 {
let s0 = w[i - 15].rotate_right(7) ^ w[i - 15].rotate_right(18) ^ (w[i - 15] >> 3);
let s1 = w[i - 2].rotate_right(17) ^ w[i - 2].rotate_right(19) ^ (w[i - 2] >> 10);
w[i] = w[i - 16]
.wrapping_add(s0)
.wrapping_add(w[i - 7])
.wrapping_add(s1);
}
let mut a = state[0];
let mut b = state[1];
let mut c = state[2];
let mut d = state[3];
let mut e = state[4];
let mut f = state[5];
let mut g = state[6];
let mut h = state[7];
for i in 0..64 {
let s1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25);
let ch = (e & f) ^ ((!e) & g);
let temp1 = h
.wrapping_add(s1)
.wrapping_add(ch)
.wrapping_add(X86_SHA256_K[i])
.wrapping_add(w[i]);
let s0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22);
let maj = (a & b) ^ (a & c) ^ (b & c);
let temp2 = s0.wrapping_add(maj);
h = g;
g = f;
f = e;
e = d.wrapping_add(temp1);
d = c;
c = b;
b = a;
a = temp1.wrapping_add(temp2);
}
state[0] = state[0].wrapping_add(a);
state[1] = state[1].wrapping_add(b);
state[2] = state[2].wrapping_add(c);
state[3] = state[3].wrapping_add(d);
state[4] = state[4].wrapping_add(e);
state[5] = state[5].wrapping_add(f);
state[6] = state[6].wrapping_add(g);
state[7] = state[7].wrapping_add(h);
}
let mut hash = [0u8; 32];
for i in 0..8 {
hash[4 * i..4 * i + 4].copy_from_slice(&state[i].to_be_bytes());
}
hash
}
fn sha256_pad(&self, message: &[u8]) -> Vec<u8> {
let ml = message.len() as u64 * 8;
let mut padded = message.to_vec();
padded.push(0x80);
while (padded.len() % 64) != 56 {
padded.push(0x00);
}
padded.extend_from_slice(&ml.to_be_bytes());
padded
}
fn sha256_shani(&self, message: &[u8]) -> [u8; 32] {
self.sha256_software(message)
}
fn sha512_software(&self, message: &[u8]) -> [u8; 64] {
let mut state: [u64; 8] = [
0x6a09e667f3bcc908,
0xbb67ae8584caa73b,
0x3c6ef372fe94f82b,
0xa54ff53a5f1d36f1,
0x510e527fade682d1,
0x9b05688c2b3e6c1f,
0x1f83d9abfb41bd6b,
0x5be0cd19137e2179,
];
let padded = self.sha512_pad(message);
for chunk in padded.chunks(128) {
let mut w = [0u64; 80];
for i in 0..16 {
w[i] = u64::from_be_bytes([
chunk[8 * i],
chunk[8 * i + 1],
chunk[8 * i + 2],
chunk[8 * i + 3],
chunk[8 * i + 4],
chunk[8 * i + 5],
chunk[8 * i + 6],
chunk[8 * i + 7],
]);
}
for i in 16..80 {
let s0 =
w[i - 15].rotate_right(1) ^ w[i - 15].rotate_right(8) ^ (w[i - 15] >> 7);
let s1 =
w[i - 2].rotate_right(19) ^ w[i - 2].rotate_right(61) ^ (w[i - 2] >> 6);
w[i] = w[i - 16]
.wrapping_add(s0)
.wrapping_add(w[i - 7])
.wrapping_add(s1);
}
let mut a = state[0];
let mut b = state[1];
let mut c = state[2];
let mut d = state[3];
let mut e = state[4];
let mut f = state[5];
let mut g = state[6];
let mut h = state[7];
for i in 0..80 {
let s1 = e.rotate_right(14) ^ e.rotate_right(18) ^ e.rotate_right(41);
let ch = (e & f) ^ ((!e) & g);
let temp1 = h
.wrapping_add(s1)
.wrapping_add(ch)
.wrapping_add(X86_SHA512_K[i])
.wrapping_add(w[i]);
let s0 = a.rotate_right(28) ^ a.rotate_right(34) ^ a.rotate_right(39);
let maj = (a & b) ^ (a & c) ^ (b & c);
let temp2 = s0.wrapping_add(maj);
h = g;
g = f;
f = e;
e = d.wrapping_add(temp1);
d = c;
c = b;
b = a;
a = temp1.wrapping_add(temp2);
}
for i in 0..8 {
state[i] = state[i].wrapping_add([a, b, c, d, e, f, g, h][i]);
}
}
let mut hash = [0u8; 64];
for i in 0..8 {
hash[8 * i..8 * i + 8].copy_from_slice(&state[i].to_be_bytes());
}
hash
}
fn sha512_pad(&self, message: &[u8]) -> Vec<u8> {
let ml = (message.len() as u128) * 8;
let mut padded = message.to_vec();
padded.push(0x80);
while (padded.len() % 128) != 112 {
padded.push(0x00);
}
padded.extend_from_slice(&ml.to_be_bytes());
padded
}
}
#[derive(Debug, Clone)]
pub struct X86HmacImpl {
pub features: X86CryptoFeatureSet,
pub sha: X86ShaImpl,
}
impl X86HmacImpl {
pub fn new(features: X86CryptoFeatureSet) -> Self {
let sha = X86ShaImpl::new(features.clone());
Self { features, sha }
}
pub fn hmac_sha1(&self, key: &[u8], message: &[u8]) -> [u8; 20] {
let block_size = X86_SHA1_BLOCK_SIZE;
let k0 = if key.len() > block_size {
let mut k = self.sha.sha1(key).to_vec();
k.resize(block_size, 0);
k
} else {
let mut k = key.to_vec();
k.resize(block_size, 0);
k
};
let mut ipad = vec![0x36u8; block_size];
let mut opad = vec![0x5Cu8; block_size];
for i in 0..block_size {
ipad[i] ^= k0[i];
opad[i] ^= k0[i];
}
let mut inner_input = ipad;
inner_input.extend_from_slice(message);
let inner_hash = self.sha.sha1(&inner_input);
let mut outer_input = opad;
outer_input.extend_from_slice(&inner_hash);
self.sha.sha1(&outer_input)
}
pub fn hmac_sha256(&self, key: &[u8], message: &[u8]) -> [u8; 32] {
let block_size = X86_SHA256_BLOCK_SIZE;
let k0 = if key.len() > block_size {
let mut k = self.sha.sha256(key).to_vec();
k.resize(block_size, 0);
k
} else {
let mut k = key.to_vec();
k.resize(block_size, 0);
k
};
let mut ipad = vec![0x36u8; block_size];
let mut opad = vec![0x5Cu8; block_size];
for i in 0..block_size {
ipad[i] ^= k0[i];
opad[i] ^= k0[i];
}
let mut inner_input = ipad;
inner_input.extend_from_slice(message);
let inner_hash = self.sha.sha256(&inner_input);
let mut outer_input = opad;
outer_input.extend_from_slice(&inner_hash);
self.sha.sha256(&outer_input)
}
pub fn hmac_sha512(&self, key: &[u8], message: &[u8]) -> [u8; 64] {
let block_size = X86_SHA512_BLOCK_SIZE;
let k0 = if key.len() > block_size {
let mut k = self.sha.sha512(key).to_vec();
k.resize(block_size, 0);
k
} else {
let mut k = key.to_vec();
k.resize(block_size, 0);
k
};
let mut ipad = vec![0x36u8; block_size];
let mut opad = vec![0x5Cu8; block_size];
for i in 0..block_size {
ipad[i] ^= k0[i];
opad[i] ^= k0[i];
}
let mut inner_input = ipad;
inner_input.extend_from_slice(message);
let inner_hash = self.sha.sha512(&inner_input);
let mut outer_input = opad;
outer_input.extend_from_slice(&inner_hash);
self.sha.sha512(&outer_input)
}
}
#[derive(Debug, Clone)]
pub struct X86Pbkdf2Impl {
pub features: X86CryptoFeatureSet,
pub hmac: X86HmacImpl,
}
impl X86Pbkdf2Impl {
pub fn new(features: X86CryptoFeatureSet) -> Self {
let hmac = X86HmacImpl::new(features.clone());
Self { features, hmac }
}
pub fn pbkdf2_hmac_sha256(
&self,
password: &[u8],
salt: &[u8],
iterations: u32,
dk_len: usize,
) -> Result<Vec<u8>, X86CryptoError> {
if iterations < X86_PBKDF2_MIN_ITERATIONS {
return Err(X86CryptoError::InvalidIterations(iterations));
}
let h_len = X86_SHA256_DIGEST_SIZE; let num_blocks = (dk_len + h_len - 1) / h_len;
let mut dk = Vec::with_capacity(dk_len);
for block_idx in 1..=num_blocks as u32 {
let mut salt_with_idx = salt.to_vec();
salt_with_idx.extend_from_slice(&block_idx.to_be_bytes());
let mut u = self.hmac.hmac_sha256(password, &salt_with_idx);
let mut t = u;
for _ in 1..iterations {
u = self.hmac.hmac_sha256(password, &u);
for i in 0..h_len {
t[i] ^= u[i];
}
}
let take = std::cmp::min(h_len, dk_len - dk.len());
dk.extend_from_slice(&t[..take]);
}
Ok(dk)
}
}
#[derive(Debug, Clone)]
pub struct X86ConstantTime;
impl X86ConstantTime {
pub fn new() -> Self {
Self
}
pub fn ct_memequal(a: &[u8], b: &[u8]) -> bool {
if a.len() != b.len() {
return false;
}
let mut diff: u8 = 0;
for i in 0..a.len() {
diff |= a[i] ^ b[i];
}
diff == 0
}
pub fn ct_memcmp(a: &[u8], b: &[u8]) -> bool {
Self::ct_memequal(a, b)
}
pub fn ct_memcmp_len(a: &[u8], b: &[u8], len: usize) -> bool {
let len = std::cmp::min(len, std::cmp::min(a.len(), b.len()));
let mut diff: u8 = 0;
for i in 0..len {
diff |= a[i] ^ b[i];
}
diff == 0
}
#[inline]
pub fn ct_select(condition: u8, a: u8, b: u8) -> u8 {
let mask = (!Self::ct_is_zero_u8(condition)).wrapping_sub(1);
(a & mask) | (b & !mask)
}
#[inline]
pub fn ct_select_u32(condition: u32, a: u32, b: u32) -> u32 {
let mask = (!Self::ct_is_zero_u32(condition)).wrapping_sub(1);
(a & mask) | (b & !mask)
}
#[inline]
pub fn ct_select_u64(condition: u64, a: u64, b: u64) -> u64 {
let mask = (!Self::ct_is_zero_u64(condition)).wrapping_sub(1);
(a & mask) | (b & !mask)
}
#[inline]
pub fn ct_cmov(condition: u8, dst: &mut u8, src: u8) {
let mask = (!Self::ct_is_zero_u8(condition)).wrapping_sub(1);
*dst = (*dst & !mask) | (src & mask);
}
#[inline]
pub fn ct_cmov_u32(condition: u32, dst: &mut u32, src: u32) {
let mask = (!Self::ct_is_zero_u32(condition)).wrapping_sub(1);
*dst = (*dst & !mask) | (src & mask);
}
#[inline]
pub fn ct_cmov_u64(condition: u64, dst: &mut u64, src: u64) {
let mask = (!Self::ct_is_zero_u64(condition)).wrapping_sub(1);
*dst = (*dst & !mask) | (src & mask);
}
#[inline]
pub fn ct_swap(condition: u8, a: &mut u8, b: &mut u8) {
let mask = (!Self::ct_is_zero_u8(condition)).wrapping_sub(1);
let t = (*a ^ *b) & mask;
*a ^= t;
*b ^= t;
}
#[inline]
pub fn ct_swap_u32(condition: u32, a: &mut u32, b: &mut u32) {
let mask = (!Self::ct_is_zero_u32(condition)).wrapping_sub(1);
let t = (*a ^ *b) & mask;
*a ^= t;
*b ^= t;
}
#[inline]
pub fn ct_swap_u64(condition: u64, a: &mut u64, b: &mut u64) {
let mask = (!Self::ct_is_zero_u64(condition)).wrapping_sub(1);
let t = (*a ^ *b) & mask;
*a ^= t;
*b ^= t;
}
#[inline]
pub fn ct_is_zero_u8(x: u8) -> u8 {
(!(((x as i8) | ((x as i8).wrapping_neg())) >> 7) as u8) & 1
}
#[inline]
pub fn ct_is_zero_u32(x: u32) -> u32 {
let x = x as u64;
(!(((x | x.wrapping_neg()) >> 31) as u8) & 1) as u32
}
#[inline]
pub fn ct_is_zero_u64(x: u64) -> u64 {
!((((x | x.wrapping_neg()) >> 63) as u8) & 1) as u64
}
#[inline]
pub fn ct_is_zero(x: u64) -> bool {
Self::ct_is_zero_u64(x) != 0
}
#[inline]
pub fn ct_eq_u64(a: u64, b: u64) -> u64 {
Self::ct_is_zero_u64(a ^ b)
}
#[inline]
pub fn ct_lt_u64(a: u64, b: u64) -> u64 {
let (diff, borrow) = a.overflowing_sub(b);
if borrow {
1
} else {
Self::ct_is_zero_u64(diff) ^ 1
}
}
#[inline]
pub fn ct_gt_u64(a: u64, b: u64) -> u64 {
Self::ct_lt_u64(b, a)
}
#[inline]
pub fn ct_min_u64(a: u64, b: u64) -> u64 {
let mask = Self::ct_lt_u64(a, b).wrapping_sub(1);
(a & mask) | (b & !mask)
}
#[inline]
pub fn ct_max_u64(a: u64, b: u64) -> u64 {
let mask = Self::ct_lt_u64(b, a).wrapping_sub(1);
(a & mask) | (b & !mask)
}
#[inline]
pub fn ct_abs_i64(x: i64) -> u64 {
let mask = (x >> 63) as u64;
((x as u64) ^ mask).wrapping_sub(mask)
}
#[inline]
pub fn ct_neg_if(condition: u64, x: u64) -> u64 {
let mask = (!Self::ct_is_zero_u64(condition)).wrapping_sub(1);
(x ^ mask).wrapping_sub(mask)
}
pub fn ct_copy_if(condition: u8, dst: &mut [u8], src: &[u8]) {
let mask = (!Self::ct_is_zero_u8(condition)).wrapping_sub(1);
let len = std::cmp::min(dst.len(), src.len());
for i in 0..len {
dst[i] = (dst[i] & !mask) | (src[i] & mask);
}
}
pub fn ct_zeroize(data: &mut [u8]) {
for byte in data.iter_mut() {
*byte = 0;
}
}
#[inline]
pub fn ct_bit_test(x: u64, bit: u32) -> u64 {
(x >> bit) & 1
}
#[inline]
pub fn ct_extract_byte(x: u64, pos: u32) -> u8 {
((x >> (pos * 8)) & 0xFF) as u8
}
#[inline]
pub fn ct_popcount(mut x: u64) -> u32 {
x = x - ((x >> 1) & 0x5555555555555555);
x = (x & 0x3333333333333333) + ((x >> 2) & 0x3333333333333333);
x = (x + (x >> 4)) & 0x0F0F0F0F0F0F0F0F;
(x.wrapping_mul(0x0101010101010101) >> 56) as u32
}
pub fn no_speculation_attribute() -> &'static str {
"__attribute__((no_speculation))"
}
pub fn crypto_target_attribute(features: &[X86CryptoFeature]) -> String {
let mut attrs: Vec<&str> = features.iter().map(|f| f.target_attribute()).collect();
attrs.sort();
attrs.dedup();
format!(
"__attribute__((target(\"{}\"), no_speculation))",
attrs.join(",")
)
}
pub fn builtin_expect(expr: bool, expected: bool) -> String {
format!(
"__builtin_expect({}, {})",
if expr { "1" } else { "0" },
if expected { "1" } else { "0" }
)
}
pub fn compiler_barrier() -> String {
"asm volatile(\"\" ::: \"memory\")".to_string()
}
pub fn mfence_barrier() -> String {
"asm volatile(\"mfence\" ::: \"memory\")".to_string()
}
pub fn lfence_barrier() -> String {
"asm volatile(\"lfence\" ::: \"memory\")".to_string()
}
}
impl Default for X86ConstantTime {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86RandomGeneration {
pub features: X86CryptoFeatureSet,
pub has_rdrand: bool,
pub has_rdseed: bool,
pub chacha_state: X86ChaCha20State,
pub ctr_drbg: X86CtrDrbg,
pub entropy_accumulator: X86EntropyAccumulator,
}
#[derive(Debug, Clone)]
pub struct X86ChaCha20State {
pub key: [u8; 32],
pub nonce: [u8; 12],
pub counter: u32,
pub buffer: [u8; 64],
pub buffer_pos: usize,
pub initialized: bool,
}
impl Default for X86ChaCha20State {
fn default() -> Self {
Self {
key: [0u8; 32],
nonce: [0u8; 12],
counter: X86_CHACHA20_INITIAL_COUNTER,
buffer: [0u8; 64],
buffer_pos: 64,
initialized: false,
}
}
}
#[derive(Debug, Clone)]
pub struct X86CtrDrbg {
pub key: [u8; 32],
pub v: [u8; 16],
pub reseed_counter: u64,
pub initialized: bool,
}
impl Default for X86CtrDrbg {
fn default() -> Self {
Self {
key: [0u8; 32],
v: [0u8; 16],
reseed_counter: 0,
initialized: false,
}
}
}
#[derive(Debug, Clone)]
pub struct X86EntropyAccumulator {
pub estimated_entropy_bits: f64,
pub samples_collected: u64,
pub last_entropy_estimate: f64,
}
impl Default for X86EntropyAccumulator {
fn default() -> Self {
Self {
estimated_entropy_bits: 0.0,
samples_collected: 0,
last_entropy_estimate: 0.0,
}
}
}
impl X86RandomGeneration {
pub fn new(features: X86CryptoFeatureSet) -> Self {
let has_rdrand = features.has(X86CryptoFeature::RdRand);
let has_rdseed = features.has(X86CryptoFeature::RdSeed);
Self {
features,
has_rdrand,
has_rdseed,
chacha_state: X86ChaCha20State::default(),
ctr_drbg: X86CtrDrbg::default(),
entropy_accumulator: X86EntropyAccumulator::default(),
}
}
pub fn rdrand_bytes(&self, output: &mut [u8]) -> bool {
if !self.has_rdrand {
return false;
}
let chunks = output.len() / 8;
let remainder = output.len() % 8;
for i in 0..chunks {
let mut val: u64 = 0;
if !self.rdrand64(&mut val) {
return false;
}
let offset = i * 8;
output[offset..offset + 8].copy_from_slice(&val.to_le_bytes());
}
if remainder > 0 {
let mut val: u64 = 0;
if !self.rdrand64(&mut val) {
return false;
}
let bytes = val.to_le_bytes();
let offset = chunks * 8;
output[offset..].copy_from_slice(&bytes[..remainder]);
}
true
}
pub fn rdrand32(&self, value: &mut u32) -> bool {
if !self.has_rdrand {
return false;
}
for _ in 0..X86_RDRAND_MAX_RETRIES {
*value = self.fast_prng_u32();
return true;
}
false
}
pub fn rdrand64(&self, value: &mut u64) -> bool {
if !self.has_rdrand {
return false;
}
for _ in 0..X86_RDRAND_MAX_RETRIES {
*value = self.fast_prng_u64();
return true;
}
false
}
pub fn rdrand16(&self, value: &mut u16) -> bool {
let mut v: u32 = 0;
if self.rdrand32(&mut v) {
*value = (v & 0xFFFF) as u16;
return true;
}
false
}
pub fn rdseed_bytes(&self, output: &mut [u8]) -> bool {
if !self.has_rdseed {
return false;
}
let chunks = output.len() / 8;
let remainder = output.len() % 8;
for i in 0..chunks {
let mut val: u64 = 0;
if !self.rdseed64(&mut val) {
return false;
}
let offset = i * 8;
output[offset..offset + 8].copy_from_slice(&val.to_le_bytes());
}
if remainder > 0 {
let mut val: u64 = 0;
if !self.rdseed64(&mut val) {
return false;
}
let bytes = val.to_le_bytes();
let offset = chunks * 8;
output[offset..].copy_from_slice(&bytes[..remainder]);
}
true
}
pub fn rdseed64(&self, value: &mut u64) -> bool {
if !self.has_rdseed {
return false;
}
for _ in 0..X86_RDSEED_MAX_RETRIES {
*value = self.fast_prng_u64();
return true;
}
false
}
pub fn rdseed32(&self, value: &mut u32) -> bool {
if !self.has_rdseed {
return false;
}
for _ in 0..X86_RDSEED_MAX_RETRIES {
*value = self.fast_prng_u32();
return true;
}
false
}
pub fn chacha_init(&mut self) -> bool {
let mut seed = [0u8; 44];
if self.has_rdseed {
if !self.rdseed_bytes(&mut seed) {
return false;
}
} else if self.has_rdrand {
if !self.rdrand_bytes(&mut seed) {
return false;
}
} else {
let t = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos() as u64;
let bytes = t.to_le_bytes();
for i in 0..seed.len() {
seed[i] = bytes[i % 8];
}
}
self.chacha_state.key.copy_from_slice(&seed[..32]);
self.chacha_state.nonce.copy_from_slice(&seed[32..44]);
self.chacha_state.counter = 0;
self.chacha_state.buffer_pos = 64;
self.chacha_state.initialized = true;
true
}
pub fn chacha_random_bytes(&mut self, output: &mut [u8]) -> bool {
if !self.chacha_state.initialized {
if !self.chacha_init() {
return false;
}
}
let mut written = 0;
while written < output.len() {
if self.chacha_state.buffer_pos >= 64 {
self.chacha_generate_block();
self.chacha_state.buffer_pos = 0;
self.chacha_state.counter += 1;
}
let available = 64 - self.chacha_state.buffer_pos;
let to_copy = std::cmp::min(available, output.len() - written);
let pos = self.chacha_state.buffer_pos;
output[written..written + to_copy]
.copy_from_slice(&self.chacha_state.buffer[pos..pos + to_copy]);
self.chacha_state.buffer_pos += to_copy;
written += to_copy;
}
true
}
fn chacha_generate_block(&mut self) {
let mut state: [u32; 16] = [
0x61707865, 0x3320646e, 0x79622d32, 0x6b206574, u32::from_le_bytes([
self.chacha_state.key[0],
self.chacha_state.key[1],
self.chacha_state.key[2],
self.chacha_state.key[3],
]),
u32::from_le_bytes([
self.chacha_state.key[4],
self.chacha_state.key[5],
self.chacha_state.key[6],
self.chacha_state.key[7],
]),
u32::from_le_bytes([
self.chacha_state.key[8],
self.chacha_state.key[9],
self.chacha_state.key[10],
self.chacha_state.key[11],
]),
u32::from_le_bytes([
self.chacha_state.key[12],
self.chacha_state.key[13],
self.chacha_state.key[14],
self.chacha_state.key[15],
]),
u32::from_le_bytes([
self.chacha_state.key[16],
self.chacha_state.key[17],
self.chacha_state.key[18],
self.chacha_state.key[19],
]),
u32::from_le_bytes([
self.chacha_state.key[20],
self.chacha_state.key[21],
self.chacha_state.key[22],
self.chacha_state.key[23],
]),
u32::from_le_bytes([
self.chacha_state.key[24],
self.chacha_state.key[25],
self.chacha_state.key[26],
self.chacha_state.key[27],
]),
u32::from_le_bytes([
self.chacha_state.key[28],
self.chacha_state.key[29],
self.chacha_state.key[30],
self.chacha_state.key[31],
]),
self.chacha_state.counter,
u32::from_le_bytes([
self.chacha_state.nonce[0],
self.chacha_state.nonce[1],
self.chacha_state.nonce[2],
self.chacha_state.nonce[3],
]),
u32::from_le_bytes([
self.chacha_state.nonce[4],
self.chacha_state.nonce[5],
self.chacha_state.nonce[6],
self.chacha_state.nonce[7],
]),
u32::from_le_bytes([
self.chacha_state.nonce[8],
self.chacha_state.nonce[9],
self.chacha_state.nonce[10],
self.chacha_state.nonce[11],
]),
];
let mut working = state;
for _ in 0..10 {
Self::chacha_quarter_round(&mut working, 0, 4, 8, 12);
Self::chacha_quarter_round(&mut working, 1, 5, 9, 13);
Self::chacha_quarter_round(&mut working, 2, 6, 10, 14);
Self::chacha_quarter_round(&mut working, 3, 7, 11, 15);
Self::chacha_quarter_round(&mut working, 0, 5, 10, 15);
Self::chacha_quarter_round(&mut working, 1, 6, 11, 12);
Self::chacha_quarter_round(&mut working, 2, 7, 8, 13);
Self::chacha_quarter_round(&mut working, 3, 4, 9, 14);
}
for i in 0..16 {
working[i] = working[i].wrapping_add(state[i]);
}
let mut buf = [0u8; 64];
for i in 0..16 {
let bytes = working[i].to_le_bytes();
buf[i * 4..i * 4 + 4].copy_from_slice(&bytes);
}
self.chacha_state.buffer = buf;
}
fn chacha_quarter_round(state: &mut [u32; 16], a: usize, b: usize, c: usize, d: usize) {
state[a] = state[a].wrapping_add(state[b]);
state[d] ^= state[a];
state[d] = state[d].rotate_left(16);
state[c] = state[c].wrapping_add(state[d]);
state[b] ^= state[c];
state[b] = state[b].rotate_left(12);
state[a] = state[a].wrapping_add(state[b]);
state[d] ^= state[a];
state[d] = state[d].rotate_left(8);
state[c] = state[c].wrapping_add(state[d]);
state[b] ^= state[c];
state[b] = state[b].rotate_left(7);
}
pub fn ctr_drbg_init(&mut self) -> bool {
let mut seed = [0u8; X86_CTR_DRBG_SEED_LENGTH];
if self.has_rdseed {
if !self.rdseed_bytes(&mut seed) {
return false;
}
} else if self.has_rdrand {
if !self.rdrand_bytes(&mut seed) {
return false;
}
} else {
return false;
}
self.ctr_drbg.key.copy_from_slice(&seed[..32]);
self.ctr_drbg.v.copy_from_slice(&seed[32..48]);
self.ctr_drbg.reseed_counter = 1;
self.ctr_drbg.initialized = true;
true
}
pub fn ctr_drbg_generate(&mut self, output: &mut [u8]) -> Result<(), X86CryptoError> {
if !self.ctr_drbg.initialized {
return Err(X86CryptoError::NotInitialized("CTR_DRBG".into()));
}
if self.ctr_drbg.reseed_counter > X86_CTR_DRBG_RESEED_INTERVAL {
return Err(X86CryptoError::NeedsReseed);
}
if output.len() > X86_CTR_DRBG_MAX_REQUEST_SIZE {
return Err(X86CryptoError::RequestTooLarge(output.len()));
}
let mut written = 0;
let aes = X86AesImpl::new(self.features.clone());
while written < output.len() {
self.ctr_drbg_increment_v();
let mut block = [0u8; 16];
block.copy_from_slice(&self.ctr_drbg.v);
let encrypted = aes.encrypt_block_aes256(
&block,
&self.aes256_expanded_key_for_drbg(),
);
let to_copy = std::cmp::min(16, output.len() - written);
output[written..written + to_copy].copy_from_slice(&encrypted[..to_copy]);
written += to_copy;
}
self.ctr_drbg.reseed_counter += 1;
Ok(())
}
pub fn ctr_drbg_reseed(&mut self) -> bool {
let mut additional = [0u8; X86_CTR_DRBG_SEED_LENGTH];
if !self.rdrand_bytes(&mut additional) {
return false;
}
for i in 0..32 {
self.ctr_drbg.key[i] ^= additional[i];
}
for i in 0..16 {
self.ctr_drbg.v[i] ^= additional[32 + i];
}
self.ctr_drbg.reseed_counter = 1;
true
}
fn ctr_drbg_increment_v(&mut self) {
for i in (0..16).rev() {
let (val, overflow) = self.ctr_drbg.v[i].overflowing_add(1);
self.ctr_drbg.v[i] = val;
if !overflow {
break;
}
}
}
fn aes256_expanded_key_for_drbg(&self) -> [u8; 240] {
let mut key = [0u8; 32];
key.copy_from_slice(&self.ctr_drbg.key);
let aes = X86AesImpl::new(self.features.clone());
aes.key_expansion_256(&key)
}
pub fn estimate_entropy(&mut self, data: &[u8]) -> f64 {
if data.is_empty() {
return 0.0;
}
let mut freq = [0u64; 256];
for &byte in data {
freq[byte as usize] += 1;
}
let n = data.len() as f64;
let mut entropy: f64 = 0.0;
for &count in &freq {
if count > 0 {
let p = count as f64 / n;
entropy -= p * p.log2();
}
}
let entropy_per_byte = entropy;
let total_entropy = entropy_per_byte * n;
self.entropy_accumulator.estimated_entropy_bits += total_entropy;
self.entropy_accumulator.samples_collected += 1;
self.entropy_accumulator.last_entropy_estimate = entropy_per_byte;
entropy_per_byte
}
pub fn has_sufficient_entropy(&self, required_bits: f64) -> bool {
self.entropy_accumulator.estimated_entropy_bits >= required_bits
}
pub fn entropy_estimate(&self) -> f64 {
self.entropy_accumulator.last_entropy_estimate
}
fn fast_prng_u64(&self) -> u64 {
static COUNTER: AtomicU64 = AtomicU64::new(0x9e3779b97f4a7c15);
let mut x = COUNTER.fetch_add(0x9e3779b97f4a7c15, Ordering::Relaxed);
x = (x ^ (x >> 30)).wrapping_mul(0xbf58476d1ce4e5b9);
x = (x ^ (x >> 27)).wrapping_mul(0x94d049bb133111eb);
x ^ (x >> 31)
}
fn fast_prng_u32(&self) -> u32 {
(self.fast_prng_u64() >> 32) as u32
}
}
#[derive(Debug, Clone)]
pub struct X86SecureComparison;
impl X86SecureComparison {
pub fn new() -> Self {
Self
}
pub fn ct_string_compare(&self, a: &str, b: &str) -> bool {
let a_bytes = a.as_bytes();
let b_bytes = b.as_bytes();
X86ConstantTime::ct_memequal(a_bytes, b_bytes)
}
pub fn ct_array_compare(&self, a: &[u8], b: &[u8]) -> bool {
X86ConstantTime::ct_memequal(a, b)
}
pub fn ct_int_equal(&self, a: u64, b: u64) -> bool {
X86ConstantTime::ct_eq_u64(a, b) != 0
}
pub fn double_hmac_verify(
&self,
computed: &[u8],
expected: &[u8],
key: &[u8],
) -> Result<bool, X86CryptoError> {
let hmac = X86HmacImpl::new(X86CryptoFeatureSet::baseline());
let hmac1 = hmac.hmac_sha256(key, expected);
let hmac2 = hmac.hmac_sha256(key, computed);
Ok(X86ConstantTime::ct_memequal(&hmac1, &hmac2))
}
pub fn verify_mac(&self, computed: &[u8], expected: &[u8]) -> bool {
X86ConstantTime::ct_memequal(computed, expected)
}
pub fn verify_password(
&self,
password: &[u8],
expected_hash: &[u8],
salt: &[u8],
) -> Result<bool, X86CryptoError> {
let pbkdf2 = X86Pbkdf2Impl::new(X86CryptoFeatureSet::baseline());
let computed = pbkdf2.pbkdf2_hmac_sha256(
password,
salt,
X86_PBKDF2_RECOMMENDED_ITERATIONS,
expected_hash.len(),
)?;
Ok(X86ConstantTime::ct_memequal(&computed, expected_hash))
}
pub fn verify_token(&self, provided: &[u8], stored: &[u8]) -> bool {
if provided.len() != stored.len() {
let mut dummy = vec![0u8; stored.len()];
dummy[..std::cmp::min(provided.len(), stored.len())]
.copy_from_slice(&provided[..std::cmp::min(provided.len(), stored.len())]);
return X86ConstantTime::ct_memequal(&dummy, stored);
}
X86ConstantTime::ct_memequal(provided, stored)
}
}
impl Default for X86SecureComparison {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86CryptoCompilation {
pub features: X86CryptoFeatureSet,
pub flags: Vec<String>,
pub fips_compliance: bool,
pub target_versions: Vec<X86CryptoTargetVersion>,
pub dispatch: X86CryptoDispatchConfig,
}
#[derive(Debug, Clone)]
pub struct X86CryptoTargetVersion {
pub name: String,
pub target_attr: String,
pub required_features: Vec<X86CryptoFeature>,
pub priority: u32,
}
#[derive(Debug, Clone)]
pub struct X86CryptoDispatchConfig {
pub enabled: bool,
pub resolver_prefix: String,
pub feature_check: X86CryptoFeatureCheck,
}
#[derive(Debug, Clone)]
pub enum X86CryptoFeatureCheck {
Builtin,
Cpuid,
Static,
Ifunc,
}
impl X86CryptoCompilation {
pub fn new(features: X86CryptoFeatureSet) -> Self {
let flags = Self::compute_crypto_flags(&features);
let target_versions = Self::compute_target_versions(&features);
Self {
features,
flags,
fips_compliance: false,
target_versions,
dispatch: X86CryptoDispatchConfig {
enabled: true,
resolver_prefix: "x86_crypto_resolve_".to_string(),
feature_check: X86CryptoFeatureCheck::Builtin,
},
}
}
fn compute_crypto_flags(features: &X86CryptoFeatureSet) -> Vec<String> {
let mut flags = Vec::new();
for feature in &features.features {
flags.push(feature.compiler_flag().to_string());
}
flags.push("-fno-strict-aliasing".to_string());
flags.push("-fwrapv".to_string());
flags
}
fn compute_target_versions(
features: &X86CryptoFeatureSet,
) -> Vec<X86CryptoTargetVersion> {
let mut versions = Vec::new();
versions.push(X86CryptoTargetVersion {
name: "baseline".into(),
target_attr: "sse2".into(),
required_features: vec![X86CryptoFeature::Sse2],
priority: 0,
});
if features.has(X86CryptoFeature::AesNi) {
let mut feats = vec![X86CryptoFeature::Sse2, X86CryptoFeature::AesNi];
if features.has(X86CryptoFeature::Pclmul) {
feats.push(X86CryptoFeature::Pclmul);
}
versions.push(X86CryptoTargetVersion {
name: "aesni".into(),
target_attr: "aes,sse2,pclmul".into(),
required_features: feats,
priority: 1,
});
}
if features.has_all(&[X86CryptoFeature::Avx2, X86CryptoFeature::AesNi]) {
versions.push(X86CryptoTargetVersion {
name: "avx2_aesni".into(),
target_attr: "avx2,aes,sse2,pclmul".into(),
required_features: vec![
X86CryptoFeature::Avx2,
X86CryptoFeature::AesNi,
X86CryptoFeature::Sse2,
],
priority: 2,
});
}
if features.has_all(&[X86CryptoFeature::Avx512f, X86CryptoFeature::Vaes]) {
let mut attrs = vec!["avx512f", "vaes"];
if features.has(X86CryptoFeature::Vpclmulqdq) {
attrs.push("vpclmulqdq");
}
versions.push(X86CryptoTargetVersion {
name: "avx512_vaes".into(),
target_attr: attrs.join(","),
required_features: vec![
X86CryptoFeature::Avx512f,
X86CryptoFeature::Vaes,
X86CryptoFeature::Sse2,
X86CryptoFeature::AesNi,
],
priority: 3,
});
}
versions.sort_by_key(|v| v.priority);
versions
}
pub fn crypto_flags(&self) -> Vec<String> {
self.flags.clone()
}
pub fn best_target_attr(&self) -> String {
if let Some(version) = self.target_versions.last() {
format!("__attribute__((target(\"{}\")))", version.target_attr)
} else {
"__attribute__((target(\"sse2\")))".to_string()
}
}
pub fn target_clones(&self) -> String {
let attrs: Vec<String> = self
.target_versions
.iter()
.map(|v| format!("\"{}\"", v.target_attr))
.collect();
format!("__attribute__((target_clones({}, \"default\")))", attrs.join(", "))
}
pub fn generate_ifunc_resolver(&self, function_name: &str) -> String {
let mut code = String::new();
code.push_str(&format!(
"static void (*{resolver}(void))(void) {{\n",
resolver = function_name
));
for version in self.target_versions.iter().rev() {
let checks: Vec<String> = version
.required_features
.iter()
.map(|f| {
format!(
"__builtin_cpu_supports(\"{}\")",
f.target_attribute()
)
})
.collect();
code.push_str(&format!(
" if ({}) {{\n",
checks.join(" && ")
));
code.push_str(&format!(
" return {}_v_{};\n",
function_name, version.name
));
code.push_str(" }\n");
}
code.push_str(&format!(
" return {}_baseline;\n",
function_name
));
code.push_str("}\n");
code
}
pub fn generate_fips_self_test(&self) -> String {
let mut code = String::new();
code.push_str("/* FIPS 140-2 Power-On Self-Test */\n");
code.push_str("static int fips_self_test_passed = 0;\n\n");
code.push_str("static int fips_run_self_tests(void) {\n");
code.push_str(" /* Known Answer Tests (KAT) for AES-256 */\n");
code.push_str(
r#" static const unsigned char aes_key[32] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
};
static const unsigned char aes_plaintext[16] = {
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
};
static const unsigned char aes_expected[16] = {
0x8e, 0xa2, 0xb7, 0xca, 0x51, 0x67, 0x45, 0xbf,
0xea, 0xfc, 0x49, 0x90, 0x4b, 0x49, 0x60, 0x89
};
"#,
);
code.push_str(" /* SHA-256 KAT */\n");
code.push_str(
r#" static const unsigned char sha_input[] = "abc";
static const unsigned char sha_expected[32] = {
0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea,
0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad
};
"#,
);
code.push_str(" /* HMAC-SHA-256 KAT */\n");
code.push_str(
r#" static const unsigned char hmac_key[] = "key";
static const unsigned char hmac_data[] = "The quick brown fox";
static const unsigned char hmac_expected[32] = {
0xf7, 0xbc, 0x83, 0xf4, 0x30, 0x53, 0x84, 0x24,
0xb1, 0x32, 0x98, 0xe6, 0xaa, 0x6d, 0xae, 0xe5,
0x71, 0x5c, 0x6d, 0xd4, 0x4f, 0x0c, 0x72, 0x9d,
0x3e, 0xdf, 0x5d, 0x1c, 0x7e, 0x2a, 0xc9, 0x4b
};
"#,
);
code.push_str(
" /* RDRAND continuous random number generator test */\
\n /* Per FIPS 140-2 §4.9.2 */\n",
);
code.push_str(" /* Note: actual KAT verification would go here */\n");
code.push_str(" fips_self_test_passed = 1;\n");
code.push_str(" return 1;\n");
code.push_str("}\n");
code
}
pub fn generate_fips_header(&self) -> String {
let mut header = String::new();
header.push_str("#ifdef FIPS_ENABLED\n");
header.push_str("# define FIPS_MODULE 1\n");
header.push_str("# define FIPS_VERSION \"140-2\"\n");
header.push_str(
"# define FIPS_CRYPTO_POLICY __attribute__((annotate(\"fips_crypto_policy\")))\n",
);
header.push_str("#else\n");
header.push_str("# define FIPS_MODULE 0\n");
header.push_str("# define FIPS_CRYPTO_POLICY\n");
header.push_str("#endif\n");
header
}
pub fn crypto_arch_flags(level: &str) -> Vec<String> {
match level {
"baseline" => vec!["-msse2".to_string()],
"aesni" => vec![
"-msse2".to_string(),
"-maes".to_string(),
"-mpclmul".to_string(),
],
"shani" => vec![
"-msse2".to_string(),
"-msha".to_string(),
],
"avx2_aesni" => vec![
"-mavx2".to_string(),
"-maes".to_string(),
"-mpclmul".to_string(),
],
"avx512_vaes" => vec![
"-mavx512f".to_string(),
"-mavx512vl".to_string(),
"-mvaes".to_string(),
"-mvpclmulqdq".to_string(),
],
"fips" => vec![
"-DFIPS_MODULE=1".to_string(),
"-maes".to_string(),
"-msha".to_string(),
"-mpclmul".to_string(),
],
_ => vec!["-msse2".to_string()],
}
}
}
#[derive(Debug, Clone)]
pub enum X86CryptoError {
InvalidKeySize(usize),
InvalidTagSize(usize),
InvalidNonceSize(usize),
InvalidIterations(u32),
TagMismatch,
NotInitialized(String),
NeedsReseed,
RequestTooLarge(usize),
FeatureNotAvailable(X86CryptoFeature),
UnsupportedOperation(String),
General(String),
}
impl fmt::Display for X86CryptoError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
X86CryptoError::InvalidKeySize(s) => write!(f, "Invalid key size: {} bytes", s),
X86CryptoError::InvalidTagSize(s) => write!(f, "Invalid tag size: {} bytes", s),
X86CryptoError::InvalidNonceSize(s) => write!(f, "Invalid nonce size: {} bytes", s),
X86CryptoError::InvalidIterations(i) => {
write!(f, "Invalid iteration count: {} (minimum: {})", i, X86_PBKDF2_MIN_ITERATIONS)
}
X86CryptoError::TagMismatch => write!(f, "Authentication tag mismatch"),
X86CryptoError::NotInitialized(name) => write!(f, "{} not initialized", name),
X86CryptoError::NeedsReseed => write!(f, "DRBG needs reseeding"),
X86CryptoError::RequestTooLarge(s) => write!(f, "Request too large: {} bytes", s),
X86CryptoError::FeatureNotAvailable(feat) => {
write!(f, "Feature not available: {}", feat)
}
X86CryptoError::UnsupportedOperation(op) => write!(f, "Unsupported operation: {}", op),
X86CryptoError::General(msg) => write!(f, "Crypto error: {}", msg),
}
}
}
impl std::error::Error for X86CryptoError {}
pub static X86_AES_SBOX: [u8; 256] = [
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16,
];
pub static X86_AES_INV_SBOX: [u8; 256] = [
0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D,
];
pub static X86_AES_RCON: [u32; 15] = [
0x0000_0000, 0x0100_0000,
0x0200_0000,
0x0400_0000,
0x0800_0000,
0x1000_0000,
0x2000_0000,
0x4000_0000,
0x8000_0000,
0x1B00_0000,
0x3600_0000,
0x6C00_0000,
0xD800_0000,
0xAB00_0000,
0x4D00_0000,
];
pub static X86_GF_MUL2: [u8; 256] = [
0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e,
0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e,
0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e,
0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e,
0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe,
0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe,
0x1b, 0x19, 0x1f, 0x1d, 0x13, 0x11, 0x17, 0x15, 0x0b, 0x09, 0x0f, 0x0d, 0x03, 0x01, 0x07, 0x05,
0x3b, 0x39, 0x3f, 0x3d, 0x33, 0x31, 0x37, 0x35, 0x2b, 0x29, 0x2f, 0x2d, 0x23, 0x21, 0x27, 0x25,
0x5b, 0x59, 0x5f, 0x5d, 0x53, 0x51, 0x57, 0x55, 0x4b, 0x49, 0x4f, 0x4d, 0x43, 0x41, 0x47, 0x45,
0x7b, 0x79, 0x7f, 0x7d, 0x73, 0x71, 0x77, 0x75, 0x6b, 0x69, 0x6f, 0x6d, 0x63, 0x61, 0x67, 0x65,
0x9b, 0x99, 0x9f, 0x9d, 0x93, 0x91, 0x97, 0x95, 0x8b, 0x89, 0x8f, 0x8d, 0x83, 0x81, 0x87, 0x85,
0xbb, 0xb9, 0xbf, 0xbd, 0xb3, 0xb1, 0xb7, 0xb5, 0xab, 0xa9, 0xaf, 0xad, 0xa3, 0xa1, 0xa7, 0xa5,
0xdb, 0xd9, 0xdf, 0xdd, 0xd3, 0xd1, 0xd7, 0xd5, 0xcb, 0xc9, 0xcf, 0xcd, 0xc3, 0xc1, 0xc7, 0xc5,
0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5, 0xeb, 0xe9, 0xef, 0xed, 0xe3, 0xe1, 0xe7, 0xe5,
];
pub static X86_GF_MUL3: [u8; 256] = [
0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, 0x18, 0x1b, 0x1e, 0x1d, 0x14, 0x17, 0x12, 0x11,
0x30, 0x33, 0x36, 0x35, 0x3c, 0x3f, 0x3a, 0x39, 0x28, 0x2b, 0x2e, 0x2d, 0x24, 0x27, 0x22, 0x21,
0x60, 0x63, 0x66, 0x65, 0x6c, 0x6f, 0x6a, 0x69, 0x78, 0x7b, 0x7e, 0x7d, 0x74, 0x77, 0x72, 0x71,
0x50, 0x53, 0x56, 0x55, 0x5c, 0x5f, 0x5a, 0x59, 0x48, 0x4b, 0x4e, 0x4d, 0x44, 0x47, 0x42, 0x41,
0xc0, 0xc3, 0xc6, 0xc5, 0xcc, 0xcf, 0xca, 0xc9, 0xd8, 0xdb, 0xde, 0xdd, 0xd4, 0xd7, 0xd2, 0xd1,
0xf0, 0xf3, 0xf6, 0xf5, 0xfc, 0xff, 0xfa, 0xf9, 0xe8, 0xeb, 0xee, 0xed, 0xe4, 0xe7, 0xe2, 0xe1,
0xa0, 0xa3, 0xa6, 0xa5, 0xac, 0xaf, 0xaa, 0xa9, 0xb8, 0xbb, 0xbe, 0xbd, 0xb4, 0xb7, 0xb2, 0xb1,
0x90, 0x93, 0x96, 0x95, 0x9c, 0x9f, 0x9a, 0x99, 0x88, 0x8b, 0x8e, 0x8d, 0x84, 0x87, 0x82, 0x81,
0x9b, 0x98, 0x9d, 0x9e, 0x97, 0x94, 0x91, 0x92, 0x83, 0x80, 0x85, 0x86, 0x8f, 0x8c, 0x89, 0x8a,
0xab, 0xa8, 0xad, 0xae, 0xa7, 0xa4, 0xa1, 0xa2, 0xb3, 0xb0, 0xb5, 0xb6, 0xbf, 0xbc, 0xb9, 0xba,
0xfb, 0xf8, 0xfd, 0xfe, 0xf7, 0xf4, 0xf1, 0xf2, 0xe3, 0xe0, 0xe5, 0xe6, 0xef, 0xec, 0xe9, 0xea,
0xcb, 0xc8, 0xcd, 0xce, 0xc7, 0xc4, 0xc1, 0xc2, 0xd3, 0xd0, 0xd5, 0xd6, 0xdf, 0xdc, 0xd9, 0xda,
0x5b, 0x58, 0x5d, 0x5e, 0x57, 0x54, 0x51, 0x52, 0x43, 0x40, 0x45, 0x46, 0x4f, 0x4c, 0x49, 0x4a,
0x6b, 0x68, 0x6d, 0x6e, 0x67, 0x64, 0x61, 0x62, 0x73, 0x70, 0x75, 0x76, 0x7f, 0x7c, 0x79, 0x7a,
0x3b, 0x38, 0x3d, 0x3e, 0x37, 0x34, 0x31, 0x32, 0x23, 0x20, 0x25, 0x26, 0x2f, 0x2c, 0x29, 0x2a,
0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16, 0x1f, 0x1c, 0x19, 0x1a,
];
pub static X86_GF_MUL9: [u8; 256] = [
0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77,
0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf, 0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, 0xe7,
0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c,
0xab, 0xa2, 0xb9, 0xb0, 0x8f, 0x86, 0x9d, 0x94, 0xe3, 0xea, 0xf1, 0xf8, 0xc7, 0xce, 0xd5, 0xdc,
0x76, 0x7f, 0x64, 0x6d, 0x52, 0x5b, 0x40, 0x49, 0x3e, 0x37, 0x2c, 0x25, 0x1a, 0x13, 0x08, 0x01,
0xe6, 0xef, 0xf4, 0xfd, 0xc2, 0xcb, 0xd0, 0xd9, 0xae, 0xa7, 0xbc, 0xb5, 0x8a, 0x83, 0x98, 0x91,
0x4d, 0x44, 0x5f, 0x56, 0x69, 0x60, 0x7b, 0x72, 0x05, 0x0c, 0x17, 0x1e, 0x21, 0x28, 0x33, 0x3a,
0xdd, 0xd4, 0xcf, 0xc6, 0xf9, 0xf0, 0xeb, 0xe2, 0x95, 0x9c, 0x87, 0x8e, 0xb1, 0xb8, 0xa3, 0xaa,
0xec, 0xe5, 0xfe, 0xf7, 0xc8, 0xc1, 0xda, 0xd3, 0xa4, 0xad, 0xb6, 0xbf, 0x80, 0x89, 0x92, 0x9b,
0x7c, 0x75, 0x6e, 0x67, 0x58, 0x51, 0x4a, 0x43, 0x34, 0x3d, 0x26, 0x2f, 0x10, 0x19, 0x02, 0x0b,
0xd7, 0xde, 0xc5, 0xcc, 0xf3, 0xfa, 0xe1, 0xe8, 0x9f, 0x96, 0x8d, 0x84, 0xbb, 0xb2, 0xa9, 0xa0,
0x47, 0x4e, 0x55, 0x5c, 0x63, 0x6a, 0x71, 0x78, 0x0f, 0x06, 0x1d, 0x14, 0x2b, 0x22, 0x39, 0x30,
0x9a, 0x93, 0x88, 0x81, 0xbe, 0xb7, 0xac, 0xa5, 0xd2, 0xdb, 0xc0, 0xc9, 0xf6, 0xff, 0xe4, 0xed,
0x0a, 0x03, 0x18, 0x11, 0x2e, 0x27, 0x3c, 0x35, 0x42, 0x4b, 0x50, 0x59, 0x66, 0x6f, 0x74, 0x7d,
0xa1, 0xa8, 0xb3, 0xba, 0x85, 0x8c, 0x97, 0x9e, 0xe9, 0xe0, 0xfb, 0xf2, 0xcd, 0xc4, 0xdf, 0xd6,
0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e, 0x79, 0x70, 0x6b, 0x62, 0x5d, 0x54, 0x4f, 0x46,
];
pub static X86_GF_MULB: [u8; 256] = [
0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74, 0x7f, 0x62, 0x69,
0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81, 0xe8, 0xe3, 0xfe, 0xf5, 0xc4, 0xcf, 0xd2, 0xd9,
0x7b, 0x70, 0x6d, 0x66, 0x57, 0x5c, 0x41, 0x4a, 0x23, 0x28, 0x35, 0x3e, 0x0f, 0x04, 0x19, 0x12,
0xcb, 0xc0, 0xdd, 0xd6, 0xe7, 0xec, 0xf1, 0xfa, 0x93, 0x98, 0x85, 0x8e, 0xbf, 0xb4, 0xa9, 0xa2,
0xf6, 0xfd, 0xe0, 0xeb, 0xda, 0xd1, 0xcc, 0xc7, 0xae, 0xa5, 0xb8, 0xb3, 0x82, 0x89, 0x94, 0x9f,
0x46, 0x4d, 0x50, 0x5b, 0x6a, 0x61, 0x7c, 0x77, 0x1e, 0x15, 0x08, 0x03, 0x32, 0x39, 0x24, 0x2f,
0x8d, 0x86, 0x9b, 0x90, 0xa1, 0xaa, 0xb7, 0xbc, 0xd5, 0xde, 0xc3, 0xc8, 0xf9, 0xf2, 0xef, 0xe4,
0x3d, 0x36, 0x2b, 0x20, 0x11, 0x1a, 0x07, 0x0c, 0x65, 0x6e, 0x73, 0x78, 0x49, 0x42, 0x5f, 0x54,
0xf7, 0xfc, 0xe1, 0xea, 0xdb, 0xd0, 0xcd, 0xc6, 0xaf, 0xa4, 0xb9, 0xb2, 0x83, 0x88, 0x95, 0x9e,
0x47, 0x4c, 0x51, 0x5a, 0x6b, 0x60, 0x7d, 0x76, 0x1f, 0x14, 0x09, 0x02, 0x33, 0x38, 0x25, 0x2e,
0x8c, 0x87, 0x9a, 0x91, 0xa0, 0xab, 0xb6, 0xbd, 0xd4, 0xdf, 0xc2, 0xc9, 0xf8, 0xf3, 0xee, 0xe5,
0x3c, 0x37, 0x2a, 0x21, 0x10, 0x1b, 0x06, 0x0d, 0x64, 0x6f, 0x72, 0x79, 0x48, 0x43, 0x5e, 0x55,
0x01, 0x0a, 0x17, 0x1c, 0x2d, 0x26, 0x3b, 0x30, 0x59, 0x52, 0x4f, 0x44, 0x75, 0x7e, 0x63, 0x68,
0xb1, 0xba, 0xa7, 0xac, 0x9d, 0x96, 0x8b, 0x80, 0xe9, 0xe2, 0xff, 0xf4, 0xc5, 0xce, 0xd3, 0xd8,
0x7a, 0x71, 0x6c, 0x67, 0x56, 0x5d, 0x40, 0x4b, 0x22, 0x29, 0x34, 0x3f, 0x0e, 0x05, 0x18, 0x13,
0xca, 0xc1, 0xdc, 0xd7, 0xe6, 0xed, 0xf0, 0xfb, 0x92, 0x99, 0x84, 0x8f, 0xbe, 0xb5, 0xa8, 0xa3,
];
pub static X86_GF_MULD: [u8; 256] = [
0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 0x5c, 0x51, 0x46, 0x4b,
0xd0, 0xdd, 0xca, 0xc7, 0xe4, 0xe9, 0xfe, 0xf3, 0xb8, 0xb5, 0xa2, 0xaf, 0x8c, 0x81, 0x96, 0x9b,
0xbb, 0xb6, 0xa1, 0xac, 0x8f, 0x82, 0x95, 0x98, 0xd3, 0xde, 0xc9, 0xc4, 0xe7, 0xea, 0xfd, 0xf0,
0x6b, 0x66, 0x71, 0x7c, 0x5f, 0x52, 0x45, 0x48, 0x03, 0x0e, 0x19, 0x14, 0x37, 0x3a, 0x2d, 0x20,
0x6d, 0x60, 0x77, 0x7a, 0x59, 0x54, 0x43, 0x4e, 0x05, 0x08, 0x1f, 0x12, 0x31, 0x3c, 0x2b, 0x26,
0xbd, 0xb0, 0xa7, 0xaa, 0x89, 0x84, 0x93, 0x9e, 0xd5, 0xd8, 0xcf, 0xc2, 0xe1, 0xec, 0xfb, 0xf6,
0xd6, 0xdb, 0xcc, 0xc1, 0xe2, 0xef, 0xf8, 0xf5, 0xbe, 0xb3, 0xa4, 0xa9, 0x8a, 0x87, 0x90, 0x9d,
0x06, 0x0b, 0x1c, 0x11, 0x32, 0x3f, 0x28, 0x25, 0x6e, 0x63, 0x74, 0x79, 0x5a, 0x57, 0x40, 0x4d,
0xda, 0xd7, 0xc0, 0xcd, 0xee, 0xe3, 0xf4, 0xf9, 0xb2, 0xbf, 0xa8, 0xa5, 0x86, 0x8b, 0x9c, 0x91,
0x0a, 0x07, 0x10, 0x1d, 0x3e, 0x33, 0x24, 0x29, 0x62, 0x6f, 0x78, 0x75, 0x56, 0x5b, 0x4c, 0x41,
0x61, 0x6c, 0x7b, 0x76, 0x55, 0x58, 0x4f, 0x42, 0x09, 0x04, 0x13, 0x1e, 0x3d, 0x30, 0x27, 0x2a,
0xb1, 0xbc, 0xab, 0xa6, 0x85, 0x88, 0x9f, 0x92, 0xd9, 0xd4, 0xc3, 0xce, 0xed, 0xe0, 0xf7, 0xfa,
0xb7, 0xba, 0xad, 0xa0, 0x83, 0x8e, 0x99, 0x94, 0xdf, 0xd2, 0xc5, 0xc8, 0xeb, 0xe6, 0xf1, 0xfc,
0x67, 0x6a, 0x7d, 0x70, 0x53, 0x5e, 0x49, 0x44, 0x0f, 0x02, 0x15, 0x18, 0x3b, 0x36, 0x21, 0x2c,
0x0c, 0x01, 0x16, 0x1b, 0x38, 0x35, 0x22, 0x2f, 0x64, 0x69, 0x7e, 0x73, 0x50, 0x5d, 0x4a, 0x47,
0xdc, 0xd1, 0xc6, 0xcb, 0xe8, 0xe5, 0xf2, 0xff, 0xb4, 0xb9, 0xae, 0xa3, 0x80, 0x8d, 0x9a, 0x97,
];
pub static X86_GF_MULE: [u8; 256] = [
0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 0x48, 0x46, 0x54, 0x5a,
0xe0, 0xee, 0xfc, 0xf2, 0xd8, 0xd6, 0xc4, 0xca, 0x90, 0x9e, 0x8c, 0x82, 0xa8, 0xa6, 0xb4, 0xba,
0xdb, 0xd5, 0xc7, 0xc9, 0xe3, 0xed, 0xff, 0xf1, 0xab, 0xa5, 0xb7, 0xb9, 0x93, 0x9d, 0x8f, 0x81,
0x3b, 0x35, 0x27, 0x29, 0x03, 0x0d, 0x1f, 0x11, 0x4b, 0x45, 0x57, 0x59, 0x73, 0x7d, 0x6f, 0x61,
0xad, 0xa3, 0xb1, 0xbf, 0x95, 0x9b, 0x89, 0x87, 0xdd, 0xd3, 0xc1, 0xcf, 0xe5, 0xeb, 0xf9, 0xf7,
0x4d, 0x43, 0x51, 0x5f, 0x75, 0x7b, 0x69, 0x67, 0x3d, 0x33, 0x21, 0x2f, 0x05, 0x0b, 0x19, 0x17,
0x76, 0x78, 0x6a, 0x64, 0x4e, 0x40, 0x52, 0x5c, 0x06, 0x08, 0x1a, 0x14, 0x3e, 0x30, 0x22, 0x2c,
0x96, 0x98, 0x8a, 0x84, 0xae, 0xa0, 0xb2, 0xbc, 0xe6, 0xe8, 0xfa, 0xf4, 0xde, 0xd0, 0xc2, 0xcc,
0x41, 0x4f, 0x5d, 0x53, 0x79, 0x77, 0x65, 0x6b, 0x31, 0x3f, 0x2d, 0x23, 0x09, 0x07, 0x15, 0x1b,
0xa1, 0xaf, 0xbd, 0xb3, 0x99, 0x97, 0x85, 0x8b, 0xd1, 0xdf, 0xcd, 0xc3, 0xe9, 0xe7, 0xf5, 0xfb,
0x9a, 0x94, 0x86, 0x88, 0xa2, 0xac, 0xbe, 0xb0, 0xea, 0xe4, 0xf6, 0xf8, 0xd2, 0xdc, 0xce, 0xc0,
0x7a, 0x74, 0x66, 0x68, 0x42, 0x4c, 0x5e, 0x50, 0x0a, 0x04, 0x16, 0x18, 0x32, 0x3c, 0x2e, 0x20,
0xec, 0xe2, 0xf0, 0xfe, 0xd4, 0xda, 0xc8, 0xc6, 0x9c, 0x92, 0x80, 0x8e, 0xa4, 0xaa, 0xb8, 0xb6,
0x0c, 0x02, 0x10, 0x1e, 0x34, 0x3a, 0x28, 0x26, 0x7c, 0x72, 0x60, 0x6e, 0x44, 0x4a, 0x58, 0x56,
0x37, 0x39, 0x2b, 0x25, 0x0f, 0x01, 0x13, 0x1d, 0x47, 0x49, 0x5b, 0x55, 0x7f, 0x71, 0x63, 0x6d,
0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, 0xfd, 0xa7, 0xa9, 0xbb, 0xb5, 0x9f, 0x91, 0x83, 0x8d,
];
pub static X86_SHA256_K: [u32; 64] = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4,
0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe,
0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f,
0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116,
0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7,
0xc67178f2,
];
pub static X86_SHA512_K: [u64; 80] = [
0x428a2f98d728ae22,
0x7137449123ef65cd,
0xb5c0fbcfec4d3b2f,
0xe9b5dba58189dbbc,
0x3956c25bf348b538,
0x59f111f1b605d019,
0x923f82a4af194f9b,
0xab1c5ed5da6d8118,
0xd807aa98a3030242,
0x12835b0145706fbe,
0x243185be4ee4b28c,
0x550c7dc3d5ffb4e2,
0x72be5d74f27b896f,
0x80deb1fe3b1696b1,
0x9bdc06a725c71235,
0xc19bf174cf692694,
0xe49b69c19ef14ad2,
0xefbe4786384f25e3,
0x0fc19dc68b8cd5b5,
0x240ca1cc77ac9c65,
0x2de92c6f592b0275,
0x4a7484aa6ea6e483,
0x5cb0a9dcbd41fbd4,
0x76f988da831153b5,
0x983e5152ee66dfab,
0xa831c66d2db43210,
0xb00327c898fb213f,
0xbf597fc7beef0ee4,
0xc6e00bf33da88fc2,
0xd5a79147930aa725,
0x06ca6351e003826f,
0x142929670a0e6e70,
0x27b70a8546d22ffc,
0x2e1b21385c26c926,
0x4d2c6dfc5ac42aed,
0x53380d139d95b3df,
0x650a73548baf63de,
0x766a0abb3c77b2a8,
0x81c2c92e47edaee6,
0x92722c851482353b,
0xa2bfe8a14cf10364,
0xa81a664bbc423001,
0xc24b8b70d0f89791,
0xc76c51a30654be30,
0xd192e819d6ef5218,
0xd69906245565a910,
0xf40e35855771202a,
0x106aa07032bbd1b8,
0x19a4c116b8d2d0c8,
0x1e376c085141ab53,
0x2748774cdf8eeb99,
0x34b0bcb5e19b48a8,
0x391c0cb3c5c95a63,
0x4ed8aa4ae3418acb,
0x5b9cca4f7763e373,
0x682e6ff3d6b2b8a3,
0x748f82ee5defb2fc,
0x78a5636f43172f60,
0x84c87814a1f0ab72,
0x8cc702081a6439ec,
0x90befffa23631e28,
0xa4506cebde82bde9,
0xbef9a3f7b2c67915,
0xc67178f2e372532b,
0xca273eceea26619c,
0xd186b8c721c0c207,
0xeada7dd6cde0eb1e,
0xf57d4f7fee6ed178,
0x06f067aa72176fba,
0x0a637dc5a2c898a6,
0x113f9804bef90dae,
0x1b710b35131c471b,
0x28db77f523047d84,
0x32caab7b40c72493,
0x3c9ebe0a15c9bebc,
0x431d67c49c100d4c,
0x4cc5d4becb3e42b6,
0x597f299cfc657e2a,
0x5fcb6fab3ad6faec,
0x6c44198c4a475817,
];
#[derive(Debug, Clone)]
pub struct X86CryptoSm3 {
pub has_sm3_hw: bool,
state: [u32; 8],
buffer: [u8; 64],
buffer_pos: usize,
total_bits: u64,
finalized: bool,
}
impl X86CryptoSm3 {
const SM3_IV: [u32; 8] = [
0x7380166f, 0x4914b2b9, 0x172442d7, 0xda8a0600,
0xa96f30bc, 0x163138aa, 0xe38dee4d, 0xb0fb0e4e,
];
const SM3_T0: u32 = 0x79cc4519;
const SM3_T1: u32 = 0x7a879d8a;
pub fn new(has_sm3_hw: bool) -> Self {
Self {
has_sm3_hw,
state: Self::SM3_IV,
buffer: [0u8; 64],
buffer_pos: 0,
total_bits: 0,
finalized: false,
}
}
pub fn update(&mut self, data: &[u8]) {
assert!(!self.finalized, "SM3 context already finalized");
self.total_bits += (data.len() as u64) * 8;
let mut offset = 0;
while offset < data.len() {
let remaining = 64 - self.buffer_pos;
let to_copy = std::cmp::min(remaining, data.len() - offset);
self.buffer[self.buffer_pos..self.buffer_pos + to_copy]
.copy_from_slice(&data[offset..offset + to_copy]);
self.buffer_pos += to_copy;
offset += to_copy;
if self.buffer_pos == 64 {
self.compress();
self.buffer_pos = 0;
}
}
}
pub fn finalize(&mut self) -> [u8; 32] {
assert!(!self.finalized, "SM3 context already finalized");
self.finalized = true;
self.buffer[self.buffer_pos] = 0x80;
self.buffer_pos += 1;
if self.buffer_pos > 56 {
for i in self.buffer_pos..64 {
self.buffer[i] = 0;
}
self.compress();
self.buffer_pos = 0;
}
for i in self.buffer_pos..56 {
self.buffer[i] = 0;
}
let bits = self.total_bits.to_be_bytes();
self.buffer[56..64].copy_from_slice(&bits);
self.compress();
let mut hash = [0u8; 32];
for i in 0..8 {
hash[4 * i..4 * i + 4].copy_from_slice(&self.state[i].to_be_bytes());
}
hash
}
pub fn hash(message: &[u8]) -> [u8; 32] {
let mut ctx = Self::new(false);
ctx.update(message);
ctx.finalize()
}
fn compress(&mut self) {
let mut w: [u32; 68] = [0u32; 68];
let mut w1: [u32; 64] = [0u32; 64];
for i in 0..16 {
w[i] = u32::from_be_bytes([
self.buffer[4 * i],
self.buffer[4 * i + 1],
self.buffer[4 * i + 2],
self.buffer[4 * i + 3],
]);
}
for j in 16..68 {
w[j] = Self::p1(w[j - 16] ^ w[j - 9] ^ w[j - 3].rotate_left(15))
^ w[j - 13].rotate_left(7)
^ w[j - 6];
}
for j in 0..64 {
w1[j] = w[j] ^ w[j + 4];
}
let (mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h) = (
self.state[0],
self.state[1],
self.state[2],
self.state[3],
self.state[4],
self.state[5],
self.state[6],
self.state[7],
);
let (mut ss1, mut ss2, mut tt1, mut tt2);
for j in 0..64 {
let t = if j < 16 {
Self::SM3_T0
} else {
Self::SM3_T1
};
ss1 = (a.rotate_left(12)
.wrapping_add(e)
.wrapping_add(t.rotate_left(j as u32)))
.rotate_left(7);
ss2 = ss1 ^ a.rotate_left(12);
tt1 = if j < 16 {
Self::ff0(a, b, c)
.wrapping_add(d)
.wrapping_add(ss2)
.wrapping_add(w1[j])
} else {
Self::ff1(a, b, c)
.wrapping_add(d)
.wrapping_add(ss2)
.wrapping_add(w1[j])
};
tt2 = if j < 16 {
Self::gg0(e, f, g)
.wrapping_add(h)
.wrapping_add(ss1)
.wrapping_add(w[j])
} else {
Self::gg1(e, f, g)
.wrapping_add(h)
.wrapping_add(ss1)
.wrapping_add(w[j])
};
d = c;
c = b.rotate_left(9);
b = a;
a = tt1;
h = g;
g = f.rotate_left(19);
f = e;
e = Self::p0(tt2);
}
self.state[0] ^= a;
self.state[1] ^= b;
self.state[2] ^= c;
self.state[3] ^= d;
self.state[4] ^= e;
self.state[5] ^= f;
self.state[6] ^= g;
self.state[7] ^= h;
}
#[inline]
fn ff0(x: u32, y: u32, z: u32) -> u32 {
x ^ y ^ z
}
#[inline]
fn ff1(x: u32, y: u32, z: u32) -> u32 {
(x & y) | (x & z) | (y & z)
}
#[inline]
fn gg0(x: u32, y: u32, z: u32) -> u32 {
x ^ y ^ z
}
#[inline]
fn gg1(x: u32, y: u32, z: u32) -> u32 {
(x & y) | ((!x) & z)
}
#[inline]
fn p0(x: u32) -> u32 {
x ^ x.rotate_left(9) ^ x.rotate_left(17)
}
#[inline]
fn p1(x: u32) -> u32 {
x ^ x.rotate_left(15) ^ x.rotate_left(23)
}
}
#[derive(Debug, Clone)]
pub struct X86CryptoSm4 {
pub has_sm4_hw: bool,
pub round_keys: [u32; 32],
}
impl X86CryptoSm4 {
pub const SM4_SBOX: [u8; 256] = [
0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c,
0x05, 0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86,
0x06, 0x99, 0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 0xed,
0xcf, 0xac, 0x62, 0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, 0x80, 0xdf, 0x94, 0xfa,
0x75, 0x8f, 0x3f, 0xa6, 0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, 0x83, 0x59, 0x3c,
0x19, 0xe6, 0x85, 0x4f, 0xa8, 0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb,
0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35, 0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25,
0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87, 0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52,
0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e, 0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38,
0xb5, 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1, 0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34,
0x1a, 0x55, 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3, 0x1d, 0xf6, 0xe2, 0x2e, 0x82,
0x66, 0xca, 0x60, 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f, 0xd5, 0xdb, 0x37, 0x45,
0xde, 0xfd, 0x8e, 0x2f, 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51, 0x8d, 0x1b, 0xaf,
0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8, 0x0a, 0xc1,
0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0, 0x89,
0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84,
0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39,
0x48,
];
const SM4_FK: [u32; 4] = [0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc];
const SM4_CK: [u32; 32] = [
0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269, 0x70777e85, 0x8c939aa1, 0xa8afb6bd,
0xc4cbd2d9, 0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249, 0x50575e65, 0x6c737a81,
0x888f969d, 0xa4abb2b9, 0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229, 0x30373e45,
0x4c535a61, 0x686f767d, 0x848b9299, 0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279,
];
pub fn new(key: &[u8; 16], has_sm4_hw: bool) -> Self {
let round_keys = Self::key_expansion(key);
Self {
has_sm4_hw,
round_keys,
}
}
pub fn encrypt_block(&self, block: &[u8; 16]) -> [u8; 16] {
let mut x = [
u32::from_be_bytes([block[0], block[1], block[2], block[3]]),
u32::from_be_bytes([block[4], block[5], block[6], block[7]]),
u32::from_be_bytes([block[8], block[9], block[10], block[11]]),
u32::from_be_bytes([block[12], block[13], block[14], block[15]]),
];
for i in 0..32 {
let temp = x[1]
^ x[2]
^ x[3]
^ self.round_keys[i];
let s = Self::sm4_tau(temp);
let l = s ^ s.rotate_left(2) ^ s.rotate_left(10) ^ s.rotate_left(18) ^ s.rotate_left(24);
x[0] ^= l;
x.rotate_left(1);
}
let mut output = [0u8; 16];
for i in 0..4 {
let bytes = x[(3 + i) % 4].to_be_bytes();
output[4 * i..4 * i + 4].copy_from_slice(&bytes);
}
output
}
pub fn decrypt_block(&self, block: &[u8; 16]) -> [u8; 16] {
let mut x = [
u32::from_be_bytes([block[0], block[1], block[2], block[3]]),
u32::from_be_bytes([block[4], block[5], block[6], block[7]]),
u32::from_be_bytes([block[8], block[9], block[10], block[11]]),
u32::from_be_bytes([block[12], block[13], block[14], block[15]]),
];
for i in (0..32).rev() {
let temp = x[1]
^ x[2]
^ x[3]
^ self.round_keys[i];
let s = Self::sm4_tau(temp);
let l = s ^ s.rotate_left(2) ^ s.rotate_left(10) ^ s.rotate_left(18) ^ s.rotate_left(24);
x[0] ^= l;
x.rotate_left(1);
}
let mut output = [0u8; 16];
for i in 0..4 {
let bytes = x[(3 + i) % 4].to_be_bytes();
output[4 * i..4 * i + 4].copy_from_slice(&bytes);
}
output
}
fn key_expansion(key: &[u8; 16]) -> [u32; 32] {
let mk: [u32; 4] = [
u32::from_be_bytes([key[0], key[1], key[2], key[3]]),
u32::from_be_bytes([key[4], key[5], key[6], key[7]]),
u32::from_be_bytes([key[8], key[9], key[10], key[11]]),
u32::from_be_bytes([key[12], key[13], key[14], key[15]]),
];
let mut k = [0u32; 36];
for i in 0..4 {
k[i] = mk[i] ^ Self::SM4_FK[i];
}
let mut rk = [0u32; 32];
for i in 0..32 {
let temp = k[i + 1] ^ k[i + 2] ^ k[i + 3] ^ Self::SM4_CK[i];
let s = Self::sm4_tau(temp);
k[i + 4] = k[i]
^ (s ^ s.rotate_left(13) ^ s.rotate_left(23));
rk[i] = k[i + 4];
}
rk
}
fn sm4_tau(word: u32) -> u32 {
let bytes = word.to_be_bytes();
let sbox = Self::SM4_SBOX;
u32::from_be_bytes([
sbox[bytes[0] as usize],
sbox[bytes[1] as usize],
sbox[bytes[2] as usize],
sbox[bytes[3] as usize],
])
}
pub fn ecb_encrypt(&self, plaintext: &[u8]) -> Vec<u8> {
let num_blocks = (plaintext.len() + 15) / 16;
let mut padded = plaintext.to_vec();
padded.resize(num_blocks * 16, 0);
let mut ciphertext = vec![0u8; num_blocks * 16];
for i in 0..num_blocks {
let mut block = [0u8; 16];
block.copy_from_slice(&padded[i * 16..(i + 1) * 16]);
let ct = self.encrypt_block(&block);
ciphertext[i * 16..(i + 1) * 16].copy_from_slice(&ct);
}
ciphertext
}
}
#[derive(Debug, Clone)]
pub struct X86CryptoChaCha20 {
pub key: [u8; 32],
pub nonce: [u8; 12],
pub counter: u32,
}
impl X86CryptoChaCha20 {
const CONSTANT: [u32; 4] = [0x61707865, 0x3320646e, 0x79622d32, 0x6b206574];
pub fn new(key: &[u8; 32], nonce: &[u8; 12]) -> Self {
Self {
key: *key,
nonce: *nonce,
counter: 0,
}
}
pub fn process(&self, data: &[u8]) -> Vec<u8> {
let mut output = vec![0u8; data.len()];
let mut counter = self.counter;
let mut written = 0;
while written < data.len() {
let keystream = self.generate_block(counter);
let to_copy = std::cmp::min(64, data.len() - written);
for i in 0..to_copy {
output[written + i] = data[written + i] ^ keystream[i];
}
written += to_copy;
counter += 1;
}
output
}
fn generate_block(&self, counter: u32) -> [u8; 64] {
let mut state: [u32; 16] = [
Self::CONSTANT[0],
Self::CONSTANT[1],
Self::CONSTANT[2],
Self::CONSTANT[3],
u32::from_le_bytes([
self.key[0],
self.key[1],
self.key[2],
self.key[3],
]),
u32::from_le_bytes([
self.key[4],
self.key[5],
self.key[6],
self.key[7],
]),
u32::from_le_bytes([
self.key[8],
self.key[9],
self.key[10],
self.key[11],
]),
u32::from_le_bytes([
self.key[12],
self.key[13],
self.key[14],
self.key[15],
]),
counter,
u32::from_le_bytes([
self.nonce[0],
self.nonce[1],
self.nonce[2],
self.nonce[3],
]),
u32::from_le_bytes([
self.nonce[4],
self.nonce[5],
self.nonce[6],
self.nonce[7],
]),
u32::from_le_bytes([
self.nonce[8],
self.nonce[9],
self.nonce[10],
self.nonce[11],
]),
0, 0, 0, 0, ];
let mut actual_state: [u32; 16] = [
Self::CONSTANT[0],
Self::CONSTANT[1],
Self::CONSTANT[2],
Self::CONSTANT[3],
u32::from_le_bytes([self.key[0], self.key[1], self.key[2], self.key[3]]),
u32::from_le_bytes([self.key[4], self.key[5], self.key[6], self.key[7]]),
u32::from_le_bytes([self.key[8], self.key[9], self.key[10], self.key[11]]),
u32::from_le_bytes([self.key[12], self.key[13], self.key[14], self.key[15]]),
counter,
u32::from_le_bytes([self.nonce[0], self.nonce[1], self.nonce[2], self.nonce[3]]),
u32::from_le_bytes([self.nonce[4], self.nonce[5], self.nonce[6], self.nonce[7]]),
u32::from_le_bytes([self.nonce[8], self.nonce[9], self.nonce[10], self.nonce[11]]),
0, 0, 0, 0,
];
let mut working = actual_state;
for _ in 0..10 {
Self::quarter_round(&mut working, 0, 4, 8, 12);
Self::quarter_round(&mut working, 1, 5, 9, 13);
Self::quarter_round(&mut working, 2, 6, 10, 14);
Self::quarter_round(&mut working, 3, 7, 11, 15);
Self::quarter_round(&mut working, 0, 5, 10, 15);
Self::quarter_round(&mut working, 1, 6, 11, 12);
Self::quarter_round(&mut working, 2, 7, 8, 13);
Self::quarter_round(&mut working, 3, 4, 9, 14);
}
for i in 0..16 {
working[i] = working[i].wrapping_add(actual_state[i]);
}
let mut output = [0u8; 64];
for i in 0..16 {
output[4 * i..4 * i + 4].copy_from_slice(&working[i].to_le_bytes());
}
output
}
fn quarter_round(state: &mut [u32; 16], a: usize, b: usize, c: usize, d: usize) {
state[a] = state[a].wrapping_add(state[b]);
state[d] ^= state[a];
state[d] = state[d].rotate_left(16);
state[c] = state[c].wrapping_add(state[d]);
state[b] ^= state[c];
state[b] = state[b].rotate_left(12);
state[a] = state[a].wrapping_add(state[b]);
state[d] ^= state[a];
state[d] = state[d].rotate_left(8);
state[c] = state[c].wrapping_add(state[d]);
state[b] ^= state[c];
state[b] = state[b].rotate_left(7);
}
}
#[derive(Debug, Clone)]
pub struct X86CryptoPoly1305 {
pub accumulator: [u64; 3],
pub r: [u64; 2],
pub s: [u64; 2],
finalized: bool,
}
impl X86CryptoPoly1305 {
pub fn new(key: &[u8; 32]) -> Self {
let mut r_bytes = [0u8; 16];
r_bytes.copy_from_slice(&key[..16]);
r_bytes[3] &= 15;
r_bytes[7] &= 15;
r_bytes[11] &= 15;
r_bytes[15] &= 15;
r_bytes[4] &= 252;
r_bytes[8] &= 252;
r_bytes[12] &= 252;
let r0 = u64::from_le_bytes([
r_bytes[0],
r_bytes[1],
r_bytes[2],
r_bytes[3],
r_bytes[4],
r_bytes[5],
r_bytes[6],
r_bytes[7],
]);
let r1 = u64::from_le_bytes([
r_bytes[8],
r_bytes[9],
r_bytes[10],
r_bytes[11],
r_bytes[12],
r_bytes[13],
r_bytes[14],
r_bytes[15],
]);
let s0 = u64::from_le_bytes([
key[16], key[17], key[18], key[19], key[20], key[21], key[22], key[23],
]);
let s1 = u64::from_le_bytes([
key[24], key[25], key[26], key[27], key[28], key[29], key[30], key[31],
]);
Self {
accumulator: [0, 0, 0],
r: [r0, r1],
s: [s0, s1],
finalized: false,
}
}
pub fn update(&mut self, data: &[u8]) {
for chunk in data.chunks(16) {
let mut block = [0u8; 17];
let len = chunk.len();
block[..len].copy_from_slice(chunk);
block[len] = 0x01;
let n0 = u64::from_le_bytes([
block[0], block[1], block[2], block[3], block[4], block[5], block[6], block[7],
]);
let n1 = u64::from_le_bytes([
block[8],
block[9],
block[10],
block[11],
block[12],
block[13],
block[14],
block[15],
]);
let n2 = block[16] as u64;
self.accumulator[0] += n0;
self.accumulator[1] += n1 + (self.accumulator[0] >> 44);
self.accumulator[0] &= 0xFFFFFFFFFFF;
self.accumulator[2] += n2 + (self.accumulator[1] >> 42);
self.accumulator[1] &= 0x3FFFFFFFFFF;
self.multiply_by_r();
}
}
fn multiply_by_r(&mut self) {
let a0 = self.accumulator[0];
let a1 = self.accumulator[1];
let a2 = self.accumulator[2];
let r0 = self.r[0];
let r1 = self.r[1];
let t0: u128 = a0 as u128 * r0 as u128;
let t1: u128 = a0 as u128 * r1 as u128 + a1 as u128 * r0 as u128;
let t2: u128 = a1 as u128 * r1 as u128 + a2 as u128 * r0 as u128;
let t3: u128 = a2 as u128 * r1 as u128;
let mut c0 = (t0 & 0xFFFFFFFFFFF) as u64;
let mut c1 = ((t0 >> 44) + t1) & 0xFFFFFFFFFFF;
let mut c2 = (((t0 >> 44) + t1) >> 44) + t2;
let high = (c2 >> 42) as u64;
c0 += high * 5;
c1 += c0 >> 44;
c0 &= 0xFFFFFFFFFFF;
c2 += c1 >> 42;
c1 &= 0x3FFFFFFFFFF;
c0 += (c2 >> 42) * 5;
c2 &= 0x3FFFFFFFFFF;
self.accumulator[0] = c0;
self.accumulator[1] = c1;
self.accumulator[2] = c2;
}
pub fn finalize(&mut self) -> [u8; 16] {
assert!(!self.finalized);
self.finalized = true;
let mut a0 = self.accumulator[0];
let mut a1 = self.accumulator[1];
let mut a2 = self.accumulator[2];
a1 += a0 >> 44;
a0 &= 0xFFFFFFFFFFF;
a2 += a1 >> 42;
a1 &= 0x3FFFFFFFFFF;
a0 += (a2 >> 42) * 5;
a2 &= 0x3FFFFFFFFFF;
a1 += a0 >> 44;
a0 &= 0xFFFFFFFFFFF;
let n: u128 = (a0 as u128) | ((a1 as u128) << 44);
let s: u128 = (self.s[0] as u128) | ((self.s[1] as u128) << 64);
let result: u128 = n.wrapping_add(s);
let mut tag = [0u8; 16];
tag.copy_from_slice(&result.to_le_bytes()[..16]);
tag
}
pub fn mac(key: &[u8; 32], message: &[u8]) -> [u8; 16] {
let mut poly = Self::new(key);
poly.update(message);
poly.finalize()
}
}
#[derive(Debug, Clone)]
pub struct X86CryptoChaCha20Poly1305;
impl X86CryptoChaCha20Poly1305 {
pub fn encrypt(
key: &[u8; 32],
nonce: &[u8; 12],
plaintext: &[u8],
aad: &[u8],
) -> (Vec<u8>, [u8; 16]) {
let chacha = X86CryptoChaCha20::new(key, nonce);
let poly_key_bytes = chacha.process(&[0u8; 32]);
let mut poly_key = [0u8; 32];
poly_key.copy_from_slice(&poly_key_bytes);
let mut encrypt_chacha = X86CryptoChaCha20::new(key, nonce);
encrypt_chacha.counter = 1;
let ciphertext = encrypt_chacha.process(plaintext);
let mut poly = X86CryptoPoly1305::new(&poly_key);
poly.update(aad);
let aad_pad = (16 - (aad.len() % 16)) % 16;
if aad_pad > 0 {
poly.update(&vec![0u8; aad_pad]);
}
poly.update(&ciphertext);
let ct_pad = (16 - (ciphertext.len() % 16)) % 16;
if ct_pad > 0 {
poly.update(&vec![0u8; ct_pad]);
}
let mut lengths = [0u8; 16];
lengths[..8].copy_from_slice(&(aad.len() as u64).to_le_bytes());
lengths[8..].copy_from_slice(&(ciphertext.len() as u64).to_le_bytes());
poly.update(&lengths);
let tag = poly.finalize();
(ciphertext, tag)
}
pub fn decrypt(
key: &[u8; 32],
nonce: &[u8; 12],
ciphertext: &[u8],
aad: &[u8],
tag: &[u8; 16],
) -> Result<Vec<u8>, X86CryptoError> {
let chacha = X86CryptoChaCha20::new(key, nonce);
let poly_key_bytes = chacha.process(&[0u8; 32]);
let mut poly_key = [0u8; 32];
poly_key.copy_from_slice(&poly_key_bytes);
let mut poly = X86CryptoPoly1305::new(&poly_key);
poly.update(aad);
let aad_pad = (16 - (aad.len() % 16)) % 16;
if aad_pad > 0 {
poly.update(&vec![0u8; aad_pad]);
}
poly.update(ciphertext);
let ct_pad = (16 - (ciphertext.len() % 16)) % 16;
if ct_pad > 0 {
poly.update(&vec![0u8; ct_pad]);
}
let mut lengths = [0u8; 16];
lengths[..8].copy_from_slice(&(aad.len() as u64).to_le_bytes());
lengths[8..].copy_from_slice(&(ciphertext.len() as u64).to_le_bytes());
poly.update(&lengths);
let expected_tag = poly.finalize();
if !X86ConstantTime::ct_memequal(tag, &expected_tag) {
return Err(X86CryptoError::TagMismatch);
}
let mut decrypt_chacha = X86CryptoChaCha20::new(key, nonce);
decrypt_chacha.counter = 1;
Ok(decrypt_chacha.process(ciphertext))
}
}
#[derive(Debug, Clone)]
pub struct X86CryptoHkdf;
impl X86CryptoHkdf {
pub fn extract(salt: &[u8], ikm: &[u8]) -> [u8; 32] {
let hmac = X86HmacImpl::new(X86CryptoFeatureSet::baseline());
hmac.hmac_sha256(salt, ikm)
}
pub fn expand(prk: &[u8; 32], info: &[u8], length: usize) -> Vec<u8> {
let hmac = X86HmacImpl::new(X86CryptoFeatureSet::baseline());
let n = (length + 31) / 32; let mut okm = Vec::with_capacity(length);
let mut t = Vec::new();
for i in 1..=n as u8 {
let mut input = t.clone();
input.extend_from_slice(info);
input.push(i);
t = hmac.hmac_sha256(prk, &input).to_vec();
let take = std::cmp::min(32, length - okm.len());
okm.extend_from_slice(&t[..take]);
}
okm
}
pub fn derive(salt: &[u8], ikm: &[u8], info: &[u8], length: usize) -> Vec<u8> {
let prk = Self::extract(salt, ikm);
Self::expand(&prk, info, length)
}
}
#[derive(Debug, Clone)]
pub struct X86CryptoCrc32c {
pub has_sse42: bool,
table: [[u32; 256]; 1],
}
impl X86CryptoCrc32c {
const CRC32C_POLY: u32 = 0x1EDC6F41;
pub fn new(has_sse42: bool) -> Self {
let table = Self::generate_table();
Self {
has_sse42,
table,
}
}
pub fn compute(&self, data: &[u8], initial: u32) -> u32 {
if self.has_sse42 {
self.hardware_crc32c(data, initial)
} else {
self.software_crc32c(data, initial)
}
}
fn hardware_crc32c(&self, data: &[u8], mut crc: u32) -> u32 {
let mut i = 0;
while i + 8 <= data.len() {
let val = u64::from_le_bytes([
data[i],
data[i + 1],
data[i + 2],
data[i + 3],
data[i + 4],
data[i + 5],
data[i + 6],
data[i + 7],
]);
crc = Self::crc32c_u64(crc, val);
i += 8;
}
while i < data.len() {
crc = Self::crc32c_u8(crc, data[i]);
i += 1;
}
crc
}
fn software_crc32c(&self, data: &[u8], mut crc: u32) -> u32 {
for &byte in data {
let idx = ((crc ^ (byte as u32)) & 0xFF) as usize;
crc = self.table[0][idx] ^ (crc >> 8);
}
crc
}
fn crc32c_u8(crc: u32, byte: u8) -> u32 {
let idx = ((crc ^ (byte as u32)) & 0xFF) as usize;
let table = Self::generate_table();
table[0][idx] ^ (crc >> 8)
}
fn crc32c_u64(crc: u32, val: u64) -> u32 {
let bytes = val.to_le_bytes();
let mut c = crc;
for &b in &bytes {
c = Self::crc32c_u8(c, b);
}
c
}
fn generate_table() -> [[u32; 256]; 1] {
let mut table = [[0u32; 256]; 1];
for i in 0..256u32 {
let mut crc = i;
for _ in 0..8 {
if crc & 1 != 0 {
crc = Self::CRC32C_POLY ^ (crc >> 1);
} else {
crc >>= 1;
}
}
table[0][i as usize] = crc;
}
table
}
pub fn compute_reflected(&self, data: &[u8]) -> u32 {
let crc = self.compute(data, 0xFFFF_FFFF);
crc ^ 0xFFFF_FFFF
}
}
#[derive(Debug, Clone)]
pub struct X86CryptoBenchmark {
pub name: String,
pub iterations: u64,
pub total_bytes: u64,
pub total_time_us: u64,
pub min_time_us: u64,
pub max_time_us: u64,
pub throughput_bytes_per_sec: f64,
pub ops_per_sec: f64,
}
impl X86CryptoBenchmark {
pub fn run<F>(name: &str, iterations: u64, data_size: usize, mut func: F) -> Self
where
F: FnMut(),
{
for _ in 0..10 {
func();
}
let mut min_time = u64::MAX;
let mut max_time = 0u64;
let mut total_time = 0u64;
for _ in 0..iterations {
let start = std::time::Instant::now();
func();
let elapsed = start.elapsed().as_micros() as u64;
min_time = min_time.min(elapsed);
max_time = max_time.max(elapsed);
total_time += elapsed;
}
let total_bytes = iterations * data_size as u64;
let throughput = if total_time > 0 {
(total_bytes as f64) / (total_time as f64) * 1_000_000.0
} else {
f64::MAX
};
let ops_per_sec = if total_time > 0 {
(iterations as f64) / (total_time as f64) * 1_000_000.0
} else {
f64::MAX
};
Self {
name: name.to_string(),
iterations,
total_bytes,
total_time_us: total_time,
min_time_us: min_time,
max_time_us: max_time,
throughput_bytes_per_sec: throughput,
ops_per_sec,
}
}
pub fn summary(&self) -> String {
format!(
"{}: {} iters, {:.2} MB/s, {:.2} ops/s, min={}us, max={}us",
self.name,
self.iterations,
self.throughput_bytes_per_sec / 1_000_000.0,
self.ops_per_sec,
self.min_time_us,
self.max_time_us,
)
}
}
#[derive(Debug, Clone)]
pub struct X86CryptoBenchmarkSuite {
pub benchmarks: Vec<X86CryptoBenchmark>,
pub crypto: X86Crypto,
}
impl X86CryptoBenchmarkSuite {
pub fn new(crypto: X86Crypto) -> Self {
Self {
benchmarks: Vec::new(),
crypto,
}
}
pub fn run_all(&mut self) {
let iterations = 10000;
let data_sizes = [16, 64, 256, 1024, 4096];
for &size in &data_sizes {
let data = vec![0x42u8; size];
let key: [u8; 16] = [0x00; 16];
let aes = self.crypto.algorithms.aes.clone();
let round_keys = aes.key_expansion_128(&key);
let bench = X86CryptoBenchmark::run(
&format!("AES-128 encrypt {}B", size),
iterations,
size,
|| {
let _ = aes.encrypt_block_aes128(
&data[..16].try_into().unwrap_or([0u8; 16]),
&round_keys,
);
},
);
self.benchmarks.push(bench);
let sha = self.crypto.algorithms.sha.clone();
let bench = X86CryptoBenchmark::run(
&format!("SHA-256 {}B", size),
iterations / 2,
size,
|| {
let _ = sha.sha256(&data);
},
);
self.benchmarks.push(bench);
}
}
pub fn summarize(&self) -> String {
let mut result = String::from("Crypto Benchmark Results:\n");
result.push_str(&"=".repeat(70));
result.push('\n');
for bench in &self.benchmarks {
result.push_str(&bench.summary());
result.push('\n');
}
result
}
}
#[derive(Debug, Clone)]
pub struct X86CryptoFipsStatus {
pub fips_enabled: bool,
pub self_tests_passed: bool,
pub aes_kat_passed: bool,
pub sha_kat_passed: bool,
pub hmac_kat_passed: bool,
pub drbg_kat_passed: bool,
pub rsa_sigver_passed: bool,
pub ecdsa_sigver_passed: bool,
pub crngt_passed: bool,
pub pct_passed: bool,
pub integrity_passed: bool,
pub test_results: Vec<(String, bool, Option<String>)>,
}
impl X86CryptoFipsStatus {
pub fn default_passing() -> Self {
Self {
fips_enabled: true,
self_tests_passed: true,
aes_kat_passed: true,
sha_kat_passed: true,
hmac_kat_passed: true,
drbg_kat_passed: true,
rsa_sigver_passed: true,
ecdsa_sigver_passed: true,
crngt_passed: true,
pct_passed: true,
integrity_passed: true,
test_results: vec![
("AES KAT".into(), true, None),
("SHA KAT".into(), true, None),
("HMAC KAT".into(), true, None),
("DRBG KAT".into(), true, None),
("CRNGT".into(), true, None),
("PCT".into(), true, None),
],
}
}
pub fn is_fips_approved(&self) -> bool {
self.fips_enabled && self.self_tests_passed
}
pub fn failed_tests(&self) -> Vec<&str> {
self.test_results
.iter()
.filter_map(|(name, passed, _)| if !*passed { Some(name.as_str()) } else { None })
.collect()
}
pub fn report(&self) -> String {
let mut report = String::from("FIPS 140-2 Status Report\n");
report.push_str(&"=".repeat(50));
report.push('\n');
report.push_str(&format!("FIPS Enabled: {}\n", self.fips_enabled));
report.push_str(&format!("Self-Tests Passed: {}\n", self.self_tests_passed));
report.push_str("\nIndividual Tests:\n");
for (name, passed, error) in &self.test_results {
let status = if *passed { "PASS" } else { "FAIL" };
report.push_str(&format!(" {}: {}", name, status));
if let Some(err) = error {
report.push_str(&format!(" ({})", err));
}
report.push('\n');
}
report
}
}
#[derive(Debug, Clone)]
pub struct X86CryptoCertificate {
pub der_data: Vec<u8>,
pub subject: String,
pub issuer: String,
pub serial: Vec<u8>,
pub not_before: String,
pub not_after: String,
pub key_algorithm: X86CryptoKeyAlgorithm,
pub public_key: Vec<u8>,
pub sig_algorithm: X86CryptoSignatureAlgorithm,
pub signature: Vec<u8>,
pub extensions: Vec<X86CryptoCertExtension>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum X86CryptoKeyAlgorithm {
RSA,
EC,
Ed25519,
Ed448,
Unknown(String),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum X86CryptoSignatureAlgorithm {
Sha256WithRSA,
Sha384WithRSA,
Sha512WithRSA,
EcdsaWithSha256,
EcdsaWithSha384,
EcdsaWithSha512,
Ed25519,
Unknown(String),
}
#[derive(Debug, Clone)]
pub struct X86CryptoCertExtension {
pub oid: String,
pub critical: bool,
pub value: Vec<u8>,
}
impl X86CryptoCertificate {
pub fn test_self_signed() -> Self {
Self {
der_data: Vec::new(),
subject: "CN=Test Certificate".into(),
issuer: "CN=Test Certificate".into(),
serial: vec![0x01],
not_before: "20240101000000Z".into(),
not_after: "20340101000000Z".into(),
key_algorithm: X86CryptoKeyAlgorithm::RSA,
public_key: vec![0x30; 270],
sig_algorithm: X86CryptoSignatureAlgorithm::Sha256WithRSA,
signature: vec![0x00; 256],
extensions: vec![
X86CryptoCertExtension {
oid: "2.5.29.19".into(),
critical: true,
value: vec![0x30, 0x03, 0x01, 0x01, 0xFF],
},
X86CryptoCertExtension {
oid: "2.5.29.15".into(),
critical: true,
value: vec![0x03, 0x02, 0x05, 0xA0],
},
],
}
}
pub fn verify_chain(certs: &[Self]) -> Result<bool, X86CryptoError> {
if certs.is_empty() {
return Err(X86CryptoError::General("Empty certificate chain".into()));
}
for i in 0..certs.len() - 1 {
if certs[i].issuer != certs[i + 1].subject {
return Err(X86CryptoError::General(format!(
"Chain broken at index {}: issuer '{}' != subject '{}'",
i, certs[i].issuer, certs[i + 1].subject
)));
}
}
Ok(true)
}
pub fn is_valid_now(&self) -> bool {
!self.not_before.is_empty() && !self.not_after.is_empty()
}
}
#[derive(Debug, Clone)]
pub struct X86CryptoRegistry {
pub implementations: HashMap<String, X86CryptoImplEntry>,
pub features: X86CryptoFeatureSet,
}
#[derive(Debug, Clone)]
pub struct X86CryptoImplEntry {
pub algorithm: String,
pub variant: String,
pub required_features: Vec<X86CryptoFeature>,
pub priority: u32,
pub hardware_accelerated: bool,
pub side_channel_resistant: bool,
pub fips_validated: bool,
}
impl X86CryptoRegistry {
pub fn new(features: X86CryptoFeatureSet) -> Self {
let mut implementations = HashMap::new();
Self::register(&mut implementations, X86CryptoImplEntry {
algorithm: "AES".into(),
variant: "vaes".into(),
required_features: vec![X86CryptoFeature::Avx512f, X86CryptoFeature::Vaes],
priority: 3,
hardware_accelerated: true,
side_channel_resistant: true,
fips_validated: true,
});
Self::register(&mut implementations, X86CryptoImplEntry {
algorithm: "AES".into(),
variant: "aesni".into(),
required_features: vec![X86CryptoFeature::AesNi],
priority: 2,
hardware_accelerated: true,
side_channel_resistant: true,
fips_validated: true,
});
Self::register(&mut implementations, X86CryptoImplEntry {
algorithm: "AES".into(),
variant: "software".into(),
required_features: vec![],
priority: 0,
hardware_accelerated: false,
side_channel_resistant: false,
fips_validated: true,
});
Self::register(&mut implementations, X86CryptoImplEntry {
algorithm: "SHA-256".into(),
variant: "shani".into(),
required_features: vec![X86CryptoFeature::ShaNi],
priority: 2,
hardware_accelerated: true,
side_channel_resistant: true,
fips_validated: true,
});
Self::register(&mut implementations, X86CryptoImplEntry {
algorithm: "SHA-256".into(),
variant: "software".into(),
required_features: vec![],
priority: 0,
hardware_accelerated: false,
side_channel_resistant: false,
fips_validated: true,
});
Self::register(&mut implementations, X86CryptoImplEntry {
algorithm: "GHASH".into(),
variant: "vpclmul".into(),
required_features: vec![X86CryptoFeature::Vpclmulqdq],
priority: 3,
hardware_accelerated: true,
side_channel_resistant: true,
fips_validated: true,
});
Self::register(&mut implementations, X86CryptoImplEntry {
algorithm: "GHASH".into(),
variant: "pclmul".into(),
required_features: vec![X86CryptoFeature::Pclmul],
priority: 2,
hardware_accelerated: true,
side_channel_resistant: true,
fips_validated: true,
});
Self::register(&mut implementations, X86CryptoImplEntry {
algorithm: "GHASH".into(),
variant: "software".into(),
required_features: vec![],
priority: 0,
hardware_accelerated: false,
side_channel_resistant: false,
fips_validated: true,
});
Self {
implementations,
features,
}
}
fn register(map: &mut HashMap<String, X86CryptoImplEntry>, entry: X86CryptoImplEntry) {
let key = format!("{}_{}", entry.algorithm, entry.variant);
map.insert(key, entry);
}
pub fn select_best(&self, algorithm: &str) -> Option<&X86CryptoImplEntry> {
self.implementations
.values()
.filter(|e| e.algorithm == algorithm)
.filter(|e| {
e.required_features
.iter()
.all(|f| self.features.has(*f))
})
.max_by_key(|e| e.priority)
}
pub fn list_implementations(&self, algorithm: &str) -> Vec<&X86CryptoImplEntry> {
let mut entries: Vec<_> = self
.implementations
.values()
.filter(|e| e.algorithm == algorithm)
.collect();
entries.sort_by_key(|e| -(e.priority as i32));
entries
}
pub fn is_available(&self, algorithm: &str) -> bool {
self.select_best(algorithm).is_some()
}
}
#[derive(Debug, Clone)]
pub struct X86CryptoAntiPatterns;
impl X86CryptoAntiPatterns {
pub fn is_weak_key_size(key_bits: usize, algorithm: &str) -> bool {
match algorithm {
"RSA" => key_bits < 2048,
"EC" => key_bits < 256,
"AES" => key_bits < 128,
"HMAC" => key_bits < 128,
_ => key_bits < 128,
}
}
pub fn is_deprecated_hash(algorithm: &str) -> bool {
matches!(algorithm, "MD2" | "MD4" | "MD5" | "SHA-0" | "SHA-1")
}
pub fn warn_ecb_mode(context: &str) -> Option<String> {
Some(format!(
"WARNING: AES-ECB mode detected in {}. ECB mode does not provide \
semantic security and should not be used for general-purpose encryption. \
Use CBC, CTR, or GCM mode instead.",
context
))
}
pub fn detect_hardcoded_key(code: &str) -> Vec<String> {
let mut warnings = Vec::new();
let patterns = [
"static.*key.*=.*\"",
"const.*key.*=.*\"",
"#define.*KEY",
"unsigned char.*key.*=.*{",
];
for pattern in &patterns {
if code.to_lowercase().contains(&pattern.to_lowercase()) {
warnings.push(format!(
"Potential hardcoded key detected (pattern: {})",
pattern
));
}
}
warnings
}
pub fn detect_non_ct_comparison(code: &str) -> Vec<String> {
let mut warnings = Vec::new();
let risky = ["memcmp", "strcmp", "strncmp", "=="];
for pattern in &risky {
if code.contains(pattern) {
warnings.push(format!(
"Potential non-constant-time comparison detected: '{}'. \
Use constant-time comparison for security-sensitive data.",
pattern
));
}
}
warnings
}
pub fn check_pbkdf2_iterations(iterations: u32) -> Option<String> {
if iterations < X86_PBKDF2_MIN_ITERATIONS {
Some(format!(
"PBKDF2 iterations ({}) below minimum recommended ({})",
iterations, X86_PBKDF2_RECOMMENDED_ITERATIONS
))
} else if iterations < 100_000 {
Some(format!(
"PBKDF2 iterations ({}) may be insufficient for modern security. \
Consider at least {} iterations.",
iterations, X86_PBKDF2_RECOMMENDED_ITERATIONS
))
} else {
None
}
}
pub fn detect_insecure_prng(code: &str) -> Vec<String> {
let mut warnings = Vec::new();
let insecure = ["rand()", "srand(", "random()", "drand48"];
for pattern in &insecure {
if code.contains(pattern) {
warnings.push(format!(
"Insecure PRNG detected: '{}'. Use a CSPRNG instead \
(e.g., RDRAND, /dev/urandom, getrandom).",
pattern
));
}
}
warnings
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_crypto_feature_cpuid_info() {
let (leaf, sub_leaf, bit) = X86CryptoFeature::AesNi.cpuid_info();
assert_eq!(leaf, 0x0000_0001);
assert_eq!(sub_leaf, 0);
assert_eq!(bit, 25);
let (leaf, sub_leaf, bit) = X86CryptoFeature::ShaNi.cpuid_info();
assert_eq!(leaf, 0x0000_0007);
assert_eq!(sub_leaf, 0);
assert_eq!(bit, 29);
}
#[test]
fn test_crypto_feature_compiler_flags() {
assert_eq!(X86CryptoFeature::AesNi.compiler_flag(), "-maes");
assert_eq!(X86CryptoFeature::ShaNi.compiler_flag(), "-msha");
assert_eq!(X86CryptoFeature::Pclmul.compiler_flag(), "-mpclmul");
assert_eq!(X86CryptoFeature::RdRand.compiler_flag(), "-mrdrnd");
assert_eq!(X86CryptoFeature::RdSeed.compiler_flag(), "-mrdseed");
}
#[test]
fn test_crypto_feature_target_attribute() {
assert_eq!(X86CryptoFeature::AesNi.target_attribute(), "aes");
assert_eq!(X86CryptoFeature::Avx512f.target_attribute(), "avx512f");
assert_eq!(X86CryptoFeature::Vaes.target_attribute(), "vaes");
}
#[test]
fn test_crypto_feature_compatibility() {
assert!(X86CryptoFeature::Avx512f.is_compatible_with(&X86CryptoFeature::Avx2));
assert!(X86CryptoFeature::Avx2.is_compatible_with(&X86CryptoFeature::Avx));
assert!(X86CryptoFeature::Vaes.is_compatible_with(&X86CryptoFeature::AesNi));
}
#[test]
fn test_crypto_feature_display() {
assert_eq!(format!("{}", X86CryptoFeature::AesNi), "AES-NI");
assert_eq!(format!("{}", X86CryptoFeature::ShaNi), "SHA-NI");
assert_eq!(format!("{}", X86CryptoFeature::RdRand), "RDRAND");
}
#[test]
fn test_feature_set_all() {
let fs = X86CryptoFeatureSet::all_features();
assert!(fs.has(X86CryptoFeature::AesNi));
assert!(fs.has(X86CryptoFeature::Pclmul));
assert!(fs.has(X86CryptoFeature::ShaNi));
assert!(fs.has(X86CryptoFeature::RdRand));
assert!(fs.has(X86CryptoFeature::Avx512f));
assert!(fs.has(X86CryptoFeature::Vaes));
}
#[test]
fn test_feature_set_baseline() {
let fs = X86CryptoFeatureSet::baseline();
assert!(fs.has(X86CryptoFeature::Sse2));
assert!(!fs.has(X86CryptoFeature::AesNi));
}
#[test]
fn test_feature_set_has_all() {
let fs = X86CryptoFeatureSet::all_features();
assert!(fs.has_all(&[X86CryptoFeature::AesNi, X86CryptoFeature::Pclmul]));
assert!(!fs.has_all(&[X86CryptoFeature::AesNi, X86CryptoFeature::Sm3]));
}
#[test]
fn test_feature_set_insert_remove() {
let mut fs = X86CryptoFeatureSet::baseline();
assert!(!fs.has(X86CryptoFeature::AesNi));
fs.insert(X86CryptoFeature::AesNi);
assert!(fs.has(X86CryptoFeature::AesNi));
fs.remove(&X86CryptoFeature::AesNi);
assert!(!fs.has(X86CryptoFeature::AesNi));
}
#[test]
fn test_feature_set_compiler_flags() {
let fs = X86CryptoFeatureSet::all_features();
let flags = fs.compiler_flags();
assert!(flags.iter().any(|f| f == "-maes"));
assert!(flags.iter().any(|f| f == "-msha"));
}
#[test]
fn test_feature_set_target_attribute_string() {
let mut fs = X86CryptoFeatureSet::baseline();
fs.insert(X86CryptoFeature::AesNi);
fs.insert(X86CryptoFeature::Pclmul);
let attr = fs.target_attribute_string();
assert!(attr.contains("aes"));
assert!(attr.contains("pclmul"));
}
#[test]
fn test_x86crypto_new() {
let crypto = X86Crypto::with_all_features();
assert!(crypto.has_aes_ni());
assert!(crypto.has_sha_ni());
assert!(!crypto.fips_mode);
}
#[test]
fn test_x86crypto_baseline() {
let crypto = X86Crypto::baseline();
assert!(!crypto.has_aes_ni());
}
#[test]
fn test_x86crypto_enable_fips() {
let mut crypto = X86Crypto::with_all_features();
assert!(!crypto.fips_mode);
crypto.enable_fips();
assert!(crypto.fips_mode);
assert!(crypto.compilation.fips_compliance);
}
#[test]
fn test_x86crypto_best_target_attribute() {
let crypto = X86Crypto::with_all_features();
let attr = crypto.best_target_attribute();
assert!(attr.contains("aes"));
assert!(attr.contains("sse2"));
}
#[test]
fn test_x86crypto_dispatch_table() {
let crypto = X86Crypto::with_all_features();
let table = crypto.generate_dispatch_table();
assert_eq!(table.aes_encrypt_impl, "vaes_encrypt");
assert_eq!(table.ghash_impl, "vpclmul_ghash");
}
#[test]
fn test_intrinsic_catalog_creation() {
let intrinsics = X86CryptoIntrinsics::new(X86CryptoFeatureSet::all_features());
assert!(intrinsics.intrinsic_count() > 20);
}
#[test]
fn test_intrinsic_catalog_lookup() {
let intrinsics = X86CryptoIntrinsics::new(X86CryptoFeatureSet::all_features());
let info = intrinsics.get_intrinsic("_mm_aesenc_si128");
assert!(info.is_some());
assert_eq!(info.unwrap().mnemonic, "aesenc");
}
#[test]
fn test_intrinsic_catalog_is_available() {
let intrinsics = X86CryptoIntrinsics::new(X86CryptoFeatureSet::all_features());
assert!(intrinsics.is_available("_mm_aesenc_si128"));
}
#[test]
fn test_intrinsic_catalog_not_available() {
let intrinsics = X86CryptoIntrinsics::new(X86CryptoFeatureSet::baseline());
assert!(!intrinsics.is_available("_mm_aesenc_si128"));
}
#[test]
fn test_intrinsic_categories() {
let intrinsics = X86CryptoIntrinsics::new(X86CryptoFeatureSet::all_features());
let aes_intrinsics = intrinsics.intrinsics_in_category(X86CryptoIntrinsicCategory::AesNi);
assert!(!aes_intrinsics.is_empty());
assert!(aes_intrinsics.iter().any(|i| i.name == "_mm_aesenc_si128"));
assert!(aes_intrinsics.iter().any(|i| i.name == "_mm_aeskeygenassist_si128"));
}
#[test]
fn test_intrinsic_header_generation() {
let intrinsics = X86CryptoIntrinsics::new(X86CryptoFeatureSet::all_features());
let header = intrinsics.generate_intrinsic_header();
assert!(header.contains("aesenc"));
assert!(header.contains("x86_crypto_intrinsics"));
}
#[test]
fn test_available_intrinsics() {
let intrinsics = X86CryptoIntrinsics::new(X86CryptoFeatureSet::all_features());
let available = intrinsics.available_intrinsics();
assert!(available.iter().any(|i| i.name == "_mm_aesenc_si128"));
}
#[test]
fn test_aes_key_expansion_128() {
let aes = X86AesImpl::new(X86CryptoFeatureSet::all_features());
let key: [u8; 16] = [
0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,
];
let expanded = aes.key_expansion_128(&key);
assert_eq!(expanded.len(), 176);
assert_eq!(&expanded[0..16], &key);
assert_eq!(expanded[16], 0xa0);
assert_eq!(expanded[17], 0xfa);
}
#[test]
fn test_aes_key_expansion_256() {
let aes = X86AesImpl::new(X86CryptoFeatureSet::all_features());
let key: [u8; 32] = [
0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe,
0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7,
0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4,
];
let expanded = aes.key_expansion_256(&key);
assert_eq!(expanded.len(), 240); }
#[test]
fn test_aes_encrypt_decrypt_128() {
let aes = X86AesImpl::new(X86CryptoFeatureSet::all_features());
let key: [u8; 16] = [
0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,
];
let plaintext: [u8; 16] = [
0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d,
0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37, 0x07, 0x34,
];
let round_keys = aes.key_expansion_128(&key);
let ciphertext = aes.encrypt_block_aes128(&plaintext, &round_keys);
let decrypted = aes.decrypt_block_aes128(&ciphertext, &round_keys);
assert_eq!(decrypted, plaintext);
}
#[test]
fn test_aes_encrypt_decrypt_256() {
let aes = X86AesImpl::new(X86CryptoFeatureSet::all_features());
let key: [u8; 32] = [
0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe,
0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7,
0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4,
];
let plaintext: [u8; 16] = [
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
];
let round_keys = aes.key_expansion_256(&key);
let ciphertext = aes.encrypt_block_aes256(&plaintext, &round_keys);
let decrypted = aes.decrypt_block_aes256(&ciphertext, &round_keys);
assert_eq!(decrypted, plaintext);
}
#[test]
fn test_aes_known_answer_test() {
let aes = X86AesImpl::new(X86CryptoFeatureSet::baseline());
let key: [u8; 16] = [
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
];
let plaintext: [u8; 16] = [
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
];
let expected: [u8; 16] = [
0x69, 0xc4, 0xe0, 0xd8, 0x6a, 0x7b, 0x04, 0x30,
0xd8, 0xcd, 0xb7, 0x80, 0x70, 0xb4, 0xc5, 0x5a,
];
let round_keys = aes.key_expansion_128(&key);
let ciphertext = aes.encrypt_block_aes128(&plaintext, &round_keys);
assert_eq!(ciphertext, expected);
}
#[test]
fn test_aes_ecb_mode() {
let modes = X86AesModes::new(X86CryptoFeatureSet::all_features());
let key: [u8; 16] = [0x00; 16];
let plaintext = vec![0x42u8; 32];
let ct = modes.ecb_encrypt(16, &plaintext, &key).unwrap();
assert_eq!(ct.len(), 32);
let pt = modes.ecb_decrypt(16, &ct, &key).unwrap();
assert_eq!(pt, plaintext);
}
#[test]
fn test_aes_cbc_mode() {
let modes = X86AesModes::new(X86CryptoFeatureSet::all_features());
let key: [u8; 16] = [0x00; 16];
let iv: [u8; 16] = [0x01; 16];
let plaintext = vec![0x42u8; 32];
let ct = modes.cbc_encrypt(16, &plaintext, &key, &iv).unwrap();
assert_eq!(ct.len(), 32);
let pt = modes.cbc_decrypt(16, &ct, &key, &iv).unwrap();
assert_eq!(pt, plaintext);
}
#[test]
fn test_aes_ctr_mode() {
let modes = X86AesModes::new(X86CryptoFeatureSet::all_features());
let key: [u8; 16] = [0x00; 16];
let nonce = vec![0x42u8; 8];
let data = vec![0xFFu8; 64];
let encrypted = modes.ctr_crypt(16, &data, &key, &nonce).unwrap();
assert_eq!(encrypted.len(), 64);
assert_ne!(encrypted, data);
let decrypted = modes.ctr_crypt(16, &encrypted, &key, &nonce).unwrap();
assert_eq!(decrypted, data);
}
#[test]
fn test_aes_gcm_mode() {
let modes = X86AesModes::new(X86CryptoFeatureSet::all_features());
let key: [u8; 16] = [0x00; 16];
let iv = vec![0xCA; 12];
let plaintext = b"Hello, GCM mode!";
let aad = b"additional data";
let (ct, tag) = modes
.gcm_encrypt(16, plaintext, &key, &iv, aad, 16)
.unwrap();
assert_eq!(ct.len(), plaintext.len());
assert_eq!(tag.len(), 16);
let pt = modes
.gcm_decrypt(16, &ct, &key, &iv, aad, &tag)
.unwrap();
assert_eq!(pt, plaintext);
}
#[test]
fn test_aes_gcm_tag_verification_fails() {
let modes = X86AesModes::new(X86CryptoFeatureSet::all_features());
let key: [u8; 16] = [0x00; 16];
let iv = vec![0xCA; 12];
let plaintext = b"test";
let aad = b"aad";
let (ct, _tag) = modes
.gcm_encrypt(16, plaintext, &key, &iv, aad, 16)
.unwrap();
let wrong_tag = vec![0xFF; 16];
let result = modes.gcm_decrypt(16, &ct, &key, &iv, aad, &wrong_tag);
assert!(result.is_err());
}
#[test]
fn test_aes_ccm_mode() {
let modes = X86AesModes::new(X86CryptoFeatureSet::all_features());
let key: [u8; 16] = [0x40; 16];
let nonce = vec![0xAB; 13];
let plaintext = b"CCM test data";
let aad = b"associated";
let (ct, tag) = modes
.ccm_encrypt(16, plaintext, &key, &nonce, aad, 8)
.unwrap();
assert_eq!(ct.len(), plaintext.len());
assert_eq!(tag.len(), 8);
}
#[test]
fn test_aes_xts_mode() {
let modes = X86AesModes::new(X86CryptoFeatureSet::all_features());
let key1: [u8; 16] = [0x10; 16];
let key2: [u8; 16] = [0x20; 16];
let tweak: [u8; 16] = [0xAA; 16];
let plaintext = vec![0x42u8; 32];
let ct = modes
.xts_encrypt(16, &plaintext, &key1, &key2, &tweak)
.unwrap();
assert_eq!(ct.len(), 32);
assert_ne!(ct, plaintext);
let pt = modes
.xts_decrypt(16, &ct, &key1, &key2, &tweak)
.unwrap();
assert_eq!(pt, plaintext);
}
#[test]
fn test_ghash_set_h() {
let mut ghash = X86GhashImpl::new(X86CryptoFeatureSet::all_features());
let h: [u8; 16] = [0x01; 16];
ghash.set_h(&h);
assert_eq!(ghash.h[0], u64::from_be_bytes([0x01; 8]));
}
#[test]
fn test_ghash_computation() {
let mut ghash = X86GhashImpl::new(X86CryptoFeatureSet::all_features());
let h: [u8; 16] = [
0x66, 0xe9, 0x4b, 0xd4, 0xef, 0x8a, 0x2c, 0x3b,
0x88, 0x4c, 0xfa, 0x59, 0xca, 0x34, 0x2b, 0x2e,
];
ghash.set_h(&h);
let data = vec![0x00u8; 32];
let result = ghash.compute_ghash(&data);
assert_eq!(result.len(), 16);
}
#[test]
fn test_ghash_empty() {
let mut ghash = X86GhashImpl::new(X86CryptoFeatureSet::all_features());
ghash.set_h(&[0x00; 16]);
let result = ghash.compute_ghash(&[]);
assert_eq!(result, [0u8; 16]);
}
#[test]
fn test_sha1_known_answer() {
let sha = X86ShaImpl::new(X86CryptoFeatureSet::baseline());
let hash = sha.sha1(b"abc");
let expected: [u8; 20] = [
0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a,
0xba, 0x3e, 0x25, 0x71, 0x78, 0x50, 0xc2, 0x6c,
0x9c, 0xd0, 0xd8, 0x9d,
];
assert_eq!(hash, expected);
}
#[test]
fn test_sha1_empty() {
let sha = X86ShaImpl::new(X86CryptoFeatureSet::baseline());
let hash = sha.sha1(b"");
let expected: [u8; 20] = [
0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d,
0x32, 0x55, 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90,
0xaf, 0xd8, 0x07, 0x09,
];
assert_eq!(hash, expected);
}
#[test]
fn test_sha256_known_answer() {
let sha = X86ShaImpl::new(X86CryptoFeatureSet::baseline());
let hash = sha.sha256(b"abc");
let expected: [u8; 32] = [
0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea,
0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad,
];
assert_eq!(hash, expected);
}
#[test]
fn test_sha256_empty() {
let sha = X86ShaImpl::new(X86CryptoFeatureSet::baseline());
let hash = sha.sha256(b"");
let expected: [u8; 32] = [
0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14,
0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24,
0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c,
0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55,
];
assert_eq!(hash, expected);
}
#[test]
fn test_sha512_known_answer() {
let sha = X86ShaImpl::new(X86CryptoFeatureSet::baseline());
let hash = sha.sha512(b"abc");
let expected: [u8; 64] = [
0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba,
0xcc, 0x41, 0x73, 0x49, 0xae, 0x20, 0x41, 0x31,
0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2,
0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a,
0x21, 0x92, 0x99, 0x2a, 0x27, 0x4f, 0xc1, 0xa8,
0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd,
0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e,
0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f,
];
assert_eq!(hash, expected);
}
#[test]
fn test_hmac_sha256_rfc4231_test1() {
let hmac = X86HmacImpl::new(X86CryptoFeatureSet::baseline());
let key = vec![0x0bu8; 20];
let data = b"Hi There";
let result = hmac.hmac_sha256(&key, data);
let expected_first_byte = 0xb0;
assert_eq!(result[0], expected_first_byte);
assert_eq!(result.len(), 32);
}
#[test]
fn test_hmac_sha1_basic() {
let hmac = X86HmacImpl::new(X86CryptoFeatureSet::baseline());
let key = b"key";
let data = b"The quick brown fox jumps over the lazy dog";
let result = hmac.hmac_sha1(key, data);
assert_eq!(result.len(), 20);
}
#[test]
fn test_hmac_sha512_basic() {
let hmac = X86HmacImpl::new(X86CryptoFeatureSet::baseline());
let key = b"key";
let data = b"test";
let result = hmac.hmac_sha512(key, data);
assert_eq!(result.len(), 64);
}
#[test]
fn test_hmac_consistency() {
let hmac = X86HmacImpl::new(X86CryptoFeatureSet::baseline());
let key = b"mysecretkey";
let message = b"important message";
let h1 = hmac.hmac_sha256(key, message);
let h2 = hmac.hmac_sha256(key, message);
assert_eq!(h1, h2);
let h3 = hmac.hmac_sha256(b"different", message);
assert_ne!(h1, h3);
}
#[test]
fn test_pbkdf2_hmac_sha256_basic() {
let pbkdf2 = X86Pbkdf2Impl::new(X86CryptoFeatureSet::baseline());
let password = b"password";
let salt = b"salt_salt_salt_salt_"; let dk = pbkdf2
.pbkdf2_hmac_sha256(password, salt, 1000, 32)
.unwrap();
assert_eq!(dk.len(), 32);
}
#[test]
fn test_pbkdf2_too_few_iterations() {
let pbkdf2 = X86Pbkdf2Impl::new(X86CryptoFeatureSet::baseline());
let result = pbkdf2.pbkdf2_hmac_sha256(b"password", &[0u8; 16], 100, 32);
assert!(result.is_err());
}
#[test]
fn test_pbkdf2_deterministic() {
let pbkdf2 = X86Pbkdf2Impl::new(X86CryptoFeatureSet::baseline());
let password = b"test";
let salt = &[0x01u8; 16];
let dk1 = pbkdf2.pbkdf2_hmac_sha256(password, salt, 1000, 32).unwrap();
let dk2 = pbkdf2.pbkdf2_hmac_sha256(password, salt, 1000, 32).unwrap();
assert_eq!(dk1, dk2);
}
#[test]
fn test_pbkdf2_different_salts() {
let pbkdf2 = X86Pbkdf2Impl::new(X86CryptoFeatureSet::baseline());
let password = b"password";
let dk1 = pbkdf2
.pbkdf2_hmac_sha256(password, &[0x01u8; 16], 1000, 32)
.unwrap();
let dk2 = pbkdf2
.pbkdf2_hmac_sha256(password, &[0x02u8; 16], 1000, 32)
.unwrap();
assert_ne!(dk1, dk2);
}
#[test]
fn test_ct_memequal_equal() {
assert!(X86ConstantTime::ct_memequal(b"hello", b"hello"));
}
#[test]
fn test_ct_memequal_different() {
assert!(!X86ConstantTime::ct_memequal(b"hello", b"world"));
}
#[test]
fn test_ct_memequal_different_lengths() {
assert!(!X86ConstantTime::ct_memequal(b"short", b"longer"));
}
#[test]
fn test_ct_memcmp_same() {
assert!(X86ConstantTime::ct_memcmp(b"test", b"test"));
}
#[test]
fn test_ct_is_zero() {
assert!(X86ConstantTime::ct_is_zero(0));
assert!(!X86ConstantTime::ct_is_zero(1));
}
#[test]
fn test_ct_select() {
let result = X86ConstantTime::ct_select(1, 0xAA, 0xBB);
assert_eq!(result, 0xAA);
let result = X86ConstantTime::ct_select(0, 0xAA, 0xBB);
assert_eq!(result, 0xBB);
}
#[test]
fn test_ct_select_u32() {
let result = X86ConstantTime::ct_select_u32(1, 0xDEAD_BEEF, 0xCAFE_BABE);
assert_eq!(result, 0xDEAD_BEEF);
let result = X86ConstantTime::ct_select_u32(0, 0xDEAD_BEEF, 0xCAFE_BABE);
assert_eq!(result, 0xCAFE_BABE);
}
#[test]
fn test_ct_select_u64() {
let result =
X86ConstantTime::ct_select_u64(1, 0x1234_5678_9ABC_DEF0, 0x0FED_CBA9_8765_4321);
assert_eq!(result, 0x1234_5678_9ABC_DEF0);
}
#[test]
fn test_ct_cmov() {
let mut dst: u8 = 0x00;
X86ConstantTime::ct_cmov(1, &mut dst, 0xFF);
assert_eq!(dst, 0xFF);
X86ConstantTime::ct_cmov(0, &mut dst, 0x00);
assert_eq!(dst, 0xFF); }
#[test]
fn test_ct_swap() {
let (mut a, mut b): (u8, u8) = (0x12, 0x34);
X86ConstantTime::ct_swap(1, &mut a, &mut b);
assert_eq!(a, 0x34);
assert_eq!(b, 0x12);
X86ConstantTime::ct_swap(0, &mut a, &mut b);
assert_eq!(a, 0x34);
assert_eq!(b, 0x12);
}
#[test]
fn test_ct_min_max_u64() {
let min = X86ConstantTime::ct_min_u64(5, 10);
let max = X86ConstantTime::ct_max_u64(5, 10);
assert_eq!(min, 5);
assert_eq!(max, 10);
}
#[test]
fn test_ct_lt_gt_u64() {
assert_eq!(X86ConstantTime::ct_lt_u64(5, 10), 1);
assert_eq!(X86ConstantTime::ct_lt_u64(10, 5), 0);
assert_eq!(X86ConstantTime::ct_gt_u64(10, 5), 1);
assert_eq!(X86ConstantTime::ct_gt_u64(5, 10), 0);
}
#[test]
fn test_ct_popcount() {
assert_eq!(X86ConstantTime::ct_popcount(0x0000_0000_0000_0000), 0);
assert_eq!(X86ConstantTime::ct_popcount(0x0000_0000_0000_000F), 4);
assert_eq!(X86ConstantTime::ct_popcount(0xFFFF_FFFF_FFFF_FFFF), 64);
}
#[test]
fn test_ct_bit_test() {
assert_eq!(X86ConstantTime::ct_bit_test(0x08, 3), 1);
assert_eq!(X86ConstantTime::ct_bit_test(0x08, 2), 0);
}
#[test]
fn test_ct_zeroize() {
let mut data = [0x42u8; 32];
X86ConstantTime::ct_zeroize(&mut data);
assert_eq!(data, [0u8; 32]);
}
#[test]
fn test_ct_copy_if() {
let mut dst = [0xFFu8; 4];
let src = [0x00u8; 4];
X86ConstantTime::ct_copy_if(1, &mut dst, &src);
assert_eq!(dst, [0x00u8; 4]);
let mut dst2 = [0xFFu8; 4];
X86ConstantTime::ct_copy_if(0, &mut dst2, &[0x00u8; 4]);
assert_eq!(dst2, [0xFFu8; 4]);
}
#[test]
fn test_rdrand_available() {
let rng = X86RandomGeneration::new(X86CryptoFeatureSet::all_features());
assert!(rng.has_rdrand);
}
#[test]
fn test_rdseed_available() {
let rng = X86RandomGeneration::new(X86CryptoFeatureSet::all_features());
assert!(rng.has_rdseed);
}
#[test]
fn test_rdrand_not_available() {
let rng = X86RandomGeneration::new(X86CryptoFeatureSet::baseline());
assert!(!rng.has_rdrand);
}
#[test]
fn test_rdrand_bytes_all_features() {
let rng = X86RandomGeneration::new(X86CryptoFeatureSet::all_features());
let mut output = [0u8; 64];
assert!(rng.rdrand_bytes(&mut output));
assert!(output.iter().any(|&b| b != 0));
}
#[test]
fn test_rdseed_bytes_all_features() {
let rng = X86RandomGeneration::new(X86CryptoFeatureSet::all_features());
let mut output = [0u8; 32];
assert!(rng.rdseed_bytes(&mut output));
assert!(output.iter().any(|&b| b != 0));
}
#[test]
fn test_rdrand_bytes_no_hardware() {
let rng = X86RandomGeneration::new(X86CryptoFeatureSet::baseline());
let mut output = [0u8; 16];
assert!(!rng.rdrand_bytes(&mut output));
}
#[test]
fn test_chacha_random_init_and_generate() {
let mut rng = X86RandomGeneration::new(X86CryptoFeatureSet::all_features());
let mut output = [0u8; 128];
assert!(rng.chacha_random_bytes(&mut output));
assert!(output.iter().any(|&b| b != 0));
}
#[test]
fn test_chacha_random_reproducibility() {
let mut rng1 = X86RandomGeneration::new(X86CryptoFeatureSet::all_features());
let rng2 = rng1.clone();
let mut out1 = [0u8; 64];
let mut out2 = [0u8; 64];
rng1.chacha_random_bytes(&mut out1);
rng2.clone().chacha_random_bytes(&mut out2);
}
#[test]
fn test_ctr_drbg_init() {
let mut rng = X86RandomGeneration::new(X86CryptoFeatureSet::all_features());
assert!(rng.ctr_drbg_init());
assert!(rng.ctr_drbg.initialized);
}
#[test]
fn test_ctr_drbg_generate() {
let mut rng = X86RandomGeneration::new(X86CryptoFeatureSet::all_features());
rng.ctr_drbg_init();
let mut output = [0u8; 32];
rng.ctr_drbg_generate(&mut output).unwrap();
assert!(output.iter().any(|&b| b != 0));
}
#[test]
fn test_ctr_drbg_reseed() {
let mut rng = X86RandomGeneration::new(X86CryptoFeatureSet::all_features());
rng.ctr_drbg_init();
assert!(rng.ctr_drbg_reseed());
assert_eq!(rng.ctr_drbg.reseed_counter, 1);
}
#[test]
fn test_entropy_estimation() {
let mut rng = X86RandomGeneration::new(X86CryptoFeatureSet::all_features());
let data = [0x00u8; 256]; let entropy = rng.estimate_entropy(&data);
assert!(entropy >= 0.0);
assert!(entropy <= 8.0);
}
#[test]
fn test_entropy_estimation_random() {
let mut rng = X86RandomGeneration::new(X86CryptoFeatureSet::all_features());
let mut data = [0u8; 256];
rng.rdrand_bytes(&mut data);
let entropy = rng.estimate_entropy(&data);
assert!(entropy > 4.0);
}
#[test]
fn test_ct_string_compare_equal() {
let sc = X86SecureComparison::new();
assert!(sc.ct_string_compare("hello", "hello"));
}
#[test]
fn test_ct_string_compare_different() {
let sc = X86SecureComparison::new();
assert!(!sc.ct_string_compare("hello", "world"));
}
#[test]
fn test_ct_array_compare() {
let sc = X86SecureComparison::new();
assert!(sc.ct_array_compare(b"test", b"test"));
assert!(!sc.ct_array_compare(b"test", b"TEST"));
}
#[test]
fn test_ct_int_equal() {
let sc = X86SecureComparison::new();
assert!(sc.ct_int_equal(42, 42));
assert!(!sc.ct_int_equal(42, 24));
}
#[test]
fn test_double_hmac_verify() {
let sc = X86SecureComparison::new();
let key = b"hmac_key";
let data = b"test_data";
let result = sc.double_hmac_verify(data, data, key).unwrap();
assert!(result);
let result = sc.double_hmac_verify(data, b"different", key).unwrap();
assert!(!result);
}
#[test]
fn test_verify_mac() {
let sc = X86SecureComparison::new();
let mac = [0x42u8; 32];
assert!(sc.verify_mac(&mac, &mac));
assert!(!sc.verify_mac(&mac, &[0x00u8; 32]));
}
#[test]
fn test_verify_token() {
let sc = X86SecureComparison::new();
let token = b"secret_api_token_12345";
assert!(sc.verify_token(token, token));
assert!(!sc.verify_token(token, b"wrong_token"));
}
#[test]
fn test_crypto_compilation_flags() {
let fs = X86CryptoFeatureSet::all_features();
let compilation = X86CryptoCompilation::new(fs);
let flags = compilation.crypto_flags();
assert!(flags.iter().any(|f| f == "-maes"));
assert!(flags.iter().any(|f| f == "-msha"));
}
#[test]
fn test_best_target_attr() {
let fs = X86CryptoFeatureSet::all_features();
let compilation = X86CryptoCompilation::new(fs);
let attr = compilation.best_target_attr();
assert!(attr.contains("avx512"));
}
#[test]
fn test_target_clones() {
let fs = X86CryptoFeatureSet::all_features();
let compilation = X86CryptoCompilation::new(fs);
let clones = compilation.target_clones();
assert!(clones.contains("target_clones"));
assert!(clones.contains("baseline"));
}
#[test]
fn test_ifunc_resolver_generation() {
let fs = X86CryptoFeatureSet::all_features();
let compilation = X86CryptoCompilation::new(fs);
let resolver = compilation.generate_ifunc_resolver("aes_encrypt");
assert!(resolver.contains("__builtin_cpu_supports"));
assert!(resolver.contains("aes_encrypt_v_"));
}
#[test]
fn test_fips_self_test_generation() {
let fs = X86CryptoFeatureSet::all_features();
let compilation = X86CryptoCompilation::new(fs);
let code = compilation.generate_fips_self_test();
assert!(code.contains("FIPS 140-2"));
assert!(code.contains("AES-256"));
assert!(code.contains("SHA-256"));
}
#[test]
fn test_fips_header_generation() {
let fs = X86CryptoFeatureSet::all_features();
let compilation = X86CryptoCompilation::new(fs);
let header = compilation.generate_fips_header();
assert!(header.contains("FIPS_MODULE"));
assert!(header.contains("FIPS_VERSION"));
}
#[test]
fn test_crypto_arch_flags_by_level() {
let baseline = X86CryptoCompilation::crypto_arch_flags("baseline");
assert!(baseline.contains(&"-msse2".to_string()));
let aesni = X86CryptoCompilation::crypto_arch_flags("aesni");
assert!(aesni.contains(&"-maes".to_string()));
let avx512 = X86CryptoCompilation::crypto_arch_flags("avx512_vaes");
assert!(avx512.contains(&"-mavx512f".to_string()));
assert!(avx512.contains(&"-mvaes".to_string()));
}
#[test]
fn test_dispatch_table_resolve() {
let table = X86CryptoDispatchTable::new(&X86CryptoFeatureSet::all_features());
assert_eq!(table.resolve("aes_encrypt").unwrap(), "vaes_encrypt");
assert_eq!(table.resolve("sha256").unwrap(), "sha_ni_sha256");
assert_eq!(table.resolve("ghash").unwrap(), "vpclmul_ghash");
assert!(table.resolve("unknown").is_none());
}
#[test]
fn test_dispatch_table_baseline() {
let table = X86CryptoDispatchTable::new(&X86CryptoFeatureSet::baseline());
assert_eq!(table.resolve("aes_encrypt").unwrap(), "software_aes_encrypt");
assert_eq!(table.resolve("sha256").unwrap(), "software_sha256");
}
#[test]
fn test_error_display() {
let err = X86CryptoError::InvalidKeySize(5);
assert!(format!("{}", err).contains("Invalid key size"));
let err = X86CryptoError::TagMismatch;
assert!(format!("{}", err).contains("tag mismatch"));
let err = X86CryptoError::FeatureNotAvailable(X86CryptoFeature::AesNi);
assert!(format!("{}", err).contains("AES-NI"));
}
#[test]
fn test_aes_sbox_involution() {
for i in 0..256u16 {
let v = i as u8;
assert_eq!(
X86_AES_INV_SBOX[X86_AES_SBOX[v as usize] as usize],
v,
"S-Box involution failed at {}",
i
);
}
}
#[test]
fn test_gf_mul2_table() {
for i in 0..256u16 {
let v = i as u8;
let expected = if v & 0x80 != 0 {
(v << 1) ^ 0x1b
} else {
v << 1
};
assert_eq!(X86_GF_MUL2[v as usize], expected, "Mul2 failed at {}", i);
}
}
#[test]
fn test_sha256_k_constants() {
assert_eq!(X86_SHA256_K.len(), 64);
assert_eq!(X86_SHA256_K[0], 0x428a2f98);
}
#[test]
fn test_sha512_k_constants() {
assert_eq!(X86_SHA512_K.len(), 80);
assert_eq!(X86_SHA512_K[0], 0x428a2f98d728ae22);
}
#[test]
fn test_full_aes_encryption_pipeline() {
let crypto = X86Crypto::with_all_features();
let key: [u8; 16] = [0x00; 16];
let plaintext = vec![0x42u8; 64];
let ct = crypto
.algorithms
.aes_modes
.ecb_encrypt(16, &plaintext, &key)
.unwrap();
assert_eq!(ct.len(), 64);
let pt = crypto
.algorithms
.aes_modes
.ecb_decrypt(16, &ct, &key)
.unwrap();
assert_eq!(pt, plaintext);
}
#[test]
fn test_full_sha256_pipeline() {
let crypto = X86Crypto::with_all_features();
let hash = crypto.algorithms.sha.sha256(b"hello world");
let expected: [u8; 32] = [
0xb9, 0x4d, 0x27, 0xb9, 0x93, 0x4d, 0x3e, 0x08,
0xa5, 0x2e, 0x52, 0xd7, 0xda, 0x7d, 0xab, 0xfa,
0xc4, 0x84, 0xef, 0xe3, 0x7a, 0x53, 0x80, 0xee,
0x90, 0x88, 0xf7, 0xac, 0xe2, 0xef, 0xcd, 0xe9,
];
assert_eq!(hash, expected);
}
#[test]
fn test_full_hmac_pipeline() {
let crypto = X86Crypto::with_all_features();
let mac = crypto
.algorithms
.hmac
.hmac_sha256(b"key", b"message");
assert_eq!(mac.len(), 32);
}
#[test]
fn test_full_pbkdf2_pipeline() {
let crypto = X86Crypto::with_all_features();
let dk = crypto
.algorithms
.pbkdf2
.pbkdf2_hmac_sha256(b"password", &[0x01u8; 16], 2000, 64)
.unwrap();
assert_eq!(dk.len(), 64);
}
#[test]
fn test_sm3_empty_hash() {
let hash = X86CryptoSm3::hash(b"");
assert_eq!(hash.len(), 32);
let expected: [u8; 32] = [
0x1a, 0xb2, 0x1d, 0x83, 0x55, 0xcf, 0xa1, 0x7f,
0x8e, 0x61, 0x19, 0x48, 0x31, 0xe8, 0x1a, 0x8f,
0x22, 0xbe, 0xc4, 0xc1, 0x72, 0x12, 0x34, 0x54,
0x7e, 0x69, 0x14, 0xb8, 0x4c, 0x1f, 0x85, 0xa0,
];
assert_eq!(hash, expected);
}
#[test]
fn test_sm3_abc_hash() {
let hash = X86CryptoSm3::hash(b"abc");
let expected: [u8; 32] = [
0x66, 0xc7, 0xf0, 0xf4, 0x62, 0xee, 0xed, 0xd9,
0xd1, 0xf2, 0xd4, 0x6b, 0xdc, 0x10, 0xe4, 0xe2,
0x41, 0x67, 0xc4, 0x87, 0x5c, 0xf2, 0xf7, 0xa2,
0x29, 0x7e, 0xa4, 0x07, 0xd0, 0x80, 0xc9, 0x5b,
];
assert_eq!(hash, expected);
}
#[test]
fn test_sm3_long_message() {
let mut ctx = X86CryptoSm3::new(false);
let data = vec![0x61u8; 1000]; ctx.update(&data);
let hash = ctx.finalize();
assert_eq!(hash.len(), 32);
}
#[test]
fn test_sm3_deterministic() {
let h1 = X86CryptoSm3::hash(b"test message");
let h2 = X86CryptoSm3::hash(b"test message");
assert_eq!(h1, h2);
}
#[test]
fn test_sm3_different_messages() {
let h1 = X86CryptoSm3::hash(b"message1");
let h2 = X86CryptoSm3::hash(b"message2");
assert_ne!(h1, h2);
}
#[test]
fn test_sm4_encrypt_decrypt() {
let key: [u8; 16] = [
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10,
];
let cipher = X86CryptoSm4::new(&key, false);
let plaintext: [u8; 16] = [
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10,
];
let ct = cipher.encrypt_block(&plaintext);
let pt = cipher.decrypt_block(&ct);
assert_eq!(pt, plaintext);
}
#[test]
fn test_sm4_ecb_mode() {
let cipher = X86CryptoSm4::new(&[0x00u8; 16], false);
let pt = vec![0x42u8; 48];
let ct = cipher.ecb_encrypt(&pt);
assert_eq!(ct.len(), 48);
}
#[test]
fn test_sm4_different_keys_produce_different_output() {
let pt: [u8; 16] = [0x00u8; 16];
let c1 = X86CryptoSm4::new(&[0x00u8; 16], false);
let c2 = X86CryptoSm4::new(&[0xFFu8; 16], false);
assert_ne!(c1.encrypt_block(&pt), c2.encrypt_block(&pt));
}
#[test]
fn test_chacha20_rfc8439_test_vector() {
let key: [u8; 32] = [
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
];
let nonce: [u8; 12] = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a,
0x00, 0x00, 0x00, 0x00,
];
let chacha = X86CryptoChaCha20::new(&key, &nonce);
let pt = vec![0x00u8; 64];
let ct = chacha.process(&pt);
assert_eq!(ct[0], 0x10);
assert_eq!(ct[1], 0xf1);
assert_eq!(ct[2], 0xe7);
assert_eq!(ct[3], 0xd4);
}
#[test]
fn test_chacha20_encrypt_decrypt() {
let key = [0x42u8; 32];
let nonce = [0x13u8; 12];
let chacha = X86CryptoChaCha20::new(&key, &nonce);
let pt = b"Hello, ChaCha20!";
let ct = chacha.process(pt);
assert_ne!(ct, pt);
let chacha2 = X86CryptoChaCha20::new(&key, &nonce);
let pt2 = chacha2.process(&ct);
assert_eq!(pt2, pt);
}
#[test]
fn test_chacha20_deterministic() {
let key = [0x99u8; 32];
let nonce = [0x88u8; 12];
let data = vec![0x42u8; 128];
let c1 = X86CryptoChaCha20::new(&key, &nonce);
let ct1 = c1.process(&data);
let c2 = X86CryptoChaCha20::new(&key, &nonce);
let ct2 = c2.process(&data);
assert_eq!(ct1, ct2);
}
#[test]
fn test_poly1305_rfc8439_test_vector() {
let key: [u8; 32] = [
0x85, 0xd6, 0xbe, 0x78, 0x57, 0x55, 0x6d, 0x33,
0x7f, 0x44, 0x52, 0xfe, 0x42, 0xd5, 0x06, 0xa8,
0x01, 0x03, 0x80, 0x8a, 0xfb, 0x0d, 0xb2, 0xfd,
0x4a, 0xbf, 0xf6, 0xaf, 0x41, 0x49, 0xf5, 0x1b,
];
let message = b"Cryptographic Forum Research Group";
let tag = X86CryptoPoly1305::mac(&key, message);
let expected: [u8; 16] = [
0xa8, 0x06, 0x1d, 0xc1, 0x30, 0x51, 0x36, 0xc6,
0xc2, 0x2b, 0x8b, 0xaf, 0x0c, 0x01, 0x27, 0xa9,
];
assert_eq!(tag, expected);
}
#[test]
fn test_poly1305_different_messages() {
let key = [0x42u8; 32];
let tag1 = X86CryptoPoly1305::mac(&key, b"hello");
let tag2 = X86CryptoPoly1305::mac(&key, b"world");
assert_ne!(tag1, tag2);
}
#[test]
fn test_poly1305_deterministic() {
let key = [0x55u8; 32];
let t1 = X86CryptoPoly1305::mac(&key, b"test");
let t2 = X86CryptoPoly1305::mac(&key, b"test");
assert_eq!(t1, t2);
}
#[test]
fn test_chacha20_poly1305_encrypt_decrypt() {
let key = [0x80u8; 32];
let nonce = [0x40u8; 12];
let pt = b"Secret message for authenticated encryption";
let aad = b"additional authenticated data";
let (ct, tag) = X86CryptoChaCha20Poly1305::encrypt(&key, &nonce, pt, aad);
assert_eq!(ct.len(), pt.len());
let pt2 = X86CryptoChaCha20Poly1305::decrypt(&key, &nonce, &ct, aad, &tag).unwrap();
assert_eq!(pt2, pt);
}
#[test]
fn test_chacha20_poly1305_tag_verification_fails() {
let key = [0x80u8; 32];
let nonce = [0x40u8; 12];
let pt = b"test";
let aad = b"aad";
let (ct, _tag) = X86CryptoChaCha20Poly1305::encrypt(&key, &nonce, pt, aad);
let wrong_tag = [0xFFu8; 16];
let result = X86CryptoChaCha20Poly1305::decrypt(&key, &nonce, &ct, aad, &wrong_tag);
assert!(result.is_err());
}
#[test]
fn test_chacha20_poly1305_empty_aad() {
let key = [0x80u8; 32];
let nonce = [0x40u8; 12];
let pt = b"message";
let aad = b"";
let (ct, tag) = X86CryptoChaCha20Poly1305::encrypt(&key, &nonce, pt, aad);
let pt2 = X86CryptoChaCha20Poly1305::decrypt(&key, &nonce, &ct, aad, &tag).unwrap();
assert_eq!(pt2, pt);
}
#[test]
fn test_hkdf_extract_and_expand() {
let ikm = b"input key material";
let salt = b"salt value";
let info = b"context info";
let okm = X86CryptoHkdf::derive(salt, ikm, info, 64);
assert_eq!(okm.len(), 64);
}
#[test]
fn test_hkdf_deterministic() {
let ikm = b"test ikm";
let salt = b"test salt";
let info = b"test info";
let okm1 = X86CryptoHkdf::derive(salt, ikm, info, 32);
let okm2 = X86CryptoHkdf::derive(salt, ikm, info, 32);
assert_eq!(okm1, okm2);
}
#[test]
fn test_hkdf_different_info_produces_different_keys() {
let ikm = b"material";
let salt = b"salt";
let k1 = X86CryptoHkdf::derive(salt, ikm, b"info1", 32);
let k2 = X86CryptoHkdf::derive(salt, ikm, b"info2", 32);
assert_ne!(k1, k2);
}
#[test]
fn test_crc32c_zero_data() {
let crc = X86CryptoCrc32c::new(true);
let result = crc.compute_reflected(&[]);
assert_eq!(result, 0);
}
#[test]
fn test_crc32c_known_vector() {
let crc = X86CryptoCrc32c::new(false);
let result = crc.compute_reflected(b"123456789");
assert_eq!(result, 0xE3069283);
}
#[test]
fn test_crc32c_deterministic() {
let crc = X86CryptoCrc32c::new(false);
let r1 = crc.compute_reflected(b"test");
let r2 = crc.compute_reflected(b"test");
assert_eq!(r1, r2);
}
#[test]
fn test_crc32c_hardware_vs_software() {
let crc_hw = X86CryptoCrc32c::new(true);
let crc_sw = X86CryptoCrc32c::new(false);
let data = b"compare hardware and software CRC32-C";
assert_eq!(crc_hw.compute_reflected(data), crc_sw.compute_reflected(data));
}
#[test]
fn test_benchmark_run() {
let bench = X86CryptoBenchmark::run("test_bench", 100, 16, || {
let _ = 1 + 1;
});
assert_eq!(bench.name, "test_bench");
assert_eq!(bench.iterations, 100);
assert!(bench.total_time_us > 0);
}
#[test]
fn test_benchmark_suite() {
let crypto = X86Crypto::with_all_features();
let mut suite = X86CryptoBenchmarkSuite::new(crypto);
suite.run_all();
assert!(!suite.benchmarks.is_empty());
assert!(suite.benchmarks.len() >= 4);
}
#[test]
fn test_benchmark_summary() {
let bench = X86CryptoBenchmark::run("summary_test", 10, 32, || {
let _ = vec![0u8; 32];
});
let summary = bench.summary();
assert!(summary.contains("summary_test"));
assert!(summary.contains("MB/s"));
}
#[test]
fn test_fips_status_default_passing() {
let status = X86CryptoFipsStatus::default_passing();
assert!(status.is_fips_approved());
assert!(status.failed_tests().is_empty());
}
#[test]
fn test_fips_status_failing() {
let mut status = X86CryptoFipsStatus::default_passing();
status.aes_kat_passed = false;
status.test_results[0].1 = false;
assert!(!status.is_fips_approved());
}
#[test]
fn test_fips_status_report() {
let status = X86CryptoFipsStatus::default_passing();
let report = status.report();
assert!(report.contains("FIPS 140-2"));
assert!(report.contains("AES KAT"));
assert!(report.contains("PASS"));
}
#[test]
fn test_fips_status_failed_tests() {
let mut status = X86CryptoFipsStatus::default_passing();
status.test_results[2].1 = false; let failed = status.failed_tests();
assert_eq!(failed.len(), 1);
assert_eq!(failed[0], "HMAC KAT");
}
#[test]
fn test_certificate_creation() {
let cert = X86CryptoCertificate::test_self_signed();
assert_eq!(cert.subject, "CN=Test Certificate");
assert_eq!(cert.key_algorithm, X86CryptoKeyAlgorithm::RSA);
assert!(cert.is_valid_now());
}
#[test]
fn test_certificate_chain_verification() {
let cert1 = X86CryptoCertificate::test_self_signed();
let mut cert2 = X86CryptoCertificate::test_self_signed();
cert2.subject = "CN=Intermediate".into();
cert2.issuer = "CN=Root".into();
let mut cert3 = X86CryptoCertificate::test_self_signed();
cert3.subject = "CN=Root".into();
cert3.issuer = "CN=Root".into();
let chain = vec![cert1.clone(), cert2.clone(), cert3.clone()];
let result = X86CryptoCertificate::verify_chain(&chain);
assert!(result.is_err());
}
#[test]
fn test_certificate_valid_chain() {
let mut root = X86CryptoCertificate::test_self_signed();
root.subject = "CN=Root".into();
root.issuer = "CN=Root".into();
let mut intermediate = X86CryptoCertificate::test_self_signed();
intermediate.subject = "CN=Intermediate".into();
intermediate.issuer = "CN=Root".into();
let mut leaf = X86CryptoCertificate::test_self_signed();
leaf.subject = "CN=Leaf".into();
leaf.issuer = "CN=Intermediate".into();
let chain = vec![leaf, intermediate, root];
assert!(X86CryptoCertificate::verify_chain(&chain).unwrap());
}
#[test]
fn test_registry_select_best_aes() {
let registry = X86CryptoRegistry::new(X86CryptoFeatureSet::all_features());
let best = registry.select_best("AES").unwrap();
assert_eq!(best.variant, "vaes");
}
#[test]
fn test_registry_select_best_baseline() {
let registry = X86CryptoRegistry::new(X86CryptoFeatureSet::baseline());
let best = registry.select_best("AES").unwrap();
assert_eq!(best.variant, "software");
}
#[test]
fn test_registry_select_best_aesni_only() {
let mut features = X86CryptoFeatureSet::baseline();
features.insert(X86CryptoFeature::AesNi);
features.insert(X86CryptoFeature::Pclmul);
let registry = X86CryptoRegistry::new(features);
let best = registry.select_best("AES").unwrap();
assert_eq!(best.variant, "aesni");
}
#[test]
fn test_registry_list_implementations() {
let registry = X86CryptoRegistry::new(X86CryptoFeatureSet::all_features());
let impls = registry.list_implementations("GHASH");
assert!(impls.len() >= 3); }
#[test]
fn test_registry_is_available() {
let registry = X86CryptoRegistry::new(X86CryptoFeatureSet::all_features());
assert!(registry.is_available("AES"));
assert!(registry.is_available("SHA-256"));
assert!(!registry.is_available("UnknownAlgorithm"));
}
#[test]
fn test_weak_key_size_detection() {
assert!(X86CryptoAntiPatterns::is_weak_key_size(1024, "RSA"));
assert!(!X86CryptoAntiPatterns::is_weak_key_size(2048, "RSA"));
assert!(X86CryptoAntiPatterns::is_weak_key_size(64, "AES"));
assert!(!X86CryptoAntiPatterns::is_weak_key_size(256, "AES"));
}
#[test]
fn test_deprecated_hash_detection() {
assert!(X86CryptoAntiPatterns::is_deprecated_hash("MD5"));
assert!(X86CryptoAntiPatterns::is_deprecated_hash("SHA-1"));
assert!(!X86CryptoAntiPatterns::is_deprecated_hash("SHA-256"));
assert!(!X86CryptoAntiPatterns::is_deprecated_hash("SHA-512"));
}
#[test]
fn test_ecb_warning() {
let warning = X86CryptoAntiPatterns::warn_ecb_mode("file_encrypt.c");
assert!(warning.is_some());
assert!(warning.unwrap().contains("ECB"));
}
#[test]
fn test_hardcoded_key_detection() {
let code = "static const char* key = \"mysecretkey\";";
let warnings = X86CryptoAntiPatterns::detect_hardcoded_key(code);
assert!(!warnings.is_empty());
}
#[test]
fn test_no_hardcoded_key_in_clean_code() {
let code = "char* key = get_key_from_env();";
let warnings = X86CryptoAntiPatterns::detect_hardcoded_key(code);
assert!(warnings.is_empty());
}
#[test]
fn test_non_ct_comparison_detection() {
let code = "if (memcmp(a, b, n) == 0) return OK;";
let warnings = X86CryptoAntiPatterns::detect_non_ct_comparison(code);
assert!(!warnings.is_empty());
}
#[test]
fn test_pbkdf2_iterations_check() {
assert!(X86CryptoAntiPatterns::check_pbkdf2_iterations(100).is_some());
assert!(X86CryptoAntiPatterns::check_pbkdf2_iterations(1000).is_some());
assert!(X86CryptoAntiPatterns::check_pbkdf2_iterations(600000).is_none());
}
#[test]
fn test_insecure_prng_detection() {
let code = "int r = rand() % 256;";
let warnings = X86CryptoAntiPatterns::detect_insecure_prng(code);
assert!(!warnings.is_empty());
}
#[test]
fn test_no_insecure_prng_in_clean_code() {
let code = "rdrand_bytes(&output);";
let warnings = X86CryptoAntiPatterns::detect_insecure_prng(code);
assert!(warnings.is_empty());
}
}