use std::path::Path;
use crate::bjca::bindings;
use crate::error::Error;
const BJCA_SIGN_BUF_LEN: usize = 16 * 1024;
const BJCA_CERT_BUF_LEN: usize = 16 * 1024;
const BJCA_DECODE_BUF_LEN: usize = 16 * 1024;
pub struct BjcaSignOutput {
pub signature_der: Vec<u8>,
pub leaf_cert_der: Vec<u8>,
}
struct BjcaHandleGuard {
handle: bindings::BJCA_HANDLE,
}
impl Drop for BjcaHandleGuard {
fn drop(&mut self) {
unsafe {
let mut h = self.handle;
let _ = bindings::BJCA_SVS_Final(&mut h);
self.handle = std::ptr::null_mut();
}
}
}
pub fn sign_signed_attrs_with_bjca(
signed_attrs_der: &[u8],
config_path: Option<&Path>,
) -> Result<BjcaSignOutput, Error> {
let mut handle: bindings::BJCA_HANDLE = std::ptr::null_mut();
let default_ini = Path::new("/etc/BJCA_SVS_Config.ini");
let mut cfg_owned: Option<Vec<u8>> = None;
let cfg_ptr: *mut bindings::BJCA_CHAR;
match config_path {
Some(path) => {
let mut ini_bytes = path.to_string_lossy().into_owned().into_bytes();
ini_bytes.push(0);
cfg_ptr = ini_bytes.as_mut_ptr() as *mut bindings::BJCA_CHAR;
cfg_owned = Some(ini_bytes);
}
None => {
if !default_ini.exists() {
return Err(Error::CmsSign(format!(
"BJCA config not found: {}; pass --bjca-config explicitly",
default_ini.display()
)));
}
cfg_ptr = std::ptr::null_mut();
}
}
let ret = unsafe { bindings::BJCA_SVS_Init_Default(&mut handle, cfg_ptr) };
let _keepalive = cfg_owned;
if ret != 0 {
let cfg_desc = match config_path {
Some(path) => path.display().to_string(),
None => format!("NULL(default:{})", default_ini.display()),
};
return Err(Error::CmsSign(format!("BJCA_SVS_Init_Default failed: {ret}, config={cfg_desc}")));
}
let guard = BjcaHandleGuard { handle };
let mut sign_b64 = vec![0u8; BJCA_SIGN_BUF_LEN];
let mut sign_b64_len = sign_b64.len() as bindings::BJCA_ULONG;
let ret = unsafe {
bindings::BJCA_SVS_SignData(
guard.handle,
signed_attrs_der.as_ptr() as *mut bindings::BJCA_UCHAR,
signed_attrs_der.len() as bindings::BJCA_ULONG,
sign_b64.as_mut_ptr(),
&mut sign_b64_len,
)
};
if ret != 0 {
return Err(Error::CmsSign(format!("BJCA_SVS_SignData failed: {ret}")));
}
sign_b64.truncate(sign_b64_len as usize);
let signature_der = base64_decode(guard.handle, &sign_b64, BJCA_DECODE_BUF_LEN)?;
let mut cert_b64 = vec![0u8; BJCA_CERT_BUF_LEN];
let mut cert_b64_len = cert_b64.len() as bindings::BJCA_ULONG;
let ret = unsafe {
bindings::BJCA_SVS_GetServerCertificate(
guard.handle,
cert_b64.as_mut_ptr(),
&mut cert_b64_len,
)
};
if ret != 0 {
return Err(Error::CmsSign(format!(
"BJCA_SVS_GetServerCertificate failed: {ret}"
)));
}
cert_b64.truncate(cert_b64_len as usize);
let leaf_cert_der = base64_decode(guard.handle, &cert_b64, BJCA_DECODE_BUF_LEN)?;
Ok(BjcaSignOutput {
signature_der,
leaf_cert_der,
})
}
fn base64_decode(
handle: bindings::BJCA_HANDLE,
input: &[u8],
initial_out_len: usize,
) -> Result<Vec<u8>, Error> {
let mut out = vec![0u8; initial_out_len.max(input.len() + 256)];
let mut out_len = out.len() as bindings::BJCA_ULONG;
let ret = unsafe {
bindings::BJCA_SVS_Base64Decode(
handle,
input.as_ptr() as *mut bindings::BJCA_UCHAR,
input.len() as bindings::BJCA_ULONG,
out.as_mut_ptr(),
&mut out_len,
)
};
if ret != 0 {
return Err(Error::CmsSign(format!(
"BJCA_SVS_Base64Decode failed: {ret}"
)));
}
out.truncate(out_len as usize);
Ok(out)
}