From 2343bd8310386697bee71147fac74d5018025de2 Mon Sep 17 00:00:00 2001 From: Heiko Schaefer Date: Sat, 10 Jul 2021 21:58:13 +0200 Subject: [PATCH] Initial scdc experimentation. --- Cargo.toml | 1 + openpgp-card-sequoia/Cargo.toml | 2 + openpgp-card-sequoia/src/main.rs | 22 ++++-- openpgp-card/src/apdu/mod.rs | 6 +- openpgp-card/src/card_app.rs | 7 +- openpgp-card/src/key_upload.rs | 6 +- openpgp-card/src/lib.rs | 28 +++++++- scdc/Cargo.toml | 15 ++++ scdc/src/lib.rs | 115 +++++++++++++++++++++++++++++++ 9 files changed, 181 insertions(+), 21 deletions(-) create mode 100644 scdc/Cargo.toml create mode 100644 scdc/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 45c5ddb..e48f5a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,5 @@ members = [ "openpgp-card", "openpgp-card-sequoia", + "scdc", ] diff --git a/openpgp-card-sequoia/Cargo.toml b/openpgp-card-sequoia/Cargo.toml index 4fb0e30..118e899 100644 --- a/openpgp-card-sequoia/Cargo.toml +++ b/openpgp-card-sequoia/Cargo.toml @@ -14,6 +14,8 @@ documentation = "https://docs.rs/crate/openpgp-card-sequoia" [dependencies] sequoia-openpgp = "1.3" openpgp-card = { path = "../openpgp-card", version = "0.0.1" } +openpgp-card-scdc = { path = "../scdc" } +tokio = "0.2" chrono = "0.4" anyhow = "1" thiserror = "1" diff --git a/openpgp-card-sequoia/src/main.rs b/openpgp-card-sequoia/src/main.rs index 47da491..0796aa0 100644 --- a/openpgp-card-sequoia/src/main.rs +++ b/openpgp-card-sequoia/src/main.rs @@ -9,18 +9,22 @@ use sequoia_openpgp::parse::Parse; use sequoia_openpgp::Cert; use openpgp_card::{CardBase, KeyType}; +use openpgp_card_scdc::ScdClient; // Filename of test key and test message to use: -// const TEST_KEY_PATH: &str = "example/test4k.sec"; -// const TEST_ENC_MSG: &str = "example/encrypted_to_rsa4k.asc"; +const TEST_KEY_PATH: &str = "example/test4k.sec"; +const TEST_ENC_MSG: &str = "example/encrypted_to_rsa4k.asc"; // const TEST_KEY_PATH: &str = "example/nist521.sec"; // const TEST_ENC_MSG: &str = "example/encrypted_to_nist521.asc"; -const TEST_KEY_PATH: &str = "example/test25519.sec"; -const TEST_ENC_MSG: &str = "example/encrypted_to_25519.asc"; +// const TEST_KEY_PATH: &str = "example/test25519.sec"; +// const TEST_ENC_MSG: &str = "example/encrypted_to_25519.asc"; +const SOCKET: &str = "/run/user/1000/gnupg/S.scdaemon"; + +// #[tokio::main] fn main() -> Result<(), Box> { env_logger::init(); @@ -29,7 +33,8 @@ fn main() -> Result<(), Box> { if let Ok(test_card_ident) = test_card_ident { println!("** get card"); - let mut oc = CardBase::open_by_ident(&test_card_ident)?; + // let mut oc = CardBase::open_by_ident(&test_card_ident)?; + let mut oc = ScdClient::open_scdc(SOCKET)?; // card metadata @@ -140,7 +145,9 @@ fn main() -> Result<(), Box> { // Open fresh Card for decrypt // ----------------------------- - let mut oc = CardBase::open_by_ident(&test_card_ident)?; + // let mut oc = CardBase::open_by_ident(&test_card_ident)?; + let mut oc = ScdClient::open_scdc(SOCKET)?; + let app_id = oc.get_aid()?; // Check that we're still using the expected card @@ -179,7 +186,8 @@ fn main() -> Result<(), Box> { // ----------------------------- // Open fresh Card for signing // ----------------------------- - let oc = CardBase::open_by_ident(&test_card_ident)?; + // let oc = CardBase::open_by_ident(&test_card_ident)?; + let mut oc = ScdClient::open_scdc(SOCKET)?; // Sign match oc.verify_pw1_for_signing("123456") { diff --git a/openpgp-card/src/apdu/mod.rs b/openpgp-card/src/apdu/mod.rs index c10f432..c91923b 100644 --- a/openpgp-card/src/apdu/mod.rs +++ b/openpgp-card/src/apdu/mod.rs @@ -12,7 +12,7 @@ use std::convert::TryFrom; use crate::apdu::command::Command; use crate::apdu::response::Response; use crate::errors::{OcErrorStatus, OpenpgpCardError, SmartcardError}; -use crate::CardCaps; +use crate::{CardCaps, CardClient}; #[derive(Clone, Copy, PartialEq)] pub(crate) enum Le { @@ -179,10 +179,6 @@ fn send_command_low_level( } } -pub trait CardClient { - fn transmit(&mut self, cmd: &[u8], buf_size: usize) -> Result>; -} - pub struct PcscClient { card: Card, } diff --git a/openpgp-card/src/card_app.rs b/openpgp-card/src/card_app.rs index 85675ee..7bca175 100644 --- a/openpgp-card/src/card_app.rs +++ b/openpgp-card/src/card_app.rs @@ -28,14 +28,13 @@ use crate::errors::OpenpgpCardError; use crate::tlv::tag::Tag; use crate::tlv::TlvEntry; -use crate::apdu::CardClient; use crate::Hash; use crate::{ - apdu, key_upload, parse, tlv, CardCaps, CardUploadableKey, DecryptMe, - KeyType, Sex, + apdu, key_upload, parse, tlv, CardCaps, CardClient, CardUploadableKey, + DecryptMe, KeyType, Sex, }; -pub(crate) struct CardApp { +pub struct CardApp { card_client: Box, card_caps: Option, } diff --git a/openpgp-card/src/key_upload.rs b/openpgp-card/src/key_upload.rs index edc9832..c07fb0d 100644 --- a/openpgp-card/src/key_upload.rs +++ b/openpgp-card/src/key_upload.rs @@ -4,13 +4,13 @@ use anyhow::{anyhow, Result}; use crate::apdu::command::Command; -use crate::apdu::{commands, CardClient}; +use crate::apdu::commands; use crate::card_app::CardApp; use crate::errors::OpenpgpCardError; use crate::parse::algo_attrs::{Algo, RsaAttrs}; use crate::parse::algo_info::AlgoInfo; use crate::tlv::{tag::Tag, Tlv, TlvEntry}; -use crate::{apdu, CardCaps}; +use crate::{apdu, CardCaps, CardClient}; use crate::{ tlv, CardUploadableKey, EccKey, EccType, KeyType, PrivateKeyMaterial, RSAKey, @@ -26,6 +26,8 @@ pub(crate) fn upload_key( key_type: KeyType, algo_list: Option, ) -> Result<(), OpenpgpCardError> { + println!("upload key"); + let (algo_cmd, key_cmd) = match key.get_key()? { PrivateKeyMaterial::R(rsa_key) => { // RSA bitsize diff --git a/openpgp-card/src/lib.rs b/openpgp-card/src/lib.rs index 0098aca..1caa72a 100644 --- a/openpgp-card/src/lib.rs +++ b/openpgp-card/src/lib.rs @@ -13,28 +13,46 @@ use parse::{ }; use tlv::Tlv; -use crate::apdu::{CardClient, PcscClient}; +use crate::apdu::PcscClient; use crate::card_app::CardApp; use crate::errors::{OpenpgpCardError, SmartcardError}; use std::ops::{Deref, DerefMut}; mod apdu; mod card; -mod card_app; +pub mod card_app; pub mod errors; mod key_upload; mod parse; mod tlv; +pub trait CardClient { + fn transmit(&mut self, cmd: &[u8], buf_size: usize) -> Result>; +} + /// Information about the capabilities of the card. /// (feature configuration from card metadata) #[derive(Clone, Copy)] -pub(crate) struct CardCaps { +pub struct CardCaps { pub(crate) ext_support: bool, pub(crate) chaining_support: bool, pub(crate) max_cmd_bytes: u16, } +impl CardCaps { + pub fn new( + ext_support: bool, + chaining_support: bool, + max_cmd_bytes: u16, + ) -> CardCaps { + Self { + ext_support, + chaining_support, + max_cmd_bytes, + } + } +} + /// Container for a hash value. /// These hash values can be signed by the card. pub enum Hash<'a> { @@ -228,6 +246,10 @@ pub struct CardBase { } impl CardBase { + pub fn new(card_app: CardApp, ard: Tlv) -> Self { + Self { card_app, ard } + } + /// Get all cards that can be opened as an OpenPGP card applet pub fn list_cards() -> Result> { let cards = card::get_cards().map_err(|err| anyhow!(err))?; diff --git a/scdc/Cargo.toml b/scdc/Cargo.toml new file mode 100644 index 0000000..be910cf --- /dev/null +++ b/scdc/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "openpgp-card-scdc" +description = "Experimental SCDaemon Client, intended for use in the openpgp-card crate" +version = "0.0.1" +edition = "2018" + +[dependencies] +openpgp-card = { path = "../openpgp-card" } +sequoia-ipc = { path = "../../sequoia/ipc" } +libc = "0.2" +hex = "0.4" +anyhow = "1" +futures = "0.3" +tokio = "0.2" +lazy_static = "1.4" \ No newline at end of file diff --git a/scdc/src/lib.rs b/scdc/src/lib.rs new file mode 100644 index 0000000..9dd9bc9 --- /dev/null +++ b/scdc/src/lib.rs @@ -0,0 +1,115 @@ +use anyhow::{anyhow, Result}; +use futures::StreamExt; +use sequoia_ipc::assuan::{Client, Response}; +use std::sync::{Arc, Mutex}; +use tokio::runtime::Runtime; + +use lazy_static::lazy_static; +use openpgp_card::card_app::CardApp; +use openpgp_card::errors::OpenpgpCardError; +use openpgp_card::{CardBase, CardCaps, CardClient}; + +lazy_static! { + pub(crate) static ref RT: Mutex = + { Mutex::new(tokio::runtime::Runtime::new().unwrap()) }; +} + +pub struct ScdClient { + client: Arc>, +} + +impl ScdClient { + /// Create a CardBase object that uses an scdaemon instance as its + /// backend. + pub fn open_scdc(socket: &str) -> Result { + println!("open_scdc"); + + let card_client = ScdClient::new(socket)?; + + let ccb = Box::new(card_client) as Box; + + println!("get ard"); + + // read and cache "application related data" + let mut card_app = CardApp::new(ccb); + let ard = card_app.get_app_data()?; + + println!("got ard"); + + // Determine chaining/extended length support from card + // metadata and cache this information in CardApp (as a + // CardCaps) + + let mut ext_support = false; + let mut chaining_support = false; + + if let Ok(hist) = CardApp::get_historical(&ard) { + if let Some(cc) = hist.get_card_capabilities() { + chaining_support = cc.get_command_chaining(); + ext_support = cc.get_extended_lc_le(); + } + } + + let max_cmd_bytes = if let Ok(Some(eli)) = + CardApp::get_extended_length_information(&ard) + { + eli.max_command_bytes + } else { + 255 + }; + + let caps = CardCaps::new(ext_support, chaining_support, max_cmd_bytes); + let card_app = card_app.set_caps(caps); + + Ok(CardBase::new(card_app, ard)) + } + + pub fn new(socket: &str) -> Result { + let client = RT.lock().unwrap().block_on(Client::connect(socket))?; + let client = Arc::new(Mutex::new(client)); + Ok(Self { client }) + } + + async fn transmit_async(&mut self, cmd: &[u8]) -> Result> { + let hex = hex::encode(cmd); + + let mut client = self.client.lock().unwrap(); + + let mut res = None; + + { + let send = format!("APDU {}\n", hex); + println!("send: '{}'", send); + client.send(send)?; + + while let Some(response) = client.next().await { + println!("res: {:?}", response); + if let Ok(Response::Data { partial }) = response { + res = Some(partial); + + // drop remaining lines + while let Some(_) = client.next().await {} + + break; + } + } + } + + match res { + Some(s) => Ok(s), + None => Err(anyhow!("no response found")), + } + } +} + +impl CardClient for ScdClient { + fn transmit(&mut self, cmd: &[u8], _buf_size: usize) -> Result> { + let mut res = None; + + { + res = Some(RT.lock().unwrap().block_on(self.transmit_async(cmd))); + } + + res.unwrap() + } +}