mirror of
https://gitlab.com/fabinfra/fabaccess/bffh.git
synced 2024-11-26 00:24:55 +01:00
working Desfire auth in the api!
This commit is contained in:
parent
37db05a557
commit
5c5c9710c5
3
Cargo.lock
generated
3
Cargo.lock
generated
@ -958,6 +958,9 @@ name = "hex"
|
|||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hmac"
|
name = "hmac"
|
||||||
|
@ -77,7 +77,7 @@ async-rustls = "0.2"
|
|||||||
|
|
||||||
# Desfire
|
# Desfire
|
||||||
desfire = { git = "https://gitlab.com/fabinfra/fabaccess/nfc_rs.git", branch = "main" }
|
desfire = { git = "https://gitlab.com/fabinfra/fabaccess/nfc_rs.git", branch = "main" }
|
||||||
hex = "0.4.3"
|
hex = { version = "0.4.3", features = ["serde"] }
|
||||||
linkme = "0.2"
|
linkme = "0.2"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
@ -11,3 +11,4 @@ passwd = "secret"
|
|||||||
# It will get stored in the `kv` field in UserData.
|
# It will get stored in the `kv` field in UserData.
|
||||||
# This is not used for anything at the moment
|
# This is not used for anything at the moment
|
||||||
noot = "noot!"
|
noot = "noot!"
|
||||||
|
cardkey = "7ab8704a61b5317e1fe4cae9e3e1fd8d"
|
||||||
|
104
src/api/auth.rs
104
src/api/auth.rs
@ -6,6 +6,7 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
use std::convert::TryFrom;
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
|
||||||
@ -18,7 +19,7 @@ use rsasl::callback::Callback;
|
|||||||
use rsasl::error::SessionError;
|
use rsasl::error::SessionError;
|
||||||
use rsasl::mechname::Mechname;
|
use rsasl::mechname::Mechname;
|
||||||
use rsasl::property::{AuthId, Password};
|
use rsasl::property::{AuthId, Password};
|
||||||
use rsasl::SASL;
|
use rsasl::{Property, SASL};
|
||||||
use rsasl::session::Step;
|
use rsasl::session::Step;
|
||||||
use rsasl::validate::{Validation, validations};
|
use rsasl::validate::{Validation, validations};
|
||||||
|
|
||||||
@ -32,6 +33,7 @@ use crate::db::access::AccessControl as AccessDB;
|
|||||||
|
|
||||||
mod fabfire;
|
mod fabfire;
|
||||||
use fabfire::FABFIRE;
|
use fabfire::FABFIRE;
|
||||||
|
use crate::api::auth::fabfire::FabFireCardKey;
|
||||||
|
|
||||||
pub struct AppData {
|
pub struct AppData {
|
||||||
userdb: Arc<UserDB>,
|
userdb: Arc<UserDB>,
|
||||||
@ -73,10 +75,35 @@ impl Callback for CB {
|
|||||||
};
|
};
|
||||||
Err(ret)
|
Err(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn provide_prop(
|
||||||
|
&self,
|
||||||
|
session: &mut rsasl::session::SessionData,
|
||||||
|
property: Property,
|
||||||
|
) -> Result<(), SessionError> {
|
||||||
|
match property {
|
||||||
|
FABFIRECARDKEY => {
|
||||||
|
// Access the authentication id, i.e. the username to check the password for
|
||||||
|
let authcid = session.get_property_or_callback::<AuthId>()?;
|
||||||
|
println!("auth'ing user {:?}", authcid);
|
||||||
|
self.userdb.get_user(authcid.unwrap().as_ref()).map(|user| {
|
||||||
|
let kvs= user.unwrap().data.kv;
|
||||||
|
println!("kvs: {:?}", kvs);
|
||||||
|
kvs.get("cardkey").map(|key| {
|
||||||
|
session.set_property::<FabFireCardKey>(Arc::new(<[u8; 16]>::try_from(hex::decode(key).unwrap()).unwrap()));
|
||||||
|
});
|
||||||
|
}).ok();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
_ => Err(SessionError::NoProperty { property }),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Auth {
|
pub struct Auth {
|
||||||
pub ctx: SASL,
|
pub ctx: SASL,
|
||||||
|
sasl_session: Option<rsasl::session::Session>,
|
||||||
session: Rc<RefCell<Option<Session>>>,
|
session: Rc<RefCell<Option<Session>>>,
|
||||||
userdb: Arc<UserDB>,
|
userdb: Arc<UserDB>,
|
||||||
access: Arc<AccessDB>,
|
access: Arc<AccessDB>,
|
||||||
@ -89,7 +116,7 @@ impl Auth {
|
|||||||
ctx.register(&FABFIRE);
|
ctx.register(&FABFIRE);
|
||||||
ctx.install_callback(Arc::new(CB::new(dbs.userdb.clone())));
|
ctx.install_callback(Arc::new(CB::new(dbs.userdb.clone())));
|
||||||
|
|
||||||
Self { log, ctx, session, userdb: dbs.userdb.clone(), access: dbs.access.clone() }
|
Self { log, ctx, sasl_session: None, session, userdb: dbs.userdb.clone(), access: dbs.access.clone() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,6 +161,7 @@ impl authentication_system::Server for Auth {
|
|||||||
// Or fail at that and thrown an exception TODO: return Outcome
|
// Or fail at that and thrown an exception TODO: return Outcome
|
||||||
let mech = pry!(req.get_mechanism());
|
let mech = pry!(req.get_mechanism());
|
||||||
if !((mech == "PLAIN") || (mech == "X-FABFIRE")) {
|
if !((mech == "PLAIN") || (mech == "X-FABFIRE")) {
|
||||||
|
debug!(self.log, "Invalid SASL mech: {}", mech);
|
||||||
return Promise::err(capnp::Error {
|
return Promise::err(capnp::Error {
|
||||||
kind: capnp::ErrorKind::Failed,
|
kind: capnp::ErrorKind::Failed,
|
||||||
description: format!("Invalid SASL mech"),
|
description: format!("Invalid SASL mech"),
|
||||||
@ -142,8 +170,8 @@ impl authentication_system::Server for Auth {
|
|||||||
|
|
||||||
let mech = Mechname::new(mech.as_bytes()).unwrap();
|
let mech = Mechname::new(mech.as_bytes()).unwrap();
|
||||||
|
|
||||||
let mut session = match self.ctx.server_start(mech) {
|
self.sasl_session = match self.ctx.server_start(mech) {
|
||||||
Ok(s) => s,
|
Ok(s) => Some(s),
|
||||||
Err(e) =>
|
Err(e) =>
|
||||||
return Promise::err(capnp::Error {
|
return Promise::err(capnp::Error {
|
||||||
kind: capnp::ErrorKind::Failed,
|
kind: capnp::ErrorKind::Failed,
|
||||||
@ -164,10 +192,11 @@ impl authentication_system::Server for Auth {
|
|||||||
|
|
||||||
Ok(Which::None(_)) => {
|
Ok(Which::None(_)) => {
|
||||||
// FIXME: Actually this needs to indicate NO data instead of SOME data of 0 length
|
// FIXME: Actually this needs to indicate NO data instead of SOME data of 0 length
|
||||||
session.step(Option::<&[u8]>::None, &mut out)
|
self.sasl_session.as_mut().unwrap().step(Option::<&[u8]>::None, &mut out)
|
||||||
}
|
}
|
||||||
Ok(Which::Initial(data)) => {
|
Ok(Which::Initial(data)) => {
|
||||||
session.step(Some(pry!(data)), &mut out)
|
debug!(self.log, "running step() with initial data");
|
||||||
|
self.sasl_session.as_mut().unwrap().step(Some(pry!(data)), &mut out)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -176,7 +205,7 @@ impl authentication_system::Server for Auth {
|
|||||||
|
|
||||||
match step_res {
|
match step_res {
|
||||||
Ok(Step::Done(b)) => {
|
Ok(Step::Done(b)) => {
|
||||||
let user = session
|
let user = self.sasl_session.as_mut().unwrap()
|
||||||
.get_property::<AuthId>()
|
.get_property::<AuthId>()
|
||||||
.and_then(|data| {
|
.and_then(|data| {
|
||||||
self.userdb.get_user(data.as_str()).unwrap()
|
self.userdb.get_user(data.as_str()).unwrap()
|
||||||
@ -207,8 +236,67 @@ impl authentication_system::Server for Auth {
|
|||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
error!(self.log, "SASL Auth failed: {}", e);
|
||||||
let mut outcome = pry!(res.get().get_response()).init_outcome();
|
let mut outcome = pry!(res.get().get_response()).init_outcome();
|
||||||
outcome.reborrow().set_result(response::Result::InvalidCredentials);
|
outcome.reborrow().set_result(response::Result::InvalidCredentials); //FIXME: Check if problem where invalid creds or something else
|
||||||
|
let text = format!("{}", e);
|
||||||
|
outcome.set_help_text(&text);
|
||||||
|
Promise::ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn step(&mut self, params: authentication_system::StepParams, mut res: authentication_system::StepResults) -> Promise<(), capnp::Error> {
|
||||||
|
let resp = pry!(pry!(params.get()).get_response());
|
||||||
|
|
||||||
|
let mut out = Cursor::new(Vec::new());
|
||||||
|
|
||||||
|
// Use the data the Client has provided
|
||||||
|
let step_res = self.sasl_session.as_mut().unwrap().step(Some(resp), &mut out);
|
||||||
|
|
||||||
|
|
||||||
|
debug!(self.log, "running step() with additional data");
|
||||||
|
|
||||||
|
|
||||||
|
// The step may either return an error, a success or the need for more data
|
||||||
|
// TODO: Set the session user. Needs a lookup though <.>
|
||||||
|
|
||||||
|
match step_res {
|
||||||
|
Ok(Step::Done(b)) => {
|
||||||
|
let user = self.sasl_session.as_mut().unwrap()
|
||||||
|
.get_property::<AuthId>()
|
||||||
|
.and_then(|data| {
|
||||||
|
self.userdb.get_user(data.as_str()).unwrap()
|
||||||
|
})
|
||||||
|
.expect("Authentication returned OK but the given AuthId is invalid");
|
||||||
|
|
||||||
|
let perms = pry!(self.access.collect_permrules(&user.data)
|
||||||
|
.map_err(|e| capnp::Error::failed(format!("AccessDB lookup failed: {}", e))));
|
||||||
|
self.session.replace(Some(Session::new(
|
||||||
|
self.log.new(o!()),
|
||||||
|
user.id,
|
||||||
|
"".to_string(),
|
||||||
|
user.data.roles.into_boxed_slice(),
|
||||||
|
perms.into_boxed_slice()
|
||||||
|
)));
|
||||||
|
|
||||||
|
let mut outcome = pry!(res.get().get_response()).init_outcome();
|
||||||
|
outcome.reborrow().set_result(response::Result::Successful);
|
||||||
|
if b.is_some() {
|
||||||
|
outcome.init_additional_data().set_additional(&out.get_ref());
|
||||||
|
}
|
||||||
|
Promise::ok(())
|
||||||
|
},
|
||||||
|
Ok(Step::NeedsMore(b)) => {
|
||||||
|
if b.is_some() {
|
||||||
|
pry!(res.get().get_response()).set_challence(&out.get_ref());
|
||||||
|
}
|
||||||
|
Promise::ok(())
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!(self.log, "SASL Auth failed: {}", e);
|
||||||
|
let mut outcome = pry!(res.get().get_response()).init_outcome();
|
||||||
|
outcome.reborrow().set_result(response::Result::InvalidCredentials); //FIXME: Check if problem where invalid creds or something else
|
||||||
let text = format!("{}", e);
|
let text = format!("{}", e);
|
||||||
outcome.set_help_text(&text);
|
outcome.set_help_text(&text);
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
|
@ -18,3 +18,27 @@ pub static FABFIRE: Mechanism = Mechanism {
|
|||||||
server: Some(FabFire::new_server),
|
server: Some(FabFire::new_server),
|
||||||
first: Side::Client,
|
first: Side::Client,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use rsasl::property::{Property, PropertyQ, PropertyDefinition};
|
||||||
|
// 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",
|
||||||
|
));
|
||||||
|
@ -6,16 +6,24 @@ use rsasl::SASL;
|
|||||||
use rsasl::session::{SessionData, StepResult};
|
use rsasl::session::{SessionData, StepResult};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use desfire::desfire::Desfire;
|
use desfire::desfire::Desfire;
|
||||||
use desfire::iso7816_4::apducommand::APDUCommand;
|
|
||||||
use desfire::iso7816_4::apduresponse::APDUResponse;
|
use desfire::iso7816_4::apduresponse::APDUResponse;
|
||||||
use desfire::error::{Error as DesfireError, Error};
|
use desfire::error::{Error as DesfireError};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::ops::Deref;
|
use std::sync::Arc;
|
||||||
|
use desfire::desfire::desfire::MAX_BYTES_PER_TRANSACTION;
|
||||||
|
use rsasl::property::AuthId;
|
||||||
|
use crate::api::auth::fabfire::FabFireCardKey;
|
||||||
|
|
||||||
enum FabFireError {
|
enum FabFireError {
|
||||||
ParseError,
|
ParseError,
|
||||||
SerializationError,
|
SerializationError,
|
||||||
|
DeserializationError(serde_json::Error),
|
||||||
CardError(DesfireError),
|
CardError(DesfireError),
|
||||||
|
InvalidMagic(String),
|
||||||
|
InvalidToken(String),
|
||||||
|
InvalidURN(String),
|
||||||
|
InvalidCredentials(String),
|
||||||
|
Session(SessionError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for FabFireError {
|
impl Debug for FabFireError {
|
||||||
@ -23,7 +31,13 @@ impl Debug for FabFireError {
|
|||||||
match self {
|
match self {
|
||||||
FabFireError::ParseError => write!(f, "ParseError"),
|
FabFireError::ParseError => write!(f, "ParseError"),
|
||||||
FabFireError::SerializationError => write!(f, "SerializationError"),
|
FabFireError::SerializationError => write!(f, "SerializationError"),
|
||||||
|
FabFireError::DeserializationError(e) => write!(f, "DeserializationError: {}", e),
|
||||||
FabFireError::CardError(err) => write!(f, "CardError: {}", err),
|
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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -33,7 +47,13 @@ impl Display for FabFireError {
|
|||||||
match self {
|
match self {
|
||||||
FabFireError::ParseError => write!(f, "ParseError"),
|
FabFireError::ParseError => write!(f, "ParseError"),
|
||||||
FabFireError::SerializationError => write!(f, "SerializationError"),
|
FabFireError::SerializationError => write!(f, "SerializationError"),
|
||||||
|
FabFireError::DeserializationError(e) => write!(f, "DeserializationError: {}", e),
|
||||||
FabFireError::CardError(err) => write!(f, "CardError: {}", err),
|
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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -43,45 +63,61 @@ impl MechanismError for FabFireError {
|
|||||||
match self {
|
match self {
|
||||||
FabFireError::ParseError => MechanismErrorKind::Parse,
|
FabFireError::ParseError => MechanismErrorKind::Parse,
|
||||||
FabFireError::SerializationError => MechanismErrorKind::Protocol,
|
FabFireError::SerializationError => MechanismErrorKind::Protocol,
|
||||||
|
FabFireError::DeserializationError(_) => MechanismErrorKind::Parse,
|
||||||
FabFireError::CardError(_) => MechanismErrorKind::Protocol,
|
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)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
struct CardInfo {
|
struct CardInfo {
|
||||||
card_uid: [u8; 7],
|
#[serde(rename = "UID", with = "hex")]
|
||||||
|
uid: [u8; 7],
|
||||||
key_old: Option<Box<[u8]>>,
|
key_old: Option<Box<[u8]>>,
|
||||||
key_new: Option<Box<[u8]>>
|
key_new: Option<Box<[u8]>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct KeyInfo {
|
struct KeyInfo {
|
||||||
key_id: u8,
|
key_id: u8,
|
||||||
key: Box<[u8]>
|
key: Box<[u8]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AuthInfo {
|
struct AuthInfo {
|
||||||
rnd_a: Vec<u8>,
|
rnd_a: Vec<u8>,
|
||||||
rnd_b: Vec<u8>,
|
rnd_b: Vec<u8>,
|
||||||
iv: Vec<u8>
|
iv: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
#[serde(tag = "Cmd")]
|
#[serde(tag = "Cmd")]
|
||||||
enum CardCommand {
|
enum CardCommand {
|
||||||
message {
|
message {
|
||||||
|
#[serde(rename = "MssgID", skip_serializing_if = "Option::is_none")]
|
||||||
msg_id: Option<u32>,
|
msg_id: Option<u32>,
|
||||||
|
#[serde(rename = "ClrTxt", skip_serializing_if = "Option::is_none")]
|
||||||
clr_txt: Option<String>,
|
clr_txt: Option<String>,
|
||||||
|
#[serde(rename = "AddnTxt", skip_serializing_if = "Option::is_none")]
|
||||||
addn_txt: Option<String>,
|
addn_txt: Option<String>,
|
||||||
},
|
},
|
||||||
sendPICC {
|
sendPICC {
|
||||||
data: String
|
#[serde(deserialize_with = "hex::deserialize", serialize_with = "hex::serialize_upper")]
|
||||||
|
data: Vec<u8>
|
||||||
|
},
|
||||||
|
readPICC {
|
||||||
|
#[serde(deserialize_with = "hex::deserialize", serialize_with = "hex::serialize_upper")]
|
||||||
|
data: Vec<u8>
|
||||||
},
|
},
|
||||||
haltPICC,
|
haltPICC,
|
||||||
Key {
|
Key {
|
||||||
data: String
|
data: String
|
||||||
},
|
},
|
||||||
ConfirmUser
|
ConfirmUser,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Step {
|
enum Step {
|
||||||
@ -92,7 +128,6 @@ enum Step {
|
|||||||
GetToken,
|
GetToken,
|
||||||
Authenticate1,
|
Authenticate1,
|
||||||
Authenticate2,
|
Authenticate2,
|
||||||
Authenticate3,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FabFire {
|
pub struct FabFire {
|
||||||
@ -117,52 +152,70 @@ impl Authentication for FabFire {
|
|||||||
fn step(&mut self, session: &mut SessionData, input: Option<&[u8]>, writer: &mut dyn Write) -> StepResult {
|
fn step(&mut self, session: &mut SessionData, input: Option<&[u8]>, writer: &mut dyn Write) -> StepResult {
|
||||||
match self.step {
|
match self.step {
|
||||||
Step::New => {
|
Step::New => {
|
||||||
|
// println!("Step: New");
|
||||||
//receive card info (especially card UID) from reader
|
//receive card info (especially card UID) from reader
|
||||||
return match input {
|
return match input {
|
||||||
None => { Err(SessionError::InputDataRequired) },
|
None => { Err(SessionError::InputDataRequired) }
|
||||||
Some(cardinfo) => {
|
Some(cardinfo) => {
|
||||||
self.card_info = match serde_json::from_slice(cardinfo) {
|
self.card_info = match serde_json::from_slice(cardinfo) {
|
||||||
Ok(card_info) => Some(card_info),
|
Ok(card_info) => Some(card_info),
|
||||||
Err(_) => {
|
Err(e) => {
|
||||||
return Err(FabFireError::ParseError.into())
|
// eprintln!("{:?}", e);
|
||||||
|
return Err(FabFireError::DeserializationError(e).into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.step = Step::SelectApp;
|
|
||||||
Ok(rsasl::session::Step::NeedsMore(None))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Step::SelectApp => {
|
|
||||||
//select application
|
//select application
|
||||||
let buf = match self.desfire.select_application_cmd(self.app_id) {
|
let buf = match self.desfire.select_application_cmd(self.app_id) {
|
||||||
Ok(buf) => match Vec::<u8>::try_from(buf) {
|
Ok(buf) => match Vec::<u8>::try_from(buf) {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(_) => {
|
Err(e) => {
|
||||||
return Err(FabFireError::SerializationError.into())
|
// eprintln!("Failed to convert APDUCommand to Vec<u8>: {:?}", e);
|
||||||
|
return Err(FabFireError::SerializationError.into());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(_) => {
|
Err(e) => {
|
||||||
return Err(FabFireError::SerializationError.into())
|
// eprintln!("Failed to generate APDUCommand: {:?}", e);
|
||||||
|
return Err(FabFireError::SerializationError.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let cmd = CardCommand::sendPICC { data: hex::encode_upper(buf) };
|
let cmd = CardCommand::sendPICC { data: buf };
|
||||||
return match serde_json::to_writer(writer, &cmd) {
|
return match serde_json::to_vec(&cmd) {
|
||||||
Ok(_) => {
|
Ok(send_buf) => {
|
||||||
self.step = Step::VerifyMagic;
|
self.step = Step::SelectApp;
|
||||||
Ok(rsasl::session::Step::NeedsMore(None))
|
writer.write_all(&send_buf).map_err(|e| SessionError::Io { source: e })?;
|
||||||
|
Ok(rsasl::session::Step::NeedsMore(Some(send_buf.len())))
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(e) => {
|
||||||
|
// eprintln!("Failed to serialize APDUCommand: {:?}", e);
|
||||||
Err(FabFireError::SerializationError.into())
|
Err(FabFireError::SerializationError.into())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
Step::VerifyMagic => {
|
|
||||||
// check that we successfully selected the application
|
|
||||||
let response = match input {
|
|
||||||
None => {return Err(SessionError::InputDataRequired)},
|
|
||||||
Some(buf) => APDUResponse::new(buf)
|
|
||||||
};
|
};
|
||||||
response.check().map_err(|e| FabFireError::CardError(e))?;
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Step::SelectApp => {
|
||||||
|
// println!("Step: SelectApp");
|
||||||
|
// check that we successfully selected the application
|
||||||
|
let response: CardCommand = match input {
|
||||||
|
None => { return Err(SessionError::InputDataRequired); }
|
||||||
|
Some(buf) => match serde_json::from_slice(buf).map_err(|e| FabFireError::DeserializationError(e)) {
|
||||||
|
Ok(response) => response,
|
||||||
|
Err(e) => {
|
||||||
|
// eprintln!("{:?}", e);
|
||||||
|
return Err(e.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let apdu_response = match response {
|
||||||
|
CardCommand::readPICC { data } => { APDUResponse::new(&*data) }
|
||||||
|
_ => {
|
||||||
|
// eprintln!("Unexpected response: {:?}", response);
|
||||||
|
return Err(FabFireError::ParseError.into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
apdu_response.check().map_err(|e| FabFireError::CardError(e))?;
|
||||||
|
|
||||||
// request the contents of the file containing the magic string
|
// request the contents of the file containing the magic string
|
||||||
const MAGIC_FILE_ID: u8 = 0x01;
|
const MAGIC_FILE_ID: u8 = 0x01;
|
||||||
@ -171,40 +224,58 @@ impl Authentication for FabFire {
|
|||||||
Ok(buf) => match Vec::<u8>::try_from(buf) {
|
Ok(buf) => match Vec::<u8>::try_from(buf) {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return Err(FabFireError::SerializationError.into())
|
return Err(FabFireError::SerializationError.into());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return Err(FabFireError::SerializationError.into())
|
return Err(FabFireError::SerializationError.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let cmd = CardCommand::sendPICC { data: hex::encode_upper(buf) };
|
let cmd = CardCommand::sendPICC { data: buf };
|
||||||
return match serde_json::to_writer(writer, &cmd) {
|
return match serde_json::to_vec(&cmd) {
|
||||||
Ok(_) => {
|
Ok(send_buf) => {
|
||||||
self.step = Step::GetURN;
|
self.step = Step::VerifyMagic;
|
||||||
Ok(rsasl::session::Step::NeedsMore(None))
|
writer.write_all(&send_buf).map_err(|e| SessionError::Io { source: e })?;
|
||||||
|
Ok(rsasl::session::Step::NeedsMore(Some(send_buf.len())))
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
Err(FabFireError::SerializationError.into())
|
Err(FabFireError::SerializationError.into())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
Step::GetURN => {
|
|
||||||
// verify the magic string to determine that we have a valid fabfire card
|
|
||||||
let response = match input {
|
|
||||||
None => {return Err(SessionError::InputDataRequired)},
|
|
||||||
Some(buf) => APDUResponse::new(buf)
|
|
||||||
};
|
};
|
||||||
match response.check() {
|
}
|
||||||
|
Step::VerifyMagic => {
|
||||||
|
// println!("Step: VerifyMagic");
|
||||||
|
// verify the magic string to determine that we have a valid fabfire card
|
||||||
|
let response: CardCommand = match input {
|
||||||
|
None => { return Err(SessionError::InputDataRequired); }
|
||||||
|
Some(buf) => match serde_json::from_slice(buf).map_err(|e| FabFireError::DeserializationError(e)) {
|
||||||
|
Ok(response) => response,
|
||||||
|
Err(e) => {
|
||||||
|
// eprintln!("{:?}", e);
|
||||||
|
return Err(e.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let apdu_response = match response {
|
||||||
|
CardCommand::readPICC { data } => { APDUResponse::new(&*data) }
|
||||||
|
_ => {
|
||||||
|
// eprintln!("Unexpected response: {:?}", response);
|
||||||
|
return Err(FabFireError::ParseError.into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
match apdu_response.check() {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
match response.body {
|
match apdu_response.body {
|
||||||
Some(data) => {
|
Some(data) => {
|
||||||
if std::str::from_utf8(data.as_slice()) != Ok(MAGIC) {
|
if std::str::from_utf8(data.as_slice()) != Ok(MAGIC) {
|
||||||
return Err(FabFireError::ParseError.into());
|
return Err(FabFireError::ParseError.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
return Err(FabFireError::ParseError.into())
|
return Err(FabFireError::ParseError.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -221,92 +292,143 @@ impl Authentication for FabFire {
|
|||||||
Ok(buf) => match Vec::<u8>::try_from(buf) {
|
Ok(buf) => match Vec::<u8>::try_from(buf) {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return Err(FabFireError::SerializationError.into())
|
return Err(FabFireError::SerializationError.into());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return Err(FabFireError::SerializationError.into())
|
return Err(FabFireError::SerializationError.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let cmd = CardCommand::sendPICC { data: hex::encode_upper(buf) };
|
let cmd = CardCommand::sendPICC { data: buf };
|
||||||
return match serde_json::to_writer(writer, &cmd) {
|
return match serde_json::to_vec(&cmd) {
|
||||||
Ok(_) => {
|
Ok(send_buf) => {
|
||||||
self.step = Step::GetToken;
|
self.step = Step::GetURN;
|
||||||
Ok(rsasl::session::Step::NeedsMore(None))
|
writer.write_all(&send_buf).map_err(|e| SessionError::Io { source: e })?;
|
||||||
|
Ok(rsasl::session::Step::NeedsMore(Some(send_buf.len())))
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
Err(FabFireError::SerializationError.into())
|
Err(FabFireError::SerializationError.into())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
Step::GetToken => {
|
|
||||||
// parse the urn and match it to our local urn
|
|
||||||
let response = match input {
|
|
||||||
None => {return Err(SessionError::InputDataRequired)},
|
|
||||||
Some(buf) => APDUResponse::new(buf)
|
|
||||||
};
|
};
|
||||||
match response.check() {
|
}
|
||||||
|
Step::GetURN => {
|
||||||
|
// println!("Step: GetURN");
|
||||||
|
// parse the urn and match it to our local urn
|
||||||
|
let response: CardCommand = match input {
|
||||||
|
None => { return Err(SessionError::InputDataRequired); }
|
||||||
|
Some(buf) => match serde_json::from_slice(buf).map_err(|e| FabFireError::DeserializationError(e)) {
|
||||||
|
Ok(response) => response,
|
||||||
|
Err(e) => {
|
||||||
|
// eprintln!("{:?}", e);
|
||||||
|
return Err(e.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let apdu_response = match response {
|
||||||
|
CardCommand::readPICC { data } => { APDUResponse::new(&*data) }
|
||||||
|
_ => {
|
||||||
|
// eprintln!("Unexpected response: {:?}", response);
|
||||||
|
return Err(FabFireError::ParseError.into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
match apdu_response.check() {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
match response.body {
|
match apdu_response.body {
|
||||||
Some(data) => {
|
Some(data) => {
|
||||||
if String::from_utf8(data).unwrap() != self.local_urn {
|
let received_urn = String::from_utf8(data).unwrap();
|
||||||
|
if received_urn != self.local_urn {
|
||||||
|
// eprintln!("URN mismatch: {:?} != {:?}", received_urn, self.local_urn);
|
||||||
return Err(FabFireError::ParseError.into());
|
return Err(FabFireError::ParseError.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
return Err(FabFireError::ParseError.into())
|
// eprintln!("No data in response");
|
||||||
|
return Err(FabFireError::ParseError.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(e) => {
|
||||||
|
// eprintln!("Invalid response: {:?}", e);
|
||||||
return Err(FabFireError::ParseError.into());
|
return Err(FabFireError::ParseError.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// request the contents of the file containing the URN
|
// request the contents of the file containing the URN
|
||||||
const TOKEN_FILE_ID: u8 = 0x03;
|
const TOKEN_FILE_ID: u8 = 0x03;
|
||||||
|
|
||||||
let buf = match self.desfire.read_data_chunk_cmd(TOKEN_FILE_ID, 0, 47) { // TODO: support data longer than 47 Bytes
|
let buf = 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::<u8>::try_from(buf) {
|
Ok(buf) => match Vec::<u8>::try_from(buf) {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return Err(FabFireError::SerializationError.into())
|
return Err(FabFireError::SerializationError.into());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return Err(FabFireError::SerializationError.into())
|
return Err(FabFireError::SerializationError.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let cmd = CardCommand::sendPICC { data: hex::encode_upper(buf) };
|
let cmd = CardCommand::sendPICC { data: buf };
|
||||||
return match serde_json::to_writer(writer, &cmd) {
|
return match serde_json::to_vec(&cmd) {
|
||||||
Ok(_) => {
|
Ok(send_buf) => {
|
||||||
self.step = Step::Authenticate1;
|
self.step = Step::GetToken;
|
||||||
Ok(rsasl::session::Step::NeedsMore(None))
|
writer.write_all(&send_buf).map_err(|e| SessionError::Io { source: e })?;
|
||||||
|
Ok(rsasl::session::Step::NeedsMore(Some(send_buf.len())))
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
Err(FabFireError::SerializationError.into())
|
Err(FabFireError::SerializationError.into())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
Step::Authenticate1 => {
|
|
||||||
// parse the token and select the appropriate user
|
|
||||||
let response = match input {
|
|
||||||
None => {return Err(SessionError::InputDataRequired)},
|
|
||||||
Some(buf) => APDUResponse::new(buf)
|
|
||||||
};
|
};
|
||||||
match response.check() {
|
}
|
||||||
Ok(_) => {
|
Step::GetToken => {
|
||||||
match response.body {
|
// println!("Step: GetToken");
|
||||||
Some(data) => {
|
// parse the token and select the appropriate user
|
||||||
if String::from_utf8(data).unwrap() != "LoremIpsum" { // FIXME: match against user db
|
let response: CardCommand = match input {
|
||||||
|
None => { return Err(SessionError::InputDataRequired); }
|
||||||
|
Some(buf) => match serde_json::from_slice(buf).map_err(|e| FabFireError::DeserializationError(e)) {
|
||||||
|
Ok(response) => response,
|
||||||
|
Err(e) => {
|
||||||
|
// eprintln!("{:?}", e);
|
||||||
|
return Err(e.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let apdu_response = match response {
|
||||||
|
CardCommand::readPICC { data } => { APDUResponse::new(&*data) }
|
||||||
|
_ => {
|
||||||
|
// eprintln!("Unexpected response: {:?}", response);
|
||||||
return Err(FabFireError::ParseError.into());
|
return Err(FabFireError::ParseError.into());
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
match apdu_response.check() {
|
||||||
|
Ok(_) => {
|
||||||
|
match apdu_response.body {
|
||||||
|
Some(data) => {
|
||||||
|
let token = String::from_utf8(data).unwrap();
|
||||||
|
session.set_property::<AuthId>(Arc::new(token.trim_matches(char::from(0)).to_string()));
|
||||||
|
let key = match session.get_property_or_callback::<FabFireCardKey>() {
|
||||||
|
Ok(Some(key)) => Box::from(key.as_slice()),
|
||||||
|
Ok(None) => {
|
||||||
|
return Err(FabFireError::InvalidCredentials("No keys on file for token".to_string()).into());
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
return Err(FabFireError::Session(e).into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.key_info = Some(KeyInfo{ key_id: 0x01, key });
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
return Err(FabFireError::ParseError.into())
|
// eprintln!("No data in response");
|
||||||
|
return Err(FabFireError::ParseError.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(e) => {
|
||||||
|
// eprintln!("Invalid response: {:?}", e);
|
||||||
return Err(FabFireError::ParseError.into());
|
return Err(FabFireError::ParseError.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -315,62 +437,85 @@ impl Authentication for FabFire {
|
|||||||
Ok(buf) => match Vec::<u8>::try_from(buf) {
|
Ok(buf) => match Vec::<u8>::try_from(buf) {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return Err(FabFireError::SerializationError.into())
|
return Err(FabFireError::SerializationError.into());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return Err(FabFireError::SerializationError.into())
|
return Err(FabFireError::SerializationError.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let cmd = CardCommand::sendPICC { data: hex::encode_upper(buf) };
|
let cmd = CardCommand::sendPICC { data: buf };
|
||||||
return match serde_json::to_writer(writer, &cmd) {
|
return match serde_json::to_vec(&cmd) {
|
||||||
Ok(_) => {
|
Ok(send_buf) => {
|
||||||
self.step = Step::Authenticate2;
|
self.step = Step::Authenticate1;
|
||||||
Ok(rsasl::session::Step::NeedsMore(None))
|
writer.write_all(&send_buf).map_err(|e| SessionError::Io { source: e })?;
|
||||||
|
Ok(rsasl::session::Step::NeedsMore(Some(send_buf.len())))
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
Err(FabFireError::SerializationError.into())
|
Err(FabFireError::SerializationError.into())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
Step::Authenticate2 => {
|
|
||||||
let response = match input {
|
|
||||||
None => {return Err(SessionError::InputDataRequired)},
|
|
||||||
Some(buf) => APDUResponse::new(buf)
|
|
||||||
};
|
};
|
||||||
match response.check() {
|
}
|
||||||
|
Step::Authenticate1 => {
|
||||||
|
// println!("Step: Authenticate1");
|
||||||
|
let response: CardCommand = match input {
|
||||||
|
None => { return Err(SessionError::InputDataRequired); }
|
||||||
|
Some(buf) => match serde_json::from_slice(buf).map_err(|e| FabFireError::DeserializationError(e)) {
|
||||||
|
Ok(response) => response,
|
||||||
|
Err(e) => {
|
||||||
|
// eprintln!("{:?}", e);
|
||||||
|
return Err(e.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let apdu_response = match response {
|
||||||
|
CardCommand::readPICC { data } => { APDUResponse::new(&*data) }
|
||||||
|
_ => {
|
||||||
|
// eprintln!("Unexpected response: {:?}", response);
|
||||||
|
return Err(FabFireError::ParseError.into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
match apdu_response.check() {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
match response.body {
|
match apdu_response.body {
|
||||||
Some(data) => {
|
Some(data) => {
|
||||||
let rnd_b_enc = data.as_slice();
|
let rnd_b_enc = data.as_slice();
|
||||||
|
|
||||||
//FIXME: This is ugly, we should find a better way to make the function testable
|
//FIXME: This is ugly, we should find a better way to make the function testable
|
||||||
//TODO: Check if we need a CSPRNG here
|
//TODO: Check if we need a CSPRNG here
|
||||||
let rnd_a: [u8; 16] = rand::random();
|
let rnd_a: [u8; 16] = rand::random();
|
||||||
println!("RND_A: {:x?}", rnd_a);
|
// println!("RND_A: {:x?}", rnd_a);
|
||||||
|
|
||||||
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();
|
let (cmd_challenge_response,
|
||||||
self.auth_info = Some(AuthInfo{rnd_a: Vec::<u8>::from(rnd_a), rnd_b, iv});
|
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::<u8>::from(rnd_a), rnd_b, iv });
|
||||||
let buf = match Vec::<u8>::try_from(cmd_challenge_response) {
|
let buf = match Vec::<u8>::try_from(cmd_challenge_response) {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return Err(FabFireError::SerializationError.into())
|
return Err(FabFireError::SerializationError.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let cmd = CardCommand::sendPICC { data: hex::encode_upper(buf) };
|
let cmd = CardCommand::sendPICC { data: buf };
|
||||||
return match serde_json::to_writer(writer, &cmd) {
|
return match serde_json::to_vec(&cmd) {
|
||||||
Ok(_) => {
|
Ok(send_buf) => {
|
||||||
self.step = Step::Authenticate3;
|
self.step = Step::Authenticate2;
|
||||||
Ok(rsasl::session::Step::NeedsMore(None))
|
writer.write_all(&send_buf).map_err(|e| SessionError::Io { source: e })?;
|
||||||
|
Ok(rsasl::session::Step::NeedsMore(Some(send_buf.len())))
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
Err(FabFireError::SerializationError.into())
|
Err(FabFireError::SerializationError.into())
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
return Err(FabFireError::ParseError.into())
|
return Err(FabFireError::ParseError.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -379,36 +524,67 @@ impl Authentication for FabFire {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Step::Authenticate3 => {
|
Step::Authenticate2 => {
|
||||||
let response = match input {
|
// println!("Step: Authenticate2");
|
||||||
None => {return Err(SessionError::InputDataRequired)},
|
let response: CardCommand = match input {
|
||||||
Some(buf) => APDUResponse::new(buf)
|
None => { return Err(SessionError::InputDataRequired); }
|
||||||
|
Some(buf) => match serde_json::from_slice(buf).map_err(|e| FabFireError::DeserializationError(e)) {
|
||||||
|
Ok(response) => response,
|
||||||
|
Err(e) => {
|
||||||
|
// eprintln!("{:?}", e);
|
||||||
|
return Err(e.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
match response.check() {
|
|
||||||
|
let apdu_response = match response {
|
||||||
|
CardCommand::readPICC { data } => { APDUResponse::new(&*data) }
|
||||||
|
_ => {
|
||||||
|
// eprintln!("Unexpected response: {:?}", response);
|
||||||
|
return Err(FabFireError::ParseError.into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
match apdu_response.check() {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
match response.body {
|
match apdu_response.body {
|
||||||
Some(data) => {
|
Some(data) => {
|
||||||
match self.auth_info.as_ref() {
|
match self.auth_info.as_ref() {
|
||||||
None => {return Err(FabFireError::ParseError.into())}
|
None => { return Err(FabFireError::ParseError.into()); }
|
||||||
Some(auth_info) => {
|
Some(auth_info) => {
|
||||||
if self.desfire.authenticate_iso_aes_verify(
|
if self.desfire.authenticate_iso_aes_verify(
|
||||||
data.as_slice(),
|
data.as_slice(),
|
||||||
auth_info.rnd_a.as_slice(),
|
auth_info.rnd_a.as_slice(),
|
||||||
auth_info.rnd_b.as_slice(), &*(self.key_info.as_ref().unwrap().key),
|
auth_info.rnd_b.as_slice(), &*(self.key_info.as_ref().unwrap().key),
|
||||||
auth_info.iv.as_slice()).is_ok() {
|
auth_info.iv.as_slice()).is_ok() {
|
||||||
// TODO: Do stuff with the info that we are authenticated
|
|
||||||
return Ok(rsasl::session::Step::Done(None));
|
let cmd = CardCommand::message{
|
||||||
|
msg_id: Some(4),
|
||||||
|
clr_txt: None,
|
||||||
|
addn_txt: Some("".to_string()),
|
||||||
|
};
|
||||||
|
return match serde_json::to_vec(&cmd) {
|
||||||
|
Ok(send_buf) => {
|
||||||
|
self.step = Step::Authenticate1;
|
||||||
|
writer.write_all(&send_buf).map_err(|e| SessionError::Io { source: e })?;
|
||||||
|
return Ok(rsasl::session::Step::Done(Some(send_buf.len())))
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
Err(FabFireError::SerializationError.into())
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
return Err(FabFireError::ParseError.into())
|
return Err(FabFireError::ParseError.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return Err(FabFireError::ParseError.into());
|
return Err(FabFireError::InvalidCredentials(format!("{}", apdu_response)).into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -416,5 +592,4 @@ impl Authentication for FabFire {
|
|||||||
|
|
||||||
return Ok(rsasl::session::Step::Done(None));
|
return Ok(rsasl::session::Step::Done(None));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -95,7 +95,7 @@ pub struct UserData {
|
|||||||
|
|
||||||
/// Additional data storage
|
/// Additional data storage
|
||||||
#[serde(flatten, skip_serializing_if = "HashMap::is_empty")]
|
#[serde(flatten, skip_serializing_if = "HashMap::is_empty")]
|
||||||
kv: HashMap<String, String>,
|
pub kv: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserData {
|
impl UserData {
|
||||||
|
Loading…
Reference in New Issue
Block a user