-
Notifications
You must be signed in to change notification settings - Fork 8
/
data.c
151 lines (142 loc) · 5.44 KB
/
data.c
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
#include "data.h"
#include <string.h>
void data_init(struct data *data) {
memset(data, 0, sizeof(struct data));
data->t1.check_len = LRC_XOR;
}
static enum result t0_transfer_direction(struct data_t0 *data) {
switch (data->ins) {
// First data transfer (if any) will be to the card.
// Any return data will be read with GET RESPONSE.
case 0x04: // DEACTIVATE FILE
case 0x0E: case 0x0F: // ERASE BINARY
case 0x20: case 0x21: // VERIFY
case 0x22: // MANAGE SECURITY ENVIRONMENT/KEY DERIVATION
case 0x24: // CHANGE REFERENCE DATA
case 0x26: // DISABLE VERIFICATION REQUIREMENT
case 0x28: // ENABLE VERIFICATION REQUIREMENT
case 0x2C: // RESET RETRY COUNTER
case 0x44: // ACTIVATE FILE
case 0x82: // EXTERNAL or MUTUAL AUTHENTICATE
case 0x86: case 0x87: // GENERAL AUTHENTICATE
case 0x88: // INTERNAL AUTHENTICATE
case 0xA1: // SEARCH BINARY
case 0xA2: // SEARCH RECORD
case 0xB1: // READ BINARY
case 0xB3: // READ RECORD
case 0xC2: case 0xC3: // ENVELOPE
case 0xCB: // GET DATA
case 0xD0: case 0xD1: // WRITE BINARY
case 0xD2: // WRITE RECORD
case 0xD6: case 0xD7: // UPDATE BINARY
case 0xDA: case 0xDB: // PUT DATA
case 0xDC: case 0xDD: // UPDATE RECORD
case 0xE0: // CREATE FILE
case 0xE2: // APPEND RECORD
case 0xE4: // DELETE FILE
return T0_DATA_CMD_BODY;
// First data transfer (if any) will be from the card.
case 0x70: // MANAGE CHANNEL
case 0x84: // GET CHALLENGE
case 0xB0: // READ BINARY
case 0xB2: // READ RECORD
case 0xC0: // GET RESPONSE
case 0xCA: // GET DATA
return T0_DATA_RESP_BODY;
case 0xA4: // SELECT
// ISO 7816-4 Table 39: P1 meaning for SELECT command.
switch (data->p1) {
case 0x01: // Select child DF (data = DF identifier)
case 0x02: // Select EF under current DF (data = EF identifier)
case 0x04: // Select by DF name (data = app identifier)
case 0x08: // Select from the MF (data = Path without MF id)
case 0x09: // Select from the current DF
// (data = path without the current DF identifier)
return T0_DATA_CMD_BODY;
case 0x03: // Select parent DF of the current DF (data absent)
return T0_DATA_RESP_BODY;
}
break;
}
return T0_DATA_UNK_BODY;
}
enum result data_t0_analyze(struct data *data, unsigned char byte) {
struct data_t0 *t0 = &data->t0;
switch (t0->state) {
case COMMAND:
t0->command_bytes_seen++;
if (t0->command_bytes_seen == 2) {
t0->ins = byte;
} else if (t0->command_bytes_seen == 3) {
t0->p1 = byte;
} else if (t0->command_bytes_seen == 5) {
t0->p3_len = byte;
if (PACKET_DIR(t0_transfer_direction(t0)) == DIR_FROM_CARD) {
// This is response length so 0 means 256.
// Note that this direction check has false negatives.
if (byte == 0x00) t0->p3_len = 256;
}
t0->state = PROCEDURE_BYTE;
return T0_DATA_CMD_HEADER;
}
break;
case PROCEDURE_BYTE:
if (byte == 0x60) return T0_DATA_NULL;
if ((byte & 0xF0) == 0x60 || (byte & 0xF0) == 0x90) {
t0->state = SW2;
}
if (byte == t0->ins) {
// Expect the remaining bytes, if any
if (t0->transfer_bytes_seen < t0->p3_len) {
t0->state = TRANSFER_ALL;
}
return T0_DATA_ACK;
}
if ((byte ^ 0xFF) == t0->ins) {
// Expect a single byte if not all sent already
if (t0->transfer_bytes_seen < t0->p3_len) {
t0->state = TRANSFER_ONE;
}
return T0_DATA_ACK;
}
break;
case SW2:
// Completion of whole exchange.
t0->state = COMMAND;
t0->command_bytes_seen = 0;
t0->transfer_bytes_seen = 0;
return T0_DATA_RESP_SW;
case TRANSFER_ALL:
t0->transfer_bytes_seen++;
if (t0->p3_len == t0->transfer_bytes_seen) {
t0->state = PROCEDURE_BYTE;
return t0_transfer_direction(t0);
}
break;
case TRANSFER_ONE:
t0->transfer_bytes_seen++;
t0->state = PROCEDURE_BYTE;
return t0_transfer_direction(t0);
}
return CONTINUE;
}
#define T1_PROLOGUE_LEN (3)
enum result data_t1_analyze(struct data *data, unsigned char byte) {
struct data_t1 *t1 = &data->t1;
t1->bytes_seen++;
if (t1->bytes_seen == T1_PROLOGUE_LEN) {
t1->msg_length = T1_PROLOGUE_LEN + byte + t1->check_len;
return CONTINUE;
}
if (t1->msg_length && t1->bytes_seen == t1->msg_length) {
// End of block, reset counters for next block
enum result res = T1_DATA_CMD;
if (t1->direction_from_card) res = T1_DATA_RESP;
t1->bytes_seen = 0;
t1->msg_length = 0;
// Flip direction for next packet
t1->direction_from_card ^= 1;
return res;
}
return CONTINUE;
}