/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 | 8.44k | { |
74 | 8.44k | int i = -1; |
75 | 8.44k | i = _sc_match_atr(card, rtecp_atrs, &card->type); |
76 | 8.44k | if (i >= 0) { |
77 | 77 | card->name = rtecp_atrs[i].name; |
78 | 77 | LOG_FUNC_RETURN(card->ctx, 1); |
79 | 77 | } |
80 | 8.36k | LOG_FUNC_RETURN(card->ctx, 0); |
81 | 8.36k | } |
82 | | |
83 | | static int rtecp_init(sc_card_t *card) |
84 | 77 | { |
85 | 77 | sc_algorithm_info_t info; |
86 | 77 | unsigned long flags; |
87 | | |
88 | 77 | if (!card || !card->ctx) |
89 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
90 | | |
91 | 77 | card->cla = 0; |
92 | | |
93 | 77 | if (card->type == SC_CARD_TYPE_RUTOKEN_LITE |
94 | 76 | || card->type == SC_CARD_TYPE_RUTOKEN_LITE_SC) |
95 | 2 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); |
96 | | |
97 | 75 | card->caps |= SC_CARD_CAP_RNG; |
98 | | |
99 | 75 | flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_ONBOARD_KEY_GEN |
100 | 75 | | SC_ALGORITHM_RSA_PAD_NONE | SC_ALGORITHM_RSA_HASH_NONE; |
101 | | |
102 | 75 | _sc_card_add_rsa_alg(card, 256, flags, 0); |
103 | 75 | _sc_card_add_rsa_alg(card, 512, flags, 0); |
104 | 75 | _sc_card_add_rsa_alg(card, 768, flags, 0); |
105 | 75 | _sc_card_add_rsa_alg(card, 1024, flags, 0); |
106 | 75 | _sc_card_add_rsa_alg(card, 1280, flags, 0); |
107 | 75 | _sc_card_add_rsa_alg(card, 1536, flags, 0); |
108 | 75 | _sc_card_add_rsa_alg(card, 1792, flags, 0); |
109 | 75 | _sc_card_add_rsa_alg(card, 2048, flags, 0); |
110 | 75 | _sc_card_add_rsa_alg(card, 4096, flags, 0); |
111 | | |
112 | 75 | memset(&info, 0, sizeof(info)); |
113 | 75 | info.algorithm = SC_ALGORITHM_GOSTR3410; |
114 | 75 | info.key_length = 256; |
115 | 75 | info.flags = SC_ALGORITHM_GOSTR3410_RAW | SC_ALGORITHM_ONBOARD_KEY_GEN |
116 | 75 | | SC_ALGORITHM_GOSTR3410_HASH_NONE; |
117 | 75 | _sc_card_add_algorithm(card, &info); |
118 | | |
119 | 75 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); |
120 | 75 | } |
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 | 169 | { |
140 | 169 | if (attr == 0xFF) |
141 | 37 | return SC_AC_NEVER; |
142 | 132 | else if (attr == 0) |
143 | 22 | return SC_AC_NONE; |
144 | 110 | else if (attr & 0x03) |
145 | 78 | return SC_AC_CHV; |
146 | 32 | else |
147 | 32 | return SC_AC_UNKNOWN; |
148 | 169 | } |
149 | | |
150 | | static unsigned long sec_attr_to_key_ref(unsigned int attr) |
151 | 169 | { |
152 | 169 | if (attr == 1 || attr == 2) |
153 | 45 | return attr; |
154 | 124 | return 0; |
155 | 169 | } |
156 | | |
157 | | static unsigned int to_sec_attr(unsigned int method, unsigned int key_ref) |
158 | 0 | { |
159 | 0 | if (method == SC_AC_NEVER || method == SC_AC_NONE) |
160 | 0 | return method; |
161 | 0 | if (method == SC_AC_CHV && (key_ref == 1 || key_ref == 2)) |
162 | 0 | return key_ref; |
163 | 0 | return 0; |
164 | 0 | } |
165 | | |
166 | | static void set_acl_from_sec_attr(sc_card_t *card, sc_file_t *file) |
167 | 91 | { |
168 | 91 | unsigned int method; |
169 | 91 | unsigned long key_ref; |
170 | | |
171 | 91 | if (!card || !card->ctx || !file || !file->sec_attr |
172 | 91 | || 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 | 91 | sc_file_add_acl_entry(file, SC_AC_OP_SELECT, SC_AC_NONE, SC_AC_KEY_REF_NONE); |
179 | 91 | if (file->sec_attr[0] & 0x40) /* if AccessMode.6 */ |
180 | 58 | { |
181 | 58 | method = sec_attr_to_method(file->sec_attr[1 + 6]); |
182 | 58 | key_ref = sec_attr_to_key_ref(file->sec_attr[1 + 6]); |
183 | 58 | sc_log(card->ctx, |
184 | 58 | "SC_AC_OP_DELETE %i %lu\n", |
185 | 58 | (int)method, key_ref); |
186 | 58 | sc_file_add_acl_entry(file, SC_AC_OP_DELETE, method, key_ref); |
187 | 58 | } |
188 | 91 | if (file->sec_attr[0] & 0x01) /* if AccessMode.0 */ |
189 | 71 | { |
190 | 71 | method = sec_attr_to_method(file->sec_attr[1 + 0]); |
191 | 71 | key_ref = sec_attr_to_key_ref(file->sec_attr[1 + 0]); |
192 | 71 | sc_log(card->ctx, |
193 | 71 | (file->type == SC_FILE_TYPE_DF) ? |
194 | 71 | "SC_AC_OP_CREATE %i %lu\n" |
195 | 71 | : "SC_AC_OP_READ %i %lu\n", |
196 | 71 | (int)method, key_ref); |
197 | 71 | sc_file_add_acl_entry(file, (file->type == SC_FILE_TYPE_DF) ? |
198 | 67 | SC_AC_OP_CREATE : SC_AC_OP_READ, method, key_ref); |
199 | 71 | } |
200 | 91 | if (file->type == SC_FILE_TYPE_DF) |
201 | 4 | { |
202 | 4 | sc_file_add_acl_entry(file, SC_AC_OP_LIST_FILES, |
203 | 4 | SC_AC_NONE, SC_AC_KEY_REF_NONE); |
204 | 4 | } |
205 | 87 | else |
206 | 87 | if (file->sec_attr[0] & 0x02) /* if AccessMode.1 */ |
207 | 40 | { |
208 | 40 | method = sec_attr_to_method(file->sec_attr[1 + 1]); |
209 | 40 | key_ref = sec_attr_to_key_ref(file->sec_attr[1 + 1]); |
210 | 40 | sc_log(card->ctx, |
211 | 40 | "SC_AC_OP_UPDATE %i %lu\n", |
212 | 40 | (int)method, key_ref); |
213 | 40 | sc_file_add_acl_entry(file, SC_AC_OP_UPDATE, method, key_ref); |
214 | 40 | sc_log(card->ctx, |
215 | 40 | "SC_AC_OP_WRITE %i %lu\n", |
216 | 40 | (int)method, key_ref); |
217 | 40 | sc_file_add_acl_entry(file, SC_AC_OP_WRITE, method, key_ref); |
218 | 40 | } |
219 | 91 | } |
220 | | |
221 | | static int set_sec_attr_from_acl(sc_card_t *card, sc_file_t *file) |
222 | 0 | { |
223 | 0 | const sc_acl_entry_t *entry; |
224 | 0 | u8 sec_attr[SC_RTECP_SEC_ATTR_SIZE] = { 0 }; |
225 | 0 | int r; |
226 | |
|
227 | 0 | if (!card || !card->ctx || !file |
228 | 0 | || file->sec_attr || file->sec_attr_len != 0) |
229 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
230 | | |
231 | 0 | entry = sc_file_get_acl_entry(file, SC_AC_OP_DELETE); |
232 | 0 | if (entry) |
233 | 0 | { |
234 | 0 | sec_attr[0] |= 0x40; |
235 | 0 | sec_attr[1 + 6] = to_sec_attr(entry->method, entry->key_ref); |
236 | 0 | } |
237 | 0 | if (file->type == SC_FILE_TYPE_DF) |
238 | 0 | { |
239 | 0 | entry = sc_file_get_acl_entry(file, SC_AC_OP_CREATE); |
240 | 0 | if (entry) |
241 | 0 | { |
242 | | /* ATTR: Create DF/EF file */ |
243 | 0 | sec_attr[0] |= 0x01; |
244 | 0 | sec_attr[1 + 0] = to_sec_attr(entry->method, entry->key_ref); |
245 | | /* ATTR: Create Internal EF (RSF) file */ |
246 | 0 | sec_attr[0] |= 0x02; |
247 | 0 | sec_attr[1 + 1] = to_sec_attr(entry->method, entry->key_ref); |
248 | 0 | } |
249 | 0 | } |
250 | 0 | else |
251 | 0 | { |
252 | 0 | entry = sc_file_get_acl_entry(file, SC_AC_OP_READ); |
253 | 0 | if (entry) |
254 | 0 | { |
255 | 0 | sec_attr[0] |= 0x01; |
256 | 0 | sec_attr[1 + 0] = to_sec_attr(entry->method, entry->key_ref); |
257 | 0 | } |
258 | 0 | entry = sc_file_get_acl_entry(file, SC_AC_OP_WRITE); |
259 | 0 | if (entry) |
260 | 0 | { |
261 | 0 | sec_attr[0] |= 0x02; |
262 | 0 | sec_attr[1 + 1] = to_sec_attr(entry->method, entry->key_ref); |
263 | 0 | } |
264 | 0 | entry = sc_file_get_acl_entry(file, SC_AC_OP_UPDATE); |
265 | 0 | if (entry) |
266 | 0 | { |
267 | | /* rewrite if sec_attr[1 + 1] already set */ |
268 | 0 | sec_attr[0] |= 0x02; |
269 | 0 | sec_attr[1 + 1] = to_sec_attr(entry->method, entry->key_ref); |
270 | 0 | } |
271 | 0 | } |
272 | | /* FIXME: Find the best solution */ |
273 | 0 | if (file->path.len == 2 && !memcmp(file->path.value, "\x3F\x00", 2)) |
274 | 0 | { |
275 | | /* ATTR: Put data */ |
276 | 0 | sec_attr[0] |= 0x04; |
277 | 0 | sec_attr[1 + 2] = 1; /* so-pin reference */ |
278 | 0 | } |
279 | 0 | r = sc_file_set_sec_attr(file, sec_attr, sizeof(sec_attr)); |
280 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); |
281 | 0 | } |
282 | | |
283 | | static int rtecp_select_file(sc_card_t *card, |
284 | | const sc_path_t *in_path, sc_file_t **file_out) |
285 | 371 | { |
286 | 371 | sc_file_t *file = NULL; |
287 | 371 | int r = SC_SUCCESS; |
288 | | |
289 | 371 | if (!card || !card->ctx || !in_path) |
290 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
291 | | |
292 | 371 | SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); |
293 | | |
294 | 371 | switch (in_path->type) |
295 | 371 | { |
296 | 118 | case SC_PATH_TYPE_DF_NAME: |
297 | 118 | case SC_PATH_TYPE_FROM_CURRENT: |
298 | 118 | case SC_PATH_TYPE_PARENT: |
299 | 118 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED); |
300 | 371 | } |
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 | 253 | r = iso_ops->select_file(card, in_path, &file); |
305 | 253 | if (r != SC_SUCCESS) |
306 | 147 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); |
307 | | |
308 | 106 | if (file->sec_attr && file->sec_attr_len == SC_RTECP_SEC_ATTR_SIZE) |
309 | 91 | set_acl_from_sec_attr(card, file); |
310 | 15 | else |
311 | 15 | { |
312 | 15 | sc_file_free(file); |
313 | 15 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_UNKNOWN_DATA_RECEIVED); |
314 | 15 | } |
315 | | |
316 | 91 | if (file_out) |
317 | 77 | *file_out = file; |
318 | 14 | else |
319 | 14 | sc_file_free(file); |
320 | | |
321 | 91 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); |
322 | 91 | } |
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 | for (i = 0; i < apdu.resplen; ++i) |
441 | 0 | out[i] = buf_out[apdu.resplen - 1 - i]; |
442 | 0 | r = (i > 0) ? (int)i : SC_ERROR_INTERNAL; |
443 | 0 | } |
444 | 0 | else |
445 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
446 | 0 | } |
447 | 0 | if (!sign) |
448 | 0 | sc_mem_clear(buf_out, out_len + 2); |
449 | |
|
450 | 0 | free(buf_out); |
451 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); |
452 | |
|
453 | 0 | } |
454 | | |
455 | | static int rtecp_decipher(sc_card_t *card, |
456 | | const u8 *data, size_t data_len, u8 *out, size_t out_len) |
457 | 0 | { |
458 | 0 | int r; |
459 | |
|
460 | 0 | if (!card || !card->ctx || !data || !out) |
461 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
462 | | |
463 | 0 | if (card->type == SC_CARD_TYPE_RUTOKEN_LITE |
464 | 0 | || card->type == SC_CARD_TYPE_RUTOKEN_LITE_SC) |
465 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED); |
466 | | |
467 | | /* decipher */ |
468 | 0 | r = rtecp_cipher(card, data, data_len, out, out_len, 0); |
469 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); |
470 | 0 | } |
471 | | |
472 | | static int rtecp_compute_signature(sc_card_t *card, |
473 | | const u8 *data, size_t data_len, u8 *out, size_t out_len) |
474 | 0 | { |
475 | 0 | int r; |
476 | |
|
477 | 0 | if (!card || !card->ctx || !data || !out) |
478 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
479 | | |
480 | 0 | if (card->type == SC_CARD_TYPE_RUTOKEN_LITE |
481 | 0 | || card->type == SC_CARD_TYPE_RUTOKEN_LITE_SC) |
482 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED); |
483 | | |
484 | | /* compute digital signature */ |
485 | 0 | r = rtecp_cipher(card, data, data_len, out, out_len, 1); |
486 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); |
487 | 0 | } |
488 | | |
489 | | static int rtecp_change_reference_data(sc_card_t *card, unsigned int type, |
490 | | int ref_qualifier, const u8 *old, size_t oldlen, |
491 | | const u8 *newref, size_t newlen, int *tries_left) |
492 | 0 | { |
493 | 0 | sc_apdu_t apdu; |
494 | 0 | u8 rsf_length[2], *buf, *buf_end, *p; |
495 | 0 | size_t val_length, buf_length, max_transmit_length, transmits_num; |
496 | 0 | int r; |
497 | |
|
498 | 0 | if (!card || !card->ctx || !newref) |
499 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
500 | | |
501 | 0 | sc_log(card->ctx, |
502 | 0 | "newlen = %"SC_FORMAT_LEN_SIZE_T"u\n", newlen); |
503 | 0 | if (newlen > 0xFFFF) |
504 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); |
505 | 0 | if (type == SC_AC_CHV && old && oldlen != 0) |
506 | 0 | { |
507 | 0 | r = sc_verify(card, type, ref_qualifier, old, oldlen, tries_left); |
508 | 0 | LOG_TEST_RET(card->ctx, r, "Verify old pin failed"); |
509 | 0 | } |
510 | | |
511 | 0 | max_transmit_length = sc_get_max_send_size(card); |
512 | 0 | if (max_transmit_length <= 2) |
513 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); |
514 | | /* |
515 | | * (2 + sizeof(rsf_length) + newlen) - total length of data we need to transfer, |
516 | | * (max_transmit_length - 2) - amount of useful data we can transfer in one transmit (2 bytes for 0xA5 tag) |
517 | | */ |
518 | 0 | transmits_num = (2 + sizeof(rsf_length) + newlen) / (max_transmit_length - 2) + 1; |
519 | | /* buffer length = size of 0x80 TLV + size of RSF-file + (size of Tag and Length)*(number of APDUs) */ |
520 | 0 | buf_length = (2 + sizeof(rsf_length)) + newlen + 2*(transmits_num); |
521 | 0 | p = buf = (u8 *)malloc(buf_length); |
522 | 0 | if (buf == NULL) |
523 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); |
524 | 0 | buf_end = buf + buf_length; |
525 | |
|
526 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, 0x01, ref_qualifier); |
527 | | /* put 0x80 TLV */ |
528 | 0 | rsf_length[0] = (newlen >> 8) & 0xFF; |
529 | 0 | rsf_length[1] = newlen & 0xFF; |
530 | |
|
531 | 0 | if (buf_end - p < (int)(2 + sizeof(rsf_length))) { |
532 | 0 | free(buf); |
533 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); |
534 | 0 | } |
535 | | |
536 | 0 | sc_asn1_put_tag(0x80, rsf_length, sizeof(rsf_length), p, buf_end - p, &p); |
537 | | /* put 0xA5 TLVs (one or more); each transmit must begin with 0xA5 TLV */ |
538 | 0 | while (newlen) |
539 | 0 | { |
540 | 0 | if (buf_end - p < (int)(newlen + 2)) { |
541 | 0 | free(buf); |
542 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); |
543 | 0 | } |
544 | 0 | if ((p - buf) % max_transmit_length + newlen + 2 > max_transmit_length) |
545 | 0 | val_length = max_transmit_length - (p - buf) % max_transmit_length - 2; |
546 | 0 | else |
547 | 0 | val_length = newlen; |
548 | | /* not using sc_asn1_put_tag(...) because rtecp do not support asn1 properly (when val_length > 127) */ |
549 | 0 | *p++ = 0xA5; |
550 | 0 | *p++ = (u8)val_length; |
551 | 0 | if (val_length > newlen) { |
552 | 0 | free(buf); |
553 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); |
554 | 0 | } |
555 | 0 | memcpy(p, newref, val_length); |
556 | 0 | p += val_length; |
557 | 0 | newref += val_length; |
558 | 0 | newlen -= val_length; |
559 | 0 | if (newlen) |
560 | 0 | apdu.flags |= SC_APDU_FLAGS_CHAINING; |
561 | 0 | } |
562 | 0 | apdu.lc = p - buf; |
563 | 0 | apdu.data = buf; |
564 | 0 | apdu.datalen = p - buf; |
565 | |
|
566 | 0 | r = sc_transmit_apdu(card, &apdu); |
567 | 0 | sc_mem_clear(buf, buf_length); |
568 | 0 | free(buf); |
569 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
570 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
571 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); |
572 | 0 | } |
573 | | |
574 | | static int rtecp_reset_retry_counter(sc_card_t *card, unsigned int type, |
575 | | int ref_qualifier, const u8 *puk, size_t puklen, |
576 | | const u8 *newref, size_t newlen) |
577 | 0 | { |
578 | 0 | sc_apdu_t apdu; |
579 | 0 | int r; |
580 | |
|
581 | 0 | (void)type, (void)puk, (void)puklen; /* no warning */ |
582 | |
|
583 | 0 | if (!card || !card->ctx) |
584 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
585 | | |
586 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x2C, 0x03, ref_qualifier); |
587 | 0 | r = sc_transmit_apdu(card, &apdu); |
588 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
589 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
590 | 0 | LOG_TEST_RET(card->ctx, r, "Unblock card failed"); |
591 | | |
592 | 0 | if (newref && newlen) { |
593 | 0 | u8 tmp[2], buf[SC_MAX_APDU_BUFFER_SIZE]; |
594 | 0 | u8 *p = buf; |
595 | |
|
596 | 0 | tmp[0] = (newlen >> 8) & 0xFF; |
597 | 0 | tmp[1] = newlen & 0xFF; |
598 | 0 | sc_asn1_put_tag(0x80, tmp, sizeof(tmp), p, sizeof(buf) - (p - buf), &p); |
599 | 0 | r = sc_asn1_put_tag(0xA5, newref, newlen, p, sizeof(buf) - (p - buf), &p); |
600 | 0 | LOG_TEST_RET(card->ctx, r, "Invalid new PIN length"); |
601 | | |
602 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, 0x01, ref_qualifier); |
603 | 0 | apdu.lc = p - buf; |
604 | 0 | apdu.data = buf; |
605 | 0 | apdu.datalen = p - buf; |
606 | |
|
607 | 0 | r = sc_transmit_apdu(card, &apdu); |
608 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
609 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
610 | 0 | LOG_TEST_RET(card->ctx, r, "Set PIN failed"); |
611 | 0 | } |
612 | | |
613 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); |
614 | 0 | } |
615 | | |
616 | | static int rtecp_create_file(sc_card_t *card, sc_file_t *file) |
617 | 0 | { |
618 | 0 | int r; |
619 | |
|
620 | 0 | if (!card || !card->ctx || !file) |
621 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
622 | | |
623 | 0 | if (file->sec_attr_len == 0) |
624 | 0 | { |
625 | 0 | r = set_sec_attr_from_acl(card, file); |
626 | 0 | LOG_TEST_RET(card->ctx, r, "Set sec_attr from ACL failed"); |
627 | 0 | } |
628 | | |
629 | 0 | r = iso_ops->create_file(card, file); |
630 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); |
631 | 0 | } |
632 | | |
633 | | static int rtecp_list_files(sc_card_t *card, u8 *buf, size_t buflen) |
634 | 0 | { |
635 | 0 | sc_apdu_t apdu; |
636 | 0 | u8 rbuf[SC_MAX_APDU_RESP_SIZE], previd[2]; |
637 | 0 | const u8 *tag; |
638 | 0 | size_t taglen, len = 0; |
639 | 0 | int r; |
640 | |
|
641 | 0 | if (!card || !card->ctx || !buf) |
642 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
643 | | |
644 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xA4, 0, 0); |
645 | 0 | for (;;) |
646 | 0 | { |
647 | 0 | apdu.resp = rbuf; |
648 | 0 | apdu.resplen = sizeof(rbuf); |
649 | 0 | apdu.le = sizeof(rbuf); |
650 | 0 | r = sc_transmit_apdu(card, &apdu); |
651 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
652 | 0 | if (apdu.sw1 == 0x6A && apdu.sw2 == 0x82) |
653 | 0 | break; /* Next file not found */ |
654 | | |
655 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
656 | 0 | LOG_TEST_RET(card->ctx, r, ""); |
657 | | |
658 | 0 | if (apdu.resplen <= 2) |
659 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_WRONG_LENGTH); |
660 | | |
661 | | /* save first file(dir) ID */ |
662 | 0 | tag = sc_asn1_find_tag(card->ctx, apdu.resp + 2, apdu.resplen - 2, |
663 | 0 | 0x83, &taglen); |
664 | 0 | if (!tag || taglen != sizeof(previd)) |
665 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); |
666 | 0 | memcpy(previd, tag, sizeof(previd)); |
667 | |
|
668 | 0 | if (len + sizeof(previd) <= buflen) |
669 | 0 | { |
670 | 0 | memcpy(&buf[len], previd, sizeof(previd)); |
671 | 0 | len += sizeof(previd); |
672 | 0 | } |
673 | |
|
674 | 0 | tag = sc_asn1_find_tag(card->ctx, apdu.resp + 2, apdu.resplen - 2, |
675 | 0 | 0x82, &taglen); |
676 | 0 | if (!tag || taglen != 2) |
677 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); |
678 | 0 | if (tag[0] == 0x38) |
679 | 0 | { |
680 | | /* Select parent DF of the current DF */ |
681 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xA4, 0x03, 0); |
682 | | /* We should set le and resp buf to actually call Get Response for card on T0. */ |
683 | 0 | apdu.resp = rbuf; |
684 | 0 | apdu.resplen = sizeof(rbuf); |
685 | 0 | apdu.le = sizeof(rbuf); |
686 | 0 | r = sc_transmit_apdu(card, &apdu); |
687 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
688 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
689 | 0 | LOG_TEST_RET(card->ctx, r, ""); |
690 | 0 | } |
691 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0, 0x02); |
692 | 0 | apdu.lc = sizeof(previd); |
693 | 0 | apdu.data = previd; |
694 | 0 | apdu.datalen = sizeof(previd); |
695 | 0 | } |
696 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, (int)len); |
697 | 0 | } |
698 | | |
699 | | static int rtecp_card_ctl(sc_card_t *card, unsigned long request, void *data) |
700 | 11 | { |
701 | 11 | sc_apdu_t apdu; |
702 | 11 | u8 buf[512]; |
703 | 11 | sc_rtecp_genkey_data_t *genkey_data = data; |
704 | 11 | sc_serial_number_t *serial = data; |
705 | 11 | int r; |
706 | | |
707 | 11 | const unsigned char rsa_prop[] = {0xA6, 0x06, 0x94, 0x04, 0x01, 0x00, 0x01, 0x00}; |
708 | | |
709 | 11 | if (!card || !card->ctx) |
710 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
711 | | |
712 | 11 | switch (request) |
713 | 11 | { |
714 | 0 | case SC_CARDCTL_RTECP_INIT: |
715 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x8A, 0, 0); |
716 | 0 | apdu.cla = 0x80; |
717 | 0 | break; |
718 | 0 | case SC_CARDCTL_RTECP_INIT_END: |
719 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x84, 0x4E, 0x19); |
720 | 0 | apdu.cla = 0x80; |
721 | 0 | break; |
722 | 11 | case SC_CARDCTL_GET_SERIALNR: |
723 | 11 | if (!serial) |
724 | 11 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); |
725 | 11 | sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, 0x01, 0x81); |
726 | 11 | apdu.resp = buf; |
727 | 11 | apdu.resplen = sizeof(buf); |
728 | 11 | apdu.le = 256; |
729 | 11 | serial->len = sizeof(serial->value); |
730 | 11 | break; |
731 | 0 | case SC_CARDCTL_RTECP_GENERATE_KEY: |
732 | 0 | if (!genkey_data) |
733 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); |
734 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x46, 0x80, |
735 | 0 | genkey_data->key_id); |
736 | 0 | apdu.resp = buf; |
737 | 0 | apdu.resplen = sizeof(buf); |
738 | 0 | apdu.le = 256; |
739 | 0 | if (genkey_data->type == SC_ALGORITHM_RSA) { |
740 | 0 | apdu.cse = SC_APDU_CASE_4_SHORT; |
741 | 0 | apdu.data = rsa_prop; |
742 | 0 | apdu.datalen = sizeof(rsa_prop); |
743 | 0 | apdu.lc = sizeof(rsa_prop); |
744 | 0 | } |
745 | 0 | break; |
746 | 0 | case SC_CARDCTL_LIFECYCLE_SET: |
747 | 0 | sc_log(card->ctx, "%s\n", |
748 | 0 | "SC_CARDCTL_LIFECYCLE_SET not supported"); |
749 | | /* no call sc_debug (SC_FUNC_RETURN) */ |
750 | 0 | return SC_ERROR_NOT_SUPPORTED; |
751 | 0 | default: |
752 | 0 | sc_log(card->ctx, |
753 | 0 | "request = 0x%lx\n", request); |
754 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); |
755 | 11 | } |
756 | 11 | r = sc_transmit_apdu(card, &apdu); |
757 | 11 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
758 | 10 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
759 | 10 | if (!r && request == SC_CARDCTL_RTECP_GENERATE_KEY) |
760 | 0 | { |
761 | 0 | if (genkey_data->type == SC_ALGORITHM_RSA && |
762 | 0 | genkey_data->u.rsa.modulus_len >= apdu.resplen && |
763 | 0 | genkey_data->u.rsa.exponent_len >= 3) |
764 | 0 | { |
765 | 0 | memcpy(genkey_data->u.rsa.modulus, apdu.resp, apdu.resplen); |
766 | 0 | genkey_data->u.rsa.modulus_len = apdu.resplen; |
767 | 0 | reverse(genkey_data->u.rsa.modulus, |
768 | 0 | genkey_data->u.rsa.modulus_len); |
769 | 0 | memcpy(genkey_data->u.rsa.exponent, "\x01\x00\x01", 3); |
770 | 0 | genkey_data->u.rsa.exponent_len = 3; |
771 | 0 | } |
772 | 0 | else if (genkey_data->type == SC_ALGORITHM_GOSTR3410 && |
773 | 0 | genkey_data->u.gostr3410.xy_len >= apdu.resplen) |
774 | 0 | { |
775 | 0 | memcpy(genkey_data->u.gostr3410.xy, apdu.resp, apdu.resplen); |
776 | 0 | genkey_data->u.gostr3410.xy_len = apdu.resplen; |
777 | 0 | } |
778 | 0 | else |
779 | 0 | r = SC_ERROR_BUFFER_TOO_SMALL; |
780 | 0 | } |
781 | 10 | else if (!r && request == SC_CARDCTL_GET_SERIALNR) |
782 | 8 | { |
783 | 8 | if (apdu.resplen <= sizeof(serial->value)) |
784 | 7 | { |
785 | 7 | memcpy(serial->value, apdu.resp, apdu.resplen); |
786 | 7 | serial->len = apdu.resplen; |
787 | 7 | } |
788 | 1 | else |
789 | 1 | r = SC_ERROR_BUFFER_TOO_SMALL; |
790 | 8 | } |
791 | 10 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); |
792 | 10 | } |
793 | | |
794 | | static int rtecp_construct_fci(sc_card_t *card, const sc_file_t *file, |
795 | | u8 *out, size_t *outlen) |
796 | 0 | { |
797 | 0 | u8 buf[64], *p = out; |
798 | |
|
799 | 0 | if (!card || !card->ctx || !file || !out || !outlen |
800 | 0 | || (*outlen < (size_t)(p - out) + 2)) |
801 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
802 | | |
803 | 0 | *p++ = 0x6F; /* FCI template */ |
804 | 0 | p++; /* for length */ |
805 | | |
806 | | /* 0x80 - Number of data bytes in the file, excluding structural information */ |
807 | 0 | buf[0] = (file->size >> 8) & 0xFF; |
808 | 0 | buf[1] = file->size & 0xFF; |
809 | 0 | sc_asn1_put_tag(0x80, buf, 2, p, *outlen - (p - out), &p); |
810 | | |
811 | | /* 0x82 - File descriptor byte */ |
812 | 0 | if (file->type_attr_len) |
813 | 0 | { |
814 | 0 | if (sizeof(buf) < file->type_attr_len) |
815 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); |
816 | 0 | memcpy(buf, file->type_attr, file->type_attr_len); |
817 | 0 | sc_asn1_put_tag(0x82, buf, file->type_attr_len, |
818 | 0 | p, *outlen - (p - out), &p); |
819 | 0 | } |
820 | 0 | else |
821 | 0 | { |
822 | 0 | switch (file->type) |
823 | 0 | { |
824 | 0 | case SC_FILE_TYPE_WORKING_EF: |
825 | 0 | buf[0] = 0x01; |
826 | 0 | break; |
827 | 0 | case SC_FILE_TYPE_DF: |
828 | 0 | buf[0] = 0x38; |
829 | 0 | break; |
830 | 0 | case SC_FILE_TYPE_INTERNAL_EF: |
831 | 0 | default: |
832 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); |
833 | 0 | } |
834 | 0 | buf[1] = 0; |
835 | 0 | sc_asn1_put_tag(0x82, buf, 2, p, *outlen - (p - out), &p); |
836 | 0 | } |
837 | | /* 0x83 - File identifier */ |
838 | 0 | buf[0] = (file->id >> 8) & 0xFF; |
839 | 0 | buf[1] = file->id & 0xFF; |
840 | 0 | sc_asn1_put_tag(0x83, buf, 2, p, *outlen - (p - out), &p); |
841 | |
|
842 | 0 | if (file->prop_attr_len) |
843 | 0 | { |
844 | 0 | if (sizeof(buf) < file->prop_attr_len) |
845 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); |
846 | 0 | memcpy(buf, file->prop_attr, file->prop_attr_len); |
847 | 0 | sc_asn1_put_tag(0x85, buf, file->prop_attr_len, |
848 | 0 | p, *outlen - (p - out), &p); |
849 | 0 | } |
850 | 0 | if (file->sec_attr_len) |
851 | 0 | { |
852 | 0 | if (sizeof(buf) < file->sec_attr_len) |
853 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); |
854 | 0 | memcpy(buf, file->sec_attr, file->sec_attr_len); |
855 | 0 | sc_asn1_put_tag(0x86, buf, file->sec_attr_len, |
856 | 0 | p, *outlen - (p - out), &p); |
857 | 0 | } |
858 | 0 | out[1] = p - out - 2; /* length */ |
859 | 0 | *outlen = p - out; |
860 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, 0); |
861 | 0 | } |
862 | | |
863 | | struct sc_card_driver * sc_get_rtecp_driver(void) |
864 | 15.5k | { |
865 | 15.5k | if (iso_ops == NULL) |
866 | 1 | iso_ops = sc_get_iso7816_driver()->ops; |
867 | 15.5k | rtecp_ops = *iso_ops; |
868 | | |
869 | 15.5k | rtecp_ops.match_card = rtecp_match_card; |
870 | 15.5k | rtecp_ops.init = rtecp_init; |
871 | | /* read_binary */ |
872 | 15.5k | rtecp_ops.write_binary = NULL; |
873 | | /* update_binary */ |
874 | 15.5k | rtecp_ops.read_record = NULL; |
875 | 15.5k | rtecp_ops.write_record = NULL; |
876 | 15.5k | rtecp_ops.append_record = NULL; |
877 | 15.5k | rtecp_ops.update_record = NULL; |
878 | 15.5k | rtecp_ops.select_file = rtecp_select_file; |
879 | | /* get_response */ |
880 | | /* get_challenge */ |
881 | 15.5k | rtecp_ops.verify = rtecp_verify; |
882 | 15.5k | rtecp_ops.logout = rtecp_logout; |
883 | | /* restore_security_env */ |
884 | 15.5k | rtecp_ops.set_security_env = rtecp_set_security_env; |
885 | 15.5k | rtecp_ops.decipher = rtecp_decipher; |
886 | 15.5k | rtecp_ops.compute_signature = rtecp_compute_signature; |
887 | 15.5k | rtecp_ops.change_reference_data = rtecp_change_reference_data; |
888 | 15.5k | rtecp_ops.reset_retry_counter = rtecp_reset_retry_counter; |
889 | 15.5k | rtecp_ops.create_file = rtecp_create_file; |
890 | | /* delete_file */ |
891 | 15.5k | rtecp_ops.list_files = rtecp_list_files; |
892 | | /* check_sw */ |
893 | 15.5k | rtecp_ops.card_ctl = rtecp_card_ctl; |
894 | | /* process_fci */ |
895 | 15.5k | rtecp_ops.construct_fci = rtecp_construct_fci; |
896 | | rtecp_ops.pin_cmd = NULL; |
897 | 15.5k | return &rtecp_drv; |
898 | 15.5k | } |