feat: implement LireCartePS with hardcoded reader and all errors

This commit is contained in:
Florian Briand 2024-10-02 00:42:41 +02:00
parent 405f923bc6
commit b62e21771a
Signed by: florian_briand
GPG Key ID: CC981B9E6B98E70B
4 changed files with 180 additions and 8 deletions

View File

@ -1969,6 +1969,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"fsv-sys", "fsv-sys",
"libc",
"num_enum", "num_enum",
"thiserror", "thiserror",
"utils", "utils",

View File

@ -5,7 +5,8 @@ edition = "2021"
[dependencies] [dependencies]
anyhow = "1.0.89" anyhow = "1.0.89"
num_enum = "0.7.3" libc = "0.2.159"
num_enum = { version = "0.7.3", features = ["complex-expressions"] }
thiserror = "1.0.64" thiserror = "1.0.64"
fsv-sys = { path = "../fsv-sys" } fsv-sys = { path = "../fsv-sys" }

View File

@ -4,6 +4,75 @@ use thiserror::Error;
#[derive(Error, Debug, Eq, PartialEq, FromPrimitive)] #[derive(Error, Debug, Eq, PartialEq, FromPrimitive)]
#[repr(u16)] #[repr(u16)]
pub enum SSVErrorCodes { pub enum SSVErrorCodes {
#[error("La Carte du Professionnel de Santé est absente du lecteur.")]
CPSMissing = 0xF001,
#[error("La Carte du Professionnel de Santé bloquée après trois codes porteur erronés.")]
CPSBlocked = 0xF002,
#[error("Le code porteur présenté est erroné.")]
CPSPinWrong = 0xF003,
#[error("Carte du Professionnel de Santé non valide ou inexploitable par le Logiciel Lecteur. Vérifier la présence d'un Domaine d'Assurance Maladie (DAM).")]
CPSInvalid = 0xF004,
#[error("La Carte du Professionnel de Santé est retirée du lecteur.")]
CPSRemoved = 0xF005,
#[error("Message du lecteur incohérent. Débrancher et rebrancher le lecteur.")]
PCSCInconsistentMessage = 0xF0FF,
#[error("Le nom de lecteur fourni ne correspond à aucun lecteur reconnu.")]
PCSCReaderNotFound = 0xF101,
#[error("La fonction InitLIB2 n'est pas encore appelée ou la fonction TermLIB a déjà été appelée.")]
FunctionInitLib2NotCalled = 0xF600,
#[error("La bibliothèque SSV nest pas chargée en mémoire. Vérifier que la fonction InitLIB2 a bien été appelée.")]
LibraryNotLoaded = 0xF690, // Warning
#[error("Carte vitale en opposition.")]
VitaleOpposition = 0xF6A1,
#[error("Zone de mémoire non allouée en sortie.")]
MemoryNotAllocated = 0xF800,
#[error("Erreur d'allocation de la zone de mémoire en sortie.")]
MemoryAllocationError = 0xF801,
#[error("Un des paramètres obligatoires d'entrée est non alloué ou invalide.")]
InputParameterNotAllocatedOrInvalid = 0xF802,
#[error("Zone de mémoire spécifiée en entrée non valide. Vérifier que la zone allouée ne dépasse pas la taille maximale autorisée (MAXBLOC).")]
InputMemoryInvalid = 0xF803,
#[error("Le format de la zone de mémoire d'entrée ou le nombre de zones mémoire est incorrect.")]
InputMemoryFormatIncorrect = 0xF810,
#[error("Problème lors de linitialisation du protocole. Erreur du Ressource Manager PC/SC. Vérifiez le lecteur.")]
PCSCProtocolInitError = 0xFF01,
#[error("Time-out au niveau protocolaire ou transmission déjà en cours avec le lecteur. Vérifiez le lecteur et l'insertion de la carte.")]
PCSCProtocolTimeout = 0xFF02,
#[error("Taille insuffisante allouée en entrée dune fonction du Resource Manager.")]
PCSCProtocolInputMemoryTooSmall = 0xFF03,
#[error("Erreur de transmission du protocole. Vérifiez le lecteur et l'insertion de la carte.")]
PCSCProtocolTransmissionError = 0xFF04,
#[error("Lecteur absent ou indisponible.")]
PCSCReaderMissingOrUnavailable = 0xFF05,
#[error("Le nom du lecteur transmis est inconnu du Resource Manager PC/SC.")]
PCSCReaderUnknown = 0xFF06,
#[error("Erreur inconnue remontée par le Resource Manager PC/SC.")]
PCSCUnknownError = 0xFF07,
#[error("Erreur interne Resource Manager PC/SC.")]
PCSCInternalError = 0xFF08,
#[error("Ressource PC/SC déjà prise en exclusivité. Vérifiez qu'une autre application n'utilise pas le lecteur.")]
PCSCResourceAlreadyExclusive = 0xFF09,
#[error("Protocole incompatible avec la carte à puce. Vérifiez l'insertion de la carte et son état.")]
PCSCProtocolIncompatible = 0xFF0A,
#[error("Paramètre incorrect. Erreur interne à la librairie SSV.")]
PCSCIncorrectParameter = 0xFF0B,
#[error("Carte absente. Insérez une carte dans le lecteur.")]
PCSCCardMissing = 0xFF0C,
#[error("L'état de la carte a été modifié (RAZ ou mise hors tension). Vérifiez si la carte n'a pas été retirée ou si une autre application n'utilise pas la carte.")]
PCSCCardStateChanged = 0xFF0D,
#[error("Carte muette ou non supportée. Vérifiez l'insertion de la carte.")]
PCSCCardUnsupported = 0xFF0E,
#[error("Code porteur CPS non renseigné.")]
CPSPinMissing = 0xFF21,
#[error("Ressource PC/SC déjà prise en exclusivité. Vérifiez que le processus en cours n'utilise pas déjà le lecteur.")]
PCSCReaderAlreadyExclusiveForCurrentProcess = 0xFF24,
#[error("Plusieurs lecteurs ou cartes de même type identifiés lors de la détection automatique.")]
PCSCDuplicatedReadersOrCardsDetected = 0xFF29,
#[error("Problème de chargement de la librairie cryptographique ou erreur retournée par la librairie cryptographique.")]
CryptoLibraryError = 0xFF30,
#[error("Erreurs internes aux Services SESAM-Vitale. Vérifiez les traces.")]
#[num_enum(alternatives = [0xFFF1..=0xFFFF])]
SSVInternalError = 0xFFF0,
#[error("Le fichier `tablebin.smc` est inaccessible en lecture (inexistant ou pas de droits d'accès).")] #[error("Le fichier `tablebin.smc` est inaccessible en lecture (inexistant ou pas de droits d'accès).")]
FileMissingTablebinMsc = 0xF610, // tablebin.smc FileMissingTablebinMsc = 0xF610, // tablebin.smc
#[error("Le fichier `scripts.sms` est inaccessible en lecture (inexistant ou pas de droits d'accès).")] #[error("Le fichier `scripts.sms` est inaccessible en lecture (inexistant ou pas de droits d'accès).")]
@ -80,3 +149,27 @@ pub enum SSVErrorCodes {
#[error("Erreur inattendue de la librairie SSV (code d'erreur: {0}).")] #[error("Erreur inattendue de la librairie SSV (code d'erreur: {0}).")]
Unexpected(u16), Unexpected(u16),
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_code_ranges() {
let error_code = 0xFFF1;
let error = SSVErrorCodes::from(error_code);
assert_eq!(error, SSVErrorCodes::SSVInternalError);
let error_code = 0xFFF8;
let error = SSVErrorCodes::from(error_code);
assert_eq!(error, SSVErrorCodes::SSVInternalError);
}
#[test]
fn test_catch_all() {
let error_code = 0xFBFF; // Not a valid error code
let error = SSVErrorCodes::from(error_code);
assert_eq!(error, SSVErrorCodes::Unexpected(0xFBFF));
}
}

View File

@ -1,3 +1,5 @@
use std::{ffi::CString, ptr};
use thiserror::Error; use thiserror::Error;
use fsv_sys::{ use fsv_sys::{
@ -54,7 +56,7 @@ impl SSV {
/// # Initialize the SSV library /// # Initialize the SSV library
/// Implement: SSV_InitLIB2 /// Implement: SSV_InitLIB2
pub fn init_library(&self, sesam_ini_path: &str) -> Result<(), Error> { pub fn init_library(&self, sesam_ini_path: &str) -> Result<(), Error> {
let sesam_ini_path = std::ffi::CString::new(sesam_ini_path).expect("CString::new failed"); let sesam_ini_path = CString::new(sesam_ini_path).expect("CString::new failed");
let result = match &self.library { let result = match &self.library {
SsvLibraryVersion::V1_40_13(library) => { SsvLibraryVersion::V1_40_13(library) => {
unsafe { library.ssv_init_lib2(sesam_ini_path.as_ptr()) }? unsafe { library.ssv_init_lib2(sesam_ini_path.as_ptr()) }?
@ -69,6 +71,51 @@ impl SSV {
} }
Ok(()) 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)] #[cfg(test)]
@ -80,16 +127,46 @@ mod tests {
use super::*; use super::*;
fn init() -> Result<SSV> { mod setup {
use super::*;
pub fn init() -> Result<SSV> {
load_config().unwrap(); load_config().unwrap();
Ok(SSV::new(SupportedFsvVersion::V1_40_13)?) 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] #[test]
fn test_init_library() -> Result<()> { fn test_init_library() -> Result<()> {
let lib = init()?; setup::init()?;
let sesam_ini_path = env::var("SESAM_INI_PATH").expect("SESAM_INI_PATH must be set"); Ok(())
lib.init_library(&sesam_ini_path)?; }
#[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(()) Ok(())
} }