use apdus::APDU;
use pcsc::*;
pub mod apdus;
pub mod errors;
pub mod tlvs;
pub mod response;
pub fn create_connection() -> Result<Card, errors::TalktoSCError> {
let ctx = match Context::establish(Scope::User) {
Ok(ctx) => ctx,
Err(err) => return Err(errors::TalktoSCError::ContextError(err.to_string())),
};
let mut readers_buf = [0; 2048];
let mut readers = match ctx.list_readers(&mut readers_buf) {
Ok(readers) => readers,
Err(err) => {
return Err(errors::TalktoSCError::ReaderError(err.to_string()));
}
};
let reader = match readers.next() {
Some(reader) => reader,
None => {
return Err(errors::TalktoSCError::MissingReaderError);
}
};
let card = match ctx.connect(reader, ShareMode::Shared, Protocols::ANY) {
Ok(card) => card,
Err(Error::NoSmartcard) => {
return Err(errors::TalktoSCError::MissingSmartCardError);
}
Err(err) => {
return Err(errors::TalktoSCError::SmartCardConnectionError(
err.to_string(),
));
}
};
Ok(card)
}
pub fn list_readers() -> Result<Vec<String>, errors::TalktoSCError> {
let ctx = Context::establish(Scope::User)
.map_err(|e| errors::TalktoSCError::ContextError(e.to_string()))?;
let mut readers_buf = [0; 2048];
let readers = ctx.list_readers(&mut readers_buf)
.map_err(|e| errors::TalktoSCError::ReaderError(e.to_string()))?;
Ok(readers.map(|r| r.to_str().unwrap_or("").to_string()).collect())
}
pub fn get_card_ident_from_card(card: &Card) -> Result<String, errors::TalktoSCError> {
let select_apdu = apdus::create_apdu_select_openpgp();
send_and_parse(card, select_apdu)?;
let resp = send_and_parse(card, apdus::create_apdu_get_aid())?;
let data = resp.get_data();
if data.len() < 14 {
return Err(errors::TalktoSCError::SmartCardConnectionError(
"AID response too short".to_string(),
));
}
let manufacturer = ((data[8] as u16) << 8) | (data[9] as u16);
let serial = ((data[10] as u32) << 24)
| ((data[11] as u32) << 16)
| ((data[12] as u32) << 8)
| (data[13] as u32);
Ok(format!("{:04X}:{:08X}", manufacturer, serial))
}
pub fn list_cards() -> Result<Vec<(String, String)>, errors::TalktoSCError> {
let ctx = Context::establish(Scope::User)
.map_err(|e| errors::TalktoSCError::ContextError(e.to_string()))?;
let mut readers_buf = [0; 2048];
let readers = ctx.list_readers(&mut readers_buf)
.map_err(|e| errors::TalktoSCError::ReaderError(e.to_string()))?;
let mut result = Vec::new();
for reader in readers {
let reader_name = reader.to_str().unwrap_or("").to_string();
let card = match ctx.connect(reader, ShareMode::Shared, Protocols::ANY) {
Ok(c) => c,
Err(_) => continue,
};
if let Ok(ident) = get_card_ident_from_card(&card) {
result.push((reader_name, ident));
}
let _ = card.disconnect(Disposition::LeaveCard);
}
Ok(result)
}
pub fn create_connection_by_ident(ident: &str) -> Result<Card, errors::TalktoSCError> {
let target = ident.to_ascii_uppercase();
let ctx = Context::establish(Scope::User)
.map_err(|e| errors::TalktoSCError::ContextError(e.to_string()))?;
let mut readers_buf = [0; 2048];
let readers = ctx.list_readers(&mut readers_buf)
.map_err(|e| errors::TalktoSCError::ReaderError(e.to_string()))?;
let mut matching_reader: Option<String> = None;
for reader in readers {
let reader_name = reader.to_str().unwrap_or("").to_string();
let card = match ctx.connect(reader, ShareMode::Shared, Protocols::ANY) {
Ok(c) => c,
Err(_) => continue,
};
let card_ident = get_card_ident_from_card(&card).ok();
let _ = card.disconnect(Disposition::LeaveCard);
if card_ident.as_deref() == Some(&target) {
matching_reader = Some(reader_name);
break;
}
}
let reader_name = matching_reader.as_ref().ok_or_else(|| {
errors::TalktoSCError::SmartCardConnectionError(
format!("Card with ident '{}' not found", ident),
)
})?.clone();
let ctx = Context::establish(Scope::User)
.map_err(|e| errors::TalktoSCError::ContextError(e.to_string()))?;
let mut readers_buf2 = [0; 2048];
let readers = ctx.list_readers(&mut readers_buf2)
.map_err(|e| errors::TalktoSCError::ReaderError(e.to_string()))?;
for reader in readers {
if reader.to_str().unwrap_or("") == reader_name {
let card = ctx.connect(reader, ShareMode::Shared, Protocols::ANY)
.map_err(|e| match e {
Error::NoSmartcard => errors::TalktoSCError::MissingSmartCardError,
err => errors::TalktoSCError::SmartCardConnectionError(err.to_string()),
})?;
return Ok(card);
}
}
Err(errors::TalktoSCError::MissingSmartCardError)
}
pub fn disconnect(card: Card) {
let _ = card.disconnect(Disposition::LeaveCard);
}
pub fn sendapdu(card: &Card, apdu: apdus::APDU) -> Vec<u8> {
let l = apdu.iapdus.len();
let mut i = 0;
let mut res: Vec<u8> = Vec::new();
for actual_apdu in &apdu {
let mut resp_buffer = [0; MAX_BUFFER_SIZE];
let resp = card.transmit(&actual_apdu[..], &mut resp_buffer).unwrap();
i += 1;
if i == l {
res = Vec::from(resp);
}
}
return res;
}
pub fn send_and_parse(card: &Card, apdus: APDU) -> Result<response::Response, errors::TalktoSCError> {
response::Response::new(sendapdu(&card, apdus))
}
pub fn entry(_pin: Vec<u8>) {
let card = create_connection().unwrap();
let select_openpgp = apdus::create_apdu_select_openpgp();
let resp = send_and_parse(&card, select_openpgp).unwrap();
println!("Received Final: {:x?}", resp.get_data());
let resp = send_and_parse(&card, apdus::create_apdu_get_aid()).unwrap();
println!("Serial number: {}", tlvs::parse_card_serial(resp.get_data()));
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs::File;
use std::io::Read;
#[test]
fn test_create_newapdu() {
let mut f = File::open("./data/foo2.binary").expect("no file found");
let mut buffer: Vec<u8> = Vec::new();
f.read_to_end(&mut buffer).unwrap();
let comapdu = apdus::APDU::new(0x00, 0x2A, 0x80, 0x86, Some(buffer));
assert_eq!(comapdu.iapdus.len(), 3);
assert_eq!(comapdu.iapdus[0][0], 0x10);
assert_eq!(comapdu.iapdus[1][0], 0x10);
assert_eq!(comapdu.iapdus[2][0], 0x00);
}
}