/src/opensc/src/libopensc/ctbcs.c
Line | Count | Source |
1 | | /* |
2 | | * ctbcs.c: Extended CTBCS commands, used for pcsc and ct-api readers |
3 | | * |
4 | | * Copyright (C) 2002 Olaf Kirch <okir@suse.de> |
5 | | * |
6 | | * This library is free software; you can redistribute it and/or |
7 | | * modify it under the terms of the GNU Lesser General Public |
8 | | * License as published by the Free Software Foundation; either |
9 | | * version 2.1 of the License, or (at your option) any later version. |
10 | | * |
11 | | * This library is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | | * Lesser General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU Lesser General Public |
17 | | * License along with this library; if not, write to the Free Software |
18 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
19 | | */ |
20 | | |
21 | | #ifdef HAVE_CONFIG_H |
22 | | #include "config.h" |
23 | | #endif |
24 | | |
25 | | #include <stdlib.h> |
26 | | #include <string.h> |
27 | | |
28 | | #include "internal.h" |
29 | | #include "ctbcs.h" |
30 | | |
31 | | static void |
32 | | ctbcs_init_apdu(sc_apdu_t *apdu, int cse, int ins, int p1, int p2) |
33 | 0 | { |
34 | 0 | memset(apdu, 0, sizeof(*apdu)); |
35 | 0 | apdu->cse = cse; |
36 | 0 | apdu->cla = 0x20; |
37 | 0 | apdu->ins = ins; |
38 | 0 | apdu->p1 = p1; |
39 | 0 | apdu->p2 = p2; |
40 | |
|
41 | 0 | apdu->control = 1; |
42 | 0 | } |
43 | | |
44 | | static int |
45 | | ctbcs_build_perform_verification_apdu(sc_apdu_t *apdu, struct sc_pin_cmd_data *data) |
46 | 0 | { |
47 | 0 | const char *prompt; |
48 | 0 | size_t buflen, count = 0, j = 0, len; |
49 | 0 | static u8 buf[SC_MAX_APDU_BUFFER_SIZE]; |
50 | 0 | u8 control; |
51 | |
|
52 | 0 | ctbcs_init_apdu(apdu, |
53 | 0 | SC_APDU_CASE_3_SHORT, |
54 | 0 | CTBCS_INS_PERFORM_VERIFICATION, |
55 | 0 | CTBCS_P1_INTERFACE1, |
56 | 0 | 0); |
57 | |
|
58 | 0 | buflen = sizeof(buf); |
59 | 0 | prompt = data->pin1.prompt; |
60 | 0 | if (prompt && *prompt) { |
61 | 0 | len = strlen(prompt); |
62 | 0 | if (len + 2 > buflen) |
63 | 0 | return SC_ERROR_BUFFER_TOO_SMALL; |
64 | 0 | buf[count++] = CTBCS_TAG_PROMPT; |
65 | 0 | buf[count++] = len; |
66 | 0 | memcpy(buf + count, prompt, len); |
67 | 0 | count += len; |
68 | 0 | } |
69 | | |
70 | | /* card apdu must be last in packet */ |
71 | 0 | if (!data->apdu) |
72 | 0 | return SC_ERROR_INTERNAL; |
73 | 0 | if (count + 12 > buflen) |
74 | 0 | return SC_ERROR_BUFFER_TOO_SMALL; |
75 | | |
76 | 0 | j = count; |
77 | 0 | buf[j++] = CTBCS_TAG_VERIFY_CMD; |
78 | 0 | buf[j++] = 0x00; |
79 | | |
80 | | /* Control byte - length of PIN, and encoding */ |
81 | 0 | control = 0x00; |
82 | 0 | if (data->pin1.encoding == SC_PIN_ENCODING_ASCII) |
83 | 0 | control |= CTBCS_PIN_CONTROL_ENCODE_ASCII; |
84 | 0 | else if (data->pin1.encoding != SC_PIN_ENCODING_BCD) |
85 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
86 | 0 | if (data->pin1.min_length == data->pin1.max_length) |
87 | 0 | control |= data->pin1.min_length << CTBCS_PIN_CONTROL_LEN_SHIFT; |
88 | 0 | buf[j++] = control; |
89 | 0 | buf[j++] = data->pin1.offset+1; /* Looks like offset is 1-based in CTBCS */ |
90 | 0 | buf[j++] = data->apdu->cla; |
91 | 0 | buf[j++] = data->apdu->ins; |
92 | 0 | buf[j++] = data->apdu->p1; |
93 | 0 | buf[j++] = data->apdu->p2; |
94 | |
|
95 | 0 | if (data->flags & SC_PIN_CMD_NEED_PADDING) { |
96 | 0 | len = data->pin1.pad_length; |
97 | 0 | if (2 + j + len > buflen) |
98 | 0 | return SC_ERROR_BUFFER_TOO_SMALL; |
99 | 0 | buf[j++] = len; |
100 | 0 | memset(buf+j, data->pin1.pad_char, len); |
101 | 0 | j += len; |
102 | 0 | } |
103 | | |
104 | 0 | buf[count+1] = j - count - 2; |
105 | 0 | count = j; |
106 | |
|
107 | 0 | apdu->lc = apdu->datalen = count; |
108 | 0 | apdu->data = buf; |
109 | |
|
110 | 0 | return 0; |
111 | 0 | } |
112 | | |
113 | | static int |
114 | | ctbcs_build_modify_verification_apdu(sc_apdu_t *apdu, struct sc_pin_cmd_data *data) |
115 | 0 | { |
116 | 0 | const char *prompt; |
117 | 0 | size_t buflen, count = 0, j = 0, len; |
118 | 0 | static u8 buf[SC_MAX_APDU_BUFFER_SIZE]; |
119 | 0 | u8 control; |
120 | |
|
121 | 0 | ctbcs_init_apdu(apdu, |
122 | 0 | SC_APDU_CASE_3_SHORT, |
123 | 0 | CTBCS_INS_MODIFY_VERIFICATION, |
124 | 0 | CTBCS_P1_INTERFACE1, |
125 | 0 | 0); |
126 | |
|
127 | 0 | buflen = sizeof(buf); |
128 | 0 | prompt = data->pin1.prompt; |
129 | 0 | if (prompt && *prompt) { |
130 | 0 | len = strlen(prompt); |
131 | 0 | if (len + 2 > buflen) |
132 | 0 | return SC_ERROR_BUFFER_TOO_SMALL; |
133 | 0 | buf[count++] = CTBCS_TAG_PROMPT; |
134 | 0 | buf[count++] = len; |
135 | 0 | memcpy(buf + count, prompt, len); |
136 | 0 | count += len; |
137 | 0 | } |
138 | | |
139 | | /* card apdu must be last in packet */ |
140 | 0 | if (!data->apdu) |
141 | 0 | return SC_ERROR_INTERNAL; |
142 | 0 | if (count + 12 > buflen) |
143 | 0 | return SC_ERROR_BUFFER_TOO_SMALL; |
144 | | |
145 | 0 | j = count; |
146 | 0 | buf[j++] = CTBCS_TAG_VERIFY_CMD; |
147 | 0 | buf[j++] = 0x00; |
148 | | |
149 | | /* Control byte - length of PIN, and encoding */ |
150 | 0 | control = 0x00; |
151 | 0 | if (data->pin1.encoding == SC_PIN_ENCODING_ASCII) |
152 | 0 | control |= CTBCS_PIN_CONTROL_ENCODE_ASCII; |
153 | 0 | else if (data->pin1.encoding != SC_PIN_ENCODING_BCD) |
154 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
155 | 0 | if (data->pin1.min_length == data->pin1.max_length) |
156 | 0 | control |= data->pin1.min_length << CTBCS_PIN_CONTROL_LEN_SHIFT; |
157 | 0 | buf[j++] = control; |
158 | 0 | buf[j++] = data->pin1.offset+1; /* Looks like offset is 1-based in CTBCS */ |
159 | 0 | buf[j++] = data->pin2.offset+1; |
160 | 0 | buf[j++] = data->apdu->cla; |
161 | 0 | buf[j++] = data->apdu->ins; |
162 | 0 | buf[j++] = data->apdu->p1; |
163 | 0 | buf[j++] = data->apdu->p2; |
164 | |
|
165 | 0 | if (data->flags & SC_PIN_CMD_NEED_PADDING) { |
166 | 0 | len = data->pin1.pad_length + data->pin2.pad_length; |
167 | 0 | if (2 + j + len > buflen) |
168 | 0 | return SC_ERROR_BUFFER_TOO_SMALL; |
169 | 0 | buf[j++] = len; |
170 | 0 | memset(buf+j, data->pin1.pad_char, len); |
171 | 0 | j += len; |
172 | 0 | } |
173 | | |
174 | 0 | buf[count+1] = j - count - 2; |
175 | 0 | count = j; |
176 | |
|
177 | 0 | apdu->lc = apdu->datalen = count; |
178 | 0 | apdu->data = buf; |
179 | |
|
180 | 0 | return 0; |
181 | 0 | } |
182 | | |
183 | | int |
184 | | ctbcs_pin_cmd(sc_reader_t *reader, struct sc_pin_cmd_data *data) |
185 | 0 | { |
186 | 0 | sc_card_t dummy_card, *card; |
187 | 0 | sc_apdu_t apdu; |
188 | 0 | struct sc_card_operations ops; |
189 | 0 | int r, s; |
190 | |
|
191 | 0 | switch (data->cmd) { |
192 | 0 | case SC_PIN_CMD_VERIFY: |
193 | 0 | r = ctbcs_build_perform_verification_apdu(&apdu, data); |
194 | 0 | if (r != SC_SUCCESS) |
195 | 0 | return r; |
196 | 0 | break; |
197 | 0 | case SC_PIN_CMD_CHANGE: |
198 | 0 | case SC_PIN_CMD_UNBLOCK: |
199 | 0 | r = ctbcs_build_modify_verification_apdu(&apdu, data); |
200 | 0 | if (r != SC_SUCCESS) |
201 | 0 | return r; |
202 | 0 | break; |
203 | 0 | default: |
204 | 0 | sc_log(reader->ctx, "Unknown PIN command %d", data->cmd); |
205 | 0 | return SC_ERROR_NOT_SUPPORTED; |
206 | 0 | } |
207 | | |
208 | 0 | memset(&ops, 0, sizeof(ops)); |
209 | 0 | memset(&dummy_card, 0, sizeof(dummy_card)); |
210 | 0 | dummy_card.reader = reader; |
211 | 0 | dummy_card.ctx = reader->ctx; |
212 | 0 | r = sc_mutex_create(reader->ctx, &dummy_card.mutex); |
213 | 0 | if (r != SC_SUCCESS) |
214 | 0 | return r; |
215 | 0 | dummy_card.ops = &ops; |
216 | 0 | card = &dummy_card; |
217 | |
|
218 | 0 | r = sc_transmit_apdu(card, &apdu); |
219 | 0 | s = sc_mutex_destroy(reader->ctx, card->mutex); |
220 | 0 | if (s != SC_SUCCESS) { |
221 | 0 | sc_log(reader->ctx, "unable to destroy mutex\n"); |
222 | 0 | return s; |
223 | 0 | } |
224 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
225 | | |
226 | | /* Check CTBCS status word */ |
227 | 0 | switch (((unsigned int) apdu.sw1 << 8) | apdu.sw2) { |
228 | 0 | case 0x9000: |
229 | 0 | r = 0; |
230 | 0 | break; |
231 | 0 | case 0x6400: /* Input timed out */ |
232 | 0 | r = SC_ERROR_KEYPAD_TIMEOUT; |
233 | 0 | break; |
234 | 0 | case 0x6401: /* Input cancelled */ |
235 | 0 | r = SC_ERROR_KEYPAD_CANCELLED; |
236 | 0 | break; |
237 | 0 | case 0x6402: /* PINs did not match */ |
238 | 0 | r = SC_ERROR_KEYPAD_PIN_MISMATCH; |
239 | 0 | break; |
240 | 0 | case 0x6700: /* message too long */ |
241 | 0 | r = SC_ERROR_KEYPAD_MSG_TOO_LONG; |
242 | 0 | break; |
243 | 0 | default: |
244 | 0 | r = SC_ERROR_CARD_CMD_FAILED; |
245 | 0 | break; |
246 | 0 | } |
247 | 0 | LOG_TEST_RET(card->ctx, r, "PIN command failed"); |
248 | | |
249 | | /* Calling Function may expect SW1/SW2 in data-apdu set... */ |
250 | 0 | if (data->apdu) { |
251 | 0 | data->apdu->sw1 = apdu.sw1; |
252 | 0 | data->apdu->sw2 = apdu.sw2; |
253 | 0 | } |
254 | |
|
255 | 0 | return 0; |
256 | 0 | } |