mirror of
https://gitlab.com/fabinfra/fabaccess/bffh.git
synced 2024-11-26 00:24:55 +01:00
initial integration of the X-FABFIRE mechnism
Integrates the fabfire mechanism for use with the jorisdevice and desfire cards
This commit is contained in:
parent
c8623fd62b
commit
4611ed5b48
94
Cargo.lock
generated
94
Cargo.lock
generated
@ -23,6 +23,18 @@ dependencies = [
|
|||||||
"pretty",
|
"pretty",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aes"
|
||||||
|
version = "0.7.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cipher",
|
||||||
|
"cpufeatures",
|
||||||
|
"opaque-debug 0.3.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "annotate-snippets"
|
name = "annotate-snippets"
|
||||||
version = "0.9.1"
|
version = "0.9.1"
|
||||||
@ -253,7 +265,7 @@ version = "0.7.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
|
checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"block-padding",
|
"block-padding 0.1.5",
|
||||||
"byte-tools",
|
"byte-tools",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"generic-array 0.12.4",
|
"generic-array 0.12.4",
|
||||||
@ -277,6 +289,16 @@ dependencies = [
|
|||||||
"generic-array 0.14.5",
|
"generic-array 0.14.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-modes"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2cb03d1bed155d89dce0f845b7899b18a9a163e148fd004e1c28421a783e2d8e"
|
||||||
|
dependencies = [
|
||||||
|
"block-padding 0.2.1",
|
||||||
|
"cipher",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-padding"
|
name = "block-padding"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
@ -286,6 +308,12 @@ dependencies = [
|
|||||||
"byte-tools",
|
"byte-tools",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-padding"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "blocking"
|
name = "blocking"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@ -378,6 +406,15 @@ version = "1.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cipher"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array 0.14.5",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "2.34.0"
|
version = "2.34.0"
|
||||||
@ -447,6 +484,32 @@ dependencies = [
|
|||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "des"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac41dd49fb554432020d52c875fc290e110113f864c6b1b525cd62c7e7747a5d"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"cipher",
|
||||||
|
"opaque-debug 0.3.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "desfire"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://gitlab.com/fabinfra/fabaccess/nfc_rs.git?branch=main#34d1d7f3a062f007fcdc229995f9013970e460a5"
|
||||||
|
dependencies = [
|
||||||
|
"aes",
|
||||||
|
"block-modes",
|
||||||
|
"des",
|
||||||
|
"hex",
|
||||||
|
"num-derive",
|
||||||
|
"num-traits",
|
||||||
|
"rand",
|
||||||
|
"simple-error",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dhall"
|
name = "dhall"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
@ -497,6 +560,7 @@ dependencies = [
|
|||||||
"capnp-rpc",
|
"capnp-rpc",
|
||||||
"capnpc",
|
"capnpc",
|
||||||
"clap",
|
"clap",
|
||||||
|
"desfire",
|
||||||
"easy-parallel",
|
"easy-parallel",
|
||||||
"flexbuffers",
|
"flexbuffers",
|
||||||
"futures 0.3.21",
|
"futures 0.3.21",
|
||||||
@ -504,8 +568,10 @@ dependencies = [
|
|||||||
"futures-test",
|
"futures-test",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"genawaiter",
|
"genawaiter",
|
||||||
|
"hex",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libc",
|
"libc",
|
||||||
|
"linkme",
|
||||||
"lmdb-rkv",
|
"lmdb-rkv",
|
||||||
"rand",
|
"rand",
|
||||||
"rsasl",
|
"rsasl",
|
||||||
@ -1131,6 +1197,26 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-derive"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num_cpus"
|
name = "num_cpus"
|
||||||
version = "1.13.1"
|
version = "1.13.1"
|
||||||
@ -1703,6 +1789,12 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simple-error"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cc47a29ce97772ca5c927f75bac34866b16d64e07f330c3248e2d7226623901b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.5"
|
version = "0.4.5"
|
||||||
|
@ -47,8 +47,8 @@ uuid = { version = "0.8.2", features = ["serde", "v4"] }
|
|||||||
clap = "2.33.3"
|
clap = "2.33.3"
|
||||||
|
|
||||||
# TODO update this if bindgen breaks (again)
|
# TODO update this if bindgen breaks (again)
|
||||||
rsasl = "2.0.0-preview2"
|
rsasl = { version = "2.0.0-preview2", features = ["unstable_custom_mechanism", "registry_static"] }
|
||||||
#rsasl = { path = "../../rsasl" }
|
#rsasl = { path = "../../rsasl", features = ["unstable_custom_mechanism", "registry_static"] }
|
||||||
|
|
||||||
rumqttc = { version = "0.10", features = ["url"] }
|
rumqttc = { version = "0.10", features = ["url"] }
|
||||||
async-compat = "0.2.1"
|
async-compat = "0.2.1"
|
||||||
@ -75,6 +75,11 @@ rustls = "0.19"
|
|||||||
rustls-pemfile = "0.2"
|
rustls-pemfile = "0.2"
|
||||||
async-rustls = "0.2"
|
async-rustls = "0.2"
|
||||||
|
|
||||||
|
# Desfire
|
||||||
|
desfire = { git = "https://gitlab.com/fabinfra/fabaccess/nfc_rs.git", branch = "main" }
|
||||||
|
hex = "0.4.3"
|
||||||
|
linkme = "0.2"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
capnpc = "0.14.4"
|
capnpc = "0.14.4"
|
||||||
# Used in build.rs to iterate over all files in schema/
|
# Used in build.rs to iterate over all files in schema/
|
||||||
|
7
Dockerfile.dev
Normal file
7
Dockerfile.dev
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# Setup build image for multistage build
|
||||||
|
FROM rust:latest
|
||||||
|
# install build deps
|
||||||
|
RUN apt-get update && apt-get upgrade -y
|
||||||
|
RUN apt-get install -yqq --no-install-recommends capnproto build-essential cmake clang libclang-dev libgsasl7-dev
|
||||||
|
|
||||||
|
COPY ../nfc_rs /nfc_rs
|
@ -30,6 +30,9 @@ use crate::db::Databases;
|
|||||||
use crate::db::user::{Internal as UserDB, User};
|
use crate::db::user::{Internal as UserDB, User};
|
||||||
use crate::db::access::AccessControl as AccessDB;
|
use crate::db::access::AccessControl as AccessDB;
|
||||||
|
|
||||||
|
mod fabfire;
|
||||||
|
use fabfire::FABFIRE;
|
||||||
|
|
||||||
pub struct AppData {
|
pub struct AppData {
|
||||||
userdb: Arc<UserDB>,
|
userdb: Arc<UserDB>,
|
||||||
}
|
}
|
||||||
@ -83,6 +86,7 @@ pub struct Auth {
|
|||||||
impl Auth {
|
impl Auth {
|
||||||
pub fn new(log: Logger, dbs: Databases, session: Rc<RefCell<Option<Session>>>) -> Self {
|
pub fn new(log: Logger, dbs: Databases, session: Rc<RefCell<Option<Session>>>) -> Self {
|
||||||
let mut ctx = SASL::new();
|
let mut ctx = SASL::new();
|
||||||
|
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, session, userdb: dbs.userdb.clone(), access: dbs.access.clone() }
|
||||||
@ -111,9 +115,10 @@ impl authentication_system::Server for Auth {
|
|||||||
for (i, m) in mechvec.into_iter().enumerate() {
|
for (i, m) in mechvec.into_iter().enumerate() {
|
||||||
res_mechs.set(i as u32, m);
|
res_mechs.set(i as u32, m);
|
||||||
}*/
|
}*/
|
||||||
// For now, only PLAIN
|
// For now, only PLAIN and X-FABFIRE
|
||||||
let mut res_mechs = res.get().init_mechs(1);
|
let mut res_mechs = res.get().init_mechs(2);
|
||||||
res_mechs.set(0, "PLAIN");
|
res_mechs.set(0, "PLAIN");
|
||||||
|
res_mechs.set(1, "X-FABFIRE");
|
||||||
|
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
@ -128,7 +133,7 @@ impl authentication_system::Server for Auth {
|
|||||||
// Extract the MECHANISM the client wants to use and start a session.
|
// Extract the MECHANISM the client wants to use and start a session.
|
||||||
// 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 pry!(req.get_mechanism()) != "PLAIN" {
|
if mech != "PLAIN" || mech != "X-FABFIRE" {
|
||||||
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"),
|
||||||
|
20
src/api/auth/fabfire.rs
Normal file
20
src/api/auth/fabfire.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
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");
|
||||||
|
|
||||||
|
#[linkme::distributed_slice(MECHANISMS)]
|
||||||
|
pub static FABFIRE: 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,
|
||||||
|
};
|
420
src/api/auth/fabfire/server.rs
Normal file
420
src/api/auth/fabfire/server.rs
Normal file
@ -0,0 +1,420 @@
|
|||||||
|
use std::fmt::{Debug, Display, Formatter};
|
||||||
|
use std::io::Write;
|
||||||
|
use rsasl::error::{MechanismError, MechanismErrorKind, SASLError, SessionError};
|
||||||
|
use rsasl::mechanism::Authentication;
|
||||||
|
use rsasl::SASL;
|
||||||
|
use rsasl::session::{SessionData, StepResult};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use desfire::desfire::Desfire;
|
||||||
|
use desfire::iso7816_4::apducommand::APDUCommand;
|
||||||
|
use desfire::iso7816_4::apduresponse::APDUResponse;
|
||||||
|
use desfire::error::{Error as DesfireError, Error};
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
enum FabFireError {
|
||||||
|
ParseError,
|
||||||
|
SerializationError,
|
||||||
|
CardError(DesfireError),
|
||||||
|
}
|
||||||
|
|
||||||
|
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::CardError(err) => write!(f, "CardError: {}", 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::CardError(err) => write!(f, "CardError: {}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MechanismError for FabFireError {
|
||||||
|
fn kind(&self) -> MechanismErrorKind {
|
||||||
|
match self {
|
||||||
|
FabFireError::ParseError => MechanismErrorKind::Parse,
|
||||||
|
FabFireError::SerializationError => MechanismErrorKind::Protocol,
|
||||||
|
FabFireError::CardError(_) => MechanismErrorKind::Protocol,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
struct CardInfo {
|
||||||
|
card_uid: [u8; 7],
|
||||||
|
key_old: Option<Box<[u8]>>,
|
||||||
|
key_new: Option<Box<[u8]>>
|
||||||
|
}
|
||||||
|
|
||||||
|
struct KeyInfo {
|
||||||
|
key_id: u8,
|
||||||
|
key: Box<[u8]>
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AuthInfo {
|
||||||
|
rnd_a: Vec<u8>,
|
||||||
|
rnd_b: Vec<u8>,
|
||||||
|
iv: Vec<u8>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
#[serde(tag = "Cmd")]
|
||||||
|
enum CardCommand {
|
||||||
|
message {
|
||||||
|
msg_id: Option<u32>,
|
||||||
|
clr_txt: Option<String>,
|
||||||
|
addn_txt: Option<String>,
|
||||||
|
},
|
||||||
|
sendPICC {
|
||||||
|
data: String
|
||||||
|
},
|
||||||
|
haltPICC,
|
||||||
|
Key {
|
||||||
|
data: String
|
||||||
|
},
|
||||||
|
ConfirmUser
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Step {
|
||||||
|
New,
|
||||||
|
SelectApp,
|
||||||
|
VerifyMagic,
|
||||||
|
GetURN,
|
||||||
|
GetToken,
|
||||||
|
Authenticate1,
|
||||||
|
Authenticate2,
|
||||||
|
Authenticate3,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FabFire {
|
||||||
|
step: Step,
|
||||||
|
card_info: Option<CardInfo>,
|
||||||
|
key_info: Option<KeyInfo>,
|
||||||
|
auth_info: Option<AuthInfo>,
|
||||||
|
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<Box<dyn Authentication>, SASLError> {
|
||||||
|
Ok(Box::new(Self { step: Step::New, card_info: None, key_info: None, auth_info: None, app_id: 1, 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 => {
|
||||||
|
//receive card info (especially card UID) from reader
|
||||||
|
return match input {
|
||||||
|
None => { Err(SessionError::InputDataRequired) },
|
||||||
|
Some(cardinfo) => {
|
||||||
|
self.card_info = match serde_json::from_slice(cardinfo) {
|
||||||
|
Ok(card_info) => Some(card_info),
|
||||||
|
Err(_) => {
|
||||||
|
return Err(FabFireError::ParseError.into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.step = Step::SelectApp;
|
||||||
|
Ok(rsasl::session::Step::NeedsMore(None))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Step::SelectApp => {
|
||||||
|
//select application
|
||||||
|
let buf = match self.desfire.select_application_cmd(self.app_id) {
|
||||||
|
Ok(buf) => match Vec::<u8>::try_from(buf) {
|
||||||
|
Ok(data) => data,
|
||||||
|
Err(_) => {
|
||||||
|
return Err(FabFireError::SerializationError.into())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
return Err(FabFireError::SerializationError.into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let cmd = CardCommand::sendPICC { data: hex::encode_upper(buf) };
|
||||||
|
return match serde_json::to_writer(writer, &cmd) {
|
||||||
|
Ok(_) => {
|
||||||
|
self.step = Step::VerifyMagic;
|
||||||
|
Ok(rsasl::session::Step::NeedsMore(None))
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
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))?;
|
||||||
|
|
||||||
|
// request the contents of the file containing the magic string
|
||||||
|
const MAGIC_FILE_ID: u8 = 0x01;
|
||||||
|
|
||||||
|
let buf = match self.desfire.read_data_chunk_cmd(MAGIC_FILE_ID, 0, MAGIC.len()) {
|
||||||
|
Ok(buf) => match Vec::<u8>::try_from(buf) {
|
||||||
|
Ok(data) => data,
|
||||||
|
Err(_) => {
|
||||||
|
return Err(FabFireError::SerializationError.into())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
return Err(FabFireError::SerializationError.into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let cmd = CardCommand::sendPICC { data: hex::encode_upper(buf) };
|
||||||
|
return match serde_json::to_writer(writer, &cmd) {
|
||||||
|
Ok(_) => {
|
||||||
|
self.step = Step::GetURN;
|
||||||
|
Ok(rsasl::session::Step::NeedsMore(None))
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
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() {
|
||||||
|
Ok(_) => {
|
||||||
|
match response.body {
|
||||||
|
Some(data) => {
|
||||||
|
if std::str::from_utf8(data.as_slice()) != Ok(MAGIC) {
|
||||||
|
return Err(FabFireError::ParseError.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return Err(FabFireError::ParseError.into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
return Err(FabFireError::ParseError.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// request the contents of the file containing the URN
|
||||||
|
const URN_FILE_ID: u8 = 0x02;
|
||||||
|
|
||||||
|
let buf = 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::<u8>::try_from(buf) {
|
||||||
|
Ok(data) => data,
|
||||||
|
Err(_) => {
|
||||||
|
return Err(FabFireError::SerializationError.into())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
return Err(FabFireError::SerializationError.into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let cmd = CardCommand::sendPICC { data: hex::encode_upper(buf) };
|
||||||
|
return match serde_json::to_writer(writer, &cmd) {
|
||||||
|
Ok(_) => {
|
||||||
|
self.step = Step::GetToken;
|
||||||
|
Ok(rsasl::session::Step::NeedsMore(None))
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
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() {
|
||||||
|
Ok(_) => {
|
||||||
|
match response.body {
|
||||||
|
Some(data) => {
|
||||||
|
if String::from_utf8(data).unwrap() != self.local_urn {
|
||||||
|
return Err(FabFireError::ParseError.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return Err(FabFireError::ParseError.into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
return Err(FabFireError::ParseError.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// request the contents of the file containing the URN
|
||||||
|
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
|
||||||
|
Ok(buf) => match Vec::<u8>::try_from(buf) {
|
||||||
|
Ok(data) => data,
|
||||||
|
Err(_) => {
|
||||||
|
return Err(FabFireError::SerializationError.into())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
return Err(FabFireError::SerializationError.into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let cmd = CardCommand::sendPICC { data: hex::encode_upper(buf) };
|
||||||
|
return match serde_json::to_writer(writer, &cmd) {
|
||||||
|
Ok(_) => {
|
||||||
|
self.step = Step::Authenticate1;
|
||||||
|
Ok(rsasl::session::Step::NeedsMore(None))
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
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(_) => {
|
||||||
|
match response.body {
|
||||||
|
Some(data) => {
|
||||||
|
if String::from_utf8(data).unwrap() != "LoremIpsum" { // FIXME: match against user db
|
||||||
|
return Err(FabFireError::ParseError.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return Err(FabFireError::ParseError.into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
return Err(FabFireError::ParseError.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let buf = match self.desfire.authenticate_iso_aes_challenge_cmd(self.key_info.as_ref().unwrap().key_id) {
|
||||||
|
Ok(buf) => match Vec::<u8>::try_from(buf) {
|
||||||
|
Ok(data) => data,
|
||||||
|
Err(_) => {
|
||||||
|
return Err(FabFireError::SerializationError.into())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
return Err(FabFireError::SerializationError.into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let cmd = CardCommand::sendPICC { data: hex::encode_upper(buf) };
|
||||||
|
return match serde_json::to_writer(writer, &cmd) {
|
||||||
|
Ok(_) => {
|
||||||
|
self.step = Step::Authenticate2;
|
||||||
|
Ok(rsasl::session::Step::NeedsMore(None))
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
Err(FabFireError::SerializationError.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
Step::Authenticate2 => {
|
||||||
|
let response = match input {
|
||||||
|
None => {return Err(SessionError::InputDataRequired)},
|
||||||
|
Some(buf) => APDUResponse::new(buf)
|
||||||
|
};
|
||||||
|
match response.check() {
|
||||||
|
Ok(_) => {
|
||||||
|
match 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();
|
||||||
|
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();
|
||||||
|
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) {
|
||||||
|
Ok(data) => data,
|
||||||
|
Err(_) => {
|
||||||
|
return Err(FabFireError::SerializationError.into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let cmd = CardCommand::sendPICC { data: hex::encode_upper(buf) };
|
||||||
|
return match serde_json::to_writer(writer, &cmd) {
|
||||||
|
Ok(_) => {
|
||||||
|
self.step = Step::Authenticate3;
|
||||||
|
Ok(rsasl::session::Step::NeedsMore(None))
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
Err(FabFireError::SerializationError.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return Err(FabFireError::ParseError.into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
return Err(FabFireError::ParseError.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Step::Authenticate3 => {
|
||||||
|
let response = match input {
|
||||||
|
None => {return Err(SessionError::InputDataRequired)},
|
||||||
|
Some(buf) => APDUResponse::new(buf)
|
||||||
|
};
|
||||||
|
match response.check() {
|
||||||
|
Ok(_) => {
|
||||||
|
match 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() {
|
||||||
|
// TODO: Do stuff with the info that we are authenticated
|
||||||
|
return Ok(rsasl::session::Step::Done(None));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return Err(FabFireError::ParseError.into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
return Err(FabFireError::ParseError.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(rsasl::session::Step::Done(None));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user