use super::riscv_instr_info::RiscVOpcode;
use crate::opcode::Opcode;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct RiscVCryptoPattern {
pub ir_opcode: Opcode,
pub description: &'static str,
pub result_opcode: RiscVOpcode,
pub priority: u32,
pub num_operands: u8,
pub required_extension: Option<&'static str>,
pub requires_vlen128: bool,
pub is_widening: bool,
}
pub struct RiscVCryptoIselTable {
pub patterns: Vec<RiscVCryptoPattern>,
pub total_patterns: usize,
pub extensions_covered: Vec<String>,
}
pub fn riscv_crypto_isel_table() -> Vec<RiscVCryptoPattern> {
let mut table = Vec::new();
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::And,
description: "vandn.vv vd, vs1, vs2 — vector bitwise AND with complement of vs1",
result_opcode: RiscVOpcode::VANDN_VV,
priority: 100,
num_operands: 3,
required_extension: Some("zvbb"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::And,
description: "vandn.vx vd, rs1, vs2 — vector-scalar bitwise AND with complement",
result_opcode: RiscVOpcode::VANDN_VX,
priority: 101,
num_operands: 3,
required_extension: Some("zvbb"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::ShuffleVector,
description: "vbrev8.v vd, vs1 — vector bit-reverse each byte element",
result_opcode: RiscVOpcode::VBREV8_V,
priority: 100,
num_operands: 2,
required_extension: Some("zvbb"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::ShuffleVector,
description: "vbrev.v vd, vs1 — vector bit-reverse each element",
result_opcode: RiscVOpcode::VBREV_V,
priority: 101,
num_operands: 2,
required_extension: Some("zvbb"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::ShuffleVector,
description: "vrev8.v vd, vs1 — vector byte-reverse each element",
result_opcode: RiscVOpcode::VREV8_V,
priority: 100,
num_operands: 2,
required_extension: Some("zvbb"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Call,
description: "vclz.v vd, vs1 — vector count leading zeros",
result_opcode: RiscVOpcode::VCLZ_V,
priority: 100,
num_operands: 2,
required_extension: Some("zvbb"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Call,
description: "vctz.v vd, vs1 — vector count trailing zeros",
result_opcode: RiscVOpcode::VCTZ_V,
priority: 100,
num_operands: 2,
required_extension: Some("zvbb"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Call,
description: "vcpop.v vd, vs1 — vector population count (popcnt)",
result_opcode: RiscVOpcode::VCPOP_V,
priority: 100,
num_operands: 2,
required_extension: Some("zvbb"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Shl,
description: "vrol.vv vd, vs1, vs2 — vector rotate left by vector",
result_opcode: RiscVOpcode::VROL_VV,
priority: 100,
num_operands: 3,
required_extension: Some("zvbb"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Shl,
description: "vrol.vx vd, vs1, rs1 — vector rotate left by scalar",
result_opcode: RiscVOpcode::VROL_VX,
priority: 101,
num_operands: 3,
required_extension: Some("zvbb"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::LShr,
description: "vror.vv vd, vs1, vs2 — vector rotate right by vector",
result_opcode: RiscVOpcode::VROR_VV,
priority: 100,
num_operands: 3,
required_extension: Some("zvbb"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::LShr,
description: "vror.vx vd, vs1, rs1 — vector rotate right by scalar",
result_opcode: RiscVOpcode::VROR_VX,
priority: 101,
num_operands: 3,
required_extension: Some("zvbb"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::LShr,
description: "vror.vi vd, vs1, uimm — vector rotate right by immediate",
result_opcode: RiscVOpcode::VROR_VI,
priority: 102,
num_operands: 3,
required_extension: Some("zvbb"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Shl,
description: "vwsll.vv vd, vs1, vs2 — vector widening shift left logical by vector",
result_opcode: RiscVOpcode::VWSLL_VV,
priority: 100,
num_operands: 3,
required_extension: Some("zvbb"),
requires_vlen128: true,
is_widening: true,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Shl,
description: "vwsll.vx vd, vs1, rs1 — vector widening shift left logical by scalar",
result_opcode: RiscVOpcode::VWSLL_VX,
priority: 101,
num_operands: 3,
required_extension: Some("zvbb"),
requires_vlen128: true,
is_widening: true,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Shl,
description: "vwsll.vi vd, vs1, uimm — vector widening shift left logical by immediate",
result_opcode: RiscVOpcode::VWSLL_VI,
priority: 102,
num_operands: 3,
required_extension: Some("zvbb"),
requires_vlen128: true,
is_widening: true,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Mul,
description: "vclmul.vv vd, vs1, vs2 — vector carry-less multiply (binary polynomial)",
result_opcode: RiscVOpcode::VCLMUL_VV,
priority: 200,
num_operands: 3,
required_extension: Some("zvbc"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Mul,
description: "vclmul.vx vd, vs1, rs1 — vector-scalar carry-less multiply",
result_opcode: RiscVOpcode::VCLMUL_VX,
priority: 201,
num_operands: 3,
required_extension: Some("zvbc"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Mul,
description: "vclmulh.vv vd, vs1, vs2 — vector carry-less multiply high (upper half)",
result_opcode: RiscVOpcode::VCLMULH_VV,
priority: 202,
num_operands: 3,
required_extension: Some("zvbc"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Mul,
description: "vclmulh.vx vd, vs1, rs1 — vector-scalar carry-less multiply high",
result_opcode: RiscVOpcode::VCLMULH_VX,
priority: 203,
num_operands: 3,
required_extension: Some("zvbc"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Xor,
description: "vghsh.vv vd, vs1, vs2 — vector GCM hash with Galois field multiply-add",
result_opcode: RiscVOpcode::VGHSH_VV,
priority: 300,
num_operands: 3,
required_extension: Some("zvkg"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Mul,
description: "vgmul.vv vd, vs1 — vector Galois field multiply (GHASH multiply)",
result_opcode: RiscVOpcode::VGMUL_VV,
priority: 300,
num_operands: 2,
required_extension: Some("zvkg"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Call,
description: "vaes128e.vv vd, vs1 — vector AES-128 encrypt all rounds (non-last)",
result_opcode: RiscVOpcode::VAES128E_VV,
priority: 400,
num_operands: 2,
required_extension: Some("zvkned"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Call,
description: "vaes128d.vv vd, vs1 — vector AES-128 decrypt all rounds (non-last)",
result_opcode: RiscVOpcode::VAES128D_VV,
priority: 401,
num_operands: 2,
required_extension: Some("zvkned"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Call,
description: "vaes256e.vv vd, vs1 — vector AES-256 encrypt all rounds",
result_opcode: RiscVOpcode::VAES256E_VV,
priority: 402,
num_operands: 2,
required_extension: Some("zvkned"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Call,
description: "vaes256d.vv vd, vs1 — vector AES-256 decrypt all rounds",
result_opcode: RiscVOpcode::VAES256D_VV,
priority: 403,
num_operands: 2,
required_extension: Some("zvkned"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Call,
description: "vaes128e.vi vd, vs1, rnd — vector AES-128 encrypt single round by imm",
result_opcode: RiscVOpcode::VAES128E_VI,
priority: 410,
num_operands: 2,
required_extension: Some("zvkned"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Call,
description: "vaes128d.vi vd, vs1, rnd — vector AES-128 decrypt single round by imm",
result_opcode: RiscVOpcode::VAES128D_VI,
priority: 411,
num_operands: 2,
required_extension: Some("zvkned"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Call,
description: "vaes256e.vi vd, vs1, rnd — vector AES-256 encrypt single round by imm",
result_opcode: RiscVOpcode::VAES256E_VI,
priority: 412,
num_operands: 2,
required_extension: Some("zvkned"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Call,
description: "vaes256d.vi vd, vs1, rnd — vector AES-256 decrypt single round by imm",
result_opcode: RiscVOpcode::VAES256D_VI,
priority: 413,
num_operands: 2,
required_extension: Some("zvkned"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Call,
description: "vsha256ms.vv vd, vs1, vs2 — vector SHA-256 message schedule",
result_opcode: RiscVOpcode::VSHA256MS_VV,
priority: 500,
num_operands: 3,
required_extension: Some("zvknhb"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Call,
description: "vsha256ch.vv vd, vs1, vs2 — vector SHA-256 compress (choose)",
result_opcode: RiscVOpcode::VSHA256CH_VV,
priority: 501,
num_operands: 3,
required_extension: Some("zvknhb"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Call,
description: "vsha256cl.vv vd, vs1, vs2 — vector SHA-256 compress (majority + Σ)",
result_opcode: RiscVOpcode::VSHA256CL_VV,
priority: 502,
num_operands: 3,
required_extension: Some("zvknhb"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Call,
description: "vsm4e.vv vd, vs1 — vector SM4 block cipher encrypt single round",
result_opcode: RiscVOpcode::VSM4E_VV,
priority: 600,
num_operands: 2,
required_extension: Some("zvksed"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Call,
description: "vsm4e.vi vd, vs1, rnd — vector SM4 encrypt per-round by immediate",
result_opcode: RiscVOpcode::VSM4E_VI,
priority: 601,
num_operands: 2,
required_extension: Some("zvksed"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Call,
description: "vsm4d.vv vd, vs1 — vector SM4 block cipher decrypt single round",
result_opcode: RiscVOpcode::VSM4D_VV,
priority: 602,
num_operands: 2,
required_extension: Some("zvksed"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Call,
description: "vsm4d.vi vd, vs1, rnd — vector SM4 decrypt per-round by immediate",
result_opcode: RiscVOpcode::VSM4D_VI,
priority: 603,
num_operands: 2,
required_extension: Some("zvksed"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Call,
description: "vsm3me.vv vd, vs1, vs2 — vector SM3 message expansion",
result_opcode: RiscVOpcode::VSM3ME_VV,
priority: 700,
num_operands: 3,
required_extension: Some("zvksh"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Call,
description: "vsm3c.vi vd, vs1, rnd — vector SM3 compress by immediate",
result_opcode: RiscVOpcode::VSM3C_VI,
priority: 701,
num_operands: 2,
required_extension: Some("zvksh"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Call,
description: "vsha512ms.vv vd, vs1, vs2 — vector SHA-512 message schedule",
result_opcode: RiscVOpcode::VSHA512MS_VV,
priority: 800,
num_operands: 3,
required_extension: Some("zvknha"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Call,
description: "vsha512ch.vv vd, vs1, vs2 — vector SHA-512 compress (choose)",
result_opcode: RiscVOpcode::VSHA512CH_VV,
priority: 801,
num_operands: 3,
required_extension: Some("zvknha"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Call,
description: "vsha512cl.vv vd, vs1, vs2 — vector SHA-512 compress (majority + Σ)",
result_opcode: RiscVOpcode::VSHA512CL_VV,
priority: 802,
num_operands: 3,
required_extension: Some("zvknha"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Call,
description: "vsha512sum0.v vd, vs1 — vector SHA-512 Σ0 (sum0) operation",
result_opcode: RiscVOpcode::VSHA512SUM0_V,
priority: 810,
num_operands: 2,
required_extension: Some("zvknha"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Call,
description: "vsha512sum1.v vd, vs1 — vector SHA-512 Σ1 (sum1) operation",
result_opcode: RiscVOpcode::VSHA512SUM1_V,
priority: 811,
num_operands: 2,
required_extension: Some("zvknha"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Call,
description: "vsha512sig0.v vd, vs1 — vector SHA-512 σ0 (sig0) operation",
result_opcode: RiscVOpcode::VSHA512SIG0_V,
priority: 812,
num_operands: 2,
required_extension: Some("zvknha"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Call,
description: "vsha512sig1.v vd, vs1 — vector SHA-512 σ1 (sig1) operation",
result_opcode: RiscVOpcode::VSHA512SIG1_V,
priority: 813,
num_operands: 2,
required_extension: Some("zvknha"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::ICmp,
description: "vczero.eqz vd, vs1, vs2 — vector conditional zero if vs2==0",
result_opcode: RiscVOpcode::VAES128E_VV, priority: 900,
num_operands: 3,
required_extension: Some("zicond"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::ICmp,
description: "vczero.nez vd, vs1, vs2 — vector conditional zero if vs2!=0",
result_opcode: RiscVOpcode::VAES128E_VV, priority: 901,
num_operands: 3,
required_extension: Some("zicond"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::And,
description: "vandn.vv vd, vs1, vs2 — vector AND-NOT for crypto bit permute (Zvkb)",
result_opcode: RiscVOpcode::VANDN_VV,
priority: 130,
num_operands: 3,
required_extension: Some("zvkb"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Xor,
description: "vxor.vv vd, vs1, vs2 — vector XOR for crypto XOR chain (Zvkb)",
result_opcode: RiscVOpcode::VANDN_VV,
priority: 131,
num_operands: 3,
required_extension: Some("zvkb"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::Or,
description: "vor.vv vd, vs1, vs2 — vector OR for crypto bit merge (Zvkb)",
result_opcode: RiscVOpcode::VANDN_VV,
priority: 132,
num_operands: 3,
required_extension: Some("zvkb"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::ShuffleVector,
description: "vbrev8.v vd, vs1, vm — vector bit-reverse bytes with mask",
result_opcode: RiscVOpcode::VBREV8_V,
priority: 110,
num_operands: 3,
required_extension: Some("zvbb"),
requires_vlen128: true,
is_widening: false,
});
table.push(RiscVCryptoPattern {
ir_opcode: Opcode::ShuffleVector,
description: "vrev8.v vd, vs1, vm — vector byte-reverse with mask",
result_opcode: RiscVOpcode::VREV8_V,
priority: 110,
num_operands: 3,
required_extension: Some("zvbb"),
requires_vlen128: true,
is_widening: false,
});
table
}
pub fn build_riscv_crypto_isel_table() -> RiscVCryptoIselTable {
let patterns = riscv_crypto_isel_table();
let total = patterns.len();
let mut extensions = std::collections::HashSet::new();
for p in &patterns {
if let Some(ext) = p.required_extension {
extensions.insert(ext.to_string());
}
}
let mut covered: Vec<String> = extensions.into_iter().collect();
covered.sort();
RiscVCryptoIselTable {
patterns,
total_patterns: total,
extensions_covered: covered,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RiscVCryptoFeatures {
pub has_zvbb: bool,
pub has_zvbc: bool,
pub has_zvkg: bool,
pub has_zvkned: bool,
pub has_zvknhb: bool,
pub has_zvksed: bool,
pub has_zvksh: bool,
pub has_zvknha: bool,
pub has_zvkn: bool, pub has_zvks: bool, pub has_zvkt: bool, pub has_zicond: bool,
pub has_zvkb: bool, }
impl Default for RiscVCryptoFeatures {
fn default() -> Self {
Self {
has_zvbb: false,
has_zvbc: false,
has_zvkg: false,
has_zvkned: false,
has_zvknhb: false,
has_zvksed: false,
has_zvksh: false,
has_zvknha: false,
has_zvkn: false,
has_zvks: false,
has_zvkt: false,
has_zicond: false,
has_zvkb: false,
}
}
}
impl RiscVCryptoFeatures {
pub fn full_crypto() -> Self {
Self {
has_zvbb: true,
has_zvbc: true,
has_zvkg: true,
has_zvkned: true,
has_zvknhb: true,
has_zvksed: true,
has_zvksh: true,
has_zvknha: true,
has_zvkn: true,
has_zvks: true,
has_zvkt: true,
has_zicond: true,
has_zvkb: true,
}
}
pub fn nist_crypto() -> Self {
Self {
has_zvkned: true,
has_zvknhb: true,
has_zvknha: true,
has_zvkt: true,
has_zvkb: true,
has_zvkn: true,
..Default::default()
}
}
pub fn shangmi_crypto() -> Self {
Self {
has_zvksed: true,
has_zvksh: true,
has_zvkt: true,
has_zvkb: true,
has_zvks: true,
..Default::default()
}
}
pub fn has_extension(&self, ext: &str) -> bool {
match ext {
"zvbb" => self.has_zvbb,
"zvbc" => self.has_zvbc,
"zvkg" => self.has_zvkg,
"zvkned" => self.has_zvkned || self.has_zvkn,
"zvknhb" => self.has_zvknhb || self.has_zvkn,
"zvksed" => self.has_zvksed || self.has_zvks,
"zvksh" => self.has_zvksh || self.has_zvks,
"zvknha" => self.has_zvknha || self.has_zvkn,
"zvkn" => self.has_zvkn,
"zvks" => self.has_zvks,
"zvkt" => self.has_zvkt,
"zvkb" => self.has_zvkb,
"zicond" => self.has_zicond,
_ => false,
}
}
}
pub struct RiscVCryptoIselEngine {
pub table: RiscVCryptoIselTable,
pub features: RiscVCryptoFeatures,
}
impl RiscVCryptoIselEngine {
pub fn new(features: RiscVCryptoFeatures) -> Self {
let table = build_riscv_crypto_isel_table();
Self { table, features }
}
pub fn is_pattern_applicable(&self, pattern: &RiscVCryptoPattern) -> bool {
if let Some(ext) = pattern.required_extension {
if !self.features.has_extension(ext) {
return false;
}
}
true
}
pub fn select_pattern(&self, ir_opcodes: &[Opcode]) -> Option<&RiscVCryptoPattern> {
let mut best: Option<&RiscVCryptoPattern> = None;
for p in &self.table.patterns {
if ir_opcodes.contains(&p.ir_opcode) && self.is_pattern_applicable(p) {
match best {
None => best = Some(p),
Some(b) if p.priority < b.priority => best = Some(p),
_ => {}
}
}
}
best
}
pub fn stats(&self) -> String {
format!(
"RISC-V Crypto ISel: {} patterns, extensions: {:?}",
self.table.total_patterns, self.table.extensions_covered,
)
}
pub fn requires_constant_time(&self) -> bool {
self.features.has_zvkt
}
pub fn operation_requires_constant_time(&self, op: CryptoOperation) -> bool {
if !self.requires_constant_time() {
return false;
}
match op {
CryptoOperation::Aes128Encrypt
| CryptoOperation::Aes128Decrypt
| CryptoOperation::Aes256Encrypt
| CryptoOperation::Aes256Decrypt
| CryptoOperation::Sha256
| CryptoOperation::Sha512
| CryptoOperation::Sm3
| CryptoOperation::Sm4Encrypt
| CryptoOperation::Sm4Decrypt
| CryptoOperation::Gcm => true,
CryptoOperation::Clmul
| CryptoOperation::Rotate
| CryptoOperation::BitReverse
| CryptoOperation::PopCount => false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AesKeyScheduleMode {
Aes128,
Aes192,
Aes256,
}
impl AesKeyScheduleMode {
pub fn num_rounds(&self) -> u32 {
match self {
AesKeyScheduleMode::Aes128 => 10,
AesKeyScheduleMode::Aes192 => 12,
AesKeyScheduleMode::Aes256 => 14,
}
}
pub fn key_size_bytes(&self) -> usize {
match self {
AesKeyScheduleMode::Aes128 => 16,
AesKeyScheduleMode::Aes192 => 24,
AesKeyScheduleMode::Aes256 => 32,
}
}
pub fn encrypt_opcode(&self) -> RiscVOpcode {
match self {
AesKeyScheduleMode::Aes128 => RiscVOpcode::VAES128E_VV,
AesKeyScheduleMode::Aes192 => RiscVOpcode::VAES128E_VV, AesKeyScheduleMode::Aes256 => RiscVOpcode::VAES256E_VV,
}
}
pub fn decrypt_opcode(&self) -> RiscVOpcode {
match self {
AesKeyScheduleMode::Aes128 => RiscVOpcode::VAES128D_VV,
AesKeyScheduleMode::Aes192 => RiscVOpcode::VAES128D_VV,
AesKeyScheduleMode::Aes256 => RiscVOpcode::VAES256D_VV,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Sm4Mode {
Encrypt,
Decrypt,
KeySchedule,
}
impl Sm4Mode {
pub fn num_rounds(&self) -> u32 {
32
}
pub fn key_size_bytes(&self) -> usize {
16
}
pub fn to_opcode(&self) -> RiscVOpcode {
match self {
Sm4Mode::Encrypt => RiscVOpcode::VSM4E_VV,
Sm4Mode::Decrypt => RiscVOpcode::VSM4D_VV,
Sm4Mode::KeySchedule => RiscVOpcode::VSM4E_VV, }
}
}
#[derive(Debug, Clone)]
pub struct Sha256Operation {
pub message_schedule: RiscVOpcode,
pub compress_choose: RiscVOpcode,
pub compress_majority: RiscVOpcode,
}
impl Default for Sha256Operation {
fn default() -> Self {
Self {
message_schedule: RiscVOpcode::VSHA256MS_VV,
compress_choose: RiscVOpcode::VSHA256CH_VV,
compress_majority: RiscVOpcode::VSHA256CL_VV,
}
}
}
#[derive(Debug, Clone)]
pub struct Sha512Operation {
pub message_schedule: RiscVOpcode,
pub compress_choose: RiscVOpcode,
pub compress_majority: RiscVOpcode,
pub sum0: RiscVOpcode,
pub sum1: RiscVOpcode,
pub sig0: RiscVOpcode,
pub sig1: RiscVOpcode,
}
impl Default for Sha512Operation {
fn default() -> Self {
Self {
message_schedule: RiscVOpcode::VSHA512MS_VV,
compress_choose: RiscVOpcode::VSHA512CH_VV,
compress_majority: RiscVOpcode::VSHA512CL_VV,
sum0: RiscVOpcode::VSHA512SUM0_V,
sum1: RiscVOpcode::VSHA512SUM1_V,
sig0: RiscVOpcode::VSHA512SIG0_V,
sig1: RiscVOpcode::VSHA512SIG1_V,
}
}
}
#[derive(Debug, Clone)]
pub struct GcmOperation {
pub hash_multiply_add: RiscVOpcode,
pub gf_multiply: RiscVOpcode,
}
impl Default for GcmOperation {
fn default() -> Self {
Self {
hash_multiply_add: RiscVOpcode::VGHSH_VV,
gf_multiply: RiscVOpcode::VGMUL_VV,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ZvkbPermutation {
RorI,
RolI,
Rev8,
Pack,
Unpack,
Zip,
Unzip,
}
impl ZvkbPermutation {
pub fn to_opcode(&self) -> RiscVOpcode {
match self {
ZvkbPermutation::RorI => RiscVOpcode::VROR_VI,
ZvkbPermutation::RolI => RiscVOpcode::VROL_VX,
ZvkbPermutation::Rev8 => RiscVOpcode::VREV8_V,
ZvkbPermutation::Pack => RiscVOpcode::VANDN_VV,
ZvkbPermutation::Unpack => RiscVOpcode::VANDN_VX,
ZvkbPermutation::Zip => RiscVOpcode::VWSLL_VV,
ZvkbPermutation::Unzip => RiscVOpcode::VWSLL_VX,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CryptoOperation {
Aes128Encrypt,
Aes128Decrypt,
Aes256Encrypt,
Aes256Decrypt,
Sha256,
Sha512,
Sm3,
Sm4Encrypt,
Sm4Decrypt,
Gcm,
Clmul,
Rotate,
BitReverse,
PopCount,
}
impl CryptoOperation {
pub fn to_extension_and_opcode(self) -> (&'static str, RiscVOpcode) {
match self {
CryptoOperation::Aes128Encrypt => ("zvkned", RiscVOpcode::VAES128E_VV),
CryptoOperation::Aes128Decrypt => ("zvkned", RiscVOpcode::VAES128D_VV),
CryptoOperation::Aes256Encrypt => ("zvkned", RiscVOpcode::VAES256E_VV),
CryptoOperation::Aes256Decrypt => ("zvkned", RiscVOpcode::VAES256D_VV),
CryptoOperation::Sha256 => ("zvknhb", RiscVOpcode::VSHA256MS_VV),
CryptoOperation::Sha512 => ("zvknha", RiscVOpcode::VSHA512MS_VV),
CryptoOperation::Sm3 => ("zvksh", RiscVOpcode::VSM3ME_VV),
CryptoOperation::Sm4Encrypt => ("zvksed", RiscVOpcode::VSM4E_VV),
CryptoOperation::Sm4Decrypt => ("zvksed", RiscVOpcode::VSM4D_VV),
CryptoOperation::Gcm => ("zvkg", RiscVOpcode::VGHSH_VV),
CryptoOperation::Clmul => ("zvbc", RiscVOpcode::VCLMUL_VV),
CryptoOperation::Rotate => ("zvbb", RiscVOpcode::VROL_VV),
CryptoOperation::BitReverse => ("zvbb", RiscVOpcode::VBREV_V),
CryptoOperation::PopCount => ("zvbb", RiscVOpcode::VCPOP_V),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_crypto_isel_table_builds() {
let table = build_riscv_crypto_isel_table();
assert!(table.total_patterns > 30);
assert!(!table.extensions_covered.is_empty());
assert!(table.extensions_covered.contains(&"zvbb".to_string()));
assert!(table.extensions_covered.contains(&"zvkned".to_string()));
assert!(table.extensions_covered.contains(&"zvknhb".to_string()));
}
#[test]
fn test_crypto_features_default() {
let f = RiscVCryptoFeatures::default();
assert!(!f.has_zvbb);
assert!(!f.has_extension("zvkned"));
}
#[test]
fn test_crypto_features_full() {
let f = RiscVCryptoFeatures::full_crypto();
assert!(f.has_zvbb);
assert!(f.has_extension("zvkned"));
assert!(f.has_extension("zvks"));
assert!(f.has_zicond);
}
#[test]
fn test_crypto_features_nist() {
let f = RiscVCryptoFeatures::nist_crypto();
assert!(f.has_zvkned);
assert!(f.has_zvknhb);
assert!(f.has_zvkn);
assert!(!f.has_zvksed);
assert!(!f.has_zvks);
}
#[test]
fn test_crypto_features_shangmi() {
let f = RiscVCryptoFeatures::shangmi_crypto();
assert!(f.has_zvksed);
assert!(f.has_zvksh);
assert!(f.has_zvks);
assert!(!f.has_zvkned);
assert!(!f.has_zvkn);
}
#[test]
fn test_crypto_isel_engine_pattern_applicable() {
let engine = RiscVCryptoIselEngine::new(RiscVCryptoFeatures::full_crypto());
let pat = RiscVCryptoPattern {
ir_opcode: Opcode::Call,
description: "test",
result_opcode: RiscVOpcode::VANDN_VV,
priority: 100,
num_operands: 3,
required_extension: Some("zvbb"),
requires_vlen128: true,
is_widening: false,
};
assert!(engine.is_pattern_applicable(&pat));
}
#[test]
fn test_crypto_isel_engine_pattern_disabled() {
let engine = RiscVCryptoIselEngine::new(RiscVCryptoFeatures::default());
let pat = RiscVCryptoPattern {
ir_opcode: Opcode::Call,
description: "test",
result_opcode: RiscVOpcode::VAES128E_VV,
priority: 100,
num_operands: 2,
required_extension: Some("zvkned"),
requires_vlen128: true,
is_widening: false,
};
assert!(!engine.is_pattern_applicable(&pat));
}
#[test]
fn test_crypto_operation_mapping() {
assert_eq!(
CryptoOperation::Aes128Encrypt.to_extension_and_opcode().0,
"zvkned"
);
assert_eq!(
CryptoOperation::Sha256.to_extension_and_opcode().0,
"zvknhb"
);
assert_eq!(
CryptoOperation::Sm4Encrypt.to_extension_and_opcode().0,
"zvksed"
);
}
#[test]
fn test_crypto_constant_time() {
let f = RiscVCryptoFeatures::full_crypto();
let engine = RiscVCryptoIselEngine::new(f);
assert!(engine.requires_constant_time());
let f_no_kt = RiscVCryptoFeatures::nist_crypto();
let engine2 = RiscVCryptoIselEngine::new(f_no_kt);
assert!(engine2.requires_constant_time());
let f_min = RiscVCryptoFeatures::default();
let engine3 = RiscVCryptoIselEngine::new(f_min);
assert!(!engine3.requires_constant_time());
}
#[test]
fn test_aes_key_schedule() {
assert_eq!(AesKeyScheduleMode::Aes128.num_rounds(), 10);
assert_eq!(AesKeyScheduleMode::Aes192.num_rounds(), 12);
assert_eq!(AesKeyScheduleMode::Aes256.num_rounds(), 14);
assert_eq!(AesKeyScheduleMode::Aes128.key_size_bytes(), 16);
assert_eq!(AesKeyScheduleMode::Aes256.key_size_bytes(), 32);
}
#[test]
fn test_sm4_mode() {
assert_eq!(Sm4Mode::Encrypt.num_rounds(), 32);
assert_eq!(Sm4Mode::Decrypt.num_rounds(), 32);
assert_eq!(Sm4Mode::Encrypt.key_size_bytes(), 16);
assert_eq!(Sm4Mode::Encrypt.to_opcode(), RiscVOpcode::VSM4E_VV);
assert_eq!(Sm4Mode::Decrypt.to_opcode(), RiscVOpcode::VSM4D_VV);
}
#[test]
fn test_sha256_operation_default() {
let op = Sha256Operation::default();
assert_eq!(op.message_schedule, RiscVOpcode::VSHA256MS_VV);
assert_eq!(op.compress_choose, RiscVOpcode::VSHA256CH_VV);
assert_eq!(op.compress_majority, RiscVOpcode::VSHA256CL_VV);
}
#[test]
fn test_sha512_operation_default() {
let op = Sha512Operation::default();
assert_eq!(op.message_schedule, RiscVOpcode::VSHA512MS_VV);
assert_eq!(op.sum0, RiscVOpcode::VSHA512SUM0_V);
assert_eq!(op.sig0, RiscVOpcode::VSHA512SIG0_V);
}
#[test]
fn test_gcm_operation_default() {
let op = GcmOperation::default();
assert_eq!(op.hash_multiply_add, RiscVOpcode::VGHSH_VV);
assert_eq!(op.gf_multiply, RiscVOpcode::VGMUL_VV);
}
#[test]
fn test_zvkb_permutation_mapping() {
assert_eq!(ZvkbPermutation::RorI.to_opcode(), RiscVOpcode::VROR_VI);
assert_eq!(ZvkbPermutation::Rev8.to_opcode(), RiscVOpcode::VREV8_V);
}
#[test]
fn test_crypto_operation_constant_time_check() {
let engine = RiscVCryptoIselEngine::new(RiscVCryptoFeatures::full_crypto());
assert!(engine.operation_requires_constant_time(CryptoOperation::Aes128Encrypt));
assert!(engine.operation_requires_constant_time(CryptoOperation::Sha256));
assert!(engine.operation_requires_constant_time(CryptoOperation::Gcm));
assert!(!engine.operation_requires_constant_time(CryptoOperation::Clmul));
assert!(!engine.operation_requires_constant_time(CryptoOperation::PopCount));
let engine_no_kt = RiscVCryptoIselEngine::new(RiscVCryptoFeatures::default());
assert!(!engine_no_kt.operation_requires_constant_time(CryptoOperation::Aes128Encrypt));
}
#[test]
fn test_crypto_engine_select_priority() {
let engine = RiscVCryptoIselEngine::new(RiscVCryptoFeatures::full_crypto());
let pat = engine.select_pattern(&[Opcode::And]);
assert!(pat.is_some());
let pat_mul = engine.select_pattern(&[Opcode::Mul]);
assert!(pat_mul.is_some());
}
#[test]
fn test_crypto_isel_all_patterns_have_opcode() {
let table = build_riscv_crypto_isel_table();
for p in &table.patterns {
assert!(p.num_operands > 0);
assert!(p.priority > 0);
}
}
#[test]
fn test_crypto_pattern_extension_coverage() {
let table = build_riscv_crypto_isel_table();
let covered: std::collections::HashSet<&str> = table
.extensions_covered
.iter()
.map(|s| s.as_str())
.collect();
assert!(covered.contains("zvbb"));
assert!(covered.contains("zvbc"));
assert!(covered.contains("zvkg"));
assert!(covered.contains("zvkned"));
assert!(covered.contains("zvknhb"));
assert!(covered.contains("zvksed"));
assert!(covered.contains("zvksh"));
assert!(covered.contains("zvknha"));
}
#[test]
fn test_crypto_aes_round_counts() {
assert_eq!(AesKeyScheduleMode::Aes128.num_rounds(), 10);
assert_eq!(AesKeyScheduleMode::Aes256.num_rounds(), 14);
}
#[test]
fn test_crypto_sm4_round_count() {
assert_eq!(Sm4Mode::Encrypt.num_rounds(), 32);
assert_eq!(Sm4Mode::Decrypt.num_rounds(), 32);
}
#[test]
fn test_zvkb_permutation_all_variants() {
let perms = [
ZvkbPermutation::RorI,
ZvkbPermutation::RolI,
ZvkbPermutation::Rev8,
ZvkbPermutation::Pack,
ZvkbPermutation::Unpack,
ZvkbPermutation::Zip,
ZvkbPermutation::Unzip,
];
for perm in &perms {
let op = perm.to_opcode();
assert!(matches!(
op,
RiscVOpcode::VROR_VI
| RiscVOpcode::VROL_VX
| RiscVOpcode::VREV8_V
| RiscVOpcode::VANDN_VV
| RiscVOpcode::VANDN_VX
| RiscVOpcode::VWSLL_VV
| RiscVOpcode::VWSLL_VX
));
}
}
#[test]
fn test_crypto_operation_all_mappings() {
let ops = [
CryptoOperation::Aes128Encrypt,
CryptoOperation::Aes128Decrypt,
CryptoOperation::Aes256Encrypt,
CryptoOperation::Aes256Decrypt,
CryptoOperation::Sha256,
CryptoOperation::Sha512,
CryptoOperation::Sm3,
CryptoOperation::Sm4Encrypt,
CryptoOperation::Sm4Decrypt,
CryptoOperation::Gcm,
CryptoOperation::Clmul,
CryptoOperation::Rotate,
CryptoOperation::BitReverse,
CryptoOperation::PopCount,
];
for op in &ops {
let (ext, opcode) = op.to_extension_and_opcode();
assert!(!ext.is_empty());
}
}
#[test]
fn test_crypto_aes192_opcodes() {
let mode = AesKeyScheduleMode::Aes192;
assert_eq!(mode.num_rounds(), 12);
assert_eq!(mode.key_size_bytes(), 24);
assert_eq!(mode.encrypt_opcode(), RiscVOpcode::VAES128E_VV);
assert_eq!(mode.decrypt_opcode(), RiscVOpcode::VAES128D_VV);
}
#[test]
fn test_sha512_helper_functions() {
let op = Sha512Operation::default();
assert_eq!(op.sum0, RiscVOpcode::VSHA512SUM0_V);
assert_eq!(op.sum1, RiscVOpcode::VSHA512SUM1_V);
assert_eq!(op.sig0, RiscVOpcode::VSHA512SIG0_V);
assert_eq!(op.sig1, RiscVOpcode::VSHA512SIG1_V);
assert_ne!(op.sum0, op.sum1);
assert_ne!(op.sig0, op.sig1);
}
#[test]
fn test_gcm_operation_is_complete() {
let op = GcmOperation::default();
assert_ne!(op.hash_multiply_add, op.gf_multiply);
assert!(matches!(op.hash_multiply_add, RiscVOpcode::VGHSH_VV));
assert!(matches!(op.gf_multiply, RiscVOpcode::VGMUL_VV));
}
#[test]
fn test_sm4_key_schedule_mode() {
let ks = Sm4Mode::KeySchedule;
assert_eq!(ks.num_rounds(), 32);
assert_eq!(ks.key_size_bytes(), 16);
assert_eq!(ks.to_opcode(), RiscVOpcode::VSM4E_VV);
}
}