use std::fs;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use mbedtls::hash::Type as MdType;
use mbedtls::pk::ECDSA_MAX_LEN;
use mbedtls::rng::{CtrDrbg, OsEntropy};
use mbedtls::x509::Certificate;
use crate::error::Error;
pub const DEFAULT_GMSSL: &str = "./GmSSL/build/bin/gmssl";
pub const DEFAULT_SM2_ID: &str = "1234567812345678";
pub fn find_gmssl_in_path(program: &str) -> Option<PathBuf> {
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
std::env::var_os("PATH").and_then(|paths| {
std::env::split_paths(&paths).find_map(|dir| {
let full = dir.join(program);
if full.is_file() {
if let Ok(m) = fs::metadata(&full) {
if m.permissions().mode() & 0o111 != 0 {
return Some(full);
}
}
}
None
})
})
}
#[cfg(windows)]
{
std::env::var_os("PATH").and_then(|paths| {
std::env::split_paths(&paths).find_map(|dir| {
let with_exe = dir.join(format!("{program}.exe"));
if with_exe.is_file() {
return Some(with_exe);
}
let full = dir.join(program);
if full.is_file() {
return Some(full);
}
None
})
})
}
#[cfg(not(any(unix, windows)))]
{
let _ = program;
None
}
}
pub fn resolve_gmssl_path(cli_override: Option<&Path>) -> PathBuf {
if let Some(explicit) = cli_override {
return explicit.to_path_buf();
}
if let Ok(s) = std::env::var("GMSSL") {
let t = s.trim();
if !t.is_empty() {
return PathBuf::from(t);
}
}
if let Some(p) = find_gmssl_in_path("gmssl") {
return p;
}
let candidate = PathBuf::from(DEFAULT_GMSSL);
if candidate.exists() {
return candidate;
}
let mut cwd = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
loop {
let c = cwd.join("GmSSL/build/bin/gmssl");
if c.exists() {
return c;
}
if !cwd.pop() {
break;
}
}
PathBuf::from(DEFAULT_GMSSL)
}
pub fn sm2_sign(
_gmssl: &str,
key_pem: &Path,
pass: &str,
input: &Path,
out_sig_der: &Path,
) -> Result<(), Error> {
let pem = fs::read_to_string(key_pem)?;
let mut pk = crate::key::sm2_pk_from_pkcs8_pem_with_pass(&pem, pass)?;
let data = fs::read(input)?;
let entropy = Arc::new(OsEntropy::new());
let mut rng = CtrDrbg::new(entropy, None)?;
let mut sig = vec![0u8; ECDSA_MAX_LEN];
let n = pk.sm2_sign(MdType::SM3, &data, &mut sig, &mut rng)?;
sig.truncate(n);
fs::write(out_sig_der, &sig)?;
Ok(())
}
pub fn sm2_verify(
_gmssl: &str,
cert_pem: &Path,
input: &Path,
sig_der: &Path,
) -> Result<(), Error> {
let mut pem = fs::read(cert_pem)?;
pem.push(0);
let mut cert = Certificate::from_pem(&pem)?;
let data = fs::read(input)?;
let sig = fs::read(sig_der)?;
cert.public_key_mut()
.sm2_verify(MdType::SM3, &data, &sig)?;
Ok(())
}