/src/opensc/src/libopensc/pkcs15-tccardos.c
Line | Count | Source |
1 | | /* |
2 | | * pkcs15-tccardos.c: PKCS#15 profile for TC CardOS M4 cards |
3 | | * |
4 | | * Copyright (C) 2005 Nils Larsch <nils@larsch.net> |
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 <stdlib.h> |
26 | | #include <string.h> |
27 | | |
28 | | #include "internal.h" |
29 | | #include "log.h" |
30 | | #include "pkcs15.h" |
31 | | |
32 | 0 | #define MANU_ID "SIEMENS AG" |
33 | 0 | #define TC_CARDOS_APP_DF "3F001002" |
34 | 0 | #define TC_CARDOS_LABEL "TC CardOS M4" |
35 | | |
36 | 0 | #define TC_CARDOS_SIGN 0x0020 |
37 | 0 | #define TC_CARDOS_AUTH 0x0040 |
38 | 0 | #define TC_CARDOS_DEC 0x0080 |
39 | 0 | #define TC_CARDOS_NOPIN 0x1000 |
40 | 0 | #define TC_CARDOS_LOCALPIN 0x2000 |
41 | | #define TC_CARDOS_GLOBALPIN 0x3000 |
42 | 0 | #define TC_CARDOS_PIN_MASK 0x3000 |
43 | | |
44 | | static int read_file(struct sc_card *card, const char *file, u8 *buf, |
45 | | size_t *len) |
46 | 0 | { |
47 | 0 | int r; |
48 | 0 | struct sc_path path; |
49 | 0 | struct sc_file *fid = NULL; |
50 | |
|
51 | 0 | sc_format_path(file, &path); |
52 | 0 | r = sc_select_file(card, &path, &fid); |
53 | 0 | if (r != SC_SUCCESS) |
54 | 0 | return r; |
55 | 0 | if (!fid) |
56 | 0 | return SC_ERROR_INTERNAL; |
57 | 0 | if (fid->size < *len) |
58 | 0 | *len = fid->size; |
59 | 0 | r = sc_read_binary(card, 0, buf, *len, 0); |
60 | 0 | sc_file_free(fid); |
61 | 0 | if ((size_t)r < *len) |
62 | 0 | return SC_ERROR_INTERNAL; |
63 | | |
64 | 0 | return SC_SUCCESS; |
65 | 0 | } |
66 | | |
67 | | static const char *get_keyholder(int fileId) |
68 | 0 | { |
69 | 0 | u8 tmp = fileId & 0x0f; |
70 | |
|
71 | 0 | if (tmp < 0x08) |
72 | 0 | return "CH"; |
73 | 0 | else if (tmp < 0x0d) |
74 | 0 | return "CA"; |
75 | 0 | else if (tmp == 0x0e) |
76 | 0 | return "RCA"; |
77 | 0 | else |
78 | 0 | return "error"; |
79 | 0 | } |
80 | | |
81 | | static const char *get_service(int fileId) |
82 | 0 | { |
83 | 0 | u8 tmp = (fileId >> 8) & 0x0f; |
84 | |
|
85 | 0 | if (tmp == 0) |
86 | 0 | return "DS"; |
87 | 0 | else if (tmp == 2 || tmp == 3) |
88 | 0 | return "KE"; |
89 | 0 | else if (tmp == 5) |
90 | 0 | return "AUT"; |
91 | 0 | else |
92 | 0 | return "error"; |
93 | 0 | } |
94 | | |
95 | | static int create_cert_obj(sc_pkcs15_card_t *p15card, int fileId) |
96 | 0 | { |
97 | 0 | sc_pkcs15_object_t p15obj; |
98 | 0 | sc_pkcs15_cert_info_t cinfo; |
99 | |
|
100 | 0 | memset(&p15obj, 0, sizeof(p15obj)); |
101 | 0 | memset(&cinfo, 0, sizeof(cinfo)); |
102 | | /* the certificate attributes */ |
103 | 0 | cinfo.id.value[0] = (fileId >> 8) & 0xff; |
104 | 0 | cinfo.id.value[1] = fileId & 0xff; |
105 | 0 | cinfo.id.len = 2; |
106 | 0 | cinfo.authority = fileId & 0x08 ? 1 : 0; |
107 | 0 | cinfo.path.value[0] = (fileId >> 8) & 0xff; |
108 | 0 | cinfo.path.value[1] = fileId & 0xff; |
109 | 0 | cinfo.path.len = 2; |
110 | 0 | cinfo.path.type = SC_PATH_TYPE_FILE_ID; |
111 | 0 | cinfo.path.index = 0; |
112 | 0 | cinfo.path.count = -1; |
113 | | |
114 | | /* compose the certificate name from the fileID */ |
115 | 0 | sprintf(p15obj.label, "C.%s.%s", get_keyholder(fileId), get_service(fileId)); |
116 | 0 | p15obj.flags = 0; /* XXX */ |
117 | 0 | p15obj.user_consent = 0; |
118 | |
|
119 | 0 | return sc_pkcs15emu_add_x509_cert(p15card, &p15obj, &cinfo); |
120 | 0 | } |
121 | | |
122 | | static int create_pkey_obj(sc_pkcs15_card_t *p15card, int cert, int key_descr, |
123 | | unsigned int keyId, unsigned int pinId) |
124 | 0 | { |
125 | 0 | sc_pkcs15_object_t p15obj; |
126 | 0 | sc_pkcs15_prkey_info_t pinfo; |
127 | | |
128 | | /* init data objects */ |
129 | 0 | memset(&p15obj, 0, sizeof(p15obj)); |
130 | 0 | memset(&pinfo, 0, sizeof(pinfo)); |
131 | | /* the private key attributes */ |
132 | 0 | pinfo.id.value[0] = (cert >> 8) & 0xff; |
133 | 0 | pinfo.id.value[1] = cert & 0xff; |
134 | 0 | pinfo.id.len = 2; |
135 | 0 | pinfo.native = 1; |
136 | 0 | pinfo.key_reference = (u8)keyId; |
137 | 0 | pinfo.modulus_length = 1024; /* XXX */ |
138 | 0 | pinfo.access_flags = SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE; |
139 | 0 | pinfo.usage = 0; |
140 | 0 | if (key_descr & TC_CARDOS_SIGN) |
141 | 0 | pinfo.usage = SC_PKCS15_PRKEY_USAGE_SIGN | |
142 | 0 | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; |
143 | 0 | if (key_descr & TC_CARDOS_AUTH) |
144 | 0 | pinfo.usage |= SC_PKCS15_PRKEY_USAGE_SIGN; |
145 | 0 | if (key_descr & TC_CARDOS_DEC) |
146 | 0 | pinfo.usage = SC_PKCS15_PRKEY_USAGE_ENCRYPT | |
147 | 0 | SC_PKCS15_PRKEY_USAGE_DECRYPT | |
148 | 0 | SC_PKCS15_PRKEY_USAGE_WRAP | |
149 | 0 | SC_PKCS15_PRKEY_USAGE_UNWRAP; |
150 | 0 | sc_format_path(TC_CARDOS_APP_DF, &pinfo.path); |
151 | 0 | pinfo.path.index = 0; |
152 | 0 | pinfo.path.count = 0; |
153 | | /* the common object attributes */ |
154 | 0 | sprintf(p15obj.label, "SK.CH.%s", get_service(cert)); |
155 | 0 | if (pinId && (key_descr & TC_CARDOS_PIN_MASK)) { |
156 | 0 | p15obj.auth_id.value[0] = (u8)pinId; |
157 | 0 | p15obj.auth_id.len = 1; |
158 | 0 | } |
159 | 0 | p15obj.flags = SC_PKCS15_CO_FLAG_PRIVATE; |
160 | 0 | p15obj.user_consent = 0; |
161 | 0 | p15obj.type = SC_PKCS15_TYPE_PRKEY_RSA; |
162 | |
|
163 | 0 | return sc_pkcs15emu_add_rsa_prkey(p15card, &p15obj, &pinfo); |
164 | 0 | } |
165 | | |
166 | | static int create_pin_obj(sc_pkcs15_card_t *p15card, int cert, |
167 | | int key_descr, unsigned int pinId) |
168 | 0 | { |
169 | 0 | sc_pkcs15_object_t p15obj; |
170 | 0 | sc_pkcs15_auth_info_t ainfo; |
171 | | |
172 | | /* init data objects */ |
173 | 0 | memset(&p15obj, 0, sizeof(p15obj)); |
174 | 0 | memset(&ainfo, 0, sizeof(ainfo)); |
175 | | /* the authentication object attributes */ |
176 | 0 | ainfo.auth_id.value[0] = (u8)pinId; |
177 | 0 | ainfo.auth_id.len = 1; |
178 | 0 | ainfo.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; |
179 | 0 | ainfo.attrs.pin.reference = (u8)pinId; |
180 | 0 | ainfo.attrs.pin.flags = SC_PKCS15_PIN_FLAG_EXCHANGE_REF_DATA; |
181 | 0 | if ((key_descr & TC_CARDOS_PIN_MASK) == TC_CARDOS_LOCALPIN) |
182 | 0 | ainfo.attrs.pin.flags |= SC_PKCS15_PIN_FLAG_LOCAL; |
183 | 0 | ainfo.attrs.pin.type = SC_PKCS15_PIN_TYPE_BCD; /* XXX */ |
184 | 0 | ainfo.attrs.pin.min_length = 6; /* XXX */ |
185 | 0 | ainfo.attrs.pin.stored_length = 8; /* XXX */ |
186 | 0 | ainfo.attrs.pin.max_length = 8; |
187 | 0 | ainfo.attrs.pin.pad_char = 0; |
188 | 0 | ainfo.tries_left = 3; /* XXX */ |
189 | 0 | ainfo.logged_in = SC_PIN_STATE_UNKNOWN; |
190 | 0 | sc_format_path(TC_CARDOS_APP_DF, &ainfo.path); |
191 | 0 | ainfo.path.index = 0; |
192 | 0 | ainfo.path.count = 0; |
193 | | /* the common object attributes */ |
194 | 0 | sprintf(p15obj.label, "PIN.CH.%s", get_service(cert)); |
195 | 0 | p15obj.flags = SC_PKCS15_CO_FLAG_PRIVATE; |
196 | 0 | p15obj.user_consent = 0; |
197 | 0 | p15obj.type = SC_PKCS15_TYPE_AUTH_PIN; |
198 | |
|
199 | 0 | return sc_pkcs15emu_add_pin_obj(p15card, &p15obj, &ainfo); |
200 | 0 | } |
201 | | |
202 | 0 | #define MAX_INFO1_SIZE 256 |
203 | 0 | #define MAX_INFO2_SIZE 256 |
204 | | |
205 | | static int parse_EF_CardInfo(sc_pkcs15_card_t *p15card) |
206 | 0 | { |
207 | 0 | int r; |
208 | 0 | u8 info1[MAX_INFO1_SIZE]; |
209 | 0 | size_t info1_len = MAX_INFO1_SIZE; |
210 | 0 | u8 info2[MAX_INFO2_SIZE]; |
211 | 0 | size_t info2_len = MAX_INFO2_SIZE; |
212 | 0 | u8 *p1, *p2; |
213 | 0 | size_t i; |
214 | 0 | unsigned int key_num; |
215 | 0 | struct sc_context *ctx = p15card->card->ctx; |
216 | 0 | size_t offset; |
217 | | |
218 | | /* read EF_CardInfo1 */ |
219 | 0 | r = read_file(p15card->card, "3F001003b200", info1, &info1_len); |
220 | 0 | if (r != SC_SUCCESS || info1_len < 4) |
221 | 0 | return SC_ERROR_WRONG_CARD; |
222 | | /* read EF_CardInfo2 */ |
223 | 0 | r = read_file(p15card->card, "3F001003b201", info2, &info2_len); |
224 | 0 | if (r != SC_SUCCESS) |
225 | 0 | return SC_ERROR_WRONG_CARD; |
226 | | /* get the number of private keys */ |
227 | 0 | key_num = ((unsigned int) info1[info1_len-1]) |
228 | 0 | | (((unsigned int) info1[info1_len-2]) << 8) |
229 | 0 | | (((unsigned int) info1[info1_len-3]) << 16) |
230 | 0 | | (((unsigned int) info1[info1_len-4]) << 24); |
231 | 0 | sc_log(ctx, |
232 | 0 | "found %d private keys\n", (int)key_num); |
233 | | /* set p1 to the address of the first key descriptor */ |
234 | 0 | offset = info1_len - 4 - key_num * 2; |
235 | 0 | if (offset >= info1_len) |
236 | 0 | return SC_ERROR_INVALID_DATA; |
237 | 0 | p1 = info1 + offset; |
238 | 0 | p2 = info2; |
239 | | |
240 | | /* This is the minimum amount of data expected by the following code without |
241 | | * overunning the buffer without additional condition for cert_count == 4 */ |
242 | 0 | if (info2_len < key_num * 14) |
243 | 0 | return SC_ERROR_INVALID_DATA; |
244 | 0 | for (i=0; i<key_num; i++) { |
245 | 0 | u8 pinId, keyId, cert_count; |
246 | 0 | int ch_cert, ca_cert, r1_cert, r2_cert = 0; |
247 | 0 | int key_descr; |
248 | | /* evaluate CertInfo2 */ |
249 | 0 | cert_count = *p2++; |
250 | 0 | p2 += 2; /* ignore cert DF (it's always 1002) */ |
251 | 0 | keyId = *p2++; |
252 | 0 | p2++; /* ignore transport pin XXX */ |
253 | 0 | pinId = *p2++; |
254 | 0 | p2 += 2; /* RFU */ |
255 | 0 | ch_cert = (p2[0] << 8) | p2[1]; |
256 | 0 | p2 += 2; |
257 | 0 | ca_cert = (p2[0] << 8) | p2[1]; |
258 | 0 | p2 += 2; |
259 | 0 | r1_cert = (p2[0] << 8) | p2[1]; |
260 | 0 | p2 += 2; |
261 | 0 | if (cert_count == 4) { |
262 | 0 | r2_cert = (p2[0] << 8) | p2[1]; |
263 | 0 | p2 += 2; |
264 | 0 | } |
265 | | /* evaluate CertInfo1 */ |
266 | 0 | key_descr = (p1[0] << 8) | p1[1]; |
267 | 0 | p1 += 2; |
268 | | /* create and add certificates */ |
269 | 0 | if (ch_cert) { |
270 | 0 | r = create_cert_obj(p15card, ch_cert); |
271 | 0 | if (r < 0) |
272 | 0 | return r; |
273 | 0 | } |
274 | 0 | if (ca_cert) { |
275 | 0 | r = create_cert_obj(p15card, ca_cert); |
276 | 0 | if (r < 0) |
277 | 0 | return r; |
278 | 0 | } |
279 | 0 | if (r1_cert) { |
280 | 0 | r = create_cert_obj(p15card, r1_cert); |
281 | 0 | if (r < 0) |
282 | 0 | return r; |
283 | 0 | } |
284 | 0 | if (r2_cert) { |
285 | 0 | r = create_cert_obj(p15card, r2_cert); |
286 | 0 | if (r < 0) |
287 | 0 | return r; |
288 | 0 | } |
289 | | /* create and add pin object */ |
290 | 0 | if ((key_descr & TC_CARDOS_PIN_MASK) != TC_CARDOS_NOPIN) { |
291 | 0 | r = create_pin_obj(p15card, ch_cert, key_descr, pinId); |
292 | 0 | if (r < 0) |
293 | 0 | return r; |
294 | 0 | } else |
295 | 0 | pinId = 0; |
296 | | /* create and add private key */ |
297 | 0 | r = create_pkey_obj(p15card, ch_cert, key_descr, keyId, pinId); |
298 | 0 | if (r < 0) |
299 | 0 | return r; |
300 | 0 | } |
301 | 0 | return SC_SUCCESS; |
302 | 0 | } |
303 | | |
304 | | |
305 | | static int sc_pkcs15_tccardos_init_func(sc_pkcs15_card_t *p15card) |
306 | 0 | { |
307 | 0 | int r; |
308 | 0 | struct sc_path path; |
309 | 0 | struct sc_file *file = NULL; |
310 | 0 | char hex_buf[256]; |
311 | 0 | struct sc_card *card = p15card->card; |
312 | 0 | sc_serial_number_t iccsn; |
313 | 0 | iccsn.len = sizeof iccsn.value; |
314 | | |
315 | | /* check if we have the correct card OS */ |
316 | 0 | if (strcmp(card->name, "CardOS M4")) |
317 | 0 | return SC_ERROR_WRONG_CARD; |
318 | | /* create pkcs15 objects */ |
319 | 0 | r = parse_EF_CardInfo(p15card); |
320 | 0 | if (r != SC_SUCCESS) |
321 | 0 | return r; |
322 | | /* set card label */ |
323 | 0 | set_string(&p15card->tokeninfo->label, TC_CARDOS_LABEL); |
324 | 0 | if (p15card->tokeninfo->label == NULL) |
325 | 0 | return SC_ERROR_OUT_OF_MEMORY; |
326 | | /* set the manufacturer ID */ |
327 | 0 | set_string(&p15card->tokeninfo->manufacturer_id, MANU_ID); |
328 | 0 | if (p15card->tokeninfo->manufacturer_id == NULL) { |
329 | 0 | r = SC_ERROR_OUT_OF_MEMORY; |
330 | 0 | goto err; |
331 | 0 | } |
332 | | /* set the serial number */ |
333 | 0 | r = sc_parse_ef_gdo(card, iccsn.value, &iccsn.len, NULL, 0); |
334 | 0 | if (r != SC_SUCCESS || iccsn.len < 5+8) { |
335 | 0 | r = SC_ERROR_INTERNAL; |
336 | 0 | goto err; |
337 | 0 | } |
338 | 0 | sc_bin_to_hex(iccsn.value + 5, 8, hex_buf, sizeof(hex_buf), 0); |
339 | 0 | set_string(&p15card->tokeninfo->serial_number, hex_buf); |
340 | 0 | if (p15card->tokeninfo->serial_number == NULL) { |
341 | 0 | r = SC_ERROR_OUT_OF_MEMORY; |
342 | 0 | goto err; |
343 | 0 | } |
344 | | /* select the application DF */ |
345 | 0 | sc_format_path(TC_CARDOS_APP_DF, &path); |
346 | 0 | r = sc_select_file(card, &path, &file); |
347 | 0 | if (r != SC_SUCCESS || file == NULL) { |
348 | 0 | r = SC_ERROR_INTERNAL; |
349 | 0 | goto err; |
350 | 0 | } |
351 | | /* set the application DF */ |
352 | 0 | sc_file_free(p15card->file_app); |
353 | 0 | p15card->file_app = file; |
354 | |
|
355 | 0 | return SC_SUCCESS; |
356 | | |
357 | 0 | err: |
358 | 0 | sc_pkcs15_card_clear(p15card); |
359 | 0 | return r; |
360 | 0 | } |
361 | | |
362 | | int sc_pkcs15emu_tccardos_init_ex(sc_pkcs15_card_t *p15card, |
363 | | struct sc_aid *aid) |
364 | 0 | { |
365 | 0 | return sc_pkcs15_tccardos_init_func(p15card); |
366 | 0 | } |