Ensure the APDUs are shaped like we expect
This is probably a tiny bit fragile, but the WOO source, as well as the fact that the counts of APDUs is hard-coded in the client, means this is plausibly safe. We leave the stage 1 (certificate validation) requests a bit more flexible, to allow for server-side changes in certificate authentication. Post-terminal authentication APDUs, however, are scrutinized more.
This commit is contained in:
parent
759b9ac93d
commit
1172e81b3a
1 changed files with 77 additions and 1 deletions
78
src/main.rs
78
src/main.rs
|
|
@ -1,4 +1,4 @@
|
|||
use std::{collections::HashMap, env::args, thread, time::Duration};
|
||||
use std::{collections::HashMap, env::args, io::ErrorKind, thread, time::Duration};
|
||||
|
||||
use der::{Any, Decode, asn1::SetOfVec, oid::ObjectIdentifier};
|
||||
use openssl::{bn::BigNumContext, ec::PointConversionForm, pkey::PKey};
|
||||
|
|
@ -93,6 +93,53 @@ pub trait Card {
|
|||
) -> impl Future<Output = std::io::Result<ResultAPDU>> + Send;
|
||||
}
|
||||
|
||||
fn is_stage_1_apdu_expected(apdu_buf: &[u8]) -> bool {
|
||||
// MSE:Set DST, encrypted
|
||||
if apdu_buf.starts_with(&[0x0c, 0x22, 0x81, 0xb6]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// PSO:Verify self-descriptive certificate, encrypted (start of chain)
|
||||
if apdu_buf.starts_with(&[0x1c, 0x2a, 0x00, 0xbe]) {
|
||||
return true;
|
||||
}
|
||||
// Rest of the chain
|
||||
if apdu_buf.starts_with(&[0x0c, 0x2a, 0x00, 0xbe]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set AT for external authentication
|
||||
if apdu_buf.starts_with(&[0x0c, 0x22, 0x81, 0xa4]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get challenge (terminal authentication)
|
||||
if apdu_buf.starts_with(&[0x0c, 0x84, 0x00, 0x00]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn are_stage_2_apdus_expected(apdus: &[Vec<u8>]) -> bool {
|
||||
// External Authenticate
|
||||
if !apdus[0].starts_with(&[0x0c, 0x82, 0x00, 0x00]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// MSE:Set AT, chip/restricted authentication
|
||||
if !apdus[1].starts_with(&[0x0c, 0x22, 0x41, 0xa4]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// General Authenticate, unknown P1/P2
|
||||
if !apdus[2].starts_with(&[0x0c, 0x86, 0x00, 0x83]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
async fn run_auth(
|
||||
host: String,
|
||||
session_id: String,
|
||||
|
|
@ -446,6 +493,21 @@ async fn run_auth(
|
|||
let apdu_count = apdus.len();
|
||||
for apdu in apdus {
|
||||
counter += 1;
|
||||
if !is_stage_1_apdu_expected(&apdu) {
|
||||
ctg_pipe
|
||||
.send(pipe::CardToGUI::ProcessingMessage {
|
||||
message: format!(
|
||||
"Refusing to run APDU (unexpected command) ({}/{})",
|
||||
counter, apdu_count
|
||||
),
|
||||
})
|
||||
.await;
|
||||
|
||||
tokio::time::sleep(Duration::from_secs(10)).await;
|
||||
ctg_pipe.send(pipe::CardToGUI::Done).await;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
ctg_pipe
|
||||
.send(pipe::CardToGUI::ProcessingMessage {
|
||||
message: format!("Running server-sent APDUs... ({}/{})", counter, apdu_count),
|
||||
|
|
@ -483,6 +545,20 @@ async fn run_auth(
|
|||
// - 0x89 Recipient key set version (INTEGER)
|
||||
// - 0x8a Type (INTEGER)
|
||||
// - 0x8b Sequence number (BCD string)
|
||||
|
||||
// Ensure we receive the APDUs we expected
|
||||
if apdus.len() != 3 || !are_stage_2_apdus_expected(&apdus) {
|
||||
ctg_pipe
|
||||
.send(pipe::CardToGUI::ProcessingMessage {
|
||||
message: format!("Refusing to run stage 2 APDUs ({}/{})", counter, apdu_count),
|
||||
})
|
||||
.await;
|
||||
|
||||
tokio::time::sleep(Duration::from_secs(10)).await;
|
||||
ctg_pipe.send(pipe::CardToGUI::Done).await;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let apdu_count = apdus.len() as isize + counter;
|
||||
for apdu in apdus {
|
||||
counter += 1;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue