/src/opensc/src/libopensc/iasecc-sdo.c
Line | Count | Source |
1 | | /* |
2 | | * iasecc-sdo.c: library to manipulate the Security Data Objects (SDO) |
3 | | * used by IAS/ECC card support. |
4 | | * |
5 | | * Copyright (C) 2010 Viktor Tarasov <vtarasov@opentrust.com> |
6 | | * OpenTrust <www.opentrust.com> |
7 | | * |
8 | | * This library is free software; you can redistribute it and/or |
9 | | * modify it under the terms of the GNU Lesser General Public |
10 | | * License as published by the Free Software Foundation; either |
11 | | * version 2.1 of the License, or (at your option) any later version. |
12 | | * |
13 | | * This library is distributed in the hope that it will be useful, |
14 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | | * Lesser General Public License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU Lesser General Public |
19 | | * License along with this library; if not, write to the Free Software |
20 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
21 | | */ |
22 | | |
23 | | #ifdef HAVE_CONFIG_H |
24 | | #include <config.h> |
25 | | #endif |
26 | | |
27 | | #ifdef ENABLE_OPENSSL /* empty file without openssl */ |
28 | | |
29 | | #include <string.h> |
30 | | #include <stdlib.h> |
31 | | |
32 | | #include "internal.h" |
33 | | #include "asn1.h" |
34 | | #include "cardctl.h" |
35 | | |
36 | | #include "iasecc.h" |
37 | | #include "iasecc-sdo.h" |
38 | | |
39 | | static int iasecc_parse_size(unsigned char *data, size_t data_len, size_t *out); |
40 | | |
41 | | |
42 | | static int |
43 | | iasecc_parse_acls(struct sc_card *card, struct iasecc_sdo_docp *docp, int flags) |
44 | 224 | { |
45 | 224 | struct sc_context *ctx = card->ctx; |
46 | 224 | struct iasecc_extended_tlv *acls = &docp->acls_contact; |
47 | 224 | int ii; |
48 | 224 | size_t offs; |
49 | 224 | unsigned char mask = 0x40; |
50 | | |
51 | 224 | if (flags) |
52 | 0 | acls = &docp->acls_contactless; |
53 | | |
54 | 224 | if (!acls->size) |
55 | 224 | LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); |
56 | | |
57 | 188 | docp->amb = *(acls->value + 0); |
58 | 188 | memset(docp->scbs, 0xFF, sizeof(docp->scbs)); |
59 | 1.41k | for (ii=0, offs = 1; ii<7; ii++, mask >>= 1) |
60 | 1.24k | if (mask & docp->amb) { |
61 | 436 | if (offs >= acls->size) { |
62 | 20 | LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); |
63 | 20 | } |
64 | 416 | docp->scbs[ii] = *(acls->value + offs++); |
65 | 416 | } |
66 | | |
67 | 168 | sc_log(ctx, "iasecc_parse_docp() SCBs %02X:%02X:%02X:%02X:%02X:%02X:%02X", |
68 | 168 | docp->scbs[0],docp->scbs[1],docp->scbs[2],docp->scbs[3], |
69 | 168 | docp->scbs[4],docp->scbs[5],docp->scbs[6]); |
70 | 168 | LOG_FUNC_RETURN(ctx, SC_SUCCESS); |
71 | 168 | } |
72 | | |
73 | | |
74 | | int |
75 | | iasecc_sdo_convert_acl(struct sc_card *card, struct iasecc_sdo *sdo, |
76 | | unsigned char op, unsigned *out_method, unsigned *out_ref) |
77 | 0 | { |
78 | 0 | struct sc_context *ctx = card->ctx; |
79 | 0 | struct acl_op { |
80 | 0 | unsigned char op; |
81 | 0 | unsigned char mask; |
82 | 0 | } ops[] = { |
83 | 0 | {SC_AC_OP_PSO_COMPUTE_SIGNATURE,IASECC_ACL_PSO_SIGNATURE}, |
84 | 0 | {SC_AC_OP_INTERNAL_AUTHENTICATE,IASECC_ACL_INTERNAL_AUTHENTICATE}, |
85 | 0 | {SC_AC_OP_PSO_DECRYPT, IASECC_ACL_PSO_DECIPHER}, |
86 | 0 | {SC_AC_OP_GENERATE, IASECC_ACL_GENERATE_KEY}, |
87 | 0 | {SC_AC_OP_UPDATE, IASECC_ACL_PUT_DATA}, |
88 | 0 | {SC_AC_OP_READ, IASECC_ACL_GET_DATA}, |
89 | 0 | {0x00, 0x00} |
90 | 0 | }; |
91 | 0 | unsigned char mask = 0x80, op_mask = 0; |
92 | 0 | int ii; |
93 | |
|
94 | 0 | LOG_FUNC_CALLED(ctx); |
95 | |
|
96 | 0 | for (ii=0; ops[ii].mask; ii++) { |
97 | 0 | if (op == ops[ii].op) { |
98 | 0 | op_mask = ops[ii].mask; |
99 | 0 | break; |
100 | 0 | } |
101 | 0 | } |
102 | 0 | if (op_mask == 0) |
103 | 0 | LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); |
104 | | |
105 | 0 | sc_log(ctx, "OP:%i, mask:0x%X", op, op_mask); |
106 | 0 | sc_log(ctx, "AMB:%X, scbs:%s", sdo->docp.amb, sc_dump_hex(sdo->docp.scbs, IASECC_MAX_SCBS)); |
107 | 0 | sc_log(ctx, "docp.acls_contact:%s", sc_dump_hex(sdo->docp.acls_contact.value, sdo->docp.acls_contact.size)); |
108 | |
|
109 | 0 | if (!sdo->docp.amb && sdo->docp.acls_contact.size) { |
110 | 0 | int rv = iasecc_parse_acls(card, &sdo->docp, 0); |
111 | 0 | LOG_TEST_RET(ctx, rv, "Cannot parse ACLs in DOCP"); |
112 | 0 | } |
113 | | |
114 | 0 | *out_method = SC_AC_NEVER; |
115 | 0 | *out_ref = SC_AC_NEVER; |
116 | |
|
117 | 0 | for (ii=0; ii<7; ii++) { |
118 | 0 | mask >>= 1; |
119 | 0 | if (sdo->docp.amb & mask) { |
120 | 0 | if (op_mask == mask) { |
121 | 0 | unsigned char scb = sdo->docp.scbs[ii]; |
122 | 0 | sc_log(ctx, "ii:%i, scb:0x%X", ii, scb); |
123 | |
|
124 | 0 | *out_ref = scb & 0x0F; |
125 | 0 | if (scb == 0) |
126 | 0 | *out_method = SC_AC_NONE; |
127 | 0 | else if (scb == 0xFF) |
128 | 0 | *out_method = SC_AC_NEVER; |
129 | 0 | else if ((scb & IASECC_SCB_METHOD_MASK) == IASECC_SCB_METHOD_USER_AUTH) |
130 | 0 | *out_method = SC_AC_SEN; |
131 | 0 | else if ((scb & IASECC_SCB_METHOD_MASK) == IASECC_SCB_METHOD_EXT_AUTH) |
132 | 0 | *out_method = SC_AC_AUT; |
133 | 0 | else if ((scb & IASECC_SCB_METHOD_MASK) == IASECC_SCB_METHOD_SM) |
134 | 0 | *out_method = SC_AC_PRO; |
135 | 0 | else |
136 | 0 | *out_method = SC_AC_SCB, *out_ref = scb; |
137 | |
|
138 | 0 | break; |
139 | 0 | } |
140 | 0 | } |
141 | 0 | } |
142 | |
|
143 | 0 | sc_log(ctx, "returns method %X; ref %X", *out_method, *out_ref); |
144 | 0 | LOG_FUNC_RETURN(ctx, SC_SUCCESS); |
145 | 0 | } |
146 | | |
147 | | |
148 | | void |
149 | | iasecc_sdo_free_fields(struct sc_card *card, struct iasecc_sdo *sdo) |
150 | 1.26k | { |
151 | 1.26k | free(sdo->docp.tries_maximum.value); |
152 | 1.26k | free(sdo->docp.tries_remaining.value); |
153 | 1.26k | free(sdo->docp.usage_remaining.value); |
154 | 1.26k | free(sdo->docp.non_repudiation.value); |
155 | 1.26k | free(sdo->docp.acls_contact.value); |
156 | 1.26k | free(sdo->docp.acls_contactless.value); |
157 | 1.26k | free(sdo->docp.size.value); |
158 | 1.26k | free(sdo->docp.name.value); |
159 | 1.26k | free(sdo->docp.issuer_data.value); |
160 | | |
161 | 1.26k | if (sdo->sdo_class == IASECC_SDO_CLASS_RSA_PUBLIC) { |
162 | 2 | free(sdo->data.pub_key.n.value); |
163 | 2 | free(sdo->data.pub_key.e.value); |
164 | 2 | free(sdo->data.pub_key.compulsory.value); |
165 | 2 | free(sdo->data.pub_key.chr.value); |
166 | 2 | free(sdo->data.pub_key.cha.value); |
167 | 2 | } |
168 | 1.26k | else if (sdo->sdo_class == IASECC_SDO_CLASS_RSA_PRIVATE) { |
169 | 4 | free(sdo->data.prv_key.p.value); |
170 | 4 | free(sdo->data.prv_key.q.value); |
171 | 4 | free(sdo->data.prv_key.iqmp.value); |
172 | 4 | free(sdo->data.prv_key.dmp1.value); |
173 | 4 | free(sdo->data.prv_key.dmq1.value); |
174 | 4 | free(sdo->data.prv_key.compulsory.value); |
175 | 4 | } |
176 | 1.26k | else if (sdo->sdo_class == IASECC_SDO_CLASS_CHV) { |
177 | 625 | free(sdo->data.chv.size_max.value); |
178 | 625 | free(sdo->data.chv.size_min.value); |
179 | 625 | free(sdo->data.chv.value.value); |
180 | 625 | } |
181 | | /* invalidate all the other members too */ |
182 | 1.26k | memset(sdo, 0, sizeof(struct iasecc_sdo)); |
183 | 1.26k | } |
184 | | |
185 | | |
186 | | void |
187 | | iasecc_sdo_free(struct sc_card *card, struct iasecc_sdo *sdo) |
188 | 6 | { |
189 | 6 | iasecc_sdo_free_fields(card, sdo); |
190 | 6 | free(sdo); |
191 | 6 | } |
192 | | |
193 | | |
194 | | static int |
195 | | iasecc_crt_parse(struct sc_card *card, unsigned char *data, size_t data_len, struct iasecc_se_info *se) |
196 | 2.97k | { |
197 | 2.97k | struct sc_context *ctx = card->ctx; |
198 | 2.97k | struct sc_crt crt; |
199 | 2.97k | int ii, offs, len, parsed_len = -1; |
200 | | |
201 | 2.97k | sc_log(ctx, "iasecc_crt_parse(0x%X) called", *data); |
202 | | |
203 | 2.97k | if (data_len < 2) |
204 | 2.97k | LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); |
205 | | |
206 | 2.96k | memset(&crt, 0, sizeof(crt)); |
207 | 2.96k | crt.tag = *(data + 0); |
208 | 2.96k | len = *(data + 1); |
209 | | |
210 | 3.83k | for(offs = 2; offs < len + 2; offs += 3) { |
211 | 1.03k | if ((size_t) offs + 2 >= data_len) |
212 | 1.03k | LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); |
213 | 1.01k | sc_log(ctx, "iasecc_crt_parse(0x%X) CRT %X -> %X", *data, *(data + offs), *(data + offs + 2)); |
214 | 1.01k | if (*(data + offs) == IASECC_CRT_TAG_USAGE) { |
215 | 425 | crt.usage = *(data + offs + 2); |
216 | 425 | } |
217 | 591 | else if (*(data + offs) == IASECC_CRT_TAG_REFERENCE) { |
218 | 350 | int nn_refs = sizeof(crt.refs) / sizeof(crt.refs[0]); |
219 | | |
220 | 1.19k | for (ii=0; ii<nn_refs && crt.refs[ii]; ii++) |
221 | 841 | ; |
222 | 350 | if (ii == nn_refs) |
223 | 350 | LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); |
224 | | |
225 | 342 | crt.refs[ii] = *(data + offs + 2); |
226 | 342 | } |
227 | 241 | else if (*(data + offs) == IASECC_CRT_TAG_ALGO) { |
228 | 106 | crt.algo = *(data + offs + 2); |
229 | 106 | } |
230 | 135 | else { |
231 | 135 | LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); |
232 | 135 | } |
233 | 1.01k | } |
234 | | |
235 | 12.4k | for (ii=0; ii<SC_MAX_CRTS_IN_SE; ii++) |
236 | 12.4k | if (!se->crts[ii].tag) |
237 | 2.79k | break; |
238 | | |
239 | 2.80k | if (ii==SC_MAX_CRTS_IN_SE) |
240 | 2.80k | LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "iasecc_crt_parse() error: too much CRTs in SE"); |
241 | | |
242 | 2.79k | memcpy(&se->crts[ii], &crt, sizeof(crt)); |
243 | 2.79k | parsed_len = len + 2; |
244 | 2.79k | LOG_FUNC_RETURN(ctx, parsed_len); |
245 | 2.79k | } |
246 | | |
247 | | |
248 | | int |
249 | | iasecc_se_get_crt(struct sc_card *card, struct iasecc_se_info *se, struct sc_crt *crt) |
250 | 79 | { |
251 | 79 | struct sc_context *ctx = card->ctx; |
252 | 79 | int ii; |
253 | | |
254 | 79 | LOG_FUNC_CALLED(ctx); |
255 | 79 | if (!se || !crt) |
256 | 79 | LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); |
257 | 79 | sc_log(ctx, "CRT search template: %X:%X:%X, refs %X:%X:...", |
258 | 79 | crt->tag, crt->algo, crt->usage, crt->refs[0], crt->refs[1]); |
259 | | |
260 | 592 | for (ii=0; ii<SC_MAX_CRTS_IN_SE && se->crts[ii].tag; ii++) { |
261 | 513 | if (crt->tag != se->crts[ii].tag) |
262 | 444 | continue; |
263 | 69 | if (crt->algo && crt->algo != se->crts[ii].algo) |
264 | 0 | continue; |
265 | 69 | if (crt->usage && crt->usage != se->crts[ii].usage) |
266 | 69 | continue; |
267 | 0 | if (crt->refs[0] && crt->refs[0] != se->crts[ii].refs[0]) |
268 | 0 | continue; |
269 | | |
270 | 0 | memcpy(crt, &se->crts[ii], sizeof(*crt)); |
271 | |
|
272 | 0 | sc_log(ctx, "iasecc_se_get_crt() found CRT with refs %X:%X:...", |
273 | 0 | se->crts[ii].refs[0], se->crts[ii].refs[1]); |
274 | 0 | LOG_FUNC_RETURN(ctx, SC_SUCCESS); |
275 | 0 | } |
276 | | |
277 | 79 | sc_log(ctx, "iasecc_se_get_crt() CRT is not found"); |
278 | 79 | return SC_ERROR_DATA_OBJECT_NOT_FOUND; |
279 | 79 | } |
280 | | |
281 | | |
282 | | int |
283 | | iasecc_se_get_crt_by_usage(struct sc_card *card, struct iasecc_se_info *se, unsigned char tag, |
284 | | unsigned char usage, struct sc_crt *crt) |
285 | 0 | { |
286 | 0 | struct sc_context *ctx = card->ctx; |
287 | 0 | int ii; |
288 | |
|
289 | 0 | LOG_FUNC_CALLED(ctx); |
290 | 0 | if (!se || !crt || !tag || !usage) |
291 | 0 | LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); |
292 | 0 | sc_log(ctx, "CRT search template with TAG:0x%X and UQB:0x%X", tag, usage); |
293 | |
|
294 | 0 | for (ii=0; ii<SC_MAX_CRTS_IN_SE && se->crts[ii].tag; ii++) { |
295 | 0 | if (tag != se->crts[ii].tag) |
296 | 0 | continue; |
297 | 0 | if (usage != se->crts[ii].usage) |
298 | 0 | continue; |
299 | | |
300 | 0 | memcpy(crt, &se->crts[ii], sizeof(*crt)); |
301 | |
|
302 | 0 | sc_log(ctx, "iasecc_se_get_crt() found CRT with refs %X:%X:...", crt->refs[0], crt->refs[1]); |
303 | 0 | LOG_FUNC_RETURN(ctx, SC_SUCCESS); |
304 | 0 | } |
305 | | |
306 | 0 | sc_log(ctx, "iasecc_se_get_crt() CRT is not found"); |
307 | 0 | LOG_FUNC_RETURN(ctx, SC_ERROR_DATA_OBJECT_NOT_FOUND); |
308 | 0 | } |
309 | | |
310 | | |
311 | | int |
312 | | iasecc_se_parse(struct sc_card *card, unsigned char *data, size_t data_len, struct iasecc_se_info *se) |
313 | 658 | { |
314 | 658 | struct sc_context *ctx = card->ctx; |
315 | 658 | size_t size, offs; |
316 | 658 | int size_size; |
317 | 658 | int rv; |
318 | | |
319 | 658 | LOG_FUNC_CALLED(ctx); |
320 | 658 | sc_log(ctx, "data_len %"SC_FORMAT_LEN_SIZE_T"u", data_len); |
321 | | |
322 | 658 | if (data_len < 1) |
323 | 658 | LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); |
324 | | |
325 | 627 | if (*data == IASECC_SDO_TEMPLATE_TAG) { |
326 | 245 | size_size = iasecc_parse_size(data + 1, data_len - 1, &size); |
327 | 245 | LOG_TEST_RET(ctx, size_size, "parse error: invalid size data of IASECC_SDO_TEMPLATE"); |
328 | | |
329 | 213 | if (data_len < size + size_size + 1) |
330 | 213 | LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); |
331 | | |
332 | 175 | data += size_size + 1; |
333 | 175 | data_len = size; |
334 | 175 | sc_log(ctx, |
335 | 175 | "IASECC_SDO_TEMPLATE: size %"SC_FORMAT_LEN_SIZE_T"u, size_size %d", |
336 | 175 | size, size_size); |
337 | | |
338 | 175 | if (data_len < 3) |
339 | 175 | LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); |
340 | | |
341 | 127 | if (*data != IASECC_SDO_TAG_HEADER) |
342 | 127 | LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); |
343 | | |
344 | 78 | if ((*(data + 1) & 0x7F) != IASECC_SDO_CLASS_SE) |
345 | 78 | LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); |
346 | | |
347 | 49 | size_size = iasecc_parse_size(data + 3, data_len - 3, &size); |
348 | 49 | LOG_TEST_RET(ctx, size_size, "parse error: invalid SDO SE data size"); |
349 | | |
350 | 37 | if (data_len != size + size_size + 3) |
351 | 37 | LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: invalid SDO SE data size"); |
352 | | |
353 | 17 | data += 3 + size_size; |
354 | 17 | data_len = size; |
355 | 17 | sc_log(ctx, |
356 | 17 | "IASECC_SDO_TEMPLATE SE: size %"SC_FORMAT_LEN_SIZE_T"u, size_size %d", |
357 | 17 | size, size_size); |
358 | 17 | } |
359 | | |
360 | 399 | if (*data != IASECC_SDO_CLASS_SE) { |
361 | 57 | sc_log(ctx, |
362 | 57 | "Invalid SE tag 0x%X; data length %"SC_FORMAT_LEN_SIZE_T"u", |
363 | 57 | *data, data_len); |
364 | 57 | LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); |
365 | 57 | } |
366 | | |
367 | 342 | size_size = iasecc_parse_size(data + 1, data_len - 1, &size); |
368 | 342 | LOG_TEST_RET(ctx, size_size, "parse error: invalid size data"); |
369 | | |
370 | 317 | if (data_len != size + size_size + 1) |
371 | 317 | LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: invalid SE data size"); |
372 | | |
373 | 260 | offs = 1 + size_size; |
374 | 3.05k | for (; offs < data_len;) { |
375 | 2.97k | rv = iasecc_crt_parse(card, data + offs, data_len - offs, se); |
376 | 2.97k | LOG_TEST_RET(ctx, rv, "parse error: invalid SE data"); |
377 | | |
378 | 2.79k | offs += rv; |
379 | 2.79k | } |
380 | | |
381 | 76 | if (offs != data_len) |
382 | 76 | LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: not totally parsed"); |
383 | | |
384 | 76 | LOG_FUNC_RETURN(ctx, SC_SUCCESS); |
385 | 76 | } |
386 | | |
387 | | |
388 | | static int |
389 | | iasecc_parse_size(unsigned char *data, size_t data_len, size_t *out) |
390 | 4.81k | { |
391 | 4.81k | if (data_len > 0 && *data < 0x80) { |
392 | 4.14k | *out = *data; |
393 | 4.14k | return 1; |
394 | 4.14k | } |
395 | 674 | else if (data_len > 1 && *data == 0x81) { |
396 | 302 | *out = *(data + 1); |
397 | 302 | return 2; |
398 | 302 | } |
399 | 372 | else if (data_len > 2 && *data == 0x82) { |
400 | 202 | *out = *(data + 1) * 0x100 + *(data + 2); |
401 | 202 | return 3; |
402 | 202 | } |
403 | | |
404 | 170 | return SC_ERROR_INVALID_DATA; |
405 | 4.81k | } |
406 | | |
407 | | |
408 | | static int |
409 | | iasecc_parse_get_tlv(struct sc_card *card, unsigned char *data, size_t data_len, struct iasecc_extended_tlv *tlv) |
410 | 3.27k | { |
411 | 3.27k | struct sc_context *ctx = card->ctx; |
412 | 3.27k | int size_len, tag_len; |
413 | | |
414 | 3.27k | memset(tlv, 0, sizeof(*tlv)); |
415 | 3.27k | sc_log(ctx, "iasecc_parse_get_tlv() called for tag 0x%X", *data); |
416 | 3.27k | if (data_len < 1) |
417 | 3.27k | LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); |
418 | 3.27k | if ((*data == 0x7F) || (*data == 0x5F)) { |
419 | 312 | if (data_len < 2) |
420 | 312 | LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); |
421 | 306 | tlv->tag = *data * 0x100 + *(data + 1); |
422 | 306 | tag_len = 2; |
423 | 306 | } |
424 | 2.95k | else { |
425 | 2.95k | tlv->tag = *data; |
426 | 2.95k | tag_len = 1; |
427 | 2.95k | } |
428 | | |
429 | 3.26k | sc_log(ctx, "iasecc_parse_get_tlv() tlv->tag 0x%X", tlv->tag); |
430 | 3.26k | size_len = iasecc_parse_size(data + tag_len, data_len - tag_len, &tlv->size); |
431 | 3.26k | LOG_TEST_RET(ctx, size_len, "parse error: invalid size data"); |
432 | 3.19k | if (tag_len + size_len + tlv->size > data_len) { |
433 | 113 | LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); |
434 | 113 | } |
435 | | |
436 | 3.08k | tlv->value = calloc(1, tlv->size); |
437 | 3.08k | if (!tlv->value) |
438 | 3.08k | LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); |
439 | 3.08k | memcpy(tlv->value, data + size_len + tag_len, tlv->size); |
440 | | |
441 | 3.08k | tlv->on_card = 1; |
442 | | |
443 | 3.08k | sc_log(ctx, |
444 | 3.08k | "iasecc_parse_get_tlv() parsed %"SC_FORMAT_LEN_SIZE_T"u bytes", |
445 | 3.08k | tag_len + size_len + tlv->size); |
446 | 3.08k | return (int)(tag_len + size_len + tlv->size); |
447 | 3.08k | } |
448 | | |
449 | | |
450 | | static int |
451 | | iasecc_parse_chv(struct sc_card *card, unsigned char *data, size_t data_len, struct iasecc_sdo_chv *chv) |
452 | 154 | { |
453 | 154 | struct sc_context *ctx = card->ctx; |
454 | 154 | size_t offs = 0; |
455 | 154 | int rv; |
456 | | |
457 | 154 | LOG_FUNC_CALLED(ctx); |
458 | 707 | while(offs < data_len) { |
459 | 642 | struct iasecc_extended_tlv tlv; |
460 | | |
461 | 642 | rv = iasecc_parse_get_tlv(card, data + offs, data_len - offs, &tlv); |
462 | 642 | LOG_TEST_RET(ctx, rv, "iasecc_parse_chv() get and parse TLV error"); |
463 | | |
464 | 598 | sc_log(ctx, |
465 | 598 | "iasecc_parse_chv() get and parse TLV returned %i; tag %X; size %"SC_FORMAT_LEN_SIZE_T"u", |
466 | 598 | rv, tlv.tag, tlv.size); |
467 | | |
468 | 598 | if (tlv.tag == IASECC_SDO_CHV_TAG_SIZE_MAX) { |
469 | 117 | free(chv->size_max.value); |
470 | 117 | chv->size_max = tlv; |
471 | 481 | } else if (tlv.tag == IASECC_SDO_CHV_TAG_SIZE_MIN) { |
472 | 303 | free(chv->size_min.value); |
473 | 303 | chv->size_min = tlv; |
474 | 303 | } else if (tlv.tag == IASECC_SDO_CHV_TAG_VALUE) { |
475 | 133 | free(chv->value.value); |
476 | 133 | chv->value = tlv; |
477 | 133 | } else { |
478 | 45 | free(tlv.value); |
479 | 45 | LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "parse error: non CHV SDO tag"); |
480 | 45 | } |
481 | | |
482 | 553 | offs += rv; |
483 | 553 | } |
484 | | |
485 | 65 | LOG_FUNC_RETURN(ctx, SC_SUCCESS); |
486 | 65 | } |
487 | | |
488 | | |
489 | | static int |
490 | | iasecc_parse_prvkey(struct sc_card *card, unsigned char *data, size_t data_len, struct iasecc_sdo_prvkey *prvkey) |
491 | 0 | { |
492 | 0 | struct sc_context *ctx = card->ctx; |
493 | 0 | size_t offs = 0; |
494 | 0 | int rv; |
495 | |
|
496 | 0 | LOG_FUNC_CALLED(ctx); |
497 | 0 | while(offs < data_len) { |
498 | 0 | struct iasecc_extended_tlv tlv; |
499 | |
|
500 | 0 | rv = iasecc_parse_get_tlv(card, data + offs, data_len - offs, &tlv); |
501 | 0 | LOG_TEST_RET(ctx, rv, "iasecc_parse_prvkey() get and parse TLV error"); |
502 | | |
503 | 0 | sc_log(ctx, |
504 | 0 | "iasecc_parse_prvkey() get and parse TLV returned %i; tag %X; size %"SC_FORMAT_LEN_SIZE_T"u", |
505 | 0 | rv, tlv.tag, tlv.size); |
506 | |
|
507 | 0 | if (tlv.tag == IASECC_SDO_PRVKEY_TAG_COMPULSORY) { |
508 | 0 | free(prvkey->compulsory.value); |
509 | 0 | prvkey->compulsory = tlv; |
510 | 0 | } else { |
511 | 0 | free(tlv.value); |
512 | 0 | LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "parse error: non PrvKey SDO tag"); |
513 | 0 | } |
514 | | |
515 | 0 | offs += rv; |
516 | 0 | } |
517 | | |
518 | 0 | LOG_FUNC_RETURN(ctx, SC_SUCCESS); |
519 | 0 | } |
520 | | |
521 | | |
522 | | static int |
523 | | iasecc_parse_pubkey(struct sc_card *card, unsigned char *data, size_t data_len, struct iasecc_sdo_pubkey *pubkey) |
524 | 0 | { |
525 | 0 | struct sc_context *ctx = card->ctx; |
526 | 0 | size_t offs = 0; |
527 | 0 | int rv; |
528 | |
|
529 | 0 | LOG_FUNC_CALLED(ctx); |
530 | 0 | while(offs < data_len) { |
531 | 0 | struct iasecc_extended_tlv tlv; |
532 | |
|
533 | 0 | rv = iasecc_parse_get_tlv(card, data + offs, data_len - offs, &tlv); |
534 | 0 | LOG_TEST_RET(ctx, rv, "iasecc_parse_pubkey() get and parse TLV error"); |
535 | | |
536 | 0 | sc_log(ctx, |
537 | 0 | "iasecc_parse_pubkey() get and parse TLV returned %i; tag %X; size %"SC_FORMAT_LEN_SIZE_T"u", |
538 | 0 | rv, tlv.tag, tlv.size); |
539 | |
|
540 | 0 | if (tlv.tag == IASECC_SDO_PUBKEY_TAG_N) { |
541 | 0 | free(pubkey->n.value); |
542 | 0 | pubkey->n = tlv; |
543 | 0 | } else if (tlv.tag == IASECC_SDO_PUBKEY_TAG_E) { |
544 | 0 | free(pubkey->e.value); |
545 | 0 | pubkey->e = tlv; |
546 | 0 | } else if (tlv.tag == IASECC_SDO_PUBKEY_TAG_CHR) { |
547 | 0 | free(pubkey->chr.value); |
548 | 0 | pubkey->chr = tlv; |
549 | 0 | } else if (tlv.tag == IASECC_SDO_PUBKEY_TAG_CHA) { |
550 | 0 | free(pubkey->cha.value); |
551 | 0 | pubkey->cha = tlv; |
552 | 0 | } else if (tlv.tag == IASECC_SDO_PUBKEY_TAG_COMPULSORY) { |
553 | 0 | free(pubkey->compulsory.value); |
554 | 0 | pubkey->compulsory = tlv; |
555 | 0 | } else { |
556 | 0 | free(tlv.value); |
557 | 0 | LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "parse error: non PubKey SDO tag"); |
558 | 0 | } |
559 | | |
560 | 0 | offs += rv; |
561 | 0 | } |
562 | | |
563 | 0 | LOG_FUNC_RETURN(ctx, SC_SUCCESS); |
564 | 0 | } |
565 | | |
566 | | |
567 | | static int |
568 | | iasecc_parse_keyset(struct sc_card *card, unsigned char *data, size_t data_len, struct iasecc_sdo_keyset *keyset) |
569 | 0 | { |
570 | 0 | struct sc_context *ctx = card->ctx; |
571 | 0 | size_t offs = 0; |
572 | 0 | int rv; |
573 | |
|
574 | 0 | LOG_FUNC_CALLED(ctx); |
575 | 0 | while(offs < data_len) { |
576 | 0 | struct iasecc_extended_tlv tlv; |
577 | |
|
578 | 0 | rv = iasecc_parse_get_tlv(card, data + offs, data_len - offs, &tlv); |
579 | 0 | LOG_TEST_RET(ctx, rv, "iasecc_parse_keyset() get and parse TLV error"); |
580 | | |
581 | 0 | sc_log(ctx, |
582 | 0 | "iasecc_parse_prvkey() get and parse TLV returned %i; tag %X; size %"SC_FORMAT_LEN_SIZE_T"u", |
583 | 0 | rv, tlv.tag, tlv.size); |
584 | |
|
585 | 0 | if (tlv.tag == IASECC_SDO_KEYSET_TAG_COMPULSORY) { |
586 | 0 | free(keyset->compulsory.value); |
587 | 0 | keyset->compulsory = tlv; |
588 | 0 | } else { |
589 | 0 | free(tlv.value); |
590 | 0 | LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "parse error: non KeySet SDO tag"); |
591 | 0 | } |
592 | | |
593 | 0 | offs += rv; |
594 | 0 | } |
595 | | |
596 | 0 | LOG_FUNC_RETURN(ctx, SC_SUCCESS); |
597 | 0 | } |
598 | | |
599 | | |
600 | | static int |
601 | | iasecc_parse_docp(struct sc_card *card, unsigned char *data, size_t data_len, struct iasecc_sdo *sdo) |
602 | 488 | { |
603 | 488 | struct sc_context *ctx = card->ctx; |
604 | 488 | size_t offs = 0; |
605 | 488 | int rv; |
606 | | |
607 | 488 | LOG_FUNC_CALLED(ctx); |
608 | 1.59k | while(offs < data_len) { |
609 | 1.36k | struct iasecc_extended_tlv tlv; |
610 | | |
611 | 1.36k | rv = iasecc_parse_get_tlv(card, data + offs, data_len - offs, &tlv); |
612 | 1.36k | LOG_TEST_RET(ctx, rv, "iasecc_parse_get_tlv() get and parse TLV error"); |
613 | | |
614 | 1.28k | sc_log(ctx, |
615 | 1.28k | "iasecc_parse_docp() parse_get_tlv returned %i; tag %X; size %"SC_FORMAT_LEN_SIZE_T"u", |
616 | 1.28k | rv, tlv.tag, tlv.size); |
617 | | |
618 | 1.28k | if (tlv.tag == IASECC_DOCP_TAG_ACLS) { |
619 | 128 | int _rv = iasecc_parse_docp(card, tlv.value, tlv.size, sdo); |
620 | 128 | free(tlv.value); |
621 | 128 | LOG_TEST_RET(ctx, _rv, "parse error: cannot parse DOCP"); |
622 | 128 | } |
623 | 1.15k | else if (tlv.tag == IASECC_DOCP_TAG_ACLS_CONTACT) { |
624 | 179 | free(sdo->docp.acls_contact.value); |
625 | 179 | sdo->docp.acls_contact = tlv; |
626 | 179 | } |
627 | 978 | else if (tlv.tag == IASECC_DOCP_TAG_ACLS_CONTACTLESS) { |
628 | 103 | free(sdo->docp.acls_contactless.value); |
629 | 103 | sdo->docp.acls_contactless = tlv; |
630 | 103 | } |
631 | 875 | else if (tlv.tag == IASECC_DOCP_TAG_SIZE) { |
632 | 125 | free(sdo->docp.size.value); |
633 | 125 | sdo->docp.size = tlv; |
634 | 125 | } |
635 | 750 | else if (tlv.tag == IASECC_DOCP_TAG_NAME) { |
636 | 116 | free(sdo->docp.name.value); |
637 | 116 | sdo->docp.name = tlv; |
638 | 116 | } |
639 | 634 | else if (tlv.tag == IASECC_DOCP_TAG_ISSUER_DATA) { |
640 | 72 | free(sdo->docp.issuer_data.value); |
641 | 72 | sdo->docp.issuer_data = tlv; |
642 | 72 | } |
643 | 562 | else if (tlv.tag == IASECC_DOCP_TAG_NON_REPUDIATION) { |
644 | 69 | free(sdo->docp.non_repudiation.value); |
645 | 69 | sdo->docp.non_repudiation = tlv; |
646 | 69 | } |
647 | 493 | else if (tlv.tag == IASECC_DOCP_TAG_USAGE_REMAINING) { |
648 | 93 | free(sdo->docp.usage_remaining.value); |
649 | 93 | sdo->docp.usage_remaining = tlv; |
650 | 93 | } |
651 | 400 | else if (tlv.tag == IASECC_DOCP_TAG_TRIES_MAXIMUM) { |
652 | 107 | free(sdo->docp.tries_maximum.value); |
653 | 107 | sdo->docp.tries_maximum = tlv; |
654 | 107 | } |
655 | 293 | else if (tlv.tag == IASECC_DOCP_TAG_TRIES_REMAINING) { |
656 | 151 | free(sdo->docp.tries_remaining.value); |
657 | 151 | sdo->docp.tries_remaining = tlv; |
658 | 151 | } |
659 | 142 | else { |
660 | 142 | free(tlv.value); |
661 | 142 | LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "iasecc_parse_get_tlv() parse error: non DOCP tag"); |
662 | 142 | } |
663 | | |
664 | 1.10k | offs += rv; |
665 | 1.10k | } |
666 | | |
667 | 224 | rv = iasecc_parse_acls(card, &sdo->docp, 0); |
668 | 224 | LOG_TEST_RET(ctx, rv, "Cannot parse ACLs in DOCP"); |
669 | | |
670 | 168 | LOG_FUNC_RETURN(ctx, SC_SUCCESS); |
671 | 168 | } |
672 | | |
673 | | |
674 | | static int |
675 | | iasecc_sdo_parse_data(struct sc_card *card, unsigned char *data, size_t data_len, struct iasecc_sdo *sdo) |
676 | 1.26k | { |
677 | 1.26k | struct sc_context *ctx = card->ctx; |
678 | 1.26k | struct iasecc_extended_tlv tlv; |
679 | 1.26k | int tlv_size, rv; |
680 | | |
681 | 1.26k | LOG_FUNC_CALLED(ctx); |
682 | 1.26k | sc_log(ctx, "iasecc_sdo_parse_data() class %X; ref %X", sdo->sdo_class, sdo->sdo_ref); |
683 | | |
684 | 1.26k | tlv_size = iasecc_parse_get_tlv(card, data, data_len, &tlv); |
685 | 1.26k | LOG_TEST_RET(ctx, tlv_size, "parse error: get TLV"); |
686 | | |
687 | 1.19k | sc_log(ctx, "iasecc_sdo_parse_data() tlv.tag 0x%X", tlv.tag); |
688 | 1.19k | if (tlv.tag == IASECC_DOCP_TAG) { |
689 | 360 | sc_log(ctx, |
690 | 360 | "iasecc_sdo_parse_data() parse IASECC_DOCP_TAG: 0x%X; size %"SC_FORMAT_LEN_SIZE_T"u", |
691 | 360 | tlv.tag, tlv.size); |
692 | 360 | rv = iasecc_parse_docp(card, tlv.value, tlv.size, sdo); |
693 | 360 | sc_log(ctx, "iasecc_sdo_parse_data() parsed IASECC_DOCP_TAG rv %i", rv); |
694 | 360 | free(tlv.value); |
695 | 360 | LOG_TEST_RET(ctx, rv, "parse error: cannot parse DOCP"); |
696 | 360 | } |
697 | 839 | else if (tlv.tag == IASECC_DOCP_TAG_NON_REPUDIATION) { |
698 | 134 | free(sdo->docp.non_repudiation.value); |
699 | 134 | sdo->docp.non_repudiation = tlv; |
700 | 134 | } |
701 | 705 | else if (tlv.tag == IASECC_DOCP_TAG_USAGE_REMAINING) { |
702 | 104 | free(sdo->docp.usage_remaining.value); |
703 | 104 | sdo->docp.usage_remaining = tlv; |
704 | 104 | } |
705 | 601 | else if (tlv.tag == IASECC_DOCP_TAG_TRIES_MAXIMUM) { |
706 | 105 | free(sdo->docp.tries_maximum.value); |
707 | 105 | sdo->docp.tries_maximum = tlv; |
708 | 105 | } |
709 | 496 | else if (tlv.tag == IASECC_DOCP_TAG_TRIES_REMAINING) { |
710 | 169 | free(sdo->docp.tries_remaining.value); |
711 | 169 | sdo->docp.tries_remaining = tlv; |
712 | 169 | } |
713 | 327 | else if (tlv.tag == IASECC_SDO_CHV_TAG) { |
714 | 160 | if (sdo->sdo_class != IASECC_SDO_CLASS_CHV) { |
715 | 6 | free(tlv.value); |
716 | 6 | LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: IASECC_SDO_CHV_TAG tag in non User CHV SDO"); |
717 | 6 | } |
718 | | |
719 | 154 | rv = iasecc_parse_chv(card, tlv.value, tlv.size, &sdo->data.chv); |
720 | 154 | free(tlv.value); |
721 | 154 | LOG_TEST_RET(ctx, rv, "parse error: cannot parse SDO CHV data"); |
722 | 154 | } |
723 | 167 | else if (tlv.tag == IASECC_SDO_PUBKEY_TAG) { |
724 | 6 | if (sdo->sdo_class != IASECC_SDO_CLASS_RSA_PUBLIC) { |
725 | 6 | free(tlv.value); |
726 | 6 | LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: SDO_PUBLIC_KEY tag in non PUBLIC_KEY SDO"); |
727 | 6 | } |
728 | | |
729 | 0 | rv = iasecc_parse_pubkey(card, tlv.value, tlv.size, &sdo->data.pub_key); |
730 | 0 | free(tlv.value); |
731 | 0 | LOG_TEST_RET(ctx, rv, "parse error: cannot parse SDO PUBLIC KEY data"); |
732 | 0 | } |
733 | 161 | else if (tlv.tag == IASECC_SDO_PRVKEY_TAG) { |
734 | 7 | if (sdo->sdo_class != IASECC_SDO_CLASS_RSA_PRIVATE) { |
735 | 7 | free(tlv.value); |
736 | 7 | LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: SDO_PRIVATE_KEY tag in non PRIVATE_KEY SDO"); |
737 | 7 | } |
738 | | |
739 | 0 | rv = iasecc_parse_prvkey(card, tlv.value, tlv.size, &sdo->data.prv_key); |
740 | 0 | free(tlv.value); |
741 | 0 | LOG_TEST_RET(ctx, rv, "parse error: cannot parse SDO PRIVATE KEY data"); |
742 | 0 | } |
743 | 154 | else if (tlv.tag == IASECC_SDO_KEYSET_TAG) { |
744 | 6 | if (sdo->sdo_class != IASECC_SDO_CLASS_KEYSET) { |
745 | 6 | free(tlv.value); |
746 | 6 | LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: SDO_KEYSET tag in non KEYSET SDO"); |
747 | 6 | } |
748 | | |
749 | 0 | rv = iasecc_parse_keyset(card, tlv.value, tlv.size, &sdo->data.keyset); |
750 | 0 | free(tlv.value); |
751 | 0 | LOG_TEST_RET(ctx, rv, "parse error: cannot parse SDO KEYSET data"); |
752 | 0 | } |
753 | 148 | else { |
754 | 148 | sc_log(ctx, "iasecc_sdo_parse_data() non supported tag 0x%X", tlv.tag); |
755 | 148 | free(tlv.value); |
756 | 148 | LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); |
757 | 148 | } |
758 | | |
759 | 658 | return tlv_size; |
760 | 1.19k | } |
761 | | |
762 | | |
763 | | int |
764 | | iasecc_sdo_parse(struct sc_card *card, unsigned char *data, size_t data_len, struct iasecc_sdo *sdo) |
765 | 934 | { |
766 | 934 | struct sc_context *ctx = card->ctx; |
767 | 934 | size_t size, offs; |
768 | 934 | int size_size; |
769 | 934 | int rv; |
770 | | |
771 | 934 | LOG_FUNC_CALLED(ctx); |
772 | | |
773 | 934 | if (data == NULL || data_len < 2) |
774 | 934 | LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); |
775 | | |
776 | 902 | if (*data == IASECC_SDO_TEMPLATE_TAG) { |
777 | 215 | size_size = iasecc_parse_size(data + 1, data_len - 1, &size); |
778 | 215 | LOG_TEST_RET(ctx, size_size, "parse error: invalid size data of IASECC_SDO_TEMPLATE"); |
779 | | |
780 | 191 | if (data_len < (size_t)(size_size + 1)) { |
781 | 0 | LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); |
782 | 0 | } |
783 | 191 | data += size_size + 1; |
784 | 191 | data_len -= (size_size + 1); |
785 | 191 | if (size > data_len) { |
786 | 93 | LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); |
787 | 93 | } |
788 | 98 | data_len = size; |
789 | 98 | sc_log(ctx, |
790 | 98 | "IASECC_SDO_TEMPLATE: size %"SC_FORMAT_LEN_SIZE_T"u, size_size %d", |
791 | 98 | size, size_size); |
792 | 98 | } |
793 | | |
794 | 785 | if (data_len < 4 || *data != IASECC_SDO_TAG_HEADER) |
795 | 785 | LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); |
796 | | |
797 | 715 | if (sdo->sdo_class != (*(data + 1) & 0x7F)) |
798 | 715 | LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); |
799 | | |
800 | 709 | if (sdo->sdo_ref != (*(data + 2) & 0x3F)) |
801 | 709 | LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); |
802 | | |
803 | 702 | size_size = iasecc_parse_size(data + 3, data_len - 3, &size); |
804 | 702 | LOG_TEST_RET(ctx, size_size, "parse error: invalid size data"); |
805 | | |
806 | 695 | if (data_len != size + size_size + 3) |
807 | 695 | LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: invalid SDO data size"); |
808 | | |
809 | 631 | sc_log(ctx, |
810 | 631 | "sz %"SC_FORMAT_LEN_SIZE_T"u, sz_size %d", |
811 | 631 | size, size_size); |
812 | | |
813 | 631 | offs = 3 + size_size; |
814 | 1.28k | for (; offs < data_len;) { |
815 | 1.26k | rv = iasecc_sdo_parse_data(card, data + offs, data_len - offs, sdo); |
816 | 1.26k | if (rv != SC_SUCCESS) { |
817 | 1.26k | iasecc_sdo_free_fields(card, sdo); |
818 | 1.26k | LOG_TEST_RET(ctx, rv, "parse error: invalid SDO data"); |
819 | 1.26k | } |
820 | | |
821 | 658 | offs += rv; |
822 | 658 | } |
823 | | |
824 | 26 | if (offs != data_len) |
825 | 26 | LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: not totally parsed"); |
826 | | |
827 | 26 | sc_log(ctx, |
828 | 26 | "docp.acls_contact.size %"SC_FORMAT_LEN_SIZE_T"u, docp.size.size %"SC_FORMAT_LEN_SIZE_T"u", |
829 | 26 | sdo->docp.acls_contact.size, sdo->docp.size.size); |
830 | | |
831 | 26 | LOG_FUNC_RETURN(ctx, SC_SUCCESS); |
832 | 26 | } |
833 | | |
834 | | |
835 | | int |
836 | | iasecc_sdo_allocate_and_parse(struct sc_card *card, unsigned char *data, size_t data_len, |
837 | | struct iasecc_sdo **out) |
838 | 4 | { |
839 | 4 | struct sc_context *ctx = card->ctx; |
840 | 4 | struct iasecc_sdo *sdo = NULL; |
841 | 4 | size_t size, offs; |
842 | 4 | int size_size; |
843 | 4 | int rv; |
844 | | |
845 | 4 | LOG_FUNC_CALLED(ctx); |
846 | | |
847 | 4 | if (*data != IASECC_SDO_TAG_HEADER) |
848 | 4 | LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); |
849 | | |
850 | 4 | if (data_len < 3) |
851 | 4 | LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); |
852 | | |
853 | 4 | sdo = calloc(1, sizeof(struct iasecc_sdo)); |
854 | 4 | if (!sdo) |
855 | 0 | return SC_ERROR_OUT_OF_MEMORY; |
856 | 4 | *out = sdo; |
857 | | |
858 | 4 | sdo->sdo_class = *(data + 1) & 0x7F; |
859 | 4 | sdo->sdo_ref = *(data + 2) & 0x3F; |
860 | | |
861 | 4 | sc_log(ctx, "sdo_class 0x%X, sdo_ref 0x%X", sdo->sdo_class, sdo->sdo_ref); |
862 | 4 | if (data_len == 3) |
863 | 4 | LOG_FUNC_RETURN(ctx, SC_SUCCESS); |
864 | | |
865 | 0 | size_size = iasecc_parse_size(data + 3, data_len - 3, &size); |
866 | 0 | LOG_TEST_RET(ctx, size_size, "parse error: invalid size data"); |
867 | | |
868 | 0 | if (data_len != size + size_size + 3) |
869 | 0 | LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: invalid SDO data size"); |
870 | | |
871 | 0 | sc_log(ctx, |
872 | 0 | "sz %"SC_FORMAT_LEN_SIZE_T"u, sz_size %d", |
873 | 0 | size, size_size); |
874 | |
|
875 | 0 | offs = 3 + size_size; |
876 | 0 | for (; offs < data_len;) { |
877 | 0 | rv = iasecc_sdo_parse_data(card, data + offs, data_len - offs, sdo); |
878 | 0 | LOG_TEST_RET(ctx, rv, "parse error: invalid SDO data"); |
879 | | |
880 | 0 | offs += rv; |
881 | 0 | } |
882 | | |
883 | 0 | if (offs != data_len) |
884 | 0 | LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: not totally parsed"); |
885 | | |
886 | 0 | sc_log(ctx, |
887 | 0 | "docp.acls_contact.size %"SC_FORMAT_LEN_SIZE_T"u; docp.size.size %"SC_FORMAT_LEN_SIZE_T"u", |
888 | 0 | sdo->docp.acls_contact.size, sdo->docp.size.size); |
889 | |
|
890 | 0 | LOG_FUNC_RETURN(ctx, SC_SUCCESS); |
891 | 0 | } |
892 | | |
893 | | |
894 | | static int |
895 | | iasecc_update_blob(struct sc_context *ctx, struct iasecc_extended_tlv *tlv, |
896 | | unsigned char **blob, size_t *blob_size) |
897 | 0 | { |
898 | 0 | unsigned char *pp = NULL; |
899 | 0 | size_t offs = 0, sz; |
900 | |
|
901 | 0 | if (tlv->size == 0) |
902 | 0 | LOG_FUNC_RETURN(ctx, SC_SUCCESS); |
903 | | |
904 | 0 | sz = tlv->size + 2; |
905 | |
|
906 | 0 | if (tlv->tag > 0xFF) |
907 | 0 | sz += 1; |
908 | |
|
909 | 0 | if (tlv->size > 0x7F && tlv->size < 0x100) |
910 | 0 | sz += 1; |
911 | 0 | else if (tlv->size >= 0x100) |
912 | 0 | sz += 2; |
913 | |
|
914 | 0 | pp = realloc(*blob, *blob_size + sz); |
915 | 0 | if (!pp) |
916 | 0 | LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); |
917 | | |
918 | 0 | if (tlv->tag > 0xFF) |
919 | 0 | *(pp + *blob_size + offs++) = (tlv->tag >> 8) & 0xFF; |
920 | 0 | *(pp + *blob_size + offs++) = tlv->tag & 0xFF; |
921 | |
|
922 | 0 | if (tlv->size >= 0x100) { |
923 | 0 | *(pp + *blob_size + offs++) = 0x82; |
924 | 0 | *(pp + *blob_size + offs++) = (tlv->size >> 8) & 0xFF; |
925 | 0 | } |
926 | 0 | else if (tlv->size > 0x7F) { |
927 | 0 | *(pp + *blob_size + offs++) = 0x81; |
928 | 0 | } |
929 | 0 | *(pp + *blob_size + offs++) = tlv->size & 0xFF; |
930 | |
|
931 | 0 | memcpy(pp + *blob_size + offs, tlv->value, tlv->size); |
932 | |
|
933 | 0 | *blob_size += sz; |
934 | 0 | *blob = pp; |
935 | |
|
936 | 0 | return 0; |
937 | 0 | } |
938 | | |
939 | | |
940 | | static int |
941 | | iasecc_encode_docp(struct sc_context *ctx, struct iasecc_sdo_docp *docp, unsigned char **out, size_t *out_len) |
942 | 0 | { |
943 | 0 | struct iasecc_extended_tlv tlv, tlv_st; |
944 | 0 | unsigned char *st_blob = NULL, *tmp_blob = NULL, *docp_blob = NULL; |
945 | 0 | size_t blob_size; |
946 | 0 | int rv; |
947 | |
|
948 | 0 | LOG_FUNC_CALLED(ctx); |
949 | 0 | if (!docp->acls_contact.size || (docp->size.size != 2)) |
950 | 0 | LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); |
951 | | |
952 | 0 | memset(&tlv, 0, sizeof(tlv)); |
953 | 0 | memset(&tlv_st, 0, sizeof(tlv_st)); |
954 | |
|
955 | 0 | blob_size = 0; |
956 | 0 | rv = iasecc_update_blob(ctx, &docp->acls_contact, &st_blob, &blob_size); |
957 | 0 | LOG_TEST_GOTO_ERR(ctx, rv, "ECC: cannot add contact ACLs to blob"); |
958 | | |
959 | 0 | rv = iasecc_update_blob(ctx, &docp->acls_contactless, &st_blob, &blob_size); |
960 | 0 | LOG_TEST_GOTO_ERR(ctx, rv, "ECC: cannot add contactless ACLs to blob"); |
961 | | |
962 | 0 | tlv.tag = IASECC_DOCP_TAG_ACLS; |
963 | 0 | tlv.size = blob_size; |
964 | 0 | tlv.value = st_blob; |
965 | |
|
966 | 0 | blob_size = 0; |
967 | 0 | rv = iasecc_update_blob(ctx, &tlv, &tmp_blob, &blob_size); |
968 | 0 | LOG_TEST_GOTO_ERR(ctx, rv, "ECC: cannot add ACLs template to blob"); |
969 | | |
970 | 0 | rv = iasecc_update_blob(ctx, &docp->name, &tmp_blob, &blob_size); |
971 | 0 | LOG_TEST_GOTO_ERR(ctx, rv, "ECC: cannot add NAME to blob"); |
972 | | |
973 | 0 | rv = iasecc_update_blob(ctx, &docp->tries_maximum, &tmp_blob, &blob_size); |
974 | 0 | LOG_TEST_GOTO_ERR(ctx, rv, "ECC: cannot add TRIES MAXIMUM to blob"); |
975 | | |
976 | 0 | rv = iasecc_update_blob(ctx, &docp->tries_remaining, &tmp_blob, &blob_size); |
977 | 0 | LOG_TEST_GOTO_ERR(ctx, rv, "ECC: cannot add TRIES REMAINING to blob"); |
978 | | |
979 | 0 | rv = iasecc_update_blob(ctx, &docp->usage_maximum, &tmp_blob, &blob_size); |
980 | 0 | LOG_TEST_GOTO_ERR(ctx, rv, "ECC: cannot add USAGE MAXIMUM to blob"); |
981 | | |
982 | 0 | rv = iasecc_update_blob(ctx, &docp->usage_remaining, &tmp_blob, &blob_size); |
983 | 0 | LOG_TEST_GOTO_ERR(ctx, rv, "ECC: cannot add USAGE REMAINING to blob"); |
984 | | |
985 | 0 | rv = iasecc_update_blob(ctx, &docp->non_repudiation, &tmp_blob, &blob_size); |
986 | 0 | LOG_TEST_GOTO_ERR(ctx, rv, "ECC: cannot add NON REPUDIATION to blob"); |
987 | | |
988 | 0 | rv = iasecc_update_blob(ctx, &docp->size, &tmp_blob, &blob_size); |
989 | 0 | LOG_TEST_GOTO_ERR(ctx, rv, "ECC: cannot add SIZE to blob"); |
990 | | |
991 | 0 | rv = iasecc_update_blob(ctx, &docp->issuer_data, &tmp_blob, &blob_size); |
992 | 0 | LOG_TEST_GOTO_ERR(ctx, rv, "ECC: cannot add IDATA to blob"); |
993 | | |
994 | 0 | tlv.tag = IASECC_DOCP_TAG; |
995 | 0 | tlv.size = blob_size; |
996 | 0 | tlv.value = tmp_blob; |
997 | |
|
998 | 0 | blob_size = 0; |
999 | 0 | rv = iasecc_update_blob(ctx, &tlv, &docp_blob, &blob_size); |
1000 | 0 | LOG_TEST_GOTO_ERR(ctx, rv, "ECC: cannot add ACLs to blob"); |
1001 | | |
1002 | 0 | if (out && out_len) { |
1003 | 0 | *out = docp_blob; |
1004 | 0 | *out_len = blob_size; |
1005 | 0 | docp_blob = NULL; |
1006 | 0 | } |
1007 | |
|
1008 | 0 | err: |
1009 | 0 | free(docp_blob); |
1010 | 0 | free(tmp_blob); |
1011 | 0 | free(st_blob); |
1012 | |
|
1013 | 0 | LOG_FUNC_RETURN(ctx, SC_SUCCESS); |
1014 | 0 | } |
1015 | | |
1016 | | |
1017 | | static unsigned |
1018 | | iasecc_sdo_encode_asn1_tag(unsigned in_tag) |
1019 | 0 | { |
1020 | 0 | unsigned short_tag; |
1021 | 0 | unsigned out_tag; |
1022 | |
|
1023 | 0 | for (short_tag = in_tag; short_tag > 0xFF; short_tag >>= 8) |
1024 | 0 | ; |
1025 | 0 | out_tag = in_tag; |
1026 | 0 | switch (short_tag & SC_ASN1_TAG_CLASS) { |
1027 | 0 | case SC_ASN1_TAG_APPLICATION: |
1028 | 0 | out_tag |= SC_ASN1_APP; |
1029 | 0 | break; |
1030 | 0 | case SC_ASN1_TAG_CONTEXT: |
1031 | 0 | out_tag |= SC_ASN1_CTX; |
1032 | 0 | break; |
1033 | 0 | case SC_ASN1_TAG_PRIVATE: |
1034 | 0 | out_tag |= SC_ASN1_PRV; |
1035 | 0 | break; |
1036 | 0 | } |
1037 | 0 | return out_tag; |
1038 | 0 | } |
1039 | | |
1040 | | |
1041 | | int |
1042 | | iasecc_sdo_encode_create(struct sc_context *ctx, struct iasecc_sdo *sdo, unsigned char **out) |
1043 | 0 | { |
1044 | 0 | struct sc_asn1_entry c_asn1_docp_data[2] = { |
1045 | 0 | { "docpData", SC_ASN1_OCTET_STRING, 0, SC_ASN1_ALLOC, NULL, NULL }, |
1046 | 0 | { NULL, 0, 0, 0, NULL, NULL } |
1047 | 0 | }; |
1048 | 0 | struct sc_asn1_entry c_asn1_create_data[2] = { |
1049 | 0 | { "createData", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_APP | SC_ASN1_CONS, 0, NULL, NULL }, |
1050 | 0 | { NULL, 0, 0, 0, NULL, NULL } |
1051 | 0 | }; |
1052 | 0 | struct sc_asn1_entry asn1_docp_data[2], asn1_create_data[2]; |
1053 | 0 | unsigned char *blob = NULL; |
1054 | 0 | size_t len, out_len; |
1055 | 0 | unsigned sdo_full_ref; |
1056 | 0 | int rv; |
1057 | |
|
1058 | 0 | LOG_FUNC_CALLED(ctx); |
1059 | 0 | sc_log(ctx, "ecc_sdo_encode_create() sdo->sdo_class %X", sdo->sdo_class); |
1060 | 0 | sc_log(ctx, "id %02X%02X%02X", IASECC_SDO_TAG_HEADER, sdo->sdo_class | 0x80, sdo->sdo_ref); |
1061 | |
|
1062 | 0 | if (out) |
1063 | 0 | *out = NULL; |
1064 | |
|
1065 | 0 | rv = iasecc_encode_docp(ctx, &sdo->docp, &blob, &len); |
1066 | 0 | LOG_TEST_RET(ctx, rv, "ECC encode DOCP error"); |
1067 | | |
1068 | 0 | sdo_full_ref = (sdo->sdo_ref&0x3F) + 0x100*(sdo->sdo_class | IASECC_OBJECT_REF_LOCAL) + 0x10000*IASECC_SDO_TAG_HEADER; |
1069 | 0 | c_asn1_docp_data[0].tag = iasecc_sdo_encode_asn1_tag(sdo_full_ref) | SC_ASN1_CONS; |
1070 | |
|
1071 | 0 | sc_copy_asn1_entry(c_asn1_docp_data, asn1_docp_data); |
1072 | 0 | sc_copy_asn1_entry(c_asn1_create_data, asn1_create_data); |
1073 | |
|
1074 | 0 | sc_format_asn1_entry(asn1_docp_data + 0, blob, &len, 1); |
1075 | 0 | sc_format_asn1_entry(asn1_create_data + 0, asn1_docp_data, NULL, 1); |
1076 | |
|
1077 | 0 | rv = sc_asn1_encode(ctx, asn1_create_data, out, &out_len); |
1078 | 0 | LOG_TEST_RET(ctx, rv, "Encode create data error"); |
1079 | 0 | if (out) |
1080 | 0 | sc_debug(ctx, SC_LOG_DEBUG_ASN1,"Create data: %s", sc_dump_hex(*out, out_len)); |
1081 | |
|
1082 | 0 | LOG_FUNC_RETURN(ctx, (int)out_len); |
1083 | 0 | } |
1084 | | |
1085 | | |
1086 | | int |
1087 | | iasecc_sdo_encode_update_field(struct sc_context *ctx, unsigned char sdo_class, unsigned char sdo_ref, |
1088 | | struct iasecc_extended_tlv *tlv, unsigned char **out) |
1089 | 0 | { |
1090 | 0 | unsigned sdo_full_ref; |
1091 | 0 | size_t out_len; |
1092 | 0 | int rv; |
1093 | |
|
1094 | 0 | struct sc_asn1_entry c_asn1_field_value[2] = { |
1095 | 0 | { "fieldValue", SC_ASN1_OCTET_STRING, 0, SC_ASN1_ALLOC, NULL, NULL }, |
1096 | 0 | { NULL, 0, 0, 0, NULL, NULL } |
1097 | 0 | }; |
1098 | 0 | struct sc_asn1_entry c_asn1_sdo_field[2] = { |
1099 | 0 | { "sdoField", SC_ASN1_STRUCT, 0, 0, NULL, NULL }, |
1100 | 0 | { NULL, 0, 0, 0, NULL, NULL } |
1101 | 0 | }; |
1102 | 0 | struct sc_asn1_entry c_asn1_class_data[2] = { |
1103 | 0 | { "classData", SC_ASN1_STRUCT, 0, 0, NULL, NULL }, |
1104 | 0 | { NULL, 0, 0, 0, NULL, NULL } |
1105 | 0 | }; |
1106 | 0 | struct sc_asn1_entry c_asn1_update_data[2] = { |
1107 | 0 | { "updateData", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_APP | SC_ASN1_CONS, 0, NULL, NULL }, |
1108 | 0 | { NULL, 0, 0, 0, NULL, NULL } |
1109 | 0 | }; |
1110 | 0 | struct sc_asn1_entry asn1_field_value[4], asn1_sdo_field[2], asn1_class_data[2], asn1_update_data[2]; |
1111 | |
|
1112 | 0 | LOG_FUNC_CALLED(ctx); |
1113 | |
|
1114 | 0 | c_asn1_field_value[0].tag = iasecc_sdo_encode_asn1_tag(tlv->tag); |
1115 | 0 | c_asn1_sdo_field[0].tag = iasecc_sdo_encode_asn1_tag(tlv->parent_tag) | SC_ASN1_CONS; |
1116 | |
|
1117 | 0 | sdo_full_ref = (sdo_ref&0x3F) + 0x100*(sdo_class | IASECC_OBJECT_REF_LOCAL) + 0x10000*IASECC_SDO_TAG_HEADER; |
1118 | 0 | c_asn1_class_data[0].tag = iasecc_sdo_encode_asn1_tag(sdo_full_ref) | SC_ASN1_CONS; |
1119 | |
|
1120 | 0 | sc_copy_asn1_entry(c_asn1_field_value, asn1_field_value); |
1121 | 0 | sc_copy_asn1_entry(c_asn1_sdo_field, asn1_sdo_field); |
1122 | 0 | sc_copy_asn1_entry(c_asn1_class_data, asn1_class_data); |
1123 | 0 | sc_copy_asn1_entry(c_asn1_update_data, asn1_update_data); |
1124 | |
|
1125 | 0 | sc_format_asn1_entry(asn1_field_value + 0, tlv->value, &tlv->size, 1); |
1126 | 0 | sc_format_asn1_entry(asn1_sdo_field + 0, asn1_field_value, NULL, 1); |
1127 | 0 | sc_format_asn1_entry(asn1_class_data + 0, asn1_sdo_field, NULL, 1); |
1128 | 0 | sc_format_asn1_entry(asn1_update_data + 0, asn1_class_data, NULL, 1); |
1129 | |
|
1130 | 0 | rv = sc_asn1_encode(ctx, asn1_update_data, out, &out_len); |
1131 | 0 | LOG_TEST_RET(ctx, rv, "Encode update data error"); |
1132 | | |
1133 | 0 | sc_debug(ctx, SC_LOG_DEBUG_ASN1,"Data: %s", sc_dump_hex(tlv->value, tlv->size)); |
1134 | 0 | sc_debug(ctx, SC_LOG_DEBUG_ASN1,"Encoded: %s", sc_dump_hex(*out, out_len)); |
1135 | 0 | LOG_FUNC_RETURN(ctx, (int)out_len); |
1136 | 0 | } |
1137 | | |
1138 | | |
1139 | | int |
1140 | | iasecc_sdo_encode_rsa_update(struct sc_context *ctx, struct iasecc_sdo *sdo, struct sc_pkcs15_prkey_rsa *rsa, |
1141 | | struct iasecc_sdo_update *sdo_update) |
1142 | 0 | { |
1143 | 0 | LOG_FUNC_CALLED(ctx); |
1144 | |
|
1145 | 0 | sc_log(ctx, "iasecc_sdo_encode_rsa_update() SDO class %X", sdo->sdo_class); |
1146 | 0 | memset(sdo_update, 0, sizeof(*sdo_update)); |
1147 | 0 | if (sdo->sdo_class == IASECC_SDO_CLASS_RSA_PRIVATE) { |
1148 | 0 | int index = 0; |
1149 | |
|
1150 | 0 | sc_log(ctx, "iasecc_sdo_encode_rsa_update(IASECC_SDO_CLASS_RSA_PRIVATE)"); |
1151 | 0 | if (!rsa->p.len || !rsa->q.len || !rsa->iqmp.len || !rsa->dmp1.len || !rsa->dmq1.len) |
1152 | 0 | LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "need all private RSA key components"); |
1153 | | |
1154 | 0 | sdo_update->magic = SC_CARDCTL_IASECC_SDO_MAGIC_PUT_DATA; |
1155 | 0 | sdo_update->sdo_ref = sdo->sdo_ref; |
1156 | |
|
1157 | 0 | sdo_update->sdo_class = IASECC_SDO_CLASS_RSA_PRIVATE; |
1158 | |
|
1159 | 0 | sdo_update->fields[index].parent_tag = IASECC_SDO_PRVKEY_TAG; |
1160 | 0 | sdo_update->fields[index].tag = IASECC_SDO_PRVKEY_TAG_P; |
1161 | 0 | sdo_update->fields[index].value = rsa->p.data; |
1162 | 0 | sdo_update->fields[index].size = rsa->p.len; |
1163 | 0 | index++; |
1164 | |
|
1165 | 0 | sdo_update->fields[index].parent_tag = IASECC_SDO_PRVKEY_TAG; |
1166 | 0 | sdo_update->fields[index].tag = IASECC_SDO_PRVKEY_TAG_Q; |
1167 | 0 | sdo_update->fields[index].value = rsa->q.data; |
1168 | 0 | sdo_update->fields[index].size = rsa->q.len; |
1169 | 0 | index++; |
1170 | |
|
1171 | 0 | sdo_update->fields[index].parent_tag = IASECC_SDO_PRVKEY_TAG; |
1172 | 0 | sdo_update->fields[index].tag = IASECC_SDO_PRVKEY_TAG_IQMP; |
1173 | 0 | sdo_update->fields[index].value = rsa->iqmp.data; |
1174 | 0 | sdo_update->fields[index].size = rsa->iqmp.len; |
1175 | 0 | index++; |
1176 | |
|
1177 | 0 | sdo_update->fields[index].parent_tag = IASECC_SDO_PRVKEY_TAG; |
1178 | 0 | sdo_update->fields[index].tag = IASECC_SDO_PRVKEY_TAG_DMP1; |
1179 | 0 | sdo_update->fields[index].value = rsa->dmp1.data; |
1180 | 0 | sdo_update->fields[index].size = rsa->dmp1.len; |
1181 | 0 | index++; |
1182 | |
|
1183 | 0 | sdo_update->fields[index].parent_tag = IASECC_SDO_PRVKEY_TAG; |
1184 | 0 | sdo_update->fields[index].tag = IASECC_SDO_PRVKEY_TAG_DMQ1; |
1185 | 0 | sdo_update->fields[index].value = rsa->dmq1.data; |
1186 | 0 | sdo_update->fields[index].size = rsa->dmq1.len; |
1187 | 0 | index++; |
1188 | |
|
1189 | 0 | sc_log(ctx, "prv_key.compulsory.on_card %i", sdo->data.prv_key.compulsory.on_card); |
1190 | 0 | if (!sdo->data.prv_key.compulsory.on_card) { |
1191 | 0 | if (sdo->data.prv_key.compulsory.value) { |
1192 | 0 | sc_log(ctx, |
1193 | 0 | "sdo_prvkey->data.prv_key.compulsory.size %"SC_FORMAT_LEN_SIZE_T"u", |
1194 | 0 | sdo->data.prv_key.compulsory.size); |
1195 | 0 | sdo_update->fields[index].parent_tag = IASECC_SDO_PRVKEY_TAG; |
1196 | 0 | sdo_update->fields[index].tag = IASECC_SDO_PRVKEY_TAG_COMPULSORY; |
1197 | 0 | sdo_update->fields[index].value = sdo->data.prv_key.compulsory.value; |
1198 | 0 | sdo_update->fields[index].size = sdo->data.prv_key.compulsory.size; |
1199 | 0 | index++; |
1200 | 0 | } |
1201 | 0 | } |
1202 | 0 | } |
1203 | 0 | else if (sdo->sdo_class == IASECC_SDO_CLASS_RSA_PUBLIC) { |
1204 | 0 | int index = 0; |
1205 | 0 | sc_log(ctx, "iasecc_sdo_encode_rsa_update(IASECC_SDO_CLASS_RSA_PUBLIC)"); |
1206 | |
|
1207 | 0 | sdo_update->magic = SC_CARDCTL_IASECC_SDO_MAGIC_PUT_DATA; |
1208 | 0 | sdo_update->sdo_ref = sdo->sdo_ref; |
1209 | 0 | sdo_update->sdo_class = sdo->sdo_class; |
1210 | |
|
1211 | 0 | if (rsa->exponent.len) { |
1212 | 0 | sdo_update->fields[index].parent_tag = IASECC_SDO_PUBKEY_TAG; |
1213 | 0 | sdo_update->fields[index].tag = IASECC_SDO_PUBKEY_TAG_E; |
1214 | 0 | sdo_update->fields[index].value = rsa->exponent.data; |
1215 | 0 | sdo_update->fields[index].size = rsa->exponent.len; |
1216 | 0 | index++; |
1217 | 0 | } |
1218 | |
|
1219 | 0 | if (rsa->modulus.len) { |
1220 | 0 | sdo_update->fields[index].parent_tag = IASECC_SDO_PUBKEY_TAG; |
1221 | 0 | sdo_update->fields[index].tag = IASECC_SDO_PUBKEY_TAG_N; |
1222 | 0 | sdo_update->fields[index].value = rsa->modulus.data; |
1223 | 0 | sdo_update->fields[index].size = rsa->modulus.len; |
1224 | 0 | index++; |
1225 | 0 | } |
1226 | |
|
1227 | 0 | if (sdo->data.pub_key.cha.value) { |
1228 | 0 | sdo_update->fields[index].parent_tag = IASECC_SDO_PUBKEY_TAG; |
1229 | 0 | sdo_update->fields[index].tag = IASECC_SDO_PUBKEY_TAG_CHA; |
1230 | 0 | sdo_update->fields[index].value = sdo->data.pub_key.cha.value; |
1231 | 0 | sdo_update->fields[index].size = sdo->data.pub_key.cha.size; |
1232 | 0 | index++; |
1233 | 0 | } |
1234 | |
|
1235 | 0 | if (sdo->data.pub_key.chr.value) { |
1236 | 0 | sdo_update->fields[index].parent_tag = IASECC_SDO_PUBKEY_TAG; |
1237 | 0 | sdo_update->fields[index].tag = IASECC_SDO_PUBKEY_TAG_CHR; |
1238 | 0 | sdo_update->fields[index].value = sdo->data.pub_key.chr.value; |
1239 | 0 | sdo_update->fields[index].size = sdo->data.pub_key.chr.size; |
1240 | 0 | index++; |
1241 | 0 | } |
1242 | | |
1243 | | /* For ECC card 'compulsory' flag should be already here */ |
1244 | 0 | if (!sdo->data.pub_key.compulsory.on_card) { |
1245 | 0 | if (sdo->data.pub_key.compulsory.value) { |
1246 | 0 | sdo_update->fields[index].parent_tag = IASECC_SDO_PUBKEY_TAG; |
1247 | 0 | sdo_update->fields[index].tag = IASECC_SDO_PUBKEY_TAG_COMPULSORY; |
1248 | 0 | sdo_update->fields[index].value = sdo->data.pub_key.compulsory.value; |
1249 | 0 | sdo_update->fields[index].size = sdo->data.pub_key.compulsory.size; |
1250 | 0 | index++; |
1251 | 0 | } |
1252 | 0 | } |
1253 | 0 | } |
1254 | 0 | else { |
1255 | 0 | LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); |
1256 | 0 | } |
1257 | | |
1258 | 0 | LOG_FUNC_RETURN(ctx, SC_SUCCESS); |
1259 | 0 | } |
1260 | | |
1261 | | |
1262 | | int |
1263 | | iasecc_sdo_parse_card_answer(struct sc_context *ctx, unsigned char *data, size_t data_len, |
1264 | | struct iasecc_sm_card_answer *out) |
1265 | 0 | { |
1266 | 0 | int have_mac = 0, have_status = 0; |
1267 | 0 | size_t size = 0, size_size, offs; |
1268 | |
|
1269 | 0 | LOG_FUNC_CALLED(ctx); |
1270 | 0 | if (!data || !data_len || !out) |
1271 | 0 | LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); |
1272 | | |
1273 | 0 | memset(out, 0, sizeof(*out)); |
1274 | 0 | for (offs=0; offs<data_len; ) { |
1275 | 0 | size_size = iasecc_parse_size(data + 1, data_len - 1, &size); |
1276 | |
|
1277 | 0 | if (*(data + offs) == IASECC_CARD_ANSWER_TAG_DATA ) { |
1278 | 0 | if (size > sizeof(out->data)) |
1279 | 0 | LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "iasecc_sm_decode_answer() unbelievable !!!"); |
1280 | | |
1281 | 0 | memcpy(out->data, data + offs + size_size + 1, size); |
1282 | 0 | out->data_len = size; |
1283 | 0 | offs += 1 + size_size + size; |
1284 | 0 | } |
1285 | 0 | else if (*(data + offs) == IASECC_CARD_ANSWER_TAG_SW ) { |
1286 | 0 | if (*(data + offs + 1) != 2) |
1287 | 0 | LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "iasecc_sm_decode_answer() SW length not 2"); |
1288 | 0 | out->sw = *(data + offs + 2) * 0x100 + *(data + offs + 3); |
1289 | |
|
1290 | 0 | memcpy(out->ticket, data + offs, 4); |
1291 | |
|
1292 | 0 | offs += 4; |
1293 | 0 | have_status = 1; |
1294 | 0 | } |
1295 | 0 | else if (*(data + offs) == IASECC_CARD_ANSWER_TAG_MAC ) { |
1296 | 0 | if (*(data + offs + 1) != 8) |
1297 | 0 | LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "iasecc_sm_decode_answer() MAC length not 8"); |
1298 | 0 | memcpy(out->mac, data + offs + 2, 8); |
1299 | |
|
1300 | 0 | memcpy(out->ticket + 4, data + offs, 10); |
1301 | |
|
1302 | 0 | offs += 10; |
1303 | 0 | have_mac = 1; |
1304 | 0 | } |
1305 | 0 | else { |
1306 | 0 | LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "iasecc_sm_decode_answer() invalid card answer tag"); |
1307 | 0 | } |
1308 | 0 | } |
1309 | | |
1310 | 0 | if (!have_mac || !have_status) |
1311 | 0 | LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "iasecc_sm_decode_answer() absent MAC or SW "); |
1312 | | |
1313 | 0 | LOG_FUNC_RETURN(ctx, SC_SUCCESS); |
1314 | 0 | } |
1315 | | |
1316 | | |
1317 | | static int |
1318 | | iasecc_tlv_copy(struct sc_context *ctx, struct iasecc_extended_tlv *in, struct iasecc_extended_tlv *out) |
1319 | 711 | { |
1320 | 711 | if (!in || !out) |
1321 | 711 | LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); |
1322 | | |
1323 | 711 | memset(out, 0, sizeof(struct iasecc_extended_tlv)); |
1324 | 711 | out->tag = in->tag; |
1325 | 711 | out->parent_tag = in->parent_tag; |
1326 | 711 | out->on_card = in->on_card; |
1327 | 711 | if (in->value && in->size) { |
1328 | 0 | out->value = calloc(1, in->size); |
1329 | 0 | if (!out->value) |
1330 | 0 | LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); |
1331 | | |
1332 | 0 | memcpy(out->value, in->value, in->size); |
1333 | 0 | out->size = in->size; |
1334 | 0 | } |
1335 | | |
1336 | 711 | return SC_SUCCESS; |
1337 | 711 | } |
1338 | | |
1339 | | |
1340 | | int |
1341 | | iasecc_docp_copy(struct sc_context *ctx, struct iasecc_sdo_docp *in, struct iasecc_sdo_docp *out) |
1342 | 79 | { |
1343 | 79 | int rv; |
1344 | | |
1345 | 79 | LOG_FUNC_CALLED(ctx); |
1346 | 79 | if (!in || !out) |
1347 | 79 | LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); |
1348 | | |
1349 | 79 | memset(out, 0, sizeof(struct iasecc_sdo_docp)); |
1350 | | |
1351 | 79 | rv = iasecc_tlv_copy(ctx, &in->name, &out->name); |
1352 | 79 | LOG_TEST_RET(ctx, rv, "TLV copy error"); |
1353 | | |
1354 | 79 | rv = iasecc_tlv_copy(ctx, &in->tries_maximum, &out->tries_maximum); |
1355 | 79 | LOG_TEST_RET(ctx, rv, "TLV copy error"); |
1356 | | |
1357 | 79 | rv = iasecc_tlv_copy(ctx, &in->tries_remaining, &out->tries_remaining); |
1358 | 79 | LOG_TEST_RET(ctx, rv, "TLV copy error"); |
1359 | | |
1360 | 79 | rv = iasecc_tlv_copy(ctx, &in->usage_maximum, &out->usage_maximum); |
1361 | 79 | LOG_TEST_RET(ctx, rv, "TLV copy error"); |
1362 | | |
1363 | 79 | rv = iasecc_tlv_copy(ctx, &in->usage_remaining, &out->usage_remaining); |
1364 | 79 | LOG_TEST_RET(ctx, rv, "TLV copy error"); |
1365 | | |
1366 | 79 | rv = iasecc_tlv_copy(ctx, &in->non_repudiation, &out->non_repudiation); |
1367 | 79 | LOG_TEST_RET(ctx, rv, "TLV copy error"); |
1368 | | |
1369 | 79 | rv = iasecc_tlv_copy(ctx, &in->size, &out->size); |
1370 | 79 | LOG_TEST_RET(ctx, rv, "TLV copy error"); |
1371 | | |
1372 | 79 | rv = iasecc_tlv_copy(ctx, &in->acls_contact, &out->acls_contact); |
1373 | 79 | LOG_TEST_RET(ctx, rv, "TLV copy error"); |
1374 | | |
1375 | 79 | rv = iasecc_tlv_copy(ctx, &in->acls_contactless, &out->acls_contactless); |
1376 | 79 | LOG_TEST_RET(ctx, rv, "TLV copy error"); |
1377 | | |
1378 | 79 | out->amb = in->amb; |
1379 | 79 | memcpy(out->scbs, in->scbs, sizeof(out->scbs)); |
1380 | | |
1381 | 79 | LOG_FUNC_RETURN(ctx, SC_SUCCESS); |
1382 | 79 | } |
1383 | | |
1384 | | #else |
1385 | | |
1386 | | /* we need to define the functions below to export them */ |
1387 | | #include "errors.h" |
1388 | | |
1389 | | int |
1390 | | iasecc_sdo_encode_update_field() |
1391 | | { |
1392 | | return SC_ERROR_NOT_SUPPORTED; |
1393 | | } |
1394 | | |
1395 | | int |
1396 | | iasecc_se_get_crt() |
1397 | | { |
1398 | | return SC_ERROR_NOT_SUPPORTED; |
1399 | | } |
1400 | | |
1401 | | #endif /* ENABLE_OPENSSL */ |