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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
use pcsc::*;
mod helpers;
mod insurance;
mod patient;
use flate2::read::GzDecoder;
use serde_json::json;
use std::io::prelude::*;

const VERSION_1: &[u8] = b"\x00\xB2\x01\x84\x00";
const VERSION_2: &[u8] = b"\x00\xB2\x02\x84\x00";
const VERSION_3: &[u8] = b"\x00\xB2\x03\x84\x00";

#[derive(Debug)]
pub enum TerminalType {
    MOBILE,
    FIXED,
    UNKNOW,
}

#[derive(Debug)]
pub enum CardType {
    SYNC,
    ASYNC,
    NOTSAVED,
    UNKNOW,
}

///Kartenterminatype ermittel
/// 9000 - stationäres Kartenterminal
/// 9500 - mobiles Kartenterminal
const RESET_CT: &[u8] = b"\x00\xA4\x04\x0C\x07\xD2\x76\x00\x01\x44\x80\x00";

///Kartentype ermittel
/// 9000 - synchrone Karte
/// 9001 - asynchrone Karte
/// 6200 -  keine Karte (eGK oder KVK) gespeichert
const REQUEST_ICC1: &[u8] = b"\x20\x12\x01\x00\x01\x01";

const SELECT_MF: &[u8] = b"\x00\xA4\x04\x0C\x07\xD2\x76\x00\x01\x44\x80\x00";
const SELECT_HCA: &[u8] = b"\x00\xA4\x04\x0C\x06\xD2\x76\x00\x00\x01\x02";
const SELECT_FILE_PD: &[u8] = b"\x00\xB0\x81\x00\x02";
const SELECT_FILE_VD: &[u8] = b"\x00\xB0\x82\x00\x08";
const CARD_ATR: &[u8] = b"\x3B\xD3\x96\xFF\x81\xB1\xFE\x45\x1F\x07\x80\x81\x05\x2D";
//const EJECT_CARD: &[u8] = b"\x20\x15\x01\x00\x01\x01";

pub fn get_card() -> Result<Card, Error>  {
    // Establish a PC/SC context.
    let ctx = match pcsc::Context::establish(Scope::User) {
        Ok(ctx) => ctx,
        Err(err) => {
            panic!("Failed to establish context: {}", err);
        }
    };

    // List available readers.
    let mut readers_buf = [0; 2048];
    let mut readers = match ctx.list_readers(&mut readers_buf) {
        Ok(readers) => readers,
        Err(err) => {
            panic!("Failed to list readers: {}", err);
            
        }
    };

    // Use the first reader.
    let reader = match readers.next() {
        Some(reader) => reader,
        None => {
            panic!("No readers are connected.");
        }
    };
    // Connect to the card.
    let card = match ctx.connect(reader, ShareMode::Shared, Protocols::ANY) {
        Ok(card) => card,
        Err(Error::NoSmartcard) => {
            panic!("A smartcard is not present in the reader.");
        }
        Err(err) => {
            panic!("Failed to connect to card: {}", err);
        }
    };
    Ok(card)
}

pub fn get_card_data(card: &Card) -> serde_json::Value {
    send_apdu(&card, SELECT_MF);
    get_card_generation(&card);
    send_apdu(&card, SELECT_HCA);
    send_apdu(&card, SELECT_FILE_PD);

    let data = send_apdu(&card, &helpers::create_read_command(0x00, 0x02));
    let mut pd_length = ((data[0] as usize) << 8) + data[1] as usize;
    pd_length -= 0x02;

    send_apdu(&card, SELECT_MF);
    send_apdu(&card, SELECT_HCA);
    send_apdu(&card, SELECT_FILE_PD);
    let mut patient_data_compressed = read_file(&card, 0x02, pd_length);

    send_apdu(&card, SELECT_MF);
    send_apdu(&card, SELECT_HCA);
    send_apdu(&card, SELECT_FILE_VD);

    let data = send_apdu(&card, &helpers::create_read_command(0x00, 0x08));

    let vd_start = ((data[0] as usize) << 8) + data[1] as usize;
    let vd_end = ((data[2] as usize) << 8) + data[3] as usize;
    let vd_length = vd_end - (vd_start - 1);

    send_apdu(&card, SELECT_MF);
    send_apdu(&card, SELECT_HCA);
    send_apdu(&card, SELECT_FILE_VD);
    let mut insurance_data_compressed = read_file(&card, vd_start, vd_length);

    patient_data_compressed.extend(&vec![0x00; 16]);
    let mut decoder_patient_data = GzDecoder::new(&patient_data_compressed as &[u8]);
    let mut buf = vec![];
    decoder_patient_data
        .read_to_end(&mut buf).unwrap();
    let patient_data_xml = String::from_utf8_lossy (&buf);

    insurance_data_compressed.extend(&vec![0x00; 16]);
    let mut decoder_insurance_data = GzDecoder::new(&insurance_data_compressed as &[u8]);
    let mut buf = vec![];
    decoder_insurance_data
        .read_to_end(&mut buf).unwrap();
    let insurance_data_xml = String::from_utf8_lossy (&buf);

    return json!({
        "insurance": insurance::parse_insurance_to_json(&insurance_data_xml),
        "patient": patient::parse_patient_to_json(&patient_data_xml),

    });
}

fn read_file(card: &Card, offset: usize, length: usize) -> Vec<u8> {
    let mut result: Vec<u8> = vec![];
    let max_read: u8 = 0xFC;
    let mut pointer = offset;
    while result.len() < length {
        let bytes_left = length - result.len();
        let readlen: usize;
        if bytes_left < max_read as usize {
            readlen = bytes_left;
        } else {
            readlen = max_read as usize;
        }
        let data_chunk = send_apdu(
            &card,
            &helpers::create_read_command(pointer as i32, readlen as i32),
        );
        pointer += readlen;
        result.extend(&data_chunk[0..data_chunk.len() - 2]);
    }
    return result;
}

pub fn get_card_generation(card: &Card) -> String {
    let version1 = get_version(&card, VERSION_1);
    let version2 = get_version(&card, VERSION_2);
    let version3 = get_version(&card, VERSION_3);
    let mut generation = String::from("unknow");
    if version1 == "3.0.0" && version2 == "3.0.0" && version3 == "3.0.2" {
        generation = String::from("G1");
    } else if version1 == "3.0.0" && version2 == "3.0.1" && version3 == "3.0.3" {
        generation = String::from("G1 plus");
    } else if version1 == "4.0.0" && version2 == "4.0.0" && version3 == "4.0.0" {
        generation = String::from("G2");
    }
    return generation;
}

pub fn get_card_type(card: Card) -> CardType {
    let a = send_apdu(&card, REQUEST_ICC1);
    let type_number = helpers::decode_bcd(&a[0..2]);
    if type_number == 9000 {
        return CardType::SYNC;
    } else if type_number == 9001 {
        return CardType::ASYNC;
    } else if type_number == 6200 {
        return CardType::NOTSAVED;
    }
    return CardType::UNKNOW;
}

pub fn get_termial_type(card: Card) -> TerminalType {
    let a = send_apdu(&card, RESET_CT);
    let type_number = helpers::decode_bcd(&a[0..4]);
    if type_number == 9000 {
        return TerminalType::FIXED;
    } else if type_number == 9500 {
        return TerminalType::MOBILE;
    }
    return TerminalType::UNKNOW;
}

pub fn get_version(card: &Card, version_string: &[u8]) -> String {
    let a = helpers::unpack_bcd(&send_apdu(&card, version_string));
    return format!(
        "{}.{}.{}",
        helpers::decode_bcd(&a[0..3]),
        helpers::decode_bcd(&a[3..6]),
        helpers::decode_bcd(&a[6..10])
    );
}
pub fn is_health_card(card: &Card) -> bool{
    let (names_len, _atr_len) = card.status2_len().expect("failed to get the status length");
    let mut names_buf = vec![0; names_len];
    let mut atr_buf = [0; MAX_ATR_SIZE];
    let status = card.status2(&mut names_buf, &mut atr_buf).expect("failed to get card status");
    if status.atr() == CARD_ATR{
        return true;
    }
    return false;
}

fn send_apdu(card: &Card, cmd: &[u8]) -> Vec<u8> {
    let mut rapdu_buf = [0; MAX_BUFFER_SIZE];
    let rapdu = match card.transmit(cmd, &mut rapdu_buf) {
        Ok(rapdu) => rapdu,
        Err(err) => {
            eprintln!("Failed to transmit APDU command to card: {}", err);
            std::process::exit(1);
        }
    };
    return rapdu.iter().cloned().collect();
}