1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
//! This crate defines APDUs and related functions to talk to the OpenPGP applet on a smartcard.
//!
//! Right now it is in the inital stage of the development.
use apdus::APDU;
use pcsc::*;

pub mod apdus;
pub mod errors;
pub mod tlvs;
pub mod response;

/// Creates a new connection to the card attached to the first reader and returns the connection,
/// or the related error.
///
/// # Example
///
/// ```
/// use talktosc::*;
///
/// let card = create_connection().unwrap();
/// ```
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())),
    };

    // List available readers.
    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()));
        }
    };

    // Use the first reader.
    let reader = match readers.next() {
        Some(reader) => reader,
        None => {
            return Err(errors::TalktoSCError::MissingReaderError);
        }
    };
    //println!("Using reader: {:?}", reader);

    // Connect to the card.
    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)
}

/// Disconnects the card via Disposition::LeaveCard
pub fn disconnect(card: Card) {
    let _ = card.disconnect(Disposition::LeaveCard);
}

//pub fn sendapdu(card: &Card, apdu: &[u8]) -> Vec<u8> {
//let mut resp_buffer = [0; MAX_BUFFER_SIZE];
//let resp = card.transmit(apdu, &mut resp_buffer).unwrap();
//let val = Vec::from(resp);
//return val;
//}

/// Sends the given APDU (if required in chained way) to the card and returns the response as a
/// vector of `u8`.
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();
        // TODO: Verify the response
        //println!("Received: {:#?}", resp);
        i += 1;
        if i == l {
            // TODO: verify the final response
            res = Vec::from(resp);
        }
    }
    return res;
}

/// Helper function to send the APDU and returns the a Result<Response, errors::TalktoSCError>.
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: [u8; 11] = [0x00, 0xA4, 0x04, 0x00, 0x06, 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01];
    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()));
    //let get_url_apdu = apdus::create_apdu_get_url();
    //let resp = sendapdu(&card, get_url_apdu);
    //let l = resp.len() - 2;
    //println!(
    //"Received at the end: {}",
    //str::from_utf8(&resp[..l]).unwrap()
    //);
    // Now let us try to verify the pin passed to us.
    //let pin_apdu = apdus::create_apdu_verify_pw1_for_others(_pin);
    //let resp = sendapdu(&card, pin_apdu);
    //let l = resp.len() - 2;
    //println!(
    //"Received at the end: {}",
    //str::from_utf8(&resp[..l]).unwrap()
    //);
}

#[cfg(test)]
mod tests {
    // Note this useful idiom: importing names from outer (for mod tests) scope.
    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);
    }
}