diff --git a/src/desfire/desfire.rs b/src/desfire/desfire.rs index 2e56181..8da1c51 100644 --- a/src/desfire/desfire.rs +++ b/src/desfire/desfire.rs @@ -16,8 +16,7 @@ use crate::iso7816_4::apducommand::{APDUCommand, IsoCase}; use crate::{error, Card}; use num_traits::FromPrimitive; - - +/// Maximum number of bytes in a single transaction while communicating with a nfc token pub const MAX_BYTES_PER_TRANSACTION: usize = 47; pub struct Desfire { @@ -43,10 +42,7 @@ impl Desfire { }) } - /// - /// Select Application by ApplicationID (AID) - /// - /// 3 Byte AID + /// Select Application by 3 Byte ApplicationID `aid` pub fn select_application(&self, aid: u32) -> Result<()> { let cmd_select_app = self.select_application_cmd(aid)?; @@ -55,12 +51,12 @@ impl Desfire { response.check() } - /// /// Authenticate to PICC, with ISO Authenticate for DES Key - /// - /// 0x01 - 0x0D - /// Array of 8/16 Bytes - /// !!! WARNING For Testing only !!! + /// + /// Arguments: + /// * `key_id`: 0x01 - 0x0D + /// * `key`: Array of 8/16 bytes + /// * `rnd_a`: !!! WARNING for testing only !!! pub fn authenticate_iso_des( &mut self, key_id: u8, @@ -243,12 +239,12 @@ impl Desfire { Ok(()) } - /// /// Authenticate to PICC, with ISO Authenticate for AES Key - /// - /// 0x01 - 0x0D - /// Array of 8/16 Bytes - /// !!! WARNING For Testing only !!! + /// + /// # Parameters + /// * `key_id`: 0x01 - 0x0D + /// * `key`: Array of 8/16 bytes + /// * `rnd_a`: !!! WARNING for testing only !!! pub fn authenticate_iso_aes( &mut self, key_id: u8, @@ -311,10 +307,10 @@ impl Desfire { ) } - /// - /// Format PICC - /// Need Authentication for PICC / Application 0x000000 - /// + /// Format PICC, this is akin to a factory reset + /// + /// # Note + /// This function will fail if you haven't authenticated against the PICC (Application ID: 0) pub fn format_picc(&self) -> Result<()> { let cmd_format = APDUCommand { case: IsoCase::Case2Short, @@ -328,10 +324,12 @@ impl Desfire { response.check() } - /// - /// Create Application for ApplicationID - /// - /// 3 Byte ID + /// Create a new application with `aid` as the application ID + /// + /// # Arguments + /// * `aid`: 3 byte ID + /// * `keysetting1`: TODO + /// * `keysetting2`: TODO pub fn create_application(&self, aid: u32, keysetting1: u8, keysetting2: u8) -> Result<()> { if aid > 0xFFFFFF { return Err(InvalidApplicationID); @@ -358,12 +356,18 @@ impl Desfire { response.check() } - /// - /// Change AES key, the same as Authenticated - /// - /// 0x01 - 0x0D - /// Array of 16 Bytes - /// Version of Key(min. 0x10) + /// Change AES key, that is currently authenticated + /// + /// # Notes + /// + /// This will fail if the key you are trying to change isn't authenticated + /// + /// # Arguments + /// * `key_id` - 0x01 - 0x0D + /// * `new_key` - Array of 16 Bytes + /// * `key_version` - Version of Key(min. 0x10) + /// + /// Also see: [`Desfire::change_other_key_aes`] pub fn change_key_aes(&mut self, key_id: u8, new_key: &[u8], key_version: u8) -> Result<()> { if key_id >= 0x0E { return Err(InvalidKeyID); @@ -426,13 +430,15 @@ impl Desfire { response.check() } - /// - /// Change AES key, other than Authenticated - /// - /// 0x01 - 0x0D - /// Array of 16 Bytes - /// Array of 16 Bytes - /// Version of Key(min. 0x10) + /// Change AES key, of a currently not authenticated key + /// + /// # Arguments: + /// * `key_id` - 0x01 - 0x0D + /// * `new_key` - Array of 16 Bytes + /// * `old_key` - Array of 16 Bytes + /// * `key_version` - Version of Key(min. 0x10) + /// + /// Also see: [`Desfire::change_key_aes`] pub fn change_other_key_aes( &mut self, key_id: u8, @@ -514,6 +520,13 @@ impl Desfire { response.check() } + /// Create a new standard file + /// + /// # Arguments + /// * `file_id` - ID of the new file (0x00 - 0x20) + /// * `communication` - TODO + /// * `access_rights` - TODO + /// * `size` - size of the file in bytes pub fn create_file_standard( &self, file_id: u8, @@ -551,12 +564,12 @@ impl Desfire { response.check() } - /// /// Write Data to File - /// - /// ID of File (0x00 - 0x20) - /// Offset for File - /// Data to write + /// + /// # Parameters + /// * `file_id` - Id of the file (0x00 - 0x20) + /// * `offset` - offset of the file + /// * `data` - data to write pub fn write_data(&self, file_id: u8, offset: u32, data: &[u8]) -> Result<()> { if file_id >= 0x20 { return Err(InvalidFileID); @@ -638,12 +651,15 @@ impl Desfire { return Ok(cmd_read_data_chunk); } - /// - /// Read Data from File - /// - /// ID of File (0x00 - 0x20) - /// Offset for File - /// Lenght of Data + /// Read data from a file + /// + /// # Note + /// This operation will fail if you try to read more data then there is in the file + /// + /// # Parameters + /// * `file_id` - ID of the file (0x00 - 0x20) + /// * `offset` - Offset of the file + /// * `length` - lenght of the data to read pub fn read_data(&self, file_id: u8, offset: u32, length: usize) -> Result> { if file_id >= 0x20 { return Err(InvalidFileID); @@ -679,29 +695,30 @@ impl Desfire { } } -/// /// Generates SessionKey for DES Authentification -/// -/// 8Byte SessionKey +/// +/// returns 8Byte SessionKey fn generate_session_key_des(rnd_a: &[u8], rnd_b: &[u8]) -> Option> { let sessionkey = [&rnd_a[..4], &rnd_b[..4]].concat(); Some([sessionkey.as_slice(), sessionkey.as_slice()].concat()) } -/// /// Generates SessionKey for AES Authentification -/// -/// 16Byte SessionKey +/// +/// returns 16Byte SessionKey fn generate_session_key_aes(rnd_a: &[u8], rnd_b: &[u8]) -> Option> { //FIXME: Check math Some([&rnd_a[..4], &rnd_b[..4], &rnd_a[12..], &rnd_b[12..]].concat()) } -/// /// Generate KeySetting1 for Application Settings or PICC Setting -/// -/// ID of Key for changing Application Keys -/// generated keysettings +/// +/// # Parameters +/// * `change_key` - ID of the key for changing application keys +/// * `change_master_key` - TODO +/// * `create_delete_file` - TODO +/// * `file_directory_access` - TODO +/// * `change_master_key` - TODO pub fn generate_keysetting1( change_key: u8, change_masterkey_settings: ChangeMasterKeySettings, @@ -731,11 +748,12 @@ pub fn generate_keysetting1( }; } -/// /// Generate KeySetting2 for Application Creation -/// -/// Number of keys that can be stored within the application (0x01-0x0D) -/// generated keysettings +/// +/// # Parameters +/// * `crypto_operations` - TODO +/// * `file_identifier` - TODO +/// * `num_keys` - Number of keys that can be stored within the application (0x01 - 0x0D) pub fn generate_keysetting2( crypto_operations: CryptoOperationsType, file_identifier: FileIdentifiers, @@ -748,14 +766,18 @@ pub fn generate_keysetting2( }; } -/// /// Generate FileAccess Rights for File Settings -/// Use enum AccesRights for Free or Never Option -/// -/// KeyID for Read Access -/// KeyID for Write Access -/// KeyID for Read and Write Access -/// KeyID for Configuration Access +/// +/// # Note +/// Use [`FileAccessRights`] for Free or Never Option +/// +/// # Parameters +/// * `read` key_id for read access +/// * `write` key_id for write access +/// * `read_write` key_id for read and write access +/// * `configure` key_id for configuration access +/// +/// [`FileAccessRights`]: crate::desfire::FileAccessRights pub fn generate_file_access_rights( read: u8, write: u8, @@ -780,23 +802,21 @@ mod tests { generate_file_access_rights, generate_keysetting1, generate_keysetting2, generate_session_key_aes, generate_session_key_des, Desfire, }; - + use crate::desfire::{ ChangeApplicationKey, ChangeMasterKey, ChangeMasterKeySettings, CreateDeleteFile, CryptoOperationsType, FileAccessRights, FileCommunication, FileDirectoryAccess, FileIdentifiers, }; - use crate::error::Error::{CardError}; + use crate::error::Error::CardError; use crate::error::Result; - + use crate::{APDUCommand, APDUResponse, Card as CardTrait}; use hex_literal::hex; - use mockall::{mock}; + use mockall::mock; use pcsc::{Card, Context, Protocols, Scope, ShareMode, MAX_BUFFER_SIZE}; use std::convert::TryFrom; - use std::ffi::{CString}; - - + use std::ffi::CString; mock! { pub VirtualCard {} diff --git a/src/desfire/mod.rs b/src/desfire/mod.rs index d4d3569..1812fd8 100644 --- a/src/desfire/mod.rs +++ b/src/desfire/mod.rs @@ -1,79 +1,51 @@ use num_derive::FromPrimitive; -/// -/// hold the Access Rights for changing application keys (Change Key command) -/// +/// Access Rights for changing application keys (Change Key command) #[repr(u8)] #[derive(Clone, Copy, FromPrimitive)] pub enum ChangeApplicationKey { - /// /// Application master key authentication is necessary to change any key (default) - /// MASTERKEY = 0x00, - /// /// Authentication with the key to be changed (same Key#) is necessary to change a key - /// SAMEKEY = 0x0E, - /// /// All keys (except application master key, see Bit 0) within this application are frozen - /// ALLKEYS = 0x0F, } -/// /// codes whether the application master key is changeable -/// #[repr(u8)] #[derive(Clone, Copy)] pub enum ChangeMasterKey { - /// /// Application master key is not changeable anymore (frozen) - /// FROZEN = 0x00, - /// /// Application master key is changeable (authentication with the current application master key necessary, default) - /// CHANGEABLE = 0x01, } -/// /// codes whether a change of the application master key settings is allowed -/// #[repr(u8)] #[derive(Clone, Copy)] pub enum ChangeMasterKeySettings { - /// /// configuration not changeable anymore (frozen) - /// FROZEN = 0x00, - /// /// this configuration is changeable if authenticated with the application master key (default) - /// WITHMASTERKEY = 0x08, } -/// /// codes whether application master key authentication is needed before “Create File” / “Delete File” -/// #[repr(u8)] #[derive(Clone, Copy)] pub enum CreateDeleteFile { - /// /// “Create File”/ “Delete File”is permitted only with application master key authentication - /// ONLYMASTERKEY = 0x00, - /// /// “Create File”/ “Delete File”is permitted also without application master key authentication (default) - /// NOKEY = 0x04, } -/// /// Crypto method of the application -/// #[repr(u8)] #[derive(Clone, Copy)] pub enum CryptoOperationsType { @@ -92,42 +64,30 @@ pub enum FileAccessRights { #[repr(u8)] #[derive(Clone, Copy)] pub enum FileCommunication { - /// /// "Plain communication" - /// PLAIN = 0x00, - /// /// Plain communication secured by DES/3DES MACing - /// MAC = 0x01, - /// /// Fully DES/3DES enciphered communication - /// ENCRYPT = 0x03, } -/// /// codes whether application master key authentication is needed for file directory access -/// #[repr(u8)] #[derive(Clone, Copy)] pub enum FileDirectoryAccess { - /// - /// Successful application master key authentication is required for executing the “Get FID List”, “Get File Settings”and “Get Key Settings”commands - /// + /// Successful application master key authentication is required for executing + /// the “Get FID List”, “Get File Settings”and “Get Key Settings”commands ONLYMASTERKEY = 0x00, - /// - /// “Get FID List”, “Get File Settings” and “Get Key Settings” commands succeed independentlyof a preceding application master key authentication (default) - /// + /// “Get FID List”, “Get File Settings” and “Get Key Settings” commands succeed + /// independently of a preceding application master key authentication (default) NOKEY = 0x02, } -/// /// Indicates use of 2 byte ISO/IEC 7816-4 File Identifies for files within the Application -/// #[repr(u8)] #[derive(Clone, Copy)] pub enum FileIdentifiers { @@ -138,29 +98,19 @@ pub enum FileIdentifiers { #[repr(u8)] #[derive(Clone, Copy)] pub enum FileTypes { - /// /// Standard Data File - /// STANDARD = 0x00, - /// /// Backup Data Files - /// BACKUP = 0x01, - /// /// Value Files with Backup - /// VALUE = 0x02, - /// /// Linear Record Files with Backup - /// LINEARRECORD = 0x03, - /// /// Cyclic Record Files with Backup - /// CYCLICRECORD = 0x04, } diff --git a/src/iso7816_4/apducommand.rs b/src/iso7816_4/apducommand.rs index d88450f..2e9fe5e 100644 --- a/src/iso7816_4/apducommand.rs +++ b/src/iso7816_4/apducommand.rs @@ -6,74 +6,60 @@ use std::fmt::Formatter; #[repr(u8)] #[derive(Eq, PartialEq, Hash, Debug, Clone)] pub enum IsoCase { - /// No command data. No response data. - /// - /// - /// lc is valued to 0. - /// le is valued to 0. - /// No data byte is present. - /// - /// + /// No command data. No response data. + /// + /// # Notes + /// * lc is valued to 0. + /// * le is valued to 0. + /// * No data byte is present. Case1 = 0, - /// No command data. Expected response data. - /// - /// - /// lc is valued to 0. - /// le is valued from 1 to 256. - /// No data byte is present. - /// - /// + /// No command data. Expected response data. + /// + /// # Notes + /// * lc is valued to 0. + /// * le is valued from 1 to 256. + /// * No data byte is present. Case2Short = 1, - /// Command data. No response data. - /// - /// - /// lc is valued from 1 to 255. - /// le is valued to 0. - /// lc data bytes are present. - /// - /// + /// Command data. No response data. + /// + /// # Notes + /// * lc is valued from 1 to 255. + /// * le is valued to 0. + /// * lc data bytes are present. Case3Short = 2, - /// Command data. Expected response data. - /// - /// - /// lc is valued from 1 to 255. - /// le is valued from 1 to 256. - /// lc data bytes are present. - /// - /// + /// Command data. Expected response data. + /// + /// # Notes + /// * lc is valued from 1 to 255. + /// * le is valued from 1 to 256. + /// * lc data bytes are present. Case4Short = 3, - /// No command data. Expected response data. - /// - /// - /// lc is valued to 0. - /// le is valued from 1 to 65536. - /// No data byte is present. - /// - /// + /// No command data. Expected response data. + /// + /// # Notes + /// * lc is valued to 0. + /// * le is valued from 1 to 65536. + /// * No data byte is present. Case2Extended = 4, - /// Command data. No response data. - /// - /// - /// lc is valued from 1 to 65536. - /// le is valued to 0. - /// lc data bytes are present. - /// - /// + /// Command data. No response data. + /// + /// # Notes + /// * lc is valued from 1 to 65536. + /// * le is valued to 0. + /// * lc data bytes are present. Case3Extended = 5, - /// Command data. Expected response data. - /// - /// - /// lc is valued from 1 to 65535. - /// le is valued from 1 to 65536. - /// lc data bytes are present. - /// - /// + /// Command data. Expected response data. + /// + /// Notes + /// * lc is valued from 1 to 65535. + /// * le is valued from 1 to 65536. + /// * lc data bytes are present. Case4Extended = 6, } @@ -86,24 +72,22 @@ impl Default for IsoCase { #[repr(u8)] #[derive(Eq, PartialEq, Hash, Debug, Clone)] pub enum SCardProtocol { - /// /// protocol not defined. - /// Unset = 0x0000, - /// T=0 active protocol. + /// T=0 active protocol. T0 = 0x0001, - /// T=1 active protocol. + /// T=1 active protocol. T1 = 0x0002, - /// Raw active protocol. Use with memory type cards. + /// Raw active protocol. Use with memory type cards. Raw = 0x0004, - /// T=15 protocol. + /// T=15 protocol. T15 = 0x0008, - /// ( | ). IFD (Interface device) determines protocol. + /// (see [`SCardProtocol::T0`] and [`SCardProtocol::T1`]). IFD (Interface device) determines protocol. Any = (0x0001 | 0x0002), } diff --git a/src/lib.rs b/src/lib.rs index c4470e6..eb9cfeb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ //! //! **WARNING** This is alpha quality software at best and should not be relied upon. **WARNING** //! -//!The `desfire` Crate provides the cryptographic methods and specific commands for interfacing +//! The `desfire` Crate provides the cryptographic methods and specific commands for interfacing //! with NXP MiFare Desfire cards. //! @@ -16,20 +16,17 @@ pub mod error; pub mod iso7816_4; pub trait Card { - /// /// Connect to Smartcard - /// fn connect(&mut self) -> Result<()>; - /// /// Disconnect from Smartcard - /// fn disconnect(&mut self) -> Result<()>; - /// /// Transmit APDU Command to Smartcard - /// - /// Application Protocol Data Unit Command - ISO 7816 - /// Application Protocol Data Unit Response - ISO 7816 + /// + /// # Parameters + /// * `apdu_cmd` - Application Protocol Data Unit Command - ISO 7816 + /// + /// returns a Application Protocol Data Unit Response - ISO 7816 fn transmit(&self, apdu_cmd: APDUCommand) -> Result; }