/src/opensc/src/libopensc/card-eoi.c
Line | Count | Source |
1 | | /* |
2 | | * Support for the eOI card |
3 | | * |
4 | | * Copyright (C) 2022 Luka Logar <luka.logar@iname.com> |
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 | | #include <stdlib.h> |
22 | | #include <string.h> |
23 | | |
24 | | #ifdef HAVE_CONFIG_H |
25 | | #include "config.h" |
26 | | #endif |
27 | | #include "opensc.h" |
28 | | |
29 | | #if defined(ENABLE_SM) && defined(ENABLE_OPENPACE) |
30 | | |
31 | | #include <openssl/aes.h> |
32 | | #include <openssl/sha.h> |
33 | | #include "internal.h" |
34 | | #include "sm/sm-eac.h" |
35 | | #include "common/compat_strlcpy.h" |
36 | | #include "card-eoi.h" |
37 | | |
38 | | static struct sc_card_operations eoi_ops; |
39 | | |
40 | | static struct { |
41 | | int len; |
42 | | struct sc_object_id oid; |
43 | | } eoi_curves[] = { |
44 | | /* secp384r1 */ |
45 | | {384, {{1, 3, 132, 0, 34, -1}}} |
46 | | }; |
47 | | |
48 | | static char *eoi_model = "ChipDocLite"; |
49 | | |
50 | | /* The description of the driver. */ |
51 | | static struct sc_card_driver eoi_drv = |
52 | | { |
53 | | "eOI (Slovenian eID card)", |
54 | | "eOI", |
55 | | &eoi_ops, |
56 | | NULL, 0, NULL |
57 | | }; |
58 | | |
59 | | static const struct sc_atr_table eoi_atrs[] = { |
60 | | /* Contact interface */ |
61 | | { "3b:d5:18:ff:81:91:fe:1f:c3:80:73:c8:21:10:0a", NULL, NULL, SC_CARD_TYPE_EOI, 0, NULL }, |
62 | | /* Contactless interface */ |
63 | | { "3b:85:80:01:80:73:c8:21:10:0e", NULL, NULL, SC_CARD_TYPE_EOI_CONTACTLESS, 0, NULL }, |
64 | | { NULL, NULL, NULL, 0, 0, NULL } |
65 | | }; |
66 | | |
67 | | /* |
68 | | * CAN is stored encrypted in a file that (looks like) is pointed to by 'Card CAN' PIN object. |
69 | | * eoi_decrypt_can() decrypts CAN from it's encrypted form |
70 | | */ |
71 | | |
72 | | static void rol(u8 *to, const u8 *from) |
73 | 0 | { |
74 | 0 | int i; |
75 | 0 | u8 b = from[0] & 0x80; |
76 | 0 | for (i = 15; i >= 0; i--) { |
77 | 0 | u8 bo = b; |
78 | 0 | b = from[i] & 0x80; |
79 | 0 | to[i] = (from[i] << 1) | (bo ? 1 : 0); |
80 | 0 | if ((i == 15) && bo) |
81 | 0 | to[i] = (to[i] ^ 0x87) | 1; |
82 | 0 | } |
83 | 0 | } |
84 | | |
85 | | static int aes256_ecb_encrypt(const u8 *key, const u8 input[AES_BLOCK_SIZE], u8 output[AES_BLOCK_SIZE]) |
86 | 0 | { |
87 | 0 | EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); |
88 | 0 | int r = 0, pos, len = pos = AES_BLOCK_SIZE; |
89 | 0 | if (!ctx) |
90 | 0 | goto err; |
91 | 0 | if (!EVP_EncryptInit(ctx, EVP_aes_256_ecb(), key, NULL)) |
92 | 0 | goto err; |
93 | | /* Disable padding, otherwise EVP_EncryptFinal() will fail */ |
94 | 0 | if (!EVP_CIPHER_CTX_set_padding(ctx, 0)) |
95 | 0 | goto err; |
96 | 0 | if (!EVP_EncryptUpdate(ctx, output, &pos, input, len)) |
97 | 0 | goto err; |
98 | 0 | len -= pos; |
99 | 0 | if (!EVP_EncryptFinal(ctx, output + pos, &len)) |
100 | 0 | goto err; |
101 | 0 | r = 1; |
102 | 0 | err: |
103 | 0 | if (ctx) |
104 | 0 | EVP_CIPHER_CTX_free(ctx); |
105 | 0 | return r; |
106 | 0 | } |
107 | | |
108 | | static int aes256_ecb_decrypt(const u8 *key, const u8 input[AES_BLOCK_SIZE], u8 output[AES_BLOCK_SIZE]) |
109 | 0 | { |
110 | 0 | EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); |
111 | 0 | int r = 0, pos, len = pos = AES_BLOCK_SIZE; |
112 | 0 | if (!ctx) |
113 | 0 | goto err; |
114 | 0 | if (!EVP_DecryptInit(ctx, EVP_aes_256_ecb(), key, NULL)) |
115 | 0 | goto err; |
116 | | /* Disable padding, otherwise it will fail to decrypt non-padded inputs */ |
117 | 0 | if (!EVP_CIPHER_CTX_set_padding(ctx, 0)) |
118 | 0 | goto err; |
119 | 0 | if (!EVP_DecryptUpdate(ctx, output, &pos, input, len)) |
120 | 0 | goto err; |
121 | 0 | len -= pos; |
122 | 0 | if (!EVP_DecryptFinal(ctx, output + pos, &len)) |
123 | 0 | goto err; |
124 | 0 | r = 1; |
125 | 0 | err: |
126 | 0 | if (ctx) |
127 | 0 | EVP_CIPHER_CTX_free(ctx); |
128 | 0 | return r; |
129 | 0 | } |
130 | | |
131 | | /* |
132 | | * CAN decrypt magic... |
133 | | */ |
134 | | static int get_can_key(const u8 *key, const u8 round, const u8 *input, u8 *output) |
135 | 0 | { |
136 | 0 | size_t i; |
137 | 0 | u8 tmp[3][AES_BLOCK_SIZE]; |
138 | 0 | memset(tmp[0], 0, AES_BLOCK_SIZE); |
139 | 0 | if (!aes256_ecb_encrypt(key, tmp[0], tmp[0])) |
140 | 0 | return 0; |
141 | 0 | rol(tmp[1], tmp[0]); |
142 | 0 | rol(tmp[0], tmp[1]); |
143 | 0 | memset(tmp[1], 0, AES_BLOCK_SIZE); |
144 | 0 | tmp[1][11] = 4; |
145 | 0 | tmp[1][13] = 1; |
146 | 0 | tmp[1][15] = round; |
147 | 0 | if (!aes256_ecb_encrypt(key, tmp[1], tmp[2])) |
148 | 0 | return 0; |
149 | 0 | memset(tmp[1], 0, AES_BLOCK_SIZE); |
150 | 0 | memcpy(tmp[1], &input[AES_BLOCK_SIZE], 8); |
151 | 0 | tmp[1][8] = 0x80; |
152 | 0 | for (i = 0; i < AES_BLOCK_SIZE; i++) |
153 | 0 | tmp[0][i] = tmp[0][i] ^ tmp[1][i] ^ tmp[2][i]; |
154 | 0 | if (!aes256_ecb_encrypt(key, tmp[0], output)) |
155 | 0 | return 0; |
156 | 0 | return 1; |
157 | 0 | } |
158 | | |
159 | | #define AES256_KEY_LEN 32 |
160 | | |
161 | 0 | static int eoi_decrypt_can(struct sc_pkcs15_u8 *enc_can, char *can) { |
162 | | /* Magic key that is used to decrypt CAN */ |
163 | 0 | const u8 magic_key[AES256_KEY_LEN] = {0xC8, 0x12, 0x0F, 0xD8, 0x21, 0x20, 0x1F, 0x77, 0xF1, 0x83, 0x9D, 0xD8, 0x86, 0xB0, 0x5C, 0xF2, 0x4F, 0x7E, 0x52, 0x66, 0xE5, 0x87, 0x89, 0x2B, 0xF4, 0xC5, 0xE5, 0x4C, 0x54, 0xA1, 0x55, 0x30}; |
164 | 0 | u8 can_key[AES256_KEY_LEN] = { 0 }; |
165 | |
|
166 | 0 | if (!can || !enc_can || !enc_can->value || enc_can->len != 24) |
167 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
168 | | |
169 | 0 | if (!get_can_key(magic_key, 0x01, enc_can->value, &can_key[0])) |
170 | 0 | return SC_ERROR_INTERNAL; |
171 | 0 | if (!get_can_key(magic_key, 0x02, enc_can->value, &can_key[AES_BLOCK_SIZE])) |
172 | 0 | return SC_ERROR_INTERNAL; |
173 | | |
174 | 0 | if (!aes256_ecb_decrypt(can_key, enc_can->value, (u8 *)can)) |
175 | 0 | return SC_ERROR_INTERNAL; |
176 | 0 | can[AES_BLOCK_SIZE - 1] = 0; |
177 | |
|
178 | 0 | return SC_SUCCESS; |
179 | 0 | } |
180 | | |
181 | | static int eoi_sm_open(struct sc_card *card) |
182 | 0 | { |
183 | 0 | int r; |
184 | 0 | struct eoi_privdata *privdata = (struct eoi_privdata *)card->drv_data; |
185 | 0 | struct establish_pace_channel_input pace_input; |
186 | 0 | struct establish_pace_channel_output pace_output; |
187 | |
|
188 | 0 | if (!privdata) |
189 | 0 | return SC_ERROR_INTERNAL; |
190 | | |
191 | 0 | if (!privdata->can[0]) { |
192 | | /* If no CAN is specified in conf, try to decrypt it from enc_can file */ |
193 | 0 | r = eoi_decrypt_can(&privdata->enc_can, privdata->can); |
194 | 0 | sc_log_openssl(card->ctx); |
195 | 0 | LOG_TEST_RET(card->ctx, r, "Cannot decrypt CAN"); |
196 | 0 | } |
197 | | /* CAN should be 6 chars long */ |
198 | 0 | if (strlen(privdata->can) != 6) |
199 | 0 | return SC_ERROR_DECRYPT_FAILED; |
200 | | |
201 | 0 | memset(&pace_input, 0, sizeof pace_input); |
202 | 0 | memset(&pace_output, 0, sizeof pace_output); |
203 | |
|
204 | 0 | pace_input.pin_id = PACE_PIN_ID_CAN; |
205 | 0 | pace_input.pin = (u8 *)privdata->can; |
206 | 0 | pace_input.pin_length = strlen(privdata->can); |
207 | | |
208 | | /* EF.CardAccess can only be read from MF */ |
209 | 0 | r = sc_select_file(card, sc_get_mf_path(), NULL); |
210 | 0 | LOG_TEST_RET(card->ctx, r, "sc_select_file failed"); |
211 | | |
212 | 0 | r = perform_pace(card, pace_input, &pace_output, EAC_TR_VERSION_2_02); |
213 | 0 | LOG_TEST_RET(card->ctx, r, "Error verifying CAN"); |
214 | | |
215 | 0 | return SC_SUCCESS; |
216 | 0 | } |
217 | | |
218 | | static int eoi_get_data(sc_card_t *card, u8 data_id, u8 *buf, size_t len) |
219 | 0 | { |
220 | 0 | int r; |
221 | 0 | sc_apdu_t apdu; |
222 | |
|
223 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, 0x01, data_id); |
224 | 0 | apdu.resp = buf; |
225 | 0 | apdu.resplen = len; |
226 | 0 | apdu.le = len; |
227 | |
|
228 | 0 | r = sc_transmit_apdu(card, &apdu); |
229 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
230 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
231 | 0 | return r; |
232 | 0 | } |
233 | | |
234 | | #define ATR_MATCH 1 |
235 | | |
236 | 2.42k | static int eoi_match_card(sc_card_t* card) { |
237 | 2.42k | LOG_FUNC_CALLED(card->ctx); |
238 | 2.42k | if (_sc_match_atr(card, eoi_atrs, &card->type) >= 0) { |
239 | 0 | sc_log(card->ctx, "ATR recognized as Slovenian eID card"); |
240 | 0 | LOG_FUNC_RETURN(card->ctx, ATR_MATCH); |
241 | 0 | } |
242 | 2.42k | LOG_FUNC_RETURN(card->ctx, !ATR_MATCH); |
243 | 2.42k | } |
244 | | |
245 | 0 | static int eoi_init(sc_card_t* card) { |
246 | 0 | struct eoi_privdata *privdata = (struct eoi_privdata *)card->drv_data; |
247 | 0 | u8 version[6]; |
248 | 0 | size_t i, j; |
249 | 0 | scconf_block **found_blocks, *block; |
250 | 0 | int r; |
251 | 0 | char *can; |
252 | |
|
253 | 0 | LOG_FUNC_CALLED(card->ctx); |
254 | |
|
255 | 0 | if (eoi_get_data(card, 0x16, version, sizeof(version)) != SC_SUCCESS) |
256 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_WRONG_CARD); |
257 | | |
258 | 0 | if (privdata) |
259 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); |
260 | 0 | privdata = sc_mem_secure_alloc(sizeof(struct eoi_privdata)); |
261 | 0 | if (!privdata) |
262 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); |
263 | | /* sc_mem_secure_alloc()-ed memory may not be zeroized */ |
264 | 0 | memset(privdata, 0, sizeof(struct eoi_privdata)); |
265 | 0 | card->drv_data = privdata; |
266 | |
|
267 | 0 | sprintf(privdata->version, "%X%02X.%02X%02X", version[0], version[1], version[2], version[3]); |
268 | 0 | sc_log(card->ctx, "App version: %s", privdata->version); |
269 | |
|
270 | 0 | memset(&card->sm_ctx, 0, sizeof card->sm_ctx); |
271 | 0 | card->sm_ctx.ops.open = eoi_sm_open; |
272 | |
|
273 | 0 | card->max_send_size = SC_MAX_APDU_DATA_SIZE; |
274 | 0 | card->max_recv_size = SC_MAX_APDU_RESP_SIZE; |
275 | |
|
276 | 0 | for (i = 0; i < sizeof eoi_curves / sizeof * eoi_curves; ++i) { |
277 | 0 | r = _sc_card_add_ec_alg(card, eoi_curves[i].len, SC_ALGORITHM_ECDSA_RAW | SC_ALGORITHM_ECDSA_HASH_NONE, 0, &eoi_curves[i].oid); |
278 | 0 | LOG_TEST_GOTO_ERR(card->ctx, r, "Add EC alg failed"); |
279 | 0 | } |
280 | | |
281 | 0 | can = getenv("EOI_CAN"); |
282 | 0 | if (can) |
283 | 0 | strlcpy(privdata->can, can, sizeof(privdata->can)); |
284 | 0 | for (i = 0; card->ctx->conf_blocks[i]; i++) { |
285 | 0 | found_blocks = scconf_find_blocks(card->ctx->conf, card->ctx->conf_blocks[i], |
286 | 0 | "card_driver", "eoi"); |
287 | 0 | if (!found_blocks) |
288 | 0 | continue; |
289 | | |
290 | 0 | for (j = 0, block = found_blocks[j]; block; j++, block = found_blocks[j]) { |
291 | 0 | if (!privdata->can[0]) { |
292 | 0 | const char *can = scconf_get_str(block, "can", NULL); |
293 | 0 | if (can) |
294 | 0 | strlcpy(privdata->can, can, sizeof(privdata->can)); |
295 | 0 | } |
296 | 0 | } |
297 | 0 | free(found_blocks); |
298 | 0 | } |
299 | |
|
300 | 0 | LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); |
301 | | |
302 | 0 | err: |
303 | 0 | if (privdata) { |
304 | 0 | sc_mem_clear(privdata, sizeof(struct eoi_privdata)); |
305 | 0 | sc_mem_secure_free(privdata, sizeof(struct eoi_privdata)); |
306 | 0 | } |
307 | 0 | card->drv_data = NULL; |
308 | |
|
309 | 0 | LOG_FUNC_RETURN(card->ctx, r); |
310 | 0 | } |
311 | | |
312 | | static int eoi_finish(sc_card_t* card) |
313 | 0 | { |
314 | 0 | struct eoi_privdata *privdata = (struct eoi_privdata *)card->drv_data; |
315 | |
|
316 | 0 | LOG_FUNC_CALLED(card->ctx); |
317 | |
|
318 | 0 | if (privdata) { |
319 | 0 | sc_mem_clear(privdata, sizeof(struct eoi_privdata)); |
320 | 0 | sc_mem_secure_free(privdata, sizeof(struct eoi_privdata)); |
321 | 0 | } |
322 | |
|
323 | 0 | card->drv_data = NULL; |
324 | |
|
325 | 0 | LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); |
326 | 0 | } |
327 | | |
328 | | static int eoi_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out) |
329 | 0 | { |
330 | 0 | struct eoi_privdata *privdata = (struct eoi_privdata *)card->drv_data; |
331 | 0 | int i; |
332 | |
|
333 | 0 | LOG_FUNC_CALLED(card->ctx); |
334 | |
|
335 | 0 | if (!privdata) |
336 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); |
337 | | |
338 | 0 | for (i = 0; i < MAX_OBJECTS && privdata->pin_paths[i]; i++) { |
339 | 0 | if (privdata->pin_paths[i] && sc_compare_path(privdata->pin_paths[i], in_path)) { |
340 | 0 | LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); |
341 | 0 | } |
342 | 0 | } |
343 | | |
344 | 0 | LOG_FUNC_RETURN(card->ctx, sc_get_iso7816_driver()->ops->select_file(card, in_path, file_out)); |
345 | 0 | } |
346 | | |
347 | | static int eoi_logout(struct sc_card *card) |
348 | 0 | { |
349 | 0 | struct eoi_privdata *privdata = (struct eoi_privdata *)card->drv_data; |
350 | 0 | struct sc_apdu apdu; |
351 | 0 | u8 buf[256]; |
352 | 0 | int r = SC_SUCCESS; |
353 | |
|
354 | 0 | LOG_FUNC_CALLED(card->ctx); |
355 | |
|
356 | 0 | if (!privdata) |
357 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); |
358 | | |
359 | 0 | iso_sm_close(card); |
360 | 0 | card->sm_ctx.sm_mode = SM_MODE_NONE; |
361 | |
|
362 | 0 | if (card->reader->flags & SC_READER_ENABLE_ESCAPE) { |
363 | | /* |
364 | | * Get the UID of the ISO 14443 A card. (see PCSC Part 3) |
365 | | * The "official" PKCS#11 does it and we do the same. |
366 | | */ |
367 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, 0x00, 0x00); |
368 | 0 | apdu.cla = 0xFF; |
369 | 0 | apdu.resp = buf; |
370 | 0 | apdu.resplen = 256; |
371 | 0 | apdu.lc = 0; |
372 | 0 | apdu.le = 256; |
373 | |
|
374 | 0 | r = sc_transmit_apdu(card, &apdu); |
375 | 0 | } |
376 | |
|
377 | 0 | LOG_FUNC_RETURN(card->ctx, r); |
378 | 0 | } |
379 | | |
380 | | static int eoi_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) |
381 | 0 | { |
382 | 0 | int r; |
383 | |
|
384 | 0 | LOG_FUNC_CALLED(card->ctx); |
385 | |
|
386 | 0 | if (data->cmd == SC_PIN_CMD_VERIFY && card->sm_ctx.sm_mode == SM_MODE_NONE) { |
387 | | /* Establish SM before any PIN VERIFY command */ |
388 | 0 | r = eoi_sm_open(card); |
389 | 0 | if (r != SC_SUCCESS) |
390 | 0 | LOG_FUNC_RETURN(card->ctx, r); |
391 | 0 | } |
392 | | |
393 | 0 | if (data->cmd == SC_PIN_CMD_UNBLOCK) { |
394 | 0 | int pin_reference = data->pin_reference; |
395 | 0 | size_t pin2_len = data->pin2.len; |
396 | | /* Verify PUK, establish SM if necessary */ |
397 | 0 | data->cmd = SC_PIN_CMD_VERIFY; |
398 | 0 | data->pin_reference = data->puk_reference; |
399 | 0 | r = eoi_pin_cmd(card, data, tries_left); |
400 | 0 | if (r != SC_SUCCESS) |
401 | 0 | LOG_FUNC_RETURN(card->ctx, r); |
402 | | /* RESET RETRY COUNTER */ |
403 | 0 | data->cmd = SC_PIN_CMD_UNBLOCK; |
404 | 0 | data->pin_reference = 0x80|pin_reference; |
405 | 0 | data->pin1.len = 0; |
406 | 0 | data->pin2.len = 0; |
407 | 0 | r = sc_get_iso7816_driver()->ops->pin_cmd(card, data, tries_left); |
408 | 0 | if (r != SC_SUCCESS) |
409 | 0 | LOG_FUNC_RETURN(card->ctx, r); |
410 | | /* Continue as CHANGE PIN */ |
411 | 0 | data->cmd = SC_PIN_CMD_CHANGE; |
412 | 0 | data->pin2.len = pin2_len; |
413 | 0 | } |
414 | | |
415 | | /* CHANGE PIN command does not send the old PIN as it should already be verified */ |
416 | 0 | if (data->cmd == SC_PIN_CMD_CHANGE) |
417 | 0 | data->pin1.len = 0; |
418 | |
|
419 | 0 | LOG_FUNC_RETURN(card->ctx, sc_get_iso7816_driver()->ops->pin_cmd(card, data, tries_left)); |
420 | 0 | } |
421 | | |
422 | | static int |
423 | | eoi_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) |
424 | 0 | { |
425 | 0 | struct sc_card_driver *iso_driver = NULL; |
426 | 0 | int r = SC_ERROR_NOT_SUPPORTED; |
427 | |
|
428 | 0 | LOG_FUNC_CALLED(card->ctx); |
429 | 0 | switch (cmd) { |
430 | 0 | case SC_CARDCTL_GET_MODEL: |
431 | 0 | if (!ptr) { |
432 | 0 | r = SC_ERROR_INVALID_ARGUMENTS; |
433 | 0 | } else { |
434 | 0 | *(char **)ptr = eoi_model; |
435 | 0 | r = SC_SUCCESS; |
436 | 0 | } |
437 | 0 | break; |
438 | 0 | default: |
439 | 0 | iso_driver = sc_get_iso7816_driver(); |
440 | 0 | if (iso_driver->ops->card_ctl != NULL) { |
441 | 0 | r = sc_get_iso7816_driver()->ops->card_ctl(card, cmd, ptr); |
442 | 0 | } |
443 | 0 | } |
444 | 0 | LOG_FUNC_RETURN(card->ctx, r); |
445 | 0 | } |
446 | | |
447 | 0 | #define ALREADY_PROCESSED 0x80000000 |
448 | | |
449 | | static int eoi_set_security_env(struct sc_card *card, const struct sc_security_env *env, int se_num) |
450 | 0 | { |
451 | 0 | struct eoi_privdata *privdata = (struct eoi_privdata *)card->drv_data; |
452 | 0 | struct sc_apdu apdu; |
453 | 0 | u8 sbuf[4]; |
454 | 0 | int i, r, locked = 0; |
455 | |
|
456 | 0 | LOG_FUNC_CALLED(card->ctx); |
457 | |
|
458 | 0 | if (!privdata) |
459 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); |
460 | | |
461 | 0 | if (!card || !env) |
462 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); |
463 | | |
464 | | /* We don't know yet which hash is used. So just store the security_env data and return */ |
465 | 0 | if (!(env->algorithm_flags & ALREADY_PROCESSED)) { |
466 | 0 | privdata->key_len = BYTES4BITS(env->algorithm_ref); |
467 | 0 | memcpy(&privdata->sec_env, env, sizeof(struct sc_security_env)); |
468 | 0 | privdata->se_num = se_num; |
469 | 0 | LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); |
470 | 0 | } |
471 | | |
472 | 0 | if (env->operation != SC_SEC_OPERATION_SIGN) |
473 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); |
474 | 0 | if (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT)) |
475 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); |
476 | 0 | if (env->key_ref_len != 1) |
477 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); |
478 | 0 | if (env->algorithm != SC_ALGORITHM_EC) |
479 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); |
480 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x81, 0xB6); |
481 | 0 | sbuf[0] = 0x91; |
482 | 0 | sbuf[1] = 0x02; |
483 | 0 | if (env->algorithm_flags & SC_ALGORITHM_ECDSA_HASH_SHA1) |
484 | 0 | sbuf[2] = 0x11; |
485 | 0 | else if (env->algorithm_flags & SC_ALGORITHM_ECDSA_HASH_SHA256) |
486 | 0 | sbuf[2] = 0x21; |
487 | 0 | else if (env->algorithm_flags & (SC_ALGORITHM_ECDSA_RAW|SC_ALGORITHM_ECDSA_HASH_NONE)) |
488 | 0 | sbuf[2] = 0x22; |
489 | 0 | else |
490 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); |
491 | 0 | for (i = 0; i < MAX_OBJECTS && privdata->prkey_mappings[i][1]; i++) { |
492 | 0 | if (privdata->prkey_mappings[i][0] == env->key_ref[0]) |
493 | 0 | break; |
494 | 0 | } |
495 | 0 | if (i == MAX_OBJECTS) |
496 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); |
497 | 0 | sbuf[3] = privdata->prkey_mappings[i][1]; |
498 | 0 | apdu.lc = 4; |
499 | 0 | apdu.datalen = 4; |
500 | 0 | apdu.data = sbuf; |
501 | 0 | if (se_num > 0) { |
502 | 0 | r = sc_lock(card); |
503 | 0 | LOG_TEST_RET(card->ctx, r, "sc_lock() failed"); |
504 | 0 | locked = 1; |
505 | 0 | } |
506 | 0 | if (apdu.datalen) { |
507 | 0 | r = sc_transmit_apdu(card, &apdu); |
508 | 0 | if (r) { |
509 | 0 | sc_log(card->ctx, "%s: APDU transmit failed", sc_strerror(r)); |
510 | 0 | goto err; |
511 | 0 | } |
512 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
513 | 0 | if (r) { |
514 | 0 | sc_log(card->ctx, "%s: Card returned error", sc_strerror(r)); |
515 | 0 | goto err; |
516 | 0 | } |
517 | 0 | } |
518 | 0 | if (se_num <= 0) { |
519 | 0 | r = SC_SUCCESS; |
520 | 0 | goto err; |
521 | 0 | } |
522 | 0 | sc_unlock(card); |
523 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
524 | | |
525 | 0 | LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); |
526 | 0 | err: |
527 | 0 | if (locked) |
528 | 0 | sc_unlock(card); |
529 | 0 | LOG_FUNC_RETURN(card->ctx, r); |
530 | 0 | } |
531 | | |
532 | | static int eoi_compute_signature(struct sc_card *card, const u8 * data, size_t data_len, u8 *out, size_t outlen) |
533 | 0 | { |
534 | 0 | struct eoi_privdata *privdata = (struct eoi_privdata *)card->drv_data; |
535 | 0 | int r; |
536 | |
|
537 | 0 | LOG_FUNC_CALLED(card->ctx); |
538 | |
|
539 | 0 | if (!privdata) |
540 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); |
541 | | |
542 | | /* |
543 | | * Guess the correct mode. If the size is less than the full-key-len, it must be a hash then |
544 | | */ |
545 | 0 | if (privdata->key_len != data_len) { |
546 | 0 | switch (data_len) { |
547 | 0 | case SHA_DIGEST_LENGTH: |
548 | 0 | privdata->sec_env.algorithm_flags = SC_ALGORITHM_ECDSA_HASH_SHA1; |
549 | 0 | break; |
550 | 0 | case SHA256_DIGEST_LENGTH: |
551 | 0 | privdata->sec_env.algorithm_flags = SC_ALGORITHM_ECDSA_HASH_SHA256; |
552 | 0 | break; |
553 | 0 | } |
554 | 0 | } |
555 | | /* Now we know which hash is used */ |
556 | 0 | privdata->sec_env.algorithm_flags |= ALREADY_PROCESSED; |
557 | | |
558 | | /* Perform the true set_security_env */ |
559 | 0 | r = eoi_set_security_env(card, &privdata->sec_env, privdata->se_num); |
560 | 0 | LOG_TEST_RET(card->ctx, r, "set_security_env failed"); |
561 | | |
562 | 0 | LOG_FUNC_RETURN(card->ctx, sc_get_iso7816_driver()->ops->compute_signature(card, data, data_len, out, outlen)); |
563 | 0 | } |
564 | | |
565 | | struct sc_card_driver *sc_get_eoi_driver(void) |
566 | 18.4k | { |
567 | 18.4k | eoi_ops = *sc_get_iso7816_driver()->ops; |
568 | | |
569 | 18.4k | eoi_ops.match_card = eoi_match_card; |
570 | 18.4k | eoi_ops.init = eoi_init; |
571 | 18.4k | eoi_ops.finish = eoi_finish; |
572 | 18.4k | eoi_ops.select_file = eoi_select_file; |
573 | 18.4k | eoi_ops.logout = eoi_logout; |
574 | 18.4k | eoi_ops.pin_cmd = eoi_pin_cmd; |
575 | 18.4k | eoi_ops.card_ctl = eoi_card_ctl; |
576 | 18.4k | eoi_ops.set_security_env = eoi_set_security_env; |
577 | 18.4k | eoi_ops.compute_signature = eoi_compute_signature; |
578 | | |
579 | 18.4k | return &eoi_drv; |
580 | 18.4k | } |
581 | | |
582 | | #else |
583 | | |
584 | | struct sc_card_driver* sc_get_eoi_driver(void) { |
585 | | return NULL; |
586 | | } |
587 | | |
588 | | #endif |