diff --git a/skf-api/Cargo.toml b/skf-api/Cargo.toml index 8b3983b..3bf6787 100644 --- a/skf-api/Cargo.toml +++ b/skf-api/Cargo.toml @@ -16,5 +16,3 @@ keywords = ["GM", "cryptography", "PKI"] categories = ["hardware-support"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[dependencies] -thiserror = "1.0" \ No newline at end of file diff --git a/skf-api/src/native/crypto.rs b/skf-api/src/native/crypto.rs index 5b3c06b..3269cfa 100644 --- a/skf-api/src/native/crypto.rs +++ b/skf-api/src/native/crypto.rs @@ -39,7 +39,10 @@ //! //! see [GM/T 0016-2012](https://github.com/guanzhi/GM-Standards/blob/master/GMT%E5%AF%86%E7%A0%81%E8%A1%8C%E6%A0%87/GMT%200017-2012%20%E6%99%BA%E8%83%BD%E5%AF%86%E7%A0%81%E9%92%A5%E5%8C%99%E5%AF%86%E7%A0%81%E5%BA%94%E7%94%A8%E6%8E%A5%E5%8F%A3%E6%95%B0%E6%8D%AE%E6%A0%BC%E5%BC%8F%E8%A7%84%E8%8C%83.PDF) -use crate::native::types::{BlockCipherParam, BYTE, HANDLE, ULONG}; +use crate::native::types::{ + BlockCipherParam, ECCCipherBlob, ECCPrivateKeyBlob, ECCPublicKeyBlob, ECCSignatureBlob, + EnvelopedKeyBlob, BOOL, BYTE, HANDLE, ULONG, +}; #[allow(non_camel_case_types)] extern "C" { @@ -56,6 +59,8 @@ extern "C" { /// 明文导入会话密钥,返回密钥句柄 /// + /// **本函数仅用于测试和调试,不建议用于实际的密码服务。** + /// /// [device_handle] `[IN]`设备句柄 /// /// [key_data] `[IN]`指向会话密钥值的缓冲区 @@ -208,4 +213,280 @@ extern "C" { decrypted_data: *mut BYTE, decrypted_data_len: *mut ULONG, ) -> ULONG; + + /// 使用外部传入的ECC公钥对输入数据做加密运算并输出结果 + /// + /// [device_handle] `[IN]`设备句柄 + /// + /// [key_blob] `[IN]`ECC公钥数据结构 + /// + /// [data] `[IN]`待加密的明文数据 + /// + /// [data_len] `[IN]`待加密明文数据的长度 + /// + /// [cipher_blob] `[OUT]`密文数据 + pub fn SKF_ExtECCEncrypt( + device_handle: HANDLE, + key_blob: *const ECCPublicKeyBlob, + data: *const BYTE, + data_len: ULONG, + cipher_blob: *mut ECCCipherBlob, + ) -> ULONG; + + /// 使用外部传入的ECC私钥对输入数据做解密运算并输出结果 + /// + /// [device_handle] `[IN]`设备句柄 + /// + /// [key_blob] `[IN]`ECC私钥数据结构 + /// + /// [cipher_blob] `[IN]`待解密的密文数据 + /// + /// [data] `[OUT]`返回明文数据,如果该参数为NULL,则由data_len返回明文数据的实际长度 + /// + /// [data_len] `[IN,OUT]`调用前表示data缓冲区的长度,返回明文数据的实际长度 + pub fn SKF_ExtECCDecrypt( + device_handle: HANDLE, + key_blob: *const ECCPrivateKeyBlob, + cipher_blob: *const ECCCipherBlob, + data: *mut BYTE, + data_len: *mut ULONG, + ) -> ULONG; + + /// 使用外部传入的ECC私钥对输入数据做签名运算并输出结果。 + /// + /// **本函数仅用于测试和调试,不建议用于实际的密码服务。** + /// + /// [device_handle] `[IN]`设备句柄 + /// + /// [key_blob] `[IN]`ECC私钥数据结构 + /// + /// [data] `[IN]`待签数据的杂凑值。当使用 SM2算法时,该输入数据为待签数据经过`SM2`签名预处理的结果,预处理过程遵循`GM/T 0009` + /// + /// [data_len] `[IN]`待签名数据的长度 + /// + /// [signature] `[OUT]`签名值 + pub fn SKF_ExtECCSign( + device_handle: HANDLE, + key_blob: *const ECCPrivateKeyBlob, + data: *const BYTE, + data_len: ULONG, + signature: *mut ECCSignatureBlob, + ) -> ULONG; + + /// 外部使用传入的ECC公钥做签名验证 + /// + /// **本函数仅用于测试和调试,不建议用于实际的密码服务。** + /// + /// [device_handle] `[IN]`设备句柄 + /// + /// [key_blob] `[IN]`ECC公钥数据结构 + /// + /// [data] 待`[IN]`签数据的杂凑值。当使用 SM2算法时,该输入数据为待签数据经过`SM2`签名预处理的结果,预处理过程遵循`GM/T 0009` + /// + /// [data_len] `[IN]`待验证数据的长度 + /// + /// [signature] `[IN]`签名值 + pub fn SKF_ExtECCVerify( + device_handle: HANDLE, + key_blob: *const ECCPublicKeyBlob, + data: *const BYTE, + data_len: ULONG, + signature: *const ECCSignatureBlob, + ) -> ULONG; + /// 用ECC公钥对数据进行验签 + /// + /// [device_handle] `[IN]`设备句柄 + /// + /// [key_blob] `[IN]`ECC公钥数据结构 + /// + /// [data] `[IN]`待签数据的杂凑值。当使用 SM2算法时,该输入数据为待签数据经过`SM2`签名预处理的结果,预处理过程遵循`GM/T 0009` + /// + /// [data_len] `[IN]`数据长度 + /// + /// [signature] `[IN]`待验证的签名值 + pub fn SKF_ECCVerify( + device_handle: HANDLE, + key_blob: *const ECCPublicKeyBlob, + data: *const BYTE, + data_len: ULONG, + signature: *const ECCSignatureBlob, + ) -> ULONG; + + /// 生成会话密钥并用外部公钥加密输出 + /// + /// [ct_handle] `[IN]`容器句柄 + /// + /// [alg_id] `[IN]`会话密钥的算法标识 + /// + /// [key_blob] `[IN]`外部输入的公钥结构 + /// + /// [cipher_blob] `[OUT]`会话密钥密文 + /// + /// [session_key] `[OUT]`会话密钥句柄 + pub fn SKF_ECCExportSessionKey( + ct_handle: HANDLE, + alg_id: ULONG, + key_blob: *const ECCPublicKeyBlob, + cipher_blob: *mut ECCCipherBlob, + session_key: *mut HANDLE, + ) -> ULONG; + + /// 生成ECC签名密钥对 + /// + /// [ct_handle] `[IN]`容器句柄 + /// + /// [alg_id] `[IN]`算法标识,只支持`SGD_SM2_1`算法。 + /// + /// [key_blob] `[OUT]`返回ECC公钥数据结构 + /// + /// ## 权限要求 + /// 需要用户权限 + pub fn SKF_GenECCKeyPair( + ct_handle: HANDLE, + alg_id: ULONG, + key_blob: *mut ECCPublicKeyBlob, + ) -> ULONG; + + /// 导入ECC公私钥对 + /// + /// [ct_handle] `[IN]`容器句柄 + /// + /// [key_blob] `[IN]`ECC公私钥数据结构 + /// + /// ## 权限要求 + /// 需要用户权限 + pub fn SKF_ImportECCKeyPair(ct_handle: HANDLE, key_blob: *const EnvelopedKeyBlob) -> ULONG; + + /// 导出容器中的签名公钥或者加密公钥 + /// + /// [ct_handle] `[IN]`容器句柄 + /// + /// [sign_flag] `[IN]`TRUE表示导出签名公钥,FALSE表示导出加密公钥 + /// + /// [data] `[OUT]`指向导出公钥结构的缓冲区,指向 RSA 公钥结构(`RSAPublicKeyBlob`)或者 ECC公钥结构(`ECCPublicKeyBlob`),如果此参数为`NULL`时,由data_len返回长度。 + /// + /// [data_len] `[IN,OUT]`输入时表示导出公钥缓冲区的长度,输出时表示导出公钥结构的大小 + pub fn SKF_ExportPublicKey( + ct_handle: HANDLE, + sign_flag: BOOL, + data: *mut BYTE, + data_len: *mut ULONG, + ) -> ULONG; + + /// ECC数字签名,采用 ECC 算法和指定私钥对指定数据进行数字签名,签名后的结果存放到signature中。 + /// + /// [ct_handle] `[IN]`容器句柄 + /// + /// [data] `[IN]`待签名的数据,当使用 SM2算法时,该输入数据为待签数据经过`SM2`签名预处理的结果,预处理过程遵循`GM/T 0009`。 + /// + /// [data_len] `[IN]`待签名数据长度,必须小于密钥模长。 + /// + /// [signature] `[OUT]`签名值 + /// ## 权限要求 + /// 需要用户权限 + pub fn SKF_ECCSignData( + ct_handle: HANDLE, + data: *const BYTE, + data_len: ULONG, + signature: *mut ECCSignatureBlob, + ) -> ULONG; + + /// 使用ECC密钥协商算法,为计算会话密钥而产生协商参数,返回临时ECC密钥对的公钥及协商句柄 + /// + /// [ct_handle] `[IN]`容器句柄 + /// + /// [alg_id] `[IN]`会话密钥算法标识 + /// + /// [key_blob] `[OUT]`发起方临时ECC公钥 + /// + /// [id_data] `[IN]`发起方的ID + /// + /// [id_len] `[IN]`发起方ID的长度,不大于32 + /// + /// [key_handle] `[OUT]`返回的密钥协商句柄 + pub fn SKF_GenerateAgreementDataWithECC( + ct_handle: HANDLE, + alg_id: ULONG, + key_blob: *mut ECCPublicKeyBlob, + id_data: *const BYTE, + id_len: ULONG, + key_handle: *mut HANDLE, + ) -> ULONG; + + /// 使用ECC密钥协商算法,产生协商参数并计算会话密钥,输出临时ECC密钥对公钥,并返回产生的密钥句柄 + /// + /// [ct_handle] `[IN]`容器句柄 + /// + /// [alg_id] `[IN]`会话密钥算法标识 + /// + /// [sponsor_key] `[IN]`发起方的ECC公钥 + /// + /// [sponsor_tmp_key] `[IN]`发起方的临时ECC公钥 + /// + /// [key_blob] `[OUT]`响应方的临时ECC公钥 + /// + /// [id] `[IN]`响应方的ID + /// + /// [id_len] `[IN]`响应方ID的长度,不大于32 + /// + /// [sponsor_id] `[IN]`发起方的ID + /// + /// [sponsor_id_len] `[IN]`发起方ID的长度,不大于32 + /// + /// [key_handle] `[OUT]` 返回对称算法密钥句柄 + pub fn SKF_GenerateAgreementDataAndKeyWithECC( + ct_handle: HANDLE, + alg_id: ULONG, + sponsor_key: *const ECCPublicKeyBlob, + sponsor_tmp_key: *const ECCPublicKeyBlob, + tmp_key: *mut ECCPublicKeyBlob, + id: *const BYTE, + id_len: ULONG, + sponsor_id: *const BYTE, + sponsor_id_len: ULONG, + key_handle: *mut HANDLE, + ) -> ULONG; + + /// 导入会话密钥 + /// + /// [ct_handle] `[IN]`容器句柄 + /// + /// [alg_id] `[IN]`会话密钥的算法标识 + /// + /// [data] `[IN]`要导入的会话密钥密文。当容器为 ECC 类型时,此参数为ECCCipherBlob密文数据,当容器为RSA类型时,此参数为RSA公钥加密后的数据 + /// + /// [data_len] `[IN]`会话密钥密文长度 + /// + /// [key_handle] `[OUT]`返回会话密钥句柄 + /// ## 权限要求 + /// 需要用户权限 + pub fn SKF_ImportSessionKey( + ct_handle: HANDLE, + alg_id: ULONG, + data: *const BYTE, + data_len: ULONG, + key_handle: *mut HANDLE, + ) -> ULONG; + + /// 使用ECC密钥协商算法,使用自身协商句柄和响应方的协商参数计算会话密钥,同时返回会话密钥句柄 + /// + /// [agreement_key] `[IN]`密钥协商句柄 + /// + /// [key_blob] `[IN]`外部输入的响应方ECC公钥 + /// + /// [tmp_key] `[IN]`外部输入的响应方临时ECC公钥 + /// + /// [id] `[IN]`响应方的ID + /// + /// [id_len] `[IN]`响应方ID的长度,不大于32 + /// + /// [key_handle] `[OUT]`返回的密钥句柄 + pub fn SKF_GenerateKeyWithECC( + agreement_key: HANDLE, + key_blob: *const ECCPublicKeyBlob, + tmp_key_blob: *const ECCPublicKeyBlob, + id: *const BYTE, + id_len: ULONG, + key_handle: *mut HANDLE, + ) -> ULONG; } diff --git a/skf-api/src/native/helper.rs b/skf-api/src/native/helper.rs index 8b9783e..004e500 100644 --- a/skf-api/src/native/helper.rs +++ b/skf-api/src/native/helper.rs @@ -1,4 +1,6 @@ -use crate::native::types::{DeviceInfo, Version}; +use crate::native::types::{ + DeviceInfo, ECCCipherBlob, ECCPrivateKeyBlob, ECCPublicKeyBlob, ECCSignatureBlob, Version, BYTE, +}; impl Default for DeviceInfo { fn default() -> Self { @@ -22,3 +24,149 @@ impl Default for DeviceInfo { } } } + +impl ECCPublicKeyBlob { + /// crate `ECCPublicKeyBlob` with 256 bit x and y + /// ## Panics + /// this function will panic if `x.len() != 32 || y.len() != 32` + pub fn new_256(x: &[u8], y: &[u8]) -> Self { + if x.len() != 32 || y.len() != 32 { + panic!("x.len() != 32 || y.len() != 32"); + } + let mut xc = vec![0u8 as BYTE; 32]; + xc.extend(x); + let mut yc = vec![0u8 as BYTE; 32]; + yc.extend(y); + Self { + bit_len: 256, + x_coordinate: xc.try_into().unwrap(), + y_coordinate: yc.try_into().unwrap(), + } + } + pub fn x_value(&self) -> &[u8] { + &self.x_coordinate[self.bit_len as usize / 8..] + } + pub fn y_value(&self) -> &[u8] { + &self.y_coordinate[self.bit_len as usize / 8..] + } +} +impl Default for ECCPublicKeyBlob { + fn default() -> Self { + Self::new_256(&[0u8; 32], &[0u8; 32]) + } +} +impl ECCPrivateKeyBlob { + /// crate `ECCPrivateKeyBlob` with 256 key + /// ## Panics + /// this function will panic if `key.len() != 32` + pub fn new_256(key: &[u8]) -> Self { + if key.len() != 32 { + panic!("key.len() != 32"); + } + let mut k = vec![0u8 as BYTE; 32]; + k.extend(key); + Self { + bit_len: 256, + private_key: k.try_into().unwrap(), + } + } +} +impl Default for ECCPrivateKeyBlob { + fn default() -> Self { + Self::new_256(&[0u8; 32]) + } +} +impl ECCCipherBlob { + /// cipher filed offset in ECCCipherBlob + pub const CIPHER_INDEX: usize = std::mem::size_of::() - 1; + + /// The size of ECCCipherBlob,include dynamic size of cipher + pub fn size_of(cipher_len: usize) -> usize { + Self::CIPHER_INDEX + cipher_len + } + + /// dynamic size of ECCCipherBlob + #[inline] + pub fn dst_size(&self) -> usize { + Self::CIPHER_INDEX + self.cipher_len as usize + } + + /// raw pointer of cipher filed + pub fn cipher_ptr(&self) -> *const BYTE { + self.cipher.as_ptr() as *const BYTE + } + + /// raw bytes of ECCCipherBlob,include dynamic size of cipher + /// + /// # Safety + /// + /// This function will copy `cipher_len` bytes data from `cipher` pointer + pub unsafe fn raw_bytes(&self) -> Vec { + let mut v: Vec = Vec::with_capacity(self.dst_size()); + unsafe { + std::ptr::copy( + self.x_coordinate.as_ptr(), + v.as_mut_ptr(), + Self::CIPHER_INDEX, + ); + std::ptr::copy( + self.cipher.as_ptr(), + v.as_mut_ptr().add(Self::CIPHER_INDEX), + self.cipher_len as usize, + ); + v.set_len(self.dst_size()); + } + v + } +} + +impl Default for ECCSignatureBlob { + fn default() -> Self { + Self { + r: [0u8; 64], + s: [0u8; 64], + } + } +} +#[cfg(test)] +mod test { + use crate::native::types::{ + ECCCipherBlob, BYTE, ECC_MAX_X_COORDINATE_BITS_LEN, ECC_MAX_Y_COORDINATE_BITS_LEN, ULONG, + }; + + #[test] + fn cipher_blob_layout_test() { + #[derive(Debug, Copy, Clone)] + #[repr(C, packed(1))] + struct SizedBlob { + pub x_coordinate: [BYTE; ECC_MAX_X_COORDINATE_BITS_LEN / 8], + pub y_coordinate: [BYTE; ECC_MAX_Y_COORDINATE_BITS_LEN / 8], + pub hash: [BYTE; 32], + pub cipher_len: ULONG, + pub cipher: [BYTE; SIZE], + } + + let x = SizedBlob::<100> { + x_coordinate: [1u8; 64], + y_coordinate: [2u8; 64], + hash: [3u8; 32], + cipher_len: 100, + cipher: [4u8; 100], + }; + let blob: *const SizedBlob<100> = &x; + let blob = unsafe { &*(blob as *const ECCCipherBlob) }; + + assert_eq!(blob.x_coordinate, [1u8; 64]); + assert_eq!(blob.y_coordinate, [2u8; 64]); + assert_eq!(blob.hash, [3u8; 32]); + assert_eq!( + unsafe { std::ptr::addr_of!(blob.cipher_len).read_unaligned() }, + 100 + ); + assert_eq!(blob.cipher, [4u8; 1]); + + let bytes = unsafe { blob.raw_bytes() }; + assert_eq!(bytes.len(), blob.dst_size()); + println!("raw bytes of ECCCipherBlob = {:?}", bytes); + } +} diff --git a/skf-api/src/native/types.rs b/skf-api/src/native/types.rs index aecefef..4d468aa 100644 --- a/skf-api/src/native/types.rs +++ b/skf-api/src/native/types.rs @@ -117,7 +117,8 @@ pub struct RSAPrivateKeyBlob { #[derive(Debug, Copy, Clone)] #[repr(C, packed(1))] pub struct ECCPublicKeyBlob { - pub bit_leb: ULONG, + /// The real length of x and y + pub bit_len: ULONG, pub x_coordinate: [BYTE; ECC_MAX_X_COORDINATE_BITS_LEN / 8], pub y_coordinate: [BYTE; ECC_MAX_Y_COORDINATE_BITS_LEN / 8], } @@ -126,7 +127,8 @@ pub struct ECCPublicKeyBlob { #[derive(Debug, Copy, Clone)] #[repr(C, packed(1))] pub struct ECCPrivateKeyBlob { - pub bit_leb: ULONG, + /// The real length of the key + pub bit_len: ULONG, pub private_key: [BYTE; ECC_MAX_MODULUS_BITS_LEN / 8], } @@ -152,7 +154,7 @@ pub struct ECCSignatureBlob { /// The structure of `ENVELOPEDKEYBLOB` #[derive(Debug, Copy, Clone)] #[repr(C, packed(1))] -pub struct SKFEnvelopedKeyBlob { +pub struct EnvelopedKeyBlob { pub version: ULONG, pub sym_alg_id: ULONG, pub bits: ULONG, diff --git a/skf-rs/Cargo.toml b/skf-rs/Cargo.toml index 84b070a..44e6eb2 100644 --- a/skf-rs/Cargo.toml +++ b/skf-rs/Cargo.toml @@ -21,4 +21,7 @@ skf-api= { path = "../skf-api", version = "0.4.0" } tracing = "0.1" thiserror = "1.0" anyhow = "1.0" -libloading = "0.8" \ No newline at end of file +libloading = "0.8" + +[dev-dependencies] +hex = "0.4" \ No newline at end of file diff --git a/skf-rs/examples/agreement.rs b/skf-rs/examples/agreement.rs new file mode 100644 index 0000000..d2795d4 --- /dev/null +++ b/skf-rs/examples/agreement.rs @@ -0,0 +1,108 @@ +use skf_rs::helper::auth::encrypt_auth_key_sm1_ecb; +use skf_rs::helper::easy::{recreate_app, recreate_container}; +use skf_rs::spec::algorithm::{SGD_SM1_ECB, SGD_SM2_1}; +use skf_rs::{ + AppAttr, BlockCipherParameter, Engine, LibLoader, SkfApp, SkfContainer, SkfDevice, + FILE_PERM_EVERYONE, PIN_TYPE_USER, +}; +use std::ops::Deref; + +pub const APP_NAME: &str = "skf-app-demo"; +pub const PIN: &str = "12345678"; +pub const REQ_CONTAINER: &str = "test-agreement-1"; +pub const RSP_CONTAINER: &str = "test-agreement-2"; +pub const RESPONDER_ID: &[u8] = b"server"; +pub const INITIATOR_ID: &[u8] = b"client"; +pub const AUTH_KEY: [u8; 16] = [ + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, +]; + +pub fn main() { + let (device, app) = setup_app(); + let (initiator, responder) = setup_container(app.deref()); + + let initiator_pub_key = initiator.ecc_gen_key_pair(SGD_SM2_1).unwrap(); + let responder_pub_key = responder.ecc_gen_key_pair(SGD_SM2_1).unwrap(); + + let sk_alg = SGD_SM1_ECB; + let stage1 = initiator + .sk_gen_agreement_data(sk_alg, INITIATOR_ID) + .unwrap(); + let stage2 = responder + .sk_gen_agreement_data_and_key( + sk_alg, + &initiator_pub_key, + &stage1.0, + INITIATOR_ID, + RESPONDER_ID, + ) + .unwrap(); + let stage3 = device + .ecc_gen_session_key( + stage1.1.as_ref(), + &responder_pub_key, + &stage2.0, + RESPONDER_ID, + ) + .unwrap(); + let crypto = device.block_cipher().unwrap(); + + let plain = b"hello world"; + let cipher_param = BlockCipherParameter { + iv: vec![], + padding_type: 1, + feed_bit_len: 0, + }; + let initiator_sk = stage3.as_ref(); + crypto.encrypt_init(initiator_sk, &cipher_param).unwrap(); + let enc = crypto + .encrypt(initiator_sk, plain, plain.len() * 2) + .unwrap(); + + println!("plain: {:?}", hex::encode(plain).as_str()); + println!( + "initiator sk encrypt result: {:?}", + hex::encode(&enc).as_str() + ); + + let responder_sk = stage2.1.as_ref(); + crypto.decrypt_init(responder_sk, &cipher_param).unwrap(); + let dec = crypto.decrypt(responder_sk, &enc, enc.len() * 2).unwrap(); + + println!( + "responder sk decrypt result: {:?}", + hex::encode(dec).as_str() + ); +} +fn setup_app() -> (Box, Box) { + let device = Engine::new(LibLoader::env_lookup().expect("SKF Lib not load")) + .device_manager() + .and_then(|mgr| mgr.connect_selected(|list| Some(list[0]))) + .expect("Open device failed"); + let auth_key = + encrypt_auth_key_sm1_ecb(device.as_ref(), &AUTH_KEY).expect("auth key encrypt failed"); + device + .device_auth(auth_key.as_slice()) + .expect("device auth failed"); + + let app = recreate_app(device.as_ref(), APP_NAME, app_attr).expect("create app failed"); + app.verify_pin(PIN_TYPE_USER, PIN) + .expect("verify pin failed"); + (device, app) +} +fn app_attr() -> AppAttr { + AppAttr { + admin_pin: PIN.to_string(), + admin_pin_retry_count: 8, + user_pin: PIN.to_string(), + user_pin_retry_count: 8, + create_file_rights: FILE_PERM_EVERYONE, + } +} +fn setup_container(app: &dyn SkfApp) -> (Box, Box) { + let initiator = + recreate_container(app, REQ_CONTAINER).expect("create initiator container failed"); + let responder = + recreate_container(app, RSP_CONTAINER).expect("create responder container failed"); + (initiator, responder) +} diff --git a/skf-rs/examples/delete_all_app.rs b/skf-rs/examples/delete_all_app.rs index 277c6f6..70e92f5 100644 --- a/skf-rs/examples/delete_all_app.rs +++ b/skf-rs/examples/delete_all_app.rs @@ -8,10 +8,7 @@ pub const AUTH_KEY: [u8; 16] = [ fn main() { let engine = Engine::new(LibLoader::env_lookup().unwrap()); let manager = engine.device_manager().unwrap(); - let device = manager - .connect_selected(|list| Some(list[0])) - .unwrap() - .unwrap(); + let device = manager.connect_selected(|list| Some(list[0])).unwrap(); let auth_key = encrypt_auth_key_sm1_ecb(device.as_ref(), &AUTH_KEY).unwrap(); let _ = device.device_auth(auth_key.as_slice()); let app_list = device.enumerate_app_name().unwrap(); diff --git a/skf-rs/examples/device_auth.rs b/skf-rs/examples/device_auth.rs index 433664b..e192f6a 100644 --- a/skf-rs/examples/device_auth.rs +++ b/skf-rs/examples/device_auth.rs @@ -4,10 +4,7 @@ use skf_rs::{Engine, LibLoader}; fn main() { let engine = Engine::new(LibLoader::env_lookup().unwrap()); let manager = engine.device_manager().unwrap(); - let device = manager - .connect_selected(|list| Some(list[0])) - .unwrap() - .unwrap(); + let device = manager.connect_selected(|list| Some(list[0])).unwrap(); let auth_key = encrypt_auth_key_sm1_ecb( device.as_ref(), &[ diff --git a/skf-rs/examples/device_info.rs b/skf-rs/examples/device_info.rs index 3b5f0b8..d693f2b 100644 --- a/skf-rs/examples/device_info.rs +++ b/skf-rs/examples/device_info.rs @@ -3,10 +3,7 @@ use skf_rs::{Engine, LibLoader}; fn main() { let engine = Engine::new(LibLoader::env_lookup().unwrap()); let manager = engine.device_manager().unwrap(); - let device = manager - .connect_selected(|list| Some(list[0])) - .unwrap() - .unwrap(); + let device = manager.connect_selected(|list| Some(list[0])).unwrap(); let info = device.info().unwrap(); println!("device info '{:?}'", &info); diff --git a/skf-rs/src/engine/app.rs b/skf-rs/src/engine/app.rs index 7c2fb83..230ba7f 100644 --- a/skf-rs/src/engine/app.rs +++ b/skf-rs/src/engine/app.rs @@ -1,12 +1,16 @@ +use crate::engine::crypto::ManagedKeyImpl; use crate::engine::symbol::{ModApp, ModContainer}; use crate::error::{SkfErr, SkfPinVerifyError}; use crate::helper::{mem, param}; use crate::{ - AppSecurity, ContainerManager, Error, FileAttr, FileAttrBuilder, FileManager, PinInfo, SkfApp, - SkfContainer, FILE_PERM_NONE, + AppSecurity, ContainerManager, ECCEncryptedData, EnvelopedKeyData, Error, FileAttr, + FileAttrBuilder, FileManager, ManagedKey, PinInfo, SkfApp, SkfContainer, FILE_PERM_NONE, }; use skf_api::native::error::{SAR_OK, SAR_PIN_INCORRECT}; -use skf_api::native::types::{FileAttribute, BOOL, BYTE, CHAR, FALSE, HANDLE, LPSTR, TRUE, ULONG}; +use skf_api::native::types::{ + ECCCipherBlob, ECCPublicKeyBlob, ECCSignatureBlob, EnvelopedKeyBlob, FileAttribute, BOOL, BYTE, + CHAR, FALSE, HANDLE, LPSTR, TRUE, ULONG, +}; use std::fmt::Debug; use std::sync::Arc; use tracing::{instrument, trace}; @@ -214,7 +218,7 @@ impl FileManager for SkfAppImpl { String::from_utf8_lossy(&buff) ); // The spec says string list end with two '\0',but vendor may not do it - let list = unsafe { mem::parse_cstr_list_lossy(buff.as_ptr() as *const u8, buff.len()) }; + let list = unsafe { mem::parse_cstr_list_lossy(buff.as_ptr(), buff.len()) }; Ok(list) } @@ -339,7 +343,7 @@ impl ContainerManager for SkfAppImpl { String::from_utf8_lossy(&buff) ); // The spec says string list end with two '\0',but vendor may not do it - let list = unsafe { mem::parse_cstr_list_lossy(buff.as_ptr() as *const u8, buff.len()) }; + let list = unsafe { mem::parse_cstr_list_lossy(buff.as_ptr(), buff.len()) }; Ok(list) } @@ -401,7 +405,7 @@ impl SkfApp for SkfAppImpl {} impl From<&FileAttribute> for FileAttr { fn from(value: &FileAttribute) -> Self { let file_name: String = unsafe { - mem::parse_cstr_lossy(value.file_name.as_ptr() as *const u8, value.file_name.len()) + mem::parse_cstr_lossy(value.file_name.as_ptr(), value.file_name.len()) .unwrap_or("".to_string()) }; FileAttr { @@ -456,6 +460,7 @@ impl FileAttrBuilder { } } pub(crate) struct SkfContainerImpl { + lib: Arc, symbols: ModContainer, handle: HANDLE, } @@ -467,12 +472,17 @@ impl SkfContainerImpl { /// /// [lib] - The library handle pub fn new(handle: HANDLE, lib: &Arc) -> crate::Result { + let lc = Arc::clone(lib); let symbols = ModContainer::load_symbols(lib)?; - Ok(Self { symbols, handle }) + Ok(Self { + symbols, + handle, + lib: lc, + }) } pub fn close(&mut self) -> crate::Result<()> { - if let Some(ref func) = self.symbols.container_close { + if let Some(ref func) = self.symbols.ct_close { let ret = unsafe { func(self.handle) }; trace!("[SKF_CloseContainer]: ret = {}", ret); if ret != SAR_OK { @@ -495,12 +505,9 @@ impl Drop for SkfContainerImpl { } } impl SkfContainer for SkfContainerImpl { + #[instrument] fn get_type(&self) -> crate::Result { - let func = self - .symbols - .container_get_type - .as_ref() - .expect("Symbol not load"); + let func = self.symbols.ct_get_type.as_ref().expect("Symbol not load"); let mut type_value = 0 as ULONG; let ret = unsafe { func(self.handle, &mut type_value) }; @@ -511,12 +518,9 @@ impl SkfContainer for SkfContainerImpl { } } + #[instrument] fn import_certificate(&self, signer: bool, data: &[u8]) -> crate::Result<()> { - let func = self - .symbols - .container_imp_cert - .as_ref() - .expect("Symbol not load"); + let func = self.symbols.ct_imp_cert.as_ref().expect("Symbol not load"); let signer = match signer { true => TRUE, false => FALSE, @@ -537,12 +541,9 @@ impl SkfContainer for SkfContainerImpl { } } + #[instrument] fn export_certificate(&self, signer: bool) -> crate::Result> { - let func = self - .symbols - .container_exp_cert - .as_ref() - .expect("Symbol not load"); + let func = self.symbols.ct_exp_cert.as_ref().expect("Symbol not load"); let signer = match signer { true => TRUE, false => FALSE, @@ -562,4 +563,226 @@ impl SkfContainer for SkfContainerImpl { unsafe { buff.set_len(len as usize) }; Ok(buff) } + + #[instrument] + fn ecc_gen_key_pair(&self, alg_id: u32) -> crate::Result { + let func = self + .symbols + .ct_ecc_gen_pair + .as_ref() + .expect("Symbol not load"); + let mut blob = ECCPublicKeyBlob::default(); + let ret = unsafe { func(self.handle, alg_id, &mut blob) }; + trace!("[SKF_GenECCKeyPair]: ret = {}", ret); + match ret { + SAR_OK => Ok(blob), + _ => Err(Error::Skf(SkfErr::of_code(ret))), + } + } + + #[instrument] + fn ecc_import_key_pair(&self, enveloped_key: &EnvelopedKeyData) -> crate::Result<()> { + let func = self + .symbols + .ct_ecc_imp_pair + .as_ref() + .expect("Symbol not load"); + let raw_bytes = enveloped_key.blob_bytes(); + let ret = unsafe { func(self.handle, raw_bytes.as_ptr() as *const EnvelopedKeyBlob) }; + trace!("[SKF_ImportECCKeyPair]: ret = {}", ret); + match ret { + SAR_OK => Ok(()), + _ => Err(Error::Skf(SkfErr::of_code(ret))), + } + } + + #[instrument] + fn ecc_export_public_key(&self, sign_part: bool) -> crate::Result> { + let func = self + .symbols + .ct_ecc_exp_pub_key + .as_ref() + .expect("Symbol not load"); + let mut len: ULONG = 0; + let ret = unsafe { + func( + self.handle, + sign_part as BOOL, + std::ptr::null_mut(), + &mut len, + ) + }; + if ret != SAR_OK { + return Err(Error::Skf(SkfErr::of_code(ret))); + } + trace!("[SKF_EccExportPublicKey]: desired len = {}", len); + let mut buff = Vec::::with_capacity(len as usize); + let ret = unsafe { + func( + self.handle, + sign_part as BOOL, + buff.as_mut_ptr() as *mut BYTE, + &mut len, + ) + }; + trace!("[SKF_EccExportPublicKey]: ret = {}", ret); + if ret != SAR_OK { + return Err(Error::Skf(SkfErr::of_code(ret))); + } + unsafe { buff.set_len(len as usize) }; + Ok(buff) + } + + #[instrument] + fn ecc_sign(&self, hash: &[u8]) -> crate::Result { + let func = self.symbols.ct_ecc_sign.as_ref().expect("Symbol not load"); + let mut blob = ECCSignatureBlob::default(); + let ret = unsafe { + func( + self.handle, + hash.as_ptr() as *const BYTE, + hash.len() as ULONG, + &mut blob, + ) + }; + trace!("[SKF_ECCSignData]: ret = {}", ret); + match ret { + SAR_OK => Ok(blob), + _ => Err(Error::Skf(SkfErr::of_code(ret))), + } + } + + #[instrument] + fn sk_gen_agreement_data( + &self, + alg_id: u32, + id: &[u8], + ) -> crate::Result<(ECCPublicKeyBlob, Box)> { + let func = self + .symbols + .ct_sk_gen_agreement + .as_ref() + .expect("Symbol not load"); + let mut handle: HANDLE = std::ptr::null_mut(); + let mut pub_key = ECCPublicKeyBlob::default(); + let ret = unsafe { + func( + self.handle, + alg_id as ULONG, + &mut pub_key, + id.as_ptr() as *const BYTE, + id.len() as ULONG, + &mut handle, + ) + }; + trace!("[SKF_GenerateAgreementDataWithECC]: ret = {}", ret); + let managed_key = ManagedKeyImpl::try_new(handle, &self.lib)?; + match ret { + SAR_OK => Ok((pub_key, Box::new(managed_key))), + _ => Err(Error::Skf(SkfErr::of_code(ret))), + } + } + + #[instrument] + fn sk_gen_agreement_data_and_key( + &self, + alg_id: u32, + initiator_key: &ECCPublicKeyBlob, + initiator_tmp_key: &ECCPublicKeyBlob, + initiator_id: &[u8], + responder_id: &[u8], + ) -> crate::Result<(ECCPublicKeyBlob, Box)> { + let func = self + .symbols + .ct_sk_gen_agreement_and_key + .as_ref() + .expect("Symbol not load"); + let mut handle: HANDLE = std::ptr::null_mut(); + let mut pub_key = ECCPublicKeyBlob::default(); + let ret = unsafe { + func( + self.handle, + alg_id as ULONG, + initiator_key, + initiator_tmp_key, + &mut pub_key, + responder_id.as_ptr() as *const BYTE, + responder_id.len() as ULONG, + initiator_id.as_ptr() as *const BYTE, + initiator_id.len() as ULONG, + &mut handle, + ) + }; + trace!("[SKF_GenerateAgreementDataAndKeyWithECC]: ret = {}", ret); + let managed_key = ManagedKeyImpl::try_new(handle, &self.lib)?; + match ret { + SAR_OK => Ok((pub_key, Box::new(managed_key))), + _ => Err(Error::Skf(SkfErr::of_code(ret))), + } + } + + #[instrument] + fn sk_import(&self, alg_id: u32, key_data: &[u8]) -> crate::Result> { + let func = self.symbols.ct_sk_imp.as_ref().expect("Symbol not load"); + let mut handle: HANDLE = std::ptr::null_mut(); + let ret = unsafe { + func( + self.handle, + alg_id as ULONG, + key_data.as_ptr() as *const BYTE, + key_data.len() as ULONG, + &mut handle, + ) + }; + trace!("[SKF_ImportSessionKey]: ret = {}", ret); + let managed_key = ManagedKeyImpl::try_new(handle, &self.lib)?; + match ret { + SAR_OK => Ok(Box::new(managed_key)), + _ => Err(Error::Skf(SkfErr::of_code(ret))), + } + } + + #[instrument] + fn sk_export( + &self, + alg_id: u32, + key: &ECCPublicKeyBlob, + ) -> crate::Result<(Box, ECCEncryptedData)> { + let func = self.symbols.ct_sk_exp.as_ref().expect("Symbol not load"); + let mut handle: HANDLE = std::ptr::null_mut(); + // guess 256 is big enough + let buff_size = ECCCipherBlob::size_of(256); + let mut buff: Vec = vec![0; buff_size]; + + let ret = unsafe { + func( + self.handle, + alg_id as ULONG, + key as *const ECCPublicKeyBlob, + buff.as_mut_ptr() as *mut ECCCipherBlob, + &mut handle, + ) + }; + trace!("[SKF_ECCExportSessionKey]: ret = {}", ret); + if ret != SAR_OK { + return Err(Error::Skf(SkfErr::of_code(ret))); + } + let blob = unsafe { + let cb = &*(buff.as_ptr() as *const ECCCipherBlob); + let mut cipher: Vec = vec![]; + if cb.cipher_len > 0 { + let len = cb.cipher_len as usize; + cipher = vec![0; len]; + std::ptr::copy(cb.cipher.as_ptr(), cipher.as_mut_ptr(), len); + } + ECCEncryptedData { + ec_x: cb.x_coordinate, + ec_y: cb.y_coordinate, + hash: cb.hash, + cipher, + } + }; + let managed_key = ManagedKeyImpl::try_new(handle, &self.lib)?; + Ok((Box::new(managed_key), blob)) + } } diff --git a/skf-rs/src/engine/crypto.rs b/skf-rs/src/engine/crypto.rs index a6e8afd..1df41c1 100644 --- a/skf-rs/src/engine/crypto.rs +++ b/skf-rs/src/engine/crypto.rs @@ -3,7 +3,7 @@ use crate::error::{InvalidArgumentError, SkfErr}; use crate::{BlockCipherParameter, Error, ManagedKey, Result, SkfBlockCipher}; use skf_api::native::error::SAR_OK; use skf_api::native::types::{BlockCipherParam, BYTE, HANDLE, MAX_IV_LEN, ULONG}; -use std::fmt::Debug; +use std::fmt::{Debug, Formatter}; use std::sync::Arc; use tracing::{instrument, trace}; @@ -12,7 +12,7 @@ pub(crate) struct ManagedKeyImpl { handle: HANDLE, } impl Debug for ManagedKeyImpl { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { write!(f, "ManagedKeyImpl") } } @@ -29,7 +29,11 @@ impl AsRef for ManagedKeyImpl { } impl ManagedKey for ManagedKeyImpl {} - +impl Debug for dyn ManagedKey { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "Handle:{:p}", self.as_ref()) + } +} impl ManagedKeyImpl { pub(crate) fn try_new(handle: HANDLE, lib: &Arc) -> Result { let close_fn = unsafe { SymbolBundle::new(lib, b"SKF_CloseHandle\0")? }; @@ -37,12 +41,13 @@ impl ManagedKeyImpl { } #[instrument] - pub(crate) fn close(&self) -> Result<()> { + pub(crate) fn close(&mut self) -> Result<()> { let ret = unsafe { (self.close_fn)(self.handle) }; trace!("[SKF_CloseHandle]: ret = {}", ret); if ret != SAR_OK { return Err(Error::Skf(SkfErr::of_code(ret))); } + self.handle = std::ptr::null(); Ok(()) } } @@ -51,7 +56,7 @@ pub(crate) struct SkfBlockCipherImpl { symbols: ModBlockCipher, } impl Debug for SkfBlockCipherImpl { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { write!(f, "SkfBlockCipherImpl") } } diff --git a/skf-rs/src/engine/device.rs b/skf-rs/src/engine/device.rs index cbb5ebf..870e493 100644 --- a/skf-rs/src/engine/device.rs +++ b/skf-rs/src/engine/device.rs @@ -5,13 +5,14 @@ use crate::engine::symbol::ModDev; use crate::error::SkfErr; use crate::helper::{mem, param}; use crate::{ - AppAttr, AppManager, DeviceCtl, DeviceInformation, DeviceSecurity, ManagedKey, SkfApp, - SkfBlockCipher, SkfDevice, Version, + AppAttr, AppManager, DeviceAuth, DeviceCrypto, DeviceCtl, DeviceInformation, ECCEncryptedData, + ECCPrivateKeyBlob, ECCPublicKeyBlob, ECCSignatureBlob, ManagedKey, SkfApp, SkfBlockCipher, + SkfDevice, Version, }; use crate::{Error, Result}; use skf_api::native::error::SAR_OK; use skf_api::native::types::{ - DeviceInfo, BYTE, CHAR, DEV_LOCK_FOREVER, DWORD, HANDLE, LPSTR, ULONG, + DeviceInfo, ECCCipherBlob, BYTE, CHAR, DEV_LOCK_FOREVER, DWORD, HANDLE, LPSTR, ULONG, }; use std::fmt::Debug; use std::sync::Arc; @@ -60,7 +61,7 @@ impl Debug for SkfDeviceImpl { } } -impl DeviceSecurity for SkfDeviceImpl { +impl DeviceAuth for SkfDeviceImpl { #[instrument] fn device_auth(&self, data: &[u8]) -> Result<()> { let func = self.symbols.dev_auth.as_ref().expect("Symbol not load"); @@ -169,6 +170,9 @@ impl DeviceCtl for SkfDeviceImpl { unsafe { buffer.set_len(len as usize) }; Ok(buffer) } +} + +impl DeviceCrypto for SkfDeviceImpl { #[instrument] fn gen_random(&self, len: usize) -> Result> { let func = self.symbols.gen_random.as_ref().expect("Symbol not load"); @@ -182,6 +186,7 @@ impl DeviceCtl for SkfDeviceImpl { Ok(buffer) } + #[instrument] fn set_symmetric_key(&self, alg_id: u32, key: &[u8]) -> Result> { let func = self .symbols @@ -204,8 +209,188 @@ impl DeviceCtl for SkfDeviceImpl { let managed_key = ManagedKeyImpl::try_new(handle, &self.lib)?; Ok(Box::new(managed_key)) } -} + #[instrument] + fn ext_ecc_encrypt(&self, key: &ECCPublicKeyBlob, data: &[u8]) -> Result { + let func = self + .symbols + .ecc_ext_encrypt + .as_ref() + .expect("Symbol not load"); + let buff_size = ECCCipherBlob::size_of(data.len()); + let mut buff: Vec = vec![0; buff_size]; + + let ret = unsafe { + func( + self.handle, + key as *const ECCPublicKeyBlob, + data.as_ptr() as *const BYTE, + data.len() as ULONG, + buff.as_mut_ptr() as *mut ECCCipherBlob, + ) + }; + trace!("[SKF_ExtECCEncrypt]: ret = {}", ret); + if ret != SAR_OK { + return Err(Error::Skf(SkfErr::of_code(ret))); + } + let blob = unsafe { + let cb = &*(buff.as_ptr() as *const ECCCipherBlob); + let mut cipher: Vec = vec![]; + if cb.cipher_len > 0 { + let len = cb.cipher_len as usize; + cipher = vec![0; len]; + std::ptr::copy(cb.cipher.as_ptr(), cipher.as_mut_ptr(), len); + } + ECCEncryptedData { + ec_x: cb.x_coordinate, + ec_y: cb.y_coordinate, + hash: cb.hash, + cipher, + } + }; + Ok(blob) + } + + #[instrument] + fn ext_ecc_decrypt( + &self, + key: &ECCPrivateKeyBlob, + cipher: &ECCEncryptedData, + ) -> Result> { + let func = self + .symbols + .ecc_ext_decrypt + .as_ref() + .expect("Symbol not load"); + let cipher_mem = cipher.blob_bytes(); + let mut buff: Vec = Vec::with_capacity(cipher.cipher.len()); + let mut buff_len: ULONG = buff.len() as ULONG; + + let ret = unsafe { + func( + self.handle, + key as *const ECCPrivateKeyBlob, + cipher_mem.as_ptr() as *const ECCCipherBlob, + buff.as_mut_ptr() as *mut BYTE, + &mut buff_len, + ) + }; + trace!("[SKF_ExtECCDecrypt]: ret = {}", ret); + if ret != SAR_OK { + return Err(Error::Skf(SkfErr::of_code(ret))); + } + + trace!("[SKF_ExtECCDecrypt]: len = {}", buff_len); + unsafe { buff.set_len(buff_len as usize) }; + + Ok(buff) + } + + #[instrument] + fn ext_ecc_sign(&self, key: &ECCPrivateKeyBlob, data: &[u8]) -> Result { + let func = self.symbols.ecc_ext_sign.as_ref().expect("Symbol not load"); + + let mut sign = ECCSignatureBlob::default(); + let ret = unsafe { + func( + self.handle, + key as *const ECCPrivateKeyBlob, + data.as_ptr() as *const BYTE, + data.len() as ULONG, + &mut sign, + ) + }; + trace!("[SKF_ExtECCSign]: ret = {}", ret); + if ret != SAR_OK { + return Err(Error::Skf(SkfErr::of_code(ret))); + } + + Ok(sign) + } + + #[instrument] + fn ext_ecc_verify( + &self, + key: &ECCPublicKeyBlob, + data: &[u8], + signature: &ECCSignatureBlob, + ) -> Result<()> { + let func = self + .symbols + .ecc_ext_verify + .as_ref() + .expect("Symbol not load"); + + let ret = unsafe { + func( + self.handle, + key as *const ECCPublicKeyBlob, + data.as_ptr() as *const BYTE, + data.len() as ULONG, + signature as *const ECCSignatureBlob, + ) + }; + trace!("[SKF_ExtECCVerify]: ret = {}", ret); + if ret != SAR_OK { + return Err(Error::Skf(SkfErr::of_code(ret))); + } + Ok(()) + } + + #[instrument] + fn ecc_verify( + &self, + key: &ECCPublicKeyBlob, + hash: &[u8], + signature: &ECCSignatureBlob, + ) -> Result<()> { + let func = self.symbols.ecc_verify.as_ref().expect("Symbol not load"); + + let ret = unsafe { + func( + self.handle, + key as *const ECCPublicKeyBlob, + hash.as_ptr() as *const BYTE, + hash.len() as ULONG, + signature as *const ECCSignatureBlob, + ) + }; + trace!("[SKF_ECCVerify]: ret = {}", ret); + if ret != SAR_OK { + return Err(Error::Skf(SkfErr::of_code(ret))); + } + Ok(()) + } + + #[instrument(skip(agreement_key))] + fn ecc_gen_session_key( + &self, + agreement_key: &dyn ManagedKey, + responder_key: &ECCPublicKeyBlob, + responder_tmp_key: &ECCPublicKeyBlob, + responder_id: &[u8], + ) -> Result> { + let func = self.symbols.ecc_gen_sk.as_ref().expect("Symbol not load"); + + let mut handle: HANDLE = std::ptr::null_mut(); + let ret = unsafe { + func( + *agreement_key.as_ref(), + responder_key, + responder_tmp_key, + responder_id.as_ptr() as *const BYTE, + responder_id.len() as ULONG, + &mut handle, + ) + }; + trace!("[SKF_GenerateKeyWithECC]: ret = {}", ret); + if ret != SAR_OK { + return Err(Error::Skf(SkfErr::of_code(ret))); + } + let managed_key = ManagedKeyImpl::try_new(handle, &self.lib)?; + Ok(Box::new(managed_key)) + } +} impl AppManager for SkfDeviceImpl { #[instrument] fn enumerate_app_name(&self) -> Result> { @@ -228,7 +413,7 @@ impl AppManager for SkfDeviceImpl { String::from_utf8_lossy(&buff) ); // The spec says string list end with two '\0',but vendor may not do it - let list = unsafe { mem::parse_cstr_list_lossy(buff.as_ptr() as *const u8, buff.len()) }; + let list = unsafe { mem::parse_cstr_list_lossy(buff.as_ptr(), buff.len()) }; Ok(list) } @@ -305,26 +490,19 @@ impl From<&DeviceInfo> for DeviceInformation { minor: value.version.minor, }; let manufacturer: String = unsafe { - mem::parse_cstr_lossy( - value.manufacturer.as_ptr() as *const u8, - value.manufacturer.len(), - ) - .unwrap_or("".to_string()) + mem::parse_cstr_lossy(value.manufacturer.as_ptr(), value.manufacturer.len()) + .unwrap_or("".to_string()) }; let issuer: String = unsafe { - mem::parse_cstr_lossy(value.issuer.as_ptr() as *const u8, value.issuer.len()) + mem::parse_cstr_lossy(value.issuer.as_ptr(), value.issuer.len()) .unwrap_or("".to_string()) }; let label: String = unsafe { - mem::parse_cstr_lossy(value.label.as_ptr() as *const u8, value.label.len()) - .unwrap_or("".to_string()) + mem::parse_cstr_lossy(value.label.as_ptr(), value.label.len()).unwrap_or("".to_string()) }; let serial_number = unsafe { - mem::parse_cstr_lossy( - value.serial_number.as_ptr() as *const u8, - value.serial_number.len(), - ) - .unwrap_or("".to_string()) + mem::parse_cstr_lossy(value.serial_number.as_ptr(), value.serial_number.len()) + .unwrap_or("".to_string()) }; let hw_version = Version { major: value.hw_version.major, diff --git a/skf-rs/src/engine/manager.rs b/skf-rs/src/engine/manager.rs index 37284fb..7a3fa0d 100644 --- a/skf-rs/src/engine/manager.rs +++ b/skf-rs/src/engine/manager.rs @@ -54,7 +54,7 @@ impl DeviceManager for ManagerImpl { String::from_utf8_lossy(&buff) ); // The spec says string list end with two '\0',but vendor may not do it - let list = unsafe { mem::parse_cstr_list_lossy(buff.as_ptr() as *const u8, buff.len()) }; + let list = unsafe { mem::parse_cstr_list_lossy(buff.as_ptr(), buff.len()) }; Ok(list) } @@ -94,7 +94,7 @@ impl DeviceManager for ManagerImpl { event, len ); - let name = unsafe { mem::parse_cstr_lossy(buff.as_ptr() as *const u8, len as usize) }; + let name = unsafe { mem::parse_cstr_lossy(buff.as_ptr(), len as usize) }; let name = name.unwrap_or("".to_string()); let event = event as u8; match event { @@ -137,17 +137,62 @@ impl DeviceManager for ManagerImpl { fn connect_selected( &self, selector: fn(Vec<&str>) -> Option<&str>, - ) -> Result>> { + ) -> Result> { let list = self.enumerate_device_name(true)?; if list.is_empty() { - Ok(None) + Err(Error::NotFound("No device found".to_string())) } else { let names: Vec<&str> = list.iter().map(|x| &**x).collect(); if let Some(name) = selector(names) { let dev = self.connect(name)?; - return Ok(Some(dev)); + return Ok(dev); } - Ok(None) + Err(Error::NotFound("No matched device".to_string())) + } + } +} + +impl PluginEvent { + /// The device is plugged in + pub const EVENT_PLUGGED_IN: u8 = 1; + + /// The device is unplugged + pub const EVENT_UNPLUGGED: u8 = 2; + + pub fn new(device_name: impl Into, event: u8) -> Self { + Self { + device_name: device_name.into(), + event, + } + } + + pub fn plugged_in(device_name: impl AsRef) -> Self { + Self { + device_name: device_name.as_ref().to_string(), + event: Self::EVENT_PLUGGED_IN, + } + } + + pub fn unplugged(device_name: impl AsRef) -> Self { + Self { + device_name: device_name.as_ref().to_string(), + event: Self::EVENT_UNPLUGGED, + } + } + + pub fn is_plugged_in(&self) -> bool { + self.event == Self::EVENT_PLUGGED_IN + } + + pub fn is_unplugged(&self) -> bool { + self.event == Self::EVENT_UNPLUGGED + } + + pub fn event_description(&self) -> &'static str { + match self.event { + Self::EVENT_PLUGGED_IN => "plugged in", + Self::EVENT_UNPLUGGED => "unplugged", + _ => "unknown", } } } diff --git a/skf-rs/src/engine/symbol.rs b/skf-rs/src/engine/symbol.rs index 7bde7f0..c3cf1cf 100644 --- a/skf-rs/src/engine/symbol.rs +++ b/skf-rs/src/engine/symbol.rs @@ -1,4 +1,5 @@ use libloading::{Library, Symbol}; + use std::sync::Arc; /// Symbol bundle with library pointer @@ -151,7 +152,10 @@ pub(crate) mod container_fn { #[allow(non_camel_case_types)] pub(crate) mod crypto_fn { use crate::engine::symbol::SymbolBundle; - use skf_api::native::types::{BlockCipherParam, BYTE, HANDLE, ULONG}; + use skf_api::native::types::{ + BlockCipherParam, ECCCipherBlob, ECCPrivateKeyBlob, ECCPublicKeyBlob, ECCSignatureBlob, + EnvelopedKeyBlob, BOOL, BYTE, HANDLE, ULONG, + }; pub(crate) type SKF_GenRandom = SymbolBundle ULONG>; pub(crate) type SKF_CloseHandle = SymbolBundle ULONG>; @@ -177,6 +181,131 @@ pub(crate) mod crypto_fn { >; pub(super) type SKF_DecryptFinal = SymbolBundle ULONG>; + + pub(super) type SKF_ExtECCEncrypt = SymbolBundle< + unsafe extern "C" fn( + HANDLE, + *const ECCPublicKeyBlob, + *const BYTE, + ULONG, + *mut ECCCipherBlob, + ) -> ULONG, + >; + + pub(super) type SKF_ExtECCDecrypt = SymbolBundle< + unsafe extern "C" fn( + HANDLE, + *const ECCPrivateKeyBlob, + *const ECCCipherBlob, + *mut BYTE, + *mut ULONG, + ) -> ULONG, + >; + + pub(super) type SKF_ExtECCSign = SymbolBundle< + unsafe extern "C" fn( + HANDLE, + *const ECCPrivateKeyBlob, + *const BYTE, + ULONG, + *mut ECCSignatureBlob, + ) -> ULONG, + >; + + pub(super) type SKF_ExtECCVerify = SymbolBundle< + unsafe extern "C" fn( + HANDLE, + *const ECCPublicKeyBlob, + *const BYTE, + ULONG, + *const ECCSignatureBlob, + ) -> ULONG, + >; + pub(super) type SKF_GenECCKeyPair = + SymbolBundle ULONG>; + + pub(super) type SKF_ImportECCKeyPair = + SymbolBundle ULONG>; + + pub(super) type SKF_ECCSignData = SymbolBundle< + unsafe extern "C" fn(HANDLE, *const BYTE, ULONG, *mut ECCSignatureBlob) -> ULONG, + >; + + pub(super) type SKF_ECCVerify = SymbolBundle< + unsafe extern "C" fn( + HANDLE, + *const ECCPublicKeyBlob, + *const BYTE, + ULONG, + *const ECCSignatureBlob, + ) -> ULONG, + >; + + pub(super) type SKF_ECCExportSessionKey = SymbolBundle< + unsafe extern "C" fn( + HANDLE, + ULONG, + *const ECCPublicKeyBlob, + *mut ECCCipherBlob, + *mut HANDLE, + ) -> ULONG, + >; + + pub(super) type SKF_GenerateAgreementDataWithECC = SymbolBundle< + unsafe extern "C" fn( + HANDLE, + ULONG, + *mut ECCPublicKeyBlob, + *const BYTE, + ULONG, + *mut HANDLE, + ) -> ULONG, + >; + + pub(super) type SKF_GenerateAgreementDataAndKeyWithECC = SymbolBundle< + unsafe extern "C" fn( + HANDLE, + ULONG, + *const ECCPublicKeyBlob, + *const ECCPublicKeyBlob, + *mut ECCPublicKeyBlob, + *const BYTE, + ULONG, + *const BYTE, + ULONG, + *mut HANDLE, + ) -> ULONG, + >; + + pub(super) type SKF_ExportPublicKey = SymbolBundle< + unsafe extern "C" fn( + HANDLE, + sign_flag: BOOL, + data: *mut BYTE, + data_len: *mut ULONG, + ) -> ULONG, + >; + + pub(super) type SKF_ImportSessionKey = SymbolBundle< + unsafe extern "C" fn( + HANDLE, + ULONG, + data: *const BYTE, + data_len: ULONG, + key_handle: *mut HANDLE, + ) -> ULONG, + >; + + pub(super) type SKF_GenerateKeyWithECC = SymbolBundle< + unsafe extern "C" fn( + agreement_key: HANDLE, + *const ECCPublicKeyBlob, + *const ECCPublicKeyBlob, + *const BYTE, + ULONG, + *mut HANDLE, + ) -> ULONG, + >; } #[derive(Default)] @@ -223,6 +352,12 @@ pub(crate) struct ModDev { pub app_enum: Option, pub gen_random: Option, pub sym_key_import: Option, + pub ecc_ext_encrypt: Option, + pub ecc_ext_decrypt: Option, + pub ecc_ext_sign: Option, + pub ecc_ext_verify: Option, + pub ecc_verify: Option, + pub ecc_gen_sk: Option, } impl ModDev { @@ -242,6 +377,12 @@ impl ModDev { let app_enum = Some(unsafe { SymbolBundle::new(lib, b"SKF_EnumApplication\0")? }); let gen_random = Some(unsafe { SymbolBundle::new(lib, b"SKF_GenRandom\0")? }); let sym_key_import = Some(unsafe { SymbolBundle::new(lib, b"SKF_SetSymmKey\0")? }); + let ecc_ext_encrypt = Some(unsafe { SymbolBundle::new(lib, b"SKF_ExtECCEncrypt\0")? }); + let ecc_ext_decrypt = Some(unsafe { SymbolBundle::new(lib, b"SKF_ExtECCDecrypt\0")? }); + let ecc_ext_sign = Some(unsafe { SymbolBundle::new(lib, b"SKF_ExtECCSign\0")? }); + let ecc_ext_verify = Some(unsafe { SymbolBundle::new(lib, b"SKF_ExtECCVerify\0")? }); + let ecc_verify = Some(unsafe { SymbolBundle::new(lib, b"SKF_ECCVerify\0")? }); + let ecc_gen_sk = Some(unsafe { SymbolBundle::new(lib, b"SKF_GenerateKeyWithECC\0")? }); let holder = Self { dev_set_label, @@ -258,6 +399,12 @@ impl ModDev { app_enum, gen_random, sym_key_import, + ecc_ext_encrypt, + ecc_ext_decrypt, + ecc_ext_sign, + ecc_ext_verify, + ecc_verify, + ecc_gen_sk, }; Ok(holder) } @@ -328,26 +475,49 @@ impl ModApp { #[derive(Default)] pub(crate) struct ModContainer { - pub container_close: Option, - pub container_get_type: Option, - pub container_imp_cert: Option, - pub container_exp_cert: Option, + pub ct_close: Option, + pub ct_get_type: Option, + pub ct_imp_cert: Option, + pub ct_exp_cert: Option, + pub ct_ecc_gen_pair: Option, + pub ct_ecc_imp_pair: Option, + pub ct_ecc_sign: Option, + pub ct_sk_gen_agreement: Option, + pub ct_sk_gen_agreement_and_key: Option, + pub ct_ecc_exp_pub_key: Option, + pub ct_sk_imp: Option, + pub ct_sk_exp: Option, } impl ModContainer { pub fn load_symbols(lib: &Arc) -> crate::Result { - let container_close = Some(unsafe { SymbolBundle::new(lib, b"SKF_CloseContainer\0")? }); - let container_get_type = - Some(unsafe { SymbolBundle::new(lib, b"SKF_GetContainerType\0")? }); - let container_imp_cert = - Some(unsafe { SymbolBundle::new(lib, b"SKF_ImportCertificate\0")? }); - let container_exp_cert = - Some(unsafe { SymbolBundle::new(lib, b"SKF_ExportCertificate\0")? }); + let ct_close = Some(unsafe { SymbolBundle::new(lib, b"SKF_CloseContainer\0")? }); + let ct_get_type = Some(unsafe { SymbolBundle::new(lib, b"SKF_GetContainerType\0")? }); + let ct_imp_cert = Some(unsafe { SymbolBundle::new(lib, b"SKF_ImportCertificate\0")? }); + let ct_exp_cert = Some(unsafe { SymbolBundle::new(lib, b"SKF_ExportCertificate\0")? }); + let ct_ecc_gen_pair = Some(unsafe { SymbolBundle::new(lib, b"SKF_GenECCKeyPair\0")? }); + let ct_ecc_imp_pair = Some(unsafe { SymbolBundle::new(lib, b"SKF_ImportECCKeyPair\0")? }); + let ct_ecc_sign = Some(unsafe { SymbolBundle::new(lib, b"SKF_ECCSignData\0")? }); + let ct_sk_gen_agreement = + Some(unsafe { SymbolBundle::new(lib, b"SKF_GenerateAgreementDataWithECC\0")? }); + let ct_sk_gen_agreement_and_key = + Some(unsafe { SymbolBundle::new(lib, b"SKF_GenerateAgreementDataAndKeyWithECC\0")? }); + let ct_ecc_exp_pub_key = Some(unsafe { SymbolBundle::new(lib, b"SKF_ExportPublicKey\0")? }); + let ct_sk_imp = Some(unsafe { SymbolBundle::new(lib, b"SKF_ImportSessionKey\0")? }); + let ct_sk_exp = Some(unsafe { SymbolBundle::new(lib, b"SKF_ECCExportSessionKey\0")? }); let holder = Self { - container_close, - container_get_type, - container_imp_cert, - container_exp_cert, + ct_close, + ct_get_type, + ct_imp_cert, + ct_exp_cert, + ct_ecc_gen_pair, + ct_ecc_imp_pair, + ct_ecc_sign, + ct_sk_gen_agreement, + ct_sk_gen_agreement_and_key, + ct_ecc_exp_pub_key, + ct_sk_imp, + ct_sk_exp, }; Ok(holder) } diff --git a/skf-rs/src/error.rs b/skf-rs/src/error.rs index e5ac7a0..31be8ef 100644 --- a/skf-rs/src/error.rs +++ b/skf-rs/src/error.rs @@ -4,6 +4,8 @@ use std::fmt::{Debug, Display, Formatter}; pub enum Error { #[error(transparent)] InvalidArgument(#[from] InvalidArgumentError), + #[error("`{0}`")] + NotFound(String), #[error(transparent)] LibLoading(#[from] libloading::Error), #[error(transparent)] diff --git a/skf-rs/src/helper/easy.rs b/skf-rs/src/helper/easy.rs new file mode 100644 index 0000000..a09cad4 --- /dev/null +++ b/skf-rs/src/helper/easy.rs @@ -0,0 +1,68 @@ +//! +//! Helper functions for use this library easily +//! +use crate::{AppAttr, Result, SkfApp, SkfContainer, SkfDevice}; + +/// Open or create application by it name +/// +/// [device] - The device instance +/// +/// [name] - The application name +/// +/// [attr_fn] - A function to provide application attribute ,used when application is not exist +pub fn open_or_create_app( + device: &dyn SkfDevice, + name: &str, + attr_fn: fn() -> AppAttr, +) -> Result> { + let list = device.enumerate_app_name()?; + if list.contains(&name.to_string()) { + return device.open_app(name); + } + device.create_app(name, &attr_fn()) +} + +/// Create application, If application exist, delete it first +/// +/// [device] - The device instance +/// +/// [name] - The application name +/// +/// [attr_fn] - A function to provide application attribute +pub fn recreate_app( + device: &dyn SkfDevice, + name: &str, + attr_fn: fn() -> AppAttr, +) -> Result> { + let list = device.enumerate_app_name()?; + if list.contains(&name.to_string()) { + device.delete_app(name)?; + } + device.create_app(name, &attr_fn()) +} + +/// Open or create container by container name +/// +/// [app] - The application instance +/// +/// [name] - The container name +pub fn open_or_create_container(app: &dyn SkfApp, name: &str) -> Result> { + let list = app.enumerate_container_name()?; + if list.contains(&name.to_string()) { + return app.open_container(name); + } + app.create_container(name) +} + +/// Create container, If container exist, delete it first +/// +/// [app] - The application instance +/// +/// [name] - The container name +pub fn recreate_container(app: &dyn SkfApp, name: &str) -> Result> { + let list = app.enumerate_container_name()?; + if list.contains(&name.to_string()) { + app.delete_container(name)?; + } + app.create_container(name) +} diff --git a/skf-rs/src/helper/mod.rs b/skf-rs/src/helper/mod.rs index e505129..1bd8793 100644 --- a/skf-rs/src/helper/mod.rs +++ b/skf-rs/src/helper/mod.rs @@ -1,6 +1,10 @@ pub mod auth; +pub mod easy; pub mod mem { + use crate::{ECCEncryptedData, EnvelopedKeyData}; + + use skf_api::native::types::ECCPublicKeyBlob; use std::cmp::min; use std::ffi::CStr; use std::slice; @@ -199,9 +203,63 @@ pub mod mem { write_cstr(src, bytes); } + impl ECCEncryptedData { + /// Convert to bytes of `ECCCipherBlob` + pub fn blob_bytes(&self) -> Vec { + use skf_api::native::types::ULONG; + + let len = 64 + 64 + 32 + 4 + self.cipher.len(); + let mut vec: Vec = Vec::with_capacity(len); + let cipher_len: [u8; 4] = (self.cipher.len() as ULONG).to_ne_bytes(); + vec.extend_from_slice(&self.ec_x); + vec.extend_from_slice(&self.ec_y); + vec.extend_from_slice(&self.hash); + vec.extend_from_slice(&cipher_len); + vec.extend_from_slice(&self.cipher); + vec + } + } + + impl EnvelopedKeyData { + /// Convert to bytes of `EnvelopedKeyBlob` + pub fn blob_bytes(&self) -> Vec { + use skf_api::native::types::ULONG; + + let cipher_blob = self.ecc_cipher.blob_bytes(); + let len = 4 + 4 + 4 + 64 + std::mem::size_of::() + cipher_blob.len(); + let mut vec: Vec = Vec::with_capacity(len); + + // version + let bytes: [u8; 4] = (self.version as ULONG).to_ne_bytes(); + vec.extend_from_slice(&bytes); + // sym_alg_id + let bytes: [u8; 4] = (self.sym_alg_id as ULONG).to_ne_bytes(); + vec.extend_from_slice(&bytes); + // bits + let bytes: [u8; 4] = (self.bits as ULONG).to_ne_bytes(); + vec.extend_from_slice(&bytes); + // encrypted_pri_key + vec.extend_from_slice(&self.encrypted_pri_key); + + // pub_key.bit_len + let bytes: [u8; 4] = (self.pub_key.bit_len as ULONG).to_ne_bytes(); + vec.extend_from_slice(&bytes); + + // pub_key.x_coordinate + vec.extend_from_slice(&self.pub_key.x_coordinate); + + // pub_key.y_coordinate + vec.extend_from_slice(&self.pub_key.y_coordinate); + + // cipher + vec.extend_from_slice(&cipher_blob); + vec + } + } #[cfg(test)] mod tests { use super::*; + use crate::ECCEncryptedData; #[test] fn parse_terminated_cstr_list_test() { @@ -241,6 +299,29 @@ pub mod mem { } assert_eq!(b"\0", &buffer); } + + #[test] + fn cipher_blob_data_test() { + use skf_api::native::types::ECCCipherBlob; + let data = ECCEncryptedData { + ec_x: [1u8; 64], + ec_y: [2u8; 64], + hash: [3u8; 32], + cipher: vec![1u8, 2u8, 3u8, 4u8, 5u8], + }; + let mem = data.blob_bytes(); + assert_eq!(mem.len(), 64 + 64 + 32 + 4 + 5); + unsafe { + let blob_ptr = mem.as_ptr() as *const ECCCipherBlob; + let blob = &*blob_ptr; + + assert_eq!(blob.x_coordinate, [1u8; 64]); + assert_eq!(blob.y_coordinate, [2u8; 64]); + assert_eq!(blob.hash, [3u8; 32]); + assert_eq!(std::ptr::addr_of!(blob.cipher_len).read_unaligned(), 5); + assert_eq!(blob.cipher, [1u8]); + } + } } } @@ -266,9 +347,3 @@ pub mod param { Ok(value) } } -pub fn describe_result(result: &crate::Result) -> String { - match result.as_ref() { - Ok(_) => "OK".to_string(), - Err(e) => format!("{:?}", e), - } -} diff --git a/skf-rs/src/lib.rs b/skf-rs/src/lib.rs index ff40d49..62a7e60 100644 --- a/skf-rs/src/lib.rs +++ b/skf-rs/src/lib.rs @@ -3,7 +3,8 @@ mod error; pub mod helper; pub mod spec; -use skf_api::native::types::HANDLE; +use skf_api::native::types::{ECCPrivateKeyBlob, ECCPublicKeyBlob, ECCSignatureBlob, HANDLE}; +use std::fmt::Debug; use std::time::Duration; pub type Error = error::Error; @@ -18,50 +19,52 @@ pub struct PluginEvent { pub event: u8, } -impl PluginEvent { - /// The device is plugged in - pub const EVENT_PLUGGED_IN: u8 = 1; +/// 256-bit hash value +pub type HASH256 = [u8; 32]; - /// The device is unplugged - pub const EVENT_UNPLUGGED: u8 = 2; - - pub fn new(device_name: impl Into, event: u8) -> Self { +/// ECC encrypt output,Wrapper of [DST](https://doc.rust-lang.org/reference/dynamically-sized-types.html) `ECCCipherBlob` +#[derive(Debug)] +pub struct ECCEncryptedData { + pub ec_x: [u8; 64], + pub ec_y: [u8; 64], + pub hash: HASH256, + pub cipher: Vec, +} +impl Default for ECCEncryptedData { + fn default() -> Self { Self { - device_name: device_name.into(), - event, + ec_x: [0; 64], + ec_y: [0; 64], + hash: [0; 32], + cipher: vec![], } } +} - pub fn plugged_in(device_name: impl AsRef) -> Self { - Self { - device_name: device_name.as_ref().to_string(), - event: Self::EVENT_PLUGGED_IN, - } - } +/// ECC Enveloped key,Wrapper of [DST](https://doc.rust-lang.org/reference/dynamically-sized-types.html) `EnvelopedKeyBlob` +#[derive(Debug)] +pub struct EnvelopedKeyData { + pub version: u32, + pub sym_alg_id: u32, + pub bits: u32, + pub encrypted_pri_key: [u8; 64], + pub pub_key: ECCPublicKeyBlob, + pub ecc_cipher: ECCEncryptedData, +} - pub fn unplugged(device_name: impl AsRef) -> Self { +impl Default for EnvelopedKeyData { + fn default() -> Self { Self { - device_name: device_name.as_ref().to_string(), - event: Self::EVENT_UNPLUGGED, - } - } - - pub fn is_plugged_in(&self) -> bool { - self.event == Self::EVENT_PLUGGED_IN - } - - pub fn is_unplugged(&self) -> bool { - self.event == Self::EVENT_UNPLUGGED - } - - pub fn event_description(&self) -> &'static str { - match self.event { - Self::EVENT_PLUGGED_IN => "plugged in", - Self::EVENT_UNPLUGGED => "unplugged", - _ => "unknown", + version: 0, + sym_alg_id: 0, + bits: 0, + encrypted_pri_key: [0u8; 64], + pub_key: ECCPublicKeyBlob::default(), + ecc_cipher: ECCEncryptedData::default(), } } } + #[derive(Debug, Default)] pub struct Version { pub major: u8, @@ -126,10 +129,13 @@ pub trait DeviceManager { /// Connect to device by enumerate all devices and select one,if no device matches the selector, None will be returned /// /// [selector] - The device selector,if device list is not empty, the selector will be invoked to select one + /// + /// ## error + /// - `Error::NotFound` returned means there is no device to connect,or the selector returns None fn connect_selected( &self, selector: fn(Vec<&str>) -> Option<&str>, - ) -> Result>>; + ) -> Result>; } pub trait DeviceCtl { @@ -161,7 +167,10 @@ pub trait DeviceCtl { /// /// This function is for testing purpose fn transmit(&self, command: &[u8], recv_capacity: usize) -> Result>; +} +/// Cryptographic services provided by SKF device objects +pub trait DeviceCrypto { /// Generate random data /// /// [len] - The random data length to generate,in bytes @@ -175,6 +184,80 @@ pub trait DeviceCtl { /// ## Owner object lifetime requirement /// If owner object([SkfDevice]) is dropped, the key will be invalid fn set_symmetric_key(&self, alg_id: u32, key: &[u8]) -> Result>; + + /// Encrypt data,using external ecc public key + /// + /// [key] - The public key + /// + /// [data] - The data to encrypt + fn ext_ecc_encrypt(&self, key: &ECCPublicKeyBlob, data: &[u8]) -> Result; + + /// Decrypt data,using external ecc private key + /// + /// [key] - The private key + /// + /// [cipher] - The encrypted data,returned by `ext_ecc_encrypt` + fn ext_ecc_decrypt( + &self, + key: &ECCPrivateKeyBlob, + cipher: &ECCEncryptedData, + ) -> Result>; + + /// Sign data,using external ecc private key + /// + /// [key] - The private key + /// + /// [data] - The data to sign + fn ext_ecc_sign(&self, key: &ECCPrivateKeyBlob, data: &[u8]) -> Result; + + /// Verify signature,using external ecc public key + /// + /// [key] - The public key + /// + /// [data] - The data to verify + /// + /// [signature] - The signature,returned by `ext_ecc_sign` + fn ext_ecc_verify( + &self, + key: &ECCPublicKeyBlob, + data: &[u8], + signature: &ECCSignatureBlob, + ) -> Result<()>; + + /// Verify signature + /// + /// [key] - The public key + /// + /// [hash] - The hash value of data. + /// When using the SM2 algorithm, the data is the result of pre-processing the data to be + /// signed through the SM2 signature pre-processing. The pre-processing procedure follows `GM/T 0009`. + /// + /// [signature] - The signature,returned by `ext_ecc_sign` + fn ecc_verify( + &self, + key: &ECCPublicKeyBlob, + hash: &[u8], + signature: &ECCSignatureBlob, + ) -> Result<()>; + + /// Key exchange step: generate session key for initiator + /// + /// see [SKF_GenerateKeyWithECC] for more details + /// + /// [agreement_key] - The agreement key,returned by `SkfContainer::sk_gen_agreement_data` + /// + /// [responder_key] - The responder's public key + /// + /// [responder_tmp_key] - The responder's temporary public key,returned by `SkfContainer::sk_gen_agreement_data_and_key` + /// + /// [responder_id] - Responder's ID,max 32 bytes + fn ecc_gen_session_key( + &self, + agreement_key: &dyn ManagedKey, + responder_key: &ECCPublicKeyBlob, + responder_tmp_key: &ECCPublicKeyBlob, + responder_id: &[u8], + ) -> Result>; } #[derive(Debug, Default)] @@ -186,6 +269,7 @@ pub struct AppAttr { pub create_file_rights: u32, } +/// Application management pub trait AppManager { /// Enumerate all apps in the device,return app names fn enumerate_app_name(&self) -> Result>; @@ -210,7 +294,9 @@ pub trait AppManager { /// [name] - The app name to delete fn delete_app(&self, name: &str) -> Result<()>; } -pub trait DeviceSecurity { + +/// Device authentication +pub trait DeviceAuth { /// Device authentication /// /// [data] - The authentication data @@ -231,8 +317,8 @@ pub trait DeviceSecurity { /// Represents a device instance,call `DeviceManager::connect()` or `DeviceManager::connect_selected()` to get one /// ## Disconnect /// Device instance is disconnected when `Drop` -pub trait SkfDevice: DeviceCtl + DeviceSecurity + AppManager { - /// get block cipher service +pub trait SkfDevice: DeviceCtl + DeviceAuth + AppManager + DeviceCrypto { + /// Block cipher service fn block_cipher(&self) -> Result>; } @@ -249,6 +335,7 @@ pub struct PinInfo { pub remain_retry_count: u32, pub default_pin: bool, } + pub trait AppSecurity { /// Lock device for exclusive access /// @@ -421,8 +508,105 @@ pub trait SkfContainer { /// /// [signer] - True means The exported certificate is used for sign fn export_certificate(&self, signer: bool) -> Result>; + + /// Generate ECC key pair(signing part),the private key will be stored in the container. + /// + /// see [SKF_GenECCKeyPair] for more details + /// + /// [alg_id] - The algorithm id, supported values is `SGD_SM2_1` + fn ecc_gen_key_pair(&self, alg_id: u32) -> Result; + + /// Import ECC key pair( encryption part) to container. + /// + /// see [SKF_ImportECCKeyPair] for more details + /// + /// [enveloped_key] - The enveloped key data + /// + /// ## permission state requirement + /// user permission + fn ecc_import_key_pair(&self, enveloped_key: &EnvelopedKeyData) -> Result<()>; + + /// Export ECC public key from container. + /// + /// see [SKF_ExportPublicKey] for more details + /// + /// [sign_part] - True means The exported public key is used for sign + fn ecc_export_public_key(&self, sign_part: bool) -> Result>; + + /// Sign data use signing key in the container + /// + /// see [SKF_ECCSignData] for more details + /// + /// [hash] - The hash value of data. + /// When using the SM2 algorithm, the data is the result of pre-processing the data to be + /// signed through the SM2 signature pre-processing. The pre-processing procedure follows `GM/T 0009`. + fn ecc_sign(&self, hash: &[u8]) -> Result; + + /// Key exchange step: generate ephemeral public key and agreement key for initiator + /// + /// see [SKF_GenerateAgreementDataWithECC] for more details + /// + /// [alg_id] - The algorithm id used for session key generation + /// + /// [id] - Initiator's ID,max 32 bytes + /// + /// ## Return value + /// return ephemeral public key and key agreement handle + fn sk_gen_agreement_data( + &self, + alg_id: u32, + id: &[u8], + ) -> Result<(ECCPublicKeyBlob, Box)>; + + /// Key exchange step: generate ephemeral public key and session key for responder + /// + /// see [SKF_GenerateAgreementDataAndKeyWithECC] for more details + /// + /// [alg_id] - The algorithm id used for session key generation + /// + /// [initiator_key] - Initiator's public key + /// + /// [initiator_tmp_key] - Initiator's ephemeral public key + /// + /// [initiator_id] - Initiator's ID,max 32 bytes + /// + /// [responder_id] - Responder's ID,max 32 bytes + /// + /// ## Return value + /// return ephemeral public key and session key handle + fn sk_gen_agreement_data_and_key( + &self, + alg_id: u32, + initiator_key: &ECCPublicKeyBlob, + initiator_tmp_key: &ECCPublicKeyBlob, + initiator_id: &[u8], + responder_id: &[u8], + ) -> Result<(ECCPublicKeyBlob, Box)>; + + /// Import session key + /// + /// see [SKF_ImportSessionKey] for more details + /// + /// [alg_id] - The algorithm id + /// + /// [key_data] - The session key data + fn sk_import(&self, alg_id: u32, key_data: &[u8]) -> Result>; + + /// Generate session key and export it + /// + /// [alg_id] - The algorithm id used for session key generation + /// + /// [key] - The public key,used for encrypt session key + fn sk_export( + &self, + alg_id: u32, + key: &ECCPublicKeyBlob, + ) -> Result<(Box, ECCEncryptedData)>; } +pub type Hash256 = [u8; 32]; + +/// Block cipher parameter,wrapper of `BlockCipherParam` #[derive(Debug, Default)] pub struct BlockCipherParameter { /// IV data,max 32 bytes,Empty means no IV diff --git a/skf-rs/src/spec/mod.rs b/skf-rs/src/spec/mod.rs index b6a3126..844cad0 100644 --- a/skf-rs/src/spec/mod.rs +++ b/skf-rs/src/spec/mod.rs @@ -41,12 +41,17 @@ pub mod algorithm { pub const SGD_RSA: u32 = 0x00010000; + /// SM2 ECC pub const SGD_SM2: u32 = 0x00020100; + /// SM2 ECC(Sign) + pub const SGD_SM2_1: u32 = 0x00020200; + /// SM2 ECC(key exchange) pub const SGD_SM2_2: u32 = 0x00020400; + /// SM2 ECC(encryption) pub const SGD_SM2_3: u32 = 0x00020800; pub const SGD_SM3: u32 = 0x00000001; diff --git a/skf-rs/tests/app_test.rs b/skf-rs/tests/app_test.rs index 8233d1a..b94e99a 100644 --- a/skf-rs/tests/app_test.rs +++ b/skf-rs/tests/app_test.rs @@ -1,10 +1,9 @@ mod common; use crate::common::{ - get_or_create_test_app_1, get_or_create_test_container_1, verify_admin_pin, verify_user_pin, - TEST_ADMIN_PIN, TEST_FILE_NAME_1, TEST_USER_PIN, + describe_result, get_or_create_test_app_1, verify_admin_pin, verify_user_pin, TEST_ADMIN_PIN, + TEST_FILE_NAME_1, TEST_USER_PIN, }; -use skf_rs::helper::describe_result; use skf_rs::{FileAttr, FILE_PERM_EVERYONE, PIN_TYPE_USER}; #[test] @@ -79,28 +78,6 @@ fn container_ctl_test() { println!("invoke delete_container result: {:?}", &ret); } -#[test] -#[ignore] -fn invoke_container_fn() { - let (_dev, _app, container) = get_or_create_test_container_1(); - - let ret = container.get_type(); - println!("invoke get_type result: {:?}", &ret); - - // fake cert data - let ret = container.import_certificate(true, &[0u8; 256]); - println!( - "invoke import_certificate result: {:?}", - describe_result(&ret) - ); - - let ret = container.export_certificate(true); - println!( - "invoke export_certificate result: {:?}", - describe_result(&ret) - ); -} - #[test] #[ignore] fn file_ctl_test() { diff --git a/skf-rs/tests/block_cipher_test.rs b/skf-rs/tests/block_cipher_test.rs index 09a34ed..9636f83 100644 --- a/skf-rs/tests/block_cipher_test.rs +++ b/skf-rs/tests/block_cipher_test.rs @@ -1,6 +1,5 @@ mod common; -use crate::common::{use_block_cipher, use_first_device}; -use skf_rs::helper::describe_result; +use crate::common::{describe_result, use_block_cipher, use_first_device}; use skf_rs::spec::algorithm; use skf_rs::BlockCipherParameter; diff --git a/skf-rs/tests/common/mod.rs b/skf-rs/tests/common/mod.rs index 88a669c..e3435eb 100644 --- a/skf-rs/tests/common/mod.rs +++ b/skf-rs/tests/common/mod.rs @@ -1,3 +1,4 @@ +use skf_api::native::types::{ECCPrivateKeyBlob, ECCPublicKeyBlob}; use skf_rs::helper::auth::encrypt_auth_key_sm1_ecb; use skf_rs::{ AppAttr, DeviceManager, Engine, LibLoader, Result, SkfApp, SkfBlockCipher, SkfContainer, @@ -12,6 +13,15 @@ pub const TEST_CONTAINER_NAME_1: &str = "skf-rs-test-container-1"; pub const TEST_AUTH_KEY: [u8; 16] = [ 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, ]; +pub const SK_INITIATOR_ID: [u8; 32] = [1u8; 32]; +pub const SK_RESPONDER_ID: [u8; 32] = [2u8; 32]; + +pub fn describe_result(result: &Result) -> String { + match result.as_ref() { + Ok(_) => "OK".to_string(), + Err(e) => format!("{:?}", e), + } +} pub fn chose_first(list: Vec<&str>) -> Option<&str> { Some(list[0]) @@ -36,7 +46,6 @@ pub fn use_first_device() -> Box { manager .connect_selected(chose_first) .expect("SKF Device not found") - .unwrap() } pub fn use_first_device_with_auth() -> Box { @@ -105,3 +114,17 @@ pub fn verify_admin_pin(app: &dyn SkfApp) -> Result<()> { pub fn verify_user_pin(app: &dyn SkfApp) -> Result<()> { app.verify_pin(PIN_TYPE_USER, TEST_USER_PIN) } + +pub fn ext_ecc_key_pair() -> (ECCPrivateKeyBlob, ECCPublicKeyBlob) { + let x = + hex::decode("9EF573019D9A03B16B0BE44FC8A5B4E8E098F56034C97B312282DD0B4810AFC3").unwrap(); + let y = + hex::decode("CC759673ED0FC9B9DC7E6FA38F0E2B121E02654BF37EA6B63FAF2A0D6013EADF").unwrap(); + let key = + hex::decode("FAB8BBE670FAE338C9E9382B9FB6485225C11A3ECB84C938F10F20A93B6215F0").unwrap(); + + let public_key_blob = ECCPublicKeyBlob::new_256(&x[..], &y[..]); + let private_key_blob = ECCPrivateKeyBlob::new_256(&key[..]); + + (private_key_blob, public_key_blob) +} diff --git a/skf-rs/tests/container_test.rs b/skf-rs/tests/container_test.rs new file mode 100644 index 0000000..7fdd610 --- /dev/null +++ b/skf-rs/tests/container_test.rs @@ -0,0 +1,74 @@ +use skf_api::native::types::ECCPublicKeyBlob; +use skf_rs::spec::algorithm::SGD_SM4_ECB; +use skf_rs::EnvelopedKeyData; + +use crate::common::{ + describe_result, get_or_create_test_container_1, SK_INITIATOR_ID, SK_RESPONDER_ID, +}; + +mod common; + +#[test] +#[ignore] +fn invoke_container_fn() { + let (_dev, _app, container) = get_or_create_test_container_1(); + + let ret = container.get_type(); + println!("invoke get_type result: {:?}", &ret); + + let ret = container.import_certificate(true, &[0u8; 256]); + println!( + "invoke import_certificate result: {:?}", + describe_result(&ret) + ); + + let ret = container.export_certificate(true); + println!( + "invoke export_certificate result: {:?}", + describe_result(&ret) + ); + let ret = container.ecc_gen_key_pair(SGD_SM4_ECB); + println!( + "invoke ecc_gen_key_pair result: {:?}", + describe_result(&ret) + ); + + let ret = container.ecc_import_key_pair(&EnvelopedKeyData::default()); + println!( + "invoke ecc_import_key_pair result: {:?}", + describe_result(&ret) + ); + + let ret = container.ecc_export_public_key(true); + println!( + "invoke ecc_export_public_key result: {:?}", + describe_result(&ret) + ); + + let ret = container.ecc_sign(&[0u8; 32]); + println!("invoke ecc_sign result: {:?}", describe_result(&ret)); + + let ret = container.sk_gen_agreement_data(SGD_SM4_ECB, &SK_INITIATOR_ID); + println!( + "invoke sk_gen_agreement_data result: {:?}", + describe_result(&ret) + ); + + let ret = container.sk_gen_agreement_data_and_key( + SGD_SM4_ECB, + &ECCPublicKeyBlob::default(), + &ECCPublicKeyBlob::default(), + &SK_INITIATOR_ID, + &SK_RESPONDER_ID, + ); + println!( + "invoke sk_gen_agreement_data_and_key result: {:?}", + describe_result(&ret) + ); + + let ret = container.sk_import(SGD_SM4_ECB, &[0u8; 32]); + println!("invoke sk_import result: {:?}", describe_result(&ret)); + + let ret = container.sk_export(SGD_SM4_ECB, &ECCPublicKeyBlob::default()); + println!("invoke sk_export result: {:?}", describe_result(&ret)); +} diff --git a/skf-rs/tests/device_test.rs b/skf-rs/tests/device_test.rs index f4228fc..acae3a7 100644 --- a/skf-rs/tests/device_test.rs +++ b/skf-rs/tests/device_test.rs @@ -1,9 +1,11 @@ use skf_rs::{AppAttr, FILE_PERM_EVERYONE}; use std::time::{Duration, SystemTime}; mod common; -use crate::common::{use_first_device_with_auth, TEST_ADMIN_PIN, TEST_USER_PIN}; +use crate::common::{ + describe_result, ext_ecc_key_pair, use_first_device_with_auth, TEST_ADMIN_PIN, TEST_USER_PIN, +}; use common::use_first_device; -use skf_rs::helper::describe_result; +use skf_api::native::types::ECCSignatureBlob; #[test] #[ignore] @@ -124,3 +126,63 @@ fn app_ctl_test() { println!("result of delete_app : {:?}", describe_result(&ret)); assert!(ret.is_ok()); } + +#[test] +#[ignore] +fn ext_ecc_crypt_test() { + let (private_key_blob, public_key_blob) = ext_ecc_key_pair(); + let plain = b"Hello World"; + let dev = use_first_device(); + let ret = dev.ext_ecc_encrypt(&public_key_blob, plain); + println!("result of ext_ecc_encrypt : {:?}", &ret); + assert!(ret.is_ok()); + let cipher = ret.unwrap(); + + let ret = dev.ext_ecc_decrypt(&private_key_blob, &cipher); + println!("result of ext_ecc_decrypt : {:?}", &ret); + assert!(ret.is_ok()); + let plain2 = ret.unwrap(); + + assert_eq!(plain, plain2.as_slice()); +} + +#[test] +#[ignore] +fn ext_ecc_sign_test() { + let (private_key_blob, public_key_blob) = ext_ecc_key_pair(); + let plain = b"Hello World"; + let dev = use_first_device(); + let ret = dev.ext_ecc_sign(&private_key_blob, plain); + println!("result of ext_ecc_sign : {:?}", &ret); + assert!(ret.is_ok()); + let sign = ret.unwrap(); + let ret = dev.ext_ecc_verify(&public_key_blob, plain, &sign); + println!("result of ext_ecc_verify : {:?}", &ret); + assert!(ret.is_ok()); +} + +#[test] +#[ignore] +fn invoke_ecc_verify() { + // pre-processed hash + let hash: [u8; 32] = [ + 230, 169, 165, 142, 252, 155, 75, 123, 90, 55, 21, 21, 199, 115, 160, 145, 7, 144, 24, 121, + 81, 131, 170, 91, 103, 104, 107, 132, 242, 188, 185, 164, + ]; + let r: [u8; 64] = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 194, 177, 208, 83, 35, 238, 111, 27, 172, 87, 189, 226, 164, 84, 72, 131, 93, 166, + 39, 192, 55, 165, 54, 205, 190, 89, 100, 208, 106, 76, 203, 243, + ]; + let s: [u8; 64] = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 216, 104, 162, 234, 63, 236, 72, 26, 38, 6, 43, 80, 163, 104, 146, 175, 120, 57, 171, + 156, 6, 57, 201, 6, 250, 40, 231, 56, 204, 243, 49, 234, + ]; + let sign = ECCSignatureBlob { r, s }; + let (_private_key_blob, public_key_blob) = ext_ecc_key_pair(); + let dev = use_first_device(); + + let ret = dev.ecc_verify(&public_key_blob, &hash, &sign); + println!("result of ecc_verify : {:?}", &ret); +} diff --git a/skf-rs/tests/ctl_test.rs b/skf-rs/tests/mgr_test.rs similarity index 97% rename from skf-rs/tests/ctl_test.rs rename to skf-rs/tests/mgr_test.rs index 32f85fc..ba531b5 100644 --- a/skf-rs/tests/ctl_test.rs +++ b/skf-rs/tests/mgr_test.rs @@ -3,8 +3,8 @@ use std::sync::Arc; use std::thread; mod common; +use crate::common::describe_result; use common::chose_first; -use skf_rs::helper::describe_result; #[test] #[ignore]