This is an extremely rough prototype for handling GSM SIM cards via a serial line. My main problem with it was difficulty in getting unlocked SIM cards (or with known PIN/PUK) to test. This will probably give you a SIM that's locked to hell. Signed-off-by: Robin H. Johnson ==== Applies against r2981 of opensc trunk. Index: src/libopensc/cards.h =================================================================== --- src/libopensc/cards.h (revision 2981) +++ src/libopensc/cards.h (working copy) @@ -119,7 +119,11 @@ /* Muscle cards */ SC_CARD_TYPE_MUSCLE_BASE = 15000, - SC_CARD_TYPE_MUSCLE_GENERIC + SC_CARD_TYPE_MUSCLE_GENERIC, + + /* GSM SIM cards */ + SC_CARD_TYPE_GSM_BASE = 16000, + SC_CARD_TYPE_GSM_GENERIC }; #ifdef __cplusplus Index: src/libopensc/sc.c =================================================================== --- src/libopensc/sc.c (revision 2981) +++ src/libopensc/sc.c (working copy) @@ -646,13 +646,13 @@ p += 2; atr_len -= 2; for (i = 0; i < 4 && atr_len > 0; i++) { - if (x & (1 << i)) { - tx[i] = *p; - p++; - atr_len--; - } else - tx[i] = -1; - } + if (x & (1 << i)) { + tx[i] = *p; + p++; + atr_len--; + } else + tx[i] = -1; + } if (tx[0] >= 0) { slot->atr_info.FI = FI = tx[0] >> 4; slot->atr_info.DI = DI = tx[0] & 0x0F; Index: src/libopensc/iso7816.c =================================================================== --- src/libopensc/iso7816.c (revision 2981) +++ src/libopensc/iso7816.c (working copy) @@ -89,11 +89,11 @@ } if (sw1 == 0x90) return SC_NO_ERROR; - if (sw1 == 0x63U && (sw2 & ~0x0fU) == 0xc0U ) { - sc_error(card->ctx, "Verification failed (remaining tries: %d)\n", - (sw2 & 0x0f)); - return SC_ERROR_PIN_CODE_INCORRECT; - } + if (sw1 == 0x63U && (sw2 & ~0x0fU) == 0xc0U ) { + sc_error(card->ctx, "Verification failed (remaining tries: %d)\n", + (sw2 & 0x0f)); + return SC_ERROR_PIN_CODE_INCORRECT; + } for (i = 0; i < err_count; i++) if (iso7816_errors[i].SWs == ((sw1 << 8) | sw2)) { sc_error(card->ctx, "%s\n", iso7816_errors[i].errorstr); @@ -405,6 +405,9 @@ u8 pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf; int r, pathlen; sc_file_t *file = NULL; + /* Zero these out, so we can dump them more nicely */ + memset(buf,0,SC_MAX_APDU_BUFFER_SIZE); + memset(pathbuf,0,SC_MAX_PATH_SIZE); assert(card != NULL && in_path != NULL); ctx = card->ctx; Index: src/libopensc/errors.h =================================================================== --- src/libopensc/errors.h (revision 2981) +++ src/libopensc/errors.h (working copy) @@ -104,6 +104,14 @@ #define SC_ERROR_INVALID_PIN_REFERENCE -1009 #define SC_ERROR_FILE_TOO_SMALL -1010 +/* Relating to GSM stuff */ +#define SC_ERROR_GSM_CHVINIT -1600 +#define SC_ERROR_GSM_ACCESSDENY_RETRY -1601 // Auth PIN(CHV) or PUK(UNBLOCK CHV) needed - more attempts possible +#define SC_ERROR_GSM_ACCESSDENY_BLOCK -1602 // PIN(CHV) or PUK(UNBLOCK CHV) - no more attempts possible +#define SC_ERROR_GSM_CONTRA_CHV_STATUS -1603 // In contradiction with CHV status +#define SC_ERROR_GSM_CONTRA_INV_STATUS -1604 // In contraditction with invalidation status +#define SC_ERROR_GSM_INCREASE_MAX_REACHED -1605 + /* Errors that do not fit the categories above */ #define SC_ERROR_UNKNOWN -1900 #define SC_ERROR_PKCS15_APP_NOT_FOUND -1901 Index: src/libopensc/cardctl.h =================================================================== --- src/libopensc/cardctl.h (revision 2981) +++ src/libopensc/cardctl.h (working copy) @@ -130,7 +130,14 @@ SC_CARDCTL_MUSCLE_GENERATE_KEY, SC_CARDCTL_MUSCLE_EXTRACT_KEY, SC_CARDCTL_MUSCLE_IMPORT_KEY, - SC_CARDCTL_MUSCLE_VERIFIED_PINS + SC_CARDCTL_MUSCLE_VERIFIED_PINS, + + /* + * GSM specific calls + */ + SC_CARDCTL_GSM_BASE = _CTL_PREFIX('G','S','M'), + SC_CARDCTL_GSM_RUN_GSM_ALGORITHM, + SC_CARDCTL_GSM_SLEEP }; enum { Index: src/libopensc/ctx.c =================================================================== --- src/libopensc/ctx.c (revision 2981) +++ src/libopensc/ctx.c (working copy) @@ -70,6 +70,7 @@ #endif { "belpic", (void *(*)(void)) sc_get_belpic_driver }, { "atrust-acos",(void *(*)(void))sc_get_atrust_acos_driver }, + { "gsm", (void *(*)(void)) sc_get_gsm_driver}, // Above Muscle because some cards don't like the extra probe { "muscle", (void *(*)(void)) sc_get_muscle_driver }, // Above EMV because the detection gets caught there first { "emv", (void *(*)(void)) sc_get_emv_driver }, { "incrypto34", (void *(*)(void)) sc_get_incrypto34_driver }, @@ -480,6 +481,7 @@ const scconf_list *list; struct sc_atr_table t; const char *dname; + //int r; driver = NULL; @@ -534,7 +536,11 @@ list = list->next; } t.card_atr = b; - _sc_add_atr(ctx, driver, &t); + r = _sc_add_atr(ctx, driver, &t); + /* + if(r) + fprintf(stderr,"Failed to add card %d\n",r); + */ } free(blocks); } Index: src/libopensc/apdu.c =================================================================== --- src/libopensc/apdu.c (revision 2981) +++ src/libopensc/apdu.c (working copy) @@ -389,6 +389,7 @@ /* send APDU to the reader driver */ if (card->reader->ops->transmit == NULL) return SC_ERROR_NOT_SUPPORTED; + r = card->reader->ops->transmit(card->reader, card->slot, apdu); if (r != 0) { sc_error(ctx, "unable to transmit APDU"); Index: src/libopensc/Makefile.am =================================================================== --- src/libopensc/Makefile.am (revision 2981) +++ src/libopensc/Makefile.am (working copy) @@ -28,7 +28,7 @@ card-cardos.c card-tcos.c card-emv.c card-default.c \ card-mcrd.c card-starcos.c card-openpgp.c card-jcop.c \ card-oberthur.c card-belpic.c card-atrust-acos.c \ - card-incrypto34.c card-piv.c card-muscle.c \ + card-incrypto34.c card-piv.c card-muscle.c card-gsm.c \ \ pkcs15-openpgp.c pkcs15-infocamere.c pkcs15-starcert.c \ pkcs15-tcos.c pkcs15-esteid.c pkcs15-postecert.c pkcs15-gemsafe.c \ Index: src/libopensc/reader-openct.c =================================================================== --- src/libopensc/reader-openct.c (revision 2981) +++ src/libopensc/reader-openct.c (working copy) @@ -326,14 +326,16 @@ r = SC_ERROR_MEMORY_FAILURE; goto out; } + unsigned int protocol = SC_PROTO_RAW; + _sc_check_forced_protocol(reader->ctx, slot->atr, slot->atr_len, &protocol); /* encode and log the APDU */ - r = sc_apdu_get_octets(reader->ctx, apdu, &sbuf, &ssize, SC_PROTO_RAW); + r = sc_apdu_get_octets(reader->ctx, apdu, &sbuf, &ssize, protocol); if (r != SC_SUCCESS) goto out; /* log data if DEBUG is defined */ #ifdef DEBUG - sc_apdu_log(reader->ctx, sbuf, ssize, 1); -#endif + sc_apdu_log(reader->ctx, sbuf, ssize, 1); +#endif /*DEBUG*/ r = openct_reader_internal_transmit(reader, slot, sbuf, ssize, rbuf, &rsize, apdu->control); if (r < 0) { @@ -343,8 +345,8 @@ } /* log data if DEBUG is defined */ #ifdef DEBUG - sc_apdu_log(reader->ctx, rbuf, rsize, 0); -#endif + sc_apdu_log(reader->ctx, rbuf, rsize, 0); +#endif /*DEBUG*/ /* set response */ r = sc_apdu_set_resp(reader->ctx, apdu, rbuf, rsize); out: Index: src/libopensc/opensc.h =================================================================== --- src/libopensc/opensc.h (revision 2981) +++ src/libopensc/opensc.h (working copy) @@ -1159,6 +1159,7 @@ extern sc_card_driver_t *sc_get_incrypto34_driver(void); extern sc_card_driver_t *sc_get_piv_driver(void); extern sc_card_driver_t *sc_get_muscle_driver(void); +extern sc_card_driver_t *sc_get_gsm_driver(void); #ifdef __cplusplus } Index: src/libopensc/card-gsm.c =================================================================== --- src/libopensc/card-gsm.c (revision 0) +++ src/libopensc/card-gsm.c (revision 0) @@ -0,0 +1,150 @@ +/* + * card-gsm.c: Support for GSM SIM Cards + * + * Copyright (C) 2006 Robin H. Johnson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "internal.h" +#include + + +static struct sc_atr_table gsm_atrs[] = { + { "3b:16:94:00:00:00:00:00:00", "ff:ff:ff:00:00:00:00:00:00", "GSM SIM Card", SC_CARD_TYPE_GSM_GENERIC, NULL }, + { NULL, NULL, NULL, 0, 0, NULL } +}; + +static const struct sc_card_operations *iso_ops = NULL; +static struct sc_card_operations gsm_ops; + +static struct sc_card_driver gsm_drv = { + .name = "GSM SIM cards", + .short_name = "gsm", + .ops = &gsm_ops, + .atr_map = NULL, + .natrs = 0, + .dll = NULL +}; + +static const struct sc_card_error gsm_errors[] = +{ + { 0x9240, SC_ERROR_MEMORY_FAILURE, "Memory Problem" }, + { 0x9400, SC_ERROR_FILE_NOT_FOUND, "No EF selected" }, + { 0x9402, SC_ERROR_FILE_NOT_FOUND, "Out of range (invalid address)" }, + { 0x9404, SC_ERROR_FILE_NOT_FOUND, "File ID or pattern not found" }, + { 0x9408, SC_ERROR_FILE_NOT_FOUND, "File is inconsistent with the command" }, + { 0x9802, SC_ERROR_GSM_CHVINIT, "no CHV initialised" }, + { 0x9804, SC_ERROR_GSM_ACCESSDENY_RETRY, "Access condition not fulfilled, retry" }, + { 0x9808, SC_ERROR_GSM_CONTRA_CHV_STATUS, "In contradiction with CHV status" }, + { 0x9810, SC_ERROR_GSM_CONTRA_INV_STATUS, "In contradiction with invalidation status" }, + { 0x9840, SC_ERROR_GSM_ACCESSDENY_BLOCK, "PIN/PUK blocked" }, + { 0x9850, SC_ERROR_GSM_INCREASE_MAX_REACHED, "Increase cannot be performed, Max value reached" } +}; +static int gsm_check_sw(sc_card_t *card, unsigned int sw1, unsigned int sw2) +{ + const int err_count = sizeof(gsm_errors)/sizeof(gsm_errors[0]); + int i; + + if (card->ctx->debug >= 3) + sc_debug(card->ctx, "sw1 = 0x%02x, sw2 = 0x%02x\n", sw1, sw2); + + // 90 == no response data, 9F == response data + if (sw1 == 0x90 || sw1 == 0x9F) + return SC_NO_ERROR; + + // Incorrect length of command: Notice that the returned values are the + // response length, not the command length! + if(sw1 == 0x67) { + if(sw2 == 0x00) + sc_error(card->ctx, "Wrong length; response length unknown\n"); + else + sc_error(card->ctx, "Wrong length; correct response length is %d\n", sw2); + return SC_ERROR_WRONG_LENGTH; + } + + /* This one is only a warning in the documentation, so things are a bit + * confusing here */ + if(sw1 == 0x92 && (sw2 && 0xF0) == 0x00) { + sc_error(card->ctx,"Update successful but after using an internal retry routine %d times\n",sw2 && 0x0F); + // TODO: is this correct? + return SC_NO_ERROR; + } + + + /* check gsm error messages */ + for (i = 0; i < err_count; i++) + if (gsm_errors[i].SWs == ((sw1 << 8) | sw2)) + { + sc_error(card->ctx, "%s\n", gsm_errors[i].errorstr); + return gsm_errors[i].errorno; + } + + /* iso error */ + return iso_ops->check_sw(card, sw1, sw2); +} + +static int gsm_finish(sc_card_t *card) +{ + return 0; +} + +static int gsm_match_card(sc_card_t *card) +{ + int i; + + i = _sc_match_atr(card, gsm_atrs, &card->type); + if (i < 0) + return 0; + + return 1; +} + +static int gsm_init(sc_card_t *card) +{ + card->name = "GSM SIM Card"; + card->drv_data = NULL; + card->cla = 0xA0; + + return 0; +} + +static int gsm_select_file(sc_card_t *card, + const sc_path_t *in_path, + sc_file_t **file_out) { +} + +static struct sc_card_driver * sc_get_driver(void) +{ + struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); + if (iso_ops == NULL) + iso_ops = iso_drv->ops; + + gsm_ops = *iso_ops; + gsm_ops.match_card = gsm_match_card; + gsm_ops.init = gsm_init; + gsm_ops.finish = gsm_finish; + gsm_ops.check_sw = gsm_check_sw; + //gsm_ops.select_file = gsm_select_file; + + return &gsm_drv; +} + +#if 1 +struct sc_card_driver * sc_get_gsm_driver(void) +{ + return sc_get_driver(); +} +#endif Index: etc/opensc.conf.in =================================================================== --- etc/opensc.conf.in (revision 2981) +++ etc/opensc.conf.in (working copy) @@ -221,6 +221,7 @@ # driver = "piv"; # pkcs15emu = "PIV-II"; # } + # # Estonian ID card and Micardo driver currently play together with T=0 only. # In theory only the 'cold' ATR should be specified, as T=0 will be the preferred