/src/opensc/src/libopensc/card-skeid.c
Line | Count | Source |
1 | | /* |
2 | | * card-skeid.c: Support for (CardOS based) cards issued as identity documents in Slovakia |
3 | | * |
4 | | * Copyright (C) 2022 Juraj Ĺ arinay <juraj@sarinay.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 | | * based on card-cardos.c |
21 | | */ |
22 | | |
23 | | #ifdef HAVE_CONFIG_H |
24 | | #include "config.h" |
25 | | #endif |
26 | | |
27 | | #include <string.h> |
28 | | #include "internal.h" |
29 | | |
30 | 167 | #define SKEID_KNOWN_URL_LEN 46 |
31 | | |
32 | | static const struct sc_card_operations *iso_ops = NULL; |
33 | | |
34 | | static struct sc_card_operations skeid_ops; |
35 | | static struct sc_card_driver skeid_drv = { |
36 | | "Slovak eID card", |
37 | | "skeid", |
38 | | &skeid_ops, |
39 | | NULL, 0, NULL |
40 | | }; |
41 | | |
42 | | static const struct sc_atr_table skeid_atrs[] = { |
43 | | /* Slovak eID v3 - CardOS 5.4 |
44 | | * |
45 | | * The ATR was intentionally omitted from minidriver_registration[] within win32/customactions.cpp |
46 | | * as it is identical to that of CardOS v5.4 and therefore already included. |
47 | | * Any new ATR may need an entry in minidriver_registration[]. */ |
48 | | {"3b:d2:18:00:81:31:fe:58:c9:04:11", NULL, NULL, SC_CARD_TYPE_SKEID_V3, 0, NULL}, |
49 | | {NULL, NULL, NULL, 0, 0, NULL} |
50 | | }; |
51 | | |
52 | | static int skeid_known_url(sc_card_t * card) |
53 | 90 | { |
54 | 90 | const struct sc_aid skeid_aid_eid = {{0xE8, 0x07, 0x04, 0x00, 0x7F, 0x00, 0x07, 0x03, 0x02}, 9}; |
55 | 90 | const char *known_url = "\x80\x01\x00\x5F\x50\x28http://www.minv.sk/cif/cif-sk-eid-v3.xml"; |
56 | 90 | u8 buf[SKEID_KNOWN_URL_LEN]; |
57 | | |
58 | 90 | sc_path_t url_path; |
59 | | |
60 | 90 | int r = SC_ERROR_WRONG_CARD; |
61 | | |
62 | 90 | sc_path_set(&url_path, SC_PATH_TYPE_DF_NAME, skeid_aid_eid.value, skeid_aid_eid.len, 0, 0); |
63 | | |
64 | 90 | if (sc_select_file(card, &url_path, NULL) == SC_SUCCESS |
65 | 31 | && sc_get_data(card, 0x7F62, buf, SKEID_KNOWN_URL_LEN) == SKEID_KNOWN_URL_LEN |
66 | 15 | && !memcmp(buf, known_url, SKEID_KNOWN_URL_LEN)) |
67 | 8 | r = SC_SUCCESS; |
68 | | |
69 | 90 | return r; |
70 | 90 | } |
71 | | |
72 | | static int skeid_match_card(sc_card_t *card) |
73 | 14.1k | { |
74 | 14.1k | if (_sc_match_atr(card, skeid_atrs, &card->type) < 0 || skeid_known_url(card) != SC_SUCCESS) |
75 | 14.1k | return 0; |
76 | | |
77 | 8 | sc_log(card->ctx, "Slovak eID card v3 (CardOS 5.4)"); |
78 | | |
79 | 8 | return 1; |
80 | 14.1k | } |
81 | | |
82 | | static int skeid_get_serialnr(sc_card_t *card) |
83 | 8 | { |
84 | 8 | int r; |
85 | 8 | sc_apdu_t apdu; |
86 | 8 | u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; |
87 | | |
88 | 8 | sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x81); |
89 | 8 | apdu.resp = rbuf; |
90 | 8 | apdu.resplen = sizeof(rbuf); |
91 | 8 | apdu.le = 256; |
92 | 8 | r = sc_transmit_apdu(card, &apdu); |
93 | 8 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
94 | 7 | if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) |
95 | 4 | return SC_ERROR_INTERNAL; |
96 | 3 | if (apdu.resplen == 8) { |
97 | | /* cache serial number */ |
98 | 1 | memcpy(card->serialnr.value, rbuf, 8); |
99 | 1 | card->serialnr.len = 8; |
100 | 2 | } else { |
101 | 2 | sc_log(card->ctx, "unexpected response to GET DATA serial number"); |
102 | 2 | return SC_ERROR_INTERNAL; |
103 | 2 | } |
104 | 1 | return SC_SUCCESS; |
105 | 3 | } |
106 | | |
107 | | static int skeid_init(sc_card_t *card) |
108 | 8 | { |
109 | 8 | const unsigned long flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE; |
110 | 8 | const size_t data_field_length = 437; |
111 | 8 | int r; |
112 | | |
113 | 8 | SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); |
114 | | |
115 | 8 | card->name = "Slovak eID (CardOS)"; |
116 | 8 | card->type = SC_CARD_TYPE_SKEID_V3; |
117 | 8 | card->cla = 0x00; |
118 | | |
119 | 8 | r = skeid_get_serialnr(card); |
120 | 8 | LOG_TEST_RET(card->ctx, r, "Error reading serial number."); |
121 | | |
122 | 1 | card->caps |= SC_CARD_CAP_APDU_EXT | SC_CARD_CAP_ISO7816_PIN_INFO; |
123 | | |
124 | 1 | card->max_send_size = data_field_length - 6; |
125 | | #ifdef _WIN32 |
126 | | /* see card-cardos.c */ |
127 | | if (card->reader->max_send_size == 255 && card->reader->max_recv_size == 256) { |
128 | | sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "resetting reader to use data_field_length"); |
129 | | card->reader->max_send_size = data_field_length - 6; |
130 | | card->reader->max_recv_size = data_field_length - 3; |
131 | | } |
132 | | #endif |
133 | | |
134 | 1 | card->max_send_size = sc_get_max_send_size(card); /* see card-cardos.c */ |
135 | 1 | card->max_recv_size = data_field_length - 2; |
136 | 1 | card->max_recv_size = sc_get_max_recv_size(card); |
137 | | |
138 | 1 | r = _sc_card_add_rsa_alg(card, 3072, flags, 0); |
139 | | |
140 | 1 | LOG_FUNC_RETURN(card->ctx, r); |
141 | 1 | } |
142 | | |
143 | | static int skeid_set_security_env(sc_card_t *card, |
144 | | const sc_security_env_t *env, |
145 | | int se_num) |
146 | 0 | { |
147 | 0 | int key_id; |
148 | 0 | int r; |
149 | |
|
150 | 0 | assert(card != NULL && env != NULL); |
151 | |
|
152 | 0 | if (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT) || env->key_ref_len != 1) { |
153 | 0 | sc_log(card->ctx, "No or invalid key reference"); |
154 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
155 | 0 | } |
156 | | |
157 | | /* here we follow the behaviour of the proprietary driver accompanying the card |
158 | | * where security operations are preceded by MSE RESTORE rather than MSE SET |
159 | | */ |
160 | 0 | key_id = env->key_ref[0]; |
161 | 0 | r = sc_restore_security_env(card, key_id); |
162 | |
|
163 | 0 | return r; |
164 | 0 | } |
165 | | |
166 | | static int skeid_logout(sc_card_t *card) |
167 | 0 | { |
168 | 0 | int r; |
169 | 0 | sc_path_t path; |
170 | |
|
171 | 0 | sc_format_path("3F00", &path); |
172 | 0 | r = sc_select_file(card, &path, NULL); |
173 | 0 | return r; |
174 | |
|
175 | 0 | } |
176 | | |
177 | | struct sc_card_driver * sc_get_skeid_driver(void) |
178 | 14.6k | { |
179 | 14.6k | if (iso_ops == NULL) iso_ops = sc_get_iso7816_driver()->ops; |
180 | 14.6k | skeid_ops = *iso_ops; |
181 | 14.6k | skeid_ops.match_card = skeid_match_card; |
182 | 14.6k | skeid_ops.init = skeid_init; |
183 | 14.6k | skeid_ops.set_security_env = skeid_set_security_env; |
184 | 14.6k | skeid_ops.logout = skeid_logout; |
185 | 14.6k | return &skeid_drv; |
186 | 14.6k | } |