#![warn(missing_docs)]
#![allow(clippy::missing_safety_doc)]
#![allow(non_camel_case_types)]
#![allow(unsafe_code)]
use core::ffi::{c_char, c_int, c_void};
use core::ptr;
use core::slice;
use gmcrypto_core::asn1::ciphertext::{
decode as ciphertext_der_decode, encode as ciphertext_der_encode,
};
use gmcrypto_core::hmac::{HmacSm3 as InnerHmacSm3, hmac_sm3};
use gmcrypto_core::kdf::pbkdf2_hmac_sm3;
use gmcrypto_core::sm2::raw_ciphertext::{decode_c1c2c3_legacy, decode_c1c3c2, encode_c1c3c2};
use gmcrypto_core::sm2::{
DEFAULT_SIGNER_ID, Sm2PrivateKey, Sm2PublicKey, decrypt as sm2_decrypt, encrypt as sm2_encrypt,
sign_with_id, verify_with_id,
};
use gmcrypto_core::sm2::key_exchange::{
Sm2KxConfirm, Sm2KxEphemeralPoint, Sm2KxInitiator as InnerSm2KxInitiator,
Sm2KxInitiatorWaiting as InnerSm2KxInitiatorWaiting, Sm2KxResponder as InnerSm2KxResponder,
Sm2KxResponderWaiting as InnerSm2KxResponderWaiting,
};
use gmcrypto_core::sm3::{Sm3 as InnerSm3, hash as sm3_hash};
use gmcrypto_core::sm4::{
Sm4CbcDecryptor as InnerSm4CbcDec, Sm4CbcEncryptor as InnerSm4CbcEnc, Sm4Cipher, mode_cbc,
};
use gmcrypto_core::sm4::{
GcmTagLen, Sm4GcmDecryptor as InnerSm4GcmDec, Sm4GcmEncryptor as InnerSm4GcmEnc, mode_ccm,
mode_gcm,
};
use gmcrypto_core::sm4::mode_xts;
use gmcrypto_core::{pem, pkcs8};
use rand_core::TryRng;
pub const GMCRYPTO_OK: c_int = 0;
pub const GMCRYPTO_ERR: c_int = -1;
pub const GMCRYPTO_SM3_DIGEST_SIZE: usize = 32;
pub const GMCRYPTO_SM4_BLOCK_SIZE: usize = 16;
pub const GMCRYPTO_SM4_KEY_SIZE: usize = 16;
pub const GMCRYPTO_SM4_XTS_KEY_SIZE: usize = 2 * GMCRYPTO_SM4_KEY_SIZE;
pub const GMCRYPTO_SM2_SEC1_UNCOMPRESSED_SIZE: usize = 65;
pub const GMCRYPTO_SM2_SCALAR_SIZE: usize = 32;
pub const GMCRYPTO_SM2_KX_CONFIRM_SIZE: usize = 32;
pub struct gmcrypto_sm3_t {
inner: InnerSm3,
}
pub struct gmcrypto_hmac_sm3_t {
inner: InnerHmacSm3,
}
pub struct gmcrypto_sm4_t {
inner: Sm4Cipher,
}
pub struct gmcrypto_sm4_cbc_encryptor_t {
inner: InnerSm4CbcEnc,
}
pub struct gmcrypto_sm4_cbc_decryptor_t {
inner: InnerSm4CbcDec,
}
pub struct gmcrypto_sm4_gcm_encryptor_t {
inner: InnerSm4GcmEnc,
}
pub struct gmcrypto_sm4_gcm_decryptor_t {
inner: InnerSm4GcmDec,
}
pub struct gmcrypto_sm2_privkey_t {
inner: Sm2PrivateKey,
}
pub struct gmcrypto_sm2_pubkey_t {
inner: Sm2PublicKey,
}
pub struct gmcrypto_sm2_kx_initiator_t {
inner: InnerSm2KxInitiatorWaiting,
klen: usize,
}
enum InnerKxResponderState {
Fresh(Box<InnerSm2KxResponder>),
Waiting(Box<InnerSm2KxResponderWaiting>),
Spent,
}
pub struct gmcrypto_sm2_kx_responder_t {
state: InnerKxResponderState,
klen: usize,
}
#[allow(unsafe_code)]
unsafe fn try_slice<'a>(ptr: *const u8, len: usize) -> Option<&'a [u8]> {
if len == 0 {
Some(&[])
} else if ptr.is_null() {
None
} else {
Some(unsafe { slice::from_raw_parts(ptr, len) })
}
}
#[allow(unsafe_code)]
unsafe fn try_slice_mut<'a>(ptr: *mut u8, len: usize) -> Option<&'a mut [u8]> {
if len == 0 {
Some(&mut [])
} else if ptr.is_null() {
None
} else {
Some(unsafe { slice::from_raw_parts_mut(ptr, len) })
}
}
#[allow(unsafe_code)]
unsafe fn write_output(
bytes: &[u8],
out: *mut u8,
out_capacity: usize,
out_actual_len: *mut usize,
) -> c_int {
if out_actual_len.is_null() {
return GMCRYPTO_ERR;
}
unsafe { ptr::write(out_actual_len, bytes.len()) };
if bytes.len() > out_capacity {
return GMCRYPTO_ERR;
}
if bytes.is_empty() {
return GMCRYPTO_OK;
}
let dst = unsafe { try_slice_mut(out, bytes.len()) };
match dst {
Some(d) => {
d.copy_from_slice(bytes);
GMCRYPTO_OK
}
None => GMCRYPTO_ERR,
}
}
#[inline]
fn ffi_guard<F: FnOnce() -> c_int + std::panic::UnwindSafe>(f: F) -> c_int {
std::panic::catch_unwind(f).unwrap_or(GMCRYPTO_ERR)
}
#[unsafe(no_mangle)]
pub extern "C" fn gmcrypto_version() -> *const c_char {
const VERSION: &core::ffi::CStr = match core::ffi::CStr::from_bytes_with_nul(
concat!(env!("CARGO_PKG_VERSION"), "\0").as_bytes(),
) {
Ok(s) => s,
Err(_) => unreachable!(),
};
VERSION.as_ptr()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm3_hash(
msg: *const u8,
msg_len: usize,
out_digest: *mut u8,
) -> c_int {
ffi_guard(|| {
let input = match unsafe { try_slice(msg, msg_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let out = match unsafe { try_slice_mut(out_digest, GMCRYPTO_SM3_DIGEST_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let digest = sm3_hash(input);
out.copy_from_slice(&digest);
GMCRYPTO_OK
})
}
#[unsafe(no_mangle)]
pub extern "C" fn gmcrypto_sm3_new() -> *mut gmcrypto_sm3_t {
let boxed = Box::new(gmcrypto_sm3_t {
inner: InnerSm3::new(),
});
Box::into_raw(boxed)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm3_update(
hasher: *mut gmcrypto_sm3_t,
data: *const u8,
data_len: usize,
) -> c_int {
ffi_guard(|| {
if hasher.is_null() {
return GMCRYPTO_ERR;
}
let input = match unsafe { try_slice(data, data_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let h = unsafe { &mut *hasher };
h.inner.update(input);
GMCRYPTO_OK
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm3_finalize(
hasher: *mut gmcrypto_sm3_t,
out_digest: *mut u8,
) -> c_int {
ffi_guard(|| {
if hasher.is_null() {
return GMCRYPTO_ERR;
}
let out = match unsafe { try_slice_mut(out_digest, GMCRYPTO_SM3_DIGEST_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let boxed = unsafe { Box::from_raw(hasher) };
let digest = boxed.inner.finalize();
out.copy_from_slice(&digest);
GMCRYPTO_OK
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm3_free(hasher: *mut gmcrypto_sm3_t) {
if hasher.is_null() {
return;
}
drop(unsafe { Box::from_raw(hasher) });
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_hmac_sm3(
key: *const u8,
key_len: usize,
msg: *const u8,
msg_len: usize,
out_tag: *mut u8,
) -> c_int {
ffi_guard(|| {
let k = match unsafe { try_slice(key, key_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let m = match unsafe { try_slice(msg, msg_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let out = match unsafe { try_slice_mut(out_tag, GMCRYPTO_SM3_DIGEST_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let tag = hmac_sm3(k, m);
out.copy_from_slice(&tag);
GMCRYPTO_OK
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_hmac_sm3_new(
key: *const u8,
key_len: usize,
) -> *mut gmcrypto_hmac_sm3_t {
let result = std::panic::catch_unwind(|| {
let k = unsafe { try_slice(key, key_len) }?;
Some(Box::into_raw(Box::new(gmcrypto_hmac_sm3_t {
inner: InnerHmacSm3::new(k),
})))
});
match result {
Ok(Some(ptr)) => ptr,
_ => ptr::null_mut(),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_hmac_sm3_update(
mac: *mut gmcrypto_hmac_sm3_t,
data: *const u8,
data_len: usize,
) -> c_int {
ffi_guard(|| {
if mac.is_null() {
return GMCRYPTO_ERR;
}
let input = match unsafe { try_slice(data, data_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let m = unsafe { &mut *mac };
m.inner.update(input);
GMCRYPTO_OK
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_hmac_sm3_finalize(
mac: *mut gmcrypto_hmac_sm3_t,
out_tag: *mut u8,
) -> c_int {
ffi_guard(|| {
if mac.is_null() {
return GMCRYPTO_ERR;
}
let out = match unsafe { try_slice_mut(out_tag, GMCRYPTO_SM3_DIGEST_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let boxed = unsafe { Box::from_raw(mac) };
let tag = boxed.inner.finalize();
out.copy_from_slice(&tag);
GMCRYPTO_OK
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_hmac_sm3_verify(
mac: *mut gmcrypto_hmac_sm3_t,
expected_tag: *const u8,
) -> c_int {
ffi_guard(|| {
if mac.is_null() || expected_tag.is_null() {
return GMCRYPTO_ERR;
}
let expected = match unsafe { try_slice(expected_tag, GMCRYPTO_SM3_DIGEST_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let mut expected_arr = [0u8; GMCRYPTO_SM3_DIGEST_SIZE];
expected_arr.copy_from_slice(expected);
let boxed = unsafe { Box::from_raw(mac) };
if boxed.inner.verify(&expected_arr) {
GMCRYPTO_OK
} else {
GMCRYPTO_ERR
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_hmac_sm3_free(mac: *mut gmcrypto_hmac_sm3_t) {
if mac.is_null() {
return;
}
drop(unsafe { Box::from_raw(mac) });
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_pbkdf2_hmac_sm3(
pwd: *const u8,
pwd_len: usize,
salt: *const u8,
salt_len: usize,
iterations: u32,
out: *mut u8,
out_len: usize,
) -> c_int {
ffi_guard(|| {
let p = match unsafe { try_slice(pwd, pwd_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let s = match unsafe { try_slice(salt, salt_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let o = match unsafe { try_slice_mut(out, out_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
match pbkdf2_hmac_sm3(p, s, iterations, o) {
Some(()) => GMCRYPTO_OK,
None => GMCRYPTO_ERR,
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_new(key: *const u8) -> *mut gmcrypto_sm4_t {
let result = std::panic::catch_unwind(|| {
let k = unsafe { try_slice(key, GMCRYPTO_SM4_KEY_SIZE) }?;
let k_arr: &[u8; GMCRYPTO_SM4_KEY_SIZE] = k.try_into().ok()?;
Some(Box::into_raw(Box::new(gmcrypto_sm4_t {
inner: Sm4Cipher::new(k_arr),
})))
});
match result {
Ok(Some(p)) => p,
_ => ptr::null_mut(),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_encrypt_block(
cipher: *const gmcrypto_sm4_t,
block: *mut u8,
) -> c_int {
ffi_guard(|| {
if cipher.is_null() {
return GMCRYPTO_ERR;
}
let b = match unsafe { try_slice_mut(block, GMCRYPTO_SM4_BLOCK_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let b_arr: &mut [u8; GMCRYPTO_SM4_BLOCK_SIZE] = match b.try_into() {
Ok(a) => a,
Err(_) => return GMCRYPTO_ERR,
};
let c = unsafe { &*cipher };
c.inner.encrypt_block(b_arr);
GMCRYPTO_OK
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_decrypt_block(
cipher: *const gmcrypto_sm4_t,
block: *mut u8,
) -> c_int {
ffi_guard(|| {
if cipher.is_null() {
return GMCRYPTO_ERR;
}
let b = match unsafe { try_slice_mut(block, GMCRYPTO_SM4_BLOCK_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let b_arr: &mut [u8; GMCRYPTO_SM4_BLOCK_SIZE] = match b.try_into() {
Ok(a) => a,
Err(_) => return GMCRYPTO_ERR,
};
let c = unsafe { &*cipher };
c.inner.decrypt_block(b_arr);
GMCRYPTO_OK
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_free(cipher: *mut gmcrypto_sm4_t) {
if cipher.is_null() {
return;
}
drop(unsafe { Box::from_raw(cipher) });
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_cbc_encrypt(
key: *const u8,
iv: *const u8,
pt: *const u8,
pt_len: usize,
out: *mut u8,
out_capacity: usize,
out_actual_len: *mut usize,
) -> c_int {
ffi_guard(|| {
let k = match unsafe { try_slice(key, GMCRYPTO_SM4_KEY_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let iv_slice = match unsafe { try_slice(iv, GMCRYPTO_SM4_BLOCK_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let p = match unsafe { try_slice(pt, pt_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let k_arr: &[u8; GMCRYPTO_SM4_KEY_SIZE] = match k.try_into() {
Ok(a) => a,
Err(_) => return GMCRYPTO_ERR,
};
let iv_arr: &[u8; GMCRYPTO_SM4_BLOCK_SIZE] = match iv_slice.try_into() {
Ok(a) => a,
Err(_) => return GMCRYPTO_ERR,
};
let ciphertext = mode_cbc::encrypt(k_arr, iv_arr, p);
unsafe { write_output(&ciphertext, out, out_capacity, out_actual_len) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_cbc_decrypt(
key: *const u8,
iv: *const u8,
ct: *const u8,
ct_len: usize,
out: *mut u8,
out_capacity: usize,
out_actual_len: *mut usize,
) -> c_int {
ffi_guard(|| {
let k = match unsafe { try_slice(key, GMCRYPTO_SM4_KEY_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let iv_slice = match unsafe { try_slice(iv, GMCRYPTO_SM4_BLOCK_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let c = match unsafe { try_slice(ct, ct_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let k_arr: &[u8; GMCRYPTO_SM4_KEY_SIZE] = match k.try_into() {
Ok(a) => a,
Err(_) => return GMCRYPTO_ERR,
};
let iv_arr: &[u8; GMCRYPTO_SM4_BLOCK_SIZE] = match iv_slice.try_into() {
Ok(a) => a,
Err(_) => return GMCRYPTO_ERR,
};
match mode_cbc::decrypt(k_arr, iv_arr, c) {
Some(plaintext) => unsafe {
write_output(&plaintext, out, out_capacity, out_actual_len)
},
None => GMCRYPTO_ERR,
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_cbc_encryptor_new(
key: *const u8,
iv: *const u8,
) -> *mut gmcrypto_sm4_cbc_encryptor_t {
let result = std::panic::catch_unwind(|| {
let k = unsafe { try_slice(key, GMCRYPTO_SM4_KEY_SIZE) }?;
let v = unsafe { try_slice(iv, GMCRYPTO_SM4_BLOCK_SIZE) }?;
let k_arr: &[u8; GMCRYPTO_SM4_KEY_SIZE] = k.try_into().ok()?;
let v_arr: &[u8; GMCRYPTO_SM4_BLOCK_SIZE] = v.try_into().ok()?;
Some(Box::into_raw(Box::new(gmcrypto_sm4_cbc_encryptor_t {
inner: InnerSm4CbcEnc::new(k_arr, v_arr),
})))
});
match result {
Ok(Some(p)) => p,
_ => ptr::null_mut(),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_cbc_encryptor_update(
enc: *mut gmcrypto_sm4_cbc_encryptor_t,
pt: *const u8,
pt_len: usize,
out: *mut u8,
out_capacity: usize,
out_actual_len: *mut usize,
) -> c_int {
ffi_guard(|| {
if enc.is_null() {
return GMCRYPTO_ERR;
}
let input = match unsafe { try_slice(pt, pt_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let e = unsafe { &mut *enc };
e.inner.update(input);
let emitted = e.inner.take_output();
unsafe { write_output(&emitted, out, out_capacity, out_actual_len) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_cbc_encryptor_finalize(
enc: *mut gmcrypto_sm4_cbc_encryptor_t,
out: *mut u8,
out_capacity: usize,
out_actual_len: *mut usize,
) -> c_int {
ffi_guard(|| {
if enc.is_null() {
return GMCRYPTO_ERR;
}
let boxed = unsafe { Box::from_raw(enc) };
let final_bytes = boxed.inner.finalize();
unsafe { write_output(&final_bytes, out, out_capacity, out_actual_len) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_cbc_encryptor_free(enc: *mut gmcrypto_sm4_cbc_encryptor_t) {
if enc.is_null() {
return;
}
drop(unsafe { Box::from_raw(enc) });
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_cbc_decryptor_new(
key: *const u8,
iv: *const u8,
) -> *mut gmcrypto_sm4_cbc_decryptor_t {
let result = std::panic::catch_unwind(|| {
let k = unsafe { try_slice(key, GMCRYPTO_SM4_KEY_SIZE) }?;
let v = unsafe { try_slice(iv, GMCRYPTO_SM4_BLOCK_SIZE) }?;
let k_arr: &[u8; GMCRYPTO_SM4_KEY_SIZE] = k.try_into().ok()?;
let v_arr: &[u8; GMCRYPTO_SM4_BLOCK_SIZE] = v.try_into().ok()?;
Some(Box::into_raw(Box::new(gmcrypto_sm4_cbc_decryptor_t {
inner: InnerSm4CbcDec::new(k_arr, v_arr),
})))
});
match result {
Ok(Some(p)) => p,
_ => ptr::null_mut(),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_cbc_decryptor_update(
dec: *mut gmcrypto_sm4_cbc_decryptor_t,
ct: *const u8,
ct_len: usize,
out: *mut u8,
out_capacity: usize,
out_actual_len: *mut usize,
) -> c_int {
ffi_guard(|| {
if dec.is_null() {
return GMCRYPTO_ERR;
}
let input = match unsafe { try_slice(ct, ct_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let d = unsafe { &mut *dec };
d.inner.update(input);
let emitted = d.inner.take_output();
unsafe { write_output(&emitted, out, out_capacity, out_actual_len) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_cbc_decryptor_finalize(
dec: *mut gmcrypto_sm4_cbc_decryptor_t,
out: *mut u8,
out_capacity: usize,
out_actual_len: *mut usize,
) -> c_int {
ffi_guard(|| {
if dec.is_null() {
return GMCRYPTO_ERR;
}
let boxed = unsafe { Box::from_raw(dec) };
if let Some(final_bytes) = boxed.inner.finalize() {
unsafe { write_output(&final_bytes, out, out_capacity, out_actual_len) }
} else {
if !out_actual_len.is_null() {
unsafe { ptr::write(out_actual_len, 0) };
}
GMCRYPTO_ERR
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_cbc_decryptor_free(dec: *mut gmcrypto_sm4_cbc_decryptor_t) {
if dec.is_null() {
return;
}
drop(unsafe { Box::from_raw(dec) });
}
#[allow(clippy::too_many_arguments)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_gcm_encrypt(
key: *const u8,
nonce: *const u8,
nonce_len: usize,
aad: *const u8,
aad_len: usize,
pt: *const u8,
pt_len: usize,
ct_out: *mut u8,
ct_capacity: usize,
ct_actual_len: *mut usize,
tag_out: *mut u8,
) -> c_int {
ffi_guard(|| {
let k = match unsafe { try_slice(key, GMCRYPTO_SM4_KEY_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let n = match unsafe { try_slice(nonce, nonce_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let a = match unsafe { try_slice(aad, aad_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let p = match unsafe { try_slice(pt, pt_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let k_arr: &[u8; GMCRYPTO_SM4_KEY_SIZE] = match k.try_into() {
Ok(a) => a,
Err(_) => return GMCRYPTO_ERR,
};
let tag_dst = match unsafe { try_slice_mut(tag_out, GMCRYPTO_SM4_BLOCK_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let (ciphertext, tag) = match mode_gcm::encrypt(k_arr, n, a, p) {
Some(out) => out,
None => return GMCRYPTO_ERR,
};
let rc = unsafe { write_output(&ciphertext, ct_out, ct_capacity, ct_actual_len) };
if rc != GMCRYPTO_OK {
return rc;
}
tag_dst.copy_from_slice(&tag);
GMCRYPTO_OK
})
}
#[allow(clippy::too_many_arguments)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_gcm_decrypt(
key: *const u8,
nonce: *const u8,
nonce_len: usize,
aad: *const u8,
aad_len: usize,
ct: *const u8,
ct_len: usize,
tag: *const u8,
pt_out: *mut u8,
pt_capacity: usize,
pt_actual_len: *mut usize,
) -> c_int {
ffi_guard(|| {
let k = match unsafe { try_slice(key, GMCRYPTO_SM4_KEY_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let n = match unsafe { try_slice(nonce, nonce_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let a = match unsafe { try_slice(aad, aad_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let c = match unsafe { try_slice(ct, ct_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let t = match unsafe { try_slice(tag, GMCRYPTO_SM4_BLOCK_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let k_arr: &[u8; GMCRYPTO_SM4_KEY_SIZE] = match k.try_into() {
Ok(a) => a,
Err(_) => return GMCRYPTO_ERR,
};
let t_arr: &[u8; GMCRYPTO_SM4_BLOCK_SIZE] = match t.try_into() {
Ok(a) => a,
Err(_) => return GMCRYPTO_ERR,
};
match mode_gcm::decrypt(k_arr, n, a, c, t_arr) {
Some(plaintext) => unsafe {
write_output(&plaintext, pt_out, pt_capacity, pt_actual_len)
},
None => GMCRYPTO_ERR,
}
})
}
#[allow(clippy::too_many_arguments)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_gcm_encrypt_with_tag_len(
key: *const u8,
nonce: *const u8,
nonce_len: usize,
aad: *const u8,
aad_len: usize,
pt: *const u8,
pt_len: usize,
tag_len: usize,
ct_out: *mut u8,
ct_capacity: usize,
ct_actual_len: *mut usize,
tag_out: *mut u8,
) -> c_int {
ffi_guard(|| {
let tl = match GcmTagLen::new(tag_len) {
Some(t) => t,
None => return GMCRYPTO_ERR,
};
let k = match unsafe { try_slice(key, GMCRYPTO_SM4_KEY_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let n = match unsafe { try_slice(nonce, nonce_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let a = match unsafe { try_slice(aad, aad_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let p = match unsafe { try_slice(pt, pt_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let k_arr: &[u8; GMCRYPTO_SM4_KEY_SIZE] = match k.try_into() {
Ok(a) => a,
Err(_) => return GMCRYPTO_ERR,
};
let tag_dst = match unsafe { try_slice_mut(tag_out, tag_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let (ciphertext, tag) = match mode_gcm::encrypt_with_tag_len(k_arr, n, a, p, tl) {
Some(out) => out,
None => return GMCRYPTO_ERR,
};
let rc = unsafe { write_output(&ciphertext, ct_out, ct_capacity, ct_actual_len) };
if rc != GMCRYPTO_OK {
return rc;
}
tag_dst.copy_from_slice(&tag);
GMCRYPTO_OK
})
}
#[allow(clippy::too_many_arguments)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_gcm_decrypt_with_tag_len(
key: *const u8,
nonce: *const u8,
nonce_len: usize,
aad: *const u8,
aad_len: usize,
ct: *const u8,
ct_len: usize,
tag: *const u8,
tag_len: usize,
pt_out: *mut u8,
pt_capacity: usize,
pt_actual_len: *mut usize,
) -> c_int {
ffi_guard(|| {
let k = match unsafe { try_slice(key, GMCRYPTO_SM4_KEY_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let n = match unsafe { try_slice(nonce, nonce_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let a = match unsafe { try_slice(aad, aad_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let c = match unsafe { try_slice(ct, ct_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let t = match unsafe { try_slice(tag, tag_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let k_arr: &[u8; GMCRYPTO_SM4_KEY_SIZE] = match k.try_into() {
Ok(a) => a,
Err(_) => return GMCRYPTO_ERR,
};
match mode_gcm::decrypt_with_tag_len(k_arr, n, a, c, t) {
Some(plaintext) => unsafe {
write_output(&plaintext, pt_out, pt_capacity, pt_actual_len)
},
None => GMCRYPTO_ERR,
}
})
}
#[allow(clippy::too_many_arguments)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_ccm_encrypt(
key: *const u8,
nonce: *const u8,
nonce_len: usize,
aad: *const u8,
aad_len: usize,
pt: *const u8,
pt_len: usize,
tag_len: usize,
out: *mut u8,
out_capacity: usize,
out_actual_len: *mut usize,
) -> c_int {
ffi_guard(|| {
let k = match unsafe { try_slice(key, GMCRYPTO_SM4_KEY_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let n = match unsafe { try_slice(nonce, nonce_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let a = match unsafe { try_slice(aad, aad_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let p = match unsafe { try_slice(pt, pt_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let k_arr: &[u8; GMCRYPTO_SM4_KEY_SIZE] = match k.try_into() {
Ok(a) => a,
Err(_) => return GMCRYPTO_ERR,
};
match mode_ccm::encrypt(k_arr, n, a, p, tag_len) {
Some(ct_with_tag) => unsafe {
write_output(&ct_with_tag, out, out_capacity, out_actual_len)
},
None => GMCRYPTO_ERR,
}
})
}
#[allow(clippy::too_many_arguments)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_ccm_decrypt(
key: *const u8,
nonce: *const u8,
nonce_len: usize,
aad: *const u8,
aad_len: usize,
ct: *const u8,
ct_len: usize,
tag_len: usize,
pt_out: *mut u8,
pt_capacity: usize,
pt_actual_len: *mut usize,
) -> c_int {
ffi_guard(|| {
let k = match unsafe { try_slice(key, GMCRYPTO_SM4_KEY_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let n = match unsafe { try_slice(nonce, nonce_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let a = match unsafe { try_slice(aad, aad_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let c = match unsafe { try_slice(ct, ct_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let k_arr: &[u8; GMCRYPTO_SM4_KEY_SIZE] = match k.try_into() {
Ok(a) => a,
Err(_) => return GMCRYPTO_ERR,
};
match mode_ccm::decrypt(k_arr, n, a, c, tag_len) {
Some(plaintext) => unsafe {
write_output(&plaintext, pt_out, pt_capacity, pt_actual_len)
},
None => GMCRYPTO_ERR,
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_xts_encrypt(
key: *const u8,
tweak: *const u8,
data: *const u8,
data_len: usize,
out: *mut u8,
out_capacity: usize,
out_actual_len: *mut usize,
) -> c_int {
ffi_guard(|| {
let k = match unsafe { try_slice(key, GMCRYPTO_SM4_XTS_KEY_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let tw = match unsafe { try_slice(tweak, GMCRYPTO_SM4_BLOCK_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let d = match unsafe { try_slice(data, data_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let k_arr: &[u8; GMCRYPTO_SM4_XTS_KEY_SIZE] = match k.try_into() {
Ok(a) => a,
Err(_) => return GMCRYPTO_ERR,
};
let tw_arr: &[u8; GMCRYPTO_SM4_BLOCK_SIZE] = match tw.try_into() {
Ok(a) => a,
Err(_) => return GMCRYPTO_ERR,
};
match mode_xts::encrypt(k_arr, tw_arr, d) {
Some(ct) => unsafe { write_output(&ct, out, out_capacity, out_actual_len) },
None => GMCRYPTO_ERR,
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_xts_decrypt(
key: *const u8,
tweak: *const u8,
data: *const u8,
data_len: usize,
out: *mut u8,
out_capacity: usize,
out_actual_len: *mut usize,
) -> c_int {
ffi_guard(|| {
let k = match unsafe { try_slice(key, GMCRYPTO_SM4_XTS_KEY_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let tw = match unsafe { try_slice(tweak, GMCRYPTO_SM4_BLOCK_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let d = match unsafe { try_slice(data, data_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let k_arr: &[u8; GMCRYPTO_SM4_XTS_KEY_SIZE] = match k.try_into() {
Ok(a) => a,
Err(_) => return GMCRYPTO_ERR,
};
let tw_arr: &[u8; GMCRYPTO_SM4_BLOCK_SIZE] = match tw.try_into() {
Ok(a) => a,
Err(_) => return GMCRYPTO_ERR,
};
match mode_xts::decrypt(k_arr, tw_arr, d) {
Some(pt) => unsafe { write_output(&pt, out, out_capacity, out_actual_len) },
None => GMCRYPTO_ERR,
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_xts_encrypt_sectors(
key: *const u8,
sector_size: usize,
start_sector: u64,
buf: *mut u8,
buf_len: usize,
) -> c_int {
ffi_guard(|| {
let k = match unsafe { try_slice(key, GMCRYPTO_SM4_XTS_KEY_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let key_owned: [u8; GMCRYPTO_SM4_XTS_KEY_SIZE] = match k.try_into() {
Ok(a) => a,
Err(_) => return GMCRYPTO_ERR,
};
let b = match unsafe { try_slice_mut(buf, buf_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
match mode_xts::encrypt_sectors(&key_owned, sector_size, u128::from(start_sector), b) {
Some(()) => GMCRYPTO_OK,
None => GMCRYPTO_ERR,
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_xts_decrypt_sectors(
key: *const u8,
sector_size: usize,
start_sector: u64,
buf: *mut u8,
buf_len: usize,
) -> c_int {
ffi_guard(|| {
let k = match unsafe { try_slice(key, GMCRYPTO_SM4_XTS_KEY_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let key_owned: [u8; GMCRYPTO_SM4_XTS_KEY_SIZE] = match k.try_into() {
Ok(a) => a,
Err(_) => return GMCRYPTO_ERR,
};
let b = match unsafe { try_slice_mut(buf, buf_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
match mode_xts::decrypt_sectors(&key_owned, sector_size, u128::from(start_sector), b) {
Some(()) => GMCRYPTO_OK,
None => GMCRYPTO_ERR,
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_gcm_encryptor_new(
key: *const u8,
nonce: *const u8,
nonce_len: usize,
aad: *const u8,
aad_len: usize,
) -> *mut gmcrypto_sm4_gcm_encryptor_t {
let result = std::panic::catch_unwind(|| {
let k = unsafe { try_slice(key, GMCRYPTO_SM4_KEY_SIZE) }?;
let n = unsafe { try_slice(nonce, nonce_len) }?;
let a = unsafe { try_slice(aad, aad_len) }?;
let k_arr: &[u8; GMCRYPTO_SM4_KEY_SIZE] = k.try_into().ok()?;
Some(Box::into_raw(Box::new(gmcrypto_sm4_gcm_encryptor_t {
inner: InnerSm4GcmEnc::new(k_arr, n, a),
})))
});
match result {
Ok(Some(p)) => p,
_ => ptr::null_mut(),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_gcm_encryptor_update(
enc: *mut gmcrypto_sm4_gcm_encryptor_t,
pt: *const u8,
pt_len: usize,
out: *mut u8,
out_capacity: usize,
out_actual_len: *mut usize,
) -> c_int {
ffi_guard(|| {
if enc.is_null() {
return GMCRYPTO_ERR;
}
let input = match unsafe { try_slice(pt, pt_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let e = unsafe { &mut *enc };
match e.inner.update(input) {
Some(ct) => unsafe { write_output(&ct, out, out_capacity, out_actual_len) },
None => GMCRYPTO_ERR, }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_gcm_encryptor_finalize(
enc: *mut gmcrypto_sm4_gcm_encryptor_t,
tag_out: *mut u8,
) -> c_int {
ffi_guard(|| {
if enc.is_null() {
return GMCRYPTO_ERR;
}
let boxed = unsafe { Box::from_raw(enc) };
let tag = boxed.inner.finalize();
let tag_dst = match unsafe { try_slice_mut(tag_out, GMCRYPTO_SM4_BLOCK_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
tag_dst.copy_from_slice(&tag);
GMCRYPTO_OK
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_gcm_encryptor_finalize_with_tag_len(
enc: *mut gmcrypto_sm4_gcm_encryptor_t,
tag_len: usize,
out: *mut u8,
out_capacity: usize,
out_actual_len: *mut usize,
) -> c_int {
ffi_guard(|| {
if enc.is_null() {
return GMCRYPTO_ERR;
}
let boxed = unsafe { Box::from_raw(enc) };
let tl = match GcmTagLen::new(tag_len) {
Some(t) => t,
None => return GMCRYPTO_ERR, };
let tag = boxed.inner.finalize_with_tag_len(tl);
unsafe { write_output(&tag, out, out_capacity, out_actual_len) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_gcm_encryptor_free(enc: *mut gmcrypto_sm4_gcm_encryptor_t) {
if enc.is_null() {
return;
}
drop(unsafe { Box::from_raw(enc) });
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_gcm_decryptor_new(
key: *const u8,
nonce: *const u8,
nonce_len: usize,
aad: *const u8,
aad_len: usize,
) -> *mut gmcrypto_sm4_gcm_decryptor_t {
let result = std::panic::catch_unwind(|| {
let k = unsafe { try_slice(key, GMCRYPTO_SM4_KEY_SIZE) }?;
let n = unsafe { try_slice(nonce, nonce_len) }?;
let a = unsafe { try_slice(aad, aad_len) }?;
let k_arr: &[u8; GMCRYPTO_SM4_KEY_SIZE] = k.try_into().ok()?;
Some(Box::into_raw(Box::new(gmcrypto_sm4_gcm_decryptor_t {
inner: InnerSm4GcmDec::new(k_arr, n, a),
})))
});
match result {
Ok(Some(p)) => p,
_ => ptr::null_mut(),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_gcm_decryptor_update(
dec: *mut gmcrypto_sm4_gcm_decryptor_t,
ct: *const u8,
ct_len: usize,
) -> c_int {
ffi_guard(|| {
if dec.is_null() {
return GMCRYPTO_ERR;
}
let input = match unsafe { try_slice(ct, ct_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let d = unsafe { &mut *dec };
d.inner.update(input);
GMCRYPTO_OK
})
}
#[allow(clippy::too_many_arguments)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_gcm_decryptor_finalize_verify(
dec: *mut gmcrypto_sm4_gcm_decryptor_t,
tag: *const u8,
tag_len: usize,
out: *mut u8,
out_capacity: usize,
out_actual_len: *mut usize,
) -> c_int {
ffi_guard(|| {
if dec.is_null() {
return GMCRYPTO_ERR;
}
if !out_actual_len.is_null() {
unsafe { ptr::write(out_actual_len, 0) };
}
let boxed = unsafe { Box::from_raw(dec) };
let t = match unsafe { try_slice(tag, tag_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR, };
if let Some(pt) = boxed.inner.finalize_verify(t) {
unsafe { write_output(&pt, out, out_capacity, out_actual_len) }
} else {
GMCRYPTO_ERR }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm4_gcm_decryptor_free(dec: *mut gmcrypto_sm4_gcm_decryptor_t) {
if dec.is_null() {
return;
}
drop(unsafe { Box::from_raw(dec) });
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm2_privkey_new(d_be: *const u8) -> *mut gmcrypto_sm2_privkey_t {
let result = std::panic::catch_unwind(|| {
let bytes = unsafe { try_slice(d_be, GMCRYPTO_SM2_SCALAR_SIZE) }?;
let arr: &[u8; GMCRYPTO_SM2_SCALAR_SIZE] = bytes.try_into().ok()?;
let key_opt: Option<Sm2PrivateKey> = Sm2PrivateKey::from_bytes_be(arr).into_option();
key_opt.map(|inner| Box::into_raw(Box::new(gmcrypto_sm2_privkey_t { inner })))
});
match result {
Ok(Some(p)) => p,
_ => ptr::null_mut(),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm2_pubkey_new(
sec1_uncompressed: *const u8,
) -> *mut gmcrypto_sm2_pubkey_t {
let result = std::panic::catch_unwind(|| {
let bytes = unsafe { try_slice(sec1_uncompressed, GMCRYPTO_SM2_SEC1_UNCOMPRESSED_SIZE) }?;
let key = Sm2PublicKey::from_sec1_bytes(bytes)?;
Some(Box::into_raw(Box::new(gmcrypto_sm2_pubkey_t {
inner: key,
})))
});
match result {
Ok(Some(p)) => p,
_ => ptr::null_mut(),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm2_privkey_to_sec1_be(
key: *const gmcrypto_sm2_privkey_t,
out: *mut u8,
) -> c_int {
ffi_guard(|| {
if key.is_null() {
return GMCRYPTO_ERR;
}
let o = match unsafe { try_slice_mut(out, GMCRYPTO_SM2_SCALAR_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let k = unsafe { &*key };
let bytes = k.inner.to_bytes_be();
o.copy_from_slice(&bytes);
GMCRYPTO_OK
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm2_pubkey_to_sec1_uncompressed(
key: *const gmcrypto_sm2_pubkey_t,
out: *mut u8,
) -> c_int {
ffi_guard(|| {
if key.is_null() {
return GMCRYPTO_ERR;
}
let o = match unsafe { try_slice_mut(out, GMCRYPTO_SM2_SEC1_UNCOMPRESSED_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let k = unsafe { &*key };
let bytes = k.inner.to_sec1_uncompressed();
o.copy_from_slice(&bytes);
GMCRYPTO_OK
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm2_privkey_free(key: *mut gmcrypto_sm2_privkey_t) {
if key.is_null() {
return;
}
drop(unsafe { Box::from_raw(key) });
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm2_pubkey_free(key: *mut gmcrypto_sm2_pubkey_t) {
if key.is_null() {
return;
}
drop(unsafe { Box::from_raw(key) });
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm2_privkey_to_pkcs8(
key: *const gmcrypto_sm2_privkey_t,
password: *const u8,
pwd_len: usize,
pbkdf2_iters: u32,
out_pem: *mut u8,
out_capacity: usize,
out_actual_len: *mut usize,
) -> c_int {
ffi_guard(|| {
if key.is_null() {
return GMCRYPTO_ERR;
}
let pwd = match unsafe { try_slice(password, pwd_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let k = unsafe { &*key };
let mut salt = [0u8; 16];
let mut iv = [0u8; 16];
if getrandom::SysRng.try_fill_bytes(&mut salt).is_err() {
return GMCRYPTO_ERR;
}
if getrandom::SysRng.try_fill_bytes(&mut iv).is_err() {
return GMCRYPTO_ERR;
}
let der = match pkcs8::encrypt(&k.inner, pwd, &salt, pbkdf2_iters, &iv) {
Ok(d) => d,
Err(_) => return GMCRYPTO_ERR,
};
let pem_blob = pem::encode("ENCRYPTED PRIVATE KEY", &der);
unsafe { write_output(pem_blob.as_bytes(), out_pem, out_capacity, out_actual_len) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm2_privkey_from_pkcs8(
pem: *const u8,
pem_len: usize,
password: *const u8,
pwd_len: usize,
out_key: *mut *mut gmcrypto_sm2_privkey_t,
) -> c_int {
ffi_guard(|| {
if out_key.is_null() {
return GMCRYPTO_ERR;
}
let pem_bytes = match unsafe { try_slice(pem, pem_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let pwd = match unsafe { try_slice(password, pwd_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let pem_str = match core::str::from_utf8(pem_bytes) {
Ok(s) => s,
Err(_) => return GMCRYPTO_ERR,
};
let der = match pem::decode(pem_str, "ENCRYPTED PRIVATE KEY") {
Ok(d) => d,
Err(_) => return GMCRYPTO_ERR,
};
let key = match pkcs8::decrypt(&der, pwd) {
Ok(k) => k,
Err(_) => return GMCRYPTO_ERR,
};
let boxed = Box::into_raw(Box::new(gmcrypto_sm2_privkey_t { inner: key }));
unsafe { ptr::write(out_key, boxed) };
GMCRYPTO_OK
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm2_sign(
key: *const gmcrypto_sm2_privkey_t,
signer_id: *const u8,
signer_id_len: usize,
msg: *const u8,
msg_len: usize,
out_der_sig: *mut u8,
out_capacity: usize,
out_actual_len: *mut usize,
) -> c_int {
ffi_guard(|| {
if key.is_null() {
return GMCRYPTO_ERR;
}
let id: &[u8] = if signer_id_len == 0 {
DEFAULT_SIGNER_ID
} else {
match unsafe { try_slice(signer_id, signer_id_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
}
};
let m = match unsafe { try_slice(msg, msg_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let k = unsafe { &*key };
let mut rng = getrandom::SysRng;
let sig = match sign_with_id(&k.inner, id, m, &mut rng) {
Ok(s) => s,
Err(_) => return GMCRYPTO_ERR,
};
unsafe { write_output(&sig, out_der_sig, out_capacity, out_actual_len) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm2_verify(
key: *const gmcrypto_sm2_pubkey_t,
signer_id: *const u8,
signer_id_len: usize,
msg: *const u8,
msg_len: usize,
der_sig: *const u8,
der_sig_len: usize,
) -> c_int {
ffi_guard(|| {
if key.is_null() {
return GMCRYPTO_ERR;
}
let id: &[u8] = if signer_id_len == 0 {
DEFAULT_SIGNER_ID
} else {
match unsafe { try_slice(signer_id, signer_id_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
}
};
let m = match unsafe { try_slice(msg, msg_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let sig = match unsafe { try_slice(der_sig, der_sig_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let k = unsafe { &*key };
if verify_with_id(&k.inner, id, m, sig) {
GMCRYPTO_OK
} else {
GMCRYPTO_ERR
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm2_encrypt(
key: *const gmcrypto_sm2_pubkey_t,
pt: *const u8,
pt_len: usize,
out_der_ct: *mut u8,
out_capacity: usize,
out_actual_len: *mut usize,
) -> c_int {
ffi_guard(|| {
if key.is_null() {
return GMCRYPTO_ERR;
}
let p = match unsafe { try_slice(pt, pt_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let k = unsafe { &*key };
let mut rng = getrandom::SysRng;
let ct = match sm2_encrypt(&k.inner, p, &mut rng) {
Ok(c) => c,
Err(_) => return GMCRYPTO_ERR,
};
unsafe { write_output(&ct, out_der_ct, out_capacity, out_actual_len) }
})
}
pub type gmcrypto_rng_callback =
Option<unsafe extern "C" fn(context: *mut c_void, buf: *mut u8, buf_len: usize) -> c_int>;
#[derive(Debug)]
struct CallbackRngError;
impl core::fmt::Display for CallbackRngError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("callback returned non-zero")
}
}
impl core::error::Error for CallbackRngError {}
struct CallbackRng {
callback: unsafe extern "C" fn(context: *mut c_void, buf: *mut u8, buf_len: usize) -> c_int,
context: *mut c_void,
}
impl rand_core::TryRng for CallbackRng {
type Error = CallbackRngError;
fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Self::Error> {
let rc = unsafe { (self.callback)(self.context, dst.as_mut_ptr(), dst.len()) };
if rc == 0 {
Ok(())
} else {
Err(CallbackRngError)
}
}
fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
let mut buf = [0u8; 4];
self.try_fill_bytes(&mut buf)?;
Ok(u32::from_le_bytes(buf))
}
fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
let mut buf = [0u8; 8];
self.try_fill_bytes(&mut buf)?;
Ok(u64::from_le_bytes(buf))
}
}
impl rand_core::TryCryptoRng for CallbackRng {}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm2_decrypt(
key: *const gmcrypto_sm2_privkey_t,
der_ct: *const u8,
der_ct_len: usize,
out_pt: *mut u8,
out_capacity: usize,
out_actual_len: *mut usize,
) -> c_int {
ffi_guard(|| {
if key.is_null() {
return GMCRYPTO_ERR;
}
let c = match unsafe { try_slice(der_ct, der_ct_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let k = unsafe { &*key };
match sm2_decrypt(&k.inner, c) {
Ok(pt) => unsafe { write_output(&pt, out_pt, out_capacity, out_actual_len) },
Err(_) => GMCRYPTO_ERR,
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm2_encrypt_c1c3c2(
key: *const gmcrypto_sm2_pubkey_t,
pt: *const u8,
pt_len: usize,
out_raw_ct: *mut u8,
out_capacity: usize,
out_actual_len: *mut usize,
) -> c_int {
ffi_guard(|| {
if key.is_null() {
return GMCRYPTO_ERR;
}
let p = match unsafe { try_slice(pt, pt_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let k = unsafe { &*key };
let mut rng = getrandom::SysRng;
let der_bytes = match sm2_encrypt(&k.inner, p, &mut rng) {
Ok(b) => b,
Err(_) => return GMCRYPTO_ERR,
};
let parsed = match ciphertext_der_decode(&der_bytes) {
Some(ct) => ct,
None => return GMCRYPTO_ERR,
};
let raw_bytes = encode_c1c3c2(&parsed);
unsafe { write_output(&raw_bytes, out_raw_ct, out_capacity, out_actual_len) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm2_decrypt_c1c3c2(
key: *const gmcrypto_sm2_privkey_t,
raw_ct: *const u8,
raw_ct_len: usize,
out_pt: *mut u8,
out_capacity: usize,
out_actual_len: *mut usize,
) -> c_int {
ffi_guard(|| {
if key.is_null() {
return GMCRYPTO_ERR;
}
let c = match unsafe { try_slice(raw_ct, raw_ct_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let parsed = match decode_c1c3c2(c) {
Some(ct) => ct,
None => return GMCRYPTO_ERR,
};
let der_bytes = ciphertext_der_encode(&parsed);
let k = unsafe { &*key };
match sm2_decrypt(&k.inner, &der_bytes) {
Ok(pt) => unsafe { write_output(&pt, out_pt, out_capacity, out_actual_len) },
Err(_) => GMCRYPTO_ERR,
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm2_decrypt_c1c2c3_legacy(
key: *const gmcrypto_sm2_privkey_t,
raw_ct: *const u8,
raw_ct_len: usize,
out_pt: *mut u8,
out_capacity: usize,
out_actual_len: *mut usize,
) -> c_int {
ffi_guard(|| {
if key.is_null() {
return GMCRYPTO_ERR;
}
let c = match unsafe { try_slice(raw_ct, raw_ct_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let parsed = match decode_c1c2c3_legacy(c) {
Some(ct) => ct,
None => return GMCRYPTO_ERR,
};
let der_bytes = ciphertext_der_encode(&parsed);
let k = unsafe { &*key };
match sm2_decrypt(&k.inner, &der_bytes) {
Ok(pt) => unsafe { write_output(&pt, out_pt, out_capacity, out_actual_len) },
Err(_) => GMCRYPTO_ERR,
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm2_sign_with_rng(
key: *const gmcrypto_sm2_privkey_t,
signer_id: *const u8,
signer_id_len: usize,
msg: *const u8,
msg_len: usize,
rng_callback: gmcrypto_rng_callback,
rng_context: *mut c_void,
out_der_sig: *mut u8,
out_capacity: usize,
out_actual_len: *mut usize,
) -> c_int {
ffi_guard(|| {
if key.is_null() {
return GMCRYPTO_ERR;
}
let callback = match rng_callback {
Some(cb) => cb,
None => return GMCRYPTO_ERR,
};
let id: &[u8] = if signer_id_len == 0 {
DEFAULT_SIGNER_ID
} else {
match unsafe { try_slice(signer_id, signer_id_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
}
};
let m = match unsafe { try_slice(msg, msg_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let k = unsafe { &*key };
let mut rng = CallbackRng {
callback,
context: rng_context,
};
let sig = match sign_with_id(&k.inner, id, m, &mut rng) {
Ok(s) => s,
Err(_) => return GMCRYPTO_ERR,
};
unsafe { write_output(&sig, out_der_sig, out_capacity, out_actual_len) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm2_encrypt_with_rng(
key: *const gmcrypto_sm2_pubkey_t,
pt: *const u8,
pt_len: usize,
rng_callback: gmcrypto_rng_callback,
rng_context: *mut c_void,
out_der_ct: *mut u8,
out_capacity: usize,
out_actual_len: *mut usize,
) -> c_int {
ffi_guard(|| {
if key.is_null() {
return GMCRYPTO_ERR;
}
let callback = match rng_callback {
Some(cb) => cb,
None => return GMCRYPTO_ERR,
};
let p = match unsafe { try_slice(pt, pt_len) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let k = unsafe { &*key };
let mut rng = CallbackRng {
callback,
context: rng_context,
};
let ct = match sm2_encrypt(&k.inner, p, &mut rng) {
Ok(c) => c,
Err(_) => return GMCRYPTO_ERR,
};
unsafe { write_output(&ct, out_der_ct, out_capacity, out_actual_len) }
})
}
unsafe fn kx_id<'a>(ptr: *const u8, len: usize) -> Option<&'a [u8]> {
if len == 0 {
Some(DEFAULT_SIGNER_ID)
} else {
unsafe { try_slice(ptr, len) }
}
}
fn kx_to_array<const N: usize>(s: &[u8]) -> [u8; N] {
let mut a = [0u8; N];
a.copy_from_slice(s);
a
}
fn kx_initiator_build<R: rand_core::TryCryptoRng>(
d: &Sm2PrivateKey,
p_peer: &Sm2PublicKey,
id_a: &[u8],
id_b: &[u8],
klen: usize,
out_r_a: &mut [u8],
rng: &mut R,
) -> Option<*mut gmcrypto_sm2_kx_initiator_t> {
let initiator = InnerSm2KxInitiator::new(d, p_peer, id_a, id_b, klen).ok()?;
let (r_a, waiting) = initiator.produce_ephemeral(rng).ok()?;
out_r_a.copy_from_slice(&r_a.to_bytes());
Some(Box::into_raw(Box::new(gmcrypto_sm2_kx_initiator_t {
inner: waiting,
klen,
})))
}
fn kx_respond<R: rand_core::TryCryptoRng>(
handle: &mut gmcrypto_sm2_kx_responder_t,
r_a: &[u8; GMCRYPTO_SM2_SEC1_UNCOMPRESSED_SIZE],
rng: &mut R,
) -> Option<(
[u8; GMCRYPTO_SM2_SEC1_UNCOMPRESSED_SIZE],
[u8; GMCRYPTO_SM2_KX_CONFIRM_SIZE],
)> {
if !matches!(handle.state, InnerKxResponderState::Fresh(_)) {
return None;
}
let prev = core::mem::replace(&mut handle.state, InnerKxResponderState::Spent);
let InnerKxResponderState::Fresh(responder) = prev else {
return None;
};
let (r_b, s_b, waiting) = (*responder)
.respond(&Sm2KxEphemeralPoint::from_bytes(r_a), rng)
.ok()?;
handle.state = InnerKxResponderState::Waiting(Box::new(waiting));
Some((r_b.to_bytes(), s_b.to_bytes()))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm2_kx_initiator_new(
local_privkey: *const gmcrypto_sm2_privkey_t,
peer_pubkey: *const gmcrypto_sm2_pubkey_t,
id_a: *const u8,
id_a_len: usize,
id_b: *const u8,
id_b_len: usize,
klen: usize,
out_r_a: *mut u8,
) -> *mut gmcrypto_sm2_kx_initiator_t {
let result = std::panic::catch_unwind(|| {
if local_privkey.is_null() || peer_pubkey.is_null() {
return None;
}
let ida = unsafe { kx_id(id_a, id_a_len) }?;
let idb = unsafe { kx_id(id_b, id_b_len) }?;
let out = unsafe { try_slice_mut(out_r_a, GMCRYPTO_SM2_SEC1_UNCOMPRESSED_SIZE) }?;
let d = unsafe { &*local_privkey };
let p = unsafe { &*peer_pubkey };
kx_initiator_build(
&d.inner,
&p.inner,
ida,
idb,
klen,
out,
&mut getrandom::SysRng,
)
});
match result {
Ok(Some(p)) => p,
_ => ptr::null_mut(),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm2_kx_initiator_new_with_rng(
local_privkey: *const gmcrypto_sm2_privkey_t,
peer_pubkey: *const gmcrypto_sm2_pubkey_t,
id_a: *const u8,
id_a_len: usize,
id_b: *const u8,
id_b_len: usize,
klen: usize,
rng_callback: gmcrypto_rng_callback,
rng_context: *mut c_void,
out_r_a: *mut u8,
) -> *mut gmcrypto_sm2_kx_initiator_t {
let result = std::panic::catch_unwind(|| {
if local_privkey.is_null() || peer_pubkey.is_null() {
return None;
}
let callback = rng_callback?;
let ida = unsafe { kx_id(id_a, id_a_len) }?;
let idb = unsafe { kx_id(id_b, id_b_len) }?;
let out = unsafe { try_slice_mut(out_r_a, GMCRYPTO_SM2_SEC1_UNCOMPRESSED_SIZE) }?;
let d = unsafe { &*local_privkey };
let p = unsafe { &*peer_pubkey };
let mut rng = CallbackRng {
callback,
context: rng_context,
};
kx_initiator_build(&d.inner, &p.inner, ida, idb, klen, out, &mut rng)
});
match result {
Ok(Some(p)) => p,
_ => ptr::null_mut(),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm2_kx_initiator_confirm(
initiator: *mut gmcrypto_sm2_kx_initiator_t,
r_b: *const u8,
s_b: *const u8,
key_out: *mut u8,
out_s_a: *mut u8,
) -> c_int {
ffi_guard(|| {
if initiator.is_null() {
return GMCRYPTO_ERR;
}
let boxed = unsafe { Box::from_raw(initiator) };
let gmcrypto_sm2_kx_initiator_t { inner, klen } = *boxed;
let rb = match unsafe { try_slice(r_b, GMCRYPTO_SM2_SEC1_UNCOMPRESSED_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let sb = match unsafe { try_slice(s_b, GMCRYPTO_SM2_KX_CONFIRM_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let key_dst = match unsafe { try_slice_mut(key_out, klen) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let sa_dst = match unsafe { try_slice_mut(out_s_a, GMCRYPTO_SM2_KX_CONFIRM_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let r_b_point = Sm2KxEphemeralPoint::from_bytes(&kx_to_array(rb));
let s_b_tag = Sm2KxConfirm::from_bytes(&kx_to_array(sb));
match inner.confirm(&r_b_point, &s_b_tag) {
Ok((key, s_a)) => {
key_dst.copy_from_slice(key.as_bytes());
sa_dst.copy_from_slice(&s_a.to_bytes());
GMCRYPTO_OK
}
Err(_) => GMCRYPTO_ERR,
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm2_kx_initiator_free(
initiator: *mut gmcrypto_sm2_kx_initiator_t,
) {
if !initiator.is_null() {
drop(unsafe { Box::from_raw(initiator) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm2_kx_responder_new(
local_privkey: *const gmcrypto_sm2_privkey_t,
peer_pubkey: *const gmcrypto_sm2_pubkey_t,
id_a: *const u8,
id_a_len: usize,
id_b: *const u8,
id_b_len: usize,
klen: usize,
) -> *mut gmcrypto_sm2_kx_responder_t {
let result = std::panic::catch_unwind(|| {
if local_privkey.is_null() || peer_pubkey.is_null() {
return None;
}
let ida = unsafe { kx_id(id_a, id_a_len) }?;
let idb = unsafe { kx_id(id_b, id_b_len) }?;
let d = unsafe { &*local_privkey };
let p = unsafe { &*peer_pubkey };
let responder = InnerSm2KxResponder::new(&d.inner, &p.inner, ida, idb, klen).ok()?;
Some(Box::into_raw(Box::new(gmcrypto_sm2_kx_responder_t {
state: InnerKxResponderState::Fresh(Box::new(responder)),
klen,
})))
});
match result {
Ok(Some(p)) => p,
_ => ptr::null_mut(),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm2_kx_responder_respond(
responder: *mut gmcrypto_sm2_kx_responder_t,
r_a: *const u8,
out_r_b: *mut u8,
out_s_b: *mut u8,
) -> c_int {
ffi_guard(|| {
if responder.is_null() {
return GMCRYPTO_ERR;
}
let ra = match unsafe { try_slice(r_a, GMCRYPTO_SM2_SEC1_UNCOMPRESSED_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let rb_dst = match unsafe { try_slice_mut(out_r_b, GMCRYPTO_SM2_SEC1_UNCOMPRESSED_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let sb_dst = match unsafe { try_slice_mut(out_s_b, GMCRYPTO_SM2_KX_CONFIRM_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let handle = unsafe { &mut *responder };
match kx_respond(handle, &kx_to_array(ra), &mut getrandom::SysRng) {
Some((rb, sb)) => {
rb_dst.copy_from_slice(&rb);
sb_dst.copy_from_slice(&sb);
GMCRYPTO_OK
}
None => GMCRYPTO_ERR,
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm2_kx_responder_respond_with_rng(
responder: *mut gmcrypto_sm2_kx_responder_t,
r_a: *const u8,
rng_callback: gmcrypto_rng_callback,
rng_context: *mut c_void,
out_r_b: *mut u8,
out_s_b: *mut u8,
) -> c_int {
ffi_guard(|| {
if responder.is_null() {
return GMCRYPTO_ERR;
}
let callback = match rng_callback {
Some(cb) => cb,
None => return GMCRYPTO_ERR,
};
let ra = match unsafe { try_slice(r_a, GMCRYPTO_SM2_SEC1_UNCOMPRESSED_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let rb_dst = match unsafe { try_slice_mut(out_r_b, GMCRYPTO_SM2_SEC1_UNCOMPRESSED_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let sb_dst = match unsafe { try_slice_mut(out_s_b, GMCRYPTO_SM2_KX_CONFIRM_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let handle = unsafe { &mut *responder };
let mut rng = CallbackRng {
callback,
context: rng_context,
};
match kx_respond(handle, &kx_to_array(ra), &mut rng) {
Some((rb, sb)) => {
rb_dst.copy_from_slice(&rb);
sb_dst.copy_from_slice(&sb);
GMCRYPTO_OK
}
None => GMCRYPTO_ERR,
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm2_kx_responder_finish(
responder: *mut gmcrypto_sm2_kx_responder_t,
s_a: *const u8,
key_out: *mut u8,
) -> c_int {
ffi_guard(|| {
if responder.is_null() {
return GMCRYPTO_ERR;
}
let boxed = unsafe { Box::from_raw(responder) };
let gmcrypto_sm2_kx_responder_t { state, klen } = *boxed;
let sa = match unsafe { try_slice(s_a, GMCRYPTO_SM2_KX_CONFIRM_SIZE) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let key_dst = match unsafe { try_slice_mut(key_out, klen) } {
Some(s) => s,
None => return GMCRYPTO_ERR,
};
let InnerKxResponderState::Waiting(waiting) = state else {
return GMCRYPTO_ERR;
};
match (*waiting).finish(&Sm2KxConfirm::from_bytes(&kx_to_array(sa))) {
Ok(key) => {
key_dst.copy_from_slice(key.as_bytes());
GMCRYPTO_OK
}
Err(_) => GMCRYPTO_ERR,
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gmcrypto_sm2_kx_responder_free(
responder: *mut gmcrypto_sm2_kx_responder_t,
) {
if !responder.is_null() {
drop(unsafe { Box::from_raw(responder) });
}
}