#![allow(non_upper_case_globals, dead_code, non_snake_case)]
use crate::security::*;
use crate::x86::*;
use std::arch::asm;
use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
use std::convert::TryFrom;
use std::fmt;
use std::mem;
use std::sync::atomic::{AtomicBool, AtomicU16, AtomicU32, AtomicU64, AtomicUsize, Ordering};
use std::sync::{Arc, Mutex, Once, RwLock};
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
pub const X86_CET_SHSTK_SIZE_PAGES: u64 = 4;
pub const X86_CET_SHSTK_TOKEN_SIZE: usize = 8;
pub const X86_CET_ENDBR64: [u8; 4] = [0xF3, 0x0F, 0x1E, 0xFA];
pub const X86_CET_ENDBR32: [u8; 4] = [0xF3, 0x0F, 0x1E, 0xFB];
pub const X86_SGX_PAGE_SIZE: u64 = 4096;
pub const X86_SGX_EPC_MIN_SIZE: u64 = 1 << 25;
pub const X86_SGX_SECS_SIZE: u64 = 4096;
pub const X86_SGX_TCS_SIZE: u64 = 4096;
pub const X86_SGX_SSA_FRAME_SIZE: u32 = 1;
pub const X86_SGX_MAX_ENCLAVE_SIZE: u64 = 1u64 << 35;
pub const X86_SGX2_MAX_ENCLAVE_SIZE: u64 = 512u64 << 30;
pub const X86_SGX_REPORT_MAC_SIZE: usize = 16;
pub const X86_SGX_KEY_REQUEST_SIZE: usize = 512;
pub const X86_SGX_KEYPOLICY_MRENCLAVE: u16 = 0x0001;
pub const X86_SGX_KEYPOLICY_MRSIGNER: u16 = 0x0002;
pub const X86_MPX_BND_COUNT: usize = 4;
pub const X86_MPX_BOUND_TABLE_ENTRY_SIZE: usize = 32;
pub const X86_MPK_KEY_COUNT: usize = 16;
pub const X86_MPK_DISABLE_ACCESS: u32 = 0b01;
pub const X86_MPK_DISABLE_WRITE: u32 = 0b10;
pub const X86_CR4_SMEP: u64 = 1 << 20;
pub const X86_CR4_SMAP: u64 = 1 << 21;
pub const X86_CR4_PKE: u64 = 1 << 22;
pub const X86_CR4_CET: u64 = 1 << 23;
pub const X86_CR4_PKS: u64 = 1 << 24;
pub const X86_CR0_WP: u64 = 1 << 16;
pub const X86_MSR_IA32_U_CET: u32 = 0x6A0;
pub const X86_MSR_IA32_S_CET: u32 = 0x6A2;
pub const X86_MSR_IA32_PL0_SSP: u32 = 0x6A4;
pub const X86_MSR_IA32_PL1_SSP: u32 = 0x6A5;
pub const X86_MSR_IA32_PL2_SSP: u32 = 0x6A6;
pub const X86_MSR_IA32_PL3_SSP: u32 = 0x6A7;
pub const X86_MSR_IA32_INTERRUPT_SSP_TABLE_ADDR: u32 = 0x6A8;
pub const X86_CET_SHSTK_EN: u64 = 1 << 0;
pub const X86_CET_WRSS_EN: u64 = 1 << 1;
pub const X86_CET_ENDBR_EN: u64 = 1 << 2;
pub const X86_CET_LEG_IW_EN: u64 = 1 << 3;
pub const X86_CET_NO_TRACK_EN: u64 = 1 << 4;
pub const X86_CET_SUP_DIS: u64 = 1 << 5;
pub const X86_CET_TRACKER_MASK: u64 = 0xFFFF_FFFF_0000_0000;
pub const X86_SGX_LEAF_ECREATE: u32 = 0x00;
pub const X86_SGX_LEAF_EADD: u32 = 0x01;
pub const X86_SGX_LEAF_EINIT: u32 = 0x02;
pub const X86_SGX_LEAF_EREMOVE: u32 = 0x03;
pub const X86_SGX_LEAF_EDBGRD: u32 = 0x04;
pub const X86_SGX_LEAF_EDBGWR: u32 = 0x05;
pub const X86_SGX_LEAF_EEXTEND: u32 = 0x06;
pub const X86_SGX_LEAF_ELDB: u32 = 0x07;
pub const X86_SGX_LEAF_ELDU: u32 = 0x08;
pub const X86_SGX_LEAF_EBLOCK: u32 = 0x09;
pub const X86_SGX_LEAF_EPA: u32 = 0x0A;
pub const X86_SGX_LEAF_EWB: u32 = 0x0B;
pub const X86_SGX_LEAF_ETRACK: u32 = 0x0C;
pub const X86_SGX_LEAF_EAUG: u32 = 0x0D;
pub const X86_SGX_LEAF_EMODPR: u32 = 0x0E;
pub const X86_SGX_LEAF_EMODT: u32 = 0x0F;
pub const X86_SGX_LEAF_EACCEPT: u32 = 0x05; pub const X86_SGX_LEAF_EACCEPTCOPY: u32 = 0x07; pub const X86_SGX_LEAF_EMODPE: u32 = 0x06;
pub const X86_SGX_SUCCESS: u32 = 0x0000_0000;
pub const X86_SGX_INVALID_SIG_STRUCT: u32 = 0x0000_0001;
pub const X86_SGX_INVALID_ATTRIBUTE: u32 = 0x0000_0002;
pub const X86_SGX_BLKSTATE: u32 = 0x0000_0003;
pub const X86_SGX_INVALID_MEASUREMENT: u32 = 0x0000_0004;
pub const X86_SGX_NOTBLOCKABLE: u32 = 0x0000_0005;
pub const X86_SGX_PG_INVLD: u32 = 0x0000_0006;
pub const X86_SGX_LOCK_FAIL: u32 = 0x0000_0007;
pub const X86_SGX_INVALID_SIGNATURE: u32 = 0x0000_0008;
pub const X86_SGX_EPC_PAGE_CONFLICT: u32 = 0x0000_0009;
pub const X86_SGX_NO_DEVICE: u32 = 0x0000_000A;
pub const X86_SGX_PAGE_NOT_MODIFIABLE: u32 = 0x0000_000B;
pub const X86_SGX_CHILD_PRESENT: u32 = 0x0000_000C;
pub const X86_SGX_INVALID_EINIT_TOKEN: u32 = 0x0000_000D;
pub const X86_SGX_MAC_COMPARE_FAIL: u32 = 0x0000_000E;
pub const X86_SGX_ENTRY_LOCKED: u32 = 0x0000_0013;
pub const X86_SGX_STACK_OVERRUN: u32 = 0x0000_0015;
pub const X86_MSR_IA32_ARCH_CAPABILITIES: u32 = 0x10A;
pub const X86_ARCH_CAP_RDCL_NO: u64 = 1 << 0;
pub const X86_ARCH_CAP_IBRS_ALL: u64 = 1 << 1;
pub const X86_ARCH_CAP_RSBA: u64 = 1 << 2;
pub const X86_ARCH_CAP_SKIP_L1DFL_VMENTRY: u64 = 1 << 3;
pub const X86_ARCH_CAP_SSB_NO: u64 = 1 << 4;
pub const X86_ARCH_CAP_MDS_NO: u64 = 1 << 5;
pub const X86_ARCH_CAP_PSCHANGE_MC_NO: u64 = 1 << 6;
pub const X86_ARCH_CAP_TSX_CTRL: u64 = 1 << 7;
pub const X86_ARCH_CAP_TAA_NO: u64 = 1 << 8;
pub const X86_MSR_IA32_SPEC_CTRL: u32 = 0x48;
pub const X86_SPEC_CTRL_IBRS: u64 = 1 << 0;
pub const X86_SPEC_CTRL_STIBP: u64 = 1 << 1;
pub const X86_SPEC_CTRL_SSBD: u64 = 1 << 2;
pub const X86_SPEC_CTRL_PSFD: u64 = 1 << 7;
pub const X86_MSR_IA32_PRED_CMD: u32 = 0x49;
pub const X86_PRED_CMD_IBPB: u64 = 1 << 0;
pub const X86_MSR_IA32_FLUSH_CMD: u32 = 0x10B;
pub const X86_FLUSH_CMD_L1D: u64 = 1 << 0;
pub const X86_MSR_IA32_TSX_CTRL: u32 = 0x122;
pub const X86_TSX_CTRL_RTM_DISABLE: u64 = 1 << 0;
pub const X86_TSX_CTRL_CPUID_CLEAR: u64 = 1 << 1;
pub const X86_MSR_IA32_MCU_OPT_CTRL: u32 = 0x123;
pub const X86_MCU_OPT_CTRL_RNGDS_MITG_DIS: u64 = 1 << 0;
pub const X86_PRED_CMD_SSBD: u64 = 1 << 0;
pub const X86_ASLR_ENTROPY_BITS_64: u32 = 28;
pub const X86_ASLR_ENTROPY_BITS_32: u32 = 8;
pub const X86_ASLR_STACK_RND_BITS_64: u32 = 16;
pub const X86_ASLR_MMAP_RND_BITS_64: u32 = 28;
pub const X86_ASLR_BRK_RND_BITS_64: u32 = 13;
pub const X86_STACK_CANARY_SIZE: usize = 8; pub const X86_STACK_CANARY_SIZE_32: usize = 4;
pub const X86_FORTIFY_BUF_SIZE_MIN: usize = 1;
pub const CHACHA20_CONSTANT: [u8; 16] = *b"expand 32-byte k";
pub const CHACHA20_KEY_SIZE: usize = 32;
pub const CHACHA20_NONCE_SIZE: usize = 12;
pub const CHACHA20_BLOCK_SIZE: usize = 64;
pub const CHACHA20_ROUNDS: usize = 20;
pub const POLY1305_KEY_SIZE: usize = 32;
pub const POLY1305_TAG_SIZE: usize = 16;
pub const POLY1305_BLOCK_SIZE: usize = 16;
pub const AES_BLOCK_SIZE: usize = 16;
pub const AES128_KEY_SIZE: usize = 16;
pub const AES192_KEY_SIZE: usize = 24;
pub const AES256_KEY_SIZE: usize = 32;
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct X86SecurityFeatures {
pub cet_ibt: bool,
pub cet_ss: bool,
pub sgx1: bool,
pub sgx2: bool,
pub sgx_lc: bool,
pub smap: bool,
pub smep: bool,
pub umip: bool,
pub pku: bool,
pub pks: bool,
pub rdrand: bool,
pub rdseed: bool,
pub aes_ni: bool,
pub sha_ni: bool,
pub ibpb: bool,
pub ibrs: bool,
pub stibp: bool,
pub ssbd: bool,
pub l1d_flush: bool,
pub md_clear: bool,
pub taa: bool,
pub tsx: bool,
pub retpoline: bool,
pub kpti: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86CETState {
Disabled,
IBTOnly,
SHSTKOnly,
FullProtection,
SupervisorOnly,
LegacyCompatibility,
}
impl fmt::Display for X86CETState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
X86CETState::Disabled => write!(f, "CET Disabled"),
X86CETState::IBTOnly => write!(f, "IBT Only"),
X86CETState::SHSTKOnly => write!(f, "Shadow Stack Only"),
X86CETState::FullProtection => write!(f, "IBT + Shadow Stack"),
X86CETState::SupervisorOnly => write!(f, "Supervisor CET Only"),
X86CETState::LegacyCompatibility => write!(f, "Legacy Compat Mode"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86SGXEnclaveState {
Uninitialized,
InProgress,
Initialized,
Sealed,
Destroyed,
}
impl fmt::Display for X86SGXEnclaveState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
X86SGXEnclaveState::Uninitialized => write!(f, "Uninitialized"),
X86SGXEnclaveState::InProgress => write!(f, "In Progress"),
X86SGXEnclaveState::Initialized => write!(f, "Initialized"),
X86SGXEnclaveState::Sealed => write!(f, "Sealed"),
X86SGXEnclaveState::Destroyed => write!(f, "Destroyed"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86SGXAttestationType {
Local,
RemoteEPID,
RemoteECDSA,
None,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86SideChannelVuln {
SpectreV1,
SpectreV2,
Meltdown,
L1TF,
MDS,
TAA,
SRBDS,
MMIO,
Retbleed,
}
impl fmt::Display for X86SideChannelVuln {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
X86SideChannelVuln::SpectreV1 => write!(f, "Spectre v1 (Bounds Check Bypass)"),
X86SideChannelVuln::SpectreV2 => write!(f, "Spectre v2 (Branch Target Injection)"),
X86SideChannelVuln::Meltdown => write!(f, "Meltdown (Rogue Data Cache Load)"),
X86SideChannelVuln::L1TF => write!(f, "L1 Terminal Fault"),
X86SideChannelVuln::MDS => write!(f, "Microarchitectural Data Sampling"),
X86SideChannelVuln::TAA => write!(f, "TSX Async Abort"),
X86SideChannelVuln::SRBDS => write!(f, "Special Register Buffer Data Sampling"),
X86SideChannelVuln::MMIO => write!(f, "MMIO Stale Data"),
X86SideChannelVuln::Retbleed => write!(f, "Retbleed (RSB Poisoning)"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86ExploitMitigationType {
StackProtector,
ASLR,
PIE,
RELRO,
NX,
FortifySource,
ForwardEdgeCFI,
BackwardEdgeCFI,
SafeStack,
ShadowCallStack,
}
impl fmt::Display for X86ExploitMitigationType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
X86ExploitMitigationType::StackProtector => write!(f, "Stack Protector (Canary)"),
X86ExploitMitigationType::ASLR => write!(f, "Address Space Layout Randomization"),
X86ExploitMitigationType::PIE => write!(f, "Position-Independent Executable"),
X86ExploitMitigationType::RELRO => write!(f, "Relocation Read-Only"),
X86ExploitMitigationType::NX => write!(f, "No-Execute (NX/W^X)"),
X86ExploitMitigationType::FortifySource => write!(f, "FORTIFY_SOURCE"),
X86ExploitMitigationType::ForwardEdgeCFI => write!(f, "Forward-Edge CFI"),
X86ExploitMitigationType::BackwardEdgeCFI => write!(f, "Backward-Edge CFI"),
X86ExploitMitigationType::SafeStack => write!(f, "SafeStack"),
X86ExploitMitigationType::ShadowCallStack => write!(f, "Shadow Call Stack"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86CryptoAlgorithm {
ChaCha20,
Poly1305,
ChaCha20Poly1305,
AES128,
AES256,
AES128GCM,
AES256GCM,
}
impl fmt::Display for X86CryptoAlgorithm {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
X86CryptoAlgorithm::ChaCha20 => write!(f, "ChaCha20"),
X86CryptoAlgorithm::Poly1305 => write!(f, "Poly1305"),
X86CryptoAlgorithm::ChaCha20Poly1305 => write!(f, "ChaCha20-Poly1305"),
X86CryptoAlgorithm::AES128 => write!(f, "AES-128"),
X86CryptoAlgorithm::AES256 => write!(f, "AES-256"),
X86CryptoAlgorithm::AES128GCM => write!(f, "AES-128-GCM"),
X86CryptoAlgorithm::AES256GCM => write!(f, "AES-256-GCM"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86MemorySafetyType {
MPX,
MPK,
SMAP,
SMEP,
UMIP,
PKRS,
}
impl fmt::Display for X86MemorySafetyType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
X86MemorySafetyType::MPX => write!(f, "Memory Protection Extensions"),
X86MemorySafetyType::MPK => write!(f, "Memory Protection Keys (PKU)"),
X86MemorySafetyType::SMAP => write!(f, "Supervisor Mode Access Prevention"),
X86MemorySafetyType::SMEP => write!(f, "Supervisor Mode Execution Prevention"),
X86MemorySafetyType::UMIP => write!(f, "User-Mode Instruction Prevention"),
X86MemorySafetyType::PKRS => write!(f, "Protection Key Rights Supervisor"),
}
}
}
#[derive(Debug, Clone)]
pub struct X86CETConfig {
pub ibt_available: bool,
pub shstk_available: bool,
pub wrss_available: bool,
pub cet_ss_available: bool,
pub shstk_size: u64,
pub ibt_tracker_active: bool,
pub supervisor_cet: bool,
pub user_cet: bool,
}
impl Default for X86CETConfig {
fn default() -> Self {
X86CETConfig {
ibt_available: false,
shstk_available: false,
wrss_available: false,
cet_ss_available: false,
shstk_size: X86_CET_SHSTK_SIZE_PAGES * 4096,
ibt_tracker_active: false,
supervisor_cet: false,
user_cet: false,
}
}
}
#[derive(Debug, Clone)]
pub struct X86IBTLandingPad {
pub address: u64,
pub function_name: String,
pub valid: bool,
pub endbr_bytes: [u8; 4],
}
#[derive(Debug, Clone)]
pub struct X86ShadowStackGuard {
pub vaddr: u64,
pub paddr: u64,
pub active: bool,
}
#[derive(Debug, Clone)]
pub struct X86ShadowStack {
pub base_vaddr: u64,
pub ssp: u64,
pub limit: u64,
pub size: u64,
pub token: u64,
pub guard_pages: usize,
pub thread_id: u64,
pub active: bool,
}
pub struct X86CET {
pub config: X86CETConfig,
pub state: X86CETState,
pub landing_pads: Vec<X86IBTLandingPad>,
pub shadow_stacks: HashMap<u64, X86ShadowStack>,
pub supervisor_ssp: u64,
pub interrupt_ssp_table: u64,
pub violations: u64,
pub endbr_inserted: usize,
pub shstk_created: usize,
}
impl X86CET {
pub fn new() -> Self {
let features = Self::probe_cet_features();
let config = X86CETConfig {
ibt_available: features.cet_ibt,
shstk_available: features.cet_ss,
..Default::default()
};
let state = if features.cet_ibt && features.cet_ss {
X86CETState::FullProtection
} else if features.cet_ibt {
X86CETState::IBTOnly
} else if features.cet_ss {
X86CETState::SHSTKOnly
} else {
X86CETState::Disabled
};
X86CET {
config,
state,
landing_pads: Vec::new(),
shadow_stacks: HashMap::new(),
supervisor_ssp: 0,
interrupt_ssp_table: 0,
violations: 0,
endbr_inserted: 0,
shstk_created: 0,
}
}
pub fn probe_cet_features() -> X86SecurityFeatures {
let mut features = X86SecurityFeatures::default();
#[cfg(target_arch = "x86_64")]
{
let result = unsafe { core::arch::x86_64::__cpuid_count(7, 0) };
features.cet_ibt = (result.ebx & (1 << 20)) != 0;
features.cet_ss = (result.ecx & (1 << 7)) != 0;
}
features
}
pub fn get_state(&self) -> X86CETState {
self.state
}
pub fn is_cet_enabled(&self) -> bool {
self.state != X86CETState::Disabled
}
pub fn is_full_protection(&self) -> bool {
self.state == X86CETState::FullProtection
}
pub unsafe fn insert_endbr64(&mut self, addr: u64, func_name: &str) -> bool {
if !self.config.ibt_available {
return false;
}
let lp = X86IBTLandingPad {
address: addr,
function_name: func_name.to_string(),
valid: true,
endbr_bytes: X86_CET_ENDBR64,
};
self.landing_pads.push(lp);
self.endbr_inserted += 1;
true
}
pub unsafe fn insert_endbr32(&mut self, addr: u64, func_name: &str) -> bool {
if !self.config.ibt_available {
return false;
}
let lp = X86IBTLandingPad {
address: addr,
function_name: func_name.to_string(),
valid: true,
endbr_bytes: X86_CET_ENDBR32,
};
self.landing_pads.push(lp);
self.endbr_inserted += 1;
true
}
pub fn generate_endbr64_bytes() -> [u8; 4] {
X86_CET_ENDBR64
}
pub fn generate_endbr32_bytes() -> [u8; 4] {
X86_CET_ENDBR32
}
pub fn verify_landing_pad(&self, addr: u64) -> bool {
self.landing_pads
.iter()
.any(|lp| lp.address == addr && lp.valid)
}
pub fn emit_notrack_prefix(&self, is_64bit: bool) -> Option<u8> {
if self.config.ibt_available {
Some(0x3E) } else {
None
}
}
pub fn set_ibt_tracker(&mut self, active: bool) {
self.config.ibt_tracker_active = active;
}
pub fn is_ibt_tracker_waiting(&self) -> bool {
self.config.ibt_tracker_active
}
pub fn configure_ibt_user(&self, enable: bool) -> u64 {
if enable {
X86_CET_ENDBR_EN | X86_CET_NO_TRACK_EN
} else {
0
}
}
pub fn configure_legacy_compat(&self, enable: bool) -> u64 {
if enable {
X86_CET_LEG_IW_EN
} else {
0
}
}
pub unsafe fn create_shadow_stack(
&mut self,
thread_id: u64,
stack_size: u64,
) -> Result<u64, &'static str> {
if !self.config.shstk_available {
return Err("Shadow stack not available");
}
let size = stack_size.max(X86_CET_SHSTK_SIZE_PAGES * 4096);
let size = (size + 4095) & !4095;
let token = self.generate_shstk_token(thread_id);
let mut ss = X86ShadowStack {
base_vaddr: 0, ssp: 0,
limit: 0,
size,
token,
guard_pages: 1,
thread_id,
active: true,
};
ss.base_vaddr = 0x7F00_0000_0000 + (thread_id * 0x1000_0000);
ss.ssp = ss.base_vaddr + size - X86_CET_SHSTK_TOKEN_SIZE;
ss.limit = ss.base_vaddr;
let ssp = ss.ssp;
self.shadow_stacks.insert(thread_id, ss);
self.shstk_created += 1;
Ok(ssp)
}
fn generate_shstk_token(&self, thread_id: u64) -> u64 {
thread_id ^ 0xDEAD_C0DE_CAFE_BABE
}
#[inline(always)]
pub unsafe fn read_ssp(&self) -> u64 {
let ssp: u64;
#[cfg(target_arch = "x86_64")]
{
asm!("rdsspq {}", out(reg) ssp, options(nostack, nomem));
}
#[cfg(not(target_arch = "x86_64"))]
{
ssp = 0;
}
ssp
}
pub unsafe fn inc_ssp(&self, count: u64) {
#[cfg(target_arch = "x86_64")]
{
asm!("incsspq {}", in(reg) count, options(nostack, nomem));
}
let _ = count;
}
pub unsafe fn write_shadow_stack(&self, data: u64, offset: u64) {
#[cfg(target_arch = "x86_64")]
{
let ssp = self.read_ssp();
let addr = ssp.wrapping_sub(offset);
asm!(
"wrssq {}, {}",
in(reg) data,
in(reg) addr,
options(nostack)
);
}
let _ = (data, offset);
}
pub fn save_cet_supervisor_state(&self) -> X86CETSupervisorState {
X86CETSupervisorState {
pl0_ssp: 0,
pl1_ssp: 0,
pl2_ssp: 0,
pl3_ssp: 0,
interrupt_ssp_table: self.interrupt_ssp_table,
u_cet: 0,
s_cet: 0,
}
}
pub fn restore_cet_supervisor_state(&mut self, state: &X86CETSupervisorState) {
self.supervisor_ssp = state.pl0_ssp;
self.interrupt_ssp_table = state.interrupt_ssp_table;
}
pub fn setup_interrupt_ssp_table(
&mut self,
table_addr: u64,
num_entries: usize,
) -> Result<(), &'static str> {
if !self.config.cet_ss_available {
return Err("CET-SS not available");
}
if table_addr & 7 != 0 {
return Err("Interrupt SSP table must be 8-byte aligned");
}
self.interrupt_ssp_table = table_addr;
Ok(())
}
pub fn configure_u_cet(
&self,
enable_shstk: bool,
enable_ibt: bool,
legacy_compat: bool,
) -> u64 {
let mut value: u64 = 0;
if enable_shstk && self.config.shstk_available {
value |= X86_CET_SHSTK_EN | X86_CET_WRSS_EN;
}
if enable_ibt && self.config.ibt_available {
value |= X86_CET_ENDBR_EN | X86_CET_NO_TRACK_EN;
}
if legacy_compat {
value |= X86_CET_LEG_IW_EN;
}
value
}
pub fn configure_s_cet(&self, enable_shstk: bool, enable_ibt: bool) -> u64 {
let mut value: u64 = 0;
if enable_shstk {
value |= X86_CET_SHSTK_EN | X86_CET_WRSS_EN;
}
if enable_ibt {
value |= X86_CET_ENDBR_EN | X86_CET_NO_TRACK_EN;
}
value
}
pub fn destroy_shadow_stack(&mut self, thread_id: u64) -> bool {
self.shadow_stacks.remove(&thread_id).is_some()
}
pub fn active_shadow_stacks(&self) -> usize {
self.shadow_stacks.iter().filter(|(_, s)| s.active).count()
}
pub fn is_shadow_stack_address(&self, addr: u64) -> bool {
self.shadow_stacks
.values()
.any(|s| s.active && addr >= s.base_vaddr && addr < s.base_vaddr + s.size)
}
pub fn handle_cp_fault(&mut self, fault_addr: u64, error_code: u64) -> bool {
self.violations += 1;
let is_ibt_violation = (error_code & 0x5) != 0; let is_shstk_violation = (error_code & 0x2) != 0; is_ibt_violation || is_shstk_violation
}
}
impl Default for X86CET {
fn default() -> Self {
X86CET::new()
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct X86CETSupervisorState {
pub pl0_ssp: u64,
pub pl1_ssp: u64,
pub pl2_ssp: u64,
pub pl3_ssp: u64,
pub interrupt_ssp_table: u64,
pub u_cet: u64,
pub s_cet: u64,
}
pub type X86SGXMeasurement = [u8; 32];
pub type X86SGXSignature = [u8; 384];
#[derive(Debug, Clone, Copy, Default)]
pub struct X86SGXAttributes {
pub debug: bool,
pub mode64bit: bool,
pub provision_key: bool,
pub einittoken_key: bool,
pub cet: bool,
pub kss: bool,
}
impl X86SGXAttributes {
pub fn to_flags(&self) -> u64 {
let mut flags: u64 = 0;
if self.debug {
flags |= 0x2;
}
if self.mode64bit {
flags |= 0x4;
}
if self.provision_key {
flags |= 0x10;
}
if self.einittoken_key {
flags |= 0x20;
}
if self.cet {
flags |= 0x40;
}
if self.kss {
flags |= 0x80;
}
flags
}
pub fn from_flags(flags: u64) -> Self {
X86SGXAttributes {
debug: (flags & 0x2) != 0,
mode64bit: (flags & 0x4) != 0,
provision_key: (flags & 0x10) != 0,
einittoken_key: (flags & 0x20) != 0,
cet: (flags & 0x40) != 0,
kss: (flags & 0x80) != 0,
}
}
}
#[derive(Debug, Clone)]
pub struct X86SGXEnclaveConfig {
pub base_address: u64,
pub size: u64,
pub attributes: X86SGXAttributes,
pub xfrm: u64,
pub mrsigner: X86SGXMeasurement,
pub isv_prod_id: u16,
pub isv_svn: u16,
pub debug: bool,
pub stack_min_size: u64,
pub heap_min_size: u64,
pub tcs_num: u32,
pub sgx2_enabled: bool,
}
impl Default for X86SGXEnclaveConfig {
fn default() -> Self {
X86SGXEnclaveConfig {
base_address: 0,
size: 0x100000, attributes: X86SGXAttributes {
mode64bit: true,
..Default::default()
},
xfrm: 0x3, mrsigner: [0u8; 32],
isv_prod_id: 0,
isv_svn: 0,
debug: false,
stack_min_size: 0x4000, heap_min_size: 0x10000, tcs_num: 1,
sgx2_enabled: false,
}
}
}
#[derive(Debug, Clone)]
pub struct X86EPCPage {
pub enclave_offset: u64,
pub epc_address: u64,
pub va_address: u64,
pub page_type: X86EPCPageType,
pub permissions: u8,
pub in_epc: bool,
pub accepted: bool,
pub content_hash: [u8; 32],
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86EPCPageType {
SECS,
TCS,
REG,
VA,
TRIM,
}
impl fmt::Display for X86EPCPageType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
X86EPCPageType::SECS => write!(f, "SECS"),
X86EPCPageType::TCS => write!(f, "TCS"),
X86EPCPageType::REG => write!(f, "REG"),
X86EPCPageType::VA => write!(f, "VA"),
X86EPCPageType::TRIM => write!(f, "TRIM"),
}
}
}
#[derive(Debug, Clone)]
pub struct X86SGXKeyRequest {
pub key_name: u16,
pub key_policy: u16,
pub isv_svn: u16,
pub extended_prod_id: [u8; 16],
pub attribute_mask: [u8; 16],
pub key_id: [u8; 32],
pub cpu_svn: [u8; 16],
pub misc_select: u32,
pub config_svn: u16,
pub config_id: [u8; 64],
}
impl Default for X86SGXKeyRequest {
fn default() -> Self {
X86SGXKeyRequest {
key_name: 0,
key_policy: X86_SGX_KEYPOLICY_MRENCLAVE,
isv_svn: 0,
extended_prod_id: [0u8; 16],
attribute_mask: [0u8; 16],
key_id: [0u8; 32],
cpu_svn: [0u8; 16],
misc_select: 0,
config_svn: 0,
config_id: [0u8; 64],
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct X86SGXReport {
pub cpu_svn: [u8; 16],
pub misc_select: u32,
pub extended_prod_id: [u8; 16],
pub attributes: [u8; 16],
pub mrenclave: X86SGXMeasurement,
pub mrsigner: X86SGXMeasurement,
pub config_id: [u8; 64],
pub isv_prod_id: u16,
pub isv_svn: u16,
pub config_svn: u16,
pub isv_family_id: [u8; 16],
pub report_data: [u8; 64],
pub mac: [u8; X86_SGX_REPORT_MAC_SIZE],
}
impl Default for X86SGXReport {
fn default() -> Self {
X86SGXReport {
cpu_svn: [0u8; 16],
misc_select: 0,
extended_prod_id: [0u8; 16],
attributes: [0u8; 16],
mrenclave: [0u8; 32],
mrsigner: [0u8; 32],
config_id: [0u8; 64],
isv_prod_id: 0,
isv_svn: 0,
config_svn: 0,
isv_family_id: [0u8; 16],
report_data: [0u8; 64],
mac: [0u8; 16],
}
}
}
#[derive(Debug, Clone)]
pub struct X86SGXTargetInfo {
pub mrenclave: X86SGXMeasurement,
pub attributes: [u8; 16],
pub extended_prod_id: [u8; 16],
pub config_id: [u8; 64],
}
#[derive(Debug, Clone)]
pub struct X86SGXQuote {
pub version: u16,
pub sign_type: u16,
pub epid_group_id: u32,
pub qe_report: X86SGXReport,
pub signature: Vec<u8>,
pub attestation_key: Vec<u8>,
pub report: X86SGXReport,
}
#[derive(Debug, Clone)]
pub struct X86SGXSealedData {
pub key_policy: u16,
pub ciphertext: Vec<u8>,
pub tag: [u8; 16],
pub aad: Vec<u8>,
pub payload_size: u32,
}
pub struct X86SGX {
pub sgx1_available: bool,
pub sgx2_available: bool,
pub launch_control: bool,
pub config: X86SGXEnclaveConfig,
pub enclave_state: X86SGXEnclaveState,
pub mrenclave: X86SGXMeasurement,
pub epc_pages: Vec<X86EPCPage>,
pub secs_page: Option<X86EPCPage>,
pub epc_size: u64,
pub epc_used: u64,
pub sealed_blobs: Vec<X86SGXSealedData>,
pub local_reports: Vec<X86SGXReport>,
pub remote_quotes: Vec<X86SGXQuote>,
}
impl X86SGX {
pub fn new() -> Self {
let features = Self::probe_sgx_features();
X86SGX {
sgx1_available: features,
sgx2_available: false,
launch_control: false,
config: X86SGXEnclaveConfig::default(),
enclave_state: X86SGXEnclaveState::Uninitialized,
mrenclave: [0u8; 32],
epc_pages: Vec::new(),
secs_page: None,
epc_size: Self::get_epc_size(),
epc_used: 0,
sealed_blobs: Vec::new(),
local_reports: Vec::new(),
remote_quotes: Vec::new(),
}
}
pub fn probe_sgx_features() -> bool {
let mut available = false;
#[cfg(target_arch = "x86_64")]
{
let result = unsafe { core::arch::x86_64::__cpuid_count(7, 0) };
available = (result.ebx & (1 << 2)) != 0;
if available {
let sgx_info = unsafe { core::arch::x86_64::__cpuid_count(0x12, 0) };
let _sgx1 = (sgx_info.eax & 0x1) != 0;
let _sgx2 = (sgx_info.eax & 0x2) != 0;
}
}
available
}
pub fn get_epc_size() -> u64 {
#[cfg(target_arch = "x86_64")]
{
let result = unsafe { core::arch::x86_64::__cpuid_count(0x12, 0) };
((result.ebx as u64) & 0xFFFF_F000) as u64
}
#[cfg(not(target_arch = "x86_64"))]
{
0
}
}
pub fn ecreate(&mut self) -> Result<u64, u32> {
if !self.sgx1_available {
return Err(X86_SGX_NO_DEVICE);
}
if self.enclave_state != X86SGXEnclaveState::Uninitialized {
return Err(X86_SGX_ENTRY_LOCKED);
}
let secs = X86EPCPage {
enclave_offset: 0,
epc_address: self.config.base_address,
va_address: 0,
page_type: X86EPCPageType::SECS,
permissions: 0,
in_epc: true,
accepted: true,
content_hash: [0u8; 32],
};
self.secs_page = Some(secs);
self.epc_pages.push(X86EPCPage {
enclave_offset: 0,
epc_address: self.config.base_address,
va_address: 0,
page_type: X86EPCPageType::SECS,
permissions: 0,
in_epc: true,
accepted: true,
content_hash: [0u8; 32],
});
self.epc_used += X86_SGX_PAGE_SIZE;
self.enclave_state = X86SGXEnclaveState::InProgress;
self.mrenclave = [0u8; 32];
Ok(self.config.base_address)
}
pub fn eadd(
&mut self,
enclave_offset: u64,
data: &[u8],
page_type: X86EPCPageType,
permissions: u8,
) -> Result<u64, u32> {
if self.enclave_state != X86SGXEnclaveState::InProgress {
return Err(X86_SGX_ENTRY_LOCKED);
}
if data.len() > X86_SGX_PAGE_SIZE as usize {
return Err(X86_SGX_PG_INVLD);
}
let epc_addr = self.config.base_address + enclave_offset;
if self.epc_used + X86_SGX_PAGE_SIZE > self.epc_size {
return Err(X86_SGX_EPC_PAGE_CONFLICT);
}
let mut padded = [0u8; X86_SGX_PAGE_SIZE as usize];
let copy_len = data.len().min(padded.len());
padded[..copy_len].copy_from_slice(&data[..copy_len]);
let hash = self.hash_sha256(&padded);
let page = X86EPCPage {
enclave_offset,
epc_address: epc_addr,
va_address: 0,
page_type,
permissions,
in_epc: true,
accepted: true,
content_hash: hash,
};
self.epc_pages.push(page);
self.epc_used += X86_SGX_PAGE_SIZE;
self.eextend_measurement(enclave_offset, &padded);
Ok(epc_addr)
}
fn eextend_measurement(&mut self, _offset: u64, data: &[u8; 4096]) {
let page_hash = self.hash_sha256(data);
let mut mix_buffer = [0u8; 32 + 32 + 8];
mix_buffer[0..32].copy_from_slice(&self.mrenclave);
mix_buffer[32..64].copy_from_slice(&page_hash);
let offset_bytes = _offset.to_le_bytes();
mix_buffer[64..72].copy_from_slice(&offset_bytes);
self.mrenclave = self.hash_sha256(&mix_buffer);
}
pub fn einit(&mut self, sigstruct: &[u8]) -> Result<(), u32> {
if self.enclave_state != X86SGXEnclaveState::InProgress {
return Err(X86_SGX_ENTRY_LOCKED);
}
if sigstruct.len() < 1808 {
return Err(X86_SGX_INVALID_SIG_STRUCT);
}
let expected_mrenclave: [u8; 32] = sigstruct[128..160].try_into().unwrap_or([0u8; 32]);
if expected_mrenclave != self.mrenclave {
return Err(X86_SGX_INVALID_MEASUREMENT);
}
self.enclave_state = X86SGXEnclaveState::Initialized;
Ok(())
}
pub unsafe fn eenter(&self, tcs_addr: u64) -> Result<(), u32> {
if self.enclave_state != X86SGXEnclaveState::Initialized {
return Err(X86_SGX_ENTRY_LOCKED);
}
if tcs_addr < self.config.base_address
|| tcs_addr >= self.config.base_address + self.config.size
{
return Err(X86_SGX_PG_INVLD);
}
Ok(())
}
pub fn eexit(&self, target_addr: u64) -> Result<(), u32> {
if self.enclave_state != X86SGXEnclaveState::Initialized {
return Err(X86_SGX_ENTRY_LOCKED);
}
let _ = target_addr;
Ok(())
}
pub fn eresume(&self, tcs_addr: u64) -> Result<(), u32> {
if self.enclave_state != X86SGXEnclaveState::Initialized {
return Err(X86_SGX_ENTRY_LOCKED);
}
let _ = tcs_addr;
Ok(())
}
pub fn handle_aex(&self, tcs_addr: u64, exit_reason: u64) -> X86SGXAEXInfo {
X86SGXAEXInfo {
tcs_address: tcs_addr,
exit_reason,
ssa_frame_index: 0,
handling_recommendation: match exit_reason {
0 => "Continue execution".to_string(),
3 => "Page fault — may need EPC page-in".to_string(),
14 => "General protection — invalid enclave access".to_string(),
_ => "Unknown AEX reason".to_string(),
},
}
}
pub fn ereport(
&mut self,
target_info: &X86SGXTargetInfo,
report_data: &[u8; 64],
) -> Result<X86SGXReport, u32> {
if self.enclave_state != X86SGXEnclaveState::Initialized {
return Err(X86_SGX_ENTRY_LOCKED);
}
let mut report = X86SGXReport {
mrenclave: self.mrenclave,
mrsigner: self.config.mrsigner,
isv_prod_id: self.config.isv_prod_id,
isv_svn: self.config.isv_svn,
report_data: *report_data,
attributes: [0u8; 16],
..Default::default()
};
let attr_flags = self.config.attributes.to_flags();
report.attributes[..8].copy_from_slice(&attr_flags.to_le_bytes());
let mac = self.compute_report_mac(&report, target_info);
report.mac = mac;
self.local_reports.push(report);
Ok(report)
}
fn compute_report_mac(
&self,
report: &X86SGXReport,
_target_info: &X86SGXTargetInfo,
) -> [u8; 16] {
let mut mac_input = Vec::new();
mac_input.extend_from_slice(&report.cpu_svn);
mac_input.extend_from_slice(&report.mrenclave);
mac_input.extend_from_slice(&report.mrsigner);
mac_input.extend_from_slice(&report.report_data);
let hash = self.hash_sha256(&mac_input);
let mut mac = [0u8; 16];
mac.copy_from_slice(&hash[..16]);
mac
}
pub fn generate_quote(
&mut self,
report: &X86SGXReport,
attestation_type: X86SGXAttestationType,
) -> Result<X86SGXQuote, u32> {
let quote = X86SGXQuote {
version: 3,
sign_type: match attestation_type {
X86SGXAttestationType::RemoteEPID => 0, X86SGXAttestationType::RemoteECDSA => 2, _ => return Err(X86_SGX_INVALID_ATTRIBUTE),
},
epid_group_id: 0,
qe_report: X86SGXReport::default(),
signature: Vec::new(),
attestation_key: Vec::new(),
report: *report,
};
self.remote_quotes.push(quote.clone());
Ok(quote)
}
pub fn verify_quote(&self, quote: &X86SGXQuote) -> Result<bool, &'static str> {
let _ = quote;
Ok(true)
}
pub fn get_target_info(&self) -> X86SGXTargetInfo {
X86SGXTargetInfo {
mrenclave: self.mrenclave,
attributes: {
let mut attrs = [0u8; 16];
let flags = self.config.attributes.to_flags();
attrs[..8].copy_from_slice(&flags.to_le_bytes());
attrs
},
extended_prod_id: [0u8; 16],
config_id: [0u8; 64],
}
}
pub fn egetkey(&self, key_request: &X86SGXKeyRequest) -> Result<[u8; 16], u32> {
if self.enclave_state != X86SGXEnclaveState::Initialized {
return Err(X86_SGX_ENTRY_LOCKED);
}
let mut derived_key = [0u8; 16];
let mut derivation_input = Vec::new();
derivation_input.extend_from_slice(&key_request.key_name.to_le_bytes());
derivation_input.extend_from_slice(&key_request.key_policy.to_le_bytes());
if key_request.key_policy & X86_SGX_KEYPOLICY_MRENCLAVE != 0 {
derivation_input.extend_from_slice(&self.mrenclave);
}
if key_request.key_policy & X86_SGX_KEYPOLICY_MRSIGNER != 0 {
derivation_input.extend_from_slice(&self.config.mrsigner);
}
derivation_input.extend_from_slice(&key_request.key_id);
let hash = self.hash_sha256(&derivation_input);
derived_key.copy_from_slice(&hash[..16]);
Ok(derived_key)
}
pub fn seal_data(
&mut self,
plaintext: &[u8],
key_policy: u16,
aad: &[u8],
) -> Result<X86SGXSealedData, u32> {
let key_request = X86SGXKeyRequest {
key_name: 0x0001, key_policy,
..Default::default()
};
let key = self.egetkey(&key_request)?;
let sealed = self.seal_with_key(plaintext, &key, aad);
self.sealed_blobs.push(sealed.clone());
Ok(sealed)
}
pub fn unseal_data(&self, sealed: &X86SGXSealedData, aad: &[u8]) -> Result<Vec<u8>, u32> {
let key_request = X86SGXKeyRequest {
key_name: 0x0001,
key_policy: sealed.key_policy,
..Default::default()
};
let key = self.egetkey(&key_request)?;
self.unseal_with_key(sealed, &key, aad)
}
fn seal_with_key(&self, plaintext: &[u8], key: &[u8; 16], aad: &[u8]) -> X86SGXSealedData {
let mut ciphertext = plaintext.to_vec();
for (i, byte) in ciphertext.iter_mut().enumerate() {
*byte ^= key[i % 16]
^ aad
.get(i.wrapping_rem(aad.len().max(1)))
.copied()
.unwrap_or(0);
}
let mut tag = [0u8; 16];
let tag_input: Vec<u8> = ciphertext.iter().chain(aad.iter()).copied().collect();
let hash = self.hash_sha256(&tag_input);
tag.copy_from_slice(&hash[..16]);
X86SGXSealedData {
key_policy: X86_SGX_KEYPOLICY_MRENCLAVE,
ciphertext,
tag,
aad: aad.to_vec(),
payload_size: plaintext.len() as u32,
}
}
fn unseal_with_key(
&self,
sealed: &X86SGXSealedData,
key: &[u8; 16],
aad: &[u8],
) -> Result<Vec<u8>, u32> {
let mut tag_input: Vec<u8> = sealed
.ciphertext
.iter()
.chain(aad.iter())
.copied()
.collect();
let hash = self.hash_sha256(&tag_input);
let expected_tag = &hash[..16];
if expected_tag != sealed.tag {
return Err(X86_SGX_MAC_COMPARE_FAIL);
}
let mut plaintext = sealed.ciphertext.clone();
for (i, byte) in plaintext.iter_mut().enumerate() {
*byte ^= key[i % 16]
^ aad
.get(i.wrapping_rem(aad.len().max(1)))
.copied()
.unwrap_or(0);
}
plaintext.truncate(sealed.payload_size as usize);
Ok(plaintext)
}
pub fn epa(&mut self, page_type: X86EPCPageType) -> Result<u64, u32> {
if self.epc_used + X86_SGX_PAGE_SIZE > self.epc_size {
return Err(X86_SGX_EPC_PAGE_CONFLICT);
}
let epc_addr = X86_SGX_EPC_MIN_SIZE + self.epc_used;
let page = X86EPCPage {
enclave_offset: self.epc_used,
epc_address: epc_addr,
va_address: 0,
page_type,
permissions: 0,
in_epc: true,
accepted: true,
content_hash: [0u8; 32],
};
let addr = page.epc_address;
self.epc_pages.push(page);
self.epc_used += X86_SGX_PAGE_SIZE;
Ok(addr)
}
pub fn ewb(&mut self, epc_address: u64, va_address: u64) -> Result<Vec<u8>, u32> {
if let Some(page) = self
.epc_pages
.iter_mut()
.find(|p| p.epc_address == epc_address && p.in_epc)
{
page.in_epc = false;
page.va_address = va_address;
Ok(vec![0u8; X86_SGX_PAGE_SIZE as usize])
} else {
Err(X86_SGX_PG_INVLD)
}
}
pub fn eldb(
&mut self,
epc_address: u64,
encrypted_data: &[u8],
va_address: u64,
) -> Result<(), u32> {
if let Some(page) = self
.epc_pages
.iter_mut()
.find(|p| p.epc_address == epc_address)
{
if page.in_epc {
return Err(X86_SGX_EPC_PAGE_CONFLICT);
}
page.in_epc = true;
let _ = (encrypted_data, va_address);
Ok(())
} else {
Err(X86_SGX_PG_INVLD)
}
}
pub fn is_sgx2_available(&self) -> bool {
self.sgx2_available
}
pub fn eaug(&mut self, enclave_offset: u64, page_type: X86EPCPageType) -> Result<u64, u32> {
if !self.sgx2_available {
return Err(X86_SGX_NO_DEVICE);
}
if self.enclave_state != X86SGXEnclaveState::Initialized {
return Err(X86_SGX_ENTRY_LOCKED);
}
let epc_addr = self.config.base_address + enclave_offset;
let page = X86EPCPage {
enclave_offset,
epc_address: epc_addr,
va_address: 0,
page_type,
permissions: 0,
in_epc: true,
accepted: false, content_hash: [0u8; 32],
};
let addr = page.epc_address;
self.epc_pages.push(page);
Ok(addr)
}
pub fn emodpr(&mut self, enclave_offset: u64, new_permissions: u8) -> Result<(), u32> {
if !self.sgx2_available {
return Err(X86_SGX_NO_DEVICE);
}
if let Some(page) = self
.epc_pages
.iter_mut()
.find(|p| p.enclave_offset == enclave_offset)
{
if (new_permissions & !page.permissions) != 0 {
return Err(X86_SGX_PAGE_NOT_MODIFIABLE);
}
page.permissions = new_permissions;
page.accepted = false; Ok(())
} else {
Err(X86_SGX_PG_INVLD)
}
}
pub fn emodt(&mut self, enclave_offset: u64, new_type: X86EPCPageType) -> Result<(), u32> {
if !self.sgx2_available {
return Err(X86_SGX_NO_DEVICE);
}
if let Some(page) = self
.epc_pages
.iter_mut()
.find(|p| p.enclave_offset == enclave_offset)
{
if page.page_type == X86EPCPageType::SECS {
return Err(X86_SGX_PAGE_NOT_MODIFIABLE);
}
page.page_type = new_type;
page.accepted = false;
Ok(())
} else {
Err(X86_SGX_PG_INVLD)
}
}
pub fn eaccept(&mut self, enclave_offset: u64) -> Result<(), u32> {
if let Some(page) = self
.epc_pages
.iter_mut()
.find(|p| p.enclave_offset == enclave_offset)
{
if page.accepted {
return Ok(());
}
page.accepted = true;
Ok(())
} else {
Err(X86_SGX_PG_INVLD)
}
}
pub fn eacceptcopy(&mut self, dest_offset: u64, src_offset: u64) -> Result<(), u32> {
self.eaccept(dest_offset)?;
let _ = src_offset;
Ok(())
}
fn hash_sha256(&self, data: &[u8]) -> [u8; 32] {
let mut hash = [0u8; 32];
let mut state: [u32; 8] = [
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab,
0x5be0cd19,
];
let mut padded = data.to_vec();
let bit_len = (data.len() as u64) * 8;
padded.push(0x80);
while (padded.len() % 64) != 56 {
padded.push(0);
}
padded.extend_from_slice(&bit_len.to_be_bytes());
for block in padded.chunks(64) {
let mut w = [0u32; 64];
for i in 0..16 {
w[i] = u32::from_be_bytes([
block[i * 4],
block[i * 4 + 1],
block[i * 4 + 2],
block[i * 4 + 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(K_SHA256[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);
}
for i in 0..8 {
let bytes = state[i].to_be_bytes();
hash[i * 4..(i + 1) * 4].copy_from_slice(&bytes);
}
hash
}
pub fn get_mrenclave(&self) -> X86SGXMeasurement {
self.mrenclave
}
pub fn get_stats(&self) -> X86SGXStats {
X86SGXStats {
enclave_state: self.enclave_state,
epc_pages: self.epc_pages.len(),
epc_used: self.epc_used,
epc_total: self.epc_size,
local_reports: self.local_reports.len(),
remote_quotes: self.remote_quotes.len(),
sealed_blobs: self.sealed_blobs.len(),
}
}
}
impl Default for X86SGX {
fn default() -> Self {
X86SGX::new()
}
}
const K_SHA256: [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,
];
#[derive(Debug, Clone, Default)]
pub struct X86SGXStats {
pub enclave_state: X86SGXEnclaveState,
pub epc_pages: usize,
pub epc_used: u64,
pub epc_total: u64,
pub local_reports: usize,
pub remote_quotes: usize,
pub sealed_blobs: usize,
}
#[derive(Debug, Clone)]
pub struct X86SGXAEXInfo {
pub tcs_address: u64,
pub exit_reason: u64,
pub ssa_frame_index: u32,
pub handling_recommendation: String,
}
#[derive(Debug, Clone, Copy)]
pub struct X86MPXBounds {
pub lower_bound: u64,
pub upper_bound: u64,
pub valid: bool,
}
impl Default for X86MPXBounds {
fn default() -> Self {
X86MPXBounds {
lower_bound: 0,
upper_bound: 0,
valid: false,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct X86MPXBoundTableEntry {
pub lower_bound: u64,
pub upper_bound: u64,
pub reserved: u64,
pub pointer_value: u64,
}
#[derive(Debug, Clone, Copy)]
pub struct X86MPKKeyState {
pub index: u8,
pub access_disabled: bool,
pub write_disabled: bool,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct X86PKRSState {
pub pkrs: u32,
}
pub struct X86MemorySafety {
pub mpx_available: bool,
pub pku_available: bool,
pub pks_available: bool,
pub smap_available: bool,
pub smep_available: bool,
pub umip_available: bool,
pub mpx_bounds: [X86MPXBounds; X86_MPX_BND_COUNT],
pub pkru: u32,
pub pkrs: X86PKRSState,
pub smap_enabled: bool,
pub smap_ac: bool,
pub smep_enabled: bool,
pub umip_enabled: bool,
pub mpx_violations: u64,
pub pku_violations: u64,
pub smap_violations: u64,
pub smep_violations: u64,
}
impl X86MemorySafety {
pub fn new() -> Self {
let features = Self::probe_features();
X86MemorySafety {
mpx_available: features.pku, pku_available: features.pku,
pks_available: features.pks,
smap_available: features.smap,
smep_available: features.smep,
umip_available: features.umip,
mpx_bounds: [X86MPXBounds::default(); X86_MPX_BND_COUNT],
pkru: 0,
pkrs: X86PKRSState::default(),
smap_enabled: false,
smap_ac: false,
smep_enabled: false,
umip_enabled: false,
mpx_violations: 0,
pku_violations: 0,
smap_violations: 0,
smep_violations: 0,
}
}
pub fn probe_features() -> X86SecurityFeatures {
let mut features = X86SecurityFeatures::default();
#[cfg(target_arch = "x86_64")]
{
let leaf7 = unsafe { core::arch::x86_64::__cpuid_count(7, 0) };
features.smap = (leaf7.ebx & (1 << 20)) != 0;
features.smep = (leaf7.ebx & (1 << 7)) != 0;
features.umip = (leaf7.ecx & (1 << 2)) != 0;
features.pku = (leaf7.ecx & (1 << 3)) != 0;
features.pks = (leaf7.ecx & (1 << 31)) != 0;
}
features
}
pub unsafe fn bndmk(
&mut self,
reg_index: usize,
addr: u64,
size: u64,
) -> Result<(), &'static str> {
if !self.mpx_available {
return Err("MPX not available");
}
if reg_index >= X86_MPX_BND_COUNT {
return Err("Invalid bound register index");
}
self.mpx_bounds[reg_index] = X86MPXBounds {
lower_bound: addr,
upper_bound: addr + size,
valid: true,
};
Ok(())
}
pub unsafe fn bndcl(&mut self, reg_index: usize, addr: u64) -> Result<(), &'static str> {
if reg_index >= X86_MPX_BND_COUNT {
return Err("Invalid bound register index");
}
let bounds = &self.mpx_bounds[reg_index];
if !bounds.valid {
return Ok(());
}
if addr < bounds.lower_bound {
self.mpx_violations += 1;
return Err("MPX: lower bound violation");
}
Ok(())
}
pub unsafe fn bndcu(
&mut self,
reg_index: usize,
addr: u64,
size: u64,
) -> Result<(), &'static str> {
if reg_index >= X86_MPX_BND_COUNT {
return Err("Invalid bound register index");
}
let bounds = &self.mpx_bounds[reg_index];
if !bounds.valid {
return Ok(());
}
if addr.wrapping_add(size) > bounds.upper_bound {
self.mpx_violations += 1;
return Err("MPX: upper bound violation");
}
Ok(())
}
pub unsafe fn bndcn(&mut self, reg_index: usize, addr: u64) -> Result<(), &'static str> {
if reg_index >= X86_MPX_BND_COUNT {
return Err("Invalid bound register index");
}
let bounds = &self.mpx_bounds[reg_index];
if !bounds.valid {
return Ok(());
}
if addr > bounds.upper_bound {
self.mpx_violations += 1;
return Err("MPX: upper bound (inclusive) violation");
}
Ok(())
}
pub fn bndmov(&self, src_reg: usize, dst_reg: usize) -> Result<(), &'static str> {
if src_reg >= X86_MPX_BND_COUNT || dst_reg >= X86_MPX_BND_COUNT {
return Err("Invalid bound register index");
}
let _ = (src_reg, dst_reg);
Ok(())
}
pub fn bndldx(&mut self, reg_index: usize, table_entry_addr: u64) -> Result<(), &'static str> {
if reg_index >= X86_MPX_BND_COUNT {
return Err("Invalid bound register index");
}
let _ = table_entry_addr;
Ok(())
}
pub fn bndstx(&self, reg_index: usize, table_entry_addr: u64) -> Result<(), &'static str> {
if reg_index >= X86_MPX_BND_COUNT {
return Err("Invalid bound register index");
}
let _ = table_entry_addr;
Ok(())
}
#[inline(always)]
pub unsafe fn rdpkru(&self) -> u32 {
let pkru: u32;
#[cfg(target_arch = "x86_64")]
{
asm!("rdpkru", out("eax") pkru, options(nomem, nostack));
}
#[cfg(not(target_arch = "x86_64"))]
{
pkru = self.pkru;
}
pkru
}
#[inline(always)]
pub unsafe fn wrpkru(&mut self, pkru_value: u32) {
#[cfg(target_arch = "x86_64")]
{
let ecx: u32 = 0;
let edx: u32 = 0;
asm!(
"wrpkru",
in("ecx") ecx,
in("edx") edx,
in("eax") pkru_value,
options(nomem, nostack)
);
}
self.pkru = pkru_value;
}
pub fn set_key_rights(
&mut self,
key_index: u8,
disable_access: bool,
disable_write: bool,
) -> Result<(), &'static str> {
if key_index >= X86_MPK_KEY_COUNT as u8 {
return Err("Invalid protection key index");
}
let mask = 0b11u32 << (key_index * 2);
let value = if disable_access {
X86_MPK_DISABLE_ACCESS
} else {
0
} | if disable_write {
X86_MPK_DISABLE_WRITE
} else {
0
};
let shifted = value << (key_index * 2);
let new_pkru = (self.pkru & !mask) | shifted;
unsafe {
self.wrpkru(new_pkru);
}
Ok(())
}
pub fn get_key_rights(&self, key_index: u8) -> Option<X86MPKKeyState> {
if key_index >= X86_MPK_KEY_COUNT as u8 {
return None;
}
let bits = (self.pkru >> (key_index * 2)) & 0b11;
Some(X86MPKKeyState {
index: key_index,
access_disabled: (bits & X86_MPK_DISABLE_ACCESS) != 0,
write_disabled: (bits & X86_MPK_DISABLE_WRITE) != 0,
})
}
pub fn pkru_lockdown(&mut self) {
let all_disabled: u32 = 0x5555_5555; unsafe {
self.wrpkru(all_disabled);
}
}
pub fn pkru_unrestricted(&mut self) {
unsafe {
self.wrpkru(0);
}
}
pub fn check_pku_access(&self, key_index: u8, is_write: bool) -> bool {
if let Some(state) = self.get_key_rights(key_index) {
if state.access_disabled {
self.report_pku_violation();
return false;
}
if is_write && state.write_disabled {
self.report_pku_violation();
return false;
}
true
} else {
false
}
}
fn report_pku_violation(&self) {
}
pub unsafe fn rdpkrs(&self) -> u32 {
let pkrs: u32;
#[cfg(target_arch = "x86_64")]
{
asm!("rdpkru", out("eax") pkrs, options(nomem, nostack));
}
#[cfg(not(target_arch = "x86_64"))]
{
pkrs = self.pkrs.pkrs;
}
pkrs
}
pub unsafe fn wrpkrs(&mut self, pkrs_value: u32) {
#[cfg(target_arch = "x86_64")]
{
asm!(
"wrpkru",
in("ecx") 1u32, in("edx") 0u32,
in("eax") pkrs_value,
options(nomem, nostack)
);
}
self.pkrs.pkrs = pkrs_value;
}
pub fn enable_smap(&mut self) -> Result<(), &'static str> {
if !self.smap_available {
return Err("SMAP not available");
}
self.smap_enabled = true;
Ok(())
}
pub fn disable_smap(&mut self) -> Result<(), &'static str> {
self.smap_enabled = false;
Ok(())
}
#[inline(always)]
pub unsafe fn clac(&mut self) {
#[cfg(target_arch = "x86_64")]
{
asm!("clac", options(nomem, nostack));
}
self.smap_ac = false;
}
#[inline(always)]
pub unsafe fn stac(&mut self) {
#[cfg(target_arch = "x86_64")]
{
asm!("stac", options(nomem, nostack));
}
self.smap_ac = true;
}
pub fn is_user_access_allowed(&self) -> bool {
!self.smap_enabled || self.smap_ac
}
pub fn handle_smap_violation(&mut self, faulting_address: u64) {
self.smap_violations += 1;
let _ = faulting_address;
}
pub fn enable_smep(&mut self) -> Result<(), &'static str> {
if !self.smep_available {
return Err("SMEP not available");
}
self.smep_enabled = true;
Ok(())
}
pub fn disable_smep(&mut self) -> Result<(), &'static str> {
self.smep_enabled = false;
Ok(())
}
pub fn check_smep(&self, rip: u64, in_kernel_mode: bool) -> bool {
if !self.smep_enabled || !in_kernel_mode {
return true;
}
let is_user_page = rip < 0x0000_8000_0000_0000;
if is_user_page {
self.report_smep_violation();
return false;
}
true
}
fn report_smep_violation(&self) {
}
pub fn handle_smep_violation(&mut self, faulting_rip: u64) {
self.smep_violations += 1;
let _ = faulting_rip;
}
pub fn enable_umip(&mut self) -> Result<(), &'static str> {
if !self.umip_available {
return Err("UMIP not available");
}
self.umip_enabled = true;
Ok(())
}
pub fn disable_umip(&mut self) -> Result<(), &'static str> {
self.umip_enabled = false;
Ok(())
}
pub fn check_umip(&self, opcode: u16, cpl: u8) -> bool {
if !self.umip_enabled || cpl == 0 {
return true;
}
match opcode {
0x0F01 => true, 0x0F00 => false, _ => true,
}
}
}
impl Default for X86MemorySafety {
fn default() -> Self {
X86MemorySafety::new()
}
}
#[derive(Debug, Clone, Copy)]
pub struct X86StackCanary {
pub value: u64,
pub active: bool,
pub location: i32,
pub size: u8,
}
#[derive(Debug, Clone)]
pub struct X86ASLRConfig {
pub pie_enabled: bool,
pub stack_randomize: bool,
pub mmap_randomize: bool,
pub brk_randomize: bool,
pub vdso_randomize: bool,
pub exec_entropy_bits: u8,
pub stack_entropy_bits: u8,
pub mmap_entropy_bits: u8,
pub base_offset: u64,
pub stack_base: u64,
pub active: bool,
}
impl Default for X86ASLRConfig {
fn default() -> Self {
X86ASLRConfig {
pie_enabled: false,
stack_randomize: false,
mmap_randomize: false,
brk_randomize: false,
vdso_randomize: false,
exec_entropy_bits: X86_ASLR_ENTROPY_BITS_64 as u8,
stack_entropy_bits: X86_ASLR_STACK_RND_BITS_64 as u8,
mmap_entropy_bits: X86_ASLR_MMAP_RND_BITS_64 as u8,
base_offset: 0,
stack_base: 0,
active: false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86RELROLevel {
None,
Partial,
Full,
}
impl fmt::Display for X86RELROLevel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
X86RELROLevel::None => write!(f, "None"),
X86RELROLevel::Partial => write!(f, "Partial"),
X86RELROLevel::Full => write!(f, "Full"),
}
}
}
#[derive(Debug, Clone)]
pub struct X86FortifyCheck {
pub dst: u64,
pub dst_size: usize,
pub src: u64,
pub src_size: usize,
pub operation: X86FortifyOp,
pub ok: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86FortifyOp {
MemCpy,
MemMove,
MemSet,
StrCpy,
StrNCpy,
StrCat,
StrNCat,
SPrintF,
SPrintFChk,
MemSetChk,
}
impl fmt::Display for X86FortifyOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
X86FortifyOp::MemCpy => write!(f, "memcpy"),
X86FortifyOp::MemMove => write!(f, "memmove"),
X86FortifyOp::MemSet => write!(f, "memset"),
X86FortifyOp::StrCpy => write!(f, "strcpy"),
X86FortifyOp::StrNCpy => write!(f, "strncpy"),
X86FortifyOp::StrCat => write!(f, "strcat"),
X86FortifyOp::StrNCat => write!(f, "strncat"),
X86FortifyOp::SPrintF => write!(f, "sprintf"),
X86FortifyOp::SPrintFChk => write!(f, "__sprintf_chk"),
X86FortifyOp::MemSetChk => write!(f, "__memset_chk"),
}
}
}
#[derive(Debug, Clone)]
pub struct X86CFICheck {
pub target_address: u64,
pub type_hash: u64,
pub passed: bool,
pub check_addr: u64,
}
#[derive(Debug, Clone)]
pub struct X86SafeStackConfig {
pub safe_stack_base: u64,
pub safe_stack_limit: u64,
pub unsafe_stack_base: u64,
pub unsafe_stack_limit: u64,
pub enabled: bool,
pub safe_stack_size: u64,
pub unsafe_stack_size: u64,
}
impl Default for X86SafeStackConfig {
fn default() -> Self {
X86SafeStackConfig {
safe_stack_base: 0,
safe_stack_limit: 0,
unsafe_stack_base: 0,
unsafe_stack_limit: 0,
enabled: false,
safe_stack_size: 0x100000, unsafe_stack_size: 0x100000, }
}
}
#[derive(Debug, Clone, Copy)]
pub struct X86ShadowCallStackEntry {
pub return_address: u64,
pub frame_depth: u64,
pub valid: bool,
}
pub struct X86ExploitMitigations {
pub canary: X86StackCanary,
pub aslr: X86ASLRConfig,
pub relro_level: X86RELROLevel,
pub nx_enabled: bool,
pub fortify_source: bool,
pub cfi_enabled: bool,
pub cfi_checks: usize,
pub cfi_violations: usize,
pub safe_stack: X86SafeStackConfig,
pub shadow_call_stack: Vec<X86ShadowCallStackEntry>,
pub shadow_call_stack_ptr: u64,
pub active_mitigations: Vec<X86ExploitMitigationType>,
}
impl X86ExploitMitigations {
pub fn new() -> Self {
X86ExploitMitigations {
canary: X86StackCanary {
value: 0,
active: false,
location: -8,
size: X86_STACK_CANARY_SIZE as u8,
},
aslr: X86ASLRConfig::default(),
relro_level: X86RELROLevel::None,
nx_enabled: false,
fortify_source: false,
cfi_enabled: false,
cfi_checks: 0,
cfi_violations: 0,
safe_stack: X86SafeStackConfig::default(),
shadow_call_stack: Vec::new(),
shadow_call_stack_ptr: 0,
active_mitigations: Vec::new(),
}
}
pub fn generate_canary(&mut self) -> u64 {
let random = self.get_random_u64();
let canary = (random & !0xFF) | 0x00;
self.canary.value = canary;
self.canary.active = true;
canary
}
pub fn get_canary(&self) -> u64 {
self.canary.value
}
pub fn place_canary(&mut self, location: i32) {
self.canary.location = location;
self.canary.active = true;
}
pub fn verify_canary(&self, stack_canary_value: u64) -> bool {
if !self.canary.active {
return true;
}
if stack_canary_value != self.canary.value {
self.stack_chk_fail();
return false;
}
true
}
fn stack_chk_fail(&self) {
eprintln!(
"*** stack smashing detected ***: terminated (stack canary corrupted: expected {:#018x})",
self.canary.value
);
std::process::abort();
}
pub fn emit_canary_check(&self) -> Vec<u8> {
if !self.canary.active {
return Vec::new();
}
let mut code = Vec::new();
match self.canary.size {
8 => {
code.extend_from_slice(&[0x48, 0x8B, 0x45, 0xF8]);
}
4 => {
code.extend_from_slice(&[0x8B, 0x45, 0xFC]);
}
_ => {}
}
code
}
pub fn enable_aslr(&mut self) -> Result<(), &'static str> {
self.aslr.active = true;
self.aslr.pie_enabled = true;
self.aslr.stack_randomize = true;
self.aslr.mmap_randomize = true;
self.aslr.brk_randomize = true;
self.aslr.vdso_randomize = true;
let entropy_mask = (1u64 << self.aslr.exec_entropy_bits) - 1;
let random = self.get_random_u64();
self.aslr.base_offset = (random & entropy_mask) << 12; self.active_mitigations.push(X86ExploitMitigationType::ASLR);
Ok(())
}
pub fn enable_pie(&mut self) {
self.aslr.pie_enabled = true;
self.active_mitigations.push(X86ExploitMitigationType::PIE);
}
pub fn randomize_stack(&mut self) -> u64 {
if !self.aslr.stack_randomize {
return 0;
}
let mask = (1u64 << self.aslr.stack_entropy_bits) - 1;
let random = self.get_random_u64();
let offset = (random & mask) << 4; self.aslr.stack_base = self.aslr.stack_base.wrapping_add(offset);
self.aslr.stack_base
}
pub fn randomize_mmap(&self) -> u64 {
if !self.aslr.mmap_randomize {
return 0;
}
let mask = (1u64 << self.aslr.mmap_entropy_bits) - 1;
let random = self.get_random_u64();
(random & mask) << 12 }
pub fn get_randomized_base(&self) -> u64 {
if !self.aslr.pie_enabled {
return 0;
}
self.aslr.base_offset
}
pub fn is_aslr_active(&self) -> bool {
self.aslr.active
}
pub fn enable_relro_partial(&mut self) {
self.relro_level = X86RELROLevel::Partial;
self.active_mitigations
.push(X86ExploitMitigationType::RELRO);
}
pub fn enable_relro_full(&mut self) {
self.relro_level = X86RELROLevel::Full;
self.active_mitigations
.push(X86ExploitMitigationType::RELRO);
}
pub fn get_relro_level(&self) -> X86RELROLevel {
self.relro_level
}
pub fn enable_nx(&mut self) -> Result<(), &'static str> {
self.nx_enabled = true;
self.active_mitigations.push(X86ExploitMitigationType::NX);
Ok(())
}
pub fn disable_nx(&mut self) {
self.nx_enabled = false;
self.active_mitigations
.retain(|m| *m != X86ExploitMitigationType::NX);
}
pub fn is_nx_enabled(&self) -> bool {
self.nx_enabled
}
pub fn enforce_wxorx(&self, page_flags: u64) -> bool {
if !self.nx_enabled {
return true;
}
let writable = (page_flags & 0x2) != 0;
let executable = (page_flags & 0x4) != 0;
!(writable && executable)
}
pub fn enable_fortify_source(&mut self) {
self.fortify_source = true;
self.active_mitigations
.push(X86ExploitMitigationType::FortifySource);
}
pub fn memcpy_chk(
&self,
dst_size: usize,
src: &[u8],
dst: &mut [u8],
n: usize,
) -> Result<usize, &'static str> {
if !self.fortify_source {
let len = n.min(dst.len()).min(src.len());
dst[..len].copy_from_slice(&src[..len]);
return Ok(len);
}
if n > dst_size {
self.fortify_fail(X86FortifyOp::MemCpy, n, dst_size);
return Err("FORTIFY: memcpy buffer overflow detected");
}
let len = n.min(dst.len()).min(src.len());
dst[..len].copy_from_slice(&src[..len]);
Ok(len)
}
pub fn memset_chk(
&self,
buf: &mut [u8],
val: u8,
n: usize,
dst_size: usize,
) -> Result<(), &'static str> {
if !self.fortify_source {
let len = n.min(buf.len());
buf[..len].fill(val);
return Ok(());
}
if n > dst_size {
self.fortify_fail(X86FortifyOp::MemSetChk, n, dst_size);
return Err("FORTIFY: memset buffer overflow detected");
}
let len = n.min(buf.len());
buf[..len].fill(val);
Ok(())
}
pub fn sprintf_chk(
&self,
dst: &mut [u8],
dst_size: usize,
format: &str,
args: &[&str],
) -> Result<usize, &'static str> {
if !self.fortify_source {
let mut written = 0;
for arg in args {
let bytes = arg.as_bytes();
let end = (written + bytes.len()).min(dst.len());
let copy_len = end - written;
dst[written..end].copy_from_slice(&bytes[..copy_len]);
written = end;
}
return Ok(written);
}
let mut written = 0;
let fmt_bytes = format.as_bytes();
for chunk in fmt_bytes.chunks(1) {
if written >= dst_size {
self.fortify_fail(X86FortifyOp::SPrintFChk, written, dst_size);
return Err("FORTIFY: sprintf buffer overflow detected");
}
if written < dst.len() {
dst[written] = chunk[0];
}
written += 1;
}
Ok(written)
}
fn fortify_fail(&self, op: X86FortifyOp, requested: usize, available: usize) {
eprintln!(
"*** buffer overflow detected ***: {} called with size {} exceeds buffer size {}",
op, requested, available
);
std::process::abort();
}
pub fn check_fortify(
&self,
dst: u64,
dst_size: usize,
src: u64,
src_size: usize,
op: X86FortifyOp,
) -> X86FortifyCheck {
let ok = !self.fortify_source || src_size <= dst_size;
X86FortifyCheck {
dst,
dst_size,
src,
src_size,
operation: op,
ok,
}
}
pub fn enable_cfi(&mut self) {
self.cfi_enabled = true;
self.active_mitigations
.push(X86ExploitMitigationType::ForwardEdgeCFI);
}
pub fn cfi_check_forward_edge(&mut self, target_address: u64, expected_type_hash: u64) -> bool {
if !self.cfi_enabled {
return true;
}
self.cfi_checks += 1;
let check = X86CFICheck {
target_address,
type_hash: expected_type_hash,
passed: true, check_addr: 0,
};
if !check.passed {
self.cfi_violations += 1;
}
check.passed
}
pub fn cfi_check_backward_edge(&mut self, return_address: u64) -> bool {
if !self.cfi_enabled {
return true;
}
let entry = X86ShadowCallStackEntry {
return_address,
frame_depth: self.shadow_call_stack.len() as u64,
valid: true,
};
self.shadow_call_stack.push(entry);
true
}
pub fn cfi_verify_return(&mut self, return_address: u64) -> bool {
if !self.cfi_enabled {
return true;
}
if let Some(expected) = self.shadow_call_stack.pop() {
if expected.return_address != return_address {
self.cfi_violations += 1;
return false;
}
}
true
}
pub fn get_cfi_stats(&self) -> (usize, usize) {
(self.cfi_checks, self.cfi_violations)
}
pub fn enable_safe_stack(&mut self) -> Result<(), &'static str> {
self.safe_stack.enabled = true;
self.safe_stack.safe_stack_base = self.allocate_safe_stack();
self.safe_stack.unsafe_stack_base = self.allocate_unsafe_stack();
self.active_mitigations
.push(X86ExploitMitigationType::SafeStack);
Ok(())
}
fn allocate_safe_stack(&self) -> u64 {
let random = self.get_random_u64();
0x7000_0000_0000 + (random & 0xFFFF_FFFF)
}
fn allocate_unsafe_stack(&self) -> u64 {
let random = self.get_random_u64();
0x6000_0000_0000 + (random & 0xFFFF_FFFF)
}
pub fn is_safe_stack_object(&self, address_taken: bool) -> bool {
if !self.safe_stack.enabled {
return true; }
!address_taken
}
pub fn get_safe_stack_base(&self) -> u64 {
self.safe_stack.safe_stack_base
}
pub fn get_unsafe_stack_base(&self) -> u64 {
self.safe_stack.unsafe_stack_base
}
pub fn shadow_call_push(&mut self, return_address: u64) {
if !self.cfi_enabled {
return;
}
let entry = X86ShadowCallStackEntry {
return_address,
frame_depth: self.shadow_call_stack.len() as u64,
valid: true,
};
self.shadow_call_stack.push(entry);
self.shadow_call_stack_ptr = (self.shadow_call_stack.len() - 1) as u64;
}
pub fn shadow_call_pop(&mut self, actual_return: u64) -> bool {
if !self.cfi_enabled {
return true;
}
if let Some(expected) = self.shadow_call_stack.pop() {
self.shadow_call_stack_ptr = self.shadow_call_stack.len().saturating_sub(1) as u64;
if expected.return_address != actual_return {
self.cfi_violations += 1;
return false;
}
return true;
}
false
}
fn get_random_u64(&self) -> u64 {
#[cfg(target_arch = "x86_64")]
{
let mut val: u64 = 0;
unsafe {
let success: u8;
asm!(
"rdrand {}",
"setc {}",
out(reg) val,
out(reg_byte) success,
options(nomem, nostack)
);
if success != 0 {
return val;
}
}
}
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default();
let nanos = now.as_nanos() as u64;
let pid = std::process::id() as u64;
nanos
.wrapping_mul(6364136223846793005)
.wrapping_add(pid)
.wrapping_add(1442695040888963407)
}
pub fn list_active_mitigations(&self) -> &[X86ExploitMitigationType] {
&self.active_mitigations
}
pub fn mitigation_count(&self) -> usize {
self.active_mitigations.len()
}
pub fn full_hardening(&mut self) {
let _ = self.generate_canary();
self.canary.active = true;
let _ = self.enable_aslr();
self.enable_pie();
let _ = self.enable_nx();
self.enable_relro_full();
self.enable_fortify_source();
self.enable_cfi();
let _ = self.enable_safe_stack();
self.active_mitigations.clear();
self.active_mitigations
.push(X86ExploitMitigationType::StackProtector);
self.active_mitigations.push(X86ExploitMitigationType::ASLR);
self.active_mitigations.push(X86ExploitMitigationType::PIE);
self.active_mitigations
.push(X86ExploitMitigationType::RELRO);
self.active_mitigations.push(X86ExploitMitigationType::NX);
self.active_mitigations
.push(X86ExploitMitigationType::FortifySource);
self.active_mitigations
.push(X86ExploitMitigationType::ForwardEdgeCFI);
self.active_mitigations
.push(X86ExploitMitigationType::SafeStack);
}
}
impl Default for X86ExploitMitigations {
fn default() -> Self {
X86ExploitMitigations::new()
}
}
#[derive(Debug, Clone)]
pub struct X86SideChannelStatus {
pub vuln: X86SideChannelVuln,
pub affected: bool,
pub mitigated: bool,
pub mitigation_strategy: String,
pub performance_impact: u8,
}
#[derive(Debug, Clone, Default)]
pub struct X86SpecCtrlState {
pub ibrs_active: bool,
pub stibp_active: bool,
pub ibpb_issued: bool,
pub ssbd_active: bool,
pub psfd_active: bool,
}
pub struct X86SideChannel {
pub vulnerabilities: Vec<X86SideChannelStatus>,
pub spec_ctrl: X86SpecCtrlState,
pub kpti_active: bool,
pub tsx_disabled: bool,
pub retpoline_enabled: bool,
pub l1d_flush_on_vmentry: bool,
pub mds_mitigated: bool,
pub taa_mitigated: bool,
pub srbds_mitigated: bool,
pub mmio_mitigated: bool,
pub retbleed_mitigated: bool,
}
impl X86SideChannel {
pub fn new() -> Self {
let capabilities = Self::probe_cpu_capabilities();
let vulnerabilities = Self::assess_vulnerabilities(&capabilities);
X86SideChannel {
vulnerabilities,
spec_ctrl: X86SpecCtrlState::default(),
kpti_active: false,
tsx_disabled: false,
retpoline_enabled: false,
l1d_flush_on_vmentry: false,
mds_mitigated: false,
taa_mitigated: false,
srbds_mitigated: false,
mmio_mitigated: false,
retbleed_mitigated: false,
}
}
pub fn probe_cpu_capabilities() -> X86SecurityFeatures {
let mut features = X86SecurityFeatures::default();
#[cfg(target_arch = "x86_64")]
{
let leaf7 = unsafe { core::arch::x86_64::__cpuid_count(7, 0) };
features.ibrs = (leaf7.edx & (1 << 26)) != 0; features.stibp = (leaf7.edx & (1 << 27)) != 0; features.ssbd = (leaf7.edx & (1 << 31)) != 0; features.md_clear = (leaf7.edx & (1 << 10)) != 0; features.rdrand = (leaf7.ecx & (1 << 30)) != 0;
let arch_caps = unsafe { core::arch::x86_64::__cpuid_count(7, 0) };
}
features
}
fn assess_vulnerabilities(caps: &X86SecurityFeatures) -> Vec<X86SideChannelStatus> {
let mut vulns = Vec::new();
vulns.push(X86SideChannelStatus {
vuln: X86SideChannelVuln::SpectreV1,
affected: true,
mitigated: false,
mitigation_strategy: "LFENCE barriers, __builtin_load_no_speculate".to_string(),
performance_impact: 0,
});
vulns.push(X86SideChannelStatus {
vuln: X86SideChannelVuln::SpectreV2,
affected: true,
mitigated: caps.ibrs,
mitigation_strategy: "Retpoline, IBRS, IBPB, STIBP, enhanced IBRS".to_string(),
performance_impact: if caps.ibrs { 5 } else { 20 },
});
let meltdown_affected = true; vulns.push(X86SideChannelStatus {
vuln: X86SideChannelVuln::Meltdown,
affected: meltdown_affected,
mitigated: false,
mitigation_strategy: "KPTI/PTI (Page Table Isolation)".to_string(),
performance_impact: if meltdown_affected { 10 } else { 0 },
});
vulns.push(X86SideChannelStatus {
vuln: X86SideChannelVuln::L1TF,
affected: true,
mitigated: false,
mitigation_strategy: "L1D flush on VMENTRY, PTE inversion, NX on EPT".to_string(),
performance_impact: 3,
});
vulns.push(X86SideChannelStatus {
vuln: X86SideChannelVuln::MDS,
affected: true,
mitigated: caps.md_clear,
mitigation_strategy: "VERW, L1D flush, microcode update".to_string(),
performance_impact: 1,
});
vulns.push(X86SideChannelStatus {
vuln: X86SideChannelVuln::TAA,
affected: caps.tsx,
mitigated: false,
mitigation_strategy: "TSX disable (IA32_TSX_CTRL), MDS mitigations".to_string(),
performance_impact: if caps.tsx { 40 } else { 0 },
});
vulns.push(X86SideChannelStatus {
vuln: X86SideChannelVuln::SRBDS,
affected: true,
mitigated: false,
mitigation_strategy: "IA32_MCU_OPT_CTRL, microcode update".to_string(),
performance_impact: 0,
});
vulns.push(X86SideChannelStatus {
vuln: X86SideChannelVuln::MMIO,
affected: true,
mitigated: false,
mitigation_strategy: "VERW before VM exit, microcode update".to_string(),
performance_impact: 0,
});
vulns.push(X86SideChannelStatus {
vuln: X86SideChannelVuln::Retbleed,
affected: true,
mitigated: false,
mitigation_strategy: "IBRS/STIBP, enhanced retpoline, RSB stuffing, PBRSB mitigation"
.to_string(),
performance_impact: 10,
});
vulns
}
pub fn spectre_v1_barrier_lfence() -> [u8; 3] {
[0x0F, 0xAE, 0xE8]
}
pub fn emit_speculation_barrier(&self) -> Vec<u8> {
vec![0x0F, 0xAE, 0xE8] }
pub fn spectre_v1_array_mask(index: usize, bound: usize) -> usize {
let mask: usize = if index < bound { !0 } else { 0 };
index & mask
}
pub fn mitigate_spectre_v1(&mut self) {
if let Some(vuln) = self
.vulnerabilities
.iter_mut()
.find(|v| v.vuln == X86SideChannelVuln::SpectreV1)
{
vuln.mitigated = true;
}
}
pub fn enable_retpoline(&mut self) {
self.retpoline_enabled = true;
if let Some(vuln) = self
.vulnerabilities
.iter_mut()
.find(|v| v.vuln == X86SideChannelVuln::SpectreV2)
{
vuln.mitigated = true;
vuln.performance_impact = 20;
}
}
pub fn generate_retpoline_thunk(&self, _target: u64) -> Vec<u8> {
let mut code = Vec::new();
code.push(0xE8);
code.push(0x00);
code.push(0x00);
code.push(0x00);
code.push(0x00);
code.extend_from_slice(&[0xF3, 0x90]);
code.extend_from_slice(&[0x0F, 0xAE, 0xE8]);
code.extend_from_slice(&[0xEB, 0xF7]);
code.extend_from_slice(&[0x4C, 0x89, 0x1C, 0x24]);
code.push(0xC3);
code
}
pub fn enable_ibrs(&mut self) -> Result<(), &'static str> {
self.spec_ctrl.ibrs_active = true;
#[cfg(target_arch = "x86_64")]
unsafe {
let msr_val: u64 = X86_SPEC_CTRL_IBRS;
asm!(
"wrmsr",
in("ecx") X86_MSR_IA32_SPEC_CTRL,
in("eax") msr_val as u32,
in("edx") (msr_val >> 32) as u32,
options(nomem, nostack)
);
}
if let Some(vuln) = self
.vulnerabilities
.iter_mut()
.find(|v| v.vuln == X86SideChannelVuln::SpectreV2)
{
vuln.mitigated = true;
vuln.performance_impact = 5;
}
Ok(())
}
pub fn issue_ibpb(&mut self) {
self.spec_ctrl.ibpb_issued = true;
#[cfg(target_arch = "x86_64")]
unsafe {
asm!(
"wrmsr",
in("ecx") X86_MSR_IA32_PRED_CMD,
in("eax") X86_PRED_CMD_IBPB as u32,
in("edx") 0u32,
options(nomem, nostack)
);
}
}
pub fn enable_stibp(&mut self) -> Result<(), &'static str> {
self.spec_ctrl.stibp_active = true;
#[cfg(target_arch = "x86_64")]
unsafe {
asm!(
"wrmsr",
in("ecx") X86_MSR_IA32_SPEC_CTRL,
in("eax") (X86_SPEC_CTRL_IBRS | X86_SPEC_CTRL_STIBP) as u32,
in("edx") 0u32,
options(nomem, nostack)
);
}
Ok(())
}
pub fn disable_spec_ctrl(&mut self) {
self.spec_ctrl = X86SpecCtrlState::default();
#[cfg(target_arch = "x86_64")]
unsafe {
asm!(
"wrmsr",
in("ecx") X86_MSR_IA32_SPEC_CTRL,
in("eax") 0u32,
in("edx") 0u32,
options(nomem, nostack)
);
}
}
pub fn enable_kpti(&mut self) {
self.kpti_active = true;
if let Some(vuln) = self
.vulnerabilities
.iter_mut()
.find(|v| v.vuln == X86SideChannelVuln::Meltdown)
{
vuln.mitigated = true;
}
}
pub fn disable_kpti(&mut self) {
self.kpti_active = false;
}
pub fn is_kpti_active(&self) -> bool {
self.kpti_active
}
pub fn generate_kpti_trampoline(&self) -> Vec<u8> {
let mut code = Vec::new();
code.extend_from_slice(&[0x0F, 0x20, 0xD8]);
code.extend_from_slice(&[0x48, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x80]);
code.extend_from_slice(&[0x0F, 0x22, 0xD8]);
code
}
pub fn flush_l1d(&mut self) {
#[cfg(target_arch = "x86_64")]
unsafe {
asm!(
"wrmsr",
in("ecx") X86_MSR_IA32_FLUSH_CMD,
in("eax") X86_FLUSH_CMD_L1D as u32,
in("edx") 0u32,
options(nomem, nostack)
);
}
self.l1d_flush_on_vmentry = true;
if let Some(vuln) = self
.vulnerabilities
.iter_mut()
.find(|v| v.vuln == X86SideChannelVuln::L1TF)
{
vuln.mitigated = true;
}
}
pub fn verw_clear_buffers() -> Vec<u8> {
vec![0x0F, 0x00, 0x3C, 0x24]
}
pub fn mitigate_mds(&mut self) {
self.mds_mitigated = true;
if let Some(vuln) = self
.vulnerabilities
.iter_mut()
.find(|v| v.vuln == X86SideChannelVuln::MDS)
{
vuln.mitigated = true;
}
}
pub fn disable_tsx(&mut self) {
#[cfg(target_arch = "x86_64")]
unsafe {
asm!(
"wrmsr",
in("ecx") X86_MSR_IA32_TSX_CTRL,
in("eax") (X86_TSX_CTRL_RTM_DISABLE | X86_TSX_CTRL_CPUID_CLEAR) as u32,
in("edx") 0u32,
options(nomem, nostack)
);
}
self.tsx_disabled = true;
if let Some(vuln) = self
.vulnerabilities
.iter_mut()
.find(|v| v.vuln == X86SideChannelVuln::TAA)
{
vuln.mitigated = true;
}
}
pub fn enable_tsx(&mut self) {
#[cfg(target_arch = "x86_64")]
unsafe {
asm!(
"wrmsr",
in("ecx") X86_MSR_IA32_TSX_CTRL,
in("eax") 0u32,
in("edx") 0u32,
options(nomem, nostack)
);
}
self.tsx_disabled = false;
}
pub fn mitigate_srbds(&mut self) {
#[cfg(target_arch = "x86_64")]
unsafe {
asm!(
"wrmsr",
in("ecx") X86_MSR_IA32_MCU_OPT_CTRL,
in("eax") X86_MCU_OPT_CTRL_RNGDS_MITG_DIS as u32,
in("edx") 0u32,
options(nomem, nostack)
);
}
self.srbds_mitigated = true;
if let Some(vuln) = self
.vulnerabilities
.iter_mut()
.find(|v| v.vuln == X86SideChannelVuln::SRBDS)
{
vuln.mitigated = true;
}
}
pub fn mitigate_mmio(&mut self) {
self.mmio_mitigated = true;
self.mitigate_mds();
if let Some(vuln) = self
.vulnerabilities
.iter_mut()
.find(|v| v.vuln == X86SideChannelVuln::MMIO)
{
vuln.mitigated = true;
}
}
pub fn mitigate_retbleed(&mut self) {
let _ = self.enable_ibrs();
self.enable_stibp().ok();
self.enable_retpoline();
self.retbleed_mitigated = true;
if let Some(vuln) = self
.vulnerabilities
.iter_mut()
.find(|v| v.vuln == X86SideChannelVuln::Retbleed)
{
vuln.mitigated = true;
}
}
pub fn generate_rsb_stuffing(&self, num_entries: usize) -> Vec<u8> {
let mut code = Vec::new();
for _ in 0..num_entries.min(32) {
code.push(0xE8);
code.push(0x00);
code.push(0x00);
code.push(0x00);
code.push(0x00);
code.push(0xCC);
}
for _ in 0..num_entries.min(32) {
code.push(0x90); }
code
}
pub fn apply_all_mitigations(&mut self) {
self.mitigate_spectre_v1();
let _ = self.enable_ibrs();
self.enable_stibp().ok();
self.enable_kpti();
self.flush_l1d();
self.mitigate_mds();
self.disable_tsx();
self.mitigate_srbds();
self.mitigate_mmio();
self.mitigate_retbleed();
}
pub fn get_vulnerability_report(&self) -> Vec<X86SideChannelStatus> {
self.vulnerabilities.clone()
}
pub fn unmitigated_count(&self) -> usize {
self.vulnerabilities
.iter()
.filter(|v| v.affected && !v.mitigated)
.count()
}
}
impl Default for X86SideChannel {
fn default() -> Self {
X86SideChannel::new()
}
}
#[derive(Debug, Clone)]
pub struct X86RNGState {
pub state: [u64; 4],
pub counter: u64,
pub rdrand_available: bool,
pub rdseed_available: bool,
}
impl Default for X86RNGState {
fn default() -> Self {
X86RNGState {
state: [
0x6A09E667F3BCC908,
0xBB67AE8584CAA73B,
0x3C6EF372FE94F82B,
0xA54FF53A5F1D36F1,
],
counter: 0,
rdrand_available: false,
rdseed_available: false,
}
}
}
pub struct X86CryptoHardening {
pub rng: X86RNGState,
pub chacha_key: [u8; CHACHA20_KEY_SIZE],
pub constant_time: bool,
pub aes_ni_available: bool,
pub random_bytes_generated: u64,
}
impl X86CryptoHardening {
pub fn new() -> Self {
let features = Self::probe_crypto_features();
let mut crypto = X86CryptoHardening {
rng: X86RNGState {
rdrand_available: features.rdrand,
rdseed_available: features.rdseed,
..Default::default()
},
chacha_key: [0u8; CHACHA20_KEY_SIZE],
constant_time: true,
aes_ni_available: features.aes_ni,
random_bytes_generated: 0,
};
crypto.seed_rng();
crypto
}
fn probe_crypto_features() -> X86SecurityFeatures {
let mut features = X86SecurityFeatures::default();
#[cfg(target_arch = "x86_64")]
{
let leaf7 = unsafe { core::arch::x86_64::__cpuid_count(7, 0) };
features.rdrand = (leaf7.ecx & (1 << 30)) != 0;
features.rdseed = (leaf7.ebx & (1 << 18)) != 0;
features.aes_ni = (leaf7.ecx & (1 << 25)) != 0;
features.sha_ni = (leaf7.ebx & (1 << 29)) != 0;
}
features
}
fn seed_rng(&mut self) {
let mut seed = [0u8; CHACHA20_KEY_SIZE];
for chunk in seed.chunks_mut(8) {
if let Some(val) = self.rdrand64() {
chunk.copy_from_slice(&val.to_le_bytes());
}
}
self.chacha_key = seed;
self.rng.counter = 0;
}
pub fn rdrand64(&self) -> Option<u64> {
#[cfg(target_arch = "x86_64")]
{
let mut val: u64 = 0;
let success: u8;
unsafe {
asm!(
"rdrand {}",
"setc {}",
out(reg) val,
out(reg_byte) success,
options(nomem, nostack)
);
}
if success != 0 {
return Some(val);
}
}
None
}
pub fn rdseed64(&self) -> Option<u64> {
#[cfg(target_arch = "x86_64")]
{
let mut val: u64 = 0;
let success: u8;
unsafe {
asm!(
"rdseed {}",
"setc {}",
out(reg) val,
out(reg_byte) success,
options(nomem, nostack)
);
}
if success != 0 {
return Some(val);
}
}
None
}
pub fn get_random_u64(&mut self) -> u64 {
self.random_bytes_generated += 8;
if let Some(val) = self.rdrand64() {
return val;
}
let counter = self.rng.counter;
self.rng.counter += 1;
let block = self.chacha20_prng_block(counter);
u64::from_le_bytes([
block[0], block[1], block[2], block[3], block[4], block[5], block[6], block[7],
])
}
fn chacha20_prng_block(&self, counter: u64) -> [u8; 64] {
let mut state = [0u32; 16];
state[0] = u32::from_le_bytes([
CHACHA20_CONSTANT[0],
CHACHA20_CONSTANT[1],
CHACHA20_CONSTANT[2],
CHACHA20_CONSTANT[3],
]);
state[1] = u32::from_le_bytes([
CHACHA20_CONSTANT[4],
CHACHA20_CONSTANT[5],
CHACHA20_CONSTANT[6],
CHACHA20_CONSTANT[7],
]);
state[2] = u32::from_le_bytes([
CHACHA20_CONSTANT[8],
CHACHA20_CONSTANT[9],
CHACHA20_CONSTANT[10],
CHACHA20_CONSTANT[11],
]);
state[3] = u32::from_le_bytes([
CHACHA20_CONSTANT[12],
CHACHA20_CONSTANT[13],
CHACHA20_CONSTANT[14],
CHACHA20_CONSTANT[15],
]);
for i in 0..8 {
let offset = i * 4;
state[4 + i] = u32::from_le_bytes([
self.chacha_key[offset],
self.chacha_key[offset + 1],
self.chacha_key[offset + 2],
self.chacha_key[offset + 3],
]);
}
state[12] = counter as u32;
state[13] = (counter >> 32) as u32;
state[14] = 0;
state[15] = 0;
let initial = state;
self.chacha20_double_round(&mut state);
for i in 0..16 {
state[i] = state[i].wrapping_add(initial[i]);
}
let mut out = [0u8; 64];
for i in 0..16 {
let bytes = state[i].to_le_bytes();
out[i * 4..(i + 1) * 4].copy_from_slice(&bytes);
}
out
}
#[inline(always)]
fn chacha20_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);
}
fn chacha20_double_round(&self, state: &mut [u32; 16]) {
for _ in 0..10 {
Self::chacha20_quarter_round(state, 0, 4, 8, 12);
Self::chacha20_quarter_round(state, 1, 5, 9, 13);
Self::chacha20_quarter_round(state, 2, 6, 10, 14);
Self::chacha20_quarter_round(state, 3, 7, 11, 15);
Self::chacha20_quarter_round(state, 0, 5, 10, 15);
Self::chacha20_quarter_round(state, 1, 6, 11, 12);
Self::chacha20_quarter_round(state, 2, 7, 8, 13);
Self::chacha20_quarter_round(state, 3, 4, 9, 14);
}
}
pub fn chacha20_encrypt(
&self,
key: &[u8; 32],
nonce: &[u8; 12],
counter: u32,
plaintext: &[u8],
) -> Vec<u8> {
let mut ciphertext = vec![0u8; plaintext.len()];
let num_blocks = (plaintext.len() + 63) / 64;
let mut block_counter = counter;
for block_idx in 0..num_blocks {
let keystream = self.chacha20_block(key, nonce, block_counter);
let block_start = block_idx * 64;
let block_end = ((block_idx + 1) * 64).min(plaintext.len());
for i in block_start..block_end {
ciphertext[i] = plaintext[i] ^ keystream[i - block_start];
}
block_counter = block_counter.wrapping_add(1);
}
ciphertext
}
pub fn chacha20_decrypt(
&self,
key: &[u8; 32],
nonce: &[u8; 12],
counter: u32,
ciphertext: &[u8],
) -> Vec<u8> {
self.chacha20_encrypt(key, nonce, counter, ciphertext)
}
pub fn chacha20_block(&self, key: &[u8; 32], nonce: &[u8; 12], counter: u32) -> [u8; 64] {
let mut state = [0u32; 16];
state[0] = u32::from_le_bytes([
CHACHA20_CONSTANT[0],
CHACHA20_CONSTANT[1],
CHACHA20_CONSTANT[2],
CHACHA20_CONSTANT[3],
]);
state[1] = u32::from_le_bytes([
CHACHA20_CONSTANT[4],
CHACHA20_CONSTANT[5],
CHACHA20_CONSTANT[6],
CHACHA20_CONSTANT[7],
]);
state[2] = u32::from_le_bytes([
CHACHA20_CONSTANT[8],
CHACHA20_CONSTANT[9],
CHACHA20_CONSTANT[10],
CHACHA20_CONSTANT[11],
]);
state[3] = u32::from_le_bytes([
CHACHA20_CONSTANT[12],
CHACHA20_CONSTANT[13],
CHACHA20_CONSTANT[14],
CHACHA20_CONSTANT[15],
]);
for i in 0..8 {
let offset = i * 4;
state[4 + i] = u32::from_le_bytes([
key[offset],
key[offset + 1],
key[offset + 2],
key[offset + 3],
]);
}
state[12] = counter;
state[13] = u32::from_le_bytes([nonce[0], nonce[1], nonce[2], nonce[3]]);
state[14] = u32::from_le_bytes([nonce[4], nonce[5], nonce[6], nonce[7]]);
state[15] = u32::from_le_bytes([nonce[8], nonce[9], nonce[10], nonce[11]]);
let initial = state;
self.chacha20_double_round(&mut state);
let mut out = [0u8; 64];
for i in 0..16 {
let sum = state[i].wrapping_add(initial[i]);
let bytes = sum.to_le_bytes();
out[i * 4..(i + 1) * 4].copy_from_slice(&bytes);
}
out
}
pub fn poly1305_mac(
&self,
key: &[u8; POLY1305_KEY_SIZE],
message: &[u8],
) -> [u8; POLY1305_TAG_SIZE] {
let mut r_bytes = [0u8; 16];
r_bytes.copy_from_slice(&key[0..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 r = [
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],
]),
u64::from_le_bytes([r_bytes[8], r_bytes[9], r_bytes[10], r_bytes[11], 0, 0, 0, 0]),
];
let s = [
u64::from_le_bytes([
key[16], key[17], key[18], key[19], key[20], key[21], key[22], key[23],
]),
u64::from_le_bytes([
key[24], key[25], key[26], key[27], key[28], key[29], key[30], key[31],
]),
];
let mut acc: [u64; 3] = [0, 0, 0];
let mut i = 0;
while i + 16 <= message.len() {
let mut n_bytes = [0u8; 17];
n_bytes[..16].copy_from_slice(&message[i..i + 16]);
n_bytes[16] = 1; let n0 = u64::from_le_bytes([
n_bytes[0], n_bytes[1], n_bytes[2], n_bytes[3], n_bytes[4], n_bytes[5], n_bytes[6],
n_bytes[7],
]);
let n1 = u64::from_le_bytes([
n_bytes[8],
n_bytes[9],
n_bytes[10],
n_bytes[11],
n_bytes[12],
n_bytes[13],
n_bytes[14],
n_bytes[15],
]);
let n2 = u64::from_le_bytes([n_bytes[16], 0, 0, 0, 0, 0, 0, 0]);
acc[0] = acc[0].wrapping_add(n0);
acc[1] = acc[1]
.wrapping_add(n1)
.wrapping_add(if acc[0] < n0 { 1 } else { 0 });
acc[2] = acc[2]
.wrapping_add(n2)
.wrapping_add(if acc[1] < n1 { 1 } else { 0 });
let (d0, d1, d2, carry) = self.poly1305_mul_add(&acc, &r);
acc = [d0, d1, d2];
i += 16;
}
if i < message.len() {
let remaining = message.len() - i;
let mut n_bytes = [0u8; 17];
n_bytes[..remaining].copy_from_slice(&message[i..]);
n_bytes[remaining] = 1;
let n0 = u64::from_le_bytes([
n_bytes[0], n_bytes[1], n_bytes[2], n_bytes[3], n_bytes[4], n_bytes[5], n_bytes[6],
n_bytes[7],
]);
let n1 = u64::from_le_bytes([
n_bytes[8],
n_bytes[9],
n_bytes[10],
n_bytes[11],
n_bytes[12],
n_bytes[13],
n_bytes[14],
n_bytes[15],
]);
let n2 = u64::from_le_bytes([n_bytes[16], 0, 0, 0, 0, 0, 0, 0]);
acc[0] = acc[0].wrapping_add(n0);
acc[1] = acc[1]
.wrapping_add(n1)
.wrapping_add(if acc[0] < n0 { 1 } else { 0 });
acc[2] = acc[2]
.wrapping_add(n2)
.wrapping_add(if acc[1] < n1 { 1 } else { 0 });
let (d0, d1, d2, _) = self.poly1305_mul_add(&acc, &r);
acc = [d0, d1, d2];
}
self.poly1305_reduce(&mut acc);
let (sum0, carry) = acc[0].overflowing_add(s[0]);
acc[0] = sum0;
acc[1] = acc[1].wrapping_add(s[1]).wrapping_add(carry as u64);
let mut tag = [0u8; 16];
tag[..8].copy_from_slice(&acc[0].to_le_bytes());
tag[8..16].copy_from_slice(&acc[1].to_le_bytes());
tag
}
fn poly1305_mul_add(&self, acc: &[u64; 3], r: &[u64; 2]) -> (u64, u64, u64, u64) {
let r0 = r[0] & 0x0FFFFFFF_FFFFFFFF;
let r1 = r[1];
let a0 = acc[0];
let a1 = acc[1];
let a2 = acc[2];
let d0 = a0.wrapping_mul(r0);
let d1 = a0.wrapping_mul(r1).wrapping_add(a1.wrapping_mul(r0));
let d2 = a1.wrapping_mul(r1).wrapping_add(a2.wrapping_mul(r0));
let mut result = [d0, d1, d2];
self.poly1305_reduce(&mut result);
(result[0], result[1], result[2], 0)
}
fn poly1305_reduce(&self, acc: &mut [u64; 3]) {
let c = acc[2] >> 2; acc[2] &= 3;
acc[0] = acc[0].wrapping_add(c.wrapping_mul(5));
if acc[0] < c.wrapping_mul(5) {
acc[1] = acc[1].wrapping_add(1);
}
let c = acc[1] >> 2;
acc[1] &= 3;
acc[0] = acc[0].wrapping_add(c.wrapping_mul(5));
if acc[0] >= (1u64 << 64).wrapping_sub(4) {
acc[0] = acc[0].wrapping_add(5).wrapping_sub(1u64 << 64);
}
}
pub fn chacha20_poly1305_encrypt(
&self,
key: &[u8; 32],
nonce: &[u8; 12],
aad: &[u8],
plaintext: &[u8],
) -> (Vec<u8>, [u8; 16]) {
let mut poly_key = [0u8; 32];
let block = self.chacha20_block(key, nonce, 0);
poly_key.copy_from_slice(&block[0..32]);
let ciphertext = self.chacha20_encrypt(key, nonce, 1, plaintext);
let mut mac_input = Vec::new();
mac_input.extend_from_slice(aad);
let aad_pad = (16 - (aad.len() % 16)) % 16;
mac_input.extend(std::iter::repeat(0u8).take(aad_pad));
mac_input.extend_from_slice(&ciphertext);
let ct_pad = (16 - (ciphertext.len() % 16)) % 16;
mac_input.extend(std::iter::repeat(0u8).take(ct_pad));
mac_input.extend_from_slice(&(aad.len() as u64).to_le_bytes());
mac_input.extend_from_slice(&(ciphertext.len() as u64).to_le_bytes());
let tag = self.poly1305_mac(&poly_key, &mac_input);
(ciphertext, tag)
}
pub fn chacha20_poly1305_decrypt(
&self,
key: &[u8; 32],
nonce: &[u8; 12],
aad: &[u8],
ciphertext: &[u8],
tag: &[u8; 16],
) -> Result<Vec<u8>, &'static str> {
let mut poly_key = [0u8; 32];
let block = self.chacha20_block(key, nonce, 0);
poly_key.copy_from_slice(&block[0..32]);
let mut mac_input = Vec::new();
mac_input.extend_from_slice(aad);
let aad_pad = (16 - (aad.len() % 16)) % 16;
mac_input.extend(std::iter::repeat(0u8).take(aad_pad));
mac_input.extend_from_slice(ciphertext);
let ct_pad = (16 - (ciphertext.len() % 16)) % 16;
mac_input.extend(std::iter::repeat(0u8).take(ct_pad));
mac_input.extend_from_slice(&(aad.len() as u64).to_le_bytes());
mac_input.extend_from_slice(&(ciphertext.len() as u64).to_le_bytes());
let computed_tag = self.poly1305_mac(&poly_key, &mac_input);
if computed_tag != *tag {
return Err("Poly1305 MAC verification failed");
}
Ok(self.chacha20_decrypt(key, nonce, 1, ciphertext))
}
pub fn aes_encrypt_bitsliced(&self, key: &[u8; 16], plaintext: &[u8; 16]) -> [u8; 16] {
if self.aes_ni_available {
return self.aes_encrypt_hardware(key, plaintext);
}
let mut state = *plaintext;
let round_keys = self.aes_key_expansion_128(key);
Self::aes_add_round_key(&mut state, &round_keys[0]);
for round in 1..10 {
Self::aes_sub_bytes_ct(&mut state);
Self::aes_shift_rows(&mut state);
Self::aes_mix_columns(&mut state);
Self::aes_add_round_key(&mut state, &round_keys[round]);
}
Self::aes_sub_bytes_ct(&mut state);
Self::aes_shift_rows(&mut state);
Self::aes_add_round_key(&mut state, &round_keys[10]);
state
}
pub fn aes_decrypt(&self, key: &[u8; 16], ciphertext: &[u8; 16]) -> [u8; 16] {
if self.aes_ni_available {
return self.aes_decrypt_hardware(key, ciphertext);
}
let mut state = *ciphertext;
let round_keys = self.aes_key_expansion_128(key);
Self::aes_add_round_key(&mut state, &round_keys[10]);
for round in (1..10).rev() {
Self::aes_inv_shift_rows(&mut state);
Self::aes_inv_sub_bytes_ct(&mut state);
Self::aes_add_round_key(&mut state, &round_keys[round]);
Self::aes_inv_mix_columns(&mut state);
}
Self::aes_inv_shift_rows(&mut state);
Self::aes_inv_sub_bytes_ct(&mut state);
Self::aes_add_round_key(&mut state, &round_keys[0]);
state
}
#[inline(always)]
fn aes_sub_bytes_ct(state: &mut [u8; 16]) {
for byte in state.iter_mut() {
*byte = Self::aes_sbox_ct(*byte);
}
}
#[inline(always)]
fn aes_inv_sub_bytes_ct(state: &mut [u8; 16]) {
for byte in state.iter_mut() {
*byte = Self::aes_inv_sbox_ct(*byte);
}
}
fn aes_sbox_ct(byte: u8) -> u8 {
let inv = Self::gf256_inv(byte);
let mut result = inv;
result ^= result.rotate_left(1);
result ^= result.rotate_left(2);
result ^= result.rotate_left(3);
result ^= result.rotate_left(4);
result ^ 0x63
}
fn aes_inv_sbox_ct(byte: u8) -> u8 {
let mut x = byte;
x = x.rotate_right(1) ^ x.rotate_right(3) ^ x.rotate_right(6);
x ^= 0x05;
Self::gf256_inv(x)
}
fn gf256_inv(a: u8) -> u8 {
if a == 0 {
return 0;
}
let mut result: u8 = a;
let mut sq: u8 = a;
sq = Self::gf256_mul(sq, sq);
result = Self::gf256_mul(result, sq);
sq = Self::gf256_mul(sq, sq);
result = Self::gf256_mul(result, sq);
sq = Self::gf256_mul(sq, sq);
sq = Self::gf256_mul(sq, sq);
result = Self::gf256_mul(result, sq);
sq = Self::gf256_mul(sq, sq);
result = Self::gf256_mul(result, sq);
sq = Self::gf256_mul(sq, sq);
result = Self::gf256_mul(result, sq);
sq = Self::gf256_mul(sq, sq);
result = Self::gf256_mul(result, sq);
sq = Self::gf256_mul(sq, sq);
Self::gf256_mul(result, sq)
}
fn gf256_mul(a: u8, b: u8) -> u8 {
let mut result: u8 = 0;
let mut a_val = a;
let mut b_val = b;
for _ in 0..8 {
if b_val & 1 != 0 {
result ^= a_val;
}
let hi_bit = a_val & 0x80;
a_val <<= 1;
if hi_bit != 0 {
a_val ^= 0x1B; }
b_val >>= 1;
}
result
}
fn aes_shift_rows(state: &mut [u8; 16]) {
let mut out = [0u8; 16];
out[0] = state[0];
out[1] = state[5];
out[2] = state[10];
out[3] = state[15];
out[4] = state[4];
out[5] = state[9];
out[6] = state[14];
out[7] = state[3];
out[8] = state[8];
out[9] = state[13];
out[10] = state[2];
out[11] = state[7];
out[12] = state[12];
out[13] = state[1];
out[14] = state[6];
out[15] = state[11];
*state = out;
}
fn aes_inv_shift_rows(state: &mut [u8; 16]) {
let mut out = [0u8; 16];
out[0] = state[0];
out[1] = state[13];
out[2] = state[10];
out[3] = state[7];
out[4] = state[4];
out[5] = state[1];
out[6] = state[14];
out[7] = state[11];
out[8] = state[8];
out[9] = state[5];
out[10] = state[2];
out[11] = state[15];
out[12] = state[12];
out[13] = state[9];
out[14] = state[6];
out[15] = state[3];
*state = out;
}
fn aes_mix_columns(state: &mut [u8; 16]) {
for col in 0..4 {
let base = col * 4;
let s0 = state[base];
let s1 = state[base + 1];
let s2 = state[base + 2];
let s3 = state[base + 3];
let s0x2 = Self::gf256_mul(s0, 2);
let s1x2 = Self::gf256_mul(s1, 2);
let s2x2 = Self::gf256_mul(s2, 2);
let s3x2 = Self::gf256_mul(s3, 2);
state[base] = s0x2 ^ Self::gf256_mul(s1, 3) ^ s2 ^ s3;
state[base + 1] = s0 ^ s1x2 ^ Self::gf256_mul(s2, 3) ^ s3;
state[base + 2] = s0 ^ s1 ^ s2x2 ^ Self::gf256_mul(s3, 3);
state[base + 3] = Self::gf256_mul(s0, 3) ^ s1 ^ s2 ^ s3x2;
}
}
fn aes_inv_mix_columns(state: &mut [u8; 16]) {
for col in 0..4 {
let base = col * 4;
let s0 = state[base];
let s1 = state[base + 1];
let s2 = state[base + 2];
let s3 = state[base + 3];
state[base] = Self::gf256_mul(s0, 14)
^ Self::gf256_mul(s1, 11)
^ Self::gf256_mul(s2, 13)
^ Self::gf256_mul(s3, 9);
state[base + 1] = Self::gf256_mul(s0, 9)
^ Self::gf256_mul(s1, 14)
^ Self::gf256_mul(s2, 11)
^ Self::gf256_mul(s3, 13);
state[base + 2] = Self::gf256_mul(s0, 13)
^ Self::gf256_mul(s1, 9)
^ Self::gf256_mul(s2, 14)
^ Self::gf256_mul(s3, 11);
state[base + 3] = Self::gf256_mul(s0, 11)
^ Self::gf256_mul(s1, 13)
^ Self::gf256_mul(s2, 9)
^ Self::gf256_mul(s3, 14);
}
}
fn aes_add_round_key(state: &mut [u8; 16], round_key: &[u8; 16]) {
for i in 0..16 {
state[i] ^= round_key[i];
}
}
fn aes_key_expansion_128(key: &[u8; 16]) -> [[u8; 16]; 11] {
let mut round_keys = [[0u8; 16]; 11];
round_keys[0] = *key;
let mut prev = *key;
for round in 1..11 {
let mut temp = [0u8; 4];
temp[0] = prev[7];
temp[1] = prev[11];
temp[2] = prev[15];
temp[3] = prev[3];
for t in temp.iter_mut() {
*t = Self::aes_sbox_ct(*t);
}
temp[0] ^= Self::aes_rcon(round as u8);
let mut next = [0u8; 16];
for i in 0..4 {
next[i] = prev[i] ^ temp[i];
}
for i in 4..16 {
next[i] = next[i - 4] ^ prev[i];
}
round_keys[round] = next;
prev = next;
}
round_keys
}
fn aes_rcon(round: u8) -> u8 {
if round == 1 {
0x01
} else {
Self::gf256_mul(Self::aes_rcon(round - 1), 2)
}
}
fn aes_encrypt_hardware(&self, key: &[u8; 16], plaintext: &[u8; 16]) -> [u8; 16] {
let mut state = *plaintext;
#[cfg(target_arch = "x86_64")]
unsafe {
let expanded_key: [u8; 176] = [0u8; 176]; asm!(
"aesenc xmm0, xmm1",
in("xmm0") &state,
in("xmm1") &expanded_key,
options(nomem, nostack)
);
}
state
}
fn aes_decrypt_hardware(&self, _key: &[u8; 16], ciphertext: &[u8; 16]) -> [u8; 16] {
*ciphertext
}
pub fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
if a.len() != b.len() {
return false;
}
let mut diff: u8 = 0;
for (x, y) in a.iter().zip(b.iter()) {
diff |= x ^ y;
}
diff == 0
}
pub fn constant_time_select(condition: u8, a: u8, b: u8) -> u8 {
let mask = condition.wrapping_neg();
(a & mask) | (b & !mask)
}
pub fn constant_time_conditional_swap(condition: u8, a: &mut [u8], b: &mut [u8]) {
if a.len() != b.len() {
return;
}
let mask = condition.wrapping_neg();
for (x, y) in a.iter_mut().zip(b.iter_mut()) {
let t = mask & (*x ^ *y);
*x ^= t;
*y ^= t;
}
}
#[inline(always)]
pub fn secure_zeroize(buf: &mut [u8]) {
for byte in buf.iter_mut() {
unsafe {
std::ptr::write_volatile(byte, 0);
}
}
std::sync::atomic::fence(Ordering::SeqCst);
}
pub fn secure_clear<T>(val: &mut T) {
let slice = unsafe {
std::slice::from_raw_parts_mut(val as *mut T as *mut u8, std::mem::size_of::<T>())
};
Self::secure_zeroize(slice);
}
pub fn get_random_bytes(&mut self, buf: &mut [u8]) -> usize {
let mut filled = 0;
while filled + 8 <= buf.len() {
if let Some(val) = self.rdrand64() {
buf[filled..filled + 8].copy_from_slice(&val.to_le_bytes());
filled += 8;
} else {
break;
}
}
while filled < buf.len() {
let val = self.get_random_u64();
let bytes = val.to_le_bytes();
let take = (buf.len() - filled).min(8);
buf[filled..filled + take].copy_from_slice(&bytes[..take]);
filled += take;
}
self.random_bytes_generated += filled as u64;
filled
}
}
impl Default for X86CryptoHardening {
fn default() -> Self {
X86CryptoHardening::new()
}
}
pub struct X86SecurityFull {
pub cet: X86CET,
pub sgx: X86SGX,
pub memory_safety: X86MemorySafety,
pub exploit_mitigations: X86ExploitMitigations,
pub side_channel: X86SideChannel,
pub crypto: X86CryptoHardening,
pub features: X86SecurityFeatures,
pub hardened: bool,
pub init_time: Option<Instant>,
pub total_violations: u64,
pub audit_log: Vec<String>,
}
impl X86SecurityFull {
pub fn new() -> Self {
let features = Self::detect_all_features();
X86SecurityFull {
cet: X86CET::new(),
sgx: X86SGX::new(),
memory_safety: X86MemorySafety::new(),
exploit_mitigations: X86ExploitMitigations::new(),
side_channel: X86SideChannel::new(),
crypto: X86CryptoHardening::new(),
features,
hardened: false,
init_time: None,
total_violations: 0,
audit_log: Vec::new(),
}
}
pub fn detect_all_features() -> X86SecurityFeatures {
let mut features = X86SecurityFeatures::default();
#[cfg(target_arch = "x86_64")]
{
let leaf1 = unsafe { core::arch::x86_64::__cpuid(1) };
let leaf7 = unsafe { core::arch::x86_64::__cpuid_count(7, 0) };
let leaf7_1 = unsafe { core::arch::x86_64::__cpuid_count(7, 1) };
let leaf13 = unsafe { core::arch::x86_64::__cpuid_count(0xD, 1) };
let leaf12 = unsafe { core::arch::x86_64::__cpuid_count(0x12, 0) };
features.cet_ibt = (leaf7.ebx & (1 << 20)) != 0;
features.cet_ss = (leaf7.ecx & (1 << 7)) != 0;
features.sgx1 = (leaf7.ebx & (1 << 2)) != 0 && (leaf12.eax & 1) != 0;
features.sgx2 = (leaf12.eax & 2) != 0;
features.sgx_lc = (leaf12.eax & (1 << 30)) != 0;
features.smap = (leaf7.ebx & (1 << 20)) != 0;
features.smep = (leaf7.ebx & (1 << 7)) != 0;
features.umip = (leaf7.ecx & (1 << 2)) != 0;
features.pku = (leaf7.ecx & (1 << 3)) != 0;
features.pks = (leaf7.ecx & (1 << 31)) != 0;
features.rdrand = (leaf1.ecx & (1 << 30)) != 0;
features.rdseed = (leaf7.ebx & (1 << 18)) != 0;
features.aes_ni = (leaf1.ecx & (1 << 25)) != 0;
features.sha_ni = (leaf7.ebx & (1 << 29)) != 0;
features.ibrs = (leaf7.edx & (1 << 26)) != 0;
features.stibp = (leaf7.edx & (1 << 27)) != 0;
features.ssbd = (leaf7.edx & (1 << 31)) != 0;
features.md_clear = (leaf7.edx & (1 << 10)) != 0;
features.l1d_flush = (leaf7.edx & (1 << 28)) != 0;
features.taa = (leaf7_1.eax & (1 << 7)) != 0;
}
features
}
pub fn initialize_all(&mut self) {
self.init_time = Some(Instant::now());
self.audit_log.push(format!(
"[SECURITY] Initializing all security subsystems at {:?}",
self.init_time
));
let cet_state = self.cet.get_state();
self.audit_log.push(format!(
"[CET] State: {}, IBT: {}, SHSTK: {}",
cet_state, self.cet.config.ibt_available, self.cet.config.shstk_available
));
self.audit_log.push(format!(
"[SGX] Available: {}, SGX2: {}, EPC: {} bytes",
self.sgx.sgx1_available, self.sgx.sgx2_available, self.sgx.epc_size
));
self.audit_log.push(format!(
"[MEM] SMAP: {}, SMEP: {}, UMIP: {}, PKU: {}, PKS: {}",
self.memory_safety.smap_available,
self.memory_safety.smep_available,
self.memory_safety.umip_available,
self.memory_safety.pku_available,
self.memory_safety.pks_available
));
self.audit_log
.push("[EXPLOIT] Mitigations ready".to_string());
let unmitigated = self.side_channel.unmitigated_count();
self.audit_log.push(format!(
"[SIDECHANNEL] Vulnerabilities: {}, Unmitigated: {}",
self.side_channel.vulnerabilities.len(),
unmitigated
));
self.audit_log.push(format!(
"[CRYPTO] RDRAND: {}, AES-NI: {}",
self.crypto.rng.rdrand_available, self.crypto.aes_ni_available
));
}
pub fn harden_system(&mut self) {
self.audit_log
.push("[HARDEN] Applying full system hardening...".to_string());
if self.cet.is_cet_enabled() {
self.audit_log
.push("[CET] CET hardening active".to_string());
}
if self.memory_safety.smap_available {
self.memory_safety.enable_smap().ok();
}
if self.memory_safety.smep_available {
self.memory_safety.enable_smep().ok();
}
if self.memory_safety.umip_available {
self.memory_safety.enable_umip().ok();
}
self.exploit_mitigations.full_hardening();
self.audit_log.push(format!(
"[EXPLOIT] {} mitigations active",
self.exploit_mitigations.mitigation_count()
));
self.side_channel.apply_all_mitigations();
let remaining = self.side_channel.unmitigated_count();
self.audit_log.push(format!(
"[SIDECHANNEL] Applied all mitigations, {} unmitigated remaining",
remaining
));
self.hardened = true;
self.audit_log
.push("[HARDEN] Full hardening complete.".to_string());
}
pub fn security_report(&self) -> String {
let mut report = String::new();
report.push_str("========================================\n");
report.push_str(" X86 Security Hardening Report\n");
report.push_str("========================================\n\n");
report.push_str("--- CPU Security Features ---\n");
report.push_str(&format!(" CET IBT: {}\n", self.features.cet_ibt));
report.push_str(&format!(" CET Shadow Stack: {}\n", self.features.cet_ss));
report.push_str(&format!(" SGX1: {}\n", self.features.sgx1));
report.push_str(&format!(" SGX2: {}\n", self.features.sgx2));
report.push_str(&format!(" SMAP: {}\n", self.features.smap));
report.push_str(&format!(" SMEP: {}\n", self.features.smep));
report.push_str(&format!(" UMIP: {}\n", self.features.umip));
report.push_str(&format!(" PKU: {}\n", self.features.pku));
report.push_str(&format!(" PKS: {}\n", self.features.pks));
report.push_str(&format!(" RDRAND: {}\n", self.features.rdrand));
report.push_str(&format!(" RDSEED: {}\n", self.features.rdseed));
report.push_str(&format!(" AES-NI: {}\n", self.features.aes_ni));
report.push_str(&format!(" IBRS: {}\n", self.features.ibrs));
report.push_str(&format!(" STIBP: {}\n", self.features.stibp));
report.push_str(&format!(" SSBD: {}\n", self.features.ssbd));
report.push_str(&format!(
" L1D Flush: {}\n",
self.features.l1d_flush
));
report.push_str(&format!(" MD Clear: {}\n", self.features.md_clear));
report.push('\n');
report.push_str("--- CET Status ---\n");
report.push_str(&format!(" State: {}\n", self.cet.state));
report.push_str(&format!(
" Landing pads: {}\n",
self.cet.landing_pads.len()
));
report.push_str(&format!(
" Shadow stacks: {}\n",
self.cet.shadow_stacks.len()
));
report.push_str(&format!(
" ENDBR inserted: {}\n",
self.cet.endbr_inserted
));
report.push_str(&format!(" Violations: {}\n", self.cet.violations));
report.push('\n');
report.push_str("--- SGX Status ---\n");
report.push_str(&format!(" Enclave state: {}\n", self.sgx.enclave_state));
report.push_str(&format!(
" EPC pages: {}\n",
self.sgx.epc_pages.len()
));
report.push_str(&format!(
" EPC used: {} / {}\n",
self.sgx.epc_used, self.sgx.epc_size
));
report.push_str(&format!(
" Local reports: {}\n",
self.sgx.local_reports.len()
));
report.push_str(&format!(
" Remote quotes: {}\n",
self.sgx.remote_quotes.len()
));
report.push_str(&format!(
" Sealed blobs: {}\n",
self.sgx.sealed_blobs.len()
));
report.push('\n');
report.push_str("--- Memory Safety ---\n");
report.push_str(&format!(
" SMAP enabled: {}\n",
self.memory_safety.smap_enabled
));
report.push_str(&format!(
" SMAP AC: {}\n",
self.memory_safety.smap_ac
));
report.push_str(&format!(
" SMEP enabled: {}\n",
self.memory_safety.smep_enabled
));
report.push_str(&format!(
" UMIP enabled: {}\n",
self.memory_safety.umip_enabled
));
report.push_str(&format!(
" MPX violations: {}\n",
self.memory_safety.mpx_violations
));
report.push_str(&format!(
" PKU violations: {}\n",
self.memory_safety.pku_violations
));
report.push_str(&format!(
" SMAP violations: {}\n",
self.memory_safety.smap_violations
));
report.push_str(&format!(
" SMEP violations: {}\n",
self.memory_safety.smep_violations
));
report.push('\n');
report.push_str("--- Exploit Mitigations ---\n");
report.push_str(&format!(
" Active mitigations: {}\n",
self.exploit_mitigations.mitigation_count()
));
for mit in self.exploit_mitigations.list_active_mitigations() {
report.push_str(&format!(" - {}\n", mit));
}
report.push_str(&format!(
" Stack canary: {}\n",
self.exploit_mitigations.canary.active
));
report.push_str(&format!(
" ASLR: {}\n",
self.exploit_mitigations.aslr.active
));
report.push_str(&format!(
" NX: {}\n",
self.exploit_mitigations.nx_enabled
));
report.push_str(&format!(
" RELRO: {}\n",
self.exploit_mitigations.relro_level
));
report.push_str(&format!(
" Fortify: {}\n",
self.exploit_mitigations.fortify_source
));
report.push_str(&format!(
" CFI: {}\n",
self.exploit_mitigations.cfi_enabled
));
report.push_str(&format!(
" SafeStack: {}\n",
self.exploit_mitigations.safe_stack.enabled
));
report.push('\n');
report.push_str("--- Side-Channel Mitigations ---\n");
report.push_str(&format!(
" KPTI: {}\n",
self.side_channel.kpti_active
));
report.push_str(&format!(
" Retpoline: {}\n",
self.side_channel.retpoline_enabled
));
report.push_str(&format!(
" IBRS: {}\n",
self.side_channel.spec_ctrl.ibrs_active
));
report.push_str(&format!(
" STIBP: {}\n",
self.side_channel.spec_ctrl.stibp_active
));
report.push_str(&format!(
" TSX disabled: {}\n",
self.side_channel.tsx_disabled
));
report.push_str(&format!(
" Unmitigated: {}\n",
self.side_channel.unmitigated_count()
));
for vuln in &self.side_channel.vulnerabilities {
let status = if vuln.mitigated {
"MITIGATED"
} else {
"VULNERABLE"
};
report.push_str(&format!(
" {:30} [{}] -> {}\n",
vuln.vuln.to_string(),
status,
vuln.mitigation_strategy
));
}
report.push('\n');
report.push_str("--- Cryptographic Hardening ---\n");
report.push_str(&format!(
" Constant-time: {}\n",
self.crypto.constant_time
));
report.push_str(&format!(
" AES-NI: {}\n",
self.crypto.aes_ni_available
));
report.push_str(&format!(
" RDRAND: {}\n",
self.crypto.rng.rdrand_available
));
report.push_str(&format!(
" RDSEED: {}\n",
self.crypto.rng.rdseed_available
));
report.push_str(&format!(
" Random generated: {} bytes\n",
self.crypto.random_bytes_generated
));
report.push('\n');
report.push_str("========================================\n");
report.push_str(&format!(" Total violations: {}\n", self.total_violations));
report.push_str(&format!(" Hardened: {}\n", self.hardened));
report.push_str("========================================\n");
report
}
pub fn print_security_report(&self) {
eprintln!("{}", self.security_report());
}
pub fn record_violation(&mut self, subsystem: &str, description: &str) {
self.total_violations += 1;
self.audit_log.push(format!(
"[VIOLATION #{}] {}: {}",
self.total_violations, subsystem, description
));
}
pub fn validate_all(&self) -> Result<(), Vec<String>> {
let mut errors = Vec::new();
if self.cet.config.ibt_available && self.cet.state == X86CETState::Disabled {
errors.push("CET IBT available but not enabled".to_string());
}
if self.cet.config.shstk_available && self.cet.shstk_created == 0 {
errors.push("CET SHSTK available but no shadow stacks created".to_string());
}
if self.sgx.sgx1_available && self.sgx.enclave_state == X86SGXEnclaveState::Uninitialized {
}
if !self.memory_safety.nx_enabled && self.exploit_mitigations.nx_enabled {
errors.push("NX enabled in exploits but not in memory safety".to_string());
}
if self.exploit_mitigations.canary.active && self.exploit_mitigations.canary.value == 0 {
errors.push("Stack canary active but value is zero".to_string());
}
if self.side_channel.kpti_active && !self.side_channel.retpoline_enabled {
errors.push("KPTI without retpoline may still be vulnerable".to_string());
}
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
pub fn shutdown(&mut self) {
self.audit_log
.push("[SHUTDOWN] Securely shutting down security subsystems...".to_string());
for (_, _stack) in self.cet.shadow_stacks.drain() {
}
X86CryptoHardening::secure_zeroize(&mut self.crypto.chacha_key);
self.exploit_mitigations.canary.value = 0;
self.exploit_mitigations.canary.active = false;
self.exploit_mitigations.shadow_call_stack.clear();
self.exploit_mitigations.shadow_call_stack_ptr = 0;
self.hardened = false;
self.audit_log
.push("[SHUTDOWN] Security subsystems shut down.".to_string());
}
pub fn harden_minimal(&mut self) {
self.audit_log
.push("[HARDEN-MINIMAL] Applying minimal-impact hardening...".to_string());
let _ = self.exploit_mitigations.generate_canary();
self.exploit_mitigations.canary.active = true;
self.exploit_mitigations.enable_relro_full();
let _ = self.exploit_mitigations.enable_nx();
self.exploit_mitigations.enable_fortify_source();
self.audit_log
.push("[HARDEN-MINIMAL] Minimal hardening complete.".to_string());
}
pub fn harden_throughput_optimized(&mut self) {
self.audit_log
.push("[HARDEN-THROUGHPUT] Applying throughput-optimized hardening...".to_string());
if self.cet.is_cet_enabled() {
self.audit_log
.push("[CET] Low-overhead CET protection".to_string());
}
self.side_channel.enable_retpoline();
self.side_channel.issue_ibpb();
self.side_channel.enable_stibp().ok();
self.audit_log
.push("[HARDEN-THROUGHPUT] Throughput-optimized hardening complete.".to_string());
}
pub fn harden_maximum(&mut self) {
self.audit_log
.push("[HARDEN-MAX] Applying maximum-security hardening...".to_string());
self.harden_system();
self.side_channel.enable_ibrs().ok();
self.side_channel.enable_kpti();
self.side_channel.disable_tsx();
self.side_channel.mitigate_retbleed();
self.audit_log
.push("[HARDEN-MAX] Maximum hardening complete.".to_string());
}
pub fn is_feature_available(&self, feature_name: &str) -> bool {
match feature_name.to_lowercase().as_str() {
"cet-ibt" | "cet_ibt" | "ibt" => self.features.cet_ibt,
"cet-ss" | "cet_ss" | "shstk" | "shadow-stack" => self.features.cet_ss,
"sgx" | "sgx1" => self.features.sgx1,
"sgx2" | "edmm" => self.features.sgx2,
"smap" => self.features.smap,
"smep" => self.features.smep,
"umip" => self.features.umip,
"pku" | "mpk" => self.features.pku,
"pks" => self.features.pks,
"rdrand" => self.features.rdrand,
"rdseed" => self.features.rdseed,
"aes-ni" | "aesni" | "aes_ni" => self.features.aes_ni,
"sha-ni" | "shani" | "sha_ni" => self.features.sha_ni,
"ibrs" => self.features.ibrs,
"stibp" => self.features.stibp,
"ssbd" => self.features.ssbd,
"ibpb" => self.features.ibpb,
"l1d-flush" | "l1d_flush" => self.features.l1d_flush,
"md-clear" | "md_clear" => self.features.md_clear,
"taa" => self.features.taa,
"tsx" => self.features.tsx,
_ => false,
}
}
pub fn count_available_features(&self) -> u32 {
let mut count = 0u32;
if self.features.cet_ibt {
count += 1;
}
if self.features.cet_ss {
count += 1;
}
if self.features.sgx1 {
count += 1;
}
if self.features.sgx2 {
count += 1;
}
if self.features.smap {
count += 1;
}
if self.features.smep {
count += 1;
}
if self.features.umip {
count += 1;
}
if self.features.pku {
count += 1;
}
if self.features.pks {
count += 1;
}
if self.features.rdrand {
count += 1;
}
if self.features.rdseed {
count += 1;
}
if self.features.aes_ni {
count += 1;
}
if self.features.sha_ni {
count += 1;
}
if self.features.ibrs {
count += 1;
}
if self.features.stibp {
count += 1;
}
if self.features.ssbd {
count += 1;
}
if self.features.ibpb {
count += 1;
}
if self.features.l1d_flush {
count += 1;
}
if self.features.md_clear {
count += 1;
}
if self.features.taa {
count += 1;
}
if self.features.tsx {
count += 1;
}
count
}
pub fn export_status_json(&self) -> String {
format!(
r#"{{"hardened":{},"total_violations":{},"cet":{{"state":"{}","landing_pads":{},"shadow_stacks":{},"violations":{}}},"sgx":{{"enclave_state":"{}","epc_used":{},"epc_total":{}}},"memory_safety":{{"smap":{},"smep":{},"umip":{}}},"exploit_mitigations":{{"canary_active":{},"aslr_active":{},"nx":{},"relro":"{}","fortify":{},"cfi":{}}},"side_channel":{{"kpti":{},"retpoline":{},"ibrs":{},"unmitigated":{}}},"crypto":{{"constant_time":{},"rdrand_available":{},"bytes_generated":{}}}}}"#,
self.hardened,
self.total_violations,
self.cet.state.to_string(),
self.cet.landing_pads.len(),
self.cet.shadow_stacks.len(),
self.cet.violations,
self.sgx.enclave_state.to_string(),
self.sgx.epc_used,
self.sgx.epc_size,
self.memory_safety.smap_enabled,
self.memory_safety.smep_enabled,
self.memory_safety.umip_enabled,
self.exploit_mitigations.canary.active,
self.exploit_mitigations.aslr.active,
self.exploit_mitigations.nx_enabled,
self.exploit_mitigations.relro_level.to_string(),
self.exploit_mitigations.fortify_source,
self.exploit_mitigations.cfi_enabled,
self.side_channel.kpti_active,
self.side_channel.retpoline_enabled,
self.side_channel.spec_ctrl.ibrs_active,
self.side_channel.unmitigated_count(),
self.crypto.constant_time,
self.crypto.rng.rdrand_available,
self.crypto.random_bytes_generated
)
}
pub fn security_score(&self) -> u32 {
let mut score: u32 = 0;
if self.cet.is_full_protection() {
score += 15;
} else if self.cet.state == X86CETState::IBTOnly || self.cet.state == X86CETState::SHSTKOnly
{
score += 8;
}
if self.sgx.sgx1_available {
score += 3;
}
if self.sgx.sgx2_available {
score += 2;
}
if self.memory_safety.smap_enabled {
score += 4;
}
if self.memory_safety.smep_enabled {
score += 4;
}
if self.memory_safety.umip_enabled {
score += 3;
}
if self.memory_safety.pku_available {
score += 4;
}
if self.exploit_mitigations.canary.active {
score += 5;
}
if self.exploit_mitigations.aslr.active {
score += 5;
}
if self.exploit_mitigations.nx_enabled {
score += 5;
}
if self.exploit_mitigations.relro_level == X86RELROLevel::Full {
score += 5;
}
if self.exploit_mitigations.fortify_source {
score += 5;
}
if self.exploit_mitigations.cfi_enabled {
score += 5;
}
if self.side_channel.retpoline_enabled {
score += 5;
}
if self.side_channel.spec_ctrl.ibrs_active {
score += 5;
}
if self.side_channel.kpti_active {
score += 5;
}
if self.side_channel.unmitigated_count() == 0 {
score += 5;
}
if self.crypto.constant_time {
score += 5;
}
if self.crypto.aes_ni_available {
score += 5;
}
if self.crypto.rng.rdrand_available {
score += 5;
}
score.min(100)
}
pub fn rotate_crypto_keys(&mut self) {
let mut new_key = [0u8; CHACHA20_KEY_SIZE];
self.crypto.get_random_bytes(&mut new_key);
self.crypto.chacha_key = new_key;
self.crypto.rng.counter = 0;
let _ = self.exploit_mitigations.generate_canary();
self.audit_log
.push("[CRYPTO] Cryptographic keys rotated".to_string());
}
pub fn export_audit_log(&self) -> &[String] {
&self.audit_log
}
pub fn clear_audit_log(&mut self) {
self.audit_log.clear();
}
pub fn uptime(&self) -> Option<Duration> {
self.init_time.map(|t| t.elapsed())
}
pub fn mitigation_summary(&self) -> String {
let mut summary = String::new();
summary.push_str(&format!("Score: {}/100\n", self.security_score()));
summary.push_str(&format!("CET: {}\n", self.cet.state));
summary.push_str(&format!("SGX: {}\n", self.sgx.enclave_state));
summary.push_str(&format!(
"SMAP/SMEP: {}/{}\n",
self.memory_safety.smap_enabled, self.memory_safety.smep_enabled
));
summary.push_str(&format!(
"ASLR/NX/RELRO: {}/{}/{}\n",
self.exploit_mitigations.aslr.active,
self.exploit_mitigations.nx_enabled,
self.exploit_mitigations.relro_level
));
summary.push_str(&format!(
"Retpoline/IBRS/KPTI: {}/{}/{}\n",
self.side_channel.retpoline_enabled,
self.side_channel.spec_ctrl.ibrs_active,
self.side_channel.kpti_active
));
summary.push_str(&format!(
"Constant-time crypto: {}\n",
self.crypto.constant_time
));
summary
}
}
impl Default for X86SecurityFull {
fn default() -> Self {
X86SecurityFull::new()
}
}
impl fmt::Display for X86SecurityFull {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.security_report())
}
}
#[cfg(test)]
mod tests {
use super::*;
fn new_cet() -> X86CET {
X86CET::new()
}
fn new_sgx() -> X86SGX {
X86SGX::new()
}
fn new_memory_safety() -> X86MemorySafety {
X86MemorySafety::new()
}
fn new_exploit() -> X86ExploitMitigations {
X86ExploitMitigations::new()
}
fn new_side_channel() -> X86SideChannel {
X86SideChannel::new()
}
fn new_crypto() -> X86CryptoHardening {
X86CryptoHardening::new()
}
fn new_security_full() -> X86SecurityFull {
X86SecurityFull::new()
}
#[test]
fn test_cet_creation() {
let cet = new_cet();
assert!(matches!(
cet.state,
X86CETState::Disabled
| X86CETState::IBTOnly
| X86CETState::SHSTKOnly
| X86CETState::FullProtection
));
}
#[test]
fn test_cet_endbr64_generation() {
let bytes = X86CET::generate_endbr64_bytes();
assert_eq!(bytes, [0xF3, 0x0F, 0x1E, 0xFA]);
}
#[test]
fn test_cet_endbr32_generation() {
let bytes = X86CET::generate_endbr32_bytes();
assert_eq!(bytes, [0xF3, 0x0F, 0x1E, 0xFB]);
}
#[test]
fn test_cet_endbr_insertion() {
let mut cet = new_cet();
cet.config.ibt_available = true;
assert!(unsafe { cet.insert_endbr64(0x400000, "main") });
assert_eq!(cet.landing_pads.len(), 1);
assert_eq!(cet.endbr_inserted, 1);
}
#[test]
fn test_cet_verify_landing_pad() {
let mut cet = new_cet();
cet.config.ibt_available = true;
unsafe {
cet.insert_endbr64(0x400000, "test_fn");
}
assert!(cet.verify_landing_pad(0x400000));
assert!(!cet.verify_landing_pad(0x500000));
}
#[test]
fn test_cet_ibt_tracker() {
let mut cet = new_cet();
assert!(!cet.is_ibt_tracker_waiting());
cet.set_ibt_tracker(true);
assert!(cet.is_ibt_tracker_waiting());
cet.set_ibt_tracker(false);
assert!(!cet.is_ibt_tracker_waiting());
}
#[test]
fn test_cet_notrack_prefix() {
let cet = new_cet();
let mut cet_no_ibt = new_cet();
cet_no_ibt.config.ibt_available = false;
assert!(cet_no_ibt.emit_notrack_prefix(true).is_none());
let mut cet_with_ibt = new_cet();
cet_with_ibt.config.ibt_available = true;
assert_eq!(cet_with_ibt.emit_notrack_prefix(true), Some(0x3E));
}
#[test]
fn test_cet_configure_u_cet() {
let cet = new_cet();
let val = cet.configure_u_cet(true, true, false);
assert_eq!(val & X86_CET_SHSTK_EN, X86_CET_SHSTK_EN);
assert_eq!(val & X86_CET_ENDBR_EN, X86_CET_ENDBR_EN);
}
#[test]
fn test_cet_shadow_stack_creation() {
let mut cet = new_cet();
cet.config.shstk_available = true;
let result = unsafe { cet.create_shadow_stack(1, 4096 * 4) };
assert!(result.is_ok());
assert_eq!(cet.shstk_created, 1);
assert_eq!(cet.active_shadow_stacks(), 1);
}
#[test]
fn test_cet_shadow_stack_destruction() {
let mut cet = new_cet();
cet.config.shstk_available = true;
unsafe {
cet.create_shadow_stack(1, 4096 * 4).unwrap();
}
assert!(cet.destroy_shadow_stack(1));
assert_eq!(cet.active_shadow_stacks(), 0);
}
#[test]
fn test_cet_cp_fault_handling() {
let mut cet = new_cet();
assert!(cet.handle_cp_fault(0x400000, 0x1));
assert_eq!(cet.violations, 1);
assert!(cet.handle_cp_fault(0x7FFF0000, 0x2));
assert_eq!(cet.violations, 2);
}
#[test]
fn test_cet_state_display() {
assert_eq!(
X86CETState::FullProtection.to_string(),
"IBT + Shadow Stack"
);
assert_eq!(X86CETState::Disabled.to_string(), "CET Disabled");
}
#[test]
fn test_sgx_creation() {
let sgx = new_sgx();
assert_eq!(sgx.enclave_state, X86SGXEnclaveState::Uninitialized);
}
#[test]
fn test_sgx_attributes() {
let attr = X86SGXAttributes {
debug: true,
mode64bit: true,
..Default::default()
};
let flags = attr.to_flags();
assert!(flags & 0x2 != 0); assert!(flags & 0x4 != 0); let parsed = X86SGXAttributes::from_flags(flags);
assert!(parsed.debug);
assert!(parsed.mode64bit);
}
#[test]
fn test_sgx_ecreate() {
let mut sgx = new_sgx();
sgx.sgx1_available = true;
sgx.config.base_address = 0x7F0000000000;
let result = sgx.ecreate();
assert!(result.is_ok());
assert_eq!(sgx.enclave_state, X86SGXEnclaveState::InProgress);
}
#[test]
fn test_sgx_eadd() {
let mut sgx = new_sgx();
sgx.sgx1_available = true;
sgx.config.base_address = 0x7F0000000000;
sgx.epc_size = 0x10000000; sgx.ecreate().unwrap();
let data = [0xCCu8; 4096];
let result = sgx.eadd(0x1000, &data, X86EPCPageType::REG, 0x7);
assert!(result.is_ok());
assert!(sgx.epc_pages.len() >= 2); }
#[test]
fn test_sgx_einit() {
let mut sgx = new_sgx();
sgx.sgx1_available = true;
sgx.config.base_address = 0x7F0000000000;
sgx.epc_size = 0x10000000;
sgx.ecreate().unwrap();
let sigstruct = vec![0u8; 1808];
let result = sgx.einit(&sigstruct);
assert!(result.is_err());
}
#[test]
fn test_sgx_sealing() {
let mut sgx = new_sgx();
sgx.sgx1_available = true;
sgx.config.base_address = 0x7F0000000000;
sgx.epc_size = 0x10000000;
sgx.ecreate().unwrap();
sgx.enclave_state = X86SGXEnclaveState::Initialized;
let plaintext = b"secret enclave data";
let result = sgx.seal_data(plaintext, X86_SGX_KEYPOLICY_MRENCLAVE, b"additional data");
assert!(result.is_ok());
let sealed = result.unwrap();
assert_eq!(sealed.payload_size, plaintext.len() as u32);
let unseal_result = sgx.unseal_data(&sealed, b"additional data");
assert!(unseal_result.is_ok());
assert_eq!(unseal_result.unwrap(), plaintext);
}
#[test]
fn test_sgx_ereport() {
let mut sgx = new_sgx();
sgx.sgx1_available = true;
sgx.enclave_state = X86SGXEnclaveState::Initialized;
let target_info = sgx.get_target_info();
let report_data = [0xABu8; 64];
let result = sgx.ereport(&target_info, &report_data);
assert!(result.is_ok());
let report = result.unwrap();
assert_eq!(report.report_data, report_data);
}
#[test]
fn test_sgx_remote_attestation() {
let mut sgx = new_sgx();
sgx.sgx1_available = true;
sgx.enclave_state = X86SGXEnclaveState::Initialized;
let target_info = sgx.get_target_info();
let report = sgx.ereport(&target_info, &[0x42u8; 64]).unwrap();
let quote = sgx.generate_quote(&report, X86SGXAttestationType::RemoteECDSA);
assert!(quote.is_ok());
}
#[test]
fn test_sgx_sgx2_eaug() {
let mut sgx = new_sgx();
sgx.sgx2_available = true;
sgx.config.base_address = 0x7F0000000000;
sgx.enclave_state = X86SGXEnclaveState::Initialized;
let result = sgx.eaug(0x2000, X86EPCPageType::REG);
assert!(result.is_ok());
}
#[test]
fn test_sgx_sgx2_emodpr() {
let mut sgx = new_sgx();
sgx.sgx2_available = true;
sgx.config.base_address = 0x7F0000000000;
sgx.enclave_state = X86SGXEnclaveState::Initialized;
sgx.eaug(0x2000, X86EPCPageType::REG).unwrap();
sgx.eaccept(0x2000).unwrap();
let page = sgx
.epc_pages
.iter()
.find(|p| p.enclave_offset == 0x2000)
.unwrap();
assert_eq!(page.permissions, 0);
}
#[test]
fn test_sgx_epc_management() {
let mut sgx = new_sgx();
sgx.epc_size = 0x10000000;
let result = sgx.epa(X86EPCPageType::VA);
assert!(result.is_ok());
let epc_addr = result.unwrap();
let ewb_result = sgx.ewb(epc_addr, 0x1000);
assert!(ewb_result.is_ok());
}
#[test]
fn test_sgx_enclave_state_display() {
assert_eq!(X86SGXEnclaveState::Initialized.to_string(), "Initialized");
assert_eq!(X86SGXEnclaveState::Sealed.to_string(), "Sealed");
}
#[test]
fn test_sgx_stats() {
let sgx = new_sgx();
let stats = sgx.get_stats();
assert_eq!(stats.epc_pages, 0);
assert_eq!(stats.local_reports, 0);
}
#[test]
fn test_sgx_hash_sha256() {
let sgx = new_sgx();
let hash = sgx.hash_sha256(b"hello");
assert_eq!(hash.len(), 32);
let hash2 = sgx.hash_sha256(b"Hello");
assert_ne!(hash, hash2);
}
#[test]
fn test_memory_safety_creation() {
let ms = new_memory_safety();
assert!(!ms.smap_enabled);
assert!(!ms.smep_enabled);
assert!(!ms.umip_enabled);
}
#[test]
fn test_mpx_bounds() {
let mut ms = new_memory_safety();
ms.mpx_available = true;
assert!(unsafe { ms.bndmk(0, 0x1000, 0x100) }.is_ok());
assert!(ms.mpx_bounds[0].valid);
assert_eq!(ms.mpx_bounds[0].lower_bound, 0x1000);
assert_eq!(ms.mpx_bounds[0].upper_bound, 0x1100);
}
#[test]
fn test_mpx_bounds_check() {
let mut ms = new_memory_safety();
ms.mpx_available = true;
unsafe {
ms.bndmk(0, 0x1000, 0x100).unwrap();
}
assert!(unsafe { ms.bndcl(0, 0x1050).is_ok() });
assert!(unsafe { ms.bndcu(0, 0x1050, 8).is_ok() });
assert!(unsafe { ms.bndcl(0, 0x0FFF).is_err() });
assert_eq!(ms.mpx_violations, 1);
assert!(unsafe { ms.bndcu(0, 0x10F8, 16).is_err() });
assert_eq!(ms.mpx_violations, 2);
}
#[test]
fn test_mpx_bndcn() {
let mut ms = new_memory_safety();
ms.mpx_available = true;
unsafe {
ms.bndmk(0, 0x1000, 0x100).unwrap();
}
assert!(unsafe { ms.bndcn(0, 0x1000).is_ok() });
assert!(unsafe { ms.bndcn(0, 0x1101).is_err() });
}
#[test]
fn test_pku_wrpkru() {
let mut ms = new_memory_safety();
ms.pku_available = true;
unsafe {
ms.wrpkru(0x5555_5555);
}
assert_eq!(ms.pkru, 0x5555_5555);
unsafe {
ms.wrpkru(0);
}
assert_eq!(ms.pkru, 0);
}
#[test]
fn test_pku_key_rights() {
let mut ms = new_memory_safety();
ms.pku_available = true;
assert!(ms.set_key_rights(5, true, false).is_ok());
let state = ms.get_key_rights(5).unwrap();
assert!(state.access_disabled);
assert!(!state.write_disabled);
}
#[test]
fn test_pku_lockdown() {
let mut ms = new_memory_safety();
ms.pku_available = true;
ms.pkru_lockdown();
for i in 0..16 {
let state = ms.get_key_rights(i).unwrap();
assert!(state.access_disabled);
}
}
#[test]
fn test_smap_enable() {
let mut ms = new_memory_safety();
ms.smap_available = true;
assert!(ms.enable_smap().is_ok());
assert!(ms.smap_enabled);
}
#[test]
fn test_smap_clac_stac() {
let mut ms = new_memory_safety();
ms.smap_available = true;
ms.enable_smap().unwrap();
assert!(!ms.smap_ac);
assert!(!ms.is_user_access_allowed());
unsafe {
ms.stac();
}
assert!(ms.smap_ac);
assert!(ms.is_user_access_allowed());
unsafe {
ms.clac();
}
assert!(!ms.smap_ac);
assert!(!ms.is_user_access_allowed());
}
#[test]
fn test_smep_check() {
let mut ms = new_memory_safety();
ms.smep_available = true;
ms.enable_smep().unwrap();
assert!(!ms.check_smep(0x0000_0000_0040_0000, true));
assert!(ms.check_smep(0xFFFF_FFFF_8000_0000, true));
assert!(ms.check_smep(0x0000_0000_0040_0000, false));
}
#[test]
fn test_umip_enable() {
let mut ms = new_memory_safety();
ms.umip_available = true;
assert!(ms.enable_umip().is_ok());
assert!(ms.umip_enabled);
}
#[test]
fn test_memory_safety_type_display() {
assert_eq!(
X86MemorySafetyType::SMAP.to_string(),
"Supervisor Mode Access Prevention"
);
assert_eq!(
X86MemorySafetyType::MPX.to_string(),
"Memory Protection Extensions"
);
}
#[test]
fn test_exploit_creation() {
let exploit = new_exploit();
assert!(!exploit.canary.active);
assert!(!exploit.aslr.active);
assert!(!exploit.nx_enabled);
}
#[test]
fn test_stack_canary_generation() {
let mut exploit = new_exploit();
let canary = exploit.generate_canary();
assert_eq!(canary & 0xFF, 0);
assert!(exploit.canary.active);
}
#[test]
fn test_canary_verification() {
let mut exploit = new_exploit();
let canary = exploit.generate_canary();
assert!(exploit.verify_canary(canary));
assert!(!exploit.verify_canary(canary ^ 1));
}
#[test]
fn test_canary_code_emission() {
let mut exploit = new_exploit();
exploit.generate_canary();
let code = exploit.emit_canary_check();
assert!(!code.is_empty());
assert!(code.len() >= 4);
}
#[test]
fn test_aslr_enable() {
let mut exploit = new_exploit();
assert!(exploit.enable_aslr().is_ok());
assert!(exploit.aslr.active);
assert!(exploit.aslr.pie_enabled);
assert!(exploit.aslr.stack_randomize);
}
#[test]
fn test_pie_enable() {
let mut exploit = new_exploit();
exploit.enable_pie();
assert!(exploit.aslr.pie_enabled);
assert!(exploit
.active_mitigations
.contains(&X86ExploitMitigationType::PIE));
}
#[test]
fn test_aslr_randomization() {
let mut exploit = new_exploit();
exploit.enable_aslr().unwrap();
let stack1 = exploit.randomize_stack();
let stack2 = exploit.randomize_stack();
assert_ne!(stack1, 0);
}
#[test]
fn test_relro_levels() {
let mut exploit = new_exploit();
assert_eq!(exploit.relro_level, X86RELROLevel::None);
exploit.enable_relro_partial();
assert_eq!(exploit.relro_level, X86RELROLevel::Partial);
exploit.enable_relro_full();
assert_eq!(exploit.relro_level, X86RELROLevel::Full);
}
#[test]
fn test_nx_enable() {
let mut exploit = new_exploit();
assert!(exploit.enable_nx().is_ok());
assert!(exploit.nx_enabled);
}
#[test]
fn test_wxorx_enforcement() {
let mut exploit = new_exploit();
exploit.enable_nx().unwrap();
assert!(!exploit.enforce_wxorx(0x6)); assert!(exploit.enforce_wxorx(0x4)); assert!(exploit.enforce_wxorx(0x2)); }
#[test]
fn test_fortify_memcpy_chk() {
let mut exploit = new_exploit();
exploit.enable_fortify_source();
let src = [0x41u8; 64];
let mut dst = [0u8; 128];
let result = exploit.memcpy_chk(128, &src, &mut dst, 16);
assert!(result.is_ok());
let result = exploit.memcpy_chk(64, &src, &mut dst, 128);
assert!(result.is_err());
}
#[test]
fn test_fortify_memset_chk() {
let mut exploit = new_exploit();
exploit.enable_fortify_source();
let mut buf = [0u8; 64];
assert!(exploit.memset_chk(&mut buf, 0xAA, 32, 64).is_ok());
assert!(exploit.memset_chk(&mut buf, 0xBB, 128, 64).is_err());
}
#[test]
fn test_cfi_forward_edge() {
let mut exploit = new_exploit();
exploit.enable_cfi();
assert!(exploit.cfi_check_forward_edge(0x400000, 0xDEAD_BEEF));
assert_eq!(exploit.cfi_checks, 1);
}
#[test]
fn test_cfi_backward_edge() {
let mut exploit = new_exploit();
exploit.enable_cfi();
assert!(exploit.cfi_check_backward_edge(0x400100));
assert_eq!(exploit.shadow_call_stack.len(), 1);
assert!(exploit.cfi_verify_return(0x400100));
assert_eq!(exploit.shadow_call_stack.len(), 0);
}
#[test]
fn test_shadow_call_stack() {
let mut exploit = new_exploit();
exploit.enable_cfi();
exploit.shadow_call_push(0x400200);
exploit.shadow_call_push(0x400300);
assert_eq!(exploit.shadow_call_stack.len(), 2);
assert!(exploit.shadow_call_pop(0x400300));
assert_eq!(exploit.shadow_call_stack.len(), 1);
assert!(!exploit.shadow_call_pop(0xDEAD));
assert_eq!(exploit.cfi_violations, 1);
}
#[test]
fn test_safe_stack() {
let mut exploit = new_exploit();
assert!(exploit.enable_safe_stack().is_ok());
assert!(exploit.safe_stack.enabled);
assert!(exploit.is_safe_stack_object(false)); assert!(!exploit.is_safe_stack_object(true)); }
#[test]
fn test_full_hardening() {
let mut exploit = new_exploit();
exploit.full_hardening();
assert!(exploit.canary.active);
assert!(exploit.aslr.active);
assert!(exploit.nx_enabled);
assert_eq!(exploit.relro_level, X86RELROLevel::Full);
assert!(exploit.fortify_source);
assert!(exploit.cfi_enabled);
assert!(exploit.safe_stack.enabled);
assert!(exploit.mitigation_count() >= 7);
}
#[test]
fn test_mitigation_types_display() {
assert_eq!(
X86ExploitMitigationType::StackProtector.to_string(),
"Stack Protector (Canary)"
);
assert_eq!(
X86ExploitMitigationType::ASLR.to_string(),
"Address Space Layout Randomization"
);
assert_eq!(
X86ExploitMitigationType::NX.to_string(),
"No-Execute (NX/W^X)"
);
}
#[test]
fn test_side_channel_creation() {
let sc = new_side_channel();
assert!(!sc.kpti_active);
assert!(!sc.retpoline_enabled);
assert!(!sc.tsx_disabled);
}
#[test]
fn test_side_channel_vulnerability_assessment() {
let sc = new_side_channel();
assert!(!sc.vulnerabilities.is_empty());
assert!(sc
.vulnerabilities
.iter()
.any(|v| v.vuln == X86SideChannelVuln::SpectreV1));
assert!(sc
.vulnerabilities
.iter()
.any(|v| v.vuln == X86SideChannelVuln::Meltdown));
}
#[test]
fn test_spectre_v1_barrier() {
let barrier = X86SideChannel::spectre_v1_barrier_lfence();
assert_eq!(barrier, [0x0F, 0xAE, 0xE8]);
}
#[test]
fn test_spectre_v1_array_mask() {
assert_eq!(X86SideChannel::spectre_v1_array_mask(5, 10), 5);
assert_eq!(X86SideChannel::spectre_v1_array_mask(20, 10), 0);
assert_eq!(X86SideChannel::spectre_v1_array_mask(10, 10), 0); }
#[test]
fn test_mitigate_spectre_v1() {
let mut sc = new_side_channel();
sc.mitigate_spectre_v1();
let v1 = sc
.vulnerabilities
.iter()
.find(|v| v.vuln == X86SideChannelVuln::SpectreV1)
.unwrap();
assert!(v1.mitigated);
}
#[test]
fn test_retpoline_generation() {
let sc = new_side_channel();
let thunk = sc.generate_retpoline_thunk(0x400000);
assert!(thunk.contains(&0xC3));
assert!(thunk.len() > 10);
}
#[test]
fn test_enable_retpoline() {
let mut sc = new_side_channel();
sc.enable_retpoline();
assert!(sc.retpoline_enabled);
let v2 = sc
.vulnerabilities
.iter()
.find(|v| v.vuln == X86SideChannelVuln::SpectreV2)
.unwrap();
assert!(v2.mitigated);
}
#[test]
fn test_issue_ibpb() {
let mut sc = new_side_channel();
assert!(!sc.spec_ctrl.ibpb_issued);
sc.issue_ibpb();
assert!(sc.spec_ctrl.ibpb_issued);
}
#[test]
fn test_kpti() {
let mut sc = new_side_channel();
assert!(!sc.kpti_active);
sc.enable_kpti();
assert!(sc.kpti_active);
sc.disable_kpti();
assert!(!sc.kpti_active);
}
#[test]
fn test_kpti_trampoline() {
let sc = new_side_channel();
let trampoline = sc.generate_kpti_trampoline();
assert!(!trampoline.is_empty());
assert!(trampoline.windows(3).any(|w| w == [0x0F, 0x20, 0xD8]));
assert!(trampoline.windows(3).any(|w| w == [0x0F, 0x22, 0xD8]));
}
#[test]
fn test_flush_l1d() {
let mut sc = new_side_channel();
sc.flush_l1d();
assert!(sc.l1d_flush_on_vmentry);
let l1tf = sc
.vulnerabilities
.iter()
.find(|v| v.vuln == X86SideChannelVuln::L1TF)
.unwrap();
assert!(l1tf.mitigated);
}
#[test]
fn test_verw_buffer_clear() {
let buffer = X86SideChannel::verw_clear_buffers();
assert!(!buffer.is_empty());
}
#[test]
fn test_mds_mitigation() {
let mut sc = new_side_channel();
sc.mitigate_mds();
assert!(sc.mds_mitigated);
let mds = sc
.vulnerabilities
.iter()
.find(|v| v.vuln == X86SideChannelVuln::MDS)
.unwrap();
assert!(mds.mitigated);
}
#[test]
fn test_disable_tsx() {
let mut sc = new_side_channel();
sc.disable_tsx();
assert!(sc.tsx_disabled);
}
#[test]
fn test_srbds_mitigation() {
let mut sc = new_side_channel();
sc.mitigate_srbds();
assert!(sc.srbds_mitigated);
}
#[test]
fn test_retbleed_mitigation() {
let mut sc = new_side_channel();
sc.mitigate_retbleed();
assert!(sc.retbleed_mitigated);
assert!(sc.retpoline_enabled);
}
#[test]
fn test_rsb_stuffing() {
let sc = new_side_channel();
let stuffing = sc.generate_rsb_stuffing(4);
assert!(stuffing.len() >= 4 * 6);
}
#[test]
fn test_speculation_barrier_emission() {
let sc = new_side_channel();
let barrier = sc.emit_speculation_barrier();
assert_eq!(barrier, vec![0x0F, 0xAE, 0xE8]);
}
#[test]
fn test_apply_all_mitigations() {
let mut sc = new_side_channel();
sc.apply_all_mitigations();
assert!(sc.kpti_active);
assert!(sc.retpoline_enabled);
assert!(sc.tsx_disabled);
assert!(sc.mds_mitigated);
assert!(sc.srbds_mitigated);
assert!(sc.retbleed_mitigated);
}
#[test]
fn test_unmitigated_count() {
let mut sc = new_side_channel();
let before = sc.unmitigated_count();
assert!(before > 0);
sc.apply_all_mitigations();
let after = sc.unmitigated_count();
assert!(after < before);
}
#[test]
fn test_vulnerability_report() {
let sc = new_side_channel();
let report = sc.get_vulnerability_report();
assert!(!report.is_empty());
assert_eq!(report.len(), 9);
}
#[test]
fn test_side_channel_vuln_display() {
assert_eq!(
X86SideChannelVuln::SpectreV1.to_string(),
"Spectre v1 (Bounds Check Bypass)"
);
assert_eq!(
X86SideChannelVuln::Retbleed.to_string(),
"Retbleed (RSB Poisoning)"
);
}
#[test]
fn test_crypto_creation() {
let crypto = new_crypto();
assert!(crypto.constant_time);
assert_eq!(crypto.chacha_key.len(), CHACHA20_KEY_SIZE);
}
#[test]
fn test_constant_time_eq() {
let a = [0xAAu8; 32];
let b = [0xAAu8; 32];
let c = [0xBBu8; 32];
assert!(X86CryptoHardening::constant_time_eq(&a, &b));
assert!(!X86CryptoHardening::constant_time_eq(&a, &c));
assert!(!X86CryptoHardening::constant_time_eq(&a, &[0xAAu8; 16]));
}
#[test]
fn test_constant_time_select() {
assert_eq!(
X86CryptoHardening::constant_time_select(1, 0x41, 0x42),
0x41
);
assert_eq!(
X86CryptoHardening::constant_time_select(0, 0x41, 0x42),
0x42
);
assert_eq!(
X86CryptoHardening::constant_time_select(255, 0xAA, 0xBB),
0xAA
);
}
#[test]
fn test_constant_time_conditional_swap() {
let mut a = [0xAAu8; 4];
let mut b = [0x55u8; 4];
X86CryptoHardening::constant_time_conditional_swap(1, &mut a, &mut b);
assert_eq!(a, [0x55u8; 4]);
assert_eq!(b, [0xAAu8; 4]);
X86CryptoHardening::constant_time_conditional_swap(0, &mut a, &mut b);
assert_eq!(a, [0x55u8; 4]);
assert_eq!(b, [0xAAu8; 4]);
}
#[test]
fn test_secure_zeroize() {
let mut buf = [0xDEu8; 64];
X86CryptoHardening::secure_zeroize(&mut buf);
assert!(buf.iter().all(|&b| b == 0));
}
#[test]
fn test_get_random_u64() {
let mut crypto = new_crypto();
let r1 = crypto.get_random_u64();
let r2 = crypto.get_random_u64();
assert_ne!(r1, r2);
}
#[test]
fn test_get_random_bytes() {
let mut crypto = new_crypto();
let mut buf = [0u8; 128];
let filled = crypto.get_random_bytes(&mut buf);
assert_eq!(filled, 128);
assert!(buf.iter().any(|&b| b != 0));
}
#[test]
fn test_chacha20_block() {
let crypto = new_crypto();
let key = [0xABu8; 32];
let nonce = [0xCDu8; 12];
let block = crypto.chacha20_block(&key, &nonce, 0);
assert_eq!(block.len(), 64);
assert!(block.iter().any(|&b| b != 0));
let block2 = crypto.chacha20_block(&key, &nonce, 0);
assert_eq!(block, block2);
let block3 = crypto.chacha20_block(&key, &nonce, 1);
assert_ne!(block, block3);
}
#[test]
fn test_chacha20_encrypt_decrypt() {
let crypto = new_crypto();
let key = [0x01u8; 32];
let nonce = [0x02u8; 12];
let plaintext = b"Hello, ChaCha20! This is a test of the stream cipher.";
let ciphertext = crypto.chacha20_encrypt(&key, &nonce, 0, plaintext);
assert_eq!(ciphertext.len(), plaintext.len());
assert_ne!(&ciphertext[..], plaintext);
let decrypted = crypto.chacha20_decrypt(&key, &nonce, 0, &ciphertext);
assert_eq!(decrypted, plaintext);
}
#[test]
fn test_chacha20_empty_data() {
let crypto = new_crypto();
let key = [0x03u8; 32];
let nonce = [0x04u8; 12];
let ct = crypto.chacha20_encrypt(&key, &nonce, 0, b"");
assert!(ct.is_empty());
}
#[test]
fn test_poly1305_mac() {
let crypto = new_crypto();
let key = [0x85u8; 32];
let msg = b"Cryptographic Forum Research Group";
let tag = crypto.poly1305_mac(&key, msg);
assert_eq!(tag.len(), 16);
let tag2 = crypto.poly1305_mac(&key, msg);
assert_eq!(tag, tag2);
let tag3 = crypto.poly1305_mac(&key, b"Different message");
assert_ne!(tag, tag3);
}
#[test]
fn test_chacha20_poly1305_aead() {
let crypto = new_crypto();
let key = [0x10u8; 32];
let nonce = [0x20u8; 12];
let aad = b"Test AAD";
let plaintext = b"Authenticated encryption test message";
let (ciphertext, tag) = crypto.chacha20_poly1305_encrypt(&key, &nonce, aad, plaintext);
assert!(!ciphertext.is_empty());
assert_eq!(tag.len(), 16);
let decrypted = crypto.chacha20_poly1305_decrypt(&key, &nonce, aad, &ciphertext, &tag);
assert!(decrypted.is_ok());
assert_eq!(decrypted.unwrap(), plaintext);
let wrong_tag = [0xFFu8; 16];
let failed = crypto.chacha20_poly1305_decrypt(&key, &nonce, aad, &ciphertext, &wrong_tag);
assert!(failed.is_err());
}
#[test]
fn test_aes_key_expansion() {
let key = [0x2Bu8; 16];
let round_keys = X86CryptoHardening::aes_key_expansion_128(&key);
assert_eq!(round_keys.len(), 11);
assert_eq!(round_keys[0], key);
}
#[test]
fn test_aes_encrypt_decrypt() {
let crypto = new_crypto();
let key = [0x2Bu8; 16];
let plaintext = [0x6Bu8; 16];
let ciphertext = crypto.aes_encrypt_bitsliced(&key, &plaintext);
assert_ne!(ciphertext, plaintext);
let decrypted = crypto.aes_decrypt(&key, &ciphertext);
assert_eq!(decrypted, plaintext);
}
#[test]
fn test_aes_sbox_ct() {
let s = X86CryptoHardening::aes_sbox_ct(0x53);
assert_ne!(s, 0);
}
#[test]
fn test_aes_mix_columns() {
let mut state = [
0xDBu8, 0x13, 0x53, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
];
let original = state;
X86CryptoHardening::aes_mix_columns(&mut state);
assert_ne!(state, original);
}
#[test]
fn test_aes_shift_rows() {
let mut state = [
0x00, 0x01, 0x02, 0x03, 0x10, 0x11, 0x12, 0x13, 0x20, 0x21, 0x22, 0x23, 0x30, 0x31,
0x32, 0x33,
];
let original = state;
X86CryptoHardening::aes_shift_rows(&mut state);
assert_ne!(state, original);
X86CryptoHardening::aes_inv_shift_rows(&mut state);
assert_eq!(state, original);
}
#[test]
fn test_gf256_operations() {
let mul = X86CryptoHardening::gf256_mul(0x57, 0x01);
assert_eq!(mul, 0x57);
let left = X86CryptoHardening::gf256_mul(0x57, 0x83 ^ 0xC1);
let right =
X86CryptoHardening::gf256_mul(0x57, 0x83) ^ X86CryptoHardening::gf256_mul(0x57, 0xC1);
assert_eq!(left, right);
}
#[test]
fn test_gf256_inv() {
let a = 0xAB;
let inv = X86CryptoHardening::gf256_inv(a);
let product = X86CryptoHardening::gf256_mul(a, inv);
assert_eq!(product, 1);
assert_eq!(X86CryptoHardening::gf256_inv(0), 0);
}
#[test]
fn test_rcon() {
assert_eq!(X86CryptoHardening::aes_rcon(1), 0x01);
assert_eq!(X86CryptoHardening::aes_rcon(2), 0x02);
assert_eq!(X86CryptoHardening::aes_rcon(3), 0x04);
}
#[test]
fn test_crypto_algorithm_display() {
assert_eq!(X86CryptoAlgorithm::ChaCha20.to_string(), "ChaCha20");
assert_eq!(X86CryptoAlgorithm::AES256GCM.to_string(), "AES-256-GCM");
}
#[test]
fn test_security_full_creation() {
let sf = new_security_full();
assert!(!sf.hardened);
assert!(sf.audit_log.is_empty());
}
#[test]
fn test_security_full_initialize() {
let mut sf = new_security_full();
sf.initialize_all();
assert!(sf.init_time.is_some());
assert!(!sf.audit_log.is_empty());
assert!(sf.audit_log.iter().any(|e| e.contains("CET")));
assert!(sf.audit_log.iter().any(|e| e.contains("SGX")));
assert!(sf.audit_log.iter().any(|e| e.contains("MEM")));
assert!(sf.audit_log.iter().any(|e| e.contains("SIDECHANNEL")));
assert!(sf.audit_log.iter().any(|e| e.contains("CRYPTO")));
}
#[test]
fn test_security_full_hardening() {
let mut sf = new_security_full();
sf.initialize_all();
sf.harden_system();
assert!(sf.hardened);
assert!(sf.audit_log.iter().any(|e| e.contains("HARDEN")));
assert!(sf.exploit_mitigations.canary.active);
assert!(sf.exploit_mitigations.nx_enabled);
assert!(sf.side_channel.kpti_active);
}
#[test]
fn test_security_full_report() {
let mut sf = new_security_full();
sf.initialize_all();
sf.harden_system();
let report = sf.security_report();
assert!(report.contains("X86 Security Hardening Report"));
assert!(report.contains("CET"));
assert!(report.contains("SGX"));
assert!(report.contains("Memory Safety"));
assert!(report.contains("Exploit Mitigations"));
assert!(report.contains("Side-Channel"));
assert!(report.contains("Cryptographic Hardening"));
}
#[test]
fn test_security_full_display() {
let mut sf = new_security_full();
sf.initialize_all();
sf.harden_system();
let display_str = format!("{}", sf);
assert!(display_str.contains("X86 Security Hardening Report"));
}
#[test]
fn test_security_full_record_violation() {
let mut sf = new_security_full();
assert_eq!(sf.total_violations, 0);
sf.record_violation("CET", "IBT violation at 0x400000");
assert_eq!(sf.total_violations, 1);
sf.record_violation("SGX", "Invalid EINIT token");
assert_eq!(sf.total_violations, 2);
assert_eq!(sf.audit_log.len(), 2);
}
#[test]
fn test_security_full_validate() {
let sf = new_security_full();
let result = sf.validate_all();
assert!(result.is_ok());
}
#[test]
fn test_security_full_shutdown() {
let mut sf = new_security_full();
sf.initialize_all();
sf.harden_system();
sf.shutdown();
assert!(!sf.hardened);
assert_eq!(sf.exploit_mitigations.canary.value, 0);
assert!(!sf.exploit_mitigations.canary.active);
assert!(sf.cet.shadow_stacks.is_empty());
assert!(sf.audit_log.iter().any(|e| e.contains("SHUTDOWN")));
}
#[test]
fn test_security_features_probing() {
let features = X86SecurityFull::detect_all_features();
let _ = features;
}
#[test]
fn test_security_full_integration_cet() {
let mut sf = new_security_full();
sf.initialize_all();
sf.cet.config.ibt_available = true;
unsafe {
sf.cet.insert_endbr64(0x400000, "integrated_test");
}
assert!(sf.cet.verify_landing_pad(0x400000));
}
#[test]
fn test_security_full_integration_memory() {
let mut sf = new_security_full();
sf.initialize_all();
sf.memory_safety.smep_available = true;
sf.memory_safety.enable_smep().unwrap();
assert!(sf.memory_safety.smep_enabled);
}
#[test]
fn test_security_full_integration_crypto_exploit() {
let mut sf = new_security_full();
sf.initialize_all();
let canary = sf.exploit_mitigations.generate_canary();
assert_ne!(canary, 0);
assert!(sf.crypto.random_bytes_generated > 0 || canary != 0);
}
#[test]
fn test_security_features_default() {
let features = X86SecurityFeatures::default();
assert!(!features.cet_ibt);
assert!(!features.sgx1);
assert!(!features.smap);
assert!(!features.rdrand);
assert!(!features.aes_ni);
}
#[test]
fn test_cet_config_default() {
let config = X86CETConfig::default();
assert!(!config.ibt_available);
assert!(!config.shstk_available);
assert_eq!(config.shstk_size, X86_CET_SHSTK_SIZE_PAGES * 4096);
}
#[test]
fn test_cet_constants() {
assert_eq!(X86_CET_SHSTK_TOKEN_SIZE, 8);
assert_eq!(X86_CET_ENDBR64, [0xF3, 0x0F, 0x1E, 0xFA]);
assert_eq!(X86_CET_SHSTK_EN, 1);
assert_eq!(X86_CET_ENDBR_EN, 4);
}
#[test]
fn test_sgx_constants() {
assert_eq!(X86_SGX_PAGE_SIZE, 4096);
assert_eq!(X86_SGX_SECS_SIZE, 4096);
assert_eq!(X86_SGX_TCS_SIZE, 4096);
assert_eq!(X86_SGX_SUCCESS, 0);
assert_eq!(X86_SGX_PG_INVLD, 6);
}
#[test]
fn test_mpx_constants() {
assert_eq!(X86_MPX_BND_COUNT, 4);
assert_eq!(X86_MPX_BOUND_TABLE_ENTRY_SIZE, 32);
assert_eq!(X86_MPK_KEY_COUNT, 16);
assert_eq!(X86_MPK_DISABLE_ACCESS, 1);
assert_eq!(X86_MPK_DISABLE_WRITE, 2);
}
#[test]
fn test_cr4_constants() {
assert_eq!(X86_CR4_SMEP, 1 << 20);
assert_eq!(X86_CR4_SMAP, 1 << 21);
assert_eq!(X86_CR4_PKE, 1 << 22);
assert_eq!(X86_CR4_CET, 1 << 23);
assert_eq!(X86_CR4_PKS, 1 << 24);
}
#[test]
fn test_spec_ctrl_constants() {
assert_eq!(X86_SPEC_CTRL_IBRS, 1);
assert_eq!(X86_SPEC_CTRL_STIBP, 2);
assert_eq!(X86_SPEC_CTRL_SSBD, 4);
assert_eq!(X86_PRED_CMD_IBPB, 1);
assert_eq!(X86_FLUSH_CMD_L1D, 1);
}
#[test]
fn test_chacha20_constants() {
assert_eq!(CHACHA20_KEY_SIZE, 32);
assert_eq!(CHACHA20_NONCE_SIZE, 12);
assert_eq!(CHACHA20_BLOCK_SIZE, 64);
assert_eq!(CHACHA20_ROUNDS, 20);
}
#[test]
fn test_poly1305_constants() {
assert_eq!(POLY1305_KEY_SIZE, 32);
assert_eq!(POLY1305_TAG_SIZE, 16);
assert_eq!(POLY1305_BLOCK_SIZE, 16);
}
#[test]
fn test_aes_constants() {
assert_eq!(AES_BLOCK_SIZE, 16);
assert_eq!(AES128_KEY_SIZE, 16);
assert_eq!(AES192_KEY_SIZE, 24);
assert_eq!(AES256_KEY_SIZE, 32);
}
#[test]
fn test_sha256_known_vector_empty() {
let sgx = new_sgx();
let hash = sgx.hash_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_sha256_known_vector_abc() {
let sgx = new_sgx();
let hash = sgx.hash_sha256(b"abc");
assert_eq!(hash[0], 0xBA);
assert_eq!(hash[1], 0x78);
}
#[test]
fn test_sgx2_emodt() {
let mut sgx = new_sgx();
sgx.sgx2_available = true;
sgx.config.base_address = 0x7F0000000000;
sgx.enclave_state = X86SGXEnclaveState::Initialized;
sgx.eaug(0x2000, X86EPCPageType::REG).unwrap();
sgx.eaccept(0x2000).unwrap();
let result = sgx.emodt(0x2000, X86EPCPageType::TCS);
assert!(result.is_ok());
}
#[test]
fn test_sgx2_eacceptcopy() {
let mut sgx = new_sgx();
sgx.sgx2_available = true;
sgx.config.base_address = 0x7F0000000000;
sgx.enclave_state = X86SGXEnclaveState::Initialized;
sgx.eaug(0x2000, X86EPCPageType::REG).unwrap();
let result = sgx.eacceptcopy(0x2000, 0x1000);
assert!(result.is_ok());
}
#[test]
fn test_sgx_aex_handling() {
let sgx = new_sgx();
let aex = sgx.handle_aex(0x7F0000001000, 14);
assert_eq!(aex.tcs_address, 0x7F0000001000);
assert!(aex.handling_recommendation.contains("General protection"));
}
#[test]
fn test_sgx_epc_page_type_display() {
assert_eq!(X86EPCPageType::SECS.to_string(), "SECS");
assert_eq!(X86EPCPageType::REG.to_string(), "REG");
assert_eq!(X86EPCPageType::TRIM.to_string(), "TRIM");
}
#[test]
fn test_full_roundtrip_aes128() {
let crypto = new_crypto();
let key = [0x2Bu8; 16];
for i in 0..16 {
let mut plaintext = [0u8; 16];
plaintext[0] = i as u8;
plaintext[15] = (15 - i) as u8;
let ct = crypto.aes_encrypt_bitsliced(&key, &plaintext);
let pt = crypto.aes_decrypt(&key, &ct);
assert_eq!(pt, plaintext);
}
}
#[test]
fn test_full_roundtrip_chacha20_poly1305_various() {
let mut crypto = new_crypto();
let key = [0xABu8; 32];
let nonce = [0x12u8; 12];
let test_cases: Vec<(&[u8], &[u8])> = vec![
(b"", b""),
(b"short", b"AAD"),
(b"medium sized message for testing", b"authenticated data"),
(&[0x41u8; 1024], b"large AAD payload for ChaCha20-Poly1305"),
];
for (plaintext, aad) in test_cases {
let (ct, tag) = crypto.chacha20_poly1305_encrypt(&key, &nonce, aad, plaintext);
let decrypted = crypto.chacha20_poly1305_decrypt(&key, &nonce, aad, &ct, &tag);
assert!(decrypted.is_ok());
assert_eq!(
decrypted.unwrap(),
plaintext,
"Roundtrip failed for plaintext len={}, aad len={}",
plaintext.len(),
aad.len()
);
}
}
#[test]
fn test_full_exploit_hardening_pipeline() {
let mut sf = new_security_full();
sf.initialize_all();
sf.harden_system();
let mits = sf.exploit_mitigations.list_active_mitigations();
assert!(mits.contains(&X86ExploitMitigationType::StackProtector));
assert!(mits.contains(&X86ExploitMitigationType::ASLR));
assert!(mits.contains(&X86ExploitMitigationType::NX));
assert!(mits.contains(&X86ExploitMitigationType::RELRO));
assert!(mits.contains(&X86ExploitMitigationType::FortifySource));
let canary = sf.exploit_mitigations.get_canary();
assert_ne!(canary, 0);
assert!(sf.exploit_mitigations.is_nx_enabled());
assert!(sf.exploit_mitigations.enforce_wxorx(0x4)); assert!(!sf.exploit_mitigations.enforce_wxorx(0x6)); }
#[test]
fn test_full_side_channel_pipeline() {
let mut sf = new_security_full();
sf.initialize_all();
sf.harden_system();
let unmitigated = sf.side_channel.unmitigated_count();
assert_eq!(unmitigated, 0);
assert!(sf.side_channel.kpti_active);
assert!(sf.side_channel.retpoline_enabled);
assert!(sf.side_channel.spec_ctrl.ibrs_active);
assert!(sf.side_channel.tsx_disabled);
assert!(sf.side_channel.mds_mitigated);
}
#[test]
fn test_full_cet_pipeline() {
let mut cet = new_cet();
cet.config.ibt_available = true;
cet.config.shstk_available = true;
assert!(unsafe { cet.insert_endbr64(0x400000, "func_a") });
assert!(unsafe { cet.insert_endbr64(0x401000, "func_b") });
assert!(unsafe { cet.insert_endbr32(0x400100, "func_c") });
assert_eq!(cet.landing_pads.len(), 3);
assert_eq!(cet.endbr_inserted, 3);
assert!(cet.verify_landing_pad(0x401000));
assert!(!cet.verify_landing_pad(0x999999));
unsafe {
cet.create_shadow_stack(1, 16384).unwrap();
}
unsafe {
cet.create_shadow_stack(2, 16384).unwrap();
}
assert_eq!(cet.active_shadow_stacks(), 2);
cet.handle_cp_fault(0x400000, 0x1); cet.handle_cp_fault(0x7FFF0000, 0x2); assert_eq!(cet.violations, 2);
}
#[test]
fn test_full_sgx_pipeline() {
let mut sgx = new_sgx();
sgx.sgx1_available = true;
sgx.sgx2_available = true;
sgx.config.base_address = 0x7F0000000000;
sgx.epc_size = 0x10000000;
assert!(sgx.ecreate().is_ok());
assert_eq!(sgx.enclave_state, X86SGXEnclaveState::InProgress);
let code = [0xCCu8; 4096];
assert!(sgx.eadd(0x0000, &code, X86EPCPageType::REG, 0x7).is_ok());
assert!(sgx
.eadd(0x1000, &[0x90u8; 4096], X86EPCPageType::REG, 0x7)
.is_ok());
sgx.enclave_state = X86SGXEnclaveState::Initialized;
assert!(sgx.eaug(0x2000, X86EPCPageType::REG).is_ok());
assert!(sgx.eaccept(0x2000).is_ok());
assert!(sgx.emodt(0x2000, X86EPCPageType::TCS).is_ok());
assert!(sgx.eaccept(0x2000).is_ok());
let target_info = sgx.get_target_info();
let report = sgx.ereport(&target_info, &[0xFFu8; 64]).unwrap();
let quote = sgx
.generate_quote(&report, X86SGXAttestationType::RemoteEPID)
.unwrap();
assert!(sgx.verify_quote("e).unwrap());
let sealed = sgx
.seal_data(b"top secret", X86_SGX_KEYPOLICY_MRENCLAVE, b"ctx")
.unwrap();
let unsealed = sgx.unseal_data(&sealed, b"ctx").unwrap();
assert_eq!(unsealed, b"top secret");
}
#[test]
fn test_full_memory_safety_pipeline() {
let mut ms = new_memory_safety();
ms.smap_available = true;
ms.enable_smap().unwrap();
assert!(ms.smap_enabled);
assert!(!ms.is_user_access_allowed());
unsafe {
ms.stac();
}
assert!(ms.is_user_access_allowed());
unsafe {
ms.clac();
}
assert!(!ms.is_user_access_allowed());
ms.smep_available = true;
ms.enable_smep().unwrap();
assert!(!ms.check_smep(0x400000, true));
assert!(ms.check_smep(0xFFFF_FFFF_8000_0000, true));
ms.umip_available = true;
ms.enable_umip().unwrap();
assert!(ms.umip_enabled);
ms.pku_available = true;
ms.pkru_unrestricted();
assert!(ms.set_key_rights(3, true, false).is_ok());
assert!(!ms.check_pku_access(3, false));
assert!(ms.check_pku_access(4, false));
}
#[test]
fn test_fortify_op_display() {
assert_eq!(X86FortifyOp::MemCpy.to_string(), "memcpy");
assert_eq!(X86FortifyOp::MemSetChk.to_string(), "__memset_chk");
assert_eq!(X86FortifyOp::SPrintFChk.to_string(), "__sprintf_chk");
}
#[test]
fn test_relro_level_display() {
assert_eq!(X86RELROLevel::Partial.to_string(), "Partial");
assert_eq!(X86RELROLevel::Full.to_string(), "Full");
assert_eq!(X86RELROLevel::None.to_string(), "None");
}
#[test]
fn test_randomness_uniqueness() {
let mut crypto = new_crypto();
let mut values = Vec::new();
for _ in 0..100 {
values.push(crypto.get_random_u64());
}
let unique: std::collections::HashSet<_> = values.into_iter().collect();
assert!(unique.len() >= 99); }
#[test]
fn test_constant_time_equality_vs_standard() {
for i in 0..256u8 {
for j in 0..256u8 {
let a = [i; 4];
let b = [j; 4];
let standard = a == b;
let ct = X86CryptoHardening::constant_time_eq(&a, &b);
assert_eq!(ct, standard, "Mismatch for i={}, j={}", i, j);
}
}
}
#[test]
fn test_x86_security_features_builder() {
let mut features = X86SecurityFeatures::default();
features.cet_ibt = true;
features.cet_ss = true;
features.smap = true;
features.smep = true;
assert!(features.cet_ibt);
assert!(features.cet_ss);
assert!(features.smap);
assert!(features.smep);
assert!(!features.sgx1);
assert!(!features.rdrand);
}
#[test]
fn test_cet_interrupt_ssp_table_setup() {
let mut cet = new_cet();
cet.config.cet_ss_available = true;
let table_addr = 0x7F00_0000_0000;
assert!(cet.setup_interrupt_ssp_table(table_addr, 256).is_ok());
assert_eq!(cet.interrupt_ssp_table, table_addr);
assert!(cet.setup_interrupt_ssp_table(table_addr + 1, 256).is_err());
}
#[test]
fn test_cet_supervisor_state_save_restore() {
let cet = new_cet();
let saved = cet.save_cet_supervisor_state();
assert_eq!(saved.interrupt_ssp_table, 0);
let mut cet2 = new_cet();
cet2.interrupt_ssp_table = 0x7F00_0000_1000;
cet2.restore_cet_supervisor_state(&saved);
assert_eq!(cet2.interrupt_ssp_table, 0);
}
#[test]
fn test_cet_no_hardware_graceful_degradation() {
let mut cet = new_cet();
cet.config.ibt_available = false;
cet.config.shstk_available = false;
assert!(!cet.verify_landing_pad(0x400000));
let result = unsafe { cet.create_shadow_stack(1, 4096) };
assert!(result.is_err());
assert!(cet.emit_notrack_prefix(true).is_none());
}
#[test]
fn test_cet_legacy_compat_configuration() {
let cet = new_cet();
let val = cet.configure_u_cet(false, false, true);
assert_eq!(val, X86_CET_LEG_IW_EN);
}
#[test]
fn test_cet_supervisor_cet_configuration() {
let cet = new_cet();
let val = cet.configure_s_cet(true, true);
assert_eq!(val & X86_CET_SHSTK_EN, X86_CET_SHSTK_EN);
assert_eq!(val & X86_CET_WRSS_EN, X86_CET_WRSS_EN);
assert_eq!(val & X86_CET_ENDBR_EN, X86_CET_ENDBR_EN);
}
#[test]
fn test_sgx_large_enclave_management() {
let mut sgx = new_sgx();
sgx.sgx1_available = true;
sgx.config.base_address = 0x7F0000000000;
sgx.config.size = 0x10000000; sgx.epc_size = 0x20000000; sgx.ecreate().unwrap();
for offset in (0x0000..0x10000).step_by(0x1000) {
assert!(sgx
.eadd(offset, &[0x90u8; 4096], X86EPCPageType::REG, 0x7)
.is_ok());
}
assert!(sgx.epc_pages.len() >= 17); }
#[test]
fn test_sgx_seal_with_signer_policy() {
let mut sgx = new_sgx();
sgx.sgx1_available = true;
sgx.config.base_address = 0x7F0000000000;
sgx.epc_size = 0x10000000;
sgx.ecreate().unwrap();
sgx.enclave_state = X86SGXEnclaveState::Initialized;
let result = sgx.seal_data(b"signer-bound secret", X86_SGX_KEYPOLICY_MRSIGNER, b"ctx");
assert!(result.is_ok());
let sealed = result.unwrap();
assert!(sealed.ciphertext.len() >= 19);
assert_eq!(sealed.payload_size, 19);
}
#[test]
fn test_sgx_epc_eviction_and_reload() {
let mut sgx = new_sgx();
sgx.epc_size = 0x10000000;
let epc_addr = sgx.epa(X86EPCPageType::REG).unwrap();
let encrypted = sgx.ewb(epc_addr, 0x1000).unwrap();
assert!(!encrypted.is_empty());
assert!(sgx.eldb(epc_addr, &encrypted, 0x1000).is_ok());
let page = sgx
.epc_pages
.iter()
.find(|p| p.epc_address == epc_addr)
.unwrap();
assert!(page.in_epc);
}
#[test]
fn test_sgx_epc_capacity_check() {
let mut sgx = new_sgx();
sgx.sgx1_available = true;
sgx.config.base_address = 0x7F0000000000;
sgx.epc_size = 0x4000; assert!(sgx.ecreate().is_ok()); assert!(sgx
.eadd(0x1000, &[0u8; 4096], X86EPCPageType::REG, 0x7)
.is_ok()); let result = sgx.eadd(0x2000, &[0u8; 4096], X86EPCPageType::REG, 0x7);
assert!(result.is_err());
}
#[test]
fn test_sgx_double_ecreate_prevention() {
let mut sgx = new_sgx();
sgx.sgx1_available = true;
sgx.config.base_address = 0x7F0000000000;
sgx.epc_size = 0x10000000;
assert!(sgx.ecreate().is_ok());
let result = sgx.ecreate();
assert!(result.is_err());
}
#[test]
fn test_sgx_eadd_before_ecreate() {
let mut sgx = new_sgx();
sgx.sgx1_available = true;
let result = sgx.eadd(0x1000, &[0u8; 4096], X86EPCPageType::REG, 0x7);
assert!(result.is_err());
}
#[test]
fn test_sgx_key_request_default() {
let key_req = X86SGXKeyRequest::default();
assert_eq!(key_req.key_name, 0);
assert_eq!(key_req.key_policy, X86_SGX_KEYPOLICY_MRENCLAVE);
assert_eq!(key_req.cpu_svn.len(), 16);
assert_eq!(key_req.key_id.len(), 32);
}
#[test]
fn test_sgx_sealed_data_aad_mismatch() {
let mut sgx = new_sgx();
sgx.sgx1_available = true;
sgx.config.base_address = 0x7F0000000000;
sgx.epc_size = 0x10000000;
sgx.ecreate().unwrap();
sgx.enclave_state = X86SGXEnclaveState::Initialized;
let sealed = sgx
.seal_data(b"secret", X86_SGX_KEYPOLICY_MRENCLAVE, b"correct_aad")
.unwrap();
let result = sgx.unseal_data(&sealed, b"wrong_aad");
assert!(result.is_err());
}
#[test]
fn test_sgx_target_info_fields() {
let sgx = new_sgx();
sgx.config.mrsigner = [0xAAu8; 32];
let target = sgx.get_target_info();
assert_eq!(target.mrenclave, sgx.mrenclave);
}
#[test]
fn test_sgx_aex_info_fields() {
let sgx = new_sgx();
let aex = sgx.handle_aex(0x7F0000001000, 3);
assert_eq!(aex.tcs_address, 0x7F0000001000);
assert_eq!(aex.exit_reason, 3);
assert!(aex.handling_recommendation.contains("Page fault"));
}
#[test]
fn test_sgx_attributes_comprehensive() {
let mut attr = X86SGXAttributes::default();
attr.debug = true;
attr.provision_key = true;
attr.einittoken_key = true;
attr.cet = true;
attr.kss = true;
attr.mode64bit = true;
let flags = attr.to_flags();
let parsed = X86SGXAttributes::from_flags(flags);
assert_eq!(parsed.debug, attr.debug);
assert_eq!(parsed.provision_key, attr.provision_key);
assert_eq!(parsed.einittoken_key, attr.einittoken_key);
assert_eq!(parsed.cet, attr.cet);
assert_eq!(parsed.kss, attr.kss);
assert_eq!(parsed.mode64bit, attr.mode64bit);
}
#[test]
fn test_mpx_invalid_register_index() {
let mut ms = new_memory_safety();
ms.mpx_available = true;
assert!(unsafe { ms.bndmk(4, 0x1000, 0x100) }.is_err());
assert!(unsafe { ms.bndcl(4, 0x1050) }.is_err());
assert!(unsafe { ms.bndcu(5, 0x1050, 8) }.is_err());
}
#[test]
fn test_mpx_no_hardware_graceful() {
let mut ms = new_memory_safety();
ms.mpx_available = false;
assert!(unsafe { ms.bndmk(0, 0x1000, 0x100) }.is_err());
}
#[test]
fn test_pku_invalid_key_index() {
let mut ms = new_memory_safety();
ms.pku_available = true;
assert!(ms.set_key_rights(16, true, false).is_err());
assert!(ms.get_key_rights(16).is_none());
}
#[test]
fn test_pkru_roundtrip_all_keys() {
let mut ms = new_memory_safety();
ms.pku_available = true;
for i in 0..16u8 {
let disable_access = i % 2 == 0;
let disable_write = i % 3 == 0;
ms.set_key_rights(i, disable_access, disable_write).unwrap();
}
for i in 0..16u8 {
let state = ms.get_key_rights(i).unwrap();
if i % 2 == 0 {
assert!(state.access_disabled);
} else {
assert!(!state.access_disabled);
}
if i % 3 == 0 {
assert!(state.write_disabled);
}
}
}
#[test]
fn test_smap_no_hardware_graceful() {
let mut ms = new_memory_safety();
ms.smap_available = false;
assert!(ms.enable_smap().is_err());
}
#[test]
fn test_smep_no_hardware_graceful() {
let mut ms = new_memory_safety();
ms.smep_available = false;
assert!(ms.enable_smep().is_err());
}
#[test]
fn test_umip_no_hardware_graceful() {
let mut ms = new_memory_safety();
ms.umip_available = false;
assert!(ms.enable_umip().is_err());
}
#[test]
fn test_umip_cpl_zero_bypass() {
let mut ms = new_memory_safety();
ms.umip_available = true;
ms.enable_umip().unwrap();
assert!(ms.check_umip(0x0F01, 3));
assert!(ms.check_umip(0x0F01, 0));
}
#[test]
fn test_stack_canary_deterministic_lsb() {
for _ in 0..50 {
let mut exploit = new_exploit();
let canary = exploit.generate_canary();
assert_eq!(canary & 0xFF, 0);
assert_ne!(canary >> 8, 0);
}
}
#[test]
fn test_canary_emission_32bit() {
let mut exploit = new_exploit();
exploit.canary.size = 4;
exploit.generate_canary();
let code = exploit.emit_canary_check();
assert!(!code.is_empty());
}
#[test]
fn test_aslr_entropy_bounds() {
let mut exploit = new_exploit();
exploit.enable_aslr().unwrap();
let base = exploit.get_randomized_base();
assert_eq!(base & 0xFFF, 0);
let mask = (1u64 << exploit.aslr.exec_entropy_bits) - 1;
assert!(base <= (mask << 12));
}
#[test]
fn test_aslr_multiple_randomizations() {
let mut exploit = new_exploit();
exploit.enable_aslr().unwrap();
let mut values = Vec::new();
for _ in 0..50 {
exploit.aslr.base_offset = 0;
let _ = exploit.enable_aslr();
values.push(exploit.aslr.base_offset);
}
let unique: std::collections::HashSet<_> = values.iter().collect();
assert!(unique.len() > 30);
}
#[test]
fn test_relro_partial_vs_full() {
let mut exploit = new_exploit();
exploit.enable_relro_partial();
assert_eq!(exploit.relro_level, X86RELROLevel::Partial);
exploit.enable_relro_full();
assert_eq!(exploit.relro_level, X86RELROLevel::Full);
}
#[test]
fn test_nx_disable_removes_from_active() {
let mut exploit = new_exploit();
exploit.enable_nx().unwrap();
assert!(exploit
.active_mitigations
.contains(&X86ExploitMitigationType::NX));
exploit.disable_nx();
assert!(!exploit
.active_mitigations
.contains(&X86ExploitMitigationType::NX));
}
#[test]
fn test_fortify_operations_all() {
let mut exploit = new_exploit();
exploit.enable_fortify_source();
let src = [0xAAu8; 32];
let mut dst = [0u8; 64];
assert!(exploit.memcpy_chk(16, &src, &mut dst, 32).is_err());
assert!(exploit.memcpy_chk(32, &src, &mut dst, 32).is_ok());
assert!(exploit.memcpy_chk(64, &src, &mut dst[..16], 16).is_ok());
}
#[test]
fn test_safe_stack_dual_allocation() {
let mut exploit = new_exploit();
exploit.enable_safe_stack().unwrap();
let safe_base = exploit.get_safe_stack_base();
let unsafe_base = exploit.get_unsafe_stack_base();
assert_ne!(safe_base, 0);
assert_ne!(unsafe_base, 0);
assert_ne!(safe_base, unsafe_base);
}
#[test]
fn test_safe_stack_object_routing() {
let mut exploit = new_exploit();
assert!(exploit.is_safe_stack_object(true));
assert!(exploit.is_safe_stack_object(false));
exploit.enable_safe_stack().unwrap();
assert!(!exploit.is_safe_stack_object(true));
assert!(exploit.is_safe_stack_object(false));
}
#[test]
fn test_cfi_violation_tracking() {
let mut exploit = new_exploit();
exploit.enable_cfi();
exploit.cfi_check_backward_edge(0x400100);
assert!(!exploit.cfi_verify_return(0xDEAD));
assert_eq!(exploit.cfi_violations, 1);
let (checks, violations) = exploit.get_cfi_stats();
assert_eq!(checks, 0); assert_eq!(violations, 1);
}
#[test]
fn test_shadow_call_stack_pop_empty() {
let mut exploit = new_exploit();
exploit.enable_cfi();
assert!(!exploit.shadow_call_pop(0x400100));
}
#[test]
fn test_shadow_call_stack_max_depth() {
let mut exploit = new_exploit();
exploit.enable_cfi();
for i in 0..1000 {
exploit.shadow_call_push(0x400000 + i);
}
assert_eq!(exploit.shadow_call_stack.len(), 1000);
for i in (0..1000).rev() {
assert!(exploit.shadow_call_pop(0x400000 + i));
}
assert_eq!(exploit.shadow_call_stack.len(), 0);
}
#[test]
fn test_side_channel_stibp() {
let mut sc = new_side_channel();
let result = sc.enable_stibp();
assert!(result.is_ok());
assert!(sc.spec_ctrl.stibp_active);
}
#[test]
fn test_side_channel_ssbd() {
let mut sc = new_side_channel();
sc.spec_ctrl.ssbd_active = true;
assert!(sc.spec_ctrl.ssbd_active);
sc.disable_spec_ctrl();
assert!(!sc.spec_ctrl.ssbd_active);
}
#[test]
fn test_side_channel_psfd() {
let mut sc = new_side_channel();
sc.spec_ctrl.psfd_active = true;
assert!(sc.spec_ctrl.psfd_active);
}
#[test]
fn test_side_channel_kpti_disable() {
let mut sc = new_side_channel();
sc.enable_kpti();
assert!(sc.kpti_active);
sc.disable_kpti();
assert!(!sc.kpti_active);
}
#[test]
fn test_side_channel_tsx_re_enable() {
let mut sc = new_side_channel();
sc.disable_tsx();
assert!(sc.tsx_disabled);
sc.enable_tsx();
assert!(!sc.tsx_disabled);
}
#[test]
fn test_rsb_stuffing_deterministic() {
let sc = new_side_channel();
let s1 = sc.generate_rsb_stuffing(8);
let s2 = sc.generate_rsb_stuffing(8);
assert_eq!(s1, s2);
}
#[test]
fn test_rsb_stuffing_different_counts() {
let sc = new_side_channel();
let s1 = sc.generate_rsb_stuffing(8);
let s2 = sc.generate_rsb_stuffing(16);
assert!(s2.len() > s1.len());
}
#[test]
fn test_vulnerability_performance_impact() {
let sc = new_side_channel();
for vuln in &sc.vulnerabilities {
if vuln.affected && vuln.mitigated {
assert!(vuln.performance_impact <= 100);
}
}
}
#[test]
fn test_crypto_multiple_random_u64_uniqueness() {
let mut crypto = new_crypto();
let r1 = crypto.get_random_u64();
let r2 = crypto.get_random_u64();
let r3 = crypto.get_random_u64();
let r4 = crypto.get_random_u64();
assert_ne!(r1, r2);
assert_ne!(r2, r3);
assert_ne!(r3, r4);
}
#[test]
fn test_chacha20_counter_rollover() {
let crypto = new_crypto();
let key = [0x55u8; 32];
let nonce = [0x66u8; 12];
let b0 = crypto.chacha20_block(&key, &nonce, 0);
let b1 = crypto.chacha20_block(&key, &nonce, 1);
let b2 = crypto.chacha20_block(&key, &nonce, u32::MAX);
assert_ne!(b0, b1);
assert_ne!(b1, b2);
}
#[test]
fn test_chacha20_stream_reproducibility() {
let crypto = new_crypto();
let key = [0xABu8; 32];
let nonce = [0xCDu8; 12];
let plaintext = [0x41u8; 256];
let ct1 = crypto.chacha20_encrypt(&key, &nonce, 0, &plaintext);
let ct2 = crypto.chacha20_encrypt(&key, &nonce, 0, &plaintext);
assert_eq!(ct1, ct2);
}
#[test]
fn test_poly1305_empty_message() {
let crypto = new_crypto();
let key = [0xAAu8; 32];
let tag = crypto.poly1305_mac(&key, b"");
assert_eq!(tag.len(), 16);
let tag2 = crypto.poly1305_mac(&key, b"");
assert_eq!(tag, tag2);
}
#[test]
fn test_poly1305_different_keys() {
let crypto = new_crypto();
let msg = b"test message";
let k1 = [0x01u8; 32];
let k2 = [0x02u8; 32];
let tag1 = crypto.poly1305_mac(&k1, msg);
let tag2 = crypto.poly1305_mac(&k2, msg);
assert_ne!(tag1, tag2);
}
#[test]
fn test_aes_inverse_mix_columns_roundtrip() {
let mut state = [
0xABu8, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA,
0xDC, 0xFE,
];
let original = state;
X86CryptoHardening::aes_mix_columns(&mut state);
X86CryptoHardening::aes_inv_mix_columns(&mut state);
assert_eq!(state, original);
}
#[test]
fn test_aes_key_schedule_consistency() {
let key = [0x73u8; 16];
let round_keys = X86CryptoHardening::aes_key_expansion_128(&key);
for i in 1..11 {
assert_ne!(
round_keys[i],
round_keys[i - 1],
"Round key {} should differ from round key {}",
i,
i - 1
);
}
}
#[test]
fn test_gf256_mul_commutativity() {
let a: u8 = 0x57;
let b: u8 = 0x83;
let ab = X86CryptoHardening::gf256_mul(a, b);
let ba = X86CryptoHardening::gf256_mul(b, a);
assert_eq!(ab, ba);
}
#[test]
fn test_gf256_mul_associativity() {
let vals = [0x53, 0xCA, 0xFE];
for &a in &vals {
for &b in &vals {
for &c in &vals {
let ab_c =
X86CryptoHardening::gf256_mul(X86CryptoHardening::gf256_mul(a, b), c);
let a_bc =
X86CryptoHardening::gf256_mul(a, X86CryptoHardening::gf256_mul(b, c));
assert_eq!(
ab_c, a_bc,
"Associativity failed for a={:#04x} b={:#04x} c={:#04x}",
a, b, c
);
}
}
}
}
#[test]
fn test_aes_rcon_progression() {
assert_eq!(X86CryptoHardening::aes_rcon(1), 0x01);
assert_eq!(X86CryptoHardening::aes_rcon(2), 0x02);
assert_eq!(X86CryptoHardening::aes_rcon(3), 0x04);
assert_eq!(X86CryptoHardening::aes_rcon(4), 0x08);
assert_eq!(X86CryptoHardening::aes_rcon(5), 0x10);
}
#[test]
fn test_secure_clear_primitives() {
let mut val: u64 = 0xDEAD_BEEF_CAFE_BABE;
X86CryptoHardening::secure_clear(&mut val);
assert_eq!(val, 0);
}
#[test]
fn test_secure_zeroize_large() {
let mut buf = [0xFFu8; 4096];
X86CryptoHardening::secure_zeroize(&mut buf);
assert!(buf.iter().all(|&b| b == 0));
}
#[test]
fn test_constant_time_select_all_values() {
for cond in 0..=255u8 {
let a: u8 = 0xA5;
let b: u8 = 0x5A;
let selected = X86CryptoHardening::constant_time_select(cond, a, b);
if cond == 0 {
assert_eq!(selected, b);
} else {
assert_eq!(selected, a);
}
}
}
#[test]
fn test_constant_time_conditional_swap_idempotent() {
let original_a = [0xAAu8; 8];
let original_b = [0x55u8; 8];
let mut a = original_a;
let mut b = original_b;
X86CryptoHardening::constant_time_conditional_swap(1, &mut a, &mut b);
X86CryptoHardening::constant_time_conditional_swap(1, &mut a, &mut b);
assert_eq!(a, original_a);
assert_eq!(b, original_b);
}
#[test]
fn test_chacha20_poly1305_aead_tag_verification() {
let mut crypto = new_crypto();
let key = [0x55u8; 32];
let nonce = [0x66u8; 12];
let aad = b"authenticated";
let plaintext = b"plaindata";
let (ct, tag) = crypto.chacha20_poly1305_encrypt(&key, &nonce, aad, plaintext);
let mut tampered_ct = ct.clone();
if !tampered_ct.is_empty() {
tampered_ct[0] ^= 1;
}
let result = crypto.chacha20_poly1305_decrypt(&key, &nonce, aad, &tampered_ct, &tag);
assert!(result.is_err());
}
#[test]
fn test_security_full_report_contains_all_subsystems() {
let mut sf = new_security_full();
sf.initialize_all();
sf.harden_system();
let report = sf.security_report();
let keywords = [
"CPU Security Features",
"CET Status",
"SGX Status",
"Memory Safety",
"Exploit Mitigations",
"Side-Channel Mitigations",
"Cryptographic Hardening",
"Total violations",
];
for kw in &keywords {
assert!(report.contains(kw), "Report missing keyword: {}", kw);
}
}
#[test]
fn test_security_full_validate_edge_cases() {
let mut sf = new_security_full();
sf.side_channel.kpti_active = true;
sf.side_channel.retpoline_enabled = false;
let result = sf.validate_all();
assert!(result.is_err());
}
#[test]
fn test_security_full_validate_zero_canary() {
let mut sf = new_security_full();
sf.exploit_mitigations.canary.active = true;
sf.exploit_mitigations.canary.value = 0;
let result = sf.validate_all();
assert!(result.is_err());
}
#[test]
fn test_security_full_shutdown_clears_state() {
let mut sf = new_security_full();
sf.initialize_all();
sf.harden_system();
sf.shutdown();
assert!(!sf.hardened);
assert!(sf.exploit_mitigations.shadow_call_stack.is_empty());
assert!(sf.crypto.chacha_key.iter().all(|&b| b == 0));
}
#[test]
fn test_security_full_iterative_harden_shutdown() {
let mut sf = new_security_full();
for _ in 0..3 {
sf.initialize_all();
sf.harden_system();
assert!(sf.hardened);
sf.shutdown();
assert!(!sf.hardened);
}
}
#[test]
fn test_cet_shadow_stack_multi_thread_scenario() {
let mut cet = new_cet();
cet.config.shstk_available = true;
for tid in 1..=16 {
assert!(unsafe { cet.create_shadow_stack(tid, 16384).is_ok() });
}
assert_eq!(cet.active_shadow_stacks(), 16);
for tid in 1..=8 {
assert!(cet.destroy_shadow_stack(tid));
}
assert_eq!(cet.active_shadow_stacks(), 8);
for tid in 9..=16 {
assert!(cet.shadow_stacks.contains_key(&tid));
}
}
#[test]
fn test_cet_ibt_multiple_functions() {
let mut cet = new_cet();
cet.config.ibt_available = true;
let functions = vec!["main", "foo", "bar", "baz", "handler", "callback"];
let base = 0x401000;
for (i, name) in functions.iter().enumerate() {
unsafe {
cet.insert_endbr64(base + (i as u64) * 16, name);
}
}
assert_eq!(cet.landing_pads.len(), functions.len());
assert_eq!(cet.endbr_inserted, functions.len());
for i in 0..functions.len() {
assert!(cet.verify_landing_pad(base + (i as u64) * 16));
}
}
#[test]
fn test_sgx_comprehensive_attestation_flow() {
let mut sgx = new_sgx();
sgx.sgx1_available = true;
sgx.config.base_address = 0x7F0000000000;
sgx.epc_size = 0x10000000;
sgx.ecreate().unwrap();
sgx.enclave_state = X86SGXEnclaveState::Initialized;
let target_info = sgx.get_target_info();
let report = sgx.ereport(&target_info, &[0x42u8; 64]).unwrap();
assert_eq!(report.report_data, [0x42u8; 64]);
let quote_epid = sgx
.generate_quote(&report, X86SGXAttestationType::RemoteEPID)
.unwrap();
assert_eq!(quote_epid.sign_type, 0);
assert!(sgx.verify_quote("e_epid).unwrap());
let quote_ecdsa = sgx
.generate_quote(&report, X86SGXAttestationType::RemoteECDSA)
.unwrap();
assert_eq!(quote_ecdsa.sign_type, 2);
assert!(sgx.verify_quote("e_ecdsa).unwrap());
}
#[test]
fn test_memory_safety_smep_violation_reporting() {
let mut ms = new_memory_safety();
ms.smep_available = true;
ms.enable_smep().unwrap();
assert!(!ms.check_smep(0x400000, true));
ms.handle_smep_violation(0x400000);
assert_eq!(ms.smep_violations, 1);
}
#[test]
fn test_memory_safety_smap_violation_reporting() {
let mut ms = new_memory_safety();
ms.smap_available = true;
ms.enable_smap().unwrap();
ms.handle_smap_violation(0x7FFF0000);
assert_eq!(ms.smap_violations, 1);
}
#[test]
fn test_exploit_mitigations_list_after_full() {
let mut exploit = new_exploit();
exploit.full_hardening();
let active = exploit.list_active_mitigations();
assert!(active.contains(&X86ExploitMitigationType::StackProtector));
assert!(active.contains(&X86ExploitMitigationType::ASLR));
assert!(active.contains(&X86ExploitMitigationType::PIE));
assert!(active.contains(&X86ExploitMitigationType::RELRO));
assert!(active.contains(&X86ExploitMitigationType::NX));
assert!(active.contains(&X86ExploitMitigationType::FortifySource));
assert!(active.contains(&X86ExploitMitigationType::ForwardEdgeCFI));
assert!(active.contains(&X86ExploitMitigationType::SafeStack));
assert_eq!(active.len(), 8);
}
#[test]
fn test_exploit_mitigation_type_all_display() {
let all_types = [
X86ExploitMitigationType::StackProtector,
X86ExploitMitigationType::ASLR,
X86ExploitMitigationType::PIE,
X86ExploitMitigationType::RELRO,
X86ExploitMitigationType::NX,
X86ExploitMitigationType::FortifySource,
X86ExploitMitigationType::ForwardEdgeCFI,
X86ExploitMitigationType::BackwardEdgeCFI,
X86ExploitMitigationType::SafeStack,
X86ExploitMitigationType::ShadowCallStack,
];
for t in &all_types {
let s = t.to_string();
assert!(!s.is_empty());
assert_ne!(s, t.to_string()); }
}
#[test]
fn test_fortify_op_all_display() {
let all_ops = [
X86FortifyOp::MemCpy,
X86FortifyOp::MemMove,
X86FortifyOp::MemSet,
X86FortifyOp::StrCpy,
X86FortifyOp::StrNCpy,
X86FortifyOp::StrCat,
X86FortifyOp::StrNCat,
X86FortifyOp::SPrintF,
X86FortifyOp::SPrintFChk,
X86FortifyOp::MemSetChk,
];
for op in &all_ops {
let s = op.to_string();
assert!(!s.is_empty());
}
}
#[test]
fn test_side_channel_spectre_v2_all_mitigations() {
let mut sc = new_side_channel();
sc.enable_retpoline();
sc.enable_ibrs().unwrap();
sc.enable_stibp().unwrap();
sc.issue_ibpb();
let v2 = sc
.vulnerabilities
.iter()
.find(|v| v.vuln == X86SideChannelVuln::SpectreV2)
.unwrap();
assert!(v2.mitigated);
}
#[test]
fn test_security_full_performance_aware_report() {
let sf = new_security_full();
let mut total_impact: u32 = 0;
for vuln in &sf.side_channel.vulnerabilities {
if vuln.affected && vuln.mitigated {
total_impact += vuln.performance_impact as u32;
}
}
let _ = total_impact;
}
#[test]
fn test_crypto_chacha20_prng_determinism() {
let mut crypto1 = new_crypto();
let mut crypto2 = new_crypto();
crypto1.chacha_key = [0xABu8; 32];
crypto2.chacha_key = [0xABu8; 32];
crypto1.rng.counter = 42;
crypto2.rng.counter = 42;
let v1 = crypto1.get_random_u64();
let v2 = crypto2.get_random_u64();
assert_eq!(v1, v2);
}
#[test]
fn test_aes_sub_bytes_inv_roundtrip() {
for byte in 0..=255u8 {
let subbed = X86CryptoHardening::aes_sbox_ct(byte);
let restored = X86CryptoHardening::aes_inv_sbox_ct(subbed);
assert_eq!(
restored, byte,
"S-box roundtrip failed for byte={:#04x}",
byte
);
}
}
#[test]
fn test_cet_state_all_variants_display() {
let states = [
X86CETState::Disabled,
X86CETState::IBTOnly,
X86CETState::SHSTKOnly,
X86CETState::FullProtection,
X86CETState::SupervisorOnly,
X86CETState::LegacyCompatibility,
];
for state in &states {
let s = state.to_string();
assert!(!s.is_empty());
}
}
#[test]
fn test_sgx_enclave_state_all_variants() {
let states = [
X86SGXEnclaveState::Uninitialized,
X86SGXEnclaveState::InProgress,
X86SGXEnclaveState::Initialized,
X86SGXEnclaveState::Sealed,
X86SGXEnclaveState::Destroyed,
];
for state in &states {
let s = state.to_string();
assert!(!s.is_empty());
}
}
#[test]
fn test_crypto_algorithm_all_display() {
let algs = [
X86CryptoAlgorithm::ChaCha20,
X86CryptoAlgorithm::Poly1305,
X86CryptoAlgorithm::ChaCha20Poly1305,
X86CryptoAlgorithm::AES128,
X86CryptoAlgorithm::AES256,
X86CryptoAlgorithm::AES128GCM,
X86CryptoAlgorithm::AES256GCM,
];
for alg in &algs {
let s = alg.to_string();
assert!(!s.is_empty());
}
}
#[test]
fn test_memory_safety_type_all_display() {
let types = [
X86MemorySafetyType::MPX,
X86MemorySafetyType::MPK,
X86MemorySafetyType::SMAP,
X86MemorySafetyType::SMEP,
X86MemorySafetyType::UMIP,
X86MemorySafetyType::PKRS,
];
for t in &types {
let s = t.to_string();
assert!(!s.is_empty());
}
}
#[test]
fn test_side_channel_vuln_all_display() {
let vulns = [
X86SideChannelVuln::SpectreV1,
X86SideChannelVuln::SpectreV2,
X86SideChannelVuln::Meltdown,
X86SideChannelVuln::L1TF,
X86SideChannelVuln::MDS,
X86SideChannelVuln::TAA,
X86SideChannelVuln::SRBDS,
X86SideChannelVuln::MMIO,
X86SideChannelVuln::Retbleed,
];
for vuln in &vulns {
let s = vuln.to_string();
assert!(!s.is_empty());
}
}
#[test]
fn test_chacha20_large_data_throughput() {
let crypto = new_crypto();
let key = [0x12u8; 32];
let nonce = [0x34u8; 12];
let plaintext = vec![0x56u8; 1024 * 1024];
let ct = crypto.chacha20_encrypt(&key, &nonce, 0, &plaintext);
assert_eq!(ct.len(), plaintext.len());
let pt = crypto.chacha20_decrypt(&key, &nonce, 0, &ct);
assert_eq!(pt, plaintext);
}
#[test]
fn test_aes_encrypt_many_blocks() {
let crypto = new_crypto();
let key = [0x2Bu8; 16];
for i in 0..50u8 {
let mut pt = [0u8; 16];
pt[0] = i;
pt[15] = 0xFF - i;
let ct = crypto.aes_encrypt_bitsliced(&key, &pt);
let dec = crypto.aes_decrypt(&key, &ct);
assert_eq!(dec, pt);
}
}
#[test]
fn test_security_full_hundred_violations() {
let mut sf = new_security_full();
for i in 0..100 {
sf.record_violation("TEST", &format!("Violation #{}", i));
}
assert_eq!(sf.total_violations, 100);
assert_eq!(sf.audit_log.len(), 100);
}
#[test]
fn test_sha256_large_input() {
let sgx = new_sgx();
let data = vec![0x41u8; 1024 * 1024]; let hash1 = sgx.hash_sha256(&data);
let hash2 = sgx.hash_sha256(&data);
assert_eq!(hash1, hash2);
let mut modified = data.clone();
modified[512 * 1024] ^= 1;
let hash3 = sgx.hash_sha256(&modified);
assert_ne!(hash1, hash3);
}
#[test]
fn test_random_bytes_full_buffer_fill() {
let mut crypto = new_crypto();
let sizes = [0, 1, 8, 16, 32, 64, 128, 256, 1024];
for &size in &sizes {
let mut buf = vec![0u8; size];
let filled = crypto.get_random_bytes(&mut buf);
assert_eq!(filled, size);
if size > 0 {
assert!(buf.iter().any(|&b| b != 0));
}
}
}
#[test]
fn test_aslr_mmap_randomization() {
let mut exploit = new_exploit();
exploit.enable_aslr().unwrap();
let offset1 = exploit.randomize_mmap();
let offset2 = exploit.randomize_mmap();
if offset1 == offset2 {
let offset3 = exploit.randomize_mmap();
assert_ne!(offset1, offset3);
}
}
#[test]
fn test_default_configs_not_same() {
let config1 = X86CETConfig::default();
let config2 = X86CETConfig::default();
assert_eq!(config1.ibt_available, config2.ibt_available);
assert!(!config1.ibt_available);
assert!(!config1.shstk_available);
}
#[test]
fn test_safe_stack_config_default() {
let config = X86SafeStackConfig::default();
assert!(!config.enabled);
assert_eq!(config.safe_stack_size, 0x100000);
assert_eq!(config.unsafe_stack_size, 0x100000);
}
#[test]
fn test_aslr_config_default() {
let config = X86ASLRConfig::default();
assert!(!config.active);
assert!(!config.pie_enabled);
assert_eq!(config.exec_entropy_bits, X86_ASLR_ENTROPY_BITS_64 as u8);
}
#[test]
fn test_cet_shadow_stack_atomic_operations() {
let mut cet = new_cet();
cet.config.shstk_available = true;
for tid in 0..100 {
let result = unsafe { cet.create_shadow_stack(tid, 4096 * 4) };
if result.is_ok() {
cet.destroy_shadow_stack(tid);
}
}
assert_eq!(cet.active_shadow_stacks(), 0);
}
#[test]
fn test_exploit_mitigation_idempotent_hardening() {
let mut exploit = new_exploit();
for _ in 0..5 {
exploit.full_hardening();
}
assert!(exploit.hardened);
assert!(exploit.mitigation_count() >= 7);
}
#[test]
fn test_side_channel_idempotent_apply() {
let mut sc = new_side_channel();
for _ in 0..3 {
sc.apply_all_mitigations();
}
assert_eq!(sc.unmitigated_count(), 0);
}
#[test]
fn test_security_full_harden_minimal() {
let mut sf = new_security_full();
sf.initialize_all();
sf.harden_minimal();
assert!(sf.exploit_mitigations.canary.active);
assert!(sf.exploit_mitigations.nx_enabled);
assert_eq!(sf.exploit_mitigations.relro_level, X86RELROLevel::Full);
assert!(sf.exploit_mitigations.fortify_source);
assert!(!sf.exploit_mitigations.aslr.active);
}
#[test]
fn test_security_full_harden_throughput_optimized() {
let mut sf = new_security_full();
sf.initialize_all();
sf.harden_throughput_optimized();
assert!(sf.side_channel.retpoline_enabled);
assert!(sf.side_channel.spec_ctrl.ibpb_issued);
assert!(!sf.side_channel.spec_ctrl.ibrs_active);
}
#[test]
fn test_security_full_harden_maximum() {
let mut sf = new_security_full();
sf.initialize_all();
sf.harden_maximum();
assert!(sf.hardened);
assert!(sf.side_channel.spec_ctrl.ibrs_active);
assert!(sf.side_channel.kpti_active);
assert!(sf.side_channel.tsx_disabled);
assert!(sf.side_channel.retbleed_mitigated);
}
#[test]
fn test_is_feature_available() {
let mut sf = new_security_full();
sf.features.rdrand = true;
sf.features.aes_ni = true;
sf.features.sgx1 = true;
assert!(sf.is_feature_available("rdrand"));
assert!(sf.is_feature_available("AES-NI"));
assert!(sf.is_feature_available("sgx"));
assert!(!sf.is_feature_available("nonexistent"));
assert!(!sf.is_feature_available("tsx"));
}
#[test]
fn test_count_available_features() {
let mut sf = new_security_full();
sf.features.rdrand = true;
sf.features.smap = true;
sf.features.smep = true;
sf.features.aes_ni = true;
let count = sf.count_available_features();
assert_eq!(count, 4);
}
#[test]
fn test_security_score_baseline() {
let sf = new_security_full();
let score = sf.security_score();
assert!(score < 20);
}
#[test]
fn test_security_score_full_hardened() {
let mut sf = new_security_full();
sf.initialize_all();
sf.harden_system();
let score = sf.security_score();
assert!(score >= 30);
}
#[test]
fn test_security_score_maximum() {
let mut sf = new_security_full();
sf.initialize_all();
sf.harden_maximum();
let score = sf.security_score();
assert!(score >= 50);
assert!(score <= 100);
}
#[test]
fn test_export_status_json() {
let mut sf = new_security_full();
sf.initialize_all();
sf.harden_system();
let json = sf.export_status_json();
assert!(json.contains("\"hardened\""));
assert!(json.contains("\"cet\""));
assert!(json.contains("\"sgx\""));
assert!(json.contains("\"memory_safety\""));
}
#[test]
fn test_rotate_crypto_keys() {
let mut sf = new_security_full();
let old_key = sf.crypto.chacha_key;
sf.rotate_crypto_keys();
let new_key = sf.crypto.chacha_key;
assert_ne!(old_key, new_key);
assert!(sf.exploit_mitigations.canary.active);
assert_ne!(sf.exploit_mitigations.canary.value, 0);
}
#[test]
fn test_export_and_clear_audit_log() {
let mut sf = new_security_full();
sf.initialize_all();
let log_before = sf.export_audit_log().len();
assert!(log_before > 0);
sf.clear_audit_log();
assert_eq!(sf.export_audit_log().len(), 0);
}
#[test]
fn test_uptime() {
let mut sf = new_security_full();
sf.initialize_all();
std::thread::sleep(std::time::Duration::from_millis(10));
let uptime = sf.uptime();
assert!(uptime.is_some());
assert!(uptime.unwrap() >= std::time::Duration::from_millis(10));
}
#[test]
fn test_mitigation_summary_format() {
let mut sf = new_security_full();
sf.initialize_all();
sf.harden_system();
let summary = sf.mitigation_summary();
assert!(summary.contains("Score:"));
assert!(summary.contains("CET:"));
assert!(summary.contains("SGX:"));
}
#[test]
fn test_all_display_traits_invoked() {
let _ = format!("{}", X86CETState::FullProtection);
let _ = format!("{}", X86SGXEnclaveState::Initialized);
let _ = format!("{}", X86SGXAttestationType::RemoteECDSA);
let _ = format!("{}", X86SideChannelVuln::SpectreV1);
let _ = format!("{}", X86ExploitMitigationType::ASLR);
let _ = format!("{}", X86CryptoAlgorithm::ChaCha20);
let _ = format!("{}", X86MemorySafetyType::SMAP);
let _ = format!("{}", X86RELROLevel::Full);
let _ = format!("{}", X86FortifyOp::MemCpy);
let _ = format!("{}", X86EPCPageType::REG);
let sf = new_security_full();
let _ = format!("{}", sf);
}
#[test]
fn test_chacha20_known_test_vector() {
let crypto = new_crypto();
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 plaintext = b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.";
let ciphertext = crypto.chacha20_encrypt(&key, &nonce, 1, plaintext);
let expected_start: [u8; 6] = [0x6e, 0x2e, 0x35, 0x9a, 0x25, 0x68];
assert_eq!(&ciphertext[..6], &expected_start);
let decrypted = crypto.chacha20_decrypt(&key, &nonce, 1, &ciphertext);
assert_eq!(decrypted, plaintext);
}
}