use super::super::{CertRule, RuleViolation};
use crate::manifest::{RuleCategory, Severity};
use crate::utility::cert_c::ast_utils::get_node_text;
use tree_sitter::Node;
const WEAK_ALGORITHMS: &[&str] = &[
"CALG_DES",
"CALG_3DES",
"CALG_3DES_112",
"CALG_RC2",
"CALG_RC4",
"CALG_RC5",
"ALG_DES",
"ALG_3DES",
"ALG_RC4",
"ALG_RC5",
];
const CRYPTO_FUNCTIONS: &[(&str, usize)] = &[
("CryptDeriveKey", 1),
("CryptGenKey", 1),
("CryptImportKey", 3),
];
const WEAK_CIPHER_FUNCTIONS: &[&str] = &[
"EVP_des_ecb",
"EVP_des_cbc",
"EVP_des_cfb",
"EVP_des_ofb",
"EVP_des_ede",
"EVP_des_ede3",
"EVP_des_ede_cbc",
"EVP_des_ede3_cbc",
"EVP_rc4",
"EVP_rc2_cbc",
"EVP_rc2_ecb",
"EVP_rc2_cfb",
"EVP_rc2_ofb",
"EVP_rc5_32_12_16_cbc",
"DES_set_key",
"DES_ecb_encrypt",
"DES_ncbc_encrypt",
"DES_cbc_encrypt",
];
pub struct Msc42C;
impl Msc42C {
pub fn new() -> Self {
Self
}
fn check_node(&self, node: &Node, source: &str, violations: &mut Vec<RuleViolation>) {
if node.kind() == "call_expression" {
self.check_crypto_call(node, source, violations);
}
for i in 0..node.child_count() {
if let Some(child) = node.child(i) {
self.check_node(&child, source, violations);
}
}
}
fn check_crypto_call(&self, node: &Node, source: &str, violations: &mut Vec<RuleViolation>) {
let func_name = match node.child_by_field_name("function") {
Some(f) => get_node_text(&f, source).to_string(),
None => return,
};
if WEAK_CIPHER_FUNCTIONS.contains(&func_name.as_str()) {
violations.push(RuleViolation {
rule_id: self.rule_id().to_string(),
severity: self.severity(),
message: format!(
"Use of weak cryptographic function '{}'. \
Use a modern algorithm such as AES instead.",
func_name
),
file_path: String::new(),
line: node.start_position().row + 1,
column: node.start_position().column + 1,
suggestion: Some(
"Replace with a strong algorithm (e.g., EVP_aes_256_cbc, AES-256)".to_string(),
),
..Default::default()
});
return;
}
for &(crypto_func, alg_arg_idx) in CRYPTO_FUNCTIONS {
if func_name == crypto_func {
if let Some(args) = node.child_by_field_name("arguments") {
if let Some(alg_arg) = self.get_nth_argument(&args, alg_arg_idx) {
let alg_text = get_node_text(&alg_arg, source).trim().to_string();
if WEAK_ALGORITHMS.contains(&alg_text.as_str()) {
violations.push(RuleViolation {
rule_id: self.rule_id().to_string(),
severity: self.severity(),
message: format!(
"Use of weak cryptographic algorithm '{}' in '{}'. \
DES, 3DES, RC2, RC4, and RC5 are deprecated.",
alg_text, func_name
),
file_path: String::new(),
line: alg_arg.start_position().row + 1,
column: alg_arg.start_position().column + 1,
suggestion: Some(
"Use CALG_AES_256 or CALG_AES_128 instead".to_string(),
),
..Default::default()
});
}
}
}
return;
}
}
}
fn get_nth_argument<'a>(&self, args: &Node<'a>, index: usize) -> Option<Node<'a>> {
let mut count = 0;
for i in 0..args.child_count() {
if let Some(child) = args.child(i) {
let kind = child.kind();
if kind != "(" && kind != ")" && kind != "," {
if count == index {
return Some(child);
}
count += 1;
}
}
}
None
}
}
impl CertRule for Msc42C {
fn rule_id(&self) -> &'static str {
"MSC42-C"
}
fn description(&self) -> &'static str {
"Do not use deprecated or weak cryptographic algorithms"
}
fn severity(&self) -> Severity {
Severity::High
}
fn category(&self) -> RuleCategory {
RuleCategory::Rule
}
fn cert_id(&self) -> &'static str {
"MSC42-C"
}
fn check(&self, node: &Node, source: &str) -> Vec<RuleViolation> {
let mut violations = Vec::new();
self.check_node(node, source, &mut violations);
violations
}
}