diff --git a/crates/fsv/src/fsv_parsing/blocks.rs b/crates/fsv/src/fsv_parsing/blocks.rs index fb47a70..46af3e8 100644 --- a/crates/fsv/src/fsv_parsing/blocks.rs +++ b/crates/fsv/src/fsv_parsing/blocks.rs @@ -54,6 +54,8 @@ pub struct GroupId( #[deku(ctx = "group_id: u16", id = "group_id")] #[allow(non_camel_case_types)] pub enum DataGroup { + #[deku(id = 1)] + LireCartePS_Group1_Holder(groups::ssv_lire_carte_ps::group_1_holder::Holder), #[deku(id = 60)] LireConfig_Group60_ConfigHeader(groups::ssv_lire_config::group_60_header_config::ConfigHeader), #[deku(id = 61)] diff --git a/crates/fsv/src/fsv_parsing/groups/mod.rs b/crates/fsv/src/fsv_parsing/groups/mod.rs index b564ac8..03e1fbb 100644 --- a/crates/fsv/src/fsv_parsing/groups/mod.rs +++ b/crates/fsv/src/fsv_parsing/groups/mod.rs @@ -4,6 +4,7 @@ use deku::{deku_derive, DekuError}; use super::{ size_read, map_bytes_to_lossy_string }; +pub mod ssv_lire_carte_ps; pub mod ssv_lire_config; /// # Convert a DataField to a specific type @@ -43,6 +44,11 @@ pub struct NumericString( #[deku(map = "map_from_data_field")] String ); +impl From<&str> for NumericString { + fn from(s: &str) -> Self { + NumericString(s.to_string()) + } +} #[deku_derive(DekuRead)] #[derive(Debug, Clone, PartialEq)] diff --git a/crates/fsv/src/fsv_parsing/groups/ssv_lire_carte_ps.rs b/crates/fsv/src/fsv_parsing/groups/ssv_lire_carte_ps.rs new file mode 100644 index 0000000..5c45c26 --- /dev/null +++ b/crates/fsv/src/fsv_parsing/groups/ssv_lire_carte_ps.rs @@ -0,0 +1,145 @@ +//! # Structures de parsing des données de la fonction SSV_LireCartePS + +use deku::deku_derive; + +use crate::fsv_parsing::groups::NumericString; + +/// # Titulaire +/// 1 occurence +pub mod group_1_holder { + + use crate::fsv_parsing::groups::AlphaNumericString; + + use super::*; + + /// Groupe 1 - Titulaire + #[deku_derive(DekuRead)] + #[derive(Debug, PartialEq)] + pub struct Holder { + pub card_type: CardPSType, + pub national_id_type: NationalIDType, + pub national_id: AlphaNumericString, // /!\ CE and not CA + pub national_id_key: AlphaNumericString, + pub civility_code: CivilityCode, + pub holder_lastname: AlphaNumericString, // /!\ CE and not CA + pub holder_firstname: AlphaNumericString, // /!\ CE and not CA + pub category_card: AlphaNumericString, + } + + // Fields + #[deku_derive(DekuRead)] + #[derive(Debug, PartialEq)] + pub struct CardPSType(pub NumericString); + + #[deku_derive(DekuRead)] + #[derive(Debug, PartialEq)] + pub struct NationalIDType(pub NumericString); + + #[deku_derive(DekuRead)] + #[derive(Debug, PartialEq)] + pub struct CivilityCode(pub NumericString); + + +} + +#[cfg(test)] +mod tests { + use deku::DekuContainerRead as _; + + use crate::fsv_parsing::blocks::BlockHeader; + + use super::*; + + mod data { + pub const BUFFER: &[u8] = &[ + 0, 1, // Block ID + 53, // Block size + 1, // Type de carte PS, 2 CN + 48, // 0 + 1, // Type d'identification nationale, 1 CN + 56, // 8 + 11, // N° d'identification nationale, 8-30 CE + 57, 57, 55, 48, 48, 53, 57, 51, 54, 56, 54, + 1, // Clé du N° d'identification nationale, 1 CN + 54, // 6 + 2, // Code civilité, 2 CN + 51, 49, // 31 + 23, // Nom du PS, 27 CE + 80, 72, 65, 82, 77, 79, 70, 70, 73, 67, + 69, 32, 82, 80, 80, 83, 48, 48, 53, 57, + 51, 54, 56, + 7, // Prénom du PS, 27 CE + 71, 73, 76, 66, 69, 82, 84, + // ??? Missing ??? Catégorie Carte, 1 CA + 0, 2, // Block ID + 93, // Block size + 1, // N° logique de la situation de facturation du PS, 1 CB + 1, + 1, // Mode d'exercice, 2 CN + 48, + 1, // Statut d'exercice, 3 CN + 49, + 2, // Secteur d'activité, 3 CN + 56, 54, + 1, // Type d'identification structure, 1 CN + 49, + 9, // N° d'identification structure, 14 CA + 48, 66, 48, 50, 52, 54, 50, 56, 54, + 1, // Clé du N° d'identification structure, 1 CN + 54, + 34, // Raison sociale structure, 40 CE + 80, 72, 65, 82, 77, 65, 67, 73, 69, 32, + 68, 69, 32, 76, 65, 32, 71, 65, 82, 69, + 32, 82, 79, 85, 84, 73, 69, 82, 69, 50, + 52, 54, 50, 56, + 8, // N° d'identification de facturation du PS, 8 CN + 48, 48, 50, 48, 57, 51, 54, 56, + 1, // Clé du N° d'identification de facturation du PS, 1 CN + 48, + 0, // N° d'identification du PS remplaçant, 30 CA + 1, // Clé du N° d'identification du PS remplaçant, 1 CN + 48, + 1, // Code conventionnel, 1 CN + 49, + 2, // Code spécialité, 2 CN + 53, 48, + 2, // Code zone tarifaire, 2 CN + 49, 48, + 2, // Code zone IK + 48, 48, + 1, // Code agrément 1, 1 CN + 48, + 1, // Code agrément 2, 1 CN + 48, + 1, // Code agrément 3, 1 CN + 48, + 1, // Habilitation à signer une Facture // 1 CN + 49, + 1, // Habilitation à signer un lot // 1 CN + 49 + ]; + } + + #[test] + fn test_lire_carte_ps_first_header() { + // env_logger::init(); // Uncomment and run with RUST_LOG=trace for deku debugging + let ((_rest, _offset), block_header) = BlockHeader::from_bytes((data::BUFFER, 0)).unwrap(); + assert_eq!(block_header.group_id.0, 1, "Header ID"); + assert_eq!(block_header.data_size, 53, "Header Size"); + } + + #[test] + fn test_group_1_holder() { + env_logger::init(); // Uncomment and run with RUST_LOG=trace for deku debugging + let offset = 3*8; + let (_rest, holder) = group_1_holder::Holder::from_bytes((data::BUFFER, offset)).unwrap(); + assert_eq!(holder.card_type.0.0, "0", "Card type"); + assert_eq!(holder.national_id_type.0.0, "8", "National ID type"); + assert_eq!(holder.national_id.0, "99700593686", "National Id"); + assert_eq!(holder.national_id_key.0, "6", "National ID Key"); + assert_eq!(holder.civility_code.0.0, "31", "Civility Code"); + assert_eq!(holder.holder_lastname.0, "PHARMOFFICE RPPS0059368", "Holder Lastname"); + assert_eq!(holder.holder_firstname.0, "GILBERT", "Holder Firstname"); + assert!(holder.category_card.0.is_empty(), "Category card"); + } +} \ No newline at end of file diff --git a/crates/fsv/src/fsv_parsing/groups/ssv_lire_config.rs b/crates/fsv/src/fsv_parsing/groups/ssv_lire_config.rs index 5619dc6..7296b49 100644 --- a/crates/fsv/src/fsv_parsing/groups/ssv_lire_config.rs +++ b/crates/fsv/src/fsv_parsing/groups/ssv_lire_config.rs @@ -209,7 +209,7 @@ mod tests { let ((_rest, _offset), block_header) = BlockHeader::from_bytes((buffer, offset)).unwrap(); assert_eq!(block_header.group_id.0, 60, "Header ID"); - // assert_eq!(block_header.data_size, 15, "Header Size"); + assert_eq!(block_header.data_size, 15, "Header Size"); } #[test] @@ -289,6 +289,7 @@ mod tests { assert_eq!(content.name.0.0, "Gemalto PC Twin Reader (645D94C3) 00 00", "Reader Name"); assert_eq!(content.card_type.0.0, "2", "Card Type"); }, + _ => panic!("Unexpected data block type"), } } } diff --git a/crates/fsv/src/ssv/mod.rs b/crates/fsv/src/ssv/mod.rs index 375327c..c087647 100644 --- a/crates/fsv/src/ssv/mod.rs +++ b/crates/fsv/src/ssv/mod.rs @@ -76,7 +76,7 @@ impl SSV { /// # Read the CPS card /// Implement: SSV_LireCartePS - pub fn read_professional_card(&self, pin_code: &str) -> Result<(), Error> { + pub fn read_professional_card(&self, pin_code: &str) -> Result { let pcsc_reader_name = "Gemalto PC Twin Reader (645D94C3) 00 00"; let pin_code = CString::new(pin_code).expect("CString::new failed"); @@ -111,12 +111,13 @@ impl SSV { 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); + // Parse the buffer into a Data struct + let buffer = unsafe { std::slice::from_raw_parts(out_buffer_ptr as *const u8, out_buffer_size) }; + let (_rest, cps_blocks) = Data::from_bytes((buffer, 0)).unwrap(); + // Free memory unsafe { libc::free(out_buffer_ptr) }; - Ok(()) + Ok(cps_blocks) } /// # Get the configuration of the SSV library