/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 <assert.h> |
26 | | #include <stdlib.h> |
27 | | #include <string.h> |
28 | | |
29 | | #include "internal.h" |
30 | | #include "ctbcs.h" |
31 | | |
32 | | static void |
33 | | ctbcs_init_apdu(sc_apdu_t *apdu, int cse, int ins, int p1, int p2) |
34 | 0 | { |
35 | 0 | memset(apdu, 0, sizeof(*apdu)); |
36 | 0 | apdu->cse = cse; |
37 | 0 | apdu->cla = 0x20; |
38 | 0 | apdu->ins = ins; |
39 | 0 | apdu->p1 = p1; |
40 | 0 | apdu->p2 = p2; |
41 | |
|
42 | 0 | apdu->control = 1; |
43 | 0 | } |
44 | | |
45 | | static int |
46 | | ctbcs_build_perform_verification_apdu(sc_apdu_t *apdu, struct sc_pin_cmd_data *data) |
47 | 0 | { |
48 | 0 | const char *prompt; |
49 | 0 | size_t buflen, count = 0, j = 0, len; |
50 | 0 | static u8 buf[SC_MAX_APDU_BUFFER_SIZE]; |
51 | 0 | u8 control; |
52 | |
|
53 | 0 | ctbcs_init_apdu(apdu, |
54 | 0 | SC_APDU_CASE_3_SHORT, |
55 | 0 | CTBCS_INS_PERFORM_VERIFICATION, |
56 | 0 | CTBCS_P1_INTERFACE1, |
57 | 0 | 0); |
58 | |
|
59 | 0 | buflen = sizeof(buf); |
60 | 0 | prompt = data->pin1.prompt; |
61 | 0 | if (prompt && *prompt) { |
62 | 0 | len = strlen(prompt); |
63 | 0 | if (len + 2 > buflen) |
64 | 0 | return SC_ERROR_BUFFER_TOO_SMALL; |
65 | 0 | buf[count++] = CTBCS_TAG_PROMPT; |
66 | 0 | buf[count++] = len; |
67 | 0 | memcpy(buf + count, prompt, len); |
68 | 0 | count += len; |
69 | 0 | } |
70 | | |
71 | | /* card apdu must be last in packet */ |
72 | 0 | if (!data->apdu) |
73 | 0 | return SC_ERROR_INTERNAL; |
74 | 0 | if (count + 12 > buflen) |
75 | 0 | return SC_ERROR_BUFFER_TOO_SMALL; |
76 | | |
77 | 0 | j = count; |
78 | 0 | buf[j++] = CTBCS_TAG_VERIFY_CMD; |
79 | 0 | buf[j++] = 0x00; |
80 | | |
81 | | /* Control byte - length of PIN, and encoding */ |
82 | 0 | control = 0x00; |
83 | 0 | if (data->pin1.encoding == SC_PIN_ENCODING_ASCII) |
84 | 0 | control |= CTBCS_PIN_CONTROL_ENCODE_ASCII; |
85 | 0 | else if (data->pin1.encoding != SC_PIN_ENCODING_BCD) |
86 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
87 | 0 | if (data->pin1.min_length == data->pin1.max_length) |
88 | 0 | control |= data->pin1.min_length << CTBCS_PIN_CONTROL_LEN_SHIFT; |
89 | 0 | buf[j++] = control; |
90 | 0 | buf[j++] = data->pin1.offset+1; /* Looks like offset is 1-based in CTBCS */ |
91 | 0 | buf[j++] = data->apdu->cla; |
92 | 0 | buf[j++] = data->apdu->ins; |
93 | 0 | buf[j++] = data->apdu->p1; |
94 | 0 | buf[j++] = data->apdu->p2; |
95 | |
|
96 | 0 | if (data->flags & SC_PIN_CMD_NEED_PADDING) { |
97 | 0 | len = data->pin1.pad_length; |
98 | 0 | if (2 + j + len > buflen) |
99 | 0 | return SC_ERROR_BUFFER_TOO_SMALL; |
100 | 0 | buf[j++] = len; |
101 | 0 | memset(buf+j, data->pin1.pad_char, len); |
102 | 0 | j += len; |
103 | 0 | } |
104 | | |
105 | 0 | buf[count+1] = j - count - 2; |
106 | 0 | count = j; |
107 | |
|
108 | 0 | apdu->lc = apdu->datalen = count; |
109 | 0 | apdu->data = buf; |
110 | |
|
111 | 0 | return 0; |
112 | 0 | } |
113 | | |
114 | | static int |
115 | | ctbcs_build_modify_verification_apdu(sc_apdu_t *apdu, struct sc_pin_cmd_data *data) |
116 | 0 | { |
117 | 0 | const char *prompt; |
118 | 0 | size_t buflen, count = 0, j = 0, len; |
119 | 0 | static u8 buf[SC_MAX_APDU_BUFFER_SIZE]; |
120 | 0 | u8 control; |
121 | |
|
122 | 0 | ctbcs_init_apdu(apdu, |
123 | 0 | SC_APDU_CASE_3_SHORT, |
124 | 0 | CTBCS_INS_MODIFY_VERIFICATION, |
125 | 0 | CTBCS_P1_INTERFACE1, |
126 | 0 | 0); |
127 | |
|
128 | 0 | buflen = sizeof(buf); |
129 | 0 | prompt = data->pin1.prompt; |
130 | 0 | if (prompt && *prompt) { |
131 | 0 | len = strlen(prompt); |
132 | 0 | if (len + 2 > buflen) |
133 | 0 | return SC_ERROR_BUFFER_TOO_SMALL; |
134 | 0 | buf[count++] = CTBCS_TAG_PROMPT; |
135 | 0 | buf[count++] = len; |
136 | 0 | memcpy(buf + count, prompt, len); |
137 | 0 | count += len; |
138 | 0 | } |
139 | | |
140 | | /* card apdu must be last in packet */ |
141 | 0 | if (!data->apdu) |
142 | 0 | return SC_ERROR_INTERNAL; |
143 | 0 | if (count + 12 > buflen) |
144 | 0 | return SC_ERROR_BUFFER_TOO_SMALL; |
145 | | |
146 | 0 | j = count; |
147 | 0 | buf[j++] = CTBCS_TAG_VERIFY_CMD; |
148 | 0 | buf[j++] = 0x00; |
149 | | |
150 | | /* Control byte - length of PIN, and encoding */ |
151 | 0 | control = 0x00; |
152 | 0 | if (data->pin1.encoding == SC_PIN_ENCODING_ASCII) |
153 | 0 | control |= CTBCS_PIN_CONTROL_ENCODE_ASCII; |
154 | 0 | else if (data->pin1.encoding != SC_PIN_ENCODING_BCD) |
155 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
156 | 0 | if (data->pin1.min_length == data->pin1.max_length) |
157 | 0 | control |= data->pin1.min_length << CTBCS_PIN_CONTROL_LEN_SHIFT; |
158 | 0 | buf[j++] = control; |
159 | 0 | buf[j++] = data->pin1.offset+1; /* Looks like offset is 1-based in CTBCS */ |
160 | 0 | buf[j++] = data->pin2.offset+1; |
161 | 0 | buf[j++] = data->apdu->cla; |
162 | 0 | buf[j++] = data->apdu->ins; |
163 | 0 | buf[j++] = data->apdu->p1; |
164 | 0 | buf[j++] = data->apdu->p2; |
165 | |
|
166 | 0 | if (data->flags & SC_PIN_CMD_NEED_PADDING) { |
167 | 0 | len = data->pin1.pad_length + data->pin2.pad_length; |
168 | 0 | if (2 + j + len > buflen) |
169 | 0 | return SC_ERROR_BUFFER_TOO_SMALL; |
170 | 0 | buf[j++] = len; |
171 | 0 | memset(buf+j, data->pin1.pad_char, len); |
172 | 0 | j += len; |
173 | 0 | } |
174 | | |
175 | 0 | buf[count+1] = j - count - 2; |
176 | 0 | count = j; |
177 | |
|
178 | 0 | apdu->lc = apdu->datalen = count; |
179 | 0 | apdu->data = buf; |
180 | |
|
181 | 0 | return 0; |
182 | 0 | } |
183 | | |
184 | | int |
185 | | ctbcs_pin_cmd(sc_reader_t *reader, struct sc_pin_cmd_data *data) |
186 | 0 | { |
187 | 0 | sc_card_t dummy_card, *card; |
188 | 0 | sc_apdu_t apdu; |
189 | 0 | struct sc_card_operations ops; |
190 | 0 | int r, s; |
191 | |
|
192 | 0 | switch (data->cmd) { |
193 | 0 | case SC_PIN_CMD_VERIFY: |
194 | 0 | r = ctbcs_build_perform_verification_apdu(&apdu, data); |
195 | 0 | if (r != SC_SUCCESS) |
196 | 0 | return r; |
197 | 0 | break; |
198 | 0 | case SC_PIN_CMD_CHANGE: |
199 | 0 | case SC_PIN_CMD_UNBLOCK: |
200 | 0 | r = ctbcs_build_modify_verification_apdu(&apdu, data); |
201 | 0 | if (r != SC_SUCCESS) |
202 | 0 | return r; |
203 | 0 | break; |
204 | 0 | default: |
205 | 0 | sc_log(reader->ctx, "Unknown PIN command %d", data->cmd); |
206 | 0 | return SC_ERROR_NOT_SUPPORTED; |
207 | 0 | } |
208 | | |
209 | 0 | memset(&ops, 0, sizeof(ops)); |
210 | 0 | memset(&dummy_card, 0, sizeof(dummy_card)); |
211 | 0 | dummy_card.reader = reader; |
212 | 0 | dummy_card.ctx = reader->ctx; |
213 | 0 | r = sc_mutex_create(reader->ctx, &dummy_card.mutex); |
214 | 0 | if (r != SC_SUCCESS) |
215 | 0 | return r; |
216 | 0 | dummy_card.ops = &ops; |
217 | 0 | card = &dummy_card; |
218 | |
|
219 | 0 | r = sc_transmit_apdu(card, &apdu); |
220 | 0 | s = sc_mutex_destroy(reader->ctx, card->mutex); |
221 | 0 | if (s != SC_SUCCESS) { |
222 | 0 | sc_log(reader->ctx, "unable to destroy mutex\n"); |
223 | 0 | return s; |
224 | 0 | } |
225 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
226 | | |
227 | | /* Check CTBCS status word */ |
228 | 0 | switch (((unsigned int) apdu.sw1 << 8) | apdu.sw2) { |
229 | 0 | case 0x9000: |
230 | 0 | r = 0; |
231 | 0 | break; |
232 | 0 | case 0x6400: /* Input timed out */ |
233 | 0 | r = SC_ERROR_KEYPAD_TIMEOUT; |
234 | 0 | break; |
235 | 0 | case 0x6401: /* Input cancelled */ |
236 | 0 | r = SC_ERROR_KEYPAD_CANCELLED; |
237 | 0 | break; |
238 | 0 | case 0x6402: /* PINs did not match */ |
239 | 0 | r = SC_ERROR_KEYPAD_PIN_MISMATCH; |
240 | 0 | break; |
241 | 0 | case 0x6700: /* message too long */ |
242 | 0 | r = SC_ERROR_KEYPAD_MSG_TOO_LONG; |
243 | 0 | break; |
244 | 0 | default: |
245 | 0 | r = SC_ERROR_CARD_CMD_FAILED; |
246 | 0 | break; |
247 | 0 | } |
248 | 0 | LOG_TEST_RET(card->ctx, r, "PIN command failed"); |
249 | | |
250 | | /* Calling Function may expect SW1/SW2 in data-apdu set... */ |
251 | 0 | if (data->apdu) { |
252 | 0 | data->apdu->sw1 = apdu.sw1; |
253 | 0 | data->apdu->sw2 = apdu.sw2; |
254 | 0 | } |
255 | |
|
256 | 0 | return 0; |
257 | 0 | } |