From c171d3bdda49d9cfc4475bc6807d3f499696ea72 Mon Sep 17 00:00:00 2001 From: Kai Jan Kriegel Date: Wed, 9 Mar 2022 03:41:06 +0100 Subject: [PATCH] Initial commit --- .gitignore | 22 ++ .idea/.gitignore | 8 + .idea/discord.xml | 7 + .idea/fabfire_provision.iml | 11 + .idea/modules.xml | 8 + .idea/vcs.xml | 6 + Cargo.lock | 543 ++++++++++++++++++++++++++++++++++++ Cargo.toml | 19 ++ README.md | 15 + src/card.rs | 81 ++++++ src/main.rs | 196 +++++++++++++ 11 files changed, 916 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/discord.xml create mode 100644 .idea/fabfire_provision.iml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 src/card.rs create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aafc0d6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/rust +# Edit at https://www.toptal.com/developers/gitignore?templates=rust + +### Rust ### +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +#Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +# End of https://www.toptal.com/developers/gitignore/api/rust + diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/discord.xml b/.idea/discord.xml new file mode 100644 index 0000000..30bab2a --- /dev/null +++ b/.idea/discord.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/.idea/fabfire_provision.iml b/.idea/fabfire_provision.iml new file mode 100644 index 0000000..c254557 --- /dev/null +++ b/.idea/fabfire_provision.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..680cfe7 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..3fd8461 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,543 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[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", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-modes" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cb03d1bed155d89dce0f845b7899b18a9a163e148fd004e1c28421a783e2d8e" +dependencies = [ + "block-padding", + "cipher", +] + +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array", +] + +[[package]] +name = "clap" +version = "3.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8c93436c21e4698bacadf42917db28b23017027a4deccb35dbe47a7e7840123" +dependencies = [ + "atty", + "bitflags", + "clap_derive", + "indexmap", + "lazy_static", + "os_str_bytes", + "strsim", + "termcolor", + "textwrap", +] + +[[package]] +name = "clap_derive" +version = "3.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da95d038ede1a964ce99f49cbe27a7fb538d1da595e4b4f70b8c8f338d17bf16" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cpufeatures" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +dependencies = [ + "libc", +] + +[[package]] +name = "des" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac41dd49fb554432020d52c875fc290e110113f864c6b1b525cd62c7e7747a5d" +dependencies = [ + "byteorder", + "cipher", + "opaque-debug", +] + +[[package]] +name = "desfire" +version = "0.1.0" +dependencies = [ + "aes", + "block-modes", + "des", + "hex", + "num-derive", + "num-traits", + "rand", + "simple-error", +] + +[[package]] +name = "fabfire_provision" +version = "0.1.0" +dependencies = [ + "clap", + "desfire", + "hex", + "pcsc", + "rand", + "serde", + "serde_json", + "uriparse", + "urlencoding", + "urn", + "uuid", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "generic-array" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "indexmap" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[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]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "os_str_bytes" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" +dependencies = [ + "memchr", +] + +[[package]] +name = "pcsc" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e29e4de78a433aeecd06fb5bd55a0f9fde11dc85a14c22d482972c7edc4fdc4" +dependencies = [ + "bitflags", + "pcsc-sys", +] + +[[package]] +name = "pcsc-sys" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b7bfecba2c0f1b5efb0e7caf7533ab1c295024165bcbb066231f60d33e23ea" +dependencies = [ + "pkg-config", +] + +[[package]] +name = "pkg-config" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "serde" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "simple-error" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc47a29ce97772ca5c927f75bac34866b16d64e07f330c3248e2d7226623901b" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "uriparse" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e515b1ada404168e145ac55afba3c42f04cf972201a8552d42e2abb17c1b7221" +dependencies = [ + "fnv", + "lazy_static", +] + +[[package]] +name = "urlencoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b90931029ab9b034b300b797048cf23723400aa757e8a2bfb9d748102f9821" + +[[package]] +name = "urn" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fdd17e2f5f885834e8b89d0b10c77fbcef55281f18e6859b6bda7249fa7ce1a" + +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..e9e8da2 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "fabfire_provision" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +clap = { version = "3.1.3", features = ["derive"] } +desfire = { path = "../nfc_rs"} +serde = { version = "1.0.136", features = ["derive"] } +serde_json = "1.0.79" +hex = "0.4.3" +pcsc = "2.7.0" +rand = "0.8.5" +urn = "0.4.0" +urlencoding = "2.1.0" +uriparse = "0.6.3" +uuid = { version = "0.8.2", features = ["v4"] } diff --git a/README.md b/README.md new file mode 100644 index 0000000..2875141 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +FabFire Provisioning Tool +=== +# Usage +## Provisioning +```shell +cargo run -- --space "innovisionlab" --instance fabaccess.innovisionlab.de --contact https://innovisionlab.de/lostandfound +``` +Replace `--space`, `--instance` and `--contact` with your own values. +You can supply your own keys and Application ID with the appropriate cmdline arguments, view `--help` for more information. + +## Formating Card +```shell +cargo run -- --format +``` +Formats the card, deleting all files and keys. \ No newline at end of file diff --git a/src/card.rs b/src/card.rs new file mode 100644 index 0000000..9dc430f --- /dev/null +++ b/src/card.rs @@ -0,0 +1,81 @@ +use std::ffi::CString; +use desfire::Card as CardTrait; +use desfire::error::Error; +use desfire::error::Error::CardError; +use desfire::iso7816_4::apducommand::APDUCommand; +use desfire::iso7816_4::apduresponse::APDUResponse; +use pcsc::{Card, Context, MAX_BUFFER_SIZE, Protocols, Scope, ShareMode}; + +pub struct PCSCCard { + ctx: Context, + reader: CString, + card: Option, +} + +impl PCSCCard { + pub fn new() -> Result { + // Establish a PC/SC context. + let ctx = match Context::establish(Scope::User) { + Ok(ctx) => ctx, + Err(err) => { + return Err(err); + } + }; + +// List available readers. + let mut readers_buf = [0; 2048]; + let mut readers = match ctx.list_readers(&mut readers_buf) { + Ok(readers) => readers, + Err(err) => { + return Err(err) + } + }; + +// Use the first reader. + let reader = match readers.next() { + Some(reader) => reader, + None => { + return Err(pcsc::Error::NoReadersAvailable); + } + }; + + Ok(PCSCCard { + ctx, + reader: CString::from(reader), + card: None + }) + } +} + +impl CardTrait for PCSCCard { + fn connect(&mut self) -> Result<(), Error> { + self.card = match self.ctx.connect(&self.reader, ShareMode::Shared, Protocols::ANY) { + Ok(card) => Some(card), + Err(err) => { + eprintln!("Failed to connect to card: {}", err); + return Err(CardError) + } + }; + + return Ok(()) + } + + fn disconnect(&mut self) -> Result<(), Error> { + Ok(()) + } + + fn transmit(&self, apdu_cmd: APDUCommand) -> Result { + println!("{}", apdu_cmd); + let apdu = Vec::::try_from(apdu_cmd).unwrap(); + let mut rapdu_buf = [0; MAX_BUFFER_SIZE]; + let rapdu = match self.card.as_ref().as_ref().unwrap().transmit(apdu.as_slice(), &mut rapdu_buf) { + Ok(rapdu) => rapdu, + Err(err) => { + eprintln!("Failed to transmit APDU command to card: {}", err); + return Err(CardError) + } + }; + return Ok(APDUResponse::new(rapdu)) + } +} + diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..4a7057e --- /dev/null +++ b/src/main.rs @@ -0,0 +1,196 @@ +mod card; + +use std::ffi::CString; +use std::ops::Deref; +use clap::Parser; +use desfire::Card; +use desfire::crypto::cipher_key::CipherKey; +use desfire::crypto::cipher_type::CipherType; +use desfire::desfire::{ChangeApplicationKey, ChangeMasterKey, ChangeMasterKeySettings, CreateDeleteFile, CryptoOperationsType, Desfire, FileAccessRights, FileCommunication, FileDirectoryAccess, FileIdentifiers}; +use desfire::desfire::desfire::{generate_file_access_rights, generate_keysetting1, generate_keysetting2, MAX_BYTES_PER_TRANSACTION}; +use pcsc::{Context, Scope}; +use uriparse::{Authority, Path, Scheme, Segment, UnregisteredScheme, URI}; +use urn::UrnBuilder; +use uuid::Uuid; +use crate::card::PCSCCard; + +/// Simple program to greet a person +#[derive(Parser, Debug)] +#[clap(author, version, about, long_about = None)] +struct Args { + /// Application id to use + #[clap(long = "id", default_value = "1")] + app_id: u32, + + /// Masterkey for the PICC + #[clap(long)] + picc_masterkey: Option, + + /// Masterkey for the Application + #[clap(long)] + app_masterkey: Option, + + /// user authentication key + #[clap(long)] + app_authkey: Option, + + /// Magic string to identify cards + #[clap(short, long, default_value = "FABACCESS\0DESFIRE\01.0\0")] + magic: String, + + /// Name of the issuing space + #[clap(short, long, required_unless_present = "format")] + space: Option, + + /// BFFHd Instance for the space + #[clap(short, long, required_unless_present = "format")] + instance: Option, + + /// Contact option for lost cards + #[clap(short, long, required_unless_present = "format")] + contact: Option, + + /// User token (will be generated for you if not given) + #[clap(short, long)] + token: Option, + + /// Whether to format the card + #[clap(short, long)] + format: bool, +} + +fn main() -> Result<(), Box> { + let args = Args::parse(); + println!("{:?}", args); + + // connect to the card + let mut card = PCSCCard::new()?; + card.connect()?; + let mut desfire = Desfire { + card: Some(Box::new(card)), + cbc_iv: None, + session_key: None + }; + + // store the provided keys or generate new ones + let master_key = match args.picc_masterkey { + Some(key) => CipherKey::new_from_str(&key, CipherType::TDES, 0x00)?, + None => CipherKey::new_empty(CipherType::TDES)?, + }; + + let app_key = match args.app_masterkey { + Some(key) => CipherKey::new_from_str(&key, CipherType::AES, 0x10)?, + None => CipherKey::new_empty(CipherType::AES)?, + }; + + let user_key = match args.app_authkey { + Some(key) => CipherKey::new_from_str(&key, CipherType::AES, 0x10)?, + None => CipherKey::new(&rand::random::<[u8; 16]>(), CipherType::AES, 0x10)?, + }; + + // format the card if requested + if args.format { + desfire.select_application(0x000000); + desfire.authenticate_iso_des(0x00, master_key.key.as_ref(), None)?; + desfire.format_picc()?; + return Ok(()) + } else { + let space = match args.space { + Some(ref space) => space, + None => { return Err(Box::new(std::io::Error::new(std::io::ErrorKind::Other, "No space name provided"))) } + }; + + let instance = match args.instance { + Some(ref instance) => instance, + None => { return Err(Box::new(std::io::Error::new(std::io::ErrorKind::Other, "No instance name provided"))) } + }; + + let contact = match args.contact { + Some(ref contact) => contact, + None => { return Err(Box::new(std::io::Error::new(std::io::ErrorKind::Other, "No contact info provided"))) } + }; + + // encode the space info + let space_urn = UrnBuilder::new("fabaccess", &format!("lab:{}", urlencoding::encode(space))) + .build()?; + println!("Space URN: {}", space_urn); + + let instance_uri = URI::builder() + .with_scheme(Scheme::Unregistered(UnregisteredScheme::try_from("fabaccess")?)) + .with_authority(Some(Authority::try_from(instance.deref())?)) + .with_path(Path::try_from("")?) + .build()?; + println!("Instance URI: {}", instance_uri); + + let contact_uri = URI::try_from(contact.deref())?; + println!("Contact URI: {}", contact_uri); + + let token = match args.token { + Some(token) => token, + None => { + Uuid::new_v4().to_string() + } + }; + println!("Token: {}", token); + + // authenticate against picc + desfire.authenticate_iso_des(0x00, master_key.key.as_ref(), None)?; + + // generate a new application + let ks1 = generate_keysetting1(ChangeApplicationKey::MASTERKEY as u8, + ChangeMasterKeySettings::WITHMASTERKEY, + CreateDeleteFile::ONLYMASTERKEY, + FileDirectoryAccess::NOKEY, + ChangeMasterKey::CHANGEABLE)?; + let ks2 = generate_keysetting2(CryptoOperationsType::AES, FileIdentifiers::NOTUSED, 0x02)?; + desfire.create_application(args.app_id, ks1, ks2)?; + + // select the application + desfire.select_application(args.app_id); + + println!("generated application"); + + // change the application master key + desfire.authenticate_iso_aes(0x00, CipherKey::new_empty(CipherType::AES)?.key.as_ref(), None)?; + desfire.change_key_aes(0x00, app_key.key.as_ref(), app_key.key_version)?; + + + println!("changed application master key"); + + // authenticate with new application master key + desfire.authenticate_iso_aes(0x00, app_key.key.as_ref(), None)?; + + println!("authenticated with new application master key"); + + // set the user authentication key + desfire.change_other_key_aes(0x01, user_key.key.as_ref(), CipherKey::new_empty(CipherType::AES)?.key.as_ref(), user_key.key_version)?; + + println!("changed user authentication key"); + + println!("creating magic file with size {}", args.magic.len()); + // create file with magic + let magic_accessrights = generate_file_access_rights(FileAccessRights::FREE as u8, 0x00, 0x00, 0x00)?; + desfire.create_file_standard(0x01, FileCommunication::PLAIN, magic_accessrights, args.magic.as_bytes().len() as u32)?; + println!("created magic file"); + desfire.write_data(0x01, 0x00, args.magic.as_bytes())?; + println!("wrote magic"); + + // create file with space info + let space_accessrights = generate_file_access_rights(FileAccessRights::FREE as u8, 0x00, 0x00, 0x00)?; + desfire.create_file_standard(0x02, FileCommunication::PLAIN, space_accessrights, (MAX_BYTES_PER_TRANSACTION * 3) as u32)?; + desfire.write_data(0x02, 0x00, space_urn.as_bytes())?; + desfire.write_data(0x02, MAX_BYTES_PER_TRANSACTION as u32, instance_uri.to_string().as_bytes())?; + desfire.write_data(0x02, (MAX_BYTES_PER_TRANSACTION * 2) as u32, contact_uri.to_string().as_bytes())?; + println!("created space info file"); + + // create file with token + let token_accessrights = generate_file_access_rights(0x01, 0x00, 0x00, 0x00)?; + desfire.create_file_standard(0x03, FileCommunication::PLAIN, token_accessrights, token.as_bytes().len() as u32)?; + desfire.write_data(0x03, 0x00, token.as_bytes())?; + println!("created token file"); + + Ok(()) + } +} + +