Support removing the card while waiting for a PIN

This commit is contained in:
puck 2026-03-06 09:57:49 +00:00
parent 04127f60b0
commit 20fc4719f3
2 changed files with 143 additions and 113 deletions

View file

@ -2,7 +2,7 @@ use std::{collections::HashMap, env::args, thread, time::Duration};
use der::{Any, Decode, asn1::SetOfVec, oid::ObjectIdentifier};
use openssl::{bn::BigNumContext, ec::PointConversionForm, pkey::PKey};
use tokio::runtime::Runtime;
use tokio::{runtime::Runtime, select};
use url::Url;
use crate::{
@ -121,128 +121,144 @@ async fn run_auth(
})
.await;
ctg_pipe.send(pipe::CardToGUI::WaitForCard).await;
let mut finder = PCSCCardFinder::new();
let mut crad = loop {
let mut crad = finder.find_valid().await;
let (mut crad, creds) = 'outer: loop {
ctg_pipe.send(pipe::CardToGUI::WaitForCard).await;
let mut crad = loop {
let mut crad = finder.find_valid().await;
// Select MF
iso7816::select(
&mut crad,
0,
iso7816::SelectFile::File(&[]),
iso7816::SelectOccurrence::First,
)
.await?;
iso7816::select(
&mut crad,
0,
iso7816::files::EF_DIR,
iso7816::SelectOccurrence::First,
)
.await?;
let ef_dir = iso7816::read_binary(&mut crad, 0).await?;
if ef_dir.is_none()
|| !ef_dir.unwrap().windows(14).any(|w| {
w == [
// Select MF
iso7816::select(
&mut crad,
0,
iso7816::SelectFile::File(&[]),
iso7816::SelectOccurrence::First,
)
.await?;
iso7816::select(
&mut crad,
0,
iso7816::files::EF_DIR,
iso7816::SelectOccurrence::First,
)
.await?;
let ef_dir = iso7816::read_binary(&mut crad, 0).await?;
if ef_dir.is_none()
|| !ef_dir.unwrap().windows(14).any(|w| {
w == [
0xA0, 0x00, 0x00, 0x07, 0x88, 0x50, 0x43, 0x41, 0x2D, 0x65, 0x4D, 0x52,
0x54, 0x44,
]
})
{
ctg_pipe
.send(pipe::CardToGUI::ProcessingMessage {
message: String::from("Card is not eID"),
})
.await;
ctg_pipe.send(pipe::CardToGUI::WaitForCard).await;
continue;
}
break crad;
};
let creds = loop {
// Select the PCA application
iso7816::select(
&mut crad,
0,
iso7816::SelectFile::DedicatedFileName(&[
0xA0, 0x00, 0x00, 0x07, 0x88, 0x50, 0x43, 0x41, 0x2D, 0x65, 0x4D, 0x52, 0x54,
0x44,
]
})
{
ctg_pipe
.send(pipe::CardToGUI::ProcessingMessage {
message: String::from("Card is not eID"),
})
.await;
ctg_pipe.send(pipe::CardToGUI::WaitForCard).await;
continue;
}
]),
iso7816::SelectOccurrence::First,
)
.await?;
break crad;
};
// Select _its_ MF (what?)
iso7816::select(
&mut crad,
0,
iso7816::SelectFile::File(&[]),
iso7816::SelectOccurrence::First,
)
.await?;
let creds = loop {
// Select the PCA application
iso7816::select(
&mut crad,
0,
iso7816::SelectFile::DedicatedFileName(&[
0xA0, 0x00, 0x00, 0x07, 0x88, 0x50, 0x43, 0x41, 0x2D, 0x65, 0x4D, 0x52, 0x54, 0x44,
]),
iso7816::SelectOccurrence::First,
)
.await?;
iso7816::select(
&mut crad,
0,
iso7816::files::EF_CARDACCESS,
iso7816::SelectOccurrence::First,
)
.await?;
let ef_cardaccess_bytes = iso7816::read_binary(&mut crad, 0).await?.unwrap();
let ef_cardaccess = pace::SecurityInfos::from_der(&ef_cardaccess_bytes).unwrap();
// Select _its_ MF (what?)
iso7816::select(
&mut crad,
0,
iso7816::SelectFile::File(&[]),
iso7816::SelectOccurrence::First,
)
.await?;
let (msg, can_continue) = match pace::set_authentication_template(
&mut crad,
ef_cardaccess.get(0).unwrap().protocol,
pace::PasswordType::PIN,
)
.await
{
Ok(()) => (None, true),
Err(pace::PACEStatus::CardError(e)) => return Err(e),
Err(pace::PACEStatus::TriesLeft(n)) => (Some(format!("{} tries left", n)), true),
Err(pace::PACEStatus::Error(unk)) => {
(Some(format!("Unknown error {:04x}", unk)), false)
}
Err(pace::PACEStatus::PasswordSuspended) => {
(Some("PIN suspended. Use app.".to_string()), false)
}
Err(pace::PACEStatus::PasswordBlocked) => {
(Some("PIN blocked. Use app.".to_string()), false)
}
};
iso7816::select(
&mut crad,
0,
iso7816::files::EF_CARDACCESS,
iso7816::SelectOccurrence::First,
)
.await?;
let ef_cardaccess_bytes = iso7816::read_binary(&mut crad, 0).await?.unwrap();
let ef_cardaccess = pace::SecurityInfos::from_der(&ef_cardaccess_bytes).unwrap();
let (msg, can_continue) = match pace::set_authentication_template(
&mut crad,
ef_cardaccess.get(0).unwrap().protocol,
pace::PasswordType::PIN,
)
.await
{
Ok(()) => (None, true),
Err(pace::PACEStatus::CardError(e)) => return Err(e),
Err(pace::PACEStatus::TriesLeft(n)) => (Some(format!("{} tries left", n)), true),
Err(pace::PACEStatus::Error(unk)) => {
(Some(format!("Unknown error {:04x}", unk)), false)
if can_continue {
ctg_pipe
.send(pipe::CardToGUI::ReadyForPIN { message: msg })
.await;
} else {
ctg_pipe
.send(pipe::CardToGUI::ProcessingMessage {
message: msg.unwrap(),
})
.await;
}
Err(pace::PACEStatus::PasswordSuspended) => {
(Some("PIN suspended. Use app.".to_string()), false)
}
Err(pace::PACEStatus::PasswordBlocked) => {
(Some("PIN blocked. Use app.".to_string()), false)
let wait_until_not_present = crad.wait_until_not_present();
let next_command = gtc_pipe.recv();
select! {
_ = wait_until_not_present => {
continue 'outer;
}
val = next_command => {
let GUIToCard::PIN(pin) = val.unwrap();
ctg_pipe
.send(pipe::CardToGUI::ProcessingMessage {
message: String::from("Negotiating with the card..."),
})
.await;
match pace::authenticate_pin(
&mut crad,
pin.as_bytes(),
ef_cardaccess.get(0).unwrap().protocol,
)
.await
{
Ok(creds) => break creds,
Err(pace::PACEStatus::CardError(n)) => return Err(n),
_ => (),
}
}
}
};
if can_continue {
ctg_pipe
.send(pipe::CardToGUI::ReadyForPIN { message: msg })
.await;
} else {
ctg_pipe
.send(pipe::CardToGUI::ProcessingMessage {
message: msg.unwrap(),
})
.await;
}
let GUIToCard::PIN(pin) = gtc_pipe.recv().await.unwrap();
ctg_pipe
.send(pipe::CardToGUI::ProcessingMessage {
message: String::from("Negotiating with the card..."),
})
.await;
match pace::authenticate_pin(
&mut crad,
pin.as_bytes(),
ef_cardaccess.get(0).unwrap().protocol,
)
.await
{
Ok(creds) => break creds,
Err(pace::PACEStatus::CardError(n)) => return Err(n),
_ => (),
}
break (crad, creds);
};
let apdus;