/src/opensc/src/pkcs15init/pkcs15-rtecp.c
Line | Count | Source |
1 | | /* |
2 | | * pkcs15-rtecp.c: Rutoken ECP specific operation for PKCS15 initialization |
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 | | #include "config.h" |
22 | | |
23 | | #include <assert.h> |
24 | | #include <stddef.h> |
25 | | #include <stdlib.h> |
26 | | |
27 | | #include "libopensc/opensc.h" |
28 | | #include "libopensc/cardctl.h" |
29 | | #include "libopensc/log.h" |
30 | | #include "libopensc/pkcs15.h" |
31 | | #include "pkcs15-init.h" |
32 | | #include "profile.h" |
33 | | |
34 | 0 | #define RTECP_SO_PIN_REF 1 |
35 | 0 | #define RTECP_USER_PIN_REF 2 |
36 | | |
37 | | /* |
38 | | * Erase everything that's on the card |
39 | | */ |
40 | | static int rtecp_erase(sc_profile_t *profile, sc_pkcs15_card_t *p15card) |
41 | 0 | { |
42 | 0 | int r; |
43 | |
|
44 | 0 | if (!profile || !p15card || !p15card->card) |
45 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
46 | 0 | r = sc_card_ctl(p15card->card, SC_CARDCTL_RTECP_INIT, NULL); |
47 | 0 | if (r == SC_SUCCESS) |
48 | 0 | sc_free_apps(p15card->card); |
49 | 0 | return r; |
50 | 0 | } |
51 | | |
52 | | static int create_sysdf(sc_profile_t *profile, sc_card_t *card, const char *name) |
53 | 0 | { |
54 | 0 | sc_file_t *file; |
55 | 0 | sc_path_t path; |
56 | 0 | int r; |
57 | |
|
58 | 0 | assert(profile && card && card->ctx && name); |
59 | 0 | r = sc_profile_get_file(profile, name, &file); |
60 | 0 | if (r == SC_SUCCESS) |
61 | 0 | { |
62 | 0 | assert(file); |
63 | 0 | path = file->path; |
64 | 0 | assert(path.len > 2); |
65 | 0 | if (path.len > 2) |
66 | 0 | path.len -= 2; |
67 | 0 | r = sc_select_file(card, &path, NULL); |
68 | 0 | if (r == SC_SUCCESS) |
69 | 0 | r = sc_file_add_acl_entry(file, SC_AC_OP_CREATE, |
70 | 0 | SC_AC_CHV, RTECP_USER_PIN_REF); |
71 | 0 | if (r == SC_SUCCESS) |
72 | 0 | r = sc_file_add_acl_entry(file, SC_AC_OP_DELETE, |
73 | 0 | SC_AC_NEVER, SC_AC_KEY_REF_NONE); |
74 | 0 | if (r == SC_SUCCESS) |
75 | 0 | r = sc_create_file(card, file); |
76 | 0 | sc_file_free(file); |
77 | 0 | } |
78 | 0 | sc_log(card->ctx, |
79 | 0 | "Create %s failed: %s\n", name, sc_strerror(r)); |
80 | 0 | return r; |
81 | 0 | } |
82 | | |
83 | | /* |
84 | | * Card-specific initialization of PKCS15 meta-information |
85 | | */ |
86 | | static int rtecp_init(sc_profile_t *profile, sc_pkcs15_card_t *p15card) |
87 | 0 | { |
88 | 0 | sc_card_t *card; |
89 | 0 | sc_file_t *file; |
90 | 0 | int r; |
91 | |
|
92 | 0 | if (!profile || !p15card || !p15card->card || !p15card->card->ctx) |
93 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
94 | | |
95 | 0 | card = p15card->card; |
96 | |
|
97 | 0 | r = sc_profile_get_file(profile, "MF", &file); |
98 | 0 | LOG_TEST_RET(card->ctx, r, "Get MF info failed"); |
99 | 0 | assert(file); |
100 | 0 | r = sc_create_file(card, file); |
101 | 0 | sc_file_free(file); |
102 | 0 | LOG_TEST_RET(card->ctx, r, "Create MF failed"); |
103 | | |
104 | 0 | r = sc_profile_get_file(profile, "DIR", &file); |
105 | 0 | LOG_TEST_RET(card->ctx, r, "Get DIR file info failed"); |
106 | 0 | assert(file); |
107 | 0 | r = sc_create_file(card, file); |
108 | 0 | sc_file_free(file); |
109 | 0 | LOG_TEST_RET(card->ctx, r, "Create DIR file failed"); |
110 | | |
111 | 0 | create_sysdf(profile, card, "Sys-DF"); |
112 | 0 | create_sysdf(profile, card, "SysKey-DF"); |
113 | 0 | create_sysdf(profile, card, "PuKey-DF"); |
114 | 0 | create_sysdf(profile, card, "PrKey-DF"); |
115 | 0 | create_sysdf(profile, card, "SKey-DF"); |
116 | 0 | create_sysdf(profile, card, "Cer-DF"); |
117 | 0 | create_sysdf(profile, card, "LCHV-DF"); |
118 | |
|
119 | 0 | create_sysdf(profile, card, "Resrv1-DF"); |
120 | 0 | create_sysdf(profile, card, "Resrv2-DF"); |
121 | 0 | create_sysdf(profile, card, "Resrv3-DF"); |
122 | 0 | create_sysdf(profile, card, "Resrv4-DF"); |
123 | 0 | create_sysdf(profile, card, "Resrv5-DF"); |
124 | 0 | create_sysdf(profile, card, "Resrv6-DF"); |
125 | |
|
126 | 0 | return sc_select_file(card, sc_get_mf_path(), NULL); |
127 | 0 | } |
128 | | |
129 | | /* |
130 | | * Create a DF |
131 | | */ |
132 | | static int rtecp_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df) |
133 | 0 | { |
134 | 0 | if (!profile || !p15card || !p15card->card || !df) |
135 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
136 | 0 | return sc_create_file(p15card->card, df); |
137 | 0 | } |
138 | | |
139 | | /* |
140 | | * Select a PIN reference |
141 | | */ |
142 | | static int rtecp_select_pin_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, |
143 | | sc_pkcs15_auth_info_t *auth_info) |
144 | 0 | { |
145 | 0 | int pin_ref; |
146 | |
|
147 | 0 | if (!profile || !p15card || !p15card->card || !p15card->card->ctx || !auth_info) |
148 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
149 | | |
150 | 0 | if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) |
151 | 0 | return SC_ERROR_OBJECT_NOT_VALID; |
152 | | |
153 | 0 | if (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) |
154 | 0 | pin_ref = RTECP_SO_PIN_REF; |
155 | 0 | else |
156 | 0 | pin_ref = RTECP_USER_PIN_REF; |
157 | 0 | if (auth_info->attrs.pin.reference != pin_ref) |
158 | 0 | LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_NOT_SUPPORTED); |
159 | | |
160 | 0 | return SC_SUCCESS; |
161 | 0 | } |
162 | | |
163 | | /* |
164 | | * Create a PIN object within the given DF |
165 | | */ |
166 | | static int rtecp_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, |
167 | | sc_file_t *df, sc_pkcs15_object_t *pin_obj, |
168 | | const unsigned char *pin, size_t pin_len, |
169 | | const unsigned char *puk, size_t puk_len) |
170 | 0 | { |
171 | 0 | sc_context_t *ctx; |
172 | 0 | sc_pkcs15_auth_info_t *auth_info; |
173 | 0 | sc_file_t *file = NULL; |
174 | | /* GCHV min-length Flags Attempts Reserve */ |
175 | 0 | unsigned char prop[] = { 0x01, '?', 0x01, '?', 0, 0 }; |
176 | | /* AccessMode Unblock Change Delete */ |
177 | 0 | unsigned char sec[15] = { 0x43, '?', '?', 0, 0, 0, 0, 0xFF }; |
178 | 0 | char pin_sname[0x10]; |
179 | 0 | int r, reset_by_sopin = 0; |
180 | |
|
181 | 0 | (void)puk; /* no warning */ |
182 | 0 | if (!profile || !p15card || !p15card->card || !p15card->card->ctx || !df |
183 | 0 | || !pin_obj || !pin_obj->data || !pin || !pin_len) |
184 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
185 | | |
186 | 0 | ctx = p15card->card->ctx; |
187 | 0 | SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); |
188 | |
|
189 | 0 | if (puk_len != 0) |
190 | 0 | { |
191 | 0 | sc_log(ctx, "Do not enter User unblocking PIN (PUK): %s\n", |
192 | 0 | sc_strerror(SC_ERROR_NOT_SUPPORTED)); |
193 | 0 | return SC_ERROR_NOT_SUPPORTED; |
194 | 0 | } |
195 | | |
196 | 0 | auth_info = (sc_pkcs15_auth_info_t *)pin_obj->data; |
197 | 0 | if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) |
198 | 0 | return SC_ERROR_OBJECT_NOT_VALID; |
199 | | |
200 | 0 | if (auth_info->attrs.pin.reference != RTECP_SO_PIN_REF |
201 | 0 | && auth_info->attrs.pin.reference != RTECP_USER_PIN_REF) |
202 | 0 | { |
203 | 0 | sc_log(ctx, "PIN reference %i not found in standard" |
204 | 0 | " (Rutoken ECP) PINs\n", auth_info->attrs.pin.reference); |
205 | 0 | return SC_ERROR_NOT_SUPPORTED; |
206 | 0 | } |
207 | | |
208 | 0 | snprintf(pin_sname, sizeof(pin_sname), "CHV%i", auth_info->attrs.pin.reference); |
209 | 0 | if (auth_info->attrs.pin.reference == RTECP_USER_PIN_REF) { |
210 | 0 | r = sc_profile_get_file(profile, pin_sname, &file); |
211 | 0 | if (!r) { |
212 | 0 | const struct sc_acl_entry *acl = NULL; |
213 | |
|
214 | 0 | r = sc_pkcs15init_fixup_file(profile, p15card, file); |
215 | 0 | if (r < 0) |
216 | 0 | sc_file_free(file); |
217 | 0 | LOG_TEST_RET(p15card->card->ctx, r, "Cannot fixup the ACLs of PIN file"); |
218 | | |
219 | 0 | acl = sc_file_get_acl_entry(file, SC_AC_OP_PIN_RESET); |
220 | 0 | if (acl && acl->method == SC_AC_CHV && acl->key_ref == RTECP_SO_PIN_REF) { |
221 | 0 | sc_log(ctx, "Allow reset of User PIN with SoPIN\n"); |
222 | 0 | reset_by_sopin = 1; |
223 | 0 | } |
224 | 0 | sc_file_free(file); |
225 | 0 | } |
226 | 0 | } |
227 | | |
228 | 0 | file = sc_file_new(); |
229 | 0 | if (!file) |
230 | 0 | LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); |
231 | 0 | file->id = auth_info->attrs.pin.reference; |
232 | 0 | file->size = pin_len; |
233 | 0 | assert(sizeof(sec)/sizeof(sec[0]) > 2); |
234 | 0 | sec[1] = (auth_info->attrs.pin.reference == RTECP_SO_PIN_REF) ? 0xFF : RTECP_SO_PIN_REF; |
235 | 0 | sec[2] = (unsigned char)auth_info->attrs.pin.reference | (reset_by_sopin ? RTECP_SO_PIN_REF : 0); |
236 | 0 | r = sc_file_set_sec_attr(file, sec, sizeof(sec)); |
237 | 0 | if (r == SC_SUCCESS) |
238 | 0 | { |
239 | 0 | assert(sizeof(prop)/sizeof(prop[0]) > 3); |
240 | 0 | prop[1] = (unsigned char)auth_info->attrs.pin.min_length; |
241 | 0 | prop[3] = 0x11 * (unsigned char)(auth_info->tries_left & 0x0F); |
242 | 0 | r = sc_file_set_prop_attr(file, prop, sizeof(prop)); |
243 | 0 | } |
244 | 0 | if (r == SC_SUCCESS) |
245 | 0 | r = sc_file_set_type_attr(file, (const u8*)"\x10\x00", 2); |
246 | 0 | if (r == SC_SUCCESS) |
247 | 0 | r = sc_create_file(p15card->card, file); |
248 | 0 | sc_file_free(file); |
249 | |
|
250 | 0 | if (r == SC_SUCCESS) |
251 | 0 | r = sc_change_reference_data(p15card->card, SC_AC_CHV, |
252 | 0 | auth_info->attrs.pin.reference, NULL, 0, pin, pin_len, NULL); |
253 | 0 | LOG_FUNC_RETURN(ctx, r); |
254 | 0 | } |
255 | | |
256 | | /* |
257 | | * Select a reference for a private key object |
258 | | */ |
259 | | static int rtecp_select_key_reference(sc_profile_t *profile, |
260 | | sc_pkcs15_card_t *p15card, sc_pkcs15_prkey_info_t *key_info) |
261 | 0 | { |
262 | 0 | sc_file_t *df; |
263 | 0 | int r; |
264 | |
|
265 | 0 | if (!profile || !p15card || !p15card->card || !p15card->card->ctx || !key_info) |
266 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
267 | | |
268 | 0 | if (key_info->key_reference <= 0) |
269 | 0 | key_info->key_reference = 1; |
270 | 0 | else if (key_info->key_reference > 0xFF) |
271 | 0 | return SC_ERROR_TOO_MANY_OBJECTS; |
272 | | |
273 | 0 | r = sc_profile_get_file(profile, "PrKey-DF", &df); |
274 | 0 | LOG_TEST_RET(p15card->card->ctx, r, "Get PrKey-DF info failed"); |
275 | 0 | assert(df); |
276 | 0 | key_info->path = df->path; |
277 | 0 | sc_file_free(df); |
278 | 0 | r = sc_append_file_id(&key_info->path, key_info->key_reference); |
279 | 0 | return r; |
280 | 0 | } |
281 | | |
282 | | /* |
283 | | * Create an empty key object |
284 | | */ |
285 | | static int rtecp_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, |
286 | | sc_pkcs15_object_t *obj) |
287 | 0 | { |
288 | 0 | sc_context_t *ctx; |
289 | | /* RSA_PRkey/ Adds Miller- |
290 | | * RSA_PUBkey Rabin tests Attempts Reserve */ |
291 | 0 | const unsigned char prkey_prop[] = { 0x23, 0, 0, 0xAA, 0, 0 }; |
292 | 0 | const unsigned char pbkey_prop[] = { 0x33, 0, 0, 0xAA, 0, 0 }; |
293 | | /* GOSTR3410_PRkey/ |
294 | | * GOSTR3410_PUBkey paramset Attempts Reserve */ |
295 | 0 | unsigned char prgkey_prop[] = { 0x03, '?', 0, 0xAA, 0, 0 }; |
296 | 0 | unsigned char pbgkey_prop[] = { 0x13, '?', 0, 0xAA, 0, 0 }; |
297 | | /* AccessMode - Update Use - - - Delete */ |
298 | 0 | unsigned char prkey_sec[15] = { 0x46, 0, '?', '?', 0, 0, 0, '?' }; |
299 | 0 | unsigned char pbkey_sec[15] = { 0x46, 0, '?', 0, 0, 0, 0, '?' }; |
300 | 0 | unsigned char auth_id, paramset; |
301 | 0 | sc_pkcs15_prkey_info_t *key_info; |
302 | 0 | sc_file_t *file; |
303 | 0 | int r; |
304 | |
|
305 | 0 | if (!profile || !p15card || !p15card->card || !p15card->card->ctx |
306 | 0 | || !obj || !obj->data) |
307 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
308 | | |
309 | 0 | ctx = p15card->card->ctx; |
310 | 0 | SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); |
311 | 0 | if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA |
312 | 0 | && obj->type != SC_PKCS15_TYPE_PRKEY_GOSTR3410) |
313 | 0 | return SC_ERROR_NOT_SUPPORTED; |
314 | 0 | if (obj->auth_id.len != 1) |
315 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
316 | 0 | auth_id = obj->auth_id.value[0]; |
317 | |
|
318 | 0 | key_info = (sc_pkcs15_prkey_info_t *)obj->data; |
319 | 0 | assert(key_info); |
320 | 0 | if ((obj->type == SC_PKCS15_TYPE_PRKEY_RSA |
321 | 0 | && key_info->modulus_length % 128 != 0) |
322 | 0 | || (obj->type == SC_PKCS15_TYPE_PRKEY_GOSTR3410 |
323 | 0 | && key_info->modulus_length |
324 | 0 | != SC_PKCS15_GOSTR3410_KEYSIZE)) |
325 | 0 | { |
326 | 0 | sc_log(ctx, |
327 | 0 | "Unsupported key size %"SC_FORMAT_LEN_SIZE_T"u\n", |
328 | 0 | key_info->modulus_length); |
329 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
330 | 0 | } |
331 | 0 | if (obj->type == SC_PKCS15_TYPE_PRKEY_GOSTR3410) |
332 | 0 | { |
333 | 0 | if (key_info->params.len < sizeof(int)) |
334 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
335 | 0 | if (((int*)key_info->params.data)[0] < 1 |
336 | 0 | || ((int*)key_info->params.data)[0] > 3) |
337 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
338 | 0 | paramset = ((unsigned int*)key_info->params.data)[0] & 0x03; |
339 | 0 | assert(sizeof(prgkey_prop)/sizeof(prgkey_prop[0]) > 1); |
340 | 0 | assert(sizeof(pbgkey_prop)/sizeof(pbgkey_prop[0]) > 1); |
341 | 0 | prgkey_prop[1] = 0x10 + (paramset << 4); |
342 | 0 | pbgkey_prop[1] = prgkey_prop[1]; |
343 | 0 | } |
344 | | |
345 | 0 | r = sc_profile_get_file(profile, "PKCS15-AppDF", &file); |
346 | 0 | LOG_TEST_RET(ctx, r, "Get PKCS15-AppDF info failed"); |
347 | 0 | r = sc_file_add_acl_entry(file, SC_AC_OP_CREATE, SC_AC_CHV, auth_id); |
348 | 0 | if (r == SC_SUCCESS) |
349 | 0 | r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_CREATE); |
350 | 0 | sc_file_free(file); |
351 | 0 | LOG_TEST_RET(ctx, r, "Authenticate failed"); |
352 | | |
353 | 0 | file = sc_file_new(); |
354 | 0 | if (!file) |
355 | 0 | LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); |
356 | 0 | file->id = key_info->key_reference; |
357 | 0 | r = sc_file_set_type_attr(file, (const u8*)"\x10\x00", 2); |
358 | | |
359 | | /* private key file */ |
360 | 0 | if (obj->type == SC_PKCS15_TYPE_PRKEY_RSA) |
361 | 0 | file->size = key_info->modulus_length / 8 / 2 * 5 + 8; |
362 | 0 | else |
363 | 0 | file->size = key_info->modulus_length / 8; |
364 | 0 | if (r == SC_SUCCESS) |
365 | 0 | { |
366 | 0 | assert(sizeof(prkey_sec)/sizeof(prkey_sec[0]) > 7); |
367 | 0 | prkey_sec[2] = auth_id; |
368 | 0 | prkey_sec[3] = auth_id; |
369 | 0 | prkey_sec[7] = auth_id; |
370 | 0 | r = sc_file_set_sec_attr(file, prkey_sec, sizeof(prkey_sec)); |
371 | 0 | } |
372 | 0 | if (r == SC_SUCCESS) |
373 | 0 | { |
374 | 0 | if (obj->type == SC_PKCS15_TYPE_PRKEY_RSA) |
375 | 0 | r = sc_file_set_prop_attr(file, prkey_prop, sizeof(prkey_prop)); |
376 | 0 | else |
377 | 0 | r = sc_file_set_prop_attr(file, prgkey_prop,sizeof(prgkey_prop)); |
378 | 0 | } |
379 | 0 | if (r == SC_SUCCESS) { |
380 | 0 | sc_log(ctx, "create private key file id:%04i", file->id); |
381 | 0 | r = sc_create_file(p15card->card, file); |
382 | 0 | } |
383 | | /* public key file */ |
384 | 0 | if (obj->type == SC_PKCS15_TYPE_PRKEY_RSA) |
385 | 0 | file->size = key_info->modulus_length / 8 / 2 * 3; |
386 | 0 | else |
387 | 0 | file->size = key_info->modulus_length / 8 * 2; |
388 | 0 | if (r == SC_SUCCESS) |
389 | 0 | { |
390 | 0 | assert(sizeof(pbkey_sec)/sizeof(pbkey_sec[0]) > 7); |
391 | 0 | pbkey_sec[2] = auth_id; |
392 | 0 | pbkey_sec[7] = auth_id; |
393 | 0 | r = sc_file_set_sec_attr(file, pbkey_sec, sizeof(pbkey_sec)); |
394 | 0 | } |
395 | 0 | if (r == SC_SUCCESS) |
396 | 0 | { |
397 | 0 | if (obj->type == SC_PKCS15_TYPE_PRKEY_RSA) |
398 | 0 | r = sc_file_set_prop_attr(file, pbkey_prop, sizeof(pbkey_prop)); |
399 | 0 | else |
400 | 0 | r = sc_file_set_prop_attr(file, pbgkey_prop,sizeof(pbgkey_prop)); |
401 | 0 | } |
402 | 0 | if (r == SC_SUCCESS) { |
403 | 0 | sc_log(ctx, "create public key file id:%04i", file->id); |
404 | 0 | r = sc_create_file(p15card->card, file); |
405 | 0 | } |
406 | 0 | sc_file_free(file); |
407 | 0 | LOG_FUNC_RETURN(ctx, r); |
408 | 0 | } |
409 | | |
410 | | /* |
411 | | * Store a key on the card |
412 | | */ |
413 | | static int rtecp_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, |
414 | | sc_pkcs15_object_t *obj, sc_pkcs15_prkey_t *key) |
415 | 0 | { |
416 | 0 | sc_card_t *card; |
417 | 0 | sc_pkcs15_prkey_info_t *key_info; |
418 | 0 | sc_file_t *pukey_df; |
419 | 0 | sc_path_t path; |
420 | 0 | unsigned char *buf; |
421 | 0 | size_t buf_len, key_len, len, i; |
422 | 0 | int r; |
423 | |
|
424 | 0 | if (!profile || !p15card || !p15card->card || !p15card->card->ctx |
425 | 0 | || !obj || !obj->data || !key) |
426 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
427 | | |
428 | 0 | card = p15card->card; |
429 | 0 | SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); |
430 | |
|
431 | 0 | if ((obj->type != SC_PKCS15_TYPE_PRKEY_RSA || key->algorithm != SC_ALGORITHM_RSA) |
432 | 0 | && (obj->type != SC_PKCS15_TYPE_PRKEY_GOSTR3410 |
433 | 0 | || key->algorithm != SC_ALGORITHM_GOSTR3410)) |
434 | 0 | return SC_ERROR_NOT_SUPPORTED; |
435 | | |
436 | 0 | key_info = (sc_pkcs15_prkey_info_t *)obj->data; |
437 | 0 | assert(key_info); |
438 | |
|
439 | 0 | if (key->algorithm == SC_ALGORITHM_RSA) |
440 | 0 | { |
441 | 0 | assert(key_info->modulus_length % 128 == 0); |
442 | 0 | len = key_info->modulus_length / 8 / 2; |
443 | 0 | key_len = len * 5 + 8; |
444 | 0 | buf_len = key_len; |
445 | 0 | } |
446 | 0 | else |
447 | 0 | { |
448 | 0 | assert(key_info->modulus_length == SC_PKCS15_GOSTR3410_KEYSIZE); |
449 | 0 | len = key_info->modulus_length / 8; |
450 | 0 | key_len = len; |
451 | 0 | buf_len = len; |
452 | 0 | } |
453 | 0 | if (key->algorithm == SC_ALGORITHM_RSA && (!key->u.rsa.p.data |
454 | 0 | || !key->u.rsa.q.data || !key->u.rsa.iqmp.data |
455 | 0 | || !key->u.rsa.dmp1.data || !key->u.rsa.dmq1.data |
456 | 0 | || !key->u.rsa.modulus.data || !key->u.rsa.exponent.data |
457 | 0 | || key->u.rsa.p.len != len || key->u.rsa.q.len != len |
458 | 0 | || key->u.rsa.iqmp.len != len || key->u.rsa.dmp1.len != len |
459 | 0 | || key->u.rsa.dmq1.len != len || key->u.rsa.modulus.len != 2*len |
460 | 0 | || key->u.rsa.exponent.len > len || key->u.rsa.exponent.len == 0)) |
461 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
462 | 0 | if (key->algorithm == SC_ALGORITHM_GOSTR3410 && (!key->u.gostr3410.d.data |
463 | 0 | || key->u.gostr3410.d.len != len)) |
464 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
465 | 0 | buf = calloc(1, buf_len); |
466 | 0 | if (!buf) |
467 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); |
468 | 0 | assert(key_len <= buf_len); |
469 | 0 | if (key->algorithm == SC_ALGORITHM_RSA) |
470 | 0 | { |
471 | | /* p */ |
472 | 0 | for (i = 0; i < len; ++i) |
473 | 0 | buf[i] = key->u.rsa.p.data[len - 1 - i]; |
474 | | /* q */ |
475 | 0 | for (i = 0; i < len; ++i) |
476 | 0 | buf[len + 4 + i] = key->u.rsa.q.data[len - 1 - i]; |
477 | | /* iqmp */ |
478 | 0 | for (i = 0; i < len; ++i) |
479 | 0 | buf[len + 4 + len + 4 + i] = key->u.rsa.iqmp.data[len - 1 - i]; |
480 | | /* dmp1 */ |
481 | 0 | for (i = 0; i < len; ++i) |
482 | 0 | buf[len + 4 + len + 4 + len + i] = |
483 | 0 | key->u.rsa.dmp1.data[len - 1 - i]; |
484 | | /* dmq1 */ |
485 | 0 | for (i = 0; i < len; ++i) |
486 | 0 | buf[len * 4 + 8 + i] = key->u.rsa.dmq1.data[len - 1 - i]; |
487 | 0 | } |
488 | 0 | else |
489 | 0 | { |
490 | | /* d */ |
491 | 0 | for (i = 0; i < len; ++i) |
492 | 0 | buf[i] = key->u.gostr3410.d.data[len - 1 - i]; |
493 | 0 | } |
494 | 0 | path = key_info->path; |
495 | 0 | r = sc_select_file(card, &path, NULL); |
496 | 0 | if (r == SC_SUCCESS) |
497 | 0 | r = sc_change_reference_data(card, 0, 0, NULL, 0, buf, key_len, NULL); |
498 | 0 | assert(buf); |
499 | 0 | sc_mem_clear(buf, key_len); |
500 | | /* store public key */ |
501 | 0 | if (key->algorithm == SC_ALGORITHM_RSA) |
502 | 0 | key_len = len * 3; |
503 | 0 | else |
504 | 0 | goto end; |
505 | 0 | assert(key_len <= buf_len); |
506 | 0 | if (key->algorithm == SC_ALGORITHM_RSA) |
507 | 0 | { |
508 | | /* modulus */ |
509 | 0 | for (i = 0; i < 2*len; ++i) |
510 | 0 | buf[i] = key->u.rsa.modulus.data[2*len - 1 - i]; |
511 | | /* exponent */ |
512 | 0 | for (i = 0; i < key->u.rsa.exponent.len && i < len; ++i) |
513 | 0 | buf[2 * len + i] = key->u.rsa.exponent.data[ |
514 | 0 | key->u.rsa.exponent.len - 1 - i]; |
515 | 0 | } |
516 | 0 | if (r == SC_SUCCESS) |
517 | 0 | { |
518 | 0 | r = sc_profile_get_file(profile, "PuKey-DF", &pukey_df); |
519 | 0 | if (r == SC_SUCCESS) |
520 | 0 | { |
521 | 0 | assert(pukey_df); |
522 | 0 | path = pukey_df->path; |
523 | 0 | r = sc_append_file_id(&path, key_info->key_reference); |
524 | 0 | sc_file_free(pukey_df); |
525 | 0 | } |
526 | 0 | else if (card->ctx->debug >= 2) |
527 | 0 | sc_log(card->ctx, "%s\n", "Get PuKey-DF info failed"); |
528 | 0 | } |
529 | 0 | if (r == SC_SUCCESS) |
530 | 0 | { |
531 | 0 | r = sc_select_file(card, &path, NULL); |
532 | 0 | if (r == SC_SUCCESS) |
533 | 0 | r = sc_change_reference_data(card, 0, 0, NULL, 0, |
534 | 0 | buf, key_len, NULL); |
535 | 0 | if (r && card->ctx->debug >= 2) |
536 | 0 | sc_log(card->ctx, "%s\n", "Store public key failed"); |
537 | 0 | } |
538 | 0 | end: |
539 | 0 | assert(buf); |
540 | 0 | free(buf); |
541 | 0 | LOG_FUNC_RETURN(card->ctx, r); |
542 | 0 | } |
543 | | |
544 | | /* |
545 | | * Generate key |
546 | | */ |
547 | | static int rtecp_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, |
548 | | sc_pkcs15_object_t *obj, sc_pkcs15_pubkey_t *pubkey) |
549 | 0 | { |
550 | 0 | sc_context_t *ctx; |
551 | 0 | sc_pkcs15_prkey_info_t *key_info; |
552 | 0 | sc_rtecp_genkey_data_t data; |
553 | 0 | int r; |
554 | |
|
555 | 0 | if (!profile || !p15card || !p15card->card || !p15card->card->ctx |
556 | 0 | || !obj || !obj->data || !pubkey) |
557 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
558 | | |
559 | 0 | ctx = p15card->card->ctx; |
560 | 0 | SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); |
561 | 0 | switch (obj->type) |
562 | 0 | { |
563 | 0 | case SC_PKCS15_TYPE_PRKEY_RSA: |
564 | 0 | data.type = SC_ALGORITHM_RSA; |
565 | 0 | break; |
566 | 0 | case SC_PKCS15_TYPE_PRKEY_GOSTR3410: |
567 | 0 | data.type = SC_ALGORITHM_GOSTR3410; |
568 | 0 | break; |
569 | 0 | default: |
570 | 0 | return SC_ERROR_NOT_SUPPORTED; |
571 | 0 | } |
572 | 0 | key_info = (sc_pkcs15_prkey_info_t *)obj->data; |
573 | 0 | assert(key_info); |
574 | 0 | data.key_id = key_info->key_reference; |
575 | 0 | assert(data.key_id != 0); |
576 | 0 | switch (data.type) |
577 | 0 | { |
578 | 0 | case SC_ALGORITHM_RSA: |
579 | 0 | assert(key_info->modulus_length % 128 == 0); |
580 | 0 | data.u.rsa.modulus_len = key_info->modulus_length / 8; |
581 | 0 | data.u.rsa.modulus = calloc(1, data.u.rsa.modulus_len); |
582 | 0 | data.u.rsa.exponent_len = key_info->modulus_length / 8 / 2; |
583 | 0 | data.u.rsa.exponent = calloc(1, data.u.rsa.exponent_len); |
584 | 0 | if (!data.u.rsa.modulus || !data.u.rsa.exponent) |
585 | 0 | { |
586 | 0 | free(data.u.rsa.modulus); |
587 | 0 | free(data.u.rsa.exponent); |
588 | 0 | LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); |
589 | 0 | } |
590 | 0 | break; |
591 | 0 | case SC_ALGORITHM_GOSTR3410: |
592 | 0 | assert(key_info->modulus_length == SC_PKCS15_GOSTR3410_KEYSIZE); |
593 | 0 | data.u.gostr3410.xy_len = key_info->modulus_length / 8 * 2; |
594 | 0 | data.u.gostr3410.xy = calloc(1, data.u.gostr3410.xy_len); |
595 | 0 | if (!data.u.gostr3410.xy) |
596 | 0 | { |
597 | 0 | free(data.u.gostr3410.xy); |
598 | 0 | LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); |
599 | 0 | } |
600 | 0 | break; |
601 | 0 | default: |
602 | 0 | assert(0); |
603 | 0 | } |
604 | 0 | r = sc_card_ctl(p15card->card, SC_CARDCTL_RTECP_GENERATE_KEY, &data); |
605 | 0 | if (r == SC_SUCCESS) |
606 | 0 | { |
607 | 0 | assert(pubkey); |
608 | 0 | pubkey->algorithm = data.type; |
609 | 0 | switch (data.type) |
610 | 0 | { |
611 | 0 | case SC_ALGORITHM_RSA: |
612 | 0 | pubkey->u.rsa.modulus.data = data.u.rsa.modulus; |
613 | 0 | pubkey->u.rsa.modulus.len = data.u.rsa.modulus_len; |
614 | 0 | pubkey->u.rsa.exponent.data = data.u.rsa.exponent; |
615 | 0 | pubkey->u.rsa.exponent.len = data.u.rsa.exponent_len; |
616 | 0 | break; |
617 | 0 | case SC_ALGORITHM_GOSTR3410: |
618 | 0 | pubkey->u.gostr3410.xy.data = data.u.gostr3410.xy; |
619 | 0 | pubkey->u.gostr3410.xy.len = data.u.gostr3410.xy_len; |
620 | 0 | break; |
621 | 0 | } |
622 | 0 | } |
623 | 0 | LOG_FUNC_RETURN(ctx, r); |
624 | 0 | } |
625 | | |
626 | | /* |
627 | | * Finalize card |
628 | | * Ends the initialization phase of the smart card/token |
629 | | */ |
630 | | static int rtecp_finalize(sc_card_t *card) |
631 | 0 | { |
632 | 0 | if (!card) |
633 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
634 | 0 | return sc_card_ctl(card, SC_CARDCTL_RTECP_INIT_END, NULL); |
635 | 0 | } |
636 | | |
637 | | |
638 | | /* |
639 | | * Delete object |
640 | | * |
641 | | * Applied to private key: used to delete public part internal file |
642 | | */ |
643 | | static int rtecp_delete_object(struct sc_profile *profile, struct sc_pkcs15_card *p15card, |
644 | | struct sc_pkcs15_object *obj, const struct sc_path *path) |
645 | 0 | { |
646 | 0 | sc_context_t *ctx; |
647 | 0 | sc_file_t *df; |
648 | 0 | sc_path_t pubkey_path; |
649 | 0 | int key_ref; |
650 | 0 | int r; |
651 | |
|
652 | 0 | if (!profile || !p15card || !p15card->card || !p15card->card->ctx) |
653 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
654 | | |
655 | 0 | ctx = p15card->card->ctx; |
656 | 0 | LOG_FUNC_CALLED(ctx); |
657 | 0 | sc_log(ctx, "delete object: type %X, path %s", obj->type, sc_print_path(path)); |
658 | |
|
659 | 0 | if ((obj->type & SC_PKCS15_TYPE_CLASS_MASK) != SC_PKCS15_TYPE_PRKEY) |
660 | 0 | LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); |
661 | | |
662 | 0 | key_ref = ((struct sc_pkcs15_prkey_info *)obj->data)->key_reference; |
663 | 0 | sc_log(ctx, "key reference %04i", key_ref); |
664 | |
|
665 | 0 | r = sc_profile_get_file(profile, "PuKey-DF", &df); |
666 | 0 | LOG_TEST_RET(ctx, r, "Get PuKey-DF info failed"); |
667 | 0 | pubkey_path = df->path; |
668 | 0 | sc_file_free(df); |
669 | |
|
670 | 0 | r = sc_append_file_id(&pubkey_path, key_ref); |
671 | 0 | LOG_TEST_RET(ctx, r, "Append ID to file failed"); |
672 | | |
673 | 0 | sc_log(ctx, "delete pubkey file %s", sc_print_path(&pubkey_path)); |
674 | 0 | r = sc_pkcs15init_delete_by_path(profile, p15card, &pubkey_path); |
675 | 0 | if (r && r != SC_ERROR_FILE_NOT_FOUND) |
676 | 0 | LOG_FUNC_RETURN(ctx, r); |
677 | | |
678 | 0 | LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); |
679 | 0 | } |
680 | | |
681 | | |
682 | | static struct sc_pkcs15init_operations sc_pkcs15init_rtecp_operations = { |
683 | | rtecp_erase, /* erase_card */ |
684 | | rtecp_init, /* init_card */ |
685 | | rtecp_create_dir, /* create_dir */ |
686 | | NULL, /* create_domain */ |
687 | | rtecp_select_pin_reference, /* select_pin_reference */ |
688 | | rtecp_create_pin, /* create_pin */ |
689 | | rtecp_select_key_reference, /* select_key_reference */ |
690 | | rtecp_create_key, /* create_key */ |
691 | | rtecp_store_key, /* store_key */ |
692 | | rtecp_generate_key, /* generate_key */ |
693 | | NULL, /* encode_private_key */ |
694 | | NULL, /* encode_public_key */ |
695 | | rtecp_finalize, /* finalize_card */ |
696 | | rtecp_delete_object, /* delete_object */ |
697 | | NULL, NULL, NULL, NULL, NULL, /* pkcs15init emulation */ |
698 | | NULL /* sanity_check */ |
699 | | }; |
700 | | |
701 | | struct sc_pkcs15init_operations * sc_pkcs15init_get_rtecp_ops(void) |
702 | 9 | { |
703 | 9 | return &sc_pkcs15init_rtecp_operations; |
704 | 9 | } |
705 | | |