Compare commits

..

12 Commits

Author SHA1 Message Date
4627f9540a Merge pull request 'Ajout de la lecture de carte CPS dans le moteur SESAM-Vitale' (#29) from feature_ssv_lire_carte_ps into main
Reviewed-on: #29
Reviewed-by: theo <theo.lettermann@gmail.com>
2024-08-03 14:57:57 +02:00
83aef34750
fix: handle multiple situations on a CPS 2024-08-02 23:02:44 +02:00
9126d1311b
feat: add a field.id 2024-08-02 23:02:21 +02:00
4d004afc5e
feature: handle some errors in decode_carte_ps 2024-08-02 23:00:44 +02:00
723c06acd9
refactor: clean comments and docstring 2024-08-02 22:58:32 +02:00
e9ef6cbb4b
feat: cleaning of all the ssv_memory and ssvlib_demo to get a cleaner cps::lire_carte API 2024-08-02 00:08:49 +02:00
lienjukaisim
0be0b08f89
feat: implement some more structured version of the memory decoding and the mapping to CPS fields 2024-08-01 23:20:11 +02:00
13c2f7573b
feat: implement block and fields as Struct implementing From trait 2024-08-01 23:20:11 +02:00
4def46745d
feat: add read_element function for raw bloc or field parsing 2024-08-01 23:19:22 +02:00
2e07f0b7d1
feat: add a function to read a "bloc / field size" in SSV memory 2024-08-01 23:19:22 +02:00
lienjukaisim
d65c869949
feat: add the structure to reprensent CPS fields returned by the lire_carte_ps function 2024-08-01 23:19:22 +02:00
f799f471bc
feat: Implement a first version of the decode_zone_memoire function 2024-08-01 23:18:42 +02:00
6 changed files with 730 additions and 62 deletions

View File

@ -0,0 +1,408 @@
use libc::{c_void, size_t};
use std::ffi::CString;
use std::ptr;
use crate::libssv::SSV_LireCartePS;
use crate::ssv_memory::{decode_ssv_memory, Block};
#[derive(Debug, Default)]
pub struct CartePS {
titulaire: TitulairePS,
situations: Vec<SituationPS>,
}
// 1. CB = Caractères Binaires »
// 2. CE = Caractères « Etendus » (ISO 8859-1)
// 3. CA = Caractères Alphanumériques (ASCII?)
// 4. CN = Caractères Numériques
#[derive(Debug, Default)]
struct TitulairePS {
type_de_carte_ps: String, // CN
type_d_identification_nationale: String, // CN
numero_d_identification_nationale: String, // CE - 8 -> 30
cle_du_numero_d_identification_nationale: String, // CN
code_civilite: String, // CN
nom_du_ps: String, // CE - 27
prenom_du_ps: String, // CE - 27
categorie_carte: char, // CA
}
#[derive(Debug, Default)]
struct SituationPS {
numero_logique_de_la_situation_de_facturation_du_ps: u8,
mode_d_exercice: String,
statut_d_exercice: String,
secteur_d_activite: String,
type_d_identification_structure: String,
numero_d_identification_structure: String,
cle_du_numero_d_identification_structure: String,
raison_sociale_structure: String,
numero_d_identification_de_facturation_du_ps: String,
cle_du_numero_d_identification_de_facturation_du_ps: String,
numero_d_identification_du_ps_remplaçant: String,
cle_du_numero_d_identification_du_ps_remplaçant: String,
code_conventionnel: String,
code_specialite: String,
code_zone_tarifaire: String,
code_zone_ik: String,
code_agrement_1: String,
code_agrement_2: String,
code_agrement_3: String,
habilitation_à_signer_une_facture: String,
habilitation_à_signer_un_lot: String,
}
pub fn lire_carte(code_pin: &str, lecteur: &str) -> Result<CartePS, String> {
let resource_ps = CString::new(lecteur).expect("CString::new failed");
let resource_reader = CString::new("").expect("CString::new failed");
let card_number = CString::new(code_pin).expect("CString::new failed");
let mut buffer: *mut c_void = ptr::null_mut();
let mut size: size_t = 0;
let mut hex_values: &[u8] = &[];
unsafe {
let result = SSV_LireCartePS(
resource_ps.as_ptr(),
resource_reader.as_ptr(),
card_number.as_ptr(),
&mut buffer,
&mut size,
);
println!("SSV_LireCartePS result: {}", result);
if !buffer.is_null() {
hex_values = std::slice::from_raw_parts(buffer as *const u8, size);
libc::free(buffer);
}
}
let groups = decode_ssv_memory(hex_values, hex_values.len());
decode_carte_ps(groups)
}
fn decode_carte_ps(groups: Vec<Block>) -> Result<CartePS, String> {
let mut carte_ps = CartePS::default();
for group in groups {
for field in group.content {
match (group.id, field.id) {
(1, 1) => {
carte_ps.titulaire.type_de_carte_ps =
String::from_utf8_lossy(field.content).to_string();
}
(1, 2) => {
carte_ps.titulaire.type_d_identification_nationale =
String::from_utf8_lossy(field.content).to_string();
}
(1, 3) => {
carte_ps.titulaire.numero_d_identification_nationale =
String::from_utf8_lossy(field.content).to_string();
}
(1, 4) => {
carte_ps.titulaire.cle_du_numero_d_identification_nationale =
String::from_utf8_lossy(field.content).to_string();
}
(1, 5) => {
carte_ps.titulaire.code_civilite =
String::from_utf8_lossy(field.content).to_string();
}
(1, 6) => {
carte_ps.titulaire.nom_du_ps =
String::from_utf8_lossy(field.content).to_string();
}
(1, 7) => {
carte_ps.titulaire.prenom_du_ps =
String::from_utf8_lossy(field.content).to_string();
}
(1, 8) => {
let byte = field.content[0];
carte_ps.titulaire.categorie_carte = byte as char;
}
(2..=16, 1) => {
carte_ps.situations.push(SituationPS::default());
carte_ps
.situations
.last_mut()
.unwrap()
.numero_logique_de_la_situation_de_facturation_du_ps = field.content[0];
}
(2..=16, 2) => {
carte_ps.situations.last_mut().unwrap().mode_d_exercice =
String::from_utf8_lossy(field.content).to_string();
}
(2..=16, 3) => {
carte_ps.situations.last_mut().unwrap().statut_d_exercice =
String::from_utf8_lossy(field.content).to_string();
}
(2..=16, 4) => {
carte_ps.situations.last_mut().unwrap().secteur_d_activite =
String::from_utf8_lossy(field.content).to_string();
}
(2..=16, 5) => {
carte_ps
.situations
.last_mut()
.unwrap()
.type_d_identification_structure =
String::from_utf8_lossy(field.content).to_string();
}
(2..=16, 6) => {
carte_ps
.situations
.last_mut()
.unwrap()
.numero_d_identification_structure =
String::from_utf8_lossy(field.content).to_string();
}
(2..=16, 7) => {
carte_ps
.situations
.last_mut()
.unwrap()
.cle_du_numero_d_identification_structure =
String::from_utf8_lossy(field.content).to_string();
}
(2..=16, 8) => {
carte_ps
.situations
.last_mut()
.unwrap()
.raison_sociale_structure =
String::from_utf8_lossy(field.content).to_string();
}
(2..=16, 9) => {
carte_ps
.situations
.last_mut()
.unwrap()
.numero_d_identification_de_facturation_du_ps =
String::from_utf8_lossy(field.content).to_string();
}
(2..=16, 10) => {
carte_ps
.situations
.last_mut()
.unwrap()
.cle_du_numero_d_identification_de_facturation_du_ps =
String::from_utf8_lossy(field.content).to_string();
}
(2..=16, 11) => {
carte_ps
.situations
.last_mut()
.unwrap()
.numero_d_identification_du_ps_remplaçant =
String::from_utf8_lossy(field.content).to_string();
}
(2..=16, 12) => {
carte_ps
.situations
.last_mut()
.unwrap()
.cle_du_numero_d_identification_du_ps_remplaçant =
String::from_utf8_lossy(field.content).to_string();
}
(2..=16, 13) => {
carte_ps.situations.last_mut().unwrap().code_conventionnel =
String::from_utf8_lossy(field.content).to_string();
}
(2..=16, 14) => {
carte_ps.situations.last_mut().unwrap().code_specialite =
String::from_utf8_lossy(field.content).to_string();
}
(2..=16, 15) => {
carte_ps.situations.last_mut().unwrap().code_zone_tarifaire =
String::from_utf8_lossy(field.content).to_string();
}
(2..=16, 16) => {
carte_ps.situations.last_mut().unwrap().code_zone_ik =
String::from_utf8_lossy(field.content).to_string();
}
(2..=16, 17) => {
carte_ps.situations.last_mut().unwrap().code_agrement_1 =
String::from_utf8_lossy(field.content).to_string();
}
(2..=16, 18) => {
carte_ps.situations.last_mut().unwrap().code_agrement_2 =
String::from_utf8_lossy(field.content).to_string();
}
(2..=16, 19) => {
carte_ps.situations.last_mut().unwrap().code_agrement_3 =
String::from_utf8_lossy(field.content).to_string();
}
(2..=16, 20) => {
carte_ps
.situations
.last_mut()
.unwrap()
.habilitation_à_signer_une_facture =
String::from_utf8_lossy(field.content).to_string();
}
(2..=16, 21) => {
carte_ps
.situations
.last_mut()
.unwrap()
.habilitation_à_signer_un_lot =
String::from_utf8_lossy(field.content).to_string();
}
_ => {
return Err(format!(
"Unknown (group, field) pair: ({}, {})",
group.id, field.id
))
}
}
}
}
Ok(carte_ps)
}
#[cfg(test)]
mod test_decode_carte_ps {
use super::*;
#[test]
fn test_francoise_pharmacien0052419() {
let bytes: &[u8] = &[
0, 1, 51, // Block 01, Content size 51
1, 48, // Field 01, Content size 1
1, 56, // Field 02, Content size 1
11, 57, 57, 55, 48, 48, 53, 50, 52, 49, 57, 52, // Field 03, Content size 11
1, 52, // Field 04, Content size 1
2, 50, 50, // Field 05, Content size 2
17, 80, 72, 65, 82, 77, 65, 67, 73, 69, 78, 48, 48, 53, 50, 52, 49,
57, // Field 06, Content size 17
9, 70, 82, 65, 78, 67, 79, 73, 83, 69, // Field 07, Content size 9
1, 84, // Field 08, Content size 1
0, 2, 83, // Block 02, Content size 83
1, 1, 1, 48, 1, 49, 2, 56, 54, 1, 49, 9, 48, 66, 48, 50, 50, 49, 57, 53, 56, 1, 56, 24,
80, 72, 65, 82, 77, 65, 67, 73, 69, 32, 68, 85, 32, 67, 69, 78, 84, 82, 69, 50, 50, 49,
57, 53, 8, 48, 48, 50, 48, 50, 52, 49, 57, 1, 56, 0, 1, 48, 1, 49, 2, 53, 48, 2, 49,
48, 2, 48, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49,
];
let blocks = decode_ssv_memory(bytes, bytes.len());
let carte_ps = decode_carte_ps(blocks).unwrap();
assert_eq!(carte_ps.titulaire.type_de_carte_ps, "0");
assert_eq!(carte_ps.titulaire.type_d_identification_nationale, "8");
assert_eq!(
carte_ps.titulaire.numero_d_identification_nationale,
"99700524194"
);
assert_eq!(
carte_ps.titulaire.cle_du_numero_d_identification_nationale,
"4"
);
assert_eq!(carte_ps.titulaire.code_civilite, "22");
assert_eq!(carte_ps.titulaire.nom_du_ps, "PHARMACIEN0052419");
assert_eq!(carte_ps.titulaire.prenom_du_ps, "FRANCOISE");
assert_eq!(carte_ps.titulaire.categorie_carte, 'T');
assert_eq!(carte_ps.situations.len(), 1);
assert_eq!(
carte_ps.situations[0].numero_logique_de_la_situation_de_facturation_du_ps,
1
);
assert_eq!(carte_ps.situations[0].mode_d_exercice, "0");
assert_eq!(carte_ps.situations[0].statut_d_exercice, "1");
assert_eq!(carte_ps.situations[0].secteur_d_activite, "86");
assert_eq!(carte_ps.situations[0].type_d_identification_structure, "1");
assert_eq!(
carte_ps.situations[0].numero_d_identification_structure,
"0B0221958"
);
assert_eq!(
carte_ps.situations[0].cle_du_numero_d_identification_structure,
"8"
);
assert_eq!(
carte_ps.situations[0].raison_sociale_structure,
"PHARMACIE DU CENTRE22195"
);
assert_eq!(
carte_ps.situations[0].numero_d_identification_de_facturation_du_ps,
"00202419"
);
assert_eq!(
carte_ps.situations[0].cle_du_numero_d_identification_de_facturation_du_ps,
"8"
);
assert_eq!(
carte_ps.situations[0].numero_d_identification_du_ps_remplaçant,
""
);
assert_eq!(
carte_ps.situations[0].cle_du_numero_d_identification_du_ps_remplaçant,
"0"
);
assert_eq!(carte_ps.situations[0].code_conventionnel, "1");
assert_eq!(carte_ps.situations[0].code_specialite, "50");
assert_eq!(carte_ps.situations[0].code_zone_tarifaire, "10");
assert_eq!(carte_ps.situations[0].code_zone_ik, "00");
assert_eq!(carte_ps.situations[0].code_agrement_1, "0");
assert_eq!(carte_ps.situations[0].code_agrement_2, "0");
assert_eq!(carte_ps.situations[0].code_agrement_3, "0");
assert_eq!(
carte_ps.situations[0].habilitation_à_signer_une_facture,
"1"
);
assert_eq!(carte_ps.situations[0].habilitation_à_signer_un_lot, "1");
}
#[test]
fn test_multiple_situations() {
let bytes: &[u8] = &[
0, 1, 51, // Block 01, Content size 51
1, 48, 1, 56, 11, 57, 57, 55, 48, 48, 53, 50, 52, 49, 57, 52, 1, 52, 2, 50, 50, 17, 80,
72, 65, 82, 77, 65, 67, 73, 69, 78, 48, 48, 53, 50, 52, 49, 57, 9, 70, 82, 65, 78, 67,
79, 73, 83, 69, 1, 84, 0, 2, 83, // Block 02, Content size 83
1, 1, 1, 48, 1, 49, 2, 56, 54, 1, 49, 9, 48, 66, 48, 50, 50, 49, 57, 53, 56, 1, 56, 24,
80, 72, 65, 82, 77, 65, 67, 73, 69, 32, 68, 85, 32, 67, 69, 78, 84, 82, 69, 50, 50, 49,
57, 53, 8, 48, 48, 50, 48, 50, 52, 49, 57, 1, 56, 0, 1, 48, 1, 49, 2, 53, 48, 2, 49,
48, 2, 48, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 0, 3,
83, // Block 03, Content size 83
1, 1, 1, 48, 1, 49, 2, 56, 54, 1, 49, 9, 48, 66, 48, 50, 50, 49, 57, 53, 56, 1, 56, 24,
80, 72, 65, 82, 77, 65, 67, 73, 69, 32, 68, 85, 32, 67, 69, 78, 84, 82, 69, 50, 50, 49,
57, 53, 8, 48, 48, 50, 48, 50, 52, 49, 57, 1, 56, 0, 1, 48, 1, 49, 2, 53, 48, 2, 49,
48, 2, 48, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 0, 4,
83, // Block 04, Content size 83
1, 1, 1, 48, 1, 49, 2, 56, 54, 1, 49, 9, 48, 66, 48, 50, 50, 49, 57, 53, 56, 1, 56, 24,
80, 72, 65, 82, 77, 65, 67, 73, 69, 32, 68, 85, 32, 67, 69, 78, 84, 82, 69, 50, 50, 49,
57, 53, 8, 48, 48, 50, 48, 50, 52, 49, 57, 1, 56, 0, 1, 48, 1, 49, 2, 53, 48, 2, 49,
48, 2, 48, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49,
];
let blocks = decode_ssv_memory(bytes, bytes.len());
let carte_ps = decode_carte_ps(blocks).unwrap();
assert_eq!(carte_ps.situations.len(), 3);
assert_eq!(
carte_ps.situations[0].raison_sociale_structure,
"PHARMACIE DU CENTRE22195"
);
assert_eq!(
carte_ps.situations[1].raison_sociale_structure,
"PHARMACIE DU CENTRE22195"
);
assert_eq!(
carte_ps.situations[2].raison_sociale_structure,
"PHARMACIE DU CENTRE22195"
);
}
#[test]
#[should_panic]
fn test_missing_field() {
todo!();
}
#[test]
#[should_panic]
fn test_unknown_group_field_pair() {
todo!();
}
#[test]
#[should_panic]
fn test_invalid_field_format() {
todo!();
}
}

View File

@ -1,3 +1,8 @@
pub mod cps;
pub mod libssv;
pub mod ssv_memory;
pub mod ssvlib_demo;
pub fn add(left: usize, right: usize) -> usize {
left + right
}

View File

@ -1,17 +1,23 @@
/**
* libssv.rs
*
* Low level bindings to the SSVLIB dynamic library.
* TODO : look for creating a dedicated *-sys crate : https://kornel.ski/rust-sys-crate
*/
use libc::{ c_char, c_void, c_ushort, size_t };
/// libssv.rs
///
/// Low level bindings to the SSVLIB dynamic library.
// TODO : look for creating a dedicated *-sys crate : https://kornel.ski/rust-sys-crate
use libc::{c_char, c_ushort, c_void, size_t};
#[cfg_attr(target_os = "linux", link(name = "ssvlux64"))]
#[cfg_attr(target_os = "windows", link(name = "ssvw64"))]
extern "C" {
pub fn SSV_InitLIB2(pcRepSesamIni: *const c_char) -> c_ushort;
pub fn SSV_LireCartePS(NomRessourcePS: *const c_char, NomRessourceLecteur: *const c_char, CodePorteurPS: *const c_char, ZDonneesSortie: *mut *mut c_void, TTailleDonneesSortie: *mut size_t) -> c_ushort;
pub fn SSV_LireConfig(ZDonneesSortie: *mut *mut c_void, TTailleDonneesSortie: *mut size_t) -> c_ushort;
pub fn SSV_LireCartePS(
NomRessourcePS: *const c_char,
NomRessourceLecteur: *const c_char,
CodePorteurPS: *const c_char,
ZDonneesSortie: *mut *mut c_void,
TTailleDonneesSortie: *mut size_t,
) -> c_ushort;
pub fn SSV_LireConfig(
ZDonneesSortie: *mut *mut c_void,
TTailleDonneesSortie: *mut size_t,
) -> c_ushort;
}
/* TODO : replace void* by Rust struct : https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs */
// TODO : replace void* by Rust struct : https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs

View File

@ -1,5 +1,7 @@
mod ssvlib_demo;
mod cps;
mod libssv;
mod ssv_memory;
mod ssvlib_demo;
fn main() {
ssvlib_demo::demo();

View File

@ -0,0 +1,283 @@
/// # SSV Memory
/// Provide functions to manipulate raw memory from SSV library.
use std::convert::TryFrom;
#[derive(PartialEq, Debug)]
struct ElementSize {
pub size: usize,
pub pad: usize,
}
// TODO : Est-ce qu'on pourrait/devrait définir un type custom pour représenter les tableaux de bytes ?
impl TryFrom<&[u8]> for ElementSize {
type Error = &'static str;
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
if bytes.is_empty() {
return Err("Empty bytes input");
}
let mut element_size = ElementSize { size: 0, pad: 1 };
// Longueur:
// - si le bit de poids fort du premier octet est à 0, la longueur est codée sur un octet
// - si le bit de poids fort du premier octet est à 1, les 7 bits de poids faible codent le nombre d'octets utilisés pour coder la longueur
if bytes[0] & 0b1000_0000 == 0 {
// Size coded on 1 byte
element_size.size = bytes[0] as usize;
} else {
// Size coded on N bytes
// N are the 7 lower bits of the first byte
let size_bytes_len = (bytes[0] & 0b0111_1111) as usize;
if size_bytes_len > bytes.len() - 1 {
return Err("Invalid memory: not enough bytes to read the size");
} else if size_bytes_len > 4 {
return Err("Invalid memory: size is too big");
}
let size_bytes = &bytes[1..1 + size_bytes_len];
// u32::from_be_bytes() requires a 4 bytes array
let mut padded_bytes = [0u8; 4];
padded_bytes[size_bytes_len..].copy_from_slice(size_bytes);
element_size.size = u32::from_be_bytes(padded_bytes) as usize;
element_size.pad += size_bytes_len;
}
Ok(element_size)
}
}
#[derive(Debug)]
pub struct Block<'a> {
pub id: u16,
pub size: usize,
pub content: Vec<Field<'a>>,
}
impl<'a> From<&'a [u8]> for Block<'a> {
fn from(bytes: &'a [u8]) -> Self {
let mut offset = 0;
let id = u16::from_be_bytes(bytes[..2].try_into().unwrap());
offset += 2;
let ElementSize {
size: block_size,
pad,
} = bytes[2..].try_into().unwrap();
offset += pad;
let raw_content = &bytes[offset..];
let mut field_offset = 0;
// While there is still content to read, parse Fields
let mut content = Vec::new();
let mut field_id = 1;
while field_offset < block_size {
let mut field: Field<'a> = raw_content[field_offset..].into();
field.id = field_id;
field_offset += field.size;
field_id += 1;
content.push(field);
}
Block {
id,
size: offset + block_size,
content,
}
}
}
#[derive(Debug)]
pub struct Field<'a> {
pub id: u16,
pub size: usize,
pub content: &'a [u8],
}
impl<'a> From<&'a [u8]> for Field<'a> {
fn from(bytes: &'a [u8]) -> Self {
let ElementSize { size, pad } = bytes.try_into().unwrap();
let contenu = &bytes[pad..pad + size];
Field {
id: 0,
size: pad + size,
content: contenu,
}
}
}
pub fn decode_ssv_memory(bytes: &[u8], size: usize) -> Vec<Block> {
let mut blocks: Vec<Block> = Vec::new();
let mut offset = 0;
while offset < size {
let block: Block = bytes[offset..].into();
offset += block.size;
blocks.push(block);
}
blocks
}
#[cfg(test)]
mod test_element_size {
use super::*;
#[test]
fn short_size() {
let bytes: &[u8] = &[0b_0000_0001_u8];
let element_size: ElementSize = bytes.try_into().unwrap();
assert_eq!(element_size.size, 1);
assert_eq!(element_size.pad, 1);
let bytes: &[u8] = &[0b_0100_0000_u8];
let element_size: ElementSize = bytes.try_into().unwrap();
assert_eq!(element_size.size, 64);
assert_eq!(element_size.pad, 1);
}
#[test]
fn long_size() {
let bytes: &[u8] = &[0b_1000_0010_u8, 0b_0000_0001_u8, 0b_0100_0000_u8];
let element_size: ElementSize = bytes.try_into().unwrap();
assert_eq!(element_size.size, 320);
assert_eq!(element_size.pad, 3);
}
#[test]
fn null_size() {
let bytes: &[u8] = &[];
let result: Result<ElementSize, &str> = bytes.try_into();
assert_eq!(result, Err("Empty bytes input"),);
}
#[test]
fn invalid_memory() {
let bytes: &[u8] = &[0b_1000_0001_u8];
let result: Result<ElementSize, &str> = bytes.try_into();
assert_eq!(
result,
Err("Invalid memory: not enough bytes to read the size"),
);
let bytes: &[u8] = &[0b_1000_0010_u8, 1];
let result: Result<ElementSize, &str> = bytes.try_into();
assert_eq!(
result,
Err("Invalid memory: not enough bytes to read the size"),
);
let bytes: &[u8] = &[0b_1000_0101_u8, 1, 1, 1, 1, 1];
let result: Result<ElementSize, &str> = bytes.try_into();
assert_eq!(result, Err("Invalid memory: size is too big"),);
}
}
#[cfg(test)]
mod test_field {
use super::*;
#[test]
fn short_size() {
let bytes: &[u8] = &[
51, 1, 48, 1, 56, 11, 57, 57, 55, 48, 48, 53, 50, 52, 49, 57, 52, 1, 52, 2, 50, 50, 17,
80, 72, 65, 82, 77, 65, 67, 73, 69, 78, 48, 48, 53, 50, 52, 49, 57, 9, 70, 82, 65, 78,
67, 79, 73, 83, 69, 1, 84,
];
let element: Field = bytes.into();
assert_eq!(element.size, 52);
assert_eq!(element.content[..5], [1, 48, 1, 56, 11]);
}
#[test]
fn long_size() {
let mut bytes_vec = vec![
0b_1000_0010_u8,
0b_0000_0001_u8,
0b_0000_0000_u8, // size = 256
];
// Add 256 bytes to the content
bytes_vec.append(&mut vec![1; 256]);
let bytes: &[u8] = &bytes_vec;
let element: Field = bytes.into();
assert_eq!(element.size, 259);
assert_eq!(element.content.len(), 256);
}
}
#[cfg(test)]
mod test_block {
use super::*;
#[test]
fn test_francoise_pharmacien0052419_partial_block_1() {
let bytes: &[u8] = &[1, 48, 1, 56, 11, 57, 57, 55, 48, 48, 53, 50, 52, 49, 57, 52];
let field1: Field = bytes.into();
assert_eq!(field1.size, 2);
assert_eq!(field1.content, &[48]);
let field2: Field = bytes[field1.size..].into();
assert_eq!(field2.size, 2);
assert_eq!(field2.content, &[56]);
let field3: Field = bytes[field1.size + field2.size..].into();
assert_eq!(field3.size, 12);
assert_eq!(
field3.content,
&[57, 57, 55, 48, 48, 53, 50, 52, 49, 57, 52]
);
}
#[test]
fn test_francoise_pharmacien0052419() {
let bytes: &[u8] = &[
0, 1, 51, // 3
1, 48, // 2
1, 56, // 2
11, 57, 57, 55, 48, 48, 53, 50, 52, 49, 57, 52, // 12
1, 52, // 2
2, 50, 50, // 3
17, 80, 72, 65, 82, 77, 65, 67, 73, 69, 78, 48, 48, 53, 50, 52, 49, 57, // 18
9, 70, 82, 65, 78, 67, 79, 73, 83, 69, // 10
1, 84, // 2
// total: 54
0, 2, 83, 1, 1, 1, 48, 1, 49, 2, 56, 54, 1, 49, 9, 48, 66, 48, 50, 50, 49, 57, 53, 56,
1, 56, 24, 80, 72, 65, 82, 77, 65, 67, 73, 69, 32, 68, 85, 32, 67, 69, 78, 84, 82, 69,
50, 50, 49, 57, 53, 8, 48, 48, 50, 48, 50, 52, 49, 57, 1, 56, 0, 1, 48, 1, 49, 2, 53,
48, 2, 49, 48, 2, 48, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49,
];
let first_block: Block = bytes.into();
assert_eq!(first_block.id, 1);
assert_eq!(first_block.size, 54);
assert_eq!(first_block.content.len(), 8);
let second_block: Block = bytes[first_block.size..].into();
assert_eq!(second_block.id, 2);
assert_eq!(second_block.size, 86);
assert_eq!(second_block.content.len(), 21);
}
}
#[cfg(test)]
mod test_decode_ssv_memory {
use super::*;
#[test]
fn test_francoise_pharmacien0052419() {
let bytes: &[u8] = &[
0, 1, 51, // 3
1, 48, // 2
1, 56, // 2
11, 57, 57, 55, 48, 48, 53, 50, 52, 49, 57, 52, // 12
1, 52, // 2
2, 50, 50, // 3
17, 80, 72, 65, 82, 77, 65, 67, 73, 69, 78, 48, 48, 53, 50, 52, 49, 57, // 18
9, 70, 82, 65, 78, 67, 79, 73, 83, 69, // 10
1, 84, // 2
// total: 54
0, 2, 83, 1, 1, 1, 48, 1, 49, 2, 56, 54, 1, 49, 9, 48, 66, 48, 50, 50, 49, 57, 53, 56,
1, 56, 24, 80, 72, 65, 82, 77, 65, 67, 73, 69, 32, 68, 85, 32, 67, 69, 78, 84, 82, 69,
50, 50, 49, 57, 53, 8, 48, 48, 50, 48, 50, 52, 49, 57, 1, 56, 0, 1, 48, 1, 49, 2, 53,
48, 2, 49, 48, 2, 48, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49,
];
let blocks = decode_ssv_memory(bytes, bytes.len());
assert_eq!(blocks.len(), 2);
}
}

View File

@ -1,24 +1,14 @@
/**
* High level API for the SSV library,
* based on the low level bindings in libssv.rs.
*
*/
extern crate libc;
/// High level API for the SSV library,
/// based on the low level bindings in libssv.rs.
extern crate dotenv;
use libc::{ c_void, size_t };
use libc::{c_void, size_t};
use std::env;
use std::ffi::CString;
use std::path::PathBuf;
use std::ptr;
use std::env;
use crate::libssv:: {
SSV_InitLIB2,
SSV_LireCartePS,
SSV_LireConfig
};
use crate::cps::lire_carte;
use crate::libssv::{SSV_InitLIB2, SSV_LireConfig};
fn ssv_init_lib_2() {
let ini_str = env::var("SESAM_INI_PATH").expect("SESAM_INI_PATH must be set");
@ -29,35 +19,6 @@ fn ssv_init_lib_2() {
}
}
fn ssv_lire_carte_ps() {
let resource_ps = CString::new("PS").expect("CString::new failed");
let resource_reader = CString::new("TRANSPA1").expect("CString::new failed");
let card_number = CString::new("1234567890").expect("CString::new failed");
let mut buffer: *mut c_void = ptr::null_mut();
let mut size: size_t = 0;
unsafe {
let result = SSV_LireCartePS(
resource_ps.as_ptr(),
resource_reader.as_ptr(),
card_number.as_ptr(),
&mut buffer,
&mut size
);
println!("SSV_LireCartePS result: {}", result);
if !buffer.is_null() {
let hex_values = std::slice::from_raw_parts(buffer as *const u8, size);
for &byte in hex_values {
print!("{:02X} ", byte);
}
println!();
libc::free(buffer);
}
}
}
fn ssv_lire_config() {
let mut buffer: *mut c_void = ptr::null_mut();
let mut size: size_t = 0;
@ -78,10 +39,8 @@ fn ssv_lire_config() {
}
pub fn demo() {
/*
* TODO : this is probably not working on release, because I'm not sure it exists a CARGO_MANIFEST_DIR and so it can find the `.env`
* Maybe we could use a system standard config path to store a config file
*/
// TODO : this is probably not working on release, because I'm not sure it exists a CARGO_MANIFEST_DIR and so it can find the `.env`
// Maybe we could use a system standard config path to store a config file
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let manifest_path = PathBuf::from(manifest_dir);
dotenv::from_path(manifest_path.join(".env")).ok();
@ -89,7 +48,12 @@ pub fn demo() {
println!("------- Demo for the SSV library --------");
ssv_init_lib_2();
ssv_lire_carte_ps();
let code_pin = "1234";
let lecteur = "HID Global OMNIKEY 3x21 Smart Card Reader 0";
let carte_ps = lire_carte(code_pin, lecteur).unwrap();
println!("CartePS: {:#?}", carte_ps);
ssv_lire_config();
println!("-----------------------------------------");