/src/opensc/src/libopensc/card-rtecp.c
Line | Count | Source |
1 | | /* |
2 | | * card-rtecp.c: Support for Rutoken ECP and Rutoken Lite cards |
3 | | * |
4 | | * Copyright (C) 2009 Aleksey Samsonov <samsonov@guardant.ru> |
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 <stddef.h> |
26 | | #include <stdlib.h> |
27 | | #include <string.h> |
28 | | |
29 | | #include "internal.h" |
30 | | #include "asn1.h" |
31 | | #include "cardctl.h" |
32 | | |
33 | | static const struct sc_card_operations *iso_ops = NULL; |
34 | | static struct sc_card_operations rtecp_ops; |
35 | | |
36 | | static struct sc_card_driver rtecp_drv = { |
37 | | "Rutoken ECP and Lite driver", |
38 | | "rutoken_ecp", |
39 | | &rtecp_ops, |
40 | | NULL, 0, NULL |
41 | | }; |
42 | | |
43 | | static const struct sc_atr_table rtecp_atrs[] = { |
44 | | /* Rutoken ECP */ |
45 | | { "3B:8B:01:52:75:74:6F:6B:65:6E:20:45:43:50:A0", |
46 | | NULL, "Rutoken ECP", SC_CARD_TYPE_RUTOKEN_ECP, 0, NULL }, |
47 | | /* Rutoken ECP (DS) */ |
48 | | { "3B:8B:01:52:75:74:6F:6B:65:6E:20:44:53:20:C1", |
49 | | NULL, "Rutoken ECP (DS)", SC_CARD_TYPE_RUTOKEN_ECP, 0, NULL }, |
50 | | /* Rutoken ECP SC T0 */ |
51 | | { "3B:9C:96:00:52:75:74:6F:6B:65:6E:45:43:50:73:63", |
52 | | "00:00:00:00:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF", |
53 | | "Rutoken ECP SC", SC_CARD_TYPE_RUTOKEN_ECP_SC, 0, NULL }, |
54 | | /* Rutoken ECP SC T1 */ |
55 | | { "3B:9C:94:80:11:40:52:75:74:6F:6B:65:6E:45:43:50:73:63:C3", |
56 | | "00:00:00:00:00:00:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:00", |
57 | | "Rutoken ECP SC", SC_CARD_TYPE_RUTOKEN_ECP_SC, 0, NULL }, |
58 | | /* Rutoken ECP SC NFC */ |
59 | | { "3B:88:80:01:52:74:53:43:77:81:83:20:6A", |
60 | | "00:00:00:00:FF:FF:FF:FF:00:00:00:00:00", |
61 | | "Rutoken ECP SC NFC", SC_CARD_TYPE_RUTOKEN_ECP_SC, 0, NULL }, |
62 | | /* Rutoken Lite */ |
63 | | { "3B:8B:01:52:75:74:6F:6B:65:6E:6C:69:74:65:C2", |
64 | | NULL, "Rutoken Lite", SC_CARD_TYPE_RUTOKEN_LITE, 0, NULL }, |
65 | | /* Rutoken Lite SC*/ |
66 | | { "3B:9E:96:00:52:75:74:6F:6B:65:6E:4C:69:74:65:53:43:32", |
67 | | "00:00:00:00:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF", |
68 | | "Rutoken Lite SC", SC_CARD_TYPE_RUTOKEN_LITE_SC, 0, NULL }, |
69 | | { NULL, NULL, NULL, 0, 0, NULL } |
70 | | }; |
71 | | |
72 | | static int rtecp_match_card(sc_card_t *card) |
73 | 10.3k | { |
74 | 10.3k | int i = -1; |
75 | 10.3k | i = _sc_match_atr(card, rtecp_atrs, &card->type); |
76 | 10.3k | if (i >= 0) { |
77 | 154 | card->name = rtecp_atrs[i].name; |
78 | 154 | LOG_FUNC_RETURN(card->ctx, 1); |
79 | 154 | } |
80 | 10.2k | LOG_FUNC_RETURN(card->ctx, 0); |
81 | 10.2k | } |
82 | | |
83 | | static int rtecp_init(sc_card_t *card) |
84 | 154 | { |
85 | 154 | sc_algorithm_info_t info; |
86 | 154 | unsigned long flags; |
87 | | |
88 | 154 | if (!card || !card->ctx) |
89 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
90 | | |
91 | 154 | card->cla = 0; |
92 | | |
93 | 154 | if (card->type == SC_CARD_TYPE_RUTOKEN_LITE |
94 | 154 | || card->type == SC_CARD_TYPE_RUTOKEN_LITE_SC) |
95 | 4 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); |
96 | | |
97 | 150 | card->caps |= SC_CARD_CAP_RNG; |
98 | | |
99 | 150 | flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_ONBOARD_KEY_GEN |
100 | 150 | | SC_ALGORITHM_RSA_PAD_NONE | SC_ALGORITHM_RSA_HASH_NONE; |
101 | | |
102 | 150 | _sc_card_add_rsa_alg(card, 256, flags, 0); |
103 | 150 | _sc_card_add_rsa_alg(card, 512, flags, 0); |
104 | 150 | _sc_card_add_rsa_alg(card, 768, flags, 0); |
105 | 150 | _sc_card_add_rsa_alg(card, 1024, flags, 0); |
106 | 150 | _sc_card_add_rsa_alg(card, 1280, flags, 0); |
107 | 150 | _sc_card_add_rsa_alg(card, 1536, flags, 0); |
108 | 150 | _sc_card_add_rsa_alg(card, 1792, flags, 0); |
109 | 150 | _sc_card_add_rsa_alg(card, 2048, flags, 0); |
110 | 150 | _sc_card_add_rsa_alg(card, 4096, flags, 0); |
111 | | |
112 | 150 | memset(&info, 0, sizeof(info)); |
113 | 150 | info.algorithm = SC_ALGORITHM_GOSTR3410; |
114 | 150 | info.key_length = 256; |
115 | 150 | info.flags = SC_ALGORITHM_GOSTR3410_RAW | SC_ALGORITHM_ONBOARD_KEY_GEN |
116 | 150 | | SC_ALGORITHM_GOSTR3410_HASH_NONE; |
117 | 150 | _sc_card_add_algorithm(card, &info); |
118 | | |
119 | 150 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); |
120 | 150 | } |
121 | | |
122 | | static void reverse(unsigned char *buf, size_t len) |
123 | 0 | { |
124 | 0 | unsigned char tmp; |
125 | 0 | size_t i; |
126 | |
|
127 | 0 | if (!buf && len != 0) |
128 | 0 | return; |
129 | | |
130 | 0 | for (i = 0; i < len / 2; ++i) |
131 | 0 | { |
132 | 0 | tmp = buf[i]; |
133 | 0 | buf[i] = buf[len - 1 - i]; |
134 | 0 | buf[len - 1 - i] = tmp; |
135 | 0 | } |
136 | 0 | } |
137 | | |
138 | | static unsigned int sec_attr_to_method(unsigned int attr) |
139 | 81 | { |
140 | 81 | if (attr == 0xFF) |
141 | 12 | return SC_AC_NEVER; |
142 | 69 | else if (attr == 0) |
143 | 9 | return SC_AC_NONE; |
144 | 60 | else if (attr & 0x03) |
145 | 42 | return SC_AC_CHV; |
146 | 18 | else |
147 | 18 | return SC_AC_UNKNOWN; |
148 | 81 | } |
149 | | |
150 | | static unsigned long sec_attr_to_key_ref(unsigned int attr) |
151 | 81 | { |
152 | 81 | if (attr == 1 || attr == 2) |
153 | 16 | return attr; |
154 | 65 | return 0; |
155 | 81 | } |
156 | | |
157 | | static unsigned int to_sec_attr(unsigned int method, unsigned int key_ref) |
158 | 202 | { |
159 | 202 | if (method == SC_AC_NEVER || method == SC_AC_NONE) |
160 | 111 | return method; |
161 | 91 | if (method == SC_AC_CHV && (key_ref == 1 || key_ref == 2)) |
162 | 0 | return key_ref; |
163 | 91 | return 0; |
164 | 91 | } |
165 | | |
166 | | static void set_acl_from_sec_attr(sc_card_t *card, sc_file_t *file) |
167 | 42 | { |
168 | 42 | unsigned int method; |
169 | 42 | unsigned long key_ref; |
170 | | |
171 | 42 | if (!card || !card->ctx || !file || !file->sec_attr |
172 | 42 | || file->sec_attr_len != SC_RTECP_SEC_ATTR_SIZE |
173 | 0 | || 1 + 6 >= SC_RTECP_SEC_ATTR_SIZE) |
174 | 0 | { |
175 | 0 | return; |
176 | 0 | } |
177 | | |
178 | 42 | sc_file_add_acl_entry(file, SC_AC_OP_SELECT, SC_AC_NONE, SC_AC_KEY_REF_NONE); |
179 | 42 | if (file->sec_attr[0] & 0x40) /* if AccessMode.6 */ |
180 | 26 | { |
181 | 26 | method = sec_attr_to_method(file->sec_attr[1 + 6]); |
182 | 26 | key_ref = sec_attr_to_key_ref(file->sec_attr[1 + 6]); |
183 | 26 | sc_log(card->ctx, |
184 | 26 | "SC_AC_OP_DELETE %i %lu\n", |
185 | 26 | (int)method, key_ref); |
186 | 26 | sc_file_add_acl_entry(file, SC_AC_OP_DELETE, method, key_ref); |
187 | 26 | } |
188 | 42 | if (file->sec_attr[0] & 0x01) /* if AccessMode.0 */ |
189 | 29 | { |
190 | 29 | method = sec_attr_to_method(file->sec_attr[1 + 0]); |
191 | 29 | key_ref = sec_attr_to_key_ref(file->sec_attr[1 + 0]); |
192 | 29 | sc_log(card->ctx, |
193 | 29 | (file->type == SC_FILE_TYPE_DF) ? |
194 | 29 | "SC_AC_OP_CREATE %i %lu\n" |
195 | 29 | : "SC_AC_OP_READ %i %lu\n", |
196 | 29 | (int)method, key_ref); |
197 | 29 | sc_file_add_acl_entry(file, (file->type == SC_FILE_TYPE_DF) ? |
198 | 28 | SC_AC_OP_CREATE : SC_AC_OP_READ, method, key_ref); |
199 | 29 | } |
200 | 42 | if (file->type == SC_FILE_TYPE_DF) |
201 | 1 | { |
202 | 1 | sc_file_add_acl_entry(file, SC_AC_OP_LIST_FILES, |
203 | 1 | SC_AC_NONE, SC_AC_KEY_REF_NONE); |
204 | 1 | } |
205 | 41 | else |
206 | 41 | if (file->sec_attr[0] & 0x02) /* if AccessMode.1 */ |
207 | 26 | { |
208 | 26 | method = sec_attr_to_method(file->sec_attr[1 + 1]); |
209 | 26 | key_ref = sec_attr_to_key_ref(file->sec_attr[1 + 1]); |
210 | 26 | sc_log(card->ctx, |
211 | 26 | "SC_AC_OP_UPDATE %i %lu\n", |
212 | 26 | (int)method, key_ref); |
213 | 26 | sc_file_add_acl_entry(file, SC_AC_OP_UPDATE, method, key_ref); |
214 | 26 | sc_log(card->ctx, |
215 | 26 | "SC_AC_OP_WRITE %i %lu\n", |
216 | 26 | (int)method, key_ref); |
217 | 26 | sc_file_add_acl_entry(file, SC_AC_OP_WRITE, method, key_ref); |
218 | 26 | } |
219 | 42 | } |
220 | | |
221 | | static int set_sec_attr_from_acl(sc_card_t *card, sc_file_t *file) |
222 | 58 | { |
223 | 58 | const sc_acl_entry_t *entry; |
224 | 58 | u8 sec_attr[SC_RTECP_SEC_ATTR_SIZE] = { 0 }; |
225 | 58 | int r; |
226 | | |
227 | 58 | if (!card || !card->ctx || !file |
228 | 58 | || file->sec_attr || file->sec_attr_len != 0) |
229 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
230 | | |
231 | 58 | entry = sc_file_get_acl_entry(file, SC_AC_OP_DELETE); |
232 | 58 | if (entry) |
233 | 58 | { |
234 | 58 | sec_attr[0] |= 0x40; |
235 | 58 | sec_attr[1 + 6] = to_sec_attr(entry->method, entry->key_ref); |
236 | 58 | } |
237 | 58 | if (file->type == SC_FILE_TYPE_DF) |
238 | 30 | { |
239 | 30 | entry = sc_file_get_acl_entry(file, SC_AC_OP_CREATE); |
240 | 30 | if (entry) |
241 | 30 | { |
242 | | /* ATTR: Create DF/EF file */ |
243 | 30 | sec_attr[0] |= 0x01; |
244 | 30 | sec_attr[1 + 0] = to_sec_attr(entry->method, entry->key_ref); |
245 | | /* ATTR: Create Internal EF (RSF) file */ |
246 | 30 | sec_attr[0] |= 0x02; |
247 | 30 | sec_attr[1 + 1] = to_sec_attr(entry->method, entry->key_ref); |
248 | 30 | } |
249 | 30 | } |
250 | 28 | else |
251 | 28 | { |
252 | 28 | entry = sc_file_get_acl_entry(file, SC_AC_OP_READ); |
253 | 28 | if (entry) |
254 | 28 | { |
255 | 28 | sec_attr[0] |= 0x01; |
256 | 28 | sec_attr[1 + 0] = to_sec_attr(entry->method, entry->key_ref); |
257 | 28 | } |
258 | 28 | entry = sc_file_get_acl_entry(file, SC_AC_OP_WRITE); |
259 | 28 | if (entry) |
260 | 28 | { |
261 | 28 | sec_attr[0] |= 0x02; |
262 | 28 | sec_attr[1 + 1] = to_sec_attr(entry->method, entry->key_ref); |
263 | 28 | } |
264 | 28 | entry = sc_file_get_acl_entry(file, SC_AC_OP_UPDATE); |
265 | 28 | if (entry) |
266 | 28 | { |
267 | | /* rewrite if sec_attr[1 + 1] already set */ |
268 | 28 | sec_attr[0] |= 0x02; |
269 | 28 | sec_attr[1 + 1] = to_sec_attr(entry->method, entry->key_ref); |
270 | 28 | } |
271 | 28 | } |
272 | | /* FIXME: Find the best solution */ |
273 | 58 | if (file->path.len == 2 && !memcmp(file->path.value, "\x3F\x00", 2)) |
274 | 1 | { |
275 | | /* ATTR: Put data */ |
276 | 1 | sec_attr[0] |= 0x04; |
277 | 1 | sec_attr[1 + 2] = 1; /* so-pin reference */ |
278 | 1 | } |
279 | 58 | r = sc_file_set_sec_attr(file, sec_attr, sizeof(sec_attr)); |
280 | 58 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); |
281 | 58 | } |
282 | | |
283 | | static int rtecp_select_file(sc_card_t *card, |
284 | | const sc_path_t *in_path, sc_file_t **file_out) |
285 | 695 | { |
286 | 695 | sc_file_t *file = NULL; |
287 | 695 | int r = SC_SUCCESS; |
288 | | |
289 | 695 | if (!card || !card->ctx || !in_path) |
290 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
291 | | |
292 | 695 | SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); |
293 | | |
294 | 695 | switch (in_path->type) |
295 | 695 | { |
296 | 201 | case SC_PATH_TYPE_DF_NAME: |
297 | 201 | case SC_PATH_TYPE_FROM_CURRENT: |
298 | 201 | case SC_PATH_TYPE_PARENT: |
299 | 201 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED); |
300 | 695 | } |
301 | | |
302 | | // Card Rutoken ECP SC T0 doesn't support SELECT FILE without return a file info. |
303 | | // So here we request a file and then assign/free it depending on file_out. |
304 | 494 | r = iso_ops->select_file(card, in_path, &file); |
305 | 494 | if (r != SC_SUCCESS) |
306 | 426 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); |
307 | | |
308 | 68 | if (file->sec_attr && file->sec_attr_len == SC_RTECP_SEC_ATTR_SIZE) |
309 | 42 | set_acl_from_sec_attr(card, file); |
310 | 26 | else |
311 | 26 | { |
312 | 26 | sc_file_free(file); |
313 | 26 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_UNKNOWN_DATA_RECEIVED); |
314 | 26 | } |
315 | | |
316 | 42 | if (file_out) |
317 | 40 | *file_out = file; |
318 | 2 | else |
319 | 2 | sc_file_free(file); |
320 | | |
321 | 42 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); |
322 | 42 | } |
323 | | |
324 | | static int rtecp_verify(sc_card_t *card, unsigned int type, int ref_qualifier, |
325 | | const u8 *data, size_t data_len, int *tries_left) |
326 | 0 | { |
327 | 0 | sc_apdu_t apdu; |
328 | 0 | int r, send_logout = 0; |
329 | |
|
330 | 0 | (void)type; /* no warning */ |
331 | |
|
332 | 0 | if (!card || !card->ctx || !data) |
333 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
334 | | |
335 | 0 | for (;;) |
336 | 0 | { |
337 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, |
338 | 0 | 0x20, 0, ref_qualifier); |
339 | 0 | apdu.lc = data_len; |
340 | 0 | apdu.data = data; |
341 | 0 | apdu.datalen = data_len; |
342 | 0 | r = sc_transmit_apdu(card, &apdu); |
343 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
344 | 0 | if (send_logout++ == 0 && apdu.sw1 == 0x6F && apdu.sw2 == 0x86) |
345 | 0 | { |
346 | 0 | r = sc_logout(card); |
347 | 0 | LOG_TEST_RET(card->ctx, r, "Logout failed"); |
348 | 0 | } |
349 | 0 | else |
350 | 0 | break; |
351 | 0 | } |
352 | 0 | if (apdu.sw1 == 0x63 && apdu.sw2 == 0) |
353 | 0 | { |
354 | | /* Verification failed */ |
355 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0, ref_qualifier); |
356 | 0 | r = sc_transmit_apdu(card, &apdu); |
357 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
358 | 0 | } |
359 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
360 | 0 | if (r == SC_ERROR_PIN_CODE_INCORRECT && tries_left) |
361 | 0 | *tries_left = (int)(apdu.sw2 & 0x0F); |
362 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); |
363 | 0 | } |
364 | | |
365 | | static int rtecp_logout(sc_card_t *card) |
366 | 0 | { |
367 | 0 | sc_apdu_t apdu; |
368 | 0 | int r; |
369 | |
|
370 | 0 | if (!card || !card->ctx) |
371 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
372 | | |
373 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x40, 0, 0); |
374 | 0 | apdu.cla = 0x80; |
375 | 0 | r = sc_transmit_apdu(card, &apdu); |
376 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
377 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
378 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); |
379 | 0 | } |
380 | | |
381 | | static int rtecp_set_security_env( struct sc_card *card, |
382 | | const struct sc_security_env *env, |
383 | | int se_num) |
384 | 0 | { |
385 | 0 | struct sc_security_env se_env; |
386 | 0 | if(!env) |
387 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
388 | | |
389 | 0 | se_env= *env; |
390 | 0 | se_env.flags &= ~SC_SEC_ENV_FILE_REF_PRESENT; |
391 | 0 | return iso_ops->set_security_env(card, &se_env, se_num); |
392 | 0 | } |
393 | | |
394 | | static int rtecp_cipher(sc_card_t *card, const u8 *data, size_t data_len, |
395 | | u8 *out, size_t out_len, int sign) |
396 | 0 | { |
397 | 0 | sc_apdu_t apdu; |
398 | 0 | u8 *buf, *buf_out; |
399 | 0 | size_t i; |
400 | 0 | int r; |
401 | |
|
402 | 0 | if (!card || !card->ctx || !data || !out) |
403 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
404 | | |
405 | 0 | buf_out = malloc(out_len + 2); |
406 | 0 | buf = malloc(data_len); |
407 | 0 | if (!buf || !buf_out) |
408 | 0 | { |
409 | 0 | free(buf); |
410 | 0 | free(buf_out); |
411 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); |
412 | 0 | } |
413 | | |
414 | 0 | for (i = 0; i < data_len; ++i) |
415 | 0 | buf[i] = data[data_len - 1 - i]; |
416 | |
|
417 | 0 | if (sign) |
418 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x9E, 0x9A); |
419 | 0 | else |
420 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x80, 0x86); |
421 | 0 | apdu.lc = data_len; |
422 | 0 | apdu.data = buf; |
423 | 0 | apdu.datalen = data_len; |
424 | 0 | apdu.resp = buf_out; |
425 | 0 | apdu.resplen = out_len + 2; |
426 | 0 | apdu.le = out_len > 256 ? 256 : out_len; |
427 | 0 | if (apdu.lc > 255) |
428 | 0 | apdu.flags |= SC_APDU_FLAGS_CHAINING; |
429 | 0 | r = sc_transmit_apdu(card, &apdu); |
430 | 0 | if (!sign) |
431 | 0 | sc_mem_clear(buf, data_len); |
432 | |
|
433 | 0 | free(buf); |
434 | 0 | if (r) |
435 | 0 | sc_log(card->ctx, "APDU transmit failed: %s\n", sc_strerror(r)); |
436 | 0 | else |
437 | 0 | { |
438 | 0 | if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) |
439 | 0 | { |
440 | 0 | if (apdu.resplen > out_len) { |
441 | 0 | free(buf_out); |
442 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, |
443 | 0 | SC_ERROR_BUFFER_TOO_SMALL); |
444 | 0 | } |
445 | 0 | for (i = 0; i < apdu.resplen; ++i) |
446 | 0 | out[i] = buf_out[apdu.resplen - 1 - i]; |
447 | 0 | r = (i > 0) ? (int)i : SC_ERROR_INTERNAL; |
448 | 0 | } |
449 | 0 | else |
450 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
451 | 0 | } |
452 | 0 | if (!sign) |
453 | 0 | sc_mem_clear(buf_out, out_len + 2); |
454 | |
|
455 | 0 | free(buf_out); |
456 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); |
457 | |
|
458 | 0 | } |
459 | | |
460 | | static int rtecp_decipher(sc_card_t *card, |
461 | | const u8 *data, size_t data_len, u8 *out, size_t out_len) |
462 | 0 | { |
463 | 0 | int r; |
464 | |
|
465 | 0 | if (!card || !card->ctx || !data || !out) |
466 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
467 | | |
468 | 0 | if (card->type == SC_CARD_TYPE_RUTOKEN_LITE |
469 | 0 | || card->type == SC_CARD_TYPE_RUTOKEN_LITE_SC) |
470 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED); |
471 | | |
472 | | /* decipher */ |
473 | 0 | r = rtecp_cipher(card, data, data_len, out, out_len, 0); |
474 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); |
475 | 0 | } |
476 | | |
477 | | static int rtecp_compute_signature(sc_card_t *card, |
478 | | const u8 *data, size_t data_len, u8 *out, size_t out_len) |
479 | 0 | { |
480 | 0 | int r; |
481 | |
|
482 | 0 | if (!card || !card->ctx || !data || !out) |
483 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
484 | | |
485 | 0 | if (card->type == SC_CARD_TYPE_RUTOKEN_LITE |
486 | 0 | || card->type == SC_CARD_TYPE_RUTOKEN_LITE_SC) |
487 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED); |
488 | | |
489 | | /* compute digital signature */ |
490 | 0 | r = rtecp_cipher(card, data, data_len, out, out_len, 1); |
491 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); |
492 | 0 | } |
493 | | |
494 | | static int rtecp_change_reference_data(sc_card_t *card, unsigned int type, |
495 | | int ref_qualifier, const u8 *old, size_t oldlen, |
496 | | const u8 *newref, size_t newlen, int *tries_left) |
497 | 0 | { |
498 | 0 | sc_apdu_t apdu; |
499 | 0 | u8 rsf_length[2], *buf, *buf_end, *p; |
500 | 0 | size_t val_length, buf_length, max_transmit_length, transmits_num; |
501 | 0 | int r; |
502 | |
|
503 | 0 | if (!card || !card->ctx || !newref) |
504 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
505 | | |
506 | 0 | sc_log(card->ctx, |
507 | 0 | "newlen = %"SC_FORMAT_LEN_SIZE_T"u\n", newlen); |
508 | 0 | if (newlen > 0xFFFF) |
509 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); |
510 | 0 | if (type == SC_AC_CHV && old && oldlen != 0) |
511 | 0 | { |
512 | 0 | r = sc_verify(card, type, ref_qualifier, old, oldlen, tries_left); |
513 | 0 | LOG_TEST_RET(card->ctx, r, "Verify old pin failed"); |
514 | 0 | } |
515 | | |
516 | 0 | max_transmit_length = sc_get_max_send_size(card); |
517 | 0 | if (max_transmit_length <= 2) |
518 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); |
519 | | /* |
520 | | * (2 + sizeof(rsf_length) + newlen) - total length of data we need to transfer, |
521 | | * (max_transmit_length - 2) - amount of useful data we can transfer in one transmit (2 bytes for 0xA5 tag) |
522 | | */ |
523 | 0 | transmits_num = (2 + sizeof(rsf_length) + newlen) / (max_transmit_length - 2) + 1; |
524 | | /* buffer length = size of 0x80 TLV + size of RSF-file + (size of Tag and Length)*(number of APDUs) */ |
525 | 0 | buf_length = (2 + sizeof(rsf_length)) + newlen + 2*(transmits_num); |
526 | 0 | p = buf = (u8 *)malloc(buf_length); |
527 | 0 | if (buf == NULL) |
528 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); |
529 | 0 | buf_end = buf + buf_length; |
530 | |
|
531 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, 0x01, ref_qualifier); |
532 | | /* put 0x80 TLV */ |
533 | 0 | rsf_length[0] = (newlen >> 8) & 0xFF; |
534 | 0 | rsf_length[1] = newlen & 0xFF; |
535 | |
|
536 | 0 | if (buf_end - p < (int)(2 + sizeof(rsf_length))) { |
537 | 0 | free(buf); |
538 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); |
539 | 0 | } |
540 | | |
541 | 0 | sc_asn1_put_tag(0x80, rsf_length, sizeof(rsf_length), p, buf_end - p, &p); |
542 | | /* put 0xA5 TLVs (one or more); each transmit must begin with 0xA5 TLV */ |
543 | 0 | while (newlen) |
544 | 0 | { |
545 | 0 | if (buf_end - p < (int)(newlen + 2)) { |
546 | 0 | free(buf); |
547 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); |
548 | 0 | } |
549 | 0 | if ((p - buf) % max_transmit_length + newlen + 2 > max_transmit_length) |
550 | 0 | val_length = max_transmit_length - (p - buf) % max_transmit_length - 2; |
551 | 0 | else |
552 | 0 | val_length = newlen; |
553 | | /* not using sc_asn1_put_tag(...) because rtecp do not support asn1 properly (when val_length > 127) */ |
554 | 0 | *p++ = 0xA5; |
555 | 0 | *p++ = (u8)val_length; |
556 | 0 | if (val_length > newlen) { |
557 | 0 | free(buf); |
558 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); |
559 | 0 | } |
560 | 0 | memcpy(p, newref, val_length); |
561 | 0 | p += val_length; |
562 | 0 | newref += val_length; |
563 | 0 | newlen -= val_length; |
564 | 0 | if (newlen) |
565 | 0 | apdu.flags |= SC_APDU_FLAGS_CHAINING; |
566 | 0 | } |
567 | 0 | apdu.lc = p - buf; |
568 | 0 | apdu.data = buf; |
569 | 0 | apdu.datalen = p - buf; |
570 | |
|
571 | 0 | r = sc_transmit_apdu(card, &apdu); |
572 | 0 | sc_mem_clear(buf, buf_length); |
573 | 0 | free(buf); |
574 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
575 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
576 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); |
577 | 0 | } |
578 | | |
579 | | static int rtecp_reset_retry_counter(sc_card_t *card, unsigned int type, |
580 | | int ref_qualifier, const u8 *puk, size_t puklen, |
581 | | const u8 *newref, size_t newlen) |
582 | 0 | { |
583 | 0 | sc_apdu_t apdu; |
584 | 0 | int r; |
585 | |
|
586 | 0 | (void)type, (void)puk, (void)puklen; /* no warning */ |
587 | |
|
588 | 0 | if (!card || !card->ctx) |
589 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
590 | | |
591 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x2C, 0x03, ref_qualifier); |
592 | 0 | r = sc_transmit_apdu(card, &apdu); |
593 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
594 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
595 | 0 | LOG_TEST_RET(card->ctx, r, "Unblock card failed"); |
596 | | |
597 | 0 | if (newref && newlen) { |
598 | 0 | u8 tmp[2], buf[SC_MAX_APDU_BUFFER_SIZE]; |
599 | 0 | u8 *p = buf; |
600 | |
|
601 | 0 | tmp[0] = (newlen >> 8) & 0xFF; |
602 | 0 | tmp[1] = newlen & 0xFF; |
603 | 0 | sc_asn1_put_tag(0x80, tmp, sizeof(tmp), p, sizeof(buf) - (p - buf), &p); |
604 | 0 | r = sc_asn1_put_tag(0xA5, newref, newlen, p, sizeof(buf) - (p - buf), &p); |
605 | 0 | LOG_TEST_RET(card->ctx, r, "Invalid new PIN length"); |
606 | | |
607 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, 0x01, ref_qualifier); |
608 | 0 | apdu.lc = p - buf; |
609 | 0 | apdu.data = buf; |
610 | 0 | apdu.datalen = p - buf; |
611 | |
|
612 | 0 | r = sc_transmit_apdu(card, &apdu); |
613 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
614 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
615 | 0 | LOG_TEST_RET(card->ctx, r, "Set PIN failed"); |
616 | 0 | } |
617 | | |
618 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); |
619 | 0 | } |
620 | | |
621 | | static int rtecp_create_file(sc_card_t *card, sc_file_t *file) |
622 | 58 | { |
623 | 58 | int r; |
624 | | |
625 | 58 | if (!card || !card->ctx || !file) |
626 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
627 | | |
628 | 58 | if (file->sec_attr_len == 0) |
629 | 58 | { |
630 | 58 | r = set_sec_attr_from_acl(card, file); |
631 | 58 | LOG_TEST_RET(card->ctx, r, "Set sec_attr from ACL failed"); |
632 | 58 | } |
633 | | |
634 | 58 | r = iso_ops->create_file(card, file); |
635 | 58 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); |
636 | 58 | } |
637 | | |
638 | | static int rtecp_list_files(sc_card_t *card, u8 *buf, size_t buflen) |
639 | 0 | { |
640 | 0 | sc_apdu_t apdu; |
641 | 0 | u8 rbuf[SC_MAX_APDU_RESP_SIZE], previd[2]; |
642 | 0 | const u8 *tag; |
643 | 0 | size_t taglen, len = 0; |
644 | 0 | int r; |
645 | |
|
646 | 0 | if (!card || !card->ctx || !buf) |
647 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
648 | | |
649 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xA4, 0, 0); |
650 | 0 | for (;;) |
651 | 0 | { |
652 | 0 | apdu.resp = rbuf; |
653 | 0 | apdu.resplen = sizeof(rbuf); |
654 | 0 | apdu.le = sizeof(rbuf); |
655 | 0 | r = sc_transmit_apdu(card, &apdu); |
656 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
657 | 0 | if (apdu.sw1 == 0x6A && apdu.sw2 == 0x82) |
658 | 0 | break; /* Next file not found */ |
659 | | |
660 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
661 | 0 | LOG_TEST_RET(card->ctx, r, ""); |
662 | | |
663 | 0 | if (apdu.resplen <= 2) |
664 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_WRONG_LENGTH); |
665 | | |
666 | | /* save first file(dir) ID */ |
667 | 0 | tag = sc_asn1_find_tag(card->ctx, apdu.resp + 2, apdu.resplen - 2, |
668 | 0 | 0x83, &taglen); |
669 | 0 | if (!tag || taglen != sizeof(previd)) |
670 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); |
671 | 0 | memcpy(previd, tag, sizeof(previd)); |
672 | |
|
673 | 0 | if (len + sizeof(previd) <= buflen) |
674 | 0 | { |
675 | 0 | memcpy(&buf[len], previd, sizeof(previd)); |
676 | 0 | len += sizeof(previd); |
677 | 0 | } |
678 | |
|
679 | 0 | tag = sc_asn1_find_tag(card->ctx, apdu.resp + 2, apdu.resplen - 2, |
680 | 0 | 0x82, &taglen); |
681 | 0 | if (!tag || taglen != 2) |
682 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); |
683 | 0 | if (tag[0] == 0x38) |
684 | 0 | { |
685 | | /* Select parent DF of the current DF */ |
686 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xA4, 0x03, 0); |
687 | | /* We should set le and resp buf to actually call Get Response for card on T0. */ |
688 | 0 | apdu.resp = rbuf; |
689 | 0 | apdu.resplen = sizeof(rbuf); |
690 | 0 | apdu.le = sizeof(rbuf); |
691 | 0 | r = sc_transmit_apdu(card, &apdu); |
692 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
693 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
694 | 0 | LOG_TEST_RET(card->ctx, r, ""); |
695 | 0 | } |
696 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0, 0x02); |
697 | 0 | apdu.lc = sizeof(previd); |
698 | 0 | apdu.data = previd; |
699 | 0 | apdu.datalen = sizeof(previd); |
700 | 0 | } |
701 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, (int)len); |
702 | 0 | } |
703 | | |
704 | | static int rtecp_card_ctl(sc_card_t *card, unsigned long request, void *data) |
705 | 184 | { |
706 | 184 | sc_apdu_t apdu; |
707 | 184 | u8 buf[512]; |
708 | 184 | sc_rtecp_genkey_data_t *genkey_data = data; |
709 | 184 | sc_serial_number_t *serial = data; |
710 | 184 | int r; |
711 | | |
712 | 184 | const unsigned char rsa_prop[] = {0xA6, 0x06, 0x94, 0x04, 0x01, 0x00, 0x01, 0x00}; |
713 | | |
714 | 184 | if (!card || !card->ctx) |
715 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
716 | | |
717 | 184 | switch (request) |
718 | 184 | { |
719 | 15 | case SC_CARDCTL_RTECP_INIT: |
720 | 15 | sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x8A, 0, 0); |
721 | 15 | apdu.cla = 0x80; |
722 | 15 | break; |
723 | 15 | case SC_CARDCTL_RTECP_INIT_END: |
724 | 15 | sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x84, 0x4E, 0x19); |
725 | 15 | apdu.cla = 0x80; |
726 | 15 | break; |
727 | 0 | case SC_CARDCTL_GET_SERIALNR: |
728 | 0 | if (!serial) |
729 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); |
730 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, 0x01, 0x81); |
731 | 0 | apdu.resp = buf; |
732 | 0 | apdu.resplen = sizeof(buf); |
733 | 0 | apdu.le = 256; |
734 | 0 | serial->len = sizeof(serial->value); |
735 | 0 | break; |
736 | 0 | case SC_CARDCTL_RTECP_GENERATE_KEY: |
737 | 0 | if (!genkey_data) |
738 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); |
739 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x46, 0x80, |
740 | 0 | genkey_data->key_id); |
741 | 0 | apdu.resp = buf; |
742 | 0 | apdu.resplen = sizeof(buf); |
743 | 0 | apdu.le = 256; |
744 | 0 | if (genkey_data->type == SC_ALGORITHM_RSA) { |
745 | 0 | apdu.cse = SC_APDU_CASE_4_SHORT; |
746 | 0 | apdu.data = rsa_prop; |
747 | 0 | apdu.datalen = sizeof(rsa_prop); |
748 | 0 | apdu.lc = sizeof(rsa_prop); |
749 | 0 | } |
750 | 0 | break; |
751 | 154 | case SC_CARDCTL_LIFECYCLE_SET: |
752 | 154 | sc_log(card->ctx, "%s\n", |
753 | 154 | "SC_CARDCTL_LIFECYCLE_SET not supported"); |
754 | | /* no call sc_debug (SC_FUNC_RETURN) */ |
755 | 154 | return SC_ERROR_NOT_SUPPORTED; |
756 | 0 | default: |
757 | 0 | sc_log(card->ctx, |
758 | 0 | "request = 0x%lx\n", request); |
759 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); |
760 | 184 | } |
761 | 30 | r = sc_transmit_apdu(card, &apdu); |
762 | 30 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
763 | 27 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
764 | 27 | if (!r && request == SC_CARDCTL_RTECP_GENERATE_KEY) |
765 | 0 | { |
766 | 0 | if (genkey_data->type == SC_ALGORITHM_RSA && |
767 | 0 | genkey_data->u.rsa.modulus_len >= apdu.resplen && |
768 | 0 | genkey_data->u.rsa.exponent_len >= 3) |
769 | 0 | { |
770 | 0 | memcpy(genkey_data->u.rsa.modulus, apdu.resp, apdu.resplen); |
771 | 0 | genkey_data->u.rsa.modulus_len = apdu.resplen; |
772 | 0 | reverse(genkey_data->u.rsa.modulus, |
773 | 0 | genkey_data->u.rsa.modulus_len); |
774 | 0 | memcpy(genkey_data->u.rsa.exponent, "\x01\x00\x01", 3); |
775 | 0 | genkey_data->u.rsa.exponent_len = 3; |
776 | 0 | } |
777 | 0 | else if (genkey_data->type == SC_ALGORITHM_GOSTR3410 && |
778 | 0 | genkey_data->u.gostr3410.xy_len >= apdu.resplen) |
779 | 0 | { |
780 | 0 | memcpy(genkey_data->u.gostr3410.xy, apdu.resp, apdu.resplen); |
781 | 0 | genkey_data->u.gostr3410.xy_len = apdu.resplen; |
782 | 0 | } |
783 | 0 | else |
784 | 0 | r = SC_ERROR_BUFFER_TOO_SMALL; |
785 | 0 | } |
786 | 27 | else if (!r && request == SC_CARDCTL_GET_SERIALNR) |
787 | 0 | { |
788 | 0 | if (apdu.resplen <= sizeof(serial->value)) |
789 | 0 | { |
790 | 0 | memcpy(serial->value, apdu.resp, apdu.resplen); |
791 | 0 | serial->len = apdu.resplen; |
792 | 0 | } |
793 | 0 | else |
794 | 0 | r = SC_ERROR_BUFFER_TOO_SMALL; |
795 | 0 | } |
796 | 27 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); |
797 | 27 | } |
798 | | |
799 | | static int rtecp_construct_fci(sc_card_t *card, const sc_file_t *file, |
800 | | u8 *out, size_t *outlen) |
801 | 58 | { |
802 | 58 | u8 buf[64], *p = out; |
803 | | |
804 | 58 | if (!card || !card->ctx || !file || !out || !outlen |
805 | 58 | || (*outlen < (size_t)(p - out) + 2)) |
806 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
807 | | |
808 | 58 | *p++ = 0x6F; /* FCI template */ |
809 | 58 | p++; /* for length */ |
810 | | |
811 | | /* 0x80 - Number of data bytes in the file, excluding structural information */ |
812 | 58 | buf[0] = (file->size >> 8) & 0xFF; |
813 | 58 | buf[1] = file->size & 0xFF; |
814 | 58 | sc_asn1_put_tag(0x80, buf, 2, p, *outlen - (p - out), &p); |
815 | | |
816 | | /* 0x82 - File descriptor byte */ |
817 | 58 | if (file->type_attr_len) |
818 | 0 | { |
819 | 0 | if (sizeof(buf) < file->type_attr_len) |
820 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); |
821 | 0 | memcpy(buf, file->type_attr, file->type_attr_len); |
822 | 0 | sc_asn1_put_tag(0x82, buf, file->type_attr_len, |
823 | 0 | p, *outlen - (p - out), &p); |
824 | 0 | } |
825 | 58 | else |
826 | 58 | { |
827 | 58 | switch (file->type) |
828 | 58 | { |
829 | 22 | case SC_FILE_TYPE_WORKING_EF: |
830 | 22 | buf[0] = 0x01; |
831 | 22 | break; |
832 | 30 | case SC_FILE_TYPE_DF: |
833 | 30 | buf[0] = 0x38; |
834 | 30 | break; |
835 | 1 | case SC_FILE_TYPE_INTERNAL_EF: |
836 | 6 | default: |
837 | 6 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); |
838 | 58 | } |
839 | 52 | buf[1] = 0; |
840 | 52 | sc_asn1_put_tag(0x82, buf, 2, p, *outlen - (p - out), &p); |
841 | 52 | } |
842 | | /* 0x83 - File identifier */ |
843 | 52 | buf[0] = (file->id >> 8) & 0xFF; |
844 | 52 | buf[1] = file->id & 0xFF; |
845 | 52 | sc_asn1_put_tag(0x83, buf, 2, p, *outlen - (p - out), &p); |
846 | | |
847 | 52 | if (file->prop_attr_len) |
848 | 2 | { |
849 | 2 | if (sizeof(buf) < file->prop_attr_len) |
850 | 2 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); |
851 | 0 | memcpy(buf, file->prop_attr, file->prop_attr_len); |
852 | 0 | sc_asn1_put_tag(0x85, buf, file->prop_attr_len, |
853 | 0 | p, *outlen - (p - out), &p); |
854 | 0 | } |
855 | 50 | if (file->sec_attr_len) |
856 | 50 | { |
857 | 50 | if (sizeof(buf) < file->sec_attr_len) |
858 | 50 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); |
859 | 50 | memcpy(buf, file->sec_attr, file->sec_attr_len); |
860 | 50 | sc_asn1_put_tag(0x86, buf, file->sec_attr_len, |
861 | 50 | p, *outlen - (p - out), &p); |
862 | 50 | } |
863 | 50 | out[1] = p - out - 2; /* length */ |
864 | 50 | *outlen = p - out; |
865 | 50 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, 0); |
866 | 50 | } |
867 | | |
868 | | struct sc_card_driver * sc_get_rtecp_driver(void) |
869 | 15.8k | { |
870 | 15.8k | if (iso_ops == NULL) |
871 | 1 | iso_ops = sc_get_iso7816_driver()->ops; |
872 | 15.8k | rtecp_ops = *iso_ops; |
873 | | |
874 | 15.8k | rtecp_ops.match_card = rtecp_match_card; |
875 | 15.8k | rtecp_ops.init = rtecp_init; |
876 | | /* read_binary */ |
877 | 15.8k | rtecp_ops.write_binary = NULL; |
878 | | /* update_binary */ |
879 | 15.8k | rtecp_ops.read_record = NULL; |
880 | 15.8k | rtecp_ops.write_record = NULL; |
881 | 15.8k | rtecp_ops.append_record = NULL; |
882 | 15.8k | rtecp_ops.update_record = NULL; |
883 | 15.8k | rtecp_ops.select_file = rtecp_select_file; |
884 | | /* get_response */ |
885 | | /* get_challenge */ |
886 | 15.8k | rtecp_ops.verify = rtecp_verify; |
887 | 15.8k | rtecp_ops.logout = rtecp_logout; |
888 | | /* restore_security_env */ |
889 | 15.8k | rtecp_ops.set_security_env = rtecp_set_security_env; |
890 | 15.8k | rtecp_ops.decipher = rtecp_decipher; |
891 | 15.8k | rtecp_ops.compute_signature = rtecp_compute_signature; |
892 | 15.8k | rtecp_ops.change_reference_data = rtecp_change_reference_data; |
893 | 15.8k | rtecp_ops.reset_retry_counter = rtecp_reset_retry_counter; |
894 | 15.8k | rtecp_ops.create_file = rtecp_create_file; |
895 | | /* delete_file */ |
896 | 15.8k | rtecp_ops.list_files = rtecp_list_files; |
897 | | /* check_sw */ |
898 | 15.8k | rtecp_ops.card_ctl = rtecp_card_ctl; |
899 | | /* process_fci */ |
900 | 15.8k | rtecp_ops.construct_fci = rtecp_construct_fci; |
901 | | rtecp_ops.pin_cmd = NULL; |
902 | 15.8k | return &rtecp_drv; |
903 | 15.8k | } |