Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2697245
  • 博文数量: 877
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 5921
  • 用 户 组: 普通用户
  • 注册时间: 2013-12-05 12:25
个人简介

技术的乐趣在于分享,欢迎多多交流,多多沟通。

文章分类

全部博文(877)

文章存档

2021年(2)

2016年(20)

2015年(471)

2014年(358)

2013年(26)

分类: 嵌入式

2015-01-13 19:25:35

SmartCrad.rar
上面为源代码有用者可下载
/*
2 * iso7816.c: Functions specified by the ISO 7816 standard
3 *
4 * Copyright (C) 2001, 2002  Juha Yrj鰈?
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "config.h"
22
23 #include
24 #include
25 #include
26 #include
27
28 #include "internal.h"
29 #include "asn1.h"
30 #include "iso7816.h"
31
32 static const struct sc_card_error iso7816_errors[] = {
33        { 0x6200, SC_ERROR_MEMORY_FAILURE,      "State of non-volatile memory unchanged" },
34        { 0x6281, SC_ERROR_MEMORY_FAILURE,      "Part of returned data may be corrupted" },
35        { 0x6282, SC_ERROR_CARD_CMD_FAILED,     "End of file/record reached before reading Le bytes" },
36        { 0x6283, SC_ERROR_CARD_CMD_FAILED,     "Selected file invalidated" },
37        { 0x6284, SC_ERROR_CARD_CMD_FAILED,     "FCI not formatted according to ISO 7816-4" },
38
39        { 0x6300, SC_ERROR_MEMORY_FAILURE,      "State of non-volatile memory changed" },
40        { 0x6381, SC_ERROR_CARD_CMD_FAILED,     "File filled up by last write" },
41
42        { 0x6581, SC_ERROR_MEMORY_FAILURE,      "Memory failure" },
43
44        { 0x6700, SC_ERROR_WRONG_LENGTH,        "Wrong length" },
45
46        { 0x6800, SC_ERROR_NO_CARD_SUPPORT,     "Functions in CLA not supported" },
47        { 0x6881, SC_ERROR_NO_CARD_SUPPORT,     "Logical channel not supported" },
48        { 0x6882, SC_ERROR_NO_CARD_SUPPORT,     "Secure messaging not supported" },
49
50        { 0x6900, SC_ERROR_NOT_ALLOWED,         "Command not allowed" },
51        { 0x6981, SC_ERROR_CARD_CMD_FAILED,     "Command incompatible with file structure" },
52        { 0x6982, SC_ERROR_SECURITY_STATUS_NOT_SATISFIED, "Security status not satisfied" },
53        { 0x6983, SC_ERROR_AUTH_METHOD_BLOCKED, "Authentication method blocked" },
54        { 0x6984, SC_ERROR_CARD_CMD_FAILED,     "Referenced data invalidated" },
55        { 0x6985, SC_ERROR_NOT_ALLOWED,         "Conditions of use not satisfied" },
56        { 0x6986, SC_ERROR_NOT_ALLOWED,         "Command not allowed (no current EF)" },
57        { 0x6987, SC_ERROR_INCORRECT_PARAMETERS,"Expected SM data objects missing" },
58        { 0x6988, SC_ERROR_INCORRECT_PARAMETERS,"SM data objects incorrect" },
59
60        { 0x6A00, SC_ERROR_INCORRECT_PARAMETERS,"Wrong parameter(s) P1-P2" },
61        { 0x6A80, SC_ERROR_INCORRECT_PARAMETERS,"Incorrect parameters in the data field" },
62        { 0x6A81, SC_ERROR_NO_CARD_SUPPORT,     "Function not supported" },
63        { 0x6A82, SC_ERROR_FILE_NOT_FOUND,      "File not found" },
64        { 0x6A83, SC_ERROR_RECORD_NOT_FOUND,    "Record not found" },
65        { 0x6A84, SC_ERROR_NOT_ENOUGH_MEMORY,   "Not enough memory space in the file" },
66        { 0x6A85, SC_ERROR_INCORRECT_PARAMETERS,"Lc inconsistent with TLV structure" },
67        { 0x6A86, SC_ERROR_INCORRECT_PARAMETERS,"Incorrect parameters P1-P2" },
68        { 0x6A87, SC_ERROR_INCORRECT_PARAMETERS,"Lc inconsistent with P1-P2" },
69        { 0x6A88, SC_ERROR_DATA_OBJECT_NOT_FOUND,"Referenced data not found" },
70
71        { 0x6B00, SC_ERROR_INCORRECT_PARAMETERS,"Wrong parameter(s) P1-P2" },
72        { 0x6D00, SC_ERROR_INS_NOT_SUPPORTED,   "Instruction code not supported or invalid" },
73        { 0x6E00, SC_ERROR_CLASS_NOT_SUPPORTED, "Class not supported" },
74        { 0x6F00, SC_ERROR_CARD_CMD_FAILED,     "No precise diagnosis" },
75
76        /* Possibly TCOS / Micardo specific errors */
77        { 0x6600, SC_ERROR_INCORRECT_PARAMETERS, "Error setting the security env"},
78        { 0x66F0, SC_ERROR_INCORRECT_PARAMETERS, "No space left for padding"},
79        { 0x69F0, SC_ERROR_NOT_ALLOWED,          "Command not allowed"},
80        { 0x6A89, SC_ERROR_FILE_ALREADY_EXISTS,  "Files exists"},
81        { 0x6A8A, SC_ERROR_FILE_ALREADY_EXISTS,  "Application exists"},
82 };
83
84 static int iso7816_check_sw(sc_card_t *card, unsigned int sw1, unsigned int sw2)
85 {
86        const int err_count = sizeof(iso7816_errors)/sizeof(iso7816_errors[0]);
87        int i;
88       
89        /* Handle special cases here */
90        if (sw1 == 0x6C) {
91                sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Wrong length; correct length is %d", sw2);
92                return SC_ERROR_WRONG_LENGTH;
93        }
94        if (sw1 == 0x90)
95                return SC_SUCCESS;
96        if (sw1 == 0x63U && (sw2 & ~0x0fU) == 0xc0U ) {
97             sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Verification failed (remaining tries: %d)",
98                   (sw2 & 0x0f));
99             return SC_ERROR_PIN_CODE_INCORRECT;
100        }
101        for (i = 0; i < err_count; i++)
102                if (iso7816_errors[i].SWs == ((sw1 << 8) | sw2)) {
103                        sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s", iso7816_errors[i].errorstr);
104                        return iso7816_errors[i].errorno;
105                }
106        sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unknown SWs; SW1=%02X, SW2=%02X", sw1, sw2);
107        return SC_ERROR_CARD_CMD_FAILED;
108 }
109
110 static int iso7816_read_binary(sc_card_t *card,
111                               unsigned int idx, u8 *buf, size_t count,
112                               unsigned long flags)
113 {
114        sc_apdu_t apdu;
115        u8 recvbuf[SC_MAX_APDU_BUFFER_SIZE];
116        int r;
117
118        if (idx > 0x7fff) {
119                sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid EF offset: 0x%X > 0x7FFF", idx);
120                return SC_ERROR_OFFSET_TOO_LARGE;
121        }
122
123        assert(count <= (card->max_recv_size > 0 ? card->max_recv_size : 256));
124        sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xB0,
125                       (idx >> 8) & 0x7F, idx & 0xFF);
126        apdu.le = count;
127        apdu.resplen = count;
128        apdu.resp = recvbuf;
129
130        r = sc_transmit_apdu(card, &apdu);
131        SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
132        if (apdu.resplen == 0)
133                SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2));
134        memcpy(buf, recvbuf, apdu.resplen);
135
136        SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, apdu.resplen);
137 }
138
139 static int iso7816_read_record(sc_card_t *card,
140                               unsigned int rec_nr, u8 *buf, size_t count,
141                               unsigned long flags)
142 {
143        sc_apdu_t apdu;
144        u8 recvbuf[SC_MAX_APDU_BUFFER_SIZE];
145        int r;
146
147        sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xB2, rec_nr, 0);
148        apdu.p2 = (flags & SC_RECORD_EF_ID_MASK) << 3;
149        if (flags & SC_RECORD_BY_REC_NR)
150                apdu.p2 |= 0x04;
151       
152        apdu.le = count;
153        apdu.resplen = count;
154        apdu.resp = recvbuf;
155
156        r = sc_transmit_apdu(card, &apdu);
157        SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
158        if (apdu.resplen == 0)
159                SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2));
160        memcpy(buf, recvbuf, apdu.resplen);
161
162        SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, apdu.resplen);
163 }
164
165 static int iso7816_write_record(sc_card_t *card, unsigned int rec_nr,
166                                const u8 *buf, size_t count,
167                                unsigned long flags)
168 {
169        sc_apdu_t apdu;
170        int r;
171
172        if (count > 256) {
173                sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Trying to send too many bytes");
174                return SC_ERROR_INVALID_ARGUMENTS;
175        }
176        sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xD2, rec_nr, 0);
177        apdu.p2 = (flags & SC_RECORD_EF_ID_MASK) << 3;
178        if (flags & SC_RECORD_BY_REC_NR)
179                apdu.p2 |= 0x04;
180       
181        apdu.lc = count;
182        apdu.datalen = count;
183        apdu.data = buf;
184
185        r = sc_transmit_apdu(card, &apdu);
186        SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
187        SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2),
188                    "Card returned error");
189        SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, count);
190 }
191
192 static int iso7816_append_record(sc_card_t *card,
193                                 const u8 *buf, size_t count,
194                                 unsigned long flags)
195 {
196        sc_apdu_t apdu;
197        int r;
198
199        if (count > 256) {
200                sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Trying to send too many bytes");
201                return SC_ERROR_INVALID_ARGUMENTS;
202        }
203        sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE2, 0, 0);
204        apdu.p2 = (flags & SC_RECORD_EF_ID_MASK) << 3;
205       
206        apdu.lc = count;
207        apdu.datalen = count;
208        apdu.data = buf;
209
210        r = sc_transmit_apdu(card, &apdu);
211        SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
212        SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2),
213                    "Card returned error");
214        SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, count);
215 }
216
217 static int iso7816_update_record(sc_card_t *card, unsigned int rec_nr,
218                                 const u8 *buf, size_t count,
219                                 unsigned long flags)
220 {
221        sc_apdu_t apdu;
222        int r;
223
224        if (count > 256) {
225                sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Trying to send too many bytes");
226                return SC_ERROR_INVALID_ARGUMENTS;
227        }
228        sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xDC, rec_nr, 0);
229        apdu.p2 = (flags & SC_RECORD_EF_ID_MASK) << 3;
230        if (flags & SC_RECORD_BY_REC_NR)
231                apdu.p2 |= 0x04;
232       
233        apdu.lc = count;
234        apdu.datalen = count;
235        apdu.data = buf;
236
237        r = sc_transmit_apdu(card, &apdu);
238        SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
239        SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2),
240                    "Card returned error");
241        SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, count);
242 }
243
244 static int iso7816_write_binary(sc_card_t *card,
245                                unsigned int idx, const u8 *buf,
246                                size_t count, unsigned long flags)
247 {
248        sc_apdu_t apdu;
249        int r;
250
251        assert(count <= (card->max_send_size > 0 ? card->max_send_size : 255));
252
253        if (idx > 0x7fff) {
254                sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid EF offset: 0x%X > 0x7FFF", idx);
255                return SC_ERROR_OFFSET_TOO_LARGE;
256        }
257
258        sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xD0,
259                       (idx >> 8) & 0x7F, idx & 0xFF);
260        apdu.lc = count;
261        apdu.datalen = count;
262        apdu.data = buf;
263
264        r = sc_transmit_apdu(card, &apdu);
265        SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
266        SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2),
267                    "Card returned error");
268        SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, count);
269 }
270
271 static int iso7816_update_binary(sc_card_t *card,
272                                 unsigned int idx, const u8 *buf,
273                                size_t count, unsigned long flags)
274 {
275        sc_apdu_t apdu;
276        int r;
277
278        assert(count <= (card->max_send_size > 0 ? card->max_send_size : 255));
279
280        if (idx > 0x7fff) {
281                sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid EF offset: 0x%X > 0x7FFF", idx);
282                return SC_ERROR_OFFSET_TOO_LARGE;
283        }
284
285        sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xD6,
286                       (idx >> 8) & 0x7F, idx & 0xFF);
287        apdu.lc = count;
288        apdu.datalen = count;
289        apdu.data = buf;
290
291        r = sc_transmit_apdu(card, &apdu);
292        SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
293        SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2),
294                    "Card returned error");
295        SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, count);
296 }
297
298 static int iso7816_process_fci(sc_card_t *card, sc_file_t *file,
299                       const u8 *buf, size_t buflen)
300 {
301        sc_context_t *ctx = card->ctx;
302        size_t taglen, len = buflen;
303        const u8 *tag = NULL, *p = buf;
304
305        sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "processing FCI bytes");
306        tag = sc_asn1_find_tag(ctx, p, len, 0x83, &taglen);
307        if (tag != NULL && taglen == 2) {
308                file->id = (tag[0] << 8) | tag[1];
309                sc_debug(ctx, SC_LOG_DEBUG_NORMAL,
310                        "  file identifier: 0x%02X%02X", tag[0], tag[1]);
311        }
312        tag = sc_asn1_find_tag(ctx, p, len, 0x80, &taglen);
313        if (tag != NULL && taglen > 0 && taglen < 3) {
314                file->size = tag[0];
315                if (taglen == 2)
316                        file->size = (file->size << 8) + tag[1];
317                sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "  bytes in file: %d", file->size);
318        }
319        if (tag == NULL) {
320                tag = sc_asn1_find_tag(ctx, p, len, 0x81, &taglen);
321                if (tag != NULL && taglen >= 2) {
322                        int bytes = (tag[0] << 8) + tag[1];
323                        sc_debug(ctx, SC_LOG_DEBUG_NORMAL,
324                                "  bytes in file: %d", bytes);
325                        file->size = bytes;
326                }
327        }
328        tag = sc_asn1_find_tag(ctx, p, len, 0x82, &taglen);
329        if (tag != NULL) {
330                if (taglen > 0) {
331                        unsigned char byte = tag[0];
332                        const char *type;
333
334                        file->shareable = byte & 0x40 ? 1 : 0;
335                        sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "  shareable: %s",
336                                       (byte & 0x40) ? "yes" : "no");
337                        file->ef_structure = byte & 0x07;
338                        switch ((byte >> 3) & 7) {
339                        case 0:
340                                type = "working EF";
341                                file->type = SC_FILE_TYPE_WORKING_EF;
342                                break;
343                        case 1:
344                                type = "internal EF";
345                                file->type = SC_FILE_TYPE_INTERNAL_EF;
346                                break;
347                        case 7:
348                                type = "DF";
349                                file->type = SC_FILE_TYPE_DF;
350                                break;
351                        default:
352                                type = "unknown";
353                                break;
354                        }
355                        sc_debug(ctx, SC_LOG_DEBUG_NORMAL,
356                                "  type: %s", type);
357                        sc_debug(ctx, SC_LOG_DEBUG_NORMAL,
358                                "  EF structure: %d", byte & 0x07);
359                }
360        }
361        tag = sc_asn1_find_tag(ctx, p, len, 0x84, &taglen);
362        if (tag != NULL && taglen > 0 && taglen <= 16) {
363                char tbuf[128];
364                memcpy(file->name, tag, taglen);
365                file->namelen = taglen;
366
367                sc_hex_dump(ctx, SC_LOG_DEBUG_NORMAL,
368                        file->name, file->namelen, tbuf, sizeof(tbuf));
369                sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "  File name: %s", tbuf);
370                if (!file->type)
371                        file->type = SC_FILE_TYPE_DF;
372        }
373        tag = sc_asn1_find_tag(ctx, p, len, 0x85, &taglen);
374        if (tag != NULL && taglen) {
375                sc_file_set_prop_attr(file, tag, taglen); 
376        } else
377                file->prop_attr_len = 0;
378        tag = sc_asn1_find_tag(ctx, p, len, 0xA5, &taglen);
379        if (tag != NULL && taglen) {
380                sc_file_set_prop_attr(file, tag, taglen); 
381        }
382        tag = sc_asn1_find_tag(ctx, p, len, 0x86, &taglen);
383        if (tag != NULL && taglen) {
384                sc_file_set_sec_attr(file, tag, taglen); 
385        }
386        tag = sc_asn1_find_tag(ctx, p, len, 0x8A, &taglen);
387        if (tag != NULL && taglen==1) {
388                if (tag[0] == 0x01)
389                        file->status = SC_FILE_STATUS_CREATION;
390                else if (tag[0] == 0x07 || tag[0] == 0x05)
391                        file->status = SC_FILE_STATUS_ACTIVATED;
392                else if (tag[0] == 0x06 || tag[0] == 0x04)
393                        file->status = SC_FILE_STATUS_INVALIDATED;
394        }
395        file->magic = SC_FILE_MAGIC;
396
397        return 0;
398 }
399
400 static int iso7816_select_file(sc_card_t *card,
401                               const sc_path_t *in_path,
402                               sc_file_t **file_out)
403 {
404        sc_context_t *ctx;
405        sc_apdu_t apdu;
406        u8 buf[SC_MAX_APDU_BUFFER_SIZE];
407        u8 pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf;
408        int r, pathlen;
409        sc_file_t *file = NULL;
410
411        assert(card != NULL && in_path != NULL);
412        ctx = card->ctx;
413        memcpy(path, in_path->value, in_path->len);
414        pathlen = in_path->len;
415
416        sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0, 0);
417       
418        switch (in_path->type) {
419        case SC_PATH_TYPE_FILE_ID:
420                apdu.p1 = 0;
421                if (pathlen != 2)
422                        return SC_ERROR_INVALID_ARGUMENTS;
423                break;
424        case SC_PATH_TYPE_DF_NAME:
425                apdu.p1 = 4;
426                break;
427        case SC_PATH_TYPE_PATH:
428                apdu.p1 = 8;
429                if (pathlen >= 2 && memcmp(path, "\x3F\x00", 2) == 0) {
430                        if (pathlen == 2) {     /* only 3F00 supplied */
431                                apdu.p1 = 0;
432                                break;
433                        }
434                        path += 2;
435                        pathlen -= 2;
436                }
437                break;
438        case SC_PATH_TYPE_FROM_CURRENT:
439                apdu.p1 = 9;
440                break;
441        case SC_PATH_TYPE_PARENT:
442                apdu.p1 = 3;
443                pathlen = 0;
444                apdu.cse = SC_APDU_CASE_2_SHORT;
445                break;
446        default:
447                SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS);
448        }
449        apdu.p2 = 0;            /* first record, return FCI */
450        apdu.lc = pathlen;
451        apdu.data = path;
452        apdu.datalen = pathlen;
453
454        if (file_out != NULL) {
455                apdu.resp = buf;
456                apdu.resplen = sizeof(buf);
457                apdu.le = card->max_recv_size > 0 ? card->max_recv_size : 256;
458        } else
459                apdu.cse = (apdu.lc == 0) ? SC_APDU_CASE_1 : SC_APDU_CASE_3_SHORT;
460
461        r = sc_transmit_apdu(card, &apdu);
462        SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
463        if (file_out == NULL) {
464                if (apdu.sw1 == 0x61)
465                        SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, 0);
466                SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2));
467        }
468
469        r = sc_check_sw(card, apdu.sw1, apdu.sw2);
470        if (r)
471                SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r);
472
473        if (apdu.resplen < 2)
474                SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_UNKNOWN_DATA_RECEIVED);
475        switch (apdu.resp[0]) {
476        case ISO7816_TAG_FCI:
477        case ISO7816_TAG_FCP:
478                file = sc_file_new();
479                if (file == NULL)
480                        SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY);
481                file->path = *in_path;
482                if (card->ops->process_fci == NULL) {
483                        sc_file_free(file);
484                        SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED);
485                }
486                if ((size_t)apdu.resp[1] + 2 <= apdu.resplen)
487                        card->ops->process_fci(card, file, apdu.resp+2, apdu.resp[1]);
488                *file_out = file;
489                break;
490        case 0x00: /* proprietary coding */
491                SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_UNKNOWN_DATA_RECEIVED);
492        default:
493                SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_UNKNOWN_DATA_RECEIVED);
494        }
495        return SC_SUCCESS;
496 }
497
498 static int iso7816_get_challenge(sc_card_t *card, u8 *rnd, size_t len)
499 {
500        int r;
501        sc_apdu_t apdu;
502        u8 buf[10];
503
504        if (!rnd)
505                return SC_ERROR_INVALID_ARGUMENTS;
506
507        sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT,
508                       0x84, 0x00, 0x00);
509        apdu.le = 8;
510        apdu.resp = buf;
511        apdu.resplen = 8;       /* include SW's */
512
513        while (len > 0) {
514                size_t n = len > 8 ? 8 : len;
515               
516                r = sc_transmit_apdu(card, &apdu);
517                SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
518                if (apdu.resplen != 8)
519                        return sc_check_sw(card, apdu.sw1, apdu.sw2);
520                memcpy(rnd, apdu.resp, n);
521                len -= n;
522                rnd += n;
523        }       
524        return 0;
525 }
526
527 static int iso7816_construct_fci(sc_card_t *card, const sc_file_t *file,
528        u8 *out, size_t *outlen)
529 {
530        u8 *p = out;
531        u8 buf[64];
532
533        if (*outlen < 2)
534                return SC_ERROR_BUFFER_TOO_SMALL;
535        *p++ = 0x6F;
536        p++;
537       
538        buf[0] = (file->size >> 8) & 0xFF;
539        buf[1] = file->size & 0xFF;
540        sc_asn1_put_tag(0x81, buf, 2, p, *outlen - (p - out), &p);
541
542        if (file->type_attr_len) {
543                assert(sizeof(buf) >= file->type_attr_len);
544                memcpy(buf, file->type_attr, file->type_attr_len);
545                sc_asn1_put_tag(0x82, buf, file->type_attr_len,
546                                p, *outlen - (p - out), &p);
547        } else {
548                buf[0] = file->shareable ? 0x40 : 0;
549                switch (file->type) {
550                case SC_FILE_TYPE_INTERNAL_EF:
551                        buf[0] |= 0x08;
552                case SC_FILE_TYPE_WORKING_EF:
553                        buf[0] |= file->ef_structure & 7;
554                        break;
555                case SC_FILE_TYPE_DF:
556                        buf[0] |= 0x38;
557                        break;
558                default:
559                        return SC_ERROR_NOT_SUPPORTED;
560                }
561                sc_asn1_put_tag(0x82, buf, 1, p, *outlen - (p - out), &p);
562        }
563        buf[0] = (file->id >> 8) & 0xFF;
564        buf[1] = file->id & 0xFF;
565        sc_asn1_put_tag(0x83, buf, 2, p, *outlen - (p - out), &p);
566        /* 0x84 = DF name */
567        if (file->prop_attr_len) {
568                assert(sizeof(buf) >= file->prop_attr_len);
569                memcpy(buf, file->prop_attr, file->prop_attr_len);
570                sc_asn1_put_tag(0x85, buf, file->prop_attr_len,
571                                p, *outlen - (p - out), &p);
572        }
573        if (file->sec_attr_len) {
574                assert(sizeof(buf) >= file->prop_attr_len);
575                memcpy(buf, file->sec_attr, file->sec_attr_len);
576                sc_asn1_put_tag(0x86, buf, file->sec_attr_len,
577                                p, *outlen - (p - out), &p);
578        }
579        out[1] = p - out - 2;
580
581        *outlen = p - out;
582        return 0;
583 }
584
585 static int iso7816_create_file(sc_card_t *card, sc_file_t *file)
586 {
587        int r;
588        size_t len;
589        u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
590        sc_apdu_t apdu;
591
592        len = SC_MAX_APDU_BUFFER_SIZE;
593
594        if (card->ops->construct_fci == NULL)
595                SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED);
596        r = card->ops->construct_fci(card, file, sbuf, &len);
597        SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "construct_fci() failed");
598       
599        sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x00, 0x00);
600        apdu.lc = len;
601        apdu.datalen = len;
602        apdu.data = sbuf;
603
604        r = sc_transmit_apdu(card, &apdu);
605        SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
606        return sc_check_sw(card, apdu.sw1, apdu.sw2);
607 }
608
609 static int iso7816_get_response(sc_card_t *card, size_t *count, u8 *buf)
610 {
611        sc_apdu_t apdu;
612        int r;
613        size_t rlen;
614
615        /* request at most max_recv_size bytes */
616        if (card->max_recv_size > 0 && *count > card->max_recv_size)
617                rlen = card->max_recv_size;
618        else
619                rlen = *count;
620
621        sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xC0, 0x00, 0x00);
622        apdu.le      = rlen;
623        apdu.resplen = rlen;
624        apdu.resp    = buf;
625        /* don't call GET RESPONSE recursively */
626        apdu.flags  |= SC_APDU_FLAGS_NO_GET_RESP;
627
628        r = sc_transmit_apdu(card, &apdu);
629        SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
630        if (apdu.resplen == 0)
631                SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2));
632
633        *count = apdu.resplen;
634
635        if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00)
636                r = 0;                                  /* no more data to read */
637        else if (apdu.sw1 == 0x61)
638                r = apdu.sw2 == 0 ? 256 : apdu.sw2;     /* more data to read    */
639        else if (apdu.sw1 == 0x62 && apdu.sw2 == 0x82)
640                r = 0; /* Le not reached but file/record ended */
641        else
642                r = sc_check_sw(card, apdu.sw1, apdu.sw2);
643
644        return r;
645 }
646
647 static int iso7816_delete_file(sc_card_t *card, const sc_path_t *path)
648 {
649        int r;
650        u8 sbuf[2];
651        sc_apdu_t apdu;
652
653        SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
654        if (path->type != SC_PATH_TYPE_FILE_ID || (path->len != 0 && path->len != 2)) {
655                sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "File type has to be SC_PATH_TYPE_FILE_ID");
656                SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS);
657        }
658
659        if (path->len == 2) {
660                sbuf[0] = path->value[0];
661                sbuf[1] = path->value[1];
662                sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE4, 0x00, 0x00);
663                apdu.lc = 2;
664                apdu.datalen = 2;
665        }
666        else /* No file ID given: means currently selected file */
667                sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xE4, 0x00, 0x00);
668        apdu.data = sbuf;
669       
670        r = sc_transmit_apdu(card, &apdu);
671        SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
672        return sc_check_sw(card, apdu.sw1, apdu.sw2);
673 }
674
675 static int iso7816_set_security_env(sc_card_t *card,
676                                    const sc_security_env_t *env,
677                                    int se_num)
678 {
679        sc_apdu_t apdu;
680        u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
681        u8 *p;
682        int r, locked = 0;
683
684        assert(card != NULL && env != NULL);
685        sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0);
686        switch (env->operation) {
687        case SC_SEC_OPERATION_DECIPHER:
688                apdu.p2 = 0xB8;
689                break;
690        case SC_SEC_OPERATION_SIGN:
691                apdu.p2 = 0xB6;
692                break;
693        default:
694                return SC_ERROR_INVALID_ARGUMENTS;
695        }
696        p = sbuf;
697        if (env->flags & SC_SEC_ENV_ALG_REF_PRESENT) {
698                *p++ = 0x80;    /* algorithm reference */
699                *p++ = 0x01;
700                *p++ = env->algorithm_ref & 0xFF;
701        }
702        if (env->flags & SC_SEC_ENV_FILE_REF_PRESENT) {
703                *p++ = 0x81;
704                *p++ = env->file_ref.len;
705                assert(sizeof(sbuf) - (p - sbuf) >= env->file_ref.len);
706                memcpy(p, env->file_ref.value, env->file_ref.len);
707                p += env->file_ref.len;
708        }
709        if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) {
710                if (env->flags & SC_SEC_ENV_KEY_REF_ASYMMETRIC)
711                        *p++ = 0x83;
712                else
713                        *p++ = 0x84;
714                *p++ = env->key_ref_len;
715                assert(sizeof(sbuf) - (p - sbuf) >= env->key_ref_len);
716                memcpy(p, env->key_ref, env->key_ref_len);
717                p += env->key_ref_len;
718        }
719        r = p - sbuf;
720        apdu.lc = r;
721        apdu.datalen = r;
722        apdu.data = sbuf;
723        if (se_num > 0) {
724                r = sc_lock(card);
725                SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "sc_lock() failed");
726                locked = 1;
727        }
728        if (apdu.datalen != 0) {
729                r = sc_transmit_apdu(card, &apdu);
730                if (r) {
731                        sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
732                                "%s: APDU transmit failed", sc_strerror(r));
733                        goto err;
734                }
735                r = sc_check_sw(card, apdu.sw1, apdu.sw2);
736                if (r) {
737                        sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
738                                "%s: Card returned error", sc_strerror(r));
739                        goto err;
740                }
741        }
742        if (se_num <= 0)
743                return 0;
744        sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0xF2, se_num);
745        r = sc_transmit_apdu(card, &apdu);
746        sc_unlock(card);
747        SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
748        return sc_check_sw(card, apdu.sw1, apdu.sw2);
749 err:
750        if (locked)
751                sc_unlock(card);
752        return r;
753 }
754
755 static int iso7816_restore_security_env(sc_card_t *card, int se_num)
756 {
757        sc_apdu_t apdu;
758        int r;
759        u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
760       
761        assert(card != NULL);
762
763        sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x22, 0xF3, se_num);
764        apdu.resplen = sizeof(rbuf) > 250 ? 250 : sizeof(rbuf);
765        apdu.resp = rbuf;
766        r = sc_transmit_apdu(card, &apdu);
767        SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
768        return sc_check_sw(card, apdu.sw1, apdu.sw2);
769 }
770
771 static int iso7816_compute_signature(sc_card_t *card,
772                                     const u8 * data, size_t datalen,
773                                     u8 * out, size_t outlen)
774 {
775        int r;
776        sc_apdu_t apdu;
777        u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
778        u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
779
780        assert(card != NULL && data != NULL && out != NULL);
781        if (datalen > 255)
782                SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS);
783
784        /* INS: 0x2A  PERFORM SECURITY OPERATION
785         * P1:  0x9E  Resp: Digital Signature
786         * P2:  0x9A  Cmd: Input for Digital Signature */
787        sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x9E,
788                       0x9A);
789        apdu.resp = rbuf;
790        apdu.resplen = sizeof(rbuf); /* FIXME */
791        apdu.le = 256;
792
793        memcpy(sbuf, data, datalen);
794        apdu.data = sbuf;
795        apdu.lc = datalen;
796        apdu.datalen = datalen;
797        r = sc_transmit_apdu(card, &apdu);
798        SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
799        if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) {
800                size_t len = apdu.resplen > outlen ? outlen : apdu.resplen;
801
802                memcpy(out, apdu.resp, len);
803                SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len);
804        }
805        SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2));
806 }
807
808 static int iso7816_decipher(sc_card_t *card,
809                            const u8 * crgram, size_t crgram_len,
810                            u8 * out, size_t outlen)
811 {
812        int       r;
813        sc_apdu_t apdu;
814        u8        *sbuf = NULL;
815
816        assert(card != NULL && crgram != NULL && out != NULL);
817        SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL);
818
819        sbuf = malloc(crgram_len + 1);
820        if (sbuf == NULL)
821                return SC_ERROR_OUT_OF_MEMORY;
822
823        /* INS: 0x2A  PERFORM SECURITY OPERATION
824         * P1:  0x80  Resp: Plain value
825         * P2:  0x86  Cmd: Padding indicator byte followed by cryptogram */
826        sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x2A, 0x80, 0x86);
827        apdu.resp    = out;
828        apdu.resplen = outlen;
829        /* if less than 256 bytes are expected than set Le to 0x00
830         * to tell the card the we want everything available (note: we
831         * always have Le <= crgram_len) */
832        apdu.le      = (outlen >= 256 && crgram_len < 256) ? 256 : outlen;
833        /* Use APDU chaining with 2048bit RSA keys if the card does not do extended APDU-s */
834        if ((crgram_len+1 > 255) && !(card->caps & SC_CARD_CAP_APDU_EXT))
835                apdu.flags |= SC_APDU_FLAGS_CHAINING;
836       
837        sbuf[0] = 0; /* padding indicator byte, 0x00 = No further indication */
838        memcpy(sbuf + 1, crgram, crgram_len);
839        apdu.data = sbuf;
840        apdu.lc = crgram_len + 1;
841        apdu.datalen = crgram_len + 1;
842        r = sc_transmit_apdu(card, &apdu);
843        sc_mem_clear(sbuf, crgram_len + 1);
844        free(sbuf);
845        SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
846        if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00)
847                SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, apdu.resplen);
848        else
849                SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2));
850 }
851
852 static int iso7816_build_pin_apdu(sc_card_t *card, sc_apdu_t *apdu,
853                struct sc_pin_cmd_data *data, u8 *buf, size_t buf_len)
854 {
855        int r, len = 0, pad = 0, use_pin_pad = 0, ins, p1 = 0;
856       
857        switch (data->pin_type) {
858        case SC_AC_CHV:
859                break;
860        default:
861                return SC_ERROR_INVALID_ARGUMENTS;
862        }
863
864        if (data->flags & SC_PIN_CMD_NEED_PADDING)
865                pad = 1;
866        if (data->flags & SC_PIN_CMD_USE_PINPAD)
867                use_pin_pad = 1;
868
869        data->pin1.offset = 5;
870
871        switch (data->cmd) {
872        case SC_PIN_CMD_VERIFY:
873                ins = 0x20;
874                if ((r = sc_build_pin(buf, buf_len, &data->pin1, pad)) < 0)
875                        return r;
876                len = r;
877                break;
878        case SC_PIN_CMD_CHANGE:
879                ins = 0x24;
880                if (data->pin1.len != 0 || use_pin_pad) {
881                        if ((r = sc_build_pin(buf, buf_len, &data->pin1, pad)) < 0)
882                                return r;
883                        len += r;
884                } else {
885                        /* implicit test */
886                        p1 = 1;
887                }
888
889                data->pin2.offset = data->pin1.offset + len;
890                if ((r = sc_build_pin(buf+len, buf_len-len, &data->pin2, pad)) < 0)
891                        return r;
892                len += r;
893                break;
894        case SC_PIN_CMD_UNBLOCK:
895                ins = 0x2C;
896                if (data->pin1.len != 0 || use_pin_pad) {
897                        if ((r = sc_build_pin(buf, buf_len, &data->pin1, pad)) < 0)
898                                return r;
899                        len += r;
900                } else {
901                        p1 |= 0x02;
902                }
903
904                if (data->pin2.len != 0 || use_pin_pad) {
905                        data->pin2.offset = data->pin1.offset + len;
906                        if ((r = sc_build_pin(buf+len, buf_len-len, &data->pin2, pad)) < 0)
907                                return r;
908                        len += r;
909                } else {
910                        p1 |= 0x01;
911                }
912                break;
913        default:
914                return SC_ERROR_NOT_SUPPORTED;
915        }
916
917        sc_format_apdu(card, apdu, SC_APDU_CASE_3_SHORT,
918                                ins, p1, data->pin_reference);
919
920        apdu->lc = len;
921        apdu->datalen = len;
922        apdu->data = buf;
923        apdu->resplen = 0;
924
925        return 0;
926 }
927
928 static int iso7816_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data,
929                           int *tries_left)
930 {
931        sc_apdu_t local_apdu, *apdu;
932        int r;
933        u8  sbuf[SC_MAX_APDU_BUFFER_SIZE];
934
935        if (tries_left)
936                *tries_left = -1;
937
938        /* See if we've been called from another card driver, which is
939         * passing an APDU to us (this allows to write card drivers
940         * whose PIN functions behave "mostly like ISO" except in some
941         * special circumstances.
942         */
943        if (data->apdu == NULL) {
944                r = iso7816_build_pin_apdu(card, &local_apdu, data, sbuf, sizeof(sbuf));
945                if (r < 0)
946                        return r;
947                data->apdu = &local_apdu;
948        }
949        apdu = data->apdu;
950
951        if (!(data->flags & SC_PIN_CMD_USE_PINPAD)) {
952                /* Transmit the APDU to the card */
953                r = sc_transmit_apdu(card, apdu);
954
955                /* Clear the buffer - it may contain pins */
956                sc_mem_clear(sbuf, sizeof(sbuf));
957        } else {
958                /* Call the reader driver to collect
959                 * the PIN and pass on the APDU to the card */
960                if (data->pin1.offset == 0) {
961                        sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
962                                "Card driver didn't set PIN offset");
963                        return SC_ERROR_INVALID_ARGUMENTS;
964                }
965                if (card->reader
966                 && card->reader->ops
967                 && card->reader->ops->perform_verify) {
968                        r = card->reader->ops->perform_verify(card->reader, data);
969                        /* sw1/sw2 filled in by reader driver */
970                } else {
971                        sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
972                                "Card reader driver does not support "
973                                "PIN entry through reader key pad");
974                        r = SC_ERROR_NOT_SUPPORTED;
975                }
976        }
977
978        /* Don't pass references to local variables up to the caller. */
979        if (data->apdu == &local_apdu)
980                data->apdu = NULL;
981
982        SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
983        if (apdu->sw1 == 0x63) {
984                if ((apdu->sw2 & 0xF0) == 0xC0 && tries_left != NULL)
985                        *tries_left = apdu->sw2 & 0x0F;
986                return SC_ERROR_PIN_CODE_INCORRECT;
987        }
988        return sc_check_sw(card, apdu->sw1, apdu->sw2);
989 }
990
991 static int no_match(sc_card_t *card)
992 {
993        return 0;
994 }
995
996 static struct sc_card_operations iso_ops = {
997        no_match,
998        NULL,                   /* init   */
999        NULL,                   /* finish */
1000        iso7816_read_binary,
1001        iso7816_write_binary,
1002        iso7816_update_binary,
1003        NULL,                   /* erase_binary */
1004        iso7816_read_record,
1005        iso7816_write_record,
1006        iso7816_append_record,
1007        iso7816_update_record,
1008        iso7816_select_file,
1009        iso7816_get_response,
1010        iso7816_get_challenge,
1011        NULL,                   /* verify */
1012        NULL,                   /* logout */
1013        iso7816_restore_security_env,
1014        iso7816_set_security_env,
1015        iso7816_decipher,
1016        iso7816_compute_signature,
1017        NULL,                   /* change_reference_data */
1018        NULL,                   /* reset_retry_counter   */
1019        iso7816_create_file,
1020        iso7816_delete_file,
1021        NULL,                   /* list_files */
1022        iso7816_check_sw,
1023        NULL,                   /* card_ctl */
1024        iso7816_process_fci,
1025        iso7816_construct_fci,
1026        iso7816_pin_cmd,
1027        NULL,                   /* get_data */
1028        NULL,                   /* put_data */
1029        NULL                    /* delete_record */
1030 };
1031
1032 static struct sc_card_driver iso_driver = {
1033        "ISO 7816 reference driver",
1034        "iso7816",
1035        &iso_ops,
1036        NULL, 0, NULL
1037 };
1038
1039 struct sc_card_driver * sc_get_iso7816_driver(void)
1040 {
1041        return &iso_driver;
1042 }
阅读(7103) | 评论(4) | 转发(1) |
给主人留下些什么吧!~~

王_JM1232019-08-13 11:03:50

求7816的头文件都分享一下,谢谢了

王_JM1232019-08-13 11:03:46

求7816的头文件都分享一下,谢谢了

杨讯2018-11-19 11:27:34

也是,对应的7816.H文件共享一下,谢谢

a3055662016-07-29 14:40:33

对应的7816.H文件共享一下,谢谢