use std::fs;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use crate::crypto::hash::MdType;
use crate::crypto::key::{pk_from_cert_der, sm2_pk_from_pkcs8_pem_with_pass};
use crate::crypto::pk::ECDSA_MAX_LEN;
use crate::crypto::rng::{CtrDrbg, OsEntropy};
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 = 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 pem = fs::read_to_string(cert_pem)?;
let der = cert_der_from_pem(&pem)?;
let mut pk = pk_from_cert_der(&der).map_err(|e| Error::Sm2(e.to_string()))?;
let data = fs::read(input)?;
let sig = fs::read(sig_der)?;
pk.sm2_verify(MdType::SM3, &data, &sig)?;
Ok(())
}
fn cert_der_from_pem(pem: &str) -> Result<Vec<u8>, Error> {
use base64::Engine;
let b64: String = pem
.lines()
.filter(|l| !l.starts_with("-----"))
.collect();
base64::engine::general_purpose::STANDARD
.decode(b64)
.map_err(|e| Error::Sm2(e.to_string()))
}