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