From 410ed8cb332ebc9aa8a7324fdbdb4753f0c47c1e Mon Sep 17 00:00:00 2001 From: TheJoKlLa Date: Tue, 1 Nov 2022 15:00:52 +0100 Subject: [PATCH] Revert binarayfabfire --- bffhd/authentication/fabfire_bin/mod.rs | 44 -- bffhd/authentication/fabfire_bin/server.rs | 511 --------------------- bffhd/authentication/mod.rs | 1 - 3 files changed, 556 deletions(-) delete mode 100644 bffhd/authentication/fabfire_bin/mod.rs delete mode 100644 bffhd/authentication/fabfire_bin/server.rs diff --git a/bffhd/authentication/fabfire_bin/mod.rs b/bffhd/authentication/fabfire_bin/mod.rs deleted file mode 100644 index 0c5f271..0000000 --- a/bffhd/authentication/fabfire_bin/mod.rs +++ /dev/null @@ -1,44 +0,0 @@ -mod server; -pub use server::FabFire; - -use rsasl::mechname::Mechname; -use rsasl::registry::{Mechanism, MECHANISMS}; -use rsasl::session::Side; - -const MECHNAME: &'static Mechname = &Mechname::const_new_unchecked(b"X-FABFIRE-BIN"); - -#[linkme::distributed_slice(MECHANISMS)] -pub static FABFIRE_BIN: Mechanism = Mechanism { - mechanism: MECHNAME, - priority: 300, - // In this situation there's one struct for both sides, however you can just as well use - // different types than then have different `impl Authentication` instead of checking a value - // in self. - client: None, - server: Some(FabFire::new_server), - first: Side::Client, -}; - -use rsasl::property::{Property, PropertyDefinition, PropertyQ}; -use std::marker::PhantomData; -// All Property types must implement Debug. -#[derive(Debug)] -// The `PhantomData` in the constructor is only used so external crates can't construct this type. -pub struct FabFireCardKey(PhantomData<()>); -impl PropertyQ for FabFireCardKey { - // This is the type stored for this property. This could also be the struct itself if you - // so choose - type Item = [u8; 16]; - // You need to return the constant you define below here for things to work properly - fn property() -> Property { - FABFIRECARDKEY - } -} -// This const is used by your mechanism to query and by your users to set your property. It -// thus needs to be exported from your crate -pub const FABFIRECARDKEY: Property = Property::new(&PropertyDefinition::new( - // Short name, used in `Debug` output - "FabFireCardKey", - // A longer user-facing name used in `Display` output - "A AES128 key for a FabFire card", -)); diff --git a/bffhd/authentication/fabfire_bin/server.rs b/bffhd/authentication/fabfire_bin/server.rs deleted file mode 100644 index 602442e..0000000 --- a/bffhd/authentication/fabfire_bin/server.rs +++ /dev/null @@ -1,511 +0,0 @@ -use desfire::desfire::desfire::MAX_BYTES_PER_TRANSACTION; -use desfire::desfire::Desfire; -use desfire::error::Error as DesfireError; -use desfire::iso7816_4::apduresponse::APDUResponse; -use rsasl::error::{MechanismError, MechanismErrorKind, SASLError, SessionError}; -use rsasl::mechanism::Authentication; -use rsasl::property::AuthId; -use rsasl::session::{SessionData, StepResult}; -use rsasl::SASL; -use serde::{Deserialize, Serialize}; -use std::convert::TryFrom; -use std::fmt::{Debug, Display, Formatter}; -use std::io::Write; -use std::sync::Arc; - -use crate::authentication::fabfire::FabFireCardKey; - -enum FabFireError { - ParseError, - SerializationError, - DeserializationError(serde_json::Error), - CardError(DesfireError), - InvalidMagic(String), - InvalidToken(String), - InvalidURN(String), - InvalidCredentials(String), - Session(SessionError), -} - -impl Debug for FabFireError { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - FabFireError::ParseError => write!(f, "ParseError"), - FabFireError::SerializationError => write!(f, "SerializationError"), - FabFireError::DeserializationError(e) => write!(f, "DeserializationError: {}", e), - FabFireError::CardError(err) => write!(f, "CardError: {}", err), - FabFireError::InvalidMagic(magic) => write!(f, "InvalidMagic: {}", magic), - FabFireError::InvalidToken(token) => write!(f, "InvalidToken: {}", token), - FabFireError::InvalidURN(urn) => write!(f, "InvalidURN: {}", urn), - FabFireError::InvalidCredentials(credentials) => { - write!(f, "InvalidCredentials: {}", credentials) - } - FabFireError::Session(err) => write!(f, "Session: {}", err), - } - } -} - -impl Display for FabFireError { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - FabFireError::ParseError => write!(f, "ParseError"), - FabFireError::SerializationError => write!(f, "SerializationError"), - FabFireError::DeserializationError(e) => write!(f, "DeserializationError: {}", e), - FabFireError::CardError(err) => write!(f, "CardError: {}", err), - FabFireError::InvalidMagic(magic) => write!(f, "InvalidMagic: {}", magic), - FabFireError::InvalidToken(token) => write!(f, "InvalidToken: {}", token), - FabFireError::InvalidURN(urn) => write!(f, "InvalidURN: {}", urn), - FabFireError::InvalidCredentials(credentials) => { - write!(f, "InvalidCredentials: {}", credentials) - } - FabFireError::Session(err) => write!(f, "Session: {}", err), - } - } -} - -impl MechanismError for FabFireError { - fn kind(&self) -> MechanismErrorKind { - match self { - FabFireError::ParseError => MechanismErrorKind::Parse, - FabFireError::SerializationError => MechanismErrorKind::Protocol, - FabFireError::DeserializationError(_) => MechanismErrorKind::Parse, - FabFireError::CardError(_) => MechanismErrorKind::Protocol, - FabFireError::InvalidMagic(_) => MechanismErrorKind::Protocol, - FabFireError::InvalidToken(_) => MechanismErrorKind::Protocol, - FabFireError::InvalidURN(_) => MechanismErrorKind::Protocol, - FabFireError::InvalidCredentials(_) => MechanismErrorKind::Protocol, - FabFireError::Session(_) => MechanismErrorKind::Protocol, - } - } -} - -#[derive(Debug, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -struct CardInfo { - #[serde(rename = "UID", with = "hex")] - uid: [u8; 7], - key_old: Option>, - key_new: Option>, -} - -struct KeyInfo { - key_id: u8, - key: Box<[u8]>, -} - -struct AuthInfo { - rnd_a: Vec, - rnd_b: Vec, - iv: Vec, -} - -enum Step { - New, - SelectApp, - VerifyMagic, - GetURN, - GetToken, - Authenticate1, - Authenticate2, -} - -pub struct FabFire { - step: Step, - card_info: Option, - key_info: Option, - auth_info: Option, - app_id: u32, - local_urn: String, - desfire: Desfire, -} - -const MAGIC: &'static str = "FABACCESS\0DESFIRE\01.0\0"; - -impl FabFire { - pub fn new_server(_sasl: &SASL) -> Result, SASLError> { - Ok(Box::new(Self { - step: Step::New, - card_info: None, - key_info: None, - auth_info: None, - app_id: 0x464142, - local_urn: "urn:fabaccess:lab:innovisionlab".to_string(), - desfire: Desfire { - card: None, - session_key: None, - cbc_iv: None, - }, - })) - } -} - -impl Authentication for FabFire { - fn step( - &mut self, - session: &mut SessionData, - input: Option<&[u8]>, - writer: &mut dyn Write, - ) -> StepResult { - match self.step { - Step::New => { - tracing::trace!("Step: New"); - //receive card info (especially card UID) from reader - return match input { - None => Err(SessionError::InputDataRequired), - Some(_) => { - //select application - return match self.desfire.select_application_cmd(self.app_id) { - Ok(buf) => match Vec::::try_from(buf) { - Ok(data) => { - self.step = Step::SelectApp; - writer - .write_all(&data) - .map_err(|e| SessionError::Io { source: e })?; - Ok(rsasl::session::Step::NeedsMore(Some(data.len()))) - }, - Err(e) => { - tracing::error!( - "Failed to convert APDUCommand to Vec: {:?}", - e - ); - return Err(FabFireError::SerializationError.into()); - } - }, - Err(e) => { - tracing::error!("Failed to generate APDUCommand: {:?}", e); - return Err(FabFireError::SerializationError.into()); - } - }; - } - }; - } - Step::SelectApp => { - tracing::trace!("Step: SelectApp"); - // check that we successfully selected the application - - let apdu_response = match input { - Some(data) => APDUResponse::new(data), - None => return Err(SessionError::InputDataRequired), - }; - - apdu_response - .check() - .map_err(|e| FabFireError::CardError(e))?; - - // request the contents of the file containing the magic string - const MAGIC_FILE_ID: u8 = 0x01; - - return match self - .desfire - .read_data_chunk_cmd(MAGIC_FILE_ID, 0, MAGIC.len()) - { - Ok(buf) => match Vec::::try_from(buf) { - Ok(data) => { - self.step = Step::VerifyMagic; - writer - .write_all(&data) - .map_err(|e| SessionError::Io { source: e })?; - Ok(rsasl::session::Step::NeedsMore(Some(data.len()))) - }, - Err(e) => { - tracing::error!("Failed to convert APDUCommand to Vec: {:?}", e); - return Err(FabFireError::SerializationError.into()); - } - }, - Err(e) => { - tracing::error!("Failed to generate APDUCommand: {:?}", e); - return Err(FabFireError::SerializationError.into()); - } - }; - } - Step::VerifyMagic => { - tracing::trace!("Step: VerifyMagic"); - // verify the magic string to determine that we have a valid fabfire card - let apdu_response = match input { - Some(data) => APDUResponse::new(data), - None => return Err(SessionError::InputDataRequired), - }; - - match apdu_response.check() { - Ok(_) => { - match apdu_response.body { - Some(data) => { - if std::str::from_utf8(data.as_slice()) != Ok(MAGIC) { - tracing::error!("Invalid magic string"); - return Err(FabFireError::ParseError.into()); - } - } - None => { - tracing::error!("No data returned from card"); - return Err(FabFireError::ParseError.into()); - } - }; - } - Err(e) => { - tracing::error!("Got invalid APDUResponse: {:?}", e); - return Err(FabFireError::ParseError.into()); - } - } - - // request the contents of the file containing the URN - const URN_FILE_ID: u8 = 0x02; - - return match self.desfire.read_data_chunk_cmd( - URN_FILE_ID, - 0, - self.local_urn.as_bytes().len(), - ) { - // TODO: support urn longer than 47 Bytes - Ok(buf) => match Vec::::try_from(buf) { - Ok(data) => { - self.step = Step::GetURN; - writer - .write_all(&data) - .map_err(|e| SessionError::Io { source: e })?; - Ok(rsasl::session::Step::NeedsMore(Some(data.len()))) - }, - Err(e) => { - tracing::error!("Failed to convert APDUCommand to Vec: {:?}", e); - return Err(FabFireError::SerializationError.into()); - } - }, - Err(e) => { - tracing::error!("Failed to generate APDUCommand: {:?}", e); - return Err(FabFireError::SerializationError.into()); - } - }; - } - Step::GetURN => { - tracing::trace!("Step: GetURN"); - // parse the urn and match it to our local urn - let apdu_response = match input { - Some(data) => APDUResponse::new(data), - None => return Err(SessionError::InputDataRequired), - }; - - match apdu_response.check() { - Ok(_) => { - match apdu_response.body { - Some(data) => { - let received_urn = String::from_utf8(data).unwrap(); - if received_urn != self.local_urn { - tracing::error!( - "URN mismatch: {:?} != {:?}", - received_urn, - self.local_urn - ); - return Err(FabFireError::ParseError.into()); - } - } - None => { - tracing::error!("No data returned from card"); - return Err(FabFireError::ParseError.into()); - } - }; - } - Err(e) => { - tracing::error!("Got invalid APDUResponse: {:?}", e); - return Err(FabFireError::ParseError.into()); - } - } - // request the contents of the file containing the URN - const TOKEN_FILE_ID: u8 = 0x03; - - return match self.desfire.read_data_chunk_cmd( - TOKEN_FILE_ID, - 0, - MAX_BYTES_PER_TRANSACTION, - ) { - // TODO: support data longer than 47 Bytes - Ok(buf) => match Vec::::try_from(buf) { - Ok(data) => { - self.step = Step::GetToken; - writer - .write_all(&data) - .map_err(|e| SessionError::Io { source: e })?; - Ok(rsasl::session::Step::NeedsMore(Some(data.len()))) - }, - Err(e) => { - tracing::error!("Failed to convert APDUCommand to Vec: {:?}", e); - return Err(FabFireError::SerializationError.into()); - } - }, - Err(e) => { - tracing::error!("Failed to generate APDUCommand: {:?}", e); - return Err(FabFireError::SerializationError.into()); - } - }; - } - Step::GetToken => { - // println!("Step: GetToken"); - // parse the token and select the appropriate user - let apdu_response = match input { - Some(data) => APDUResponse::new(data), - None => return Err(SessionError::InputDataRequired), - }; - - match apdu_response.check() { - Ok(_) => { - match apdu_response.body { - Some(data) => { - let token = String::from_utf8(data).unwrap(); - session.set_property::(Arc::new( - token.trim_matches(char::from(0)).to_string(), - )); - let key = match session.get_property_or_callback::() - { - Ok(Some(key)) => Box::from(key.as_slice()), - Ok(None) => { - tracing::error!("No keys on file for token"); - return Err(FabFireError::InvalidCredentials( - "No keys on file for token".to_string(), - ) - .into()); - } - Err(e) => { - tracing::error!("Failed to get key: {:?}", e); - return Err(FabFireError::Session(e).into()); - } - }; - self.key_info = Some(KeyInfo { key_id: 0x01, key }); - } - None => { - tracing::error!("No data in response"); - return Err(FabFireError::ParseError.into()); - } - }; - } - Err(e) => { - tracing::error!("Failed to check response: {:?}", e); - return Err(FabFireError::ParseError.into()); - } - } - - return match self - .desfire - .authenticate_iso_aes_challenge_cmd(self.key_info.as_ref().unwrap().key_id) - { - Ok(buf) => match Vec::::try_from(buf) { - Ok(data) => { - self.step = Step::Authenticate1; - writer - .write_all(&data) - .map_err(|e| SessionError::Io { source: e })?; - Ok(rsasl::session::Step::NeedsMore(Some(data.len()))) - }, - Err(e) => { - tracing::error!("Failed to convert to Vec: {:?}", e); - return Err(FabFireError::SerializationError.into()); - } - }, - Err(e) => { - tracing::error!("Failed to create authenticate command: {:?}", e); - return Err(FabFireError::SerializationError.into()); - } - }; - } - Step::Authenticate1 => { - tracing::trace!("Step: Authenticate1"); - let apdu_response = match input { - Some(data) => APDUResponse::new(data), - None => return Err(SessionError::InputDataRequired), - }; - - return match apdu_response.check() { - Ok(_) => { - match apdu_response.body { - Some(data) => { - let rnd_b_enc = data.as_slice(); - - //FIXME: This is ugly, we should find a better way to make the function testable - //TODO: Check if we need a CSPRNG here - let rnd_a: [u8; 16] = rand::random(); - - let (cmd_challenge_response, rnd_b, iv) = self - .desfire - .authenticate_iso_aes_response_cmd( - rnd_b_enc, - &*(self.key_info.as_ref().unwrap().key), - &rnd_a, - ) - .unwrap(); - self.auth_info = Some(AuthInfo { - rnd_a: Vec::::from(rnd_a), - rnd_b, - iv, - }); - match Vec::::try_from(cmd_challenge_response) { - Ok(data) => { - self.step = Step::Authenticate2; - writer - .write_all(&data) - .map_err(|e| SessionError::Io { source: e })?; - Ok(rsasl::session::Step::NeedsMore(Some(data.len()))) - }, - Err(e) => { - tracing::error!("Failed to convert to Vec: {:?}", e); - return Err(FabFireError::SerializationError.into()); - } - } - } - None => { - tracing::error!("Got invalid response: {:?}", apdu_response); - Err(FabFireError::ParseError.into()) - } - } - } - Err(e) => { - tracing::error!("Failed to check response: {:?}", e); - Err(FabFireError::ParseError.into()) - } - } - } - Step::Authenticate2 => { - // println!("Step: Authenticate2"); - let apdu_response = match input { - Some(data) => APDUResponse::new(data), - None => return Err(SessionError::InputDataRequired), - }; - - match apdu_response.check() { - Ok(_) => { - match apdu_response.body { - Some(data) => match self.auth_info.as_ref() { - None => { - return Err(FabFireError::ParseError.into()); - } - Some(auth_info) => { - if self - .desfire - .authenticate_iso_aes_verify( - data.as_slice(), - auth_info.rnd_a.as_slice(), - auth_info.rnd_b.as_slice(), - &*(self.key_info.as_ref().unwrap().key), - auth_info.iv.as_slice(), - ) - .is_ok() - { - return Ok(rsasl::session::Step::Done(None)); - } - }, - }, - None => { - tracing::error!("got empty response"); - return Err(FabFireError::ParseError.into()); - } - }; - } - Err(_e) => { - tracing::error!("Got invalid response: {:?}", apdu_response); - return Err( - FabFireError::InvalidCredentials(format!("{}", apdu_response)).into(), - ); - } - } - } - } - - return Ok(rsasl::session::Step::Done(None)); - } -} diff --git a/bffhd/authentication/mod.rs b/bffhd/authentication/mod.rs index 7c94a25..68ff703 100644 --- a/bffhd/authentication/mod.rs +++ b/bffhd/authentication/mod.rs @@ -11,7 +11,6 @@ use crate::authentication::fabfire::FabFireCardKey; use crate::users::db::User; mod fabfire; -mod fabfire_bin; struct Callback { users: Users,