feat: raw (non-deserialized) implementation of the 01 group of LireCartePS

This commit is contained in:
Florian Briand 2024-10-05 11:39:51 +02:00
parent 0ef8ede764
commit 71f9d0c5f8
Signed by: florian_briand
GPG Key ID: CC981B9E6B98E70B
5 changed files with 161 additions and 6 deletions

View File

@ -54,6 +54,8 @@ pub struct GroupId(
#[deku(ctx = "group_id: u16", id = "group_id")] #[deku(ctx = "group_id: u16", id = "group_id")]
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
pub enum DataGroup { pub enum DataGroup {
#[deku(id = 1)]
LireCartePS_Group1_Holder(groups::ssv_lire_carte_ps::group_1_holder::Holder),
#[deku(id = 60)] #[deku(id = 60)]
LireConfig_Group60_ConfigHeader(groups::ssv_lire_config::group_60_header_config::ConfigHeader), LireConfig_Group60_ConfigHeader(groups::ssv_lire_config::group_60_header_config::ConfigHeader),
#[deku(id = 61)] #[deku(id = 61)]

View File

@ -4,6 +4,7 @@ use deku::{deku_derive, DekuError};
use super::{ size_read, map_bytes_to_lossy_string }; use super::{ size_read, map_bytes_to_lossy_string };
pub mod ssv_lire_carte_ps;
pub mod ssv_lire_config; pub mod ssv_lire_config;
/// # Convert a DataField to a specific type /// # Convert a DataField to a specific type
@ -43,6 +44,11 @@ pub struct NumericString(
#[deku(map = "map_from_data_field")] #[deku(map = "map_from_data_field")]
String String
); );
impl From<&str> for NumericString {
fn from(s: &str) -> Self {
NumericString(s.to_string())
}
}
#[deku_derive(DekuRead)] #[deku_derive(DekuRead)]
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]

View File

@ -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");
}
}

View File

@ -209,7 +209,7 @@ mod tests {
let ((_rest, _offset), block_header) = BlockHeader::from_bytes((buffer, offset)).unwrap(); 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.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] #[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.name.0.0, "Gemalto PC Twin Reader (645D94C3) 00 00", "Reader Name");
assert_eq!(content.card_type.0.0, "2", "Card Type"); assert_eq!(content.card_type.0.0, "2", "Card Type");
}, },
_ => panic!("Unexpected data block type"),
} }
} }
} }

View File

@ -76,7 +76,7 @@ impl SSV {
/// # Read the CPS card /// # Read the CPS card
/// Implement: SSV_LireCartePS /// Implement: SSV_LireCartePS
pub fn read_professional_card(&self, pin_code: &str) -> Result<(), Error> { pub fn read_professional_card(&self, pin_code: &str) -> Result<Data, Error> {
let pcsc_reader_name = "Gemalto PC Twin Reader (645D94C3) 00 00"; let pcsc_reader_name = "Gemalto PC Twin Reader (645D94C3) 00 00";
let pin_code = CString::new(pin_code).expect("CString::new failed"); let pin_code = CString::new(pin_code).expect("CString::new failed");
@ -111,12 +111,13 @@ impl SSV {
let error = SSVErrorCodes::from(result); let error = SSVErrorCodes::from(result);
return Err(Error::SSVError(error)); return Err(Error::SSVError(error));
} }
// Print 10 bytes of the buffer // Parse the buffer into a Data struct
let buffer = unsafe { std::slice::from_raw_parts(out_buffer_ptr as *const u8, 10) }; let buffer = unsafe { std::slice::from_raw_parts(out_buffer_ptr as *const u8, out_buffer_size) };
println!("{:?}", buffer); let (_rest, cps_blocks) = Data::from_bytes((buffer, 0)).unwrap();
// Free memory // Free memory
unsafe { libc::free(out_buffer_ptr) }; unsafe { libc::free(out_buffer_ptr) };
Ok(()) Ok(cps_blocks)
} }
/// # Get the configuration of the SSV library /// # Get the configuration of the SSV library