fixed include in pace.c. switched to autotools

git-svn-id: https://vsmartcard.svn.sourceforge.net/svnroot/vsmartcard@93 96b47cad-a561-4643-ad3b-153ac7d7599c
This commit is contained in:
frankmorgner
2010-05-07 11:50:54 +00:00
parent 7649e582a1
commit e0cf5e270f
24 changed files with 635 additions and 1311 deletions

17
ccid/src/Makefile.am Normal file
View File

@@ -0,0 +1,17 @@
IFDNFC_LIB = libifdnfc.$(DYN_LIB_EXT)
bin_PROGRAMS =
if WITH_CCID
bin_PROGRAMS += ccid
endif
if WITH_PACE
bin_PROGRAMS += pace-tool
endif
ccid_SOURCES = ccid.c usbstring.c usb.c binutil.c scutil.c
ccid_LDADD = $(OPENSC_LIBS) $(PTHREAD_LIBS)
ccid_CFLAGS = $(OPENSC_CFLAGS) $(PTHREAD_CFLAGS)
pace_tool_SOURCES = sm.c pace-tool.c binutil.c scutil.c pace.c pace_lib.c
pace_tool_LDADD = $(OPENSC_LIBS) $(OPENSSL_LIBS)
pace_tool_CFLAGS = $(OPENSC_CFLAGS) $(OPENSSL_CFLAGS)

126
ccid/src/binutil.c Normal file
View File

@@ -0,0 +1,126 @@
/*
* Copyright (C) 2010 Frank Morgner
*
* This file is part of ccid.
*
* ccid is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* ccid 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* ccid. If not, see <http://www.gnu.org/licenses/>.
*/
#include "binutil.h"
#include <stdio.h>
#include <string.h>
#include <opensc/opensc.h>
void print_usage(const char *app_name, const struct option options[],
const char *option_help[])
{
int i = 0;
printf("Usage: %s [OPTIONS]\nOptions:\n", app_name);
while (options[i].name) {
char buf[40], tmp[5];
const char *arg_str;
/* Skip "hidden" options */
if (option_help[i] == NULL) {
i++;
continue;
}
if (options[i].val > 0 && options[i].val < 128)
sprintf(tmp, "-%c", options[i].val);
else
tmp[0] = 0;
switch (options[i].has_arg) {
case 1:
arg_str = " <arg>";
break;
case 2:
arg_str = " [arg]";
break;
default:
arg_str = "";
break;
}
sprintf(buf, "--%-13s%s%s", options[i].name, tmp, arg_str);
if (strlen(buf) > 24) {
printf(" %s\n", buf);
buf[0] = '\0';
}
printf(" %-24s %s\n", buf, option_help[i]);
i++;
}
}
void parse_error(const char *app_name, const struct option options[],
const char *option_help[], const char *optarg, int opt_ind)
{
printf("Could not parse %s ('%s').\n", options[opt_ind].name, optarg);
print_usage(app_name , options, option_help);
}
static int list_readers(sc_context_t *ctx)
{
unsigned int i, rcount = sc_ctx_get_reader_count(ctx);
if (rcount == 0) {
printf("No smart card readers found.\n");
return 0;
}
printf("Readers known about:\n");
printf("Nr. Driver Name\n");
for (i = 0; i < rcount; i++) {
sc_reader_t *screader = sc_ctx_get_reader(ctx, i);
printf("%-7d%-11s%s\n", i, screader->driver->short_name,
screader->name);
}
return 0;
}
static int list_drivers(sc_context_t *ctx)
{
int i;
if (ctx->card_drivers[0] == NULL) {
printf("No card drivers installed!\n");
return 0;
}
printf("Configured card drivers:\n");
for (i = 0; ctx->card_drivers[i] != NULL; i++) {
printf(" %-16s %s\n", ctx->card_drivers[i]->short_name,
ctx->card_drivers[i]->name);
}
return 0;
}
int print_avail(int verbose)
{
sc_context_t *ctx = NULL;
int r;
r = sc_context_create(&ctx, NULL);
if (r) {
fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r));
return 1;
}
ctx->debug = verbose;
r = list_readers(ctx)|list_drivers(ctx);
if (ctx)
sc_release_context(ctx);
return r;
}

31
ccid/src/binutil.h Normal file
View File

@@ -0,0 +1,31 @@
/*
* Copyright (C) 2010 Frank Morgner
*
* This file is part of ccid.
*
* ccid is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* ccid 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* ccid. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _CCID_BINUTIL_H
#define _CCID_BINUTIL_H
#include <getopt.h>
void print_usage(const char *app_name, const struct option options[],
const char *option_help[]);
void parse_error(const char *app_name, const struct option options[],
const char *option_help[], const char *optarg, int opt_ind);
int print_avail(int verbose);
#endif

1204
ccid/src/ccid.c Normal file

File diff suppressed because it is too large Load Diff

278
ccid/src/ccid.h Normal file
View File

@@ -0,0 +1,278 @@
/*
* Copyright (C) 2009 Frank Morgner
*
* This file is part of ccid.
*
* ccid is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* ccid 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* ccid. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _CCID_H
#define _CCID_H
#include <linux/usb/ch9.h>
#include <opensc/opensc.h>
#ifdef __cplusplus
extern "C" {
#endif
#define USB_REQ_CCID 0xA1
#define CCID_CONTROL_ABORT 0x01
#define CCID_CONTROL_GET_CLOCK_FREQUENCIES 0x02
#define CCID_CONTROL_GET_DATA_RATES 0x03
#define CCID_BERROR_CMD_ABORTED 0xff /* Host aborted the current activity */
#define CCID_BERROR_ICC_MUTE 0xfe /* CCID timed out while talking to the ICC */
#define CCID_BERROR_XFR_PARITY_ERROR 0xfd /* Parity error while talking to the ICC */
#define CCID_BERROR_XFR_OVERRUN 0xfc /* Overrun error while talking to the ICC */
#define CCID_BERROR_HW_ERROR 0xfb /* An all inclusive hardware error occurred */
#define CCID_BERROR_BAD_ATR_TS 0xf
#define CCID_BERROR_BAD_ATR_TCK 0xf
#define CCID_BERROR_ICC_PROTOCOL_NOT_SUPPORTED 0xf6
#define CCID_BERROR_ICC_CLASS_NOT_SUPPORTED 0xf5
#define CCID_BERROR_PROCEDURE_BYTE_CONFLICT 0xf4
#define CCID_BERROR_DEACTIVATED_PROTOCOL 0xf3
#define CCID_BERROR_BUSY_WITH_AUTO_SEQUENCE 0xf2 /* Automatic Sequence Ongoing */
#define CCID_BERROR_PIN_TIMEOUT 0xf0
#define CCID_BERROR_PIN_CANCELLED 0xef
#define CCID_BERROR_CMD_SLOT_BUSY 0xe0 /* A second command was sent to a slot which was already processing a command. */
#define CCID_BERROR_CMD_NOT_SUPPORTED 0x00
#define CCID_BERROR_OK 0x00
#define CCID_BSTATUS_OK_ACTIVE 0x00 /* No error. An ICC is present and active */
#define CCID_BSTATUS_OK_INACTIVE 0x01 /* No error. ICC is present and inactive */
#define CCID_BSTATUS_OK_NOICC 0x02 /* No error. No ICC is present */
#define CCID_BSTATUS_ERROR_ACTIVE 0x40 /* Failed. An ICC is present and active */
#define CCID_BSTATUS_ERROR_INACTIVE 0x41 /* Failed. ICC is present and inactive */
#define CCID_BSTATUS_ERROR_NOICC 0x42 /* Failed. No ICC is present */
#define CCID_WLEVEL_DIRECT __constant_cpu_to_le16(0) /* APDU begins and ends with this command */
#define CCID_WLEVEL_CHAIN_NEXT_XFRBLOCK __constant_cpu_to_le16(1) /* APDU begins with this command, and continue in the next PC_to_RDR_XfrBlock */
#define CCID_WLEVEL_CHAIN_END __constant_cpu_to_le16(2) /* abData field continues a command APDU and ends the APDU command */
#define CCID_WLEVEL_CHAIN_CONTINUE __constant_cpu_to_le16(3) /* abData field continues a command APDU and another block is to follow */
#define CCID_WLEVEL_RESPONSE_IN_DATABLOCK __constant_cpu_to_le16(0x10) /* empty abData field, continuation of response APDU is expected in the next RDR_to_PC_DataBlock */
#define CCID_PIN_ENCODING_BIN 0x00
#define CCID_PIN_ENCODING_BCD 0x01
#define CCID_PIN_ENCODING_ASCII 0x02
#define CCID_PIN_UNITS_BYTES 0x80
#define CCID_PIN_JUSTIFY_RIGHT 0x04
#define CCID_PIN_CONFIRM_NEW 0x01
#define CCID_PIN_INSERT_OLD 0x02
#define CCID_PIN_NO_MSG 0x00
#define CCID_PIN_MSG1 0x01
#define CCID_PIN_MSG2 0x02
#define CCID_PIN_MSG_REF 0x03
#define CCID_PIN_MSG_DEFAULT 0xff
#define CCID_SLOTS_UNCHANGED 0x00
#define CCID_SLOT1_CARD_PRESENT 0x01
#define CCID_SLOT1_CHANGED 0x02
#define CCID_SLOT2_CARD_PRESENT 0x04
#define CCID_SLOT2_CHANGED 0x08
#define CCID_SLOT3_CARD_PRESENT 0x10
#define CCID_SLOT3_CHANGED 0x20
#define CCID_SLOT4_CARD_PRESENT 0x40
#define CCID_SLOT4_CHANGED 0x80
struct ccid_class_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__le16 bcdCCID;
__u8 bMaxSlotIndex;
__u8 bVoltageSupport;
__le32 dwProtocols;
__le32 dwDefaultClock;
__le32 dwMaximumClock;
__u8 bNumClockSupport;
__le32 dwDataRate;
__le32 dwMaxDataRate;
__u8 bNumDataRatesSupported;
__le32 dwMaxIFSD;
__le32 dwSynchProtocols;
__le32 dwMechanical;
__le32 dwFeatures;
__le32 dwMaxCCIDMessageLength;
__u8 bClassGetResponse;
__u8 bclassEnvelope;
__le16 wLcdLayout;
__u8 bPINSupport;
__u8 bMaxCCIDBusySlots;
} __attribute__ ((packed));
typedef struct {
__u8 bmFindexDindex;
__u8 bmTCCKST0;
__u8 bGuardTimeT0;
__u8 bWaitingIntegerT0;
__u8 bClockStop;
} __attribute__ ((packed)) abProtocolDataStructure_T0_t;
typedef struct {
__u8 bmFindexDindex;
__u8 bmTCCKST1;
__u8 bGuardTimeT1;
__u8 bWaitingIntegersT1;
__u8 bClockStop;
__u8 bIFSC;
__u8 bNadValue;
} __attribute__ ((packed)) abProtocolDataStructure_T1_t;
typedef struct {
__u8 bTimeOut;
__u8 bmFormatString;
__u8 bmPINBlockString;
__u8 bmPINLengthFormat;
__le16 wPINMaxExtraDigit;
__u8 bEntryValidationCondition;
__u8 bNumberMessage;
__le16 wLangId;
__u8 bMsgIndex;
__u8 bTeoPrologue1;
__le16 bTeoPrologue2;
} __attribute__ ((packed)) abPINDataStucture_Verification_t;
typedef struct {
__u8 bTimeOut;
__u8 bmFormatString;
__u8 bmPINBlockString;
__u8 bmPINLengthFormat;
__u8 bInsertionOffsetOld;
__u8 bInsertionOffsetNew;
__le16 wPINMaxExtraDigit;
__u8 bConfirmPIN;
__u8 bEntryValidationCondition;
__u8 bNumberMessage;
__le16 wLangId;
__u8 bMsgIndex1;
__u8 bMsgIndex2;
__u8 bMsgIndex3;
__u8 bTeoPrologue1;
__le16 bTeoPrologue2;
} __attribute__ ((packed)) abPINDataStucture_Modification_t;
typedef struct {
__u8 bMessageType;
__le32 dwLength;
__u8 bSlot;
__u8 bSeq;
__u8 bBWI;
__le16 wLevelParameter;
} __attribute__ ((packed)) PC_to_RDR_XfrBlock_t;
typedef struct {
__u8 bMessageType;
__le32 dwLength;
__u8 bSlot;
__u8 bSeq;
__u8 abRFU1;
__le16 abRFU2;
} __attribute__ ((packed)) PC_to_RDR_IccPowerOff_t;
typedef struct {
__u8 bMessageType;
__le32 dwLength;
__u8 bSlot;
__u8 bSeq;
__u8 abRFU1;
__le16 abRFU2;
} __attribute__ ((packed)) PC_to_RDR_GetSlotStatus_t;
typedef struct {
__u8 bMessageType;
__le32 dwLength;
__u8 bSlot;
__u8 bSeq;
__u8 abRFU1;
__le16 abRFU2;
} __attribute__ ((packed)) PC_to_RDR_GetParameters_t;
typedef struct {
__u8 bMessageType;
__le32 dwLength;
__u8 bSlot;
__u8 bSeq;
__u8 abRFU1;
__le16 abRFU2;
} __attribute__ ((packed)) PC_to_RDR_ResetParameters_t;
typedef struct {
__u8 bMessageType;
__le32 dwLength;
__u8 bSlot;
__u8 bSeq;
__u8 bProtocolNum;
__le16 abRFU;
} __attribute__ ((packed)) PC_to_RDR_SetParameters_t;
typedef struct {
__u8 bMessageType;
__le32 dwLength;
__u8 bSlot;
__u8 bSeq;
__u8 bBWI;
__le16 wLevelParameter;
} __attribute__ ((packed)) PC_to_RDR_Secure_t;
typedef struct {
__u8 bMessageType;
__le32 dwLength;
__u8 bSlot;
__u8 bSeq;
__u8 bPowerSelect;
__le16 abRFU;
} __attribute__ ((packed)) PC_to_RDR_IccPowerOn_t;
typedef struct {
__u8 bMessageType;
__le32 dwLength;
__u8 bSlot;
__u8 bSeq;
__u8 bStatus;
__u8 bError;
__u8 bClockStatus;
} __attribute__ ((packed)) RDR_to_PC_SlotStatus_t;
typedef struct {
__u8 bMessageType;
__le32 dwLength;
__u8 bSlot;
__u8 bSeq;
__u8 bStatus;
__u8 bError;
__u8 bChainParameter;
} __attribute__ ((packed)) RDR_to_PC_DataBlock_t;
typedef struct {
__u8 bMessageType;
__le32 dwLength;
__u8 bSlot;
__u8 bSeq;
__u8 bStatus;
__u8 bError;
__u8 bProtocolNum;
} __attribute__ ((packed)) RDR_to_PC_Parameters_t;
typedef struct {
__u8 bMessageType;
__u8 bmSlotICCState; /* we support 4 slots, so we need 2*4 bits = 1 byte */
} __attribute__ ((packed)) RDR_to_PC_NotifySlotChange_t;
struct hid_class_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__le16 bcdHID;
__le32 bCountryCode;
__u8 bNumDescriptors;
} __attribute__ ((packed));
int ccid_initialize(int reader_id, const char *cdriver, int verbose);
void ccid_shutdown();
int ccid_parse_bulkin(const __u8* inbuf, __u8** outbuf);
int ccid_parse_control(struct usb_ctrlrequest *setup, __u8 **outbuf);
int ccid_state_changed(RDR_to_PC_NotifySlotChange_t **slotchange, int timeout);
#ifdef __cplusplus
}
#endif
#endif

309
ccid/src/pace-tool.c Normal file
View File

@@ -0,0 +1,309 @@
/*
* Copyright (C) 2010 Frank Morgner
*
* This file is part of ccid.
*
* ccid is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* ccid 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* ccid. If not, see <http://www.gnu.org/licenses/>.
*/
#include "binutil.h"
#include "pace.h"
#include "scutil.h"
#include <opensc/log.h>
#include <opensc/ui.h>
#include <openssl/pace.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int verbose = 0;
static int doinfo = 0;
static u8 pin_id = 0;
static u8 dochangepin = 0;
static const char *newpin = NULL;
static int usb_reader_num = -1;
static const char *pin = NULL;
static const char *cdriver = NULL;
static sc_context_t *ctx = NULL;
static sc_card_t *card = NULL;
static sc_reader_t *reader;
#define OPT_HELP 'h'
#define OPT_READER 'r'
#define OPT_PIN 'i'
#define OPT_PUK 'u'
#define OPT_CAN 'a'
#define OPT_MRZ 'z'
#define OPT_CHANGE_PIN 'I'
#define OPT_VERBOSE 'v'
#define OPT_INFO 'o'
#define OPT_CARD 'c'
static const struct option options[] = {
{ "help", no_argument, NULL, OPT_HELP },
{ "reader", required_argument, NULL, OPT_READER },
{ "card-driver", required_argument, NULL, OPT_CARD },
{ "pin", optional_argument, NULL, OPT_PIN },
{ "puk", optional_argument, NULL, OPT_PUK },
{ "can", optional_argument, NULL, OPT_CAN },
{ "mrz", optional_argument, NULL, OPT_MRZ },
{ "new-pin", optional_argument, NULL, OPT_CHANGE_PIN },
{ "verbose", no_argument, NULL, OPT_VERBOSE },
{ "info", no_argument, NULL, OPT_INFO },
{ NULL, 0, NULL, 0 }
};
static const char *option_help[] = {
"Print help and exit",
"Number of reader to use (default: auto-detect)",
"Which card driver to use (default: auto-detect)",
"Run PACE with PIN",
"Run PACE with PUK",
"Run PACE with CAN",
"Run PACE with MRZ",
"Install a new PIN",
"Use (several times) to be more verbose",
"Print version, available readers and drivers.",
};
int pace_change_p(struct sm_ctx *ctx, sc_card_t *card, enum s_type pin_id,
const char *newp, size_t newplen)
{
sc_ui_hints_t hints;
char *p = NULL;
int r;
if (!newplen || !newp) {
memset(&hints, 0, sizeof(hints));
hints.dialog_name = "ccid.PACE";
hints.card = card;
hints.prompt = NULL;
hints.obj_label = pace_secret_name(pin_id);
hints.usage = SC_UI_USAGE_NEW_PIN;
r = sc_ui_get_pin(&hints, &p);
if (r < 0) {
sc_error(card->ctx, "Could not read new %s (%s).\n",
hints.obj_label, sc_strerror(r));
SC_FUNC_RETURN(card->ctx, SC_LOG_TYPE_ERROR, r);
}
newplen = strlen(p);
newp = p;
}
r = pace_reset_retry_counter(ctx, card, pin_id, newp, newplen);
if (p) {
OPENSSL_cleanse(p, newplen);
free(p);
}
SC_FUNC_RETURN(card->ctx, SC_LOG_TYPE_DEBUG, r);
}
int pace_test(sc_card_t *card,
enum s_type pin_id, const char *pin, size_t pinlen,
enum s_type new_pin_id, const char *new_pin, size_t new_pinlen)
{
u8 buf[0xff + 5];
char *read = NULL;
__u8 *out = NULL;
size_t outlen, readlen = 0, apdulen;
ssize_t linelen;
struct sm_ctx sctx;
sc_apdu_t apdu;
int r;
memset(&sctx, 0, sizeof(sctx));
memset(&apdu, 0, sizeof(apdu));
switch (pin_id) {
case PACE_MRZ:
case PACE_CAN:
case PACE_PIN:
case PACE_PUK:
break;
default:
sc_error(card->ctx, "Type of secret not supported");
return SC_ERROR_INVALID_ARGUMENTS;
}
if (pinlen > sizeof(buf) - 5) {
sc_error(card->ctx, "%s too long (maximal %u bytes supported)",
pace_secret_name(pin_id),
sizeof(buf) - 5);
}
buf[0] = pin_id;
buf[1] = 0; // length_chat
buf[2] = pinlen; // length_pin
memcpy(&buf[3], pin, pinlen);
buf[3 + pinlen] = 0; // length_cert_desc
buf[4 + pinlen]= 0; // length_cert_desc
SC_TEST_RET(card->ctx,
EstablishPACEChannel(card, buf, &out, &outlen, &sctx),
"Could not establish PACE channel.");
printf("Established PACE channel.\n");
if (new_pin_id) {
SC_TEST_RET(card->ctx,
pace_change_p(&sctx, card, new_pin_id, new_pin, new_pinlen),
"Could not change PACE secret.");
} else {
while (1) {
printf("Enter unencrypted APDU (empty line to exit)\n");
linelen = getline(&read, &readlen, stdin);
if (linelen <= 1) {
if (linelen < 0) {
r = SC_ERROR_INTERNAL;
sc_error(card->ctx, "Could not read line");
} else {
r = SC_SUCCESS;
printf("Thanks for flying with ccid\n");
}
break;
}
read[linelen - 1] = 0;
if (sc_hex_to_bin(read, buf, &apdulen) < 0) {
sc_error(card->ctx, "Could not format binary string");
}
r = build_apdu(card->ctx, buf, apdulen, &apdu);
if (r < 0) {
sc_error(card->ctx, "Could not format APDU");
continue;
}
apdu.resp = buf;
apdu.resplen = sizeof(buf);
r = pace_transmit_apdu(&sctx, card, &apdu);
if (r < 0) {
sc_error(card->ctx, "Could not send APDU: %s", sc_strerror(r));
continue;
}
printf("Decrypted APDU sw1=%02x sw2=%02x\n", apdu.sw1, apdu.sw2);
bin_print(stdout, "Decrypted APDU response data", apdu.resp, apdu.resplen);
}
if (read)
free(read);
}
SC_FUNC_RETURN(card->ctx, SC_LOG_TYPE_ERROR, r);
}
int
main (int argc, char **argv)
{
int i, oindex = 0;
while (1) {
i = getopt_long(argc, argv, "hr:i::u::a::z::I::voc:", options, &oindex);
if (i == -1)
break;
switch (i) {
case OPT_HELP:
print_usage(argv[0] , options, option_help);
exit(0);
break;
case OPT_READER:
if (sscanf(optarg, "%d", &usb_reader_num) != 1) {
parse_error(argv[0], options, option_help, optarg, oindex);
exit(2);
}
break;
case OPT_CARD:
cdriver = optarg;
break;
case OPT_VERBOSE:
verbose++;
break;
case OPT_INFO:
doinfo++;
break;
case OPT_PUK:
pin_id = PACE_PUK;
pin = optarg;
break;
case OPT_PIN:
pin_id = PACE_PIN;
pin = optarg;
break;
case OPT_CAN:
pin_id = PACE_CAN;
pin = optarg;
break;
case OPT_MRZ:
pin_id = PACE_MRZ;
pin = optarg;
break;
case OPT_CHANGE_PIN:
dochangepin = 3;
newpin = optarg;
break;
case '?':
/* fall through */
default:
exit(1);
break;
}
}
if (optind < argc) {
fprintf (stderr, "Unknown argument%s:", optind+1 == argc ? "" : "s");
while (optind < argc) {
fprintf(stderr, " \"%s\"", argv[optind++]);
fprintf(stderr, "%c", optind == argc ? '\n' : ',');
}
exit(1);
}
if (doinfo) {
fprintf(stderr, "%s 0.9 written by Frank Morgner.\n\n" ,
argv[0]);
return print_avail(verbose);
}
i = initialize(usb_reader_num, cdriver, verbose, &ctx, &reader);
if (i < 0) {
perror("Can't initialize reader");
return 1;
}
for (i = 0; i < SC_MAX_SLOTS; i++) {
if (sc_detect_card_presence(reader, 0) & SC_SLOT_CARD_PRESENT) {
sc_connect_card(reader, i, &card);
break;
}
}
if (i == SC_MAX_SLOTS) {
perror("No card found");
sc_disconnect_card(card, 0);
sc_release_context(ctx);
return 1;
}
i = pace_test(card, pin_id, pin, !pin ? 0 : strlen(pin),
dochangepin, newpin, !newpin ? 0 : strlen(newpin));
sc_disconnect_card(card, 0);
sc_release_context(ctx);
return i;
}

75
ccid/src/pace.h Normal file
View File

@@ -0,0 +1,75 @@
/*
* Copyright (C) 2010 Frank Morgner
*
* This file is part of ccid.
*
* ccid is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* ccid 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* ccid. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _CCID_PACE_H
#define _CCID_PACE_H
#include "pace_lib.h"
#include "sm.h"
#include <linux/usb/ch9.h>
#include <opensc/opensc.h>
#include <openssl/bn.h>
#include <openssl/pace.h>
#ifdef __cplusplus
extern "C" {
#endif
#define PACE_BITMAP_PACE 0x40
#define PACE_BITMAP_EID 0x20
#define PACE_BITMAP_ESIGN 0x10
//#define PACE_MRZ 0x01
//#define PACE_CAN 0x02
//#define PACE_PIN 0x03
//#define PACE_PUK 0x04
#define FID_EF_CARDACCESS 0x011C
#define MAX_EF_CARDACCESS 2048
int increment_ssc(struct pace_sm_ctx *psmctx);
int decrement_ssc(struct pace_sm_ctx *psmctx);
int reset_ssc(struct pace_sm_ctx *psmctx);
const char *pace_secret_name(enum s_type pin_id);
int pace_sm_encrypt(sc_card_t *card, const struct sm_ctx *ctx,
const u8 *data, size_t datalen, u8 **enc);
int pace_sm_decrypt(sc_card_t *card, const struct sm_ctx *ctx,
const u8 *enc, size_t enclen, u8 **data);
int pace_sm_authenticate(sc_card_t *card, const struct sm_ctx *ctx,
const u8 *data, size_t datalen, u8 **outdata);
int pace_sm_verify_authentication(sc_card_t *card, const struct sm_ctx *ctx,
const u8 *mac, size_t maclen,
const u8 *macdata, size_t macdatalen);
int GetReadersPACECapabilities(sc_card_t *card, const __u8
*in, __u8 **out, size_t *outlen);
int EstablishPACEChannel(sc_card_t *card, const __u8 *in,
__u8 **out, size_t *outlen, struct sm_ctx *ctx);
int pace_transmit_apdu(struct sm_ctx *sctx, sc_card_t *card,
sc_apdu_t *apdu);
int pace_reset_retry_counter(struct sm_ctx *ctx, sc_card_t *card,
enum s_type pin_id, const char *new, size_t new_len);
#ifdef __cplusplus
}
#endif
#endif

69
ccid/src/pace_lib.c Normal file
View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2010 Frank Morgner
*
* This file is part of ccid.
*
* ccid is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* ccid 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* ccid. If not, see <http://www.gnu.org/licenses/>.
*/
#include "pace_lib.h"
#include <openssl/buffer.h>
#include <openssl/pace.h>
struct pace_sm_ctx *
pace_sm_ctx_create(const BUF_MEM *key_mac,
const BUF_MEM *key_enc, PACE_CTX *ctx)
{
struct pace_sm_ctx *out = malloc(sizeof *out);
if (!out)
return NULL;
out->ssc = BN_new();
if (!out->ssc) {
free(out);
return NULL;
}
out->key_enc = key_enc;
out->key_mac = key_mac;
out->ctx = ctx;
return out;
}
void
pace_sm_ctx_free(struct pace_sm_ctx *ctx)
{
if (ctx) {
if (ctx->ssc)
BN_clear_free(ctx->ssc);
free(ctx);
}
}
void
pace_sm_ctx_clear_free(struct pace_sm_ctx *ctx)
{
if (ctx) {
if (ctx->key_mac) {
OPENSSL_cleanse(ctx->key_mac->data, ctx->key_mac->max);
BUF_MEM_free((BUF_MEM *) ctx->key_mac);
}
if (ctx->key_enc) {
OPENSSL_cleanse(ctx->key_enc->data, ctx->key_enc->max);
BUF_MEM_free((BUF_MEM *) ctx->key_enc);
}
PACE_CTX_clear_free(ctx->ctx);
pace_sm_ctx_free(ctx);
}
}

44
ccid/src/pace_lib.h Normal file
View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2010 Frank Morgner
*
* This file is part of ccid.
*
* ccid is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* ccid 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* ccid. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _CCID_PACE_LIB_H
#define _CCID_PACE_LIB_H
#include <openssl/pace.h>
#ifdef __cplusplus
extern "C" {
#endif
struct pace_sm_ctx {
BIGNUM *ssc;
const BUF_MEM *key_mac;
const BUF_MEM *key_enc;
PACE_CTX *ctx;
};
struct pace_sm_ctx * pace_sm_ctx_create(const BUF_MEM *key_mac,
const BUF_MEM *key_enc, PACE_CTX *ctx);
void pace_sm_ctx_free(struct pace_sm_ctx *ctx);
void pace_sm_ctx_clear_free(struct pace_sm_ctx *ctx);
#ifdef __cplusplus
}
#endif
#endif

142
ccid/src/scutil.c Normal file
View File

@@ -0,0 +1,142 @@
/*
* Copyright (C) 2010 Frank Morgner
*
* This file is part of ccid.
*
* ccid is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* ccid 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* ccid. If not, see <http://www.gnu.org/licenses/>.
*/
#include "scutil.h"
#include <stdio.h>
#include <string.h>
#include <opensc/log.h>
int initialize(int reader_id, const char *cdriver, int verbose,
sc_context_t **ctx, sc_reader_t **reader)
{
unsigned int i, reader_count;
if (!ctx || !reader)
return SC_ERROR_INVALID_ARGUMENTS;
int r = sc_context_create(ctx, NULL);
if (r < 0) {
printf("Failed to create initial context: %s", sc_strerror(r));
return r;
}
if (cdriver != NULL) {
r = sc_set_card_driver(*ctx, cdriver);
if (r < 0) {
sc_error(*ctx, "Card driver '%s' not found!\n", cdriver);
return r;
}
}
(*ctx)->debug = verbose;
reader_count = sc_ctx_get_reader_count(*ctx);
if (reader_count == 0)
SC_FUNC_RETURN((*ctx), SC_LOG_TYPE_ERROR, SC_ERROR_NO_READERS_FOUND);
if (reader_id < 0) {
/* Automatically try to skip to a reader with a card if reader not specified */
for (i = 0; i < reader_count; i++) {
*reader = sc_ctx_get_reader(*ctx, i);
if (sc_detect_card_presence(*reader, 0) & SC_SLOT_CARD_PRESENT) {
reader_id = i;
sc_debug(*ctx, "Using reader with a card: %s", (*reader)->name);
break;
}
}
if (reader_id >= reader_count) {
/* no reader found, use the first */
reader_id = 0;
}
}
if (reader_id >= reader_count)
SC_FUNC_RETURN((*ctx), SC_LOG_TYPE_ERROR, SC_ERROR_NO_READERS_FOUND);
*reader = sc_ctx_get_reader(*ctx, reader_id);
SC_FUNC_RETURN((*ctx), SC_LOG_TYPE_ERROR, SC_SUCCESS);
}
int build_apdu(sc_context_t *ctx, const u8 *buf, size_t len, sc_apdu_t *apdu)
{
const u8 *p;
size_t len0;
if (!buf || !apdu)
SC_FUNC_RETURN(ctx, SC_LOG_TYPE_VERBOSE, SC_ERROR_INVALID_ARGUMENTS);
len0 = len;
if (len < 4) {
sc_error(ctx, "APDU too short (must be at least 4 bytes)");
SC_FUNC_RETURN(ctx, SC_LOG_TYPE_VERBOSE, SC_ERROR_INVALID_DATA);
}
memset(apdu, 0, sizeof(*apdu));
p = buf;
apdu->cla = *p++;
apdu->ins = *p++;
apdu->p1 = *p++;
apdu->p2 = *p++;
len -= 4;
if (len > 1) {
apdu->lc = *p++;
len--;
apdu->data = p;
apdu->datalen = apdu->lc;
if (len < apdu->lc) {
sc_error(ctx, "APDU too short (need %lu bytes)\n",
(unsigned long) apdu->lc - len);
SC_FUNC_RETURN(ctx, SC_LOG_TYPE_VERBOSE, SC_ERROR_INVALID_DATA);
}
len -= apdu->lc;
p += apdu->lc;
if (len) {
apdu->le = *p++;
if (apdu->le == 0)
apdu->le = 256;
len--;
apdu->cse = SC_APDU_CASE_4_SHORT;
} else {
apdu->cse = SC_APDU_CASE_3_SHORT;
}
if (len) {
sc_error(ctx, "APDU too long (%lu bytes extra)\n",
(unsigned long) len);
SC_FUNC_RETURN(ctx, SC_LOG_TYPE_VERBOSE, SC_ERROR_INVALID_DATA);
}
} else if (len == 1) {
apdu->le = *p++;
if (apdu->le == 0)
apdu->le = 256;
len--;
apdu->cse = SC_APDU_CASE_2_SHORT;
} else {
apdu->cse = SC_APDU_CASE_1;
}
apdu->flags = SC_APDU_FLAGS_NO_GET_RESP|SC_APDU_FLAGS_NO_RETRY_WL;
sc_debug(ctx, "APDU, %d bytes:\tins=%02x p1=%02x p2=%02x",
(unsigned int) len0, apdu->ins, apdu->p1, apdu->p2);
SC_FUNC_RETURN(ctx, SC_LOG_TYPE_VERBOSE, SC_SUCCESS);
}

30
ccid/src/scutil.h Normal file
View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2010 Frank Morgner
*
* This file is part of ccid.
*
* ccid is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* ccid 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* ccid. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _CCID_SCUTIL_H
#define _CCID_SCUTIL_H
#include <opensc/opensc.h>
int initialize(int reader_id, const char *cdriver, int verbose,
sc_context_t **ctx, sc_reader_t **reader);
int build_apdu(sc_context_t *ctx, const u8 *buf, size_t len, sc_apdu_t *apdu);
#endif

594
ccid/src/sm.c Normal file
View File

@@ -0,0 +1,594 @@
/*
* Copyright (C) 2010 Frank Morgner
*
* This file is part of ccid.
*
* ccid is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* ccid 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* ccid. If not, see <http://www.gnu.org/licenses/>.
*/
#include "sm.h"
#include <arpa/inet.h>
#include <opensc/asn1.h>
#include <opensc/log.h>
#include <stdlib.h>
#include <string.h>
static const struct sc_asn1_entry c_sm_capdu[] = {
{ "Padding-content indicator followed by cryptogram",
SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x07, SC_ASN1_OPTIONAL, NULL, NULL },
{ "Protected Le",
SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x17, SC_ASN1_OPTIONAL, NULL, NULL },
{ "Cryptographic Checksum",
SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x0E, SC_ASN1_OPTIONAL, NULL, NULL },
{ NULL , 0 , 0 , 0 , NULL , NULL }
};
static const struct sc_asn1_entry c_sm_rapdu[] = {
{ "Padding-content indicator followed by cryptogram" ,
SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x07, SC_ASN1_OPTIONAL, NULL, NULL },
{ "Processing Status",
SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x19, 0 , NULL, NULL },
{ "Cryptographic Checksum",
SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x0E, SC_ASN1_OPTIONAL, NULL, NULL },
{ NULL, 0, 0, 0, NULL, NULL }
};
void _bin_log(sc_context_t *ctx, int type, const char *file, int line,
const char *func, const char *label, const u8 *data, size_t len,
FILE *f)
{
char buf[1024];
if (data)
sc_hex_dump(ctx, data, len, buf, sizeof buf);
else
buf[0] = 0;
if (!f) {
sc_do_log(ctx, type, file, line, func,
"\n%s (%u byte%s):\n%s"
"======================================================================",
label, len, len==1?"":"s", buf);
} else {
fprintf(f, "%s (%u byte%s):\n%s"
"======================================================================\n",
label, len, len==1?"":"s", buf);
}
}
int
add_iso_pad(const u8 *data, size_t datalen, int block_size, u8 **padded)
{
u8 *p;
size_t p_len;
if (!padded)
return SC_ERROR_INVALID_ARGUMENTS;
/* calculate length of padded message */
p_len = (datalen / block_size) * block_size + block_size;
p = realloc(*padded, p_len);
if (!p)
return SC_ERROR_OUT_OF_MEMORY;
if (*padded != data)
memcpy(p, data, datalen);
*padded = p;
/* now add iso padding */
memset(p + datalen, 0x80, 1);
memset(p + datalen + 1, 0, p_len - datalen - 1);
return p_len;
}
int
add_padding(const struct sm_ctx *ctx, const u8 *data, size_t datalen,
u8 **padded)
{
u8 *p;
switch (ctx->padding_indicator) {
case SM_NO_PADDING:
if (*padded != data) {
p = realloc(*padded, datalen);
if (!p)
return SC_ERROR_OUT_OF_MEMORY;
*padded = p;
memcpy(*padded, data, datalen);
}
return datalen;
case SM_ISO_PADDING:
return add_iso_pad(data, datalen, ctx->block_length, padded);
default:
return SC_ERROR_INVALID_ARGUMENTS;
}
}
int no_padding(u8 padding_indicator, const u8 *data, size_t datalen)
{
if (!datalen || !data)
return SC_ERROR_INVALID_ARGUMENTS;
size_t len;
switch (padding_indicator) {
case SM_NO_PADDING:
len = datalen;
break;
case SM_ISO_PADDING:
for (len = datalen;
len && (data[len-1] == 0);
len--) { };
if (data[len-1] != 0x80)
return SC_ERROR_INVALID_DATA;
break;
default:
return SC_ERROR_NOT_SUPPORTED;
}
return len;
}
static int format_le(size_t le, struct sc_asn1_entry *le_entry,
u8 **lebuf, size_t *le_len)
{
u8 *p;
if (!lebuf || !le_len)
return SC_ERROR_INVALID_ARGUMENTS;
p = realloc(*lebuf, *le_len);
if (!p)
return SC_ERROR_OUT_OF_MEMORY;
switch (*le_len) {
case 1:
p[0] = le;
break;
case 2:
p[0] = htons(le) >> 8;
p[1] = htons(le) & 0xff;
break;
case 3:
p[0] = 0x00;
p[1] = htons(le) >> 8;
p[2] = htons(le) & 0xff;
break;
default:
return SC_ERROR_INVALID_ARGUMENTS;
}
*lebuf = p;
sc_format_asn1_entry(le_entry, *lebuf, le_len, SC_ASN1_PRESENT);
return SC_SUCCESS;
}
static int prefix_buf(u8 prefix, u8 *buf, size_t buflen, u8 **cat)
{
u8 *p;
p = realloc(*cat, buflen + 1);
if (!p)
return SC_ERROR_OUT_OF_MEMORY;
if (*cat == buf) {
memmove(p + 1, p, buflen);
} else {
memcpy(p + 1, buf, buflen);
}
p[0] = prefix;
*cat = p;
return buflen + 1;
}
static int format_data(sc_card_t *card, const struct sm_ctx *ctx,
const u8 *data, size_t datalen,
struct sc_asn1_entry *formatted_encrypted_data_entry,
u8 **formatted_data, size_t *formatted_data_len)
{
int r;
u8 *pad_data = NULL;
size_t pad_data_len;
if (!ctx || !formatted_data || !formatted_data_len) {
r = SC_ERROR_INVALID_ARGUMENTS;
goto err;
}
r = add_padding(ctx, data, datalen, &pad_data);
if (r < 0)
goto err;
pad_data_len = r;
bin_log(card->ctx, "Data to encrypt", pad_data, pad_data_len);
r = ctx->encrypt(card, ctx, pad_data, pad_data_len, formatted_data);
if (r < 0) {
sc_error(card->ctx, "Could not encrypt the data");
goto err;
}
bin_log(card->ctx, "Cryptogram", *formatted_data, r);
r = prefix_buf(ctx->padding_indicator, *formatted_data, r, formatted_data);
if (r < 0)
goto err;
*formatted_data_len = r;
sc_format_asn1_entry(formatted_encrypted_data_entry,
*formatted_data, formatted_data_len, SC_ASN1_PRESENT);
r = SC_SUCCESS;
err:
if (pad_data) {
sc_mem_clear(pad_data, pad_data_len);
free(pad_data);
}
SC_FUNC_RETURN(card->ctx, SC_LOG_TYPE_ERROR, r);
}
static int format_head(const struct sm_ctx *ctx, const sc_apdu_t *apdu,
u8 **formatted_head)
{
if (!apdu || !formatted_head)
return SC_ERROR_INVALID_ARGUMENTS;
u8 *p = realloc(*formatted_head, 4);
if (!p)
return SC_ERROR_OUT_OF_MEMORY;
p[0] = apdu->cla;
p[1] = apdu->ins;
p[2] = apdu->p1;
p[3] = apdu->p2;
*formatted_head = p;
return add_padding(ctx, *formatted_head, 4, formatted_head);
}
static int sm_encrypt(const struct sm_ctx *ctx, sc_card_t *card,
const sc_apdu_t *apdu, sc_apdu_t *sm_apdu)
{
struct sc_asn1_entry sm_capdu[4];
u8 *p, *le = NULL, *sm_data = NULL, *fdata = NULL, *mac_data = NULL,
*asn1 = NULL, *mac = NULL;
size_t sm_data_len, fdata_len, mac_data_len, asn1_len, mac_len, le_len;
int r;
if (!apdu || !ctx || !card || !card->slot || !sm_apdu) {
r = SC_ERROR_INVALID_ARGUMENTS;
goto err;
}
if ((apdu->cla & 0x0C) == 0x0C) {
r = SC_ERROR_INVALID_ARGUMENTS;
sc_error(card->ctx, "Given APDU is already protected with some secure messaging.");
goto err;
}
sc_copy_asn1_entry(c_sm_capdu, sm_capdu);
sm_apdu->sensitive = 0;
sm_apdu->control = apdu->control;
sm_apdu->flags = apdu->flags;
sm_apdu->cla = 0x0C;
sm_apdu->ins = apdu->ins;
sm_apdu->p1 = apdu->p1;
sm_apdu->p2 = apdu->p2;
r = format_head(ctx, sm_apdu, &mac_data);
if (r < 0) {
sc_error(card->ctx, "Could not format header of SM apdu");
goto err;
}
mac_data_len = r;
/* get le and data depending on the case of the unsecure command */
switch (apdu->cse) {
case SC_APDU_CASE_1:
break;
case SC_APDU_CASE_2_SHORT:
le_len = 1;
r = format_le(apdu->le, sm_capdu + 1, &le, &le_len);
if (r < 0) {
sc_error(card->ctx, "Could not format Le of SM apdu");
goto err;
}
bin_log(card->ctx, "Protected Le (plain)", le, le_len);
break;
case SC_APDU_CASE_2_EXT:
if (card->slot->active_protocol == SC_PROTO_T0) {
/* T0 extended APDUs look just like short APDUs */
le_len = 1;
r = format_le(apdu->le, sm_capdu + 1, &le, &le_len);
if (r < 0) {
sc_error(card->ctx, "Could not format Le of SM apdu");
goto err;
}
} else {
/* in case of T1 always use 3 bytes for length */
le_len = 3;
r = format_le(apdu->le, sm_capdu + 1, &le, &le_len);
if (r < 0) {
sc_error(card->ctx, "Could not format Le of SM apdu");
goto err;
}
}
bin_log(card->ctx, "Protected Le (plain)", le, le_len);
break;
case SC_APDU_CASE_3_SHORT:
case SC_APDU_CASE_3_EXT:
r = format_data(card, ctx, apdu->data, apdu->datalen,
sm_capdu + 0, &fdata, &fdata_len);
if (r < 0) {
sc_error(card->ctx, "Could not format data of SM apdu");
goto err;
}
bin_log(card->ctx, "Padding-content indicator followed by cryptogram (plain)",
fdata, fdata_len);
break;
case SC_APDU_CASE_4_SHORT:
/* in case of T0 no Le byte is added */
if (card->slot->active_protocol != SC_PROTO_T0) {
le_len = 1;
r = format_le(apdu->le, sm_capdu + 1, &le, &le_len);
if (r < 0) {
sc_error(card->ctx, "Could not format Le of SM apdu");
goto err;
}
bin_log(card->ctx, "Protected Le (plain)", le, le_len);
}
r = format_data(card, ctx, apdu->data, apdu->datalen,
sm_capdu + 0, &fdata, &fdata_len);
if (r < 0) {
sc_error(card->ctx, "Could not format data of SM apdu");
goto err;
}
bin_log(card->ctx, "Padding-content indicator followed by cryptogram (plain)",
fdata, fdata_len);
break;
case SC_APDU_CASE_4_EXT:
if (card->slot->active_protocol == SC_PROTO_T0) {
/* again a T0 extended case 4 APDU looks just
* like a short APDU, the additional data is
* transferred using ENVELOPE and GET RESPONSE */
} else {
/* only 2 bytes are use to specify the length of the
* expected data */
le_len = 2;
r = format_le(apdu->le, sm_capdu + 1, &le, &le_len);
if (r < 0) {
sc_error(card->ctx, "Could not format Le of SM apdu");
goto err;
}
bin_log(card->ctx, "Protected Le (plain)", le, le_len);
}
r = format_data(card, ctx, apdu->data, apdu->datalen,
sm_capdu + 0, &fdata, &fdata_len);
if (r < 0) {
sc_error(card->ctx, "Could not format data of SM apdu");
goto err;
}
bin_log(card->ctx, "Padding-content indicator followed by cryptogram (plain)",
fdata, fdata_len);
break;
default:
sc_error(card->ctx, "Unhandled apdu case");
r = SC_ERROR_INVALID_DATA;
goto err;
}
r = sc_asn1_encode(card->ctx, sm_capdu, (u8 **) &asn1, &asn1_len);
if (r < 0) {
goto err;
}
if (asn1_len) {
p = realloc(mac_data, mac_data_len + asn1_len);
if (!p) {
r = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
mac_data = p;
memcpy(mac_data + mac_data_len, asn1, asn1_len);
mac_data_len += asn1_len;
r = add_padding(ctx, mac_data, mac_data_len, &mac_data);
if (r < 0) {
goto err;
}
mac_data_len = r;
}
bin_log(card->ctx, "Data to authenticate", mac_data, mac_data_len);
r = ctx->authenticate(card, ctx, mac_data, mac_data_len,
&mac);
if (r < 0) {
sc_error(card->ctx, "Could not get authentication code");
goto err;
}
mac_len = r;
sc_format_asn1_entry(sm_capdu + 2, mac, &mac_len,
SC_ASN1_PRESENT);
bin_log(card->ctx, "Cryptographic Checksum (plain)", mac, mac_len);
/* format SM apdu */
r = sc_asn1_encode(card->ctx, sm_capdu, (u8 **) &sm_data, &sm_data_len);
if (r < 0)
goto err;
if (sm_apdu->datalen < sm_data_len) {
sc_error(card->ctx, "Data for SM APDU too long");
r = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
memcpy((u8 *) sm_apdu->data, sm_data, sm_data_len);
sm_apdu->datalen = sm_data_len;
sm_apdu->lc = sm_apdu->datalen;
sm_apdu->le = 0;
sm_apdu->cse = SC_APDU_CASE_4;
bin_log(card->ctx, "ASN.1 encoded APDU data", sm_apdu->data, sm_apdu->datalen);
err:
if (fdata)
free(fdata);
if (asn1)
free(asn1);
if (mac_data)
free(mac_data);
if (mac)
free(mac);
if (le)
free(le);
if (sm_data)
free(sm_data);
SC_FUNC_RETURN(card->ctx, SC_LOG_TYPE_ERROR, r);
}
static int sm_decrypt(const struct sm_ctx *ctx, sc_card_t *card,
const sc_apdu_t *sm_apdu, sc_apdu_t *apdu)
{
int r;
struct sc_asn1_entry sm_rapdu[4];
struct sc_asn1_entry my_sm_rapdu[4];
u8 sw[2], mac[8], fdata[SC_MAX_APDU_BUFFER_SIZE];
size_t sw_len = sizeof sw, mac_len = sizeof mac, fdata_len = sizeof fdata,
buf_len, asn1_len;
const u8 *buf;
u8 *data, *mac_data = NULL, *asn1 = NULL;
sc_copy_asn1_entry(c_sm_rapdu, sm_rapdu);
sc_format_asn1_entry(sm_rapdu + 0, fdata, &fdata_len, 0);
sc_format_asn1_entry(sm_rapdu + 1, sw, &sw_len, 0);
sc_format_asn1_entry(sm_rapdu + 2, mac, &mac_len, 0);
r = sc_asn1_decode(card->ctx, sm_rapdu, sm_apdu->resp, sm_apdu->resplen,
&buf, &buf_len);
if (r < 0)
goto err;
if (buf_len > 0) {
r = SC_ERROR_UNKNOWN_DATA_RECEIVED;
goto err;
}
if (sm_rapdu[2].flags & SC_ASN1_PRESENT) {
/* copy from sm_apdu to my_sm_apdu, but leave mac at default */
sc_copy_asn1_entry(sm_rapdu, my_sm_rapdu);
sc_copy_asn1_entry(&c_sm_rapdu[2], &my_sm_rapdu[2]);
r = sc_asn1_encode(card->ctx, my_sm_rapdu, &asn1, &asn1_len);
if (r < 0)
goto err;
r = add_padding(ctx, asn1, asn1_len, &mac_data);
if (r < 0) {
goto err;
}
r = ctx->verify_authentication(card, ctx, mac, mac_len,
mac_data, r);
if (r < 0)
goto err;
} else {
r = SC_ERROR_ASN1_OBJECT_NOT_FOUND;
goto err;
}
if (sm_rapdu[0].flags & SC_ASN1_PRESENT) {
if (ctx->padding_indicator != fdata[0]) {
r = SC_ERROR_UNKNOWN_DATA_RECEIVED;
goto err;
}
r = ctx->decrypt(card, ctx, fdata + 1, fdata_len - 1, &data);
if (r < 0)
goto err;
buf_len = r;
r = no_padding(ctx->padding_indicator, data, buf_len);
if (r < 0)
goto err;
if (apdu->resplen < r) {
sc_error(card->ctx, "Response of SM APDU too long");
r = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
memcpy(apdu->resp, data, r);
apdu->resplen = r;
} else {
apdu->resplen = 0;
}
if (sm_rapdu[1].flags & SC_ASN1_PRESENT) {
if (sw_len != 2) {
sc_error(card->ctx, "Length of processing status bytes must be 2");
r = SC_ERROR_ASN1_END_OF_CONTENTS;
goto err;
}
apdu->sw1 = sw[0];
apdu->sw2 = sw[1];
} else {
r = SC_ERROR_ASN1_OBJECT_NOT_FOUND;
goto err;
}
sc_debug(card->ctx, "Decrypted APDU sw1=%02x sw2=%02x",
apdu->sw1, apdu->sw2);
bin_log(card->ctx, "Decrypted APDU response data",
apdu->resp, apdu->resplen);
/* XXX verify mac */
r = SC_SUCCESS;
err:
if (asn1) {
free(asn1);
}
if (mac_data) {
free(mac_data);
}
SC_FUNC_RETURN(card->ctx, SC_LOG_TYPE_ERROR, r);
}
int sm_transmit_apdu(const struct sm_ctx *sctx, sc_card_t *card,
sc_apdu_t *apdu)
{
sc_apdu_t sm_apdu;
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE - 2], sbuf[SC_MAX_APDU_BUFFER_SIZE - 4];
sm_apdu.data = sbuf;
sm_apdu.datalen = sizeof sbuf;
sm_apdu.resp = rbuf;
sm_apdu.resplen = sizeof rbuf;
SC_TEST_RET(card->ctx, sm_encrypt(sctx, card, apdu, &sm_apdu),
"Could not encrypt APDU.");
SC_TEST_RET(card->ctx, sc_transmit_apdu(card, &sm_apdu),
"Could not transmit SM APDU.");
SC_TEST_RET(card->ctx, sm_decrypt(sctx, card, &sm_apdu, apdu),
"Could not decrypt APDU.");
SC_TEST_RET(card->ctx, sc_check_sw(card, apdu->sw1, apdu->sw2),
"Card returned error.");
SC_FUNC_RETURN(card->ctx, SC_LOG_TYPE_ERROR, SC_SUCCESS);
}

67
ccid/src/sm.h Normal file
View File

@@ -0,0 +1,67 @@
/*
* Copyright (C) 2010 Frank Morgner
*
* This file is part of ccid.
*
* ccid is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* ccid 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* ccid. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _CCID_SM_H
#define _CCID_SM_H
#include <opensc/opensc.h>
#ifdef __cplusplus
extern "C" {
#endif
#define SM_ISO_PADDING 0x01
#define SM_NO_PADDING 0x02
struct sm_ctx {
u8 padding_indicator;
size_t block_length;
void *authentication_ctx;
int (*authenticate)(sc_card_t *card, const struct sm_ctx *ctx,
const u8 *data, size_t datalen, u8 **outdata);
int (*verify_authentication)(sc_card_t *card, const struct sm_ctx *ctx,
const u8 *mac, size_t maclen,
const u8 *macdata, size_t macdatalen);
void *cipher_ctx;
int (*encrypt)(sc_card_t *card, const struct sm_ctx *ctx,
const u8 *data, size_t datalen, u8 **enc);
int (*decrypt)(sc_card_t *card, const struct sm_ctx *ctx,
const u8 *enc, size_t enclen, u8 **data);
};
int sm_transmit_apdu(const struct sm_ctx *sctx, sc_card_t *card,
sc_apdu_t *apdu);
int add_iso_pad(const u8 *data, size_t datalen, int block_size, u8 **padded);
int add_padding(const struct sm_ctx *ctx, const u8 *data, size_t datalen,
u8 **padded);
#define bin_print(file, label, data, len) \
_bin_log(NULL, 0, NULL, 0, NULL, label, data, len, file)
#define bin_log(ctx, label, data, len) \
_bin_log(ctx, SC_LOG_TYPE_DEBUG, __FILE__, __LINE__, __FUNCTION__, label, data, len, NULL)
void _bin_log(sc_context_t *ctx, int type, const char *file, int line,
const char *func, const char *label, const u8 *data, size_t len,
FILE *f);
#ifdef __cplusplus
}
#endif
#endif

1800
ccid/src/usb.c Normal file

File diff suppressed because it is too large Load Diff

145
ccid/src/usbstring.c Normal file
View File

@@ -0,0 +1,145 @@
/*
* Copyright (C) 2003 David Brownell
*
* This program 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.
*/
#include <errno.h>
#include <string.h>
#include <linux/types.h>
#include <linux/usb/ch9.h>
#include <stdio.h>
#include "usbstring.h"
static inline void put_unaligned_le16(__u16 val, __u16 *cp)
{
__u8 *p = (void *)cp;
*p++ = (__u8) val;
*p++ = (__u8) (val >> 8);
}
static int utf8_to_utf16le(const char *s, __u16 *cp, unsigned len)
{
int count = 0;
__u8 c;
__u16 uchar;
/* this insists on correct encodings, though not minimal ones.
* BUT it currently rejects legit 4-byte UTF-8 code points,
* which need surrogate pairs. (Unicode 3.1 can use them.)
*/
while (len != 0 && (c = (__u8) *s++) != 0) {
if (c & 0x80) {
// 2-byte sequence:
// 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx
if ((c & 0xe0) == 0xc0) {
uchar = (c & 0x1f) << 6;
c = (__u8) *s++;
if ((c & 0xc0) != 0xc0)
goto fail;
c &= 0x3f;
uchar |= c;
// 3-byte sequence (most CJKV characters):
// zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx
} else if ((c & 0xf0) == 0xe0) {
uchar = (c & 0x0f) << 12;
c = (__u8) *s++;
if ((c & 0xc0) != 0xc0)
goto fail;
c &= 0x3f;
uchar |= c << 6;
c = (__u8) *s++;
if ((c & 0xc0) != 0xc0)
goto fail;
c &= 0x3f;
uchar |= c;
/* no bogus surrogates */
if (0xd800 <= uchar && uchar <= 0xdfff)
goto fail;
// 4-byte sequence (surrogate pairs, currently rare):
// 11101110wwwwzzzzyy + 110111yyyyxxxxxx
// = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
// (uuuuu = wwww + 1)
// FIXME accept the surrogate code points (only)
} else
goto fail;
} else
uchar = c;
put_unaligned_le16 (uchar, cp++);
count++;
len--;
}
return count;
fail:
return -1;
}
/**
* usb_gadget_get_string - fill out a string descriptor
* @table: of c strings encoded using UTF-8
* @id: string id, from low byte of wValue in get string descriptor
* @buf: at least 256 bytes
*
* Finds the UTF-8 string matching the ID, and converts it into a
* string descriptor in utf16-le.
* Returns length of descriptor (always even) or negative errno
*
* If your driver needs strings in multiple languages, you'll probably
* "switch (wIndex) { ... }" in your ep0 string descriptor logic,
* using this routine after choosing which set of UTF-8 strings to use.
*
* Note that US-ASCII is a strict subset of UTF-8; any string bytes with
* the eighth bit set will be multibyte UTF-8 characters, not ISO-8859/1
* characters.
*/
int
usb_gadget_get_string (struct usb_gadget_strings *table, int id, __u8 *buf)
{
struct usb_string *s;
int len;
/* descriptor 0 has the language id */
if (id == 0) {
buf [0] = 4;
buf [1] = USB_DT_STRING;
buf [2] = (__u8) table->language;
buf [3] = (__u8) (table->language >> 8);
return 4;
}
for (s = table->strings; s && s->s; s++) {
if (s->id == id)
break;
}
/* unrecognized: stall. */
if (!s || !s->s)
return -EINVAL;
/* string descriptors have length, tag, then UTF16-LE text */
len = strlen (s->s);
if (len > 126)
len = 126;
memset (buf + 2, 0, 2 * len); /* zero all the bytes */
len = utf8_to_utf16le(s->s, (__u16 *)&buf[2], len);
if (len < 0)
return -EINVAL;
buf [0] = (len + 1) * 2;
buf [1] = USB_DT_STRING;
return buf [0];
}

38
ccid/src/usbstring.h Normal file
View File

@@ -0,0 +1,38 @@
/*
* (c) Copyright 2003 by David Brownell
* All Rights Reserved.
*
* This software is licensed under the GNU LGPL version 2.
*/
/* utility to simplify dealing with string descriptors */
/**
* struct usb_string - wraps a C string and its USB id
* @id: the (nonzero) ID for this string
* @s: the string, in UTF-8 encoding
*
* If you're using usb_gadget_get_string(), use this to wrap a string
* together with its ID.
*/
struct usb_string {
__u8 id;
const char *s;
};
/**
* struct usb_gadget_strings - a set of USB strings in a given language
* @language: identifies the strings' language (0x0409 for en-us)
* @strings: array of strings with their ids
*
* If you're using usb_gadget_get_string(), use this to wrap all the
* strings for a given language.
*/
struct usb_gadget_strings {
__u16 language; /* 0x0409 for en-us */
struct usb_string *strings;
};
/* put descriptor for string with that id into buf (buflen >= 256) */
int usb_gadget_get_string (struct usb_gadget_strings *table, int id, __u8 *buf);