mirror of
https://gitlab.com/fabinfra/fabaccess/FabFire-Provisioning-Tool.git
synced 2025-03-12 23:01:53 +01:00
197 lines
7.4 KiB
Rust
197 lines
7.4 KiB
Rust
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<String>,
|
|
|
|
/// Masterkey for the Application
|
|
#[clap(long)]
|
|
app_masterkey: Option<String>,
|
|
|
|
/// user authentication key
|
|
#[clap(long)]
|
|
app_authkey: Option<String>,
|
|
|
|
/// 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<String>,
|
|
|
|
/// BFFHd Instance for the space
|
|
#[clap(short, long, required_unless_present = "format")]
|
|
instance: Option<String>,
|
|
|
|
/// Contact option for lost cards
|
|
#[clap(short, long, required_unless_present = "format")]
|
|
contact: Option<String>,
|
|
|
|
/// User token (will be generated for you if not given)
|
|
#[clap(short, long)]
|
|
token: Option<String>,
|
|
|
|
/// Whether to format the card
|
|
#[clap(short, long)]
|
|
format: bool,
|
|
}
|
|
|
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
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(FileAccessRights::FREE as u8, 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(())
|
|
}
|
|
}
|
|
|
|
|