/src/opensc/src/libopensc/card-belpic.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * card-belpic.c: Support for Belgium EID card |
3 | | * |
4 | | * Copyright (C) 2003, Zetes Belgium |
5 | | * |
6 | | * This library is free software; you can redistribute it and/or |
7 | | * modify it under the terms of the GNU Lesser General Public |
8 | | * License as published by the Free Software Foundation; either |
9 | | * version 2.1 of the License, or (at your option) any later version. |
10 | | * |
11 | | * This library is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | | * Lesser General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU Lesser General Public |
17 | | * License along with this library; if not, write to the Free Software |
18 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
19 | | */ |
20 | | |
21 | | /* About the Belpic (Belgian Personal Identity Card) card |
22 | | * |
23 | | * The Belpic card is a Cyberflex Java card, so you normally communicate |
24 | | * with an applet running on the card. In order to support a pkcs15 file |
25 | | * structure, an applet (the Belpic applet) has been build that emulates |
26 | | * this. So the card's behaviour is specific for this Belpic applet, that's |
27 | | * why a separate driver has been made. |
28 | | * |
29 | | * The card contains the citizen's ID data (name, address, photo, ...) and |
30 | | * her keys and certs. The ID data are in a separate directory on the card and |
31 | | * are not handled by this software. For the cryptographic data (keys and certs) |
32 | | * a pkcs#15 structure has been chosen and they can be accessed and used |
33 | | * by the OpenSC software. |
34 | | * |
35 | | * The current situation about the cryptographic data is: there is 1 PIN |
36 | | * that protects 2 private keys and corresponding certs. Then there is a |
37 | | * CA cert and the root cert. The first key (Auth Key) can be used for |
38 | | * authentication, the second one (NonRep Key) for non repudiation purposes |
39 | | * (so it can be used as an alternative to manual signatures). |
40 | | * |
41 | | * There are some special things to note, which all have some consequences: |
42 | | * (1) the SELECT FILE command doesn't return any FCI (file length, type, ...) |
43 | | * (2) the NonRep key needs a VERIFY PIN before a signature can be done with it |
44 | | * (3) pin pad readers had to be supported by a proprietary interface (as at |
45 | | * that moment no other solution was known/available/ready) |
46 | | * The consequences are: |
47 | | * |
48 | | * For (1): we let the SELECT FILE command return that the file length is |
49 | | * a fixed large number and that each file is a transparent working EF |
50 | | * (except the root dir 3F 00). This way however, there is a problem with the |
51 | | * sc_read_binary() function that will only stop reading until it receives |
52 | | * a 0. Therefore, we use the 'next_idx' trick. Or, if that might fail |
53 | | * and so a READ BINARY past the end of file is done, length 0 is returned |
54 | | * instead of an error code. |
55 | | * |
56 | | * For (2), we decided that a GUI for asking the PIN would be the best |
57 | | * thing to do (another option would be to make 2 virtual slots but that |
58 | | * causes other problems and is less user-friendly). A GUI being popped up |
59 | | * by the pkcs11 lib before each NonRep signature has another important |
60 | | * security advantage: applications that cache the PIN can't silently do |
61 | | * a NonRep signature because there will always be the GUI. |
62 | | * |
63 | | * For (3), we link dynamically against a pin pad lib (DLL) that implements the |
64 | | * proprietary API for a specific pin pad. For each pin pad reader (identified |
65 | | * by it's PC/SC reader name), a pin pad lib corresponds. Some reader/lib |
66 | | * name pairs are hardcoded, and others can be added in the config file. |
67 | | * Note that there's also a GUI used in this case: if a signature with the |
68 | | * NonRep key is done: a dialog box is shown that asks the user to enter |
69 | | * her PIN on the pin pad reader in order to make a legally valid signature. |
70 | | * |
71 | | * Further the (current) Belpic card as quite some limitations: |
72 | | * no key pair generation or update of data except after establishing a Secure |
73 | | * Channel or CTV-authentication (which can only be done at the municipalities), |
74 | | * no encryption. The result is that only a very limited amount of functions |
75 | | * is/had to be implemented to get the pkcs11 library working. |
76 | | * |
77 | | * About the belpic_set_language: the RA-PC software (including the pkcs11 lib) |
78 | | * in the Brussels' communities should be able to change the language of the GUI |
79 | | * messages. So the language set by this function takes priority on all other |
80 | | * language-selection functionality. |
81 | | */ |
82 | | |
83 | | #ifdef HAVE_CONFIG_H |
84 | | #include "config.h" |
85 | | #endif |
86 | | |
87 | | #include <stdlib.h> |
88 | | #include <string.h> |
89 | | |
90 | | #include "internal.h" |
91 | | #include "log.h" |
92 | | |
93 | | /* To be removed */ |
94 | | #include <time.h> |
95 | | static long t1, t2, tot_read = 0, tot_dur = 0, dur; |
96 | | |
97 | 0 | #define BELPIC_VERSION "1.4" |
98 | | |
99 | | /* Most of the #defines here are also present in the pkcs15 files, but |
100 | | * because this driver has no access to them, it's hardcoded here. If |
101 | | * other Belpic cards with other 'settings' appear, we'll have to move |
102 | | * these #defines to the struct belpic_priv_data */ |
103 | 0 | #define BELPIC_MAX_FILE_SIZE 65535 |
104 | | #define BELPIC_PIN_BUF_SIZE 8 |
105 | 0 | #define BELPIC_MIN_USER_PIN_LEN 4 |
106 | 0 | #define BELPIC_MAX_USER_PIN_LEN 12 |
107 | 0 | #define BELPIC_PIN_ENCODING SC_PIN_ENCODING_GLP |
108 | 0 | #define BELPIC_PAD_CHAR 0xFF |
109 | 0 | #define BELPIC_KEY_REF_NONREP 0x83 |
110 | | |
111 | | /* Data in the return value for the GET CARD DATA command: |
112 | | * All fields are one byte, except when noted otherwise. |
113 | | * |
114 | | * See ยง6.9 in |
115 | | * https://github.com/Fedict/eid-mw/blob/master/doc/sdk/documentation/Public_Belpic_Applet_v1%207_Ref_Manual%20-%20A01.pdf |
116 | | * for the full documentation on the GET CARD DATA command. |
117 | | */ |
118 | | // Card serial number (16 bytes) |
119 | | #define BELPIC_CARDDATA_OFF_SERIALNUM 0 |
120 | | // "Component code" |
121 | | #define BELPIC_CARDDATA_OFF_COMPCODE 16 |
122 | | // "OS number" |
123 | | #define BELPIC_CARDDATA_OFF_OSNUM 17 |
124 | | // "OS version" |
125 | | #define BELPIC_CARDDATA_OFF_OSVER 18 |
126 | | // "Softmask number" |
127 | | #define BELPIC_CARDDATA_OFF_SMNUM 19 |
128 | | // "Softmask version" |
129 | | #define BELPIC_CARDDATA_OFF_SMVER 20 |
130 | | // Applet version |
131 | 0 | #define BELPIC_CARDDATA_OFF_APPLETVERS 21 |
132 | | // Global OS version (2 bytes) |
133 | | #define BELPIC_CARDDATA_OFF_GL_OSVE 22 |
134 | | // Applet interface version |
135 | | #define BELPIC_CARDDATA_OFF_APPINTVERS 24 |
136 | | // PKCS#1 support version |
137 | | #define BELPIC_CARDDATA_OFF_PKCS1 25 |
138 | | // Key exchange version |
139 | | #define BELPIC_CARDDATA_OFF_KEYX 26 |
140 | | // Applet life cycle (Should always be 0F for released cards, is 07 when not issued yet) |
141 | | #define BELPIC_CARDDATA_OFF_APPLCYCLE 27 |
142 | | // Full length of reply |
143 | | #define BELPIC_CARDDATA_RESP_LEN 28 |
144 | | |
145 | | /* Used for a trick in select file and read binary */ |
146 | | static size_t next_idx = (size_t)-1; |
147 | | |
148 | | static const struct sc_atr_table belpic_atrs[] = { |
149 | | /* Applet V1.8 */ |
150 | | {"3B:7F:96:00:00:80:31:80:65:B0:85:04:01:20:12:0F:FF:82:90:00", NULL, NULL, SC_CARD_TYPE_BELPIC_EID, 0, NULL}, |
151 | | /* Applet V1.1 */ |
152 | | {"3B:98:13:40:0A:A5:03:01:01:01:AD:13:11", NULL, NULL, SC_CARD_TYPE_BELPIC_EID, 0, NULL}, |
153 | | /* Applet V1.0 with new EMV-compatible ATR */ |
154 | | {"3B:98:94:40:0A:A5:03:01:01:01:AD:13:10", NULL, NULL, SC_CARD_TYPE_BELPIC_EID, 0, NULL}, |
155 | | /* Applet beta 5 + V1.0 */ |
156 | | {"3B:98:94:40:FF:A5:03:01:01:01:AD:13:10", NULL, NULL, SC_CARD_TYPE_BELPIC_EID, 0, NULL}, |
157 | | {NULL, NULL, NULL, 0, 0, NULL} |
158 | | }; |
159 | | |
160 | | static struct sc_card_operations belpic_ops; |
161 | | static struct sc_card_driver belpic_drv = { |
162 | | "Belpic cards", |
163 | | "belpic", |
164 | | &belpic_ops, |
165 | | NULL, 0, NULL |
166 | | }; |
167 | | static const struct sc_card_operations *iso_ops = NULL; |
168 | | |
169 | | static int get_carddata(sc_card_t *card, u8* carddata_loc, unsigned int carddataloc_len) |
170 | 0 | { |
171 | 0 | sc_apdu_t apdu; |
172 | 0 | u8 carddata_cmd[] = { 0x80, 0xE4, 0x00, 0x00, 0x1C }; |
173 | 0 | int r; |
174 | |
|
175 | 0 | assert(carddataloc_len == BELPIC_CARDDATA_RESP_LEN); |
176 | | |
177 | 0 | r = sc_bytes2apdu(card->ctx, carddata_cmd, sizeof(carddata_cmd), &apdu); |
178 | 0 | if(r) { |
179 | 0 | sc_log(card->ctx, "bytes to APDU conversion failed: %d\n", r); |
180 | 0 | return r; |
181 | 0 | } |
182 | | |
183 | 0 | apdu.resp = carddata_loc; |
184 | 0 | apdu.resplen = carddataloc_len; |
185 | |
|
186 | 0 | r = sc_transmit_apdu(card, &apdu); |
187 | 0 | if(r) { |
188 | 0 | sc_log(card->ctx, "GetCardData command failed: %d\n", r); |
189 | 0 | return r; |
190 | 0 | } |
191 | | |
192 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
193 | 0 | if(r) { |
194 | 0 | sc_log(card->ctx, "GetCardData: card returned %d\n", r); |
195 | 0 | return r; |
196 | 0 | } |
197 | 0 | if(apdu.resplen < carddataloc_len) { |
198 | 0 | sc_log(card->ctx, |
199 | 0 | "GetCardData: card returned %"SC_FORMAT_LEN_SIZE_T"u bytes rather than expected %d\n", |
200 | 0 | apdu.resplen, carddataloc_len); |
201 | 0 | return SC_ERROR_WRONG_LENGTH; |
202 | 0 | } |
203 | | |
204 | 0 | return 0; |
205 | 0 | } |
206 | | |
207 | | static int belpic_match_card(sc_card_t *card) |
208 | 0 | { |
209 | 0 | int i; |
210 | |
|
211 | 0 | i = _sc_match_atr(card, belpic_atrs, &card->type); |
212 | 0 | if (i < 0) |
213 | 0 | return 0; |
214 | 0 | return 1; |
215 | 0 | } |
216 | | |
217 | | static int belpic_init(sc_card_t *card) |
218 | 0 | { |
219 | 0 | int key_size = 1024; |
220 | 0 | unsigned long flags = 0; |
221 | |
|
222 | 0 | sc_log(card->ctx, "Belpic V%s\n", BELPIC_VERSION); |
223 | |
|
224 | 0 | if (card->type < 0) |
225 | 0 | card->type = SC_CARD_TYPE_BELPIC_EID; /* Unknown card: assume it's the Belpic Card */ |
226 | |
|
227 | 0 | card->cla = 0x00; |
228 | 0 | if (card->type == SC_CARD_TYPE_BELPIC_EID) { |
229 | 0 | u8 carddata[BELPIC_CARDDATA_RESP_LEN]; |
230 | 0 | memset(carddata, 0, sizeof(carddata)); |
231 | |
|
232 | 0 | if(get_carddata(card, carddata, sizeof(carddata)) < 0) { |
233 | 0 | return SC_ERROR_INVALID_CARD; |
234 | 0 | } |
235 | 0 | if (carddata[BELPIC_CARDDATA_OFF_APPLETVERS] >= 0x18) { |
236 | 0 | flags = SC_ALGORITHM_ECDSA_HASH_NONE | SC_ALGORITHM_ECDSA_RAW; |
237 | 0 | _sc_card_add_ec_alg(card, 256, flags, 0, NULL); |
238 | 0 | _sc_card_add_ec_alg(card, 384, flags, 0, NULL); |
239 | 0 | _sc_card_add_ec_alg(card, 512, flags, 0, NULL); |
240 | 0 | _sc_card_add_ec_alg(card, 521, flags, 0, NULL); |
241 | 0 | } else { |
242 | 0 | if (carddata[BELPIC_CARDDATA_OFF_APPLETVERS] >= 0x17) { |
243 | 0 | key_size = 2048; |
244 | 0 | } |
245 | 0 | _sc_card_add_rsa_alg(card, key_size, |
246 | 0 | SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE, 0); |
247 | 0 | } |
248 | 0 | } |
249 | | |
250 | | /* State that we have an RNG */ |
251 | 0 | card->caps |= SC_CARD_CAP_RNG; |
252 | |
|
253 | 0 | card->max_pin_len = BELPIC_MAX_USER_PIN_LEN; |
254 | |
|
255 | 0 | return 0; |
256 | 0 | } |
257 | | |
258 | | static int belpic_select_file(sc_card_t *card, |
259 | | const sc_path_t *in_path, sc_file_t **file_out) |
260 | 0 | { |
261 | 0 | sc_apdu_t apdu; |
262 | 0 | u8 pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf; |
263 | 0 | int r; |
264 | 0 | size_t pathlen; |
265 | 0 | sc_file_t *file = NULL; |
266 | |
|
267 | 0 | assert(card != NULL && in_path != NULL); |
268 | 0 | memcpy(path, in_path->value, in_path->len); |
269 | 0 | pathlen = in_path->len; |
270 | |
|
271 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x08, 0x0C); |
272 | |
|
273 | 0 | apdu.lc = pathlen; |
274 | 0 | apdu.data = path; |
275 | 0 | apdu.datalen = pathlen; |
276 | |
|
277 | 0 | apdu.resplen = 0; |
278 | 0 | apdu.le = 0; |
279 | |
|
280 | 0 | r = sc_transmit_apdu(card, &apdu); |
281 | |
|
282 | 0 | LOG_TEST_RET(card->ctx, r, "Select File APDU transmit failed"); |
283 | | |
284 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
285 | 0 | if (r) |
286 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); |
287 | | |
288 | 0 | next_idx = (size_t)-1; /* reset */ |
289 | |
|
290 | 0 | if (file_out != NULL) { |
291 | 0 | file = sc_file_new(); |
292 | 0 | file->path = *in_path; |
293 | 0 | if (pathlen >= 2) |
294 | 0 | file->id = (in_path->value[pathlen - 2] << 8) | in_path->value[pathlen - 1]; |
295 | 0 | file->size = BELPIC_MAX_FILE_SIZE; |
296 | 0 | file->shareable = 1; |
297 | 0 | file->ef_structure = SC_FILE_EF_TRANSPARENT; |
298 | 0 | if (pathlen == 2 && memcmp("\x3F\x00", in_path->value, 2) == 0) |
299 | 0 | file->type = SC_FILE_TYPE_DF; |
300 | 0 | else |
301 | 0 | file->type = SC_FILE_TYPE_WORKING_EF; |
302 | 0 | *file_out = file; |
303 | 0 | } |
304 | |
|
305 | 0 | return 0; |
306 | 0 | } |
307 | | |
308 | | static int belpic_read_binary(sc_card_t *card, |
309 | | unsigned int idx, u8 * buf, size_t count, unsigned long *flags) |
310 | 0 | { |
311 | 0 | int r; |
312 | |
|
313 | 0 | if (next_idx == idx) |
314 | 0 | return 0; /* File was already read entirely */ |
315 | | |
316 | 0 | t1 = clock(); |
317 | 0 | r = iso_ops->read_binary(card, idx, buf, count, flags); |
318 | 0 | t2 = clock(); |
319 | | |
320 | | /* If the 'next_idx trick' shouldn't work, we hope this error |
321 | | * means that an attempt was made to read beyond the file's |
322 | | * contents, so we'll return 0 to end the loop in sc_read_binary()*/ |
323 | 0 | if (r == SC_ERROR_INCORRECT_PARAMETERS) |
324 | 0 | return 0; |
325 | | |
326 | 0 | if (r >= 0 && (size_t)r < count) |
327 | 0 | next_idx = idx + (size_t)r; |
328 | |
|
329 | 0 | dur = t2 - t1; |
330 | 0 | tot_dur += dur; |
331 | 0 | tot_read += r; |
332 | 0 | return r; |
333 | 0 | } |
334 | | |
335 | | static int belpic_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) |
336 | 0 | { |
337 | 0 | data->pin1.encoding = data->pin2.encoding = BELPIC_PIN_ENCODING; |
338 | 0 | data->pin1.pad_char = data->pin2.pad_char = BELPIC_PAD_CHAR; |
339 | 0 | data->pin1.min_length = data->pin2.min_length = BELPIC_MIN_USER_PIN_LEN; |
340 | 0 | data->pin1.max_length = data->pin2.max_length = BELPIC_MAX_USER_PIN_LEN; |
341 | 0 | data->apdu = NULL; |
342 | |
|
343 | 0 | return iso_ops->pin_cmd(card, data, tries_left); |
344 | 0 | } |
345 | | |
346 | | static int belpic_set_security_env(sc_card_t *card, |
347 | | const sc_security_env_t *env, int se_num) |
348 | 0 | { |
349 | 0 | sc_apdu_t apdu; |
350 | 0 | u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; |
351 | 0 | int r; |
352 | |
|
353 | 0 | sc_log(card->ctx, "belpic_set_security_env(), keyRef = 0x%0x, algo = 0x%0lx\n", |
354 | 0 | *env->key_ref, env->algorithm_flags); |
355 | |
|
356 | 0 | assert(card != NULL && env != NULL); |
357 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0, 0); |
358 | 0 | switch (env->operation) { |
359 | 0 | case SC_SEC_OPERATION_SIGN: |
360 | 0 | apdu.p1 = 0x41; |
361 | 0 | apdu.p2 = 0xB6; |
362 | 0 | sbuf[0] = 0x04; /* length of the following data */ |
363 | 0 | sbuf[1] = 0x80; /* tag for algorithm reference */ |
364 | 0 | if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01) |
365 | 0 | sbuf[2] = 0x01; |
366 | 0 | else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) |
367 | 0 | sbuf[2] = 0x02; |
368 | 0 | else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_MD5) |
369 | 0 | sbuf[2] = 0x04; |
370 | 0 | else if (env->algorithm_flags & SC_ALGORITHM_ECDSA_HASH_SHA256) |
371 | 0 | sbuf[2] = 0x01; |
372 | 0 | else if (env->algorithm_flags & SC_ALGORITHM_ECDSA_HASH_SHA384) |
373 | 0 | sbuf[2] = 0x02; |
374 | 0 | else if (env->algorithm_flags & SC_ALGORITHM_ECDSA_HASH_SHA512) |
375 | 0 | sbuf[2] = 0x04; |
376 | 0 | else if (env->algorithm_flags & SC_ALGORITHM_ECDSA_RAW) |
377 | 0 | sbuf[2] = 0x40; |
378 | 0 | else { |
379 | 0 | sc_log(card->ctx, "Set Sec Env: unsupported algo 0X%0lX\n", |
380 | 0 | env->algorithm_flags); |
381 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
382 | 0 | } |
383 | 0 | sbuf[3] = 0x84; /* tag for private key reference */ |
384 | 0 | sbuf[4] = *env->key_ref; /* key reference */ |
385 | 0 | apdu.lc = 5; |
386 | 0 | apdu.datalen = 5; |
387 | 0 | break; |
388 | 0 | default: |
389 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
390 | 0 | } |
391 | 0 | apdu.le = 0; |
392 | 0 | apdu.data = sbuf; |
393 | 0 | apdu.resplen = 0; |
394 | |
|
395 | 0 | r = sc_transmit_apdu(card, &apdu); |
396 | 0 | LOG_TEST_RET(card->ctx, r, "Set Security Env APDU transmit failed"); |
397 | | |
398 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
399 | 0 | LOG_TEST_RET(card->ctx, r, "Card's Set Security Env command returned error"); |
400 | | |
401 | | /* If a NonRep signature will be done, ask to enter a PIN. It would be more |
402 | | * logical to put the code below into the compute signature function because |
403 | | * a Verify Pin call must immediately precede a Compute Signature call. |
404 | | * It's not done because the Compute Signature is completely ISO7816 compliant |
405 | | * so we use the iso7816_compute_signature() function, and because this function |
406 | | * doesn't know about the key reference. |
407 | | * It's not a problem either, because this function is (for pkcs11) only called |
408 | | * by sc_pkcs15_compute_signature(), where the card is already locked, and |
409 | | * the next function to be executed will be the compute_signature function. |
410 | | */ |
411 | 0 | if (*env->key_ref == BELPIC_KEY_REF_NONREP) { |
412 | 0 | sc_log(card->ctx, "No GUI for NonRep key present, signature cancelled\n"); |
413 | 0 | return SC_ERROR_NOT_SUPPORTED; |
414 | 0 | } |
415 | | |
416 | 0 | return r; |
417 | 0 | } |
418 | | |
419 | | static struct sc_card_driver *sc_get_driver(void) |
420 | 0 | { |
421 | 0 | if (iso_ops == NULL) |
422 | 0 | iso_ops = sc_get_iso7816_driver()->ops; |
423 | |
|
424 | 0 | belpic_ops.match_card = belpic_match_card; |
425 | 0 | belpic_ops.init = belpic_init; |
426 | |
|
427 | 0 | belpic_ops.update_binary = iso_ops->update_binary; |
428 | 0 | belpic_ops.select_file = belpic_select_file; |
429 | 0 | belpic_ops.read_binary = belpic_read_binary; |
430 | 0 | belpic_ops.pin_cmd = belpic_pin_cmd; |
431 | 0 | belpic_ops.set_security_env = belpic_set_security_env; |
432 | |
|
433 | 0 | belpic_ops.compute_signature = iso_ops->compute_signature; |
434 | 0 | belpic_ops.get_challenge = iso_ops->get_challenge; |
435 | 0 | belpic_ops.get_response = iso_ops->get_response; |
436 | 0 | belpic_ops.check_sw = iso_ops->check_sw; |
437 | |
|
438 | 0 | return &belpic_drv; |
439 | 0 | } |
440 | | |
441 | | #if 1 |
442 | | struct sc_card_driver *sc_get_belpic_driver(void) |
443 | 0 | { |
444 | 0 | return sc_get_driver(); |
445 | 0 | } |
446 | | #endif |