/src/opensc/src/libopensc/pkcs15-starcos-esign.c
Line | Count | Source (jump to first uncovered line) |
1 | | /** |
2 | | * PKCS15 emulation layer for Giesecke & Devrient StarCOS 3.x cards |
3 | | * with eSign application |
4 | | * |
5 | | * Copyright (C) 2022, jozsefd |
6 | | * |
7 | | * This library is free software; you can redistribute it and/or |
8 | | * modify it under the terms of the GNU Lesser General Public |
9 | | * License as published by the Free Software Foundation; either |
10 | | * version 2.1 of the License, or (at your option) any later version. |
11 | | * |
12 | | * This library is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | | * Lesser General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU Lesser General Public |
18 | | * License along with this library; if not, write to the Free Software |
19 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | | */ |
21 | | |
22 | | #ifdef HAVE_CONFIG_H |
23 | | #include <config.h> |
24 | | #endif |
25 | | |
26 | | #include "common/compat_strlcpy.h" |
27 | | #include "internal.h" |
28 | | #include "log.h" |
29 | | #include "pkcs15.h" |
30 | | #include "cards.h" |
31 | | |
32 | | #include <stdlib.h> |
33 | | #include <string.h> |
34 | | |
35 | | /* compile time option: define ENABLE_ESIGN_ISSUER_CONTAINERS to enable containers holding the issuer certificates */ |
36 | | |
37 | | static const char name_Card[] = "ESIGN"; |
38 | | static const char name_Vendor[] = "Giesecke & Devrient"; |
39 | | static const char name_ESign[] = "ESIGN"; |
40 | | static const unsigned char aid_ESIGN[] = {0xA0, 0x00, 0x00, 0x02, 0x45, 0x53, 0x69, 0x67, 0x6E}; |
41 | | |
42 | | typedef struct cdata_st { |
43 | | const char *label; |
44 | | int authority; |
45 | | const char *path; |
46 | | const char *id; |
47 | | int obj_flags; |
48 | | } cdata, *pcdata; |
49 | | |
50 | | typedef struct pdata_st { |
51 | | const char *id; |
52 | | const char *label; |
53 | | const char *path; |
54 | | int ref; |
55 | | int type; |
56 | | unsigned int maxlen; |
57 | | unsigned int minlen; |
58 | | unsigned int storedlen; |
59 | | int flags; |
60 | | int tries_left; |
61 | | int max_tries; |
62 | | const char pad_char; |
63 | | int obj_flags; |
64 | | } pindata, *ppindata; |
65 | | |
66 | | typedef struct prdata_st { |
67 | | const char *id; |
68 | | const char *label; |
69 | | unsigned int modulus_len; |
70 | | int usage; |
71 | | const char *path; |
72 | | int ref; |
73 | | const char *auth_id; |
74 | | int obj_flags; |
75 | | } prdata, *pprdata; |
76 | | |
77 | | typedef struct container_st { |
78 | | const char *id; |
79 | | const pcdata certdata; |
80 | | const ppindata pindata; |
81 | | const pprdata prdata; |
82 | | } container; |
83 | | |
84 | | #define USAGE_NONREP SC_PKCS15_PRKEY_USAGE_NONREPUDIATION |
85 | 0 | #define USAGE_KE SC_PKCS15_PRKEY_USAGE_ENCRYPT | \ |
86 | 0 | SC_PKCS15_PRKEY_USAGE_DECRYPT | \ |
87 | 0 | SC_PKCS15_PRKEY_USAGE_WRAP | \ |
88 | 0 | SC_PKCS15_PRKEY_USAGE_UNWRAP |
89 | 0 | #define USAGE_AUT SC_PKCS15_PRKEY_USAGE_ENCRYPT | \ |
90 | 0 | SC_PKCS15_PRKEY_USAGE_DECRYPT | \ |
91 | 0 | SC_PKCS15_PRKEY_USAGE_WRAP | \ |
92 | 0 | SC_PKCS15_PRKEY_USAGE_UNWRAP | \ |
93 | 0 | SC_PKCS15_PRKEY_USAGE_SIGN |
94 | 0 | #define USER_PIN SC_PKCS15_PIN_FLAG_INITIALIZED | \ |
95 | 0 | SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | \ |
96 | 0 | SC_PKCS15_PIN_TYPE_FLAGS_PIN_GLOBAL | \ |
97 | 0 | SC_PKCS15_PIN_AUTH_TYPE_PIN |
98 | | |
99 | | static int |
100 | | get_cert_size(sc_card_t *card, sc_path_t *path, size_t *psize) |
101 | 0 | { |
102 | 0 | int r; |
103 | 0 | sc_file_t *file = NULL; |
104 | |
|
105 | 0 | r = sc_select_file(card, path, &file); |
106 | 0 | LOG_TEST_RET(card->ctx, r, "Failed to select EF certificate"); |
107 | | |
108 | 0 | *psize = file->size; |
109 | 0 | sc_file_free(file); |
110 | |
|
111 | 0 | return SC_SUCCESS; |
112 | 0 | } |
113 | | |
114 | | static int |
115 | | add_app(sc_pkcs15_card_t *p15card, const container *containers, int container_count) |
116 | 0 | { |
117 | 0 | int i, containers_added = 0, r = SC_SUCCESS; |
118 | 0 | ppindata installed_pins[4]; |
119 | 0 | size_t installed_pin_count = 0; |
120 | 0 | sc_card_t *card = p15card->card; |
121 | |
|
122 | 0 | LOG_FUNC_CALLED(card->ctx); |
123 | |
|
124 | 0 | for (i = 0; i < container_count; i++) { |
125 | 0 | struct sc_pkcs15_cert_info cert_info; |
126 | 0 | struct sc_pkcs15_object cert_obj; |
127 | 0 | size_t cert_size; |
128 | |
|
129 | 0 | memset(&cert_info, 0, sizeof(cert_info)); |
130 | 0 | memset(&cert_obj, 0, sizeof(cert_obj)); |
131 | |
|
132 | 0 | sc_pkcs15_format_id(containers[i].id, &cert_info.id); |
133 | 0 | cert_info.authority = containers[i].certdata->authority; |
134 | 0 | sc_format_path(containers[i].certdata->path, &cert_info.path); |
135 | |
|
136 | 0 | r = get_cert_size(card, &cert_info.path, &cert_size); |
137 | 0 | if ( r != SC_SUCCESS ) { |
138 | 0 | sc_log(card->ctx, "Failed to determine size of certificate %s, ignoring container", containers[i].certdata->label); |
139 | 0 | continue; |
140 | 0 | } |
141 | | |
142 | 0 | strlcpy(cert_obj.label, containers[i].certdata->label, sizeof(cert_obj.label)); |
143 | 0 | cert_obj.flags = containers[i].certdata->obj_flags; |
144 | |
|
145 | 0 | r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); |
146 | 0 | LOG_TEST_RET(card->ctx, r, "Failed to add certificate"); |
147 | | |
148 | 0 | if (containers[i].pindata != 0) { |
149 | 0 | size_t j; |
150 | 0 | int is_pin_installed = 0; |
151 | | |
152 | | /* A pin object could be used by more than 1 container, ensure it is added only once */ |
153 | 0 | for (j = 0; j < installed_pin_count; j++) { |
154 | 0 | if (installed_pins[j] == containers[i].pindata) { |
155 | 0 | is_pin_installed = 1; |
156 | 0 | break; |
157 | 0 | } |
158 | 0 | } |
159 | |
|
160 | 0 | if (!is_pin_installed) { |
161 | 0 | struct sc_pkcs15_auth_info pin_info; |
162 | 0 | struct sc_pkcs15_object pin_obj; |
163 | |
|
164 | 0 | if (installed_pin_count < (int)(sizeof(installed_pins) / sizeof(ppindata))) { |
165 | 0 | installed_pins[installed_pin_count++] = containers[i].pindata; |
166 | 0 | } else { |
167 | 0 | sc_log(card->ctx, "Warning: cannot add more than 4 pins"); |
168 | 0 | continue; |
169 | 0 | } |
170 | | |
171 | 0 | memset(&pin_info, 0, sizeof(pin_info)); |
172 | 0 | memset(&pin_obj, 0, sizeof(pin_obj)); |
173 | |
|
174 | 0 | sc_pkcs15_format_id(containers[i].pindata->id, &pin_info.auth_id); |
175 | 0 | pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; |
176 | 0 | pin_info.attrs.pin.reference = containers[i].pindata->ref; |
177 | 0 | pin_info.attrs.pin.flags = containers[i].pindata->flags; |
178 | 0 | pin_info.attrs.pin.type = containers[i].pindata->type; |
179 | 0 | pin_info.attrs.pin.min_length = containers[i].pindata->minlen; |
180 | 0 | pin_info.attrs.pin.stored_length = containers[i].pindata->storedlen; |
181 | 0 | pin_info.attrs.pin.max_length = containers[i].pindata->maxlen; |
182 | 0 | pin_info.attrs.pin.pad_char = containers[i].pindata->pad_char; |
183 | 0 | if (containers[i].pindata->path != NULL) |
184 | 0 | sc_format_path(containers[i].pindata->path, &pin_info.path); |
185 | 0 | pin_info.tries_left = -1; |
186 | 0 | pin_info.max_tries = containers[i].pindata->max_tries; |
187 | |
|
188 | 0 | strlcpy(pin_obj.label, containers[i].pindata->label, sizeof(pin_obj.label)); |
189 | 0 | pin_obj.flags = containers[i].pindata->obj_flags; |
190 | |
|
191 | 0 | r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); |
192 | 0 | LOG_TEST_RET(card->ctx, r, "Failed to add PIN object"); |
193 | 0 | } |
194 | 0 | } |
195 | | |
196 | 0 | if (containers[i].prdata != 0) { |
197 | 0 | struct sc_pkcs15_prkey_info prkey_info; |
198 | 0 | struct sc_pkcs15_object prkey_obj; |
199 | 0 | int modulus_len = containers[i].prdata->modulus_len; |
200 | 0 | memset(&prkey_info, 0, sizeof(prkey_info)); |
201 | 0 | memset(&prkey_obj, 0, sizeof(prkey_obj)); |
202 | |
|
203 | 0 | sc_pkcs15_format_id(containers[i].id, &prkey_info.id); |
204 | 0 | prkey_info.usage = containers[i].prdata->usage; |
205 | 0 | prkey_info.native = 1; |
206 | 0 | prkey_info.key_reference = containers[i].prdata->ref; |
207 | 0 | prkey_info.modulus_length = modulus_len; |
208 | 0 | sc_format_path(containers[i].prdata->path, &prkey_info.path); |
209 | |
|
210 | 0 | strlcpy(prkey_obj.label, containers[i].prdata->label, sizeof(prkey_obj.label)); |
211 | 0 | prkey_obj.flags = containers[i].prdata->obj_flags; |
212 | 0 | if (containers[i].prdata->auth_id) { |
213 | 0 | sc_pkcs15_format_id(containers[i].prdata->auth_id, &prkey_obj.auth_id); |
214 | 0 | } |
215 | |
|
216 | 0 | r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info); |
217 | 0 | LOG_TEST_RET(card->ctx, r, "Failed to add RSA prkey"); |
218 | 0 | } |
219 | | |
220 | 0 | containers_added++; |
221 | 0 | } |
222 | | |
223 | 0 | if (containers_added == 0) { |
224 | 0 | r = SC_ERROR_INVALID_CARD; |
225 | 0 | } else { |
226 | 0 | r = SC_SUCCESS; |
227 | 0 | } |
228 | |
|
229 | 0 | LOG_FUNC_RETURN(card->ctx, r); |
230 | 0 | } |
231 | | |
232 | | /* |
233 | | * adds the PKCS15 objects of the ESIGN application. The app may contain |
234 | | * 1) Authentication container |
235 | | * - 2048-bit RSA key |
236 | | * - CH certificate |
237 | | * 2) Encryption container |
238 | | * - 2048-bit RSA key |
239 | | * - CH certificate |
240 | | * 3) Authentication Issuer container |
241 | | * - issuer certificate |
242 | | * 3) Encryption Issuer container |
243 | | * - issuer certificate |
244 | | * Depending on the card profile, some containers may be missing. |
245 | | * Both RSA keys are protected with the UserPIN. The app may have a PUK, not |
246 | | * supported by this emulator. |
247 | | * |
248 | | * The issuer certificates are not included by default, define ENABLE_ESIGN_ISSUER_CONTAINERS |
249 | | * to enable them. |
250 | | */ |
251 | | static int |
252 | | starcos_add_esign_app(sc_pkcs15_card_t *p15card) |
253 | 0 | { |
254 | 0 | static cdata auth_cert = {"C.CH.AUT", 0, "3F00060843F1", "1", 0}; |
255 | 0 | static cdata encr_cert = {"C.CH.ENC", 0, "3F0006084301", "2", 0}; |
256 | | #ifdef ENABLE_ESIGN_ISSUER_CONTAINERS |
257 | | const cdata auth_root_cert = { "C.RootCA_Auth", 1, "3F00060843F0", "3", 0 }; |
258 | | const cdata encr_root_cert = { "C.RootCA_Enc", 1, "3F0006084300", "4", 0 }; |
259 | | #endif |
260 | |
|
261 | 0 | static prdata auth_key = {"1", "PrK.CH.AUT", 2048, USAGE_AUT, "3F000608", 0x81, "1", SC_PKCS15_CO_FLAG_PRIVATE}; |
262 | 0 | static prdata encr_key = {"2", "PrK.CH.ENC", 2048, USAGE_KE, "3F000608", 0x83, "1", SC_PKCS15_CO_FLAG_PRIVATE}; |
263 | |
|
264 | 0 | static pindata auth_pin = {"1", "UserPIN", "3F00", 0x01, SC_PKCS15_PIN_TYPE_UTF8, 16, 6, 0, |
265 | 0 | USER_PIN, -1, 3, 0x00, SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE}; |
266 | |
|
267 | 0 | static pindata auth_pin_v35 = {"1", "UserPIN", "3F00", 0x06, SC_PKCS15_PIN_TYPE_UTF8, 16, 6, 0, |
268 | 0 | USER_PIN, -1, 3, 0x00, SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE}; |
269 | |
|
270 | 0 | ppindata auth = (p15card->card->type == SC_CARD_TYPE_STARCOS_V3_5_ESIGN) ? &auth_pin_v35 : &auth_pin; |
271 | |
|
272 | 0 | const container containers[] = { |
273 | 0 | {"1", &auth_cert, auth, &auth_key}, |
274 | 0 | {"2", &encr_cert, auth, &encr_key}, |
275 | | #ifdef ENABLE_ESIGN_ISSUER_CONTAINERS |
276 | | { "3", &auth_root_cert, 0, 0 }, |
277 | | { "4", &encr_root_cert, 0, 0 }, |
278 | | #endif |
279 | 0 | }; |
280 | |
|
281 | 0 | return add_app(p15card, containers, sizeof(containers) / sizeof(container)); |
282 | 0 | } |
283 | | |
284 | | static int |
285 | | starcos_esign_init(sc_pkcs15_card_t *p15card, struct sc_aid *aid) |
286 | 0 | { |
287 | 0 | sc_card_t *card = p15card->card; |
288 | 0 | sc_context_t *ctx = card->ctx; |
289 | 0 | const char *label = name_Card; |
290 | 0 | int r; |
291 | 0 | int apps_added = 0; |
292 | |
|
293 | 0 | SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); |
294 | |
|
295 | 0 | if (card->type != SC_CARD_TYPE_STARCOS_V3_4_ESIGN && card->type != SC_CARD_TYPE_STARCOS_V3_5_ESIGN) { |
296 | 0 | return SC_ERROR_WRONG_CARD; |
297 | 0 | } |
298 | | |
299 | 0 | if (aid == NULL) { |
300 | | // no aid: in this case all emulated apps are added, currently only the esign_app |
301 | 0 | r = starcos_add_esign_app(p15card); |
302 | 0 | if (r == SC_SUCCESS) |
303 | 0 | apps_added++; |
304 | 0 | } else { |
305 | | // aid specified: only the matching app is added |
306 | 0 | if (aid->len == sizeof(aid_ESIGN) && memcmp(aid->value, aid_ESIGN, sizeof(aid_ESIGN)) == 0) { |
307 | 0 | r = starcos_add_esign_app(p15card); |
308 | 0 | if (r == SC_SUCCESS) { |
309 | 0 | label = name_ESign; |
310 | 0 | apps_added++; |
311 | 0 | } |
312 | 0 | } |
313 | |
|
314 | 0 | if (apps_added > 0) { |
315 | | // pkcs11 requires the file_app |
316 | 0 | struct sc_path path; |
317 | 0 | struct sc_file *file = NULL; |
318 | 0 | sc_path_set(&path, SC_PATH_TYPE_DF_NAME, aid->value, aid->len, 0, 0); |
319 | 0 | r = sc_select_file(card, &path, &file); |
320 | 0 | if (r != SC_SUCCESS || !file) |
321 | 0 | return SC_ERROR_INTERNAL; |
322 | 0 | sc_file_free(p15card->file_app); |
323 | 0 | p15card->file_app = file; |
324 | 0 | } |
325 | 0 | } |
326 | | |
327 | 0 | if (apps_added == 0) { |
328 | 0 | LOG_TEST_RET(ctx, SC_ERROR_WRONG_CARD, "No supported app found on this card"); |
329 | 0 | } |
330 | | |
331 | 0 | sc_pkcs15_free_tokeninfo(p15card->tokeninfo); |
332 | |
|
333 | 0 | p15card->tokeninfo = sc_pkcs15_tokeninfo_new(); |
334 | 0 | if (!p15card->tokeninfo) { |
335 | 0 | LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "unable to create tokeninfo struct"); |
336 | 0 | } else { |
337 | 0 | sc_serial_number_t serial; |
338 | 0 | char serial_hex[SC_MAX_SERIALNR * 2 + 2]; |
339 | 0 | r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serial); |
340 | 0 | LOG_TEST_RET(ctx, r, "Failed to query card serial number"); |
341 | | |
342 | 0 | r = sc_bin_to_hex(serial.value, serial.len, serial_hex, sizeof serial_hex, 0); |
343 | 0 | LOG_TEST_RET(ctx, r, "Failed to convert S/N to hex"); |
344 | 0 | p15card->tokeninfo->serial_number = strdup(serial_hex); |
345 | 0 | p15card->tokeninfo->label = strdup(label); |
346 | 0 | p15card->tokeninfo->manufacturer_id = strdup(name_Vendor); |
347 | 0 | p15card->tokeninfo->flags = SC_PKCS15_TOKEN_READONLY; |
348 | 0 | } |
349 | | |
350 | 0 | LOG_FUNC_RETURN(ctx, SC_SUCCESS); |
351 | 0 | } |
352 | | |
353 | | int |
354 | | sc_pkcs15emu_starcos_esign_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *aid) |
355 | 0 | { |
356 | 0 | int r = SC_ERROR_WRONG_CARD; |
357 | |
|
358 | 0 | if (!p15card || !p15card->card || !p15card->card->ctx) |
359 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
360 | | |
361 | 0 | r = starcos_esign_init(p15card, aid); |
362 | 0 | LOG_FUNC_RETURN(p15card->card->ctx, r); |
363 | 0 | } |