use std::{ffi::CString, ptr}; use thiserror::Error; use fsv_sys::{ get_library_path, Error as FsvError, SSVLibrary, SSVLibraryCommon, SupportedFsvVersion, V1_40_13, V1_40_14 }; mod errors_ssv; use errors_ssv::SSVErrorCodes; #[derive(Error, Debug)] pub enum Error { #[error(transparent)] FSVSysLibrary(#[from] FsvError), #[error(transparent)] SSVError(#[from] SSVErrorCodes), } /// Enum to hold the different versions of the SSV library enum SsvLibraryVersion { V1_40_13(SSVLibrary), V1_40_14(SSVLibrary), } /// Struct to hold the SSV library and access its functions pub struct SSV { library: SsvLibraryVersion, } impl SSV { fn new(version: SupportedFsvVersion) -> Result { let library = match version { SupportedFsvVersion::V1_40_13 => { let lib_path = get_library_path(&version); let library = SSVLibrary::::new(&lib_path)?; SsvLibraryVersion::V1_40_13(library) }, SupportedFsvVersion::V1_40_14 => { let lib_path = get_library_path(&version); let library = SSVLibrary::::new(&lib_path)?; SsvLibraryVersion::V1_40_14(library) }, }; Ok(Self { library, }) } /// # Initialize the SSV library /// Implement: SSV_InitLIB2 pub fn init_library(&self, sesam_ini_path: &str) -> Result<(), Error> { let sesam_ini_path = CString::new(sesam_ini_path).expect("CString::new failed"); let result = match &self.library { SsvLibraryVersion::V1_40_13(library) => { unsafe { library.ssv_init_lib2(sesam_ini_path.as_ptr()) }? }, SsvLibraryVersion::V1_40_14(library) => { unsafe { library.ssv_init_lib2(sesam_ini_path.as_ptr()) }? }, }; if result != 0 { let error = SSVErrorCodes::from(result); return Err(Error::SSVError(error)); } Ok(()) } /// # Read the CPS card /// Implement: SSV_LireCartePS pub fn read_professional_card(&self, pin_code: &str) -> Result<(), Error> { let pcsc_reader_name = "Gemalto PC Twin Reader (645D94C3) 00 00"; let pin_code = CString::new(pin_code).expect("CString::new failed"); let pcsc_reader_name = CString::new(pcsc_reader_name).expect("CString::new failed"); let mut out_buffer_ptr: *mut libc::c_void = ptr::null_mut(); let mut out_buffer_size: libc::size_t = 0; let result = match &self.library { SsvLibraryVersion::V1_40_13(library) => { unsafe { library.ssv_lire_carte_ps( pcsc_reader_name.as_ptr(), pcsc_reader_name.as_ptr(), pin_code.as_ptr(), &mut out_buffer_ptr, &mut out_buffer_size) }? }, SsvLibraryVersion::V1_40_14(library) => { unsafe { library.ssv_lire_carte_ps( pcsc_reader_name.as_ptr(), pcsc_reader_name.as_ptr(), pin_code.as_ptr(), &mut out_buffer_ptr, &mut out_buffer_size) }? }, }; if result != 0 { // Free memory unsafe { libc::free(out_buffer_ptr) }; let error = SSVErrorCodes::from(result); return Err(Error::SSVError(error)); } // Print 10 bytes of the buffer let buffer = unsafe { std::slice::from_raw_parts(out_buffer_ptr as *const u8, 10) }; println!("{:?}", buffer); // Free memory unsafe { libc::free(out_buffer_ptr) }; Ok(()) } } #[cfg(test)] mod tests { use std::env; use utils::config::load_config; use anyhow::Result; use super::*; mod setup { use super::*; pub fn init() -> Result { load_config().unwrap(); let sesam_ini_path = env::var("SESAM_INI_PATH").expect("SESAM_INI_PATH must be set"); let lib = SSV::new(SupportedFsvVersion::V1_40_13)?; lib.init_library(&sesam_ini_path)?; Ok(lib) } } #[test] fn test_init_library() -> Result<()> { setup::init()?; Ok(()) } #[test] fn test_read_professional_card_good_pin() -> Result<()> { let lib = setup::init()?; let pin_code = "1234"; lib.read_professional_card(pin_code)?; Ok(()) } #[ignore] #[test] fn test_read_professional_card_bad_pin() -> Result<()> { let lib = setup::init()?; let pin_code = "0000"; // Should return an error let err = lib.read_professional_card(pin_code).unwrap_err(); assert_eq!(err.to_string(), "Le code porteur présenté est erroné."); match err { Error::SSVError(err) => { assert_eq!(err as SSVErrorCodes, SSVErrorCodes::CPSPinWrong); }, _ => panic!("Error type is not SSVError"), } Ok(()) } #[test] fn test_ssv() { assert_eq!("ssv", "ssv"); } }