Initial scdc experimentation.
This commit is contained in:
parent
8e3c6c0046
commit
2343bd8310
9 changed files with 181 additions and 21 deletions
|
@ -6,4 +6,5 @@
|
||||||
members = [
|
members = [
|
||||||
"openpgp-card",
|
"openpgp-card",
|
||||||
"openpgp-card-sequoia",
|
"openpgp-card-sequoia",
|
||||||
|
"scdc",
|
||||||
]
|
]
|
||||||
|
|
|
@ -14,6 +14,8 @@ documentation = "https://docs.rs/crate/openpgp-card-sequoia"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
sequoia-openpgp = "1.3"
|
sequoia-openpgp = "1.3"
|
||||||
openpgp-card = { path = "../openpgp-card", version = "0.0.1" }
|
openpgp-card = { path = "../openpgp-card", version = "0.0.1" }
|
||||||
|
openpgp-card-scdc = { path = "../scdc" }
|
||||||
|
tokio = "0.2"
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
thiserror = "1"
|
thiserror = "1"
|
||||||
|
|
|
@ -9,18 +9,22 @@ use sequoia_openpgp::parse::Parse;
|
||||||
use sequoia_openpgp::Cert;
|
use sequoia_openpgp::Cert;
|
||||||
|
|
||||||
use openpgp_card::{CardBase, KeyType};
|
use openpgp_card::{CardBase, KeyType};
|
||||||
|
use openpgp_card_scdc::ScdClient;
|
||||||
|
|
||||||
// Filename of test key and test message to use:
|
// Filename of test key and test message to use:
|
||||||
|
|
||||||
// const TEST_KEY_PATH: &str = "example/test4k.sec";
|
const TEST_KEY_PATH: &str = "example/test4k.sec";
|
||||||
// const TEST_ENC_MSG: &str = "example/encrypted_to_rsa4k.asc";
|
const TEST_ENC_MSG: &str = "example/encrypted_to_rsa4k.asc";
|
||||||
|
|
||||||
// const TEST_KEY_PATH: &str = "example/nist521.sec";
|
// const TEST_KEY_PATH: &str = "example/nist521.sec";
|
||||||
// const TEST_ENC_MSG: &str = "example/encrypted_to_nist521.asc";
|
// const TEST_ENC_MSG: &str = "example/encrypted_to_nist521.asc";
|
||||||
|
|
||||||
const TEST_KEY_PATH: &str = "example/test25519.sec";
|
// const TEST_KEY_PATH: &str = "example/test25519.sec";
|
||||||
const TEST_ENC_MSG: &str = "example/encrypted_to_25519.asc";
|
// 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<dyn Error>> {
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
|
||||||
|
@ -29,7 +33,8 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
|
||||||
if let Ok(test_card_ident) = test_card_ident {
|
if let Ok(test_card_ident) = test_card_ident {
|
||||||
println!("** get card");
|
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
|
// card metadata
|
||||||
|
|
||||||
|
@ -140,7 +145,9 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||||
// Open fresh Card for decrypt
|
// 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()?;
|
let app_id = oc.get_aid()?;
|
||||||
|
|
||||||
// Check that we're still using the expected card
|
// Check that we're still using the expected card
|
||||||
|
@ -179,7 +186,8 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||||
// -----------------------------
|
// -----------------------------
|
||||||
// Open fresh Card for signing
|
// 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
|
// Sign
|
||||||
match oc.verify_pw1_for_signing("123456") {
|
match oc.verify_pw1_for_signing("123456") {
|
||||||
|
|
|
@ -12,7 +12,7 @@ use std::convert::TryFrom;
|
||||||
use crate::apdu::command::Command;
|
use crate::apdu::command::Command;
|
||||||
use crate::apdu::response::Response;
|
use crate::apdu::response::Response;
|
||||||
use crate::errors::{OcErrorStatus, OpenpgpCardError, SmartcardError};
|
use crate::errors::{OcErrorStatus, OpenpgpCardError, SmartcardError};
|
||||||
use crate::CardCaps;
|
use crate::{CardCaps, CardClient};
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
pub(crate) enum Le {
|
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<Vec<u8>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PcscClient {
|
pub struct PcscClient {
|
||||||
card: Card,
|
card: Card,
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,14 +28,13 @@ use crate::errors::OpenpgpCardError;
|
||||||
use crate::tlv::tag::Tag;
|
use crate::tlv::tag::Tag;
|
||||||
use crate::tlv::TlvEntry;
|
use crate::tlv::TlvEntry;
|
||||||
|
|
||||||
use crate::apdu::CardClient;
|
|
||||||
use crate::Hash;
|
use crate::Hash;
|
||||||
use crate::{
|
use crate::{
|
||||||
apdu, key_upload, parse, tlv, CardCaps, CardUploadableKey, DecryptMe,
|
apdu, key_upload, parse, tlv, CardCaps, CardClient, CardUploadableKey,
|
||||||
KeyType, Sex,
|
DecryptMe, KeyType, Sex,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) struct CardApp {
|
pub struct CardApp {
|
||||||
card_client: Box<dyn CardClient + Send + Sync>,
|
card_client: Box<dyn CardClient + Send + Sync>,
|
||||||
card_caps: Option<CardCaps>,
|
card_caps: Option<CardCaps>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,13 +4,13 @@
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
|
|
||||||
use crate::apdu::command::Command;
|
use crate::apdu::command::Command;
|
||||||
use crate::apdu::{commands, CardClient};
|
use crate::apdu::commands;
|
||||||
use crate::card_app::CardApp;
|
use crate::card_app::CardApp;
|
||||||
use crate::errors::OpenpgpCardError;
|
use crate::errors::OpenpgpCardError;
|
||||||
use crate::parse::algo_attrs::{Algo, RsaAttrs};
|
use crate::parse::algo_attrs::{Algo, RsaAttrs};
|
||||||
use crate::parse::algo_info::AlgoInfo;
|
use crate::parse::algo_info::AlgoInfo;
|
||||||
use crate::tlv::{tag::Tag, Tlv, TlvEntry};
|
use crate::tlv::{tag::Tag, Tlv, TlvEntry};
|
||||||
use crate::{apdu, CardCaps};
|
use crate::{apdu, CardCaps, CardClient};
|
||||||
use crate::{
|
use crate::{
|
||||||
tlv, CardUploadableKey, EccKey, EccType, KeyType, PrivateKeyMaterial,
|
tlv, CardUploadableKey, EccKey, EccType, KeyType, PrivateKeyMaterial,
|
||||||
RSAKey,
|
RSAKey,
|
||||||
|
@ -26,6 +26,8 @@ pub(crate) fn upload_key(
|
||||||
key_type: KeyType,
|
key_type: KeyType,
|
||||||
algo_list: Option<AlgoInfo>,
|
algo_list: Option<AlgoInfo>,
|
||||||
) -> Result<(), OpenpgpCardError> {
|
) -> Result<(), OpenpgpCardError> {
|
||||||
|
println!("upload key");
|
||||||
|
|
||||||
let (algo_cmd, key_cmd) = match key.get_key()? {
|
let (algo_cmd, key_cmd) = match key.get_key()? {
|
||||||
PrivateKeyMaterial::R(rsa_key) => {
|
PrivateKeyMaterial::R(rsa_key) => {
|
||||||
// RSA bitsize
|
// RSA bitsize
|
||||||
|
|
|
@ -13,28 +13,46 @@ use parse::{
|
||||||
};
|
};
|
||||||
use tlv::Tlv;
|
use tlv::Tlv;
|
||||||
|
|
||||||
use crate::apdu::{CardClient, PcscClient};
|
use crate::apdu::PcscClient;
|
||||||
use crate::card_app::CardApp;
|
use crate::card_app::CardApp;
|
||||||
use crate::errors::{OpenpgpCardError, SmartcardError};
|
use crate::errors::{OpenpgpCardError, SmartcardError};
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
mod apdu;
|
mod apdu;
|
||||||
mod card;
|
mod card;
|
||||||
mod card_app;
|
pub mod card_app;
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
mod key_upload;
|
mod key_upload;
|
||||||
mod parse;
|
mod parse;
|
||||||
mod tlv;
|
mod tlv;
|
||||||
|
|
||||||
|
pub trait CardClient {
|
||||||
|
fn transmit(&mut self, cmd: &[u8], buf_size: usize) -> Result<Vec<u8>>;
|
||||||
|
}
|
||||||
|
|
||||||
/// Information about the capabilities of the card.
|
/// Information about the capabilities of the card.
|
||||||
/// (feature configuration from card metadata)
|
/// (feature configuration from card metadata)
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub(crate) struct CardCaps {
|
pub struct CardCaps {
|
||||||
pub(crate) ext_support: bool,
|
pub(crate) ext_support: bool,
|
||||||
pub(crate) chaining_support: bool,
|
pub(crate) chaining_support: bool,
|
||||||
pub(crate) max_cmd_bytes: u16,
|
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.
|
/// Container for a hash value.
|
||||||
/// These hash values can be signed by the card.
|
/// These hash values can be signed by the card.
|
||||||
pub enum Hash<'a> {
|
pub enum Hash<'a> {
|
||||||
|
@ -228,6 +246,10 @@ pub struct CardBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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
|
/// Get all cards that can be opened as an OpenPGP card applet
|
||||||
pub fn list_cards() -> Result<Vec<Self>> {
|
pub fn list_cards() -> Result<Vec<Self>> {
|
||||||
let cards = card::get_cards().map_err(|err| anyhow!(err))?;
|
let cards = card::get_cards().map_err(|err| anyhow!(err))?;
|
||||||
|
|
15
scdc/Cargo.toml
Normal file
15
scdc/Cargo.toml
Normal file
|
@ -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"
|
115
scdc/src/lib.rs
Normal file
115
scdc/src/lib.rs
Normal file
|
@ -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<Runtime> =
|
||||||
|
{ Mutex::new(tokio::runtime::Runtime::new().unwrap()) };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ScdClient {
|
||||||
|
client: Arc<Mutex<Client>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScdClient {
|
||||||
|
/// Create a CardBase object that uses an scdaemon instance as its
|
||||||
|
/// backend.
|
||||||
|
pub fn open_scdc(socket: &str) -> Result<CardBase, OpenpgpCardError> {
|
||||||
|
println!("open_scdc");
|
||||||
|
|
||||||
|
let card_client = ScdClient::new(socket)?;
|
||||||
|
|
||||||
|
let ccb = Box::new(card_client) as Box<dyn CardClient + Send + Sync>;
|
||||||
|
|
||||||
|
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<Self> {
|
||||||
|
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<Vec<u8>> {
|
||||||
|
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<Vec<u8>> {
|
||||||
|
let mut res = None;
|
||||||
|
|
||||||
|
{
|
||||||
|
res = Some(RT.lock().unwrap().block_on(self.transmit_async(cmd)));
|
||||||
|
}
|
||||||
|
|
||||||
|
res.unwrap()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue