extracted libpace from ccid

git-svn-id: https://vsmartcard.svn.sourceforge.net/svnroot/vsmartcard@359 96b47cad-a561-4643-ad3b-153ac7d7599c
This commit is contained in:
frankmorgner
2010-11-14 20:34:52 +00:00
parent d6aff6c0d9
commit 915b2fe5f2
28 changed files with 2588 additions and 936 deletions

View File

@@ -1,9 +1,3 @@
AM_CPPFLAGS = -I$(top_srcdir)/src/pace -I$(top_builddir)/src/pace
libpace_la_SOURCES = sm.c scutil.c pace.c pace_lib.c
libpace_la_LIBADD = $(OPENSC_LIBS) $(OPENSSL_LIBS)
libpace_la_CFLAGS = $(OPENSC_CFLAGS) $(OPENSSL_CFLAGS)
ccid_emulator_SOURCES = ccid.c usbstring.c usb.c binutil.c
ccid_emulator_LDADD = $(OPENSC_LIBS) $(OPENSSL_LIBS) $(PTHREAD_LIBS)
ccid_emulator_CFLAGS = $(OPENSC_CFLAGS) $(OPENSSL_CFLAGS) $(PTHREAD_CFLAGS)
@@ -12,10 +6,6 @@ cats_test_SOURCES = cats-test.c pcscutil.c
cats_test_LDADD = $(PCSC_LIBS)
cats_test_CFLAGS = $(PCSC_CFLAGS)
pace_tool_SOURCES = pace-tool.c binutil.c
pace_tool_LDADD = $(OPENSC_LIBS) $(OPENSSL_LIBS) libpace.la
pace_tool_CFLAGS = $(OPENSC_CFLAGS) $(OPENSSL_CFLAGS)
bin_PROGRAMS =
noinst_HEADERS = \
@@ -38,23 +28,8 @@ endif
if WITH_PACE
bin_PROGRAMS += pace-tool
ccid_emulator_LDADD += libpace.la
lib_LTLIBRARIES = libpace.la
nobase_include_HEADERS = \
pace/sm.h \
pace/pace.h \
pace/scutil.h \
pace/pace_lib.h
ccid_emulator_LDADD += $(LIBPACE_LIBS)
ccid_emulator_CFLAGS += $(LIBPACE_CFLAGS)
else
ccid_emulator_SOURCES += scutil.c sm.c
noinst_HEADERS += \
pace/sm.h \
pace/pace.h \
pace/scutil.h \
pace/pace_lib.h
endif

View File

@@ -1,541 +0,0 @@
/*
* 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 <asm/byteorder.h>
#include <opensc/log.h>
#include <openssl/pace.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
static int verbose = 0;
static int doinfo = 0;
static u8 dobreak = 0;
static u8 dochangepin = 0;
static u8 doresumepin = 0;
static u8 dounblock = 0;
static u8 dotranslate = 0;
static const char *newpin = NULL;
static int usb_reader_num = -1;
static const char *pin = NULL;
static u8 usepin = 0;
static const char *puk = NULL;
static u8 usepuk = 0;
static const char *can = NULL;
static u8 usecan = 0;
static const char *mrz = NULL;
static u8 usemrz = 0;
static u8 chat[0xff];
static u8 desc[0xffff];
static const char *cdriver = NULL;
static char *file = 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_BREAK 'b'
#define OPT_CHAT 'C'
#define OPT_CERTDESC 'D'
#define OPT_CHANGE_PIN 'N'
#define OPT_RESUME_PIN 'R'
#define OPT_UNBLOCK_PIN 'U'
#define OPT_TRANSLATE 't'
#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 },
{ "break", no_argument, NULL, OPT_BREAK },
{ "chat", required_argument, NULL, OPT_CHAT },
{ "cert-desc", required_argument, NULL, OPT_CERTDESC },
{ "new-pin", optional_argument, NULL, OPT_CHANGE_PIN },
{ "resume-pin", no_argument, NULL, OPT_RESUME_PIN },
{ "unblock-pin", no_argument, NULL, OPT_UNBLOCK_PIN },
{ "translate", optional_argument, NULL, OPT_TRANSLATE },
{ "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 (transport) PIN",
"Run PACE with PUK",
"Run PACE with CAN",
"Run PACE with MRZ (insert MRZ without newlines)",
"Brute force the secret (only for PIN, CAN, PUK)",
"Card holder authorization template to use (hex string)",
"Certificate description to use (hex string)",
"Install a new PIN",
"Resume PIN (uses CAN to activate last retry)",
"Unblock PIN (uses PUK to activate three more retries)",
"APDUs to send through SM channel (default: stdin)",
"Use (several times) to be more verbose",
"Print version, available readers and drivers.",
};
int pace_translate_apdus(struct sm_ctx *sctx, sc_card_t *card, FILE *input)
{
u8 buf[4 + 3 + 0xffff + 3];
char *read = NULL;
size_t readlen = 0, apdulen;
sc_apdu_t apdu;
ssize_t linelen;
int r;
memset(&apdu, 0, sizeof apdu);
while (1) {
if (input == stdin)
printf("Enter unencrypted C-APDU (empty line to exit)\n");
linelen = getline(&read, &readlen, input);
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;
apdulen = sizeof buf;
if (sc_hex_to_bin(read, buf, &apdulen) < 0) {
sc_error(card->ctx, "Could not format binary string");
continue;
}
if (input != stdin)
bin_print(stdout, "Unencrypted C-APDU", buf, apdulen);
r = build_apdu(card->ctx, buf, apdulen, &apdu);
if (r < 0) {
bin_log(ctx, "Invalid C-APDU", buf, apdulen);
continue;
}
apdu.resp = buf;
apdu.resplen = sizeof buf;
r = sm_transmit_apdu(sctx, card, &apdu);
if (r < 0) {
sc_error(card->ctx, "Could not send C-APDU: %s", sc_strerror(r));
continue;
}
printf("Decrypted R-APDU sw1=%02x sw2=%02x\n", apdu.sw1, apdu.sw2);
bin_print(stdout, "Decrypted R-APDU response data", apdu.resp, apdu.resplen);
printf("======================================================================\n");
}
if (read)
free(read);
return r;
}
int
main (int argc, char **argv)
{
int i, oindex = 0;
size_t channeldatalen;
struct sm_ctx sctx, tmpctx;
struct establish_pace_channel_input pace_input;
struct establish_pace_channel_output pace_output;
time_t t_start, t_end;
size_t outlen;
memset(&sctx, 0, sizeof sctx);
memset(&tmpctx, 0, sizeof tmpctx);
memset(&pace_input, 0, sizeof pace_input);
memset(&pace_output, 0, sizeof pace_output);
while (1) {
i = getopt_long(argc, argv, "hr:i::u::a::z::bC:D:N::RUt::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 = 1;
break;
case OPT_PUK:
usepuk = 1;
puk = optarg;
if (!puk)
pin = getenv("PUK");
break;
case OPT_PIN:
usepin = 1;
pin = optarg;
if (!pin)
pin = getenv("PIN");
break;
case OPT_CAN:
usecan = 1;
can = optarg;
if (!can)
can = getenv("CAN");
break;
case OPT_MRZ:
usemrz = 1;
mrz = optarg;
if (!mrz)
can = getenv("MRZ");
break;
case OPT_BREAK:
dobreak = 1;
break;
case OPT_CHAT:
pace_input.chat = chat;
pace_input.chat_length = sizeof chat;
if (sc_hex_to_bin(optarg, (u8 *) pace_input.chat,
&pace_input.chat_length) < 0) {
parse_error(argv[0], options, option_help, optarg, oindex);
exit(2);
}
break;
case OPT_CERTDESC:
pace_input.certificate_description = desc;
pace_input.certificate_description_length = sizeof desc;
if (sc_hex_to_bin(optarg, (u8 *) pace_input.certificate_description,
&pace_input.certificate_description_length) < 0) {
parse_error(argv[0], options, option_help, optarg, oindex);
exit(2);
}
break;
case OPT_CHANGE_PIN:
dochangepin = 1;
newpin = optarg;
if (!newpin)
pin = getenv("NEWPIN");
break;
case OPT_RESUME_PIN:
doresumepin = 1;
break;
case OPT_UNBLOCK_PIN:
dounblock = 1;
break;
case OPT_TRANSLATE:
dotranslate = 1;
if (optarg) {
file = 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) {
fprintf(stderr, "Can't initialize reader\n");
exit(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) {
fprintf(stderr, "No card found\n");
sc_release_context(ctx);
exit(1);
}
if (dobreak) {
char can_ch[7];
unsigned int can_nb = 0;
if (usepin) {
pace_input.pin_id = PACE_PIN;
if (pin) {
if (sscanf(pin, "%u", &can_nb) != 1) {
fprintf(stderr, "PIN is not an unsigned integer.\n");
exit(2);
}
}
} else if (usecan) {
pace_input.pin_id = PACE_CAN;
if (can) {
if (sscanf(can, "%u", &can_nb) != 1) {
fprintf(stderr, "CAN is not an unsigned integer.\n");
exit(2);
}
}
} else if (usepuk) {
pace_input.pin_id = PACE_PUK;
if (puk) {
if (sscanf(puk, "%u", &can_nb) != 1) {
fprintf(stderr, "PUK is not an unsigned integer.\n");
exit(2);
}
}
} else {
fprintf(stderr, "Please specify whether to do PACE with "
"PIN, CAN or PUK.\n");
exit(1);
}
sprintf(can_ch, "%06u", can_nb);
pace_input.pin = can_ch;
pace_input.pin_length = strlen(can_ch);
t_start = time(NULL);
while (0 > EstablishPACEChannel(NULL, card, pace_input, &pace_output,
&sctx)) {
printf("Failed trying %s=%s\n",
pace_secret_name(pace_input.pin_id), pace_input.pin);
can_nb++;
sprintf(can_ch, "%06u", can_nb);
if (can_nb > 999999)
break;
}
t_end = time(NULL);
if (can_nb > 999999) {
printf("Tried breaking %s for %.0fs without success.\n",
pace_secret_name(pace_input.pin_id), difftime(t_end, t_start));
goto err;
} else {
printf("Tried breaking %s for %.0fs with success.\n",
pace_secret_name(pace_input.pin_id), difftime(t_end, t_start));
printf("%s=%s\n", pace_secret_name(pace_input.pin_id), pace_input.pin);
}
}
if (doresumepin) {
pace_input.pin_id = PACE_CAN;
if (can) {
pace_input.pin = can;
pace_input.pin_length = strlen(can);
} else {
pace_input.pin = NULL;
pace_input.pin_length = 0;
}
t_start = time(NULL);
i = EstablishPACEChannel(NULL, card, pace_input, &pace_output,
&tmpctx);
t_end = time(NULL);
if (i < 0)
goto err;
printf("Established PACE channel with CAN in %.0fs.\n",
difftime(t_end, t_start));
pace_input.pin_id = PACE_PIN;
if (pin) {
pace_input.pin = pin;
pace_input.pin_length = strlen(pin);
} else {
pace_input.pin = NULL;
pace_input.pin_length = 0;
}
t_start = time(NULL);
i = EstablishPACEChannel(&tmpctx, card, pace_input, &pace_output,
&sctx);
t_end = time(NULL);
if (i < 0)
goto err;
printf("Established PACE channel with PIN in %.0fs. PIN resumed.\n",
difftime(t_end, t_start));
}
if (dounblock) {
pace_input.pin_id = PACE_PUK;
if (puk) {
pace_input.pin = puk;
pace_input.pin_length = strlen(puk);
} else {
pace_input.pin = NULL;
pace_input.pin_length = 0;
}
t_start = time(NULL);
i = EstablishPACEChannel(NULL, card, pace_input, &pace_output,
&sctx);
t_end = time(NULL);
if (i < 0)
goto err;
printf("Established PACE channel with PUK in %.0fs.\n",
difftime(t_end, t_start));
i = pace_unblock_pin(&sctx, card);
if (i < 0)
goto err;
printf("Unblocked PIN.\n");
}
if (dochangepin) {
pace_input.pin_id = PACE_PIN;
if (pin) {
pace_input.pin = pin;
pace_input.pin_length = strlen(pin);
} else {
pace_input.pin = NULL;
pace_input.pin_length = 0;
}
t_start = time(NULL);
i = EstablishPACEChannel(NULL, card, pace_input, &pace_output,
&sctx);
t_end = time(NULL);
if (i < 0)
goto err;
printf("Established PACE channel with PIN in %.0fs.\n",
difftime(t_end, t_start));
i = pace_change_pin(&sctx, card, newpin, newpin ? strlen(newpin) : 0);
if (i < 0)
goto err;
printf("Changed PIN.\n");
}
if (dotranslate || (!doresumepin && !dochangepin && !dounblock && !dobreak)) {
pace_input.pin = NULL;
pace_input.pin_length = 0;
if (usepin) {
pace_input.pin_id = PACE_PIN;
if (pin) {
pace_input.pin = pin;
pace_input.pin_length = strlen(pin);
}
} else if (usecan) {
pace_input.pin_id = PACE_CAN;
if (can) {
pace_input.pin = can;
pace_input.pin_length = strlen(can);
}
} else if (usemrz) {
pace_input.pin_id = PACE_MRZ;
if (mrz) {
pace_input.pin = mrz;
pace_input.pin_length = strlen(mrz);
}
} else if (usepuk) {
pace_input.pin_id = PACE_PUK;
if (puk) {
pace_input.pin = puk;
pace_input.pin_length = strlen(puk);
}
} else {
fprintf(stderr, "Please specify whether to do PACE with "
"PIN, CAN, MRZ or PUK.\n");
exit(1);
}
t_start = time(NULL);
i = EstablishPACEChannel(NULL, card, pace_input, &pace_output,
&sctx);
t_end = time(NULL);
if (i < 0)
goto err;
printf("Established PACE channel with %s in %.0fs.\n",
pace_secret_name(pace_input.pin_id), difftime(t_end, t_start));
if (dotranslate) {
FILE *input;
if (!file || strncmp(file, "stdin", strlen("stdin")) == 0)
input = stdin;
else {
input = fopen(file, "r");
if (!input)
perror("Opening file with APDUs");
}
i = pace_translate_apdus(&sctx, card, input);
fclose(input);
if (i < 0)
goto err;
}
}
err:
pace_sm_ctx_clear_free(sctx.cipher_ctx);
pace_sm_ctx_clear_free(tmpctx.cipher_ctx);
if (pace_output.ef_cardaccess)
free(pace_output.ef_cardaccess);
if (pace_output.recent_car)
free(pace_output.recent_car);
if (pace_output.previous_car)
free(pace_output.previous_car);
if (pace_output.id_icc)
free(pace_output.id_icc);
if (pace_output.id_pcd)
free(pace_output.id_pcd);
sc_reset(card);
sc_disconnect_card(card, 0);
sc_release_context(ctx);
return -i;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,246 +0,0 @@
/*
* 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/>.
*/
/**
* @file
*/
#ifndef _CCID_PACE_H
#define _CCID_PACE_H
#include <pace/pace_lib.h>
#include <pace/sm.h>
#include <opensc/opensc.h>
#include <openssl/bn.h>
#include <openssl/pace.h>
#ifdef __cplusplus
extern "C" {
#endif
/** PACE capabilities (TR-03119): PACE */
#define PACE_BITMAP_PACE 0x40
/** PACE capabilities (TR-03119): EPA: eID */
#define PACE_BITMAP_EID 0x20
/** PACE capabilities (TR-03119): EPA: eSign */
#define PACE_BITMAP_ESIGN 0x10
/** PACE result (TR-03119): Kein Fehler */
#define PACE_SUCCESS 0x00000000
/** PACE result (TR-03119): Längen im Input sind inkonsistent */
#define PACE_ERROR_LENGTH_INCONSISTENT 0xD0000001
/** PACE result (TR-03119): Unerwartete Daten im Input */
#define PACE_ERROR_UNEXPECTED_DATA 0xD0000002
/** PACE result (TR-03119): Unerwartete Kombination von Daten im Input */
#define PACE_ERROR_UNEXPECTED_DATA_COMBINATION 0xD0000003
/** PACE result (TR-03119): Die Karte unterstützt das PACE Verfahren nicht. (Unerwartete Struktur in Antwortdaten der Karte) */
#define PACE_ERROR_CARD_NOT_SUPPORTED 0xE0000001
/** PACE result (TR-03119): Der Kartenleser unterstützt den angeforderten bzw. den ermittelten Algorithmus nicht. */
#define PACE_ERROR_ALGORITH_NOT_SUPPORTED 0xE0000002
/** PACE result (TR-03119): Der Kartenleser kennt die PIN ID nicht. */
#define PACE_ERROR_PINID_NOT_SUPPORTED 0xE0000003
/** PACE result (TR-03119): Negative Antwort der Karte auf Select EF_CardAccess (needs to be OR-ed with SW1|SW2) */
#define PACE_ERROR_SELECT_EF_CARDACCESS 0xF0000000
/** PACE result (TR-03119): Negative Antwort der Karte auf Read Binary (needs to be OR-ed with SW1|SW2) */
#define PACE_ERROR_READ_BINARY 0xF0010000
/** PACE result (TR-03119): Negative Antwort der Karte auf MSE: Set AT (needs to be OR-ed with SW1|SW2) */
#define PACE_ERROR_MSE_SET_AT 0xF0020000
/** PACE result (TR-03119): Negative Antwort der Karte auf General Authenticate Step 1 (needs to be OR-ed with SW1|SW2) */
#define PACE_ERROR_GENERAL_AUTHENTICATE_1 0xF0030000
/** PACE result (TR-03119): Negative Antwort der Karte auf General Authenticate Step 2 (needs to be OR-ed with SW1|SW2) */
#define PACE_ERROR_GENERAL_AUTHENTICATE_2 0xF0040000
/** PACE result (TR-03119): Negative Antwort der Karte auf General Authenticate Step 3 (needs to be OR-ed with SW1|SW2) */
#define PACE_ERROR_GENERAL_AUTHENTICATE_3 0xF0050000
/** PACE result (TR-03119): Negative Antwort der Karte auf General Authenticate Step 4 (needs to be OR-ed with SW1|SW2) */
#define PACE_ERROR_GENERAL_AUTHENTICATE_4 0xF0060000
/** PACE result (TR-03119): Kommunikationsabbruch mit Karte. */
#define PACE_ERROR_COMMUNICATION 0xF0100001
/** PACE result (TR-03119): Keine Karte im Feld. */
#define PACE_ERROR_NO_CARD 0xF0100002
/** PACE result (TR-03119): Benutzerabbruch. */
#define PACE_ERROR_ABORTED 0xF0200001
/** PACE result (TR-03119): Benutzer Timeout */
#define PACE_ERROR_TIMEOUT 0xF0200002
//#define PACE_MRZ 0x01
//#define PACE_CAN 0x02
//#define PACE_PIN 0x03
//#define PACE_PUK 0x04
/** File identifier of EF.CardAccess */
#define FID_EF_CARDACCESS 0x011C
/** Maximum length of EF.CardAccess */
#define MAX_EF_CARDACCESS 2048
/** Maximum length of PIN */
#define MAX_PIN_LEN 6
/** Minimum length of PIN */
#define MIN_PIN_LEN 6
/** Minimum length of MRZ */
#define MAX_MRZ_LEN 128
const char *pace_secret_name(enum s_type pin_id);
/**
* Input data for EstablishPACEChannel()
*/
struct establish_pace_channel_input {
/** Type of secret (CAN, MRZ, PIN or PUK). You may use <tt>enum s_type</tt> from \c <openssl/pace.h> */
unsigned char pin_id;
/** Length of \a chat */
size_t chat_length;
/** Card holder authorization template */
const unsigned char *chat;
/** Length of \a pin */
size_t pin_length;
/** Secret */
const unsigned char *pin;
/** Length of \a certificate_description */
size_t certificate_description_length;
/** Certificate description */
const unsigned char *certificate_description;
};
/**
* Output data for EstablishPACEChannel()
*/
struct establish_pace_channel_output {
/** PACE result (TR-03119) */
unsigned int result;
/** MSE: Set AT status byte */
unsigned char mse_set_at_sw1;
/** MSE: Set AT status byte */
unsigned char mse_set_at_sw2;
/** Length of \a ef_cardaccess */
size_t ef_cardaccess_length;
/** EF.CardAccess */
unsigned char *ef_cardaccess;
/** Length of \a recent_car */
size_t recent_car_length;
/** Most recent certificate authority reference */
unsigned char *recent_car;
/** Length of \a previous_car */
size_t previous_car_length;
/** Previous certificate authority reference */
unsigned char *previous_car;
/** Length of \a id_icc */
size_t id_icc_length;
/** ICC identifier */
unsigned char *id_icc;
/** Length of \a id_pcd */
size_t id_pcd_length;
/** PCD identifier */
unsigned char *id_pcd;
/** Length of \a hash_cert_desc */
size_t hash_cert_desc_len;
/** Hash of certificate description */
unsigned char *hash_cert_desc;
};
#ifdef BUERGERCLIENT_WORKAROUND
int get_ef_card_access(sc_card_t *card,
u8 **ef_cardaccess, size_t *length_ef_cardaccess);
#endif
/**
* @brief Get the reader's PACE capabilities
*
* @param[in,out] bitmap where to store capabilities bitmap
* @note Since this code offers no support for terminal certificate, the bitmap is always \c PACE_BITMAP_PACE|PACE_BITMAP_EID
*
* @return \c SC_SUCCESS or error code if an error occurred
*/
int GetReadersPACECapabilities(u8 *bitmap);
/**
* @brief Establish secure messaging using PACE
*
* Prints certificate description and card holder authorization template if
* given in a human readable form to stdout. If no secret is given, the user is
* asked for it. Only \a pace_input.pin_id is mandatory, the other members of
* \a pace_input can be set to \c 0 or \c NULL.
*
* The buffers in \a pace_output are allocated using \c realloc() and should be
* set to NULL, if empty. If an EF.CardAccess is already present, this file is
* reused and not fetched from the card.
*
* @param[in] oldpacectx (optional) Old SM context, if PACE is established in an existing SM channel
* @param[in] card
* @param[in] pace_input
* @param[in,out] pace_output
* @param[out] sctx
*
* @return \c SC_SUCCESS or error code if an error occurred
*/
int EstablishPACEChannel(struct sm_ctx *oldpacectx, sc_card_t *card,
struct establish_pace_channel_input pace_input,
struct establish_pace_channel_output *pace_output,
struct sm_ctx *sctx);
/**
* @brief Sends a reset retry counter APDU
*
* According to TR-03110 the reset retry counter APDU is used to set a new PIN
* or to reset the retry counter of the PIN. The standard requires this
* operation to be authorized either by an established PACE channel or by the
* effective authorization of the terminal's certificate.
*
* @param[in] ctx (optional) PACE SM context
* @param[in] card
* @param[in] pin_id Type of secret (usually PIN or CAN). You may use <tt>enum s_type</tt> from \c <openssl/pace.h>.
* @param[in] ask_for_secret whether to ask the user for the secret (\c 1) or not (\c 0)
* @param[in] new (optional) new secret
* @param[in] new_len (optional) length of \a new
*
* @return \c SC_SUCCESS or error code if an error occurred
*/
int pace_reset_retry_counter(struct sm_ctx *ctx, sc_card_t *card,
enum s_type pin_id, int ask_for_secret,
const char *new, size_t new_len);
/**
* @brief Send APDU to unblock the PIN
*
* @param[in] ctx (optional) PACE SM context
* @param[in] card
*/
#define pace_unblock_pin(ctx, card) \
pace_reset_retry_counter(ctx, card, PACE_PIN, 0, NULL, 0)
/** Send APDU to set a new PIN
*
* @param[in] ctx (optional) PACE SM context
* @param[in] card
* @param[in] new (optional) new PIN
* @param[in] new_len (optional) length of \a new
*/
#define pace_change_pin(ctx, card, newp, newplen) \
pace_reset_retry_counter(ctx, card, PACE_PIN, 1, newp, newplen)
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,77 +0,0 @@
/*
* 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/>.
*/
/**
* @file
*/
#ifndef _CCID_PACE_LIB_H
#define _CCID_PACE_LIB_H
#include <openssl/pace.h>
#ifdef __cplusplus
extern "C" {
#endif
/** PACE secure messaging context */
struct pace_sm_ctx {
/** Send sequence counter */
BIGNUM *ssc;
/** Key for message authentication code */
const BUF_MEM *key_mac;
/** Key for encryption and decryption */
const BUF_MEM *key_enc;
/** PACE context */
PACE_CTX *ctx;
};
/**
* @brief Creates a PACE SM object
*
* @param[in] key_mac Key for message authentication code
* @param[in] key_enc Key for encryption and decryption
* @param[in] ctx PACE context
*
* @return Initialized PACE SM object or NULL, if an error occurred
*/
struct pace_sm_ctx * pace_sm_ctx_create(const BUF_MEM *key_mac,
const BUF_MEM *key_enc, PACE_CTX *ctx);
/**
* @brief Frees a PACE SM object
*
* Frees memory allocated for \a ctx and its send sequence counter
*
* @param[in] ctx object to be freed
*/
void pace_sm_ctx_free(struct pace_sm_ctx *ctx);
/**
* @brief Frees a PACE SM object and all its components
*
* Frees memory allocated for \a ctx and its send sequence counter, keys and PACE context
*
* @param[in] ctx object to be freed
*/
void pace_sm_ctx_clear_free(struct pace_sm_ctx *ctx);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,94 +0,0 @@
/*
* 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/>.
*/
/**
* @file
*/
#ifndef _CCID_SCUTIL_H
#define _CCID_SCUTIL_H
#include <opensc/opensc.h>
/**
* @brief Initializes smart card context and reader
*
* @param[in] reader_id Index to the reader to be used (optional). Set to -1 to use a reader with a inserted card.
* @param[in] cdriver Card driver to be used (optional)
* @param[in] verbose verbosity level passed to \c sc_context_t
* @param[in,out] ctx Where to write the sc context
* @param[in,out] reader Where to write the reader context
*
* @return
*/
int initialize(int reader_id, const char *cdriver, int verbose,
sc_context_t **ctx, sc_reader_t **reader);
/**
* @brief Parse an APDU buffer
*
* @param[in] ctx sc context
* @param[in] buf Buffer of APDU
* @param[in] len Length of \a buf
* @param [in,out] apdu Where to write the initialized APDU
*
* @return \c SC_SUCCESS or error code if an error occurred
*/
int build_apdu(sc_context_t *ctx, const u8 *buf, size_t len, sc_apdu_t *apdu);
/**
* @brief Print binary data to a file stream
*
* @param[in] file File for printing
* @param[in] label Label to prepend to the buffer
* @param[in] data Binary data
* @param[in] len Length of \a data
*/
#define bin_print(file, label, data, len) \
_bin_log(NULL, 0, NULL, 0, NULL, label, data, len, file)
/**
* @brief Log binary data to a sc context
*
* @param[in] ctx Context for logging
* @param[in] label Label to prepend to the buffer
* @param[in] data Binary data
* @param[in] len Length of \a data
*/
#define bin_log(ctx, label, data, len) \
_bin_log(ctx, SC_LOG_TYPE_DEBUG, __FILE__, __LINE__, __FUNCTION__, label, data, len, NULL)
/**
* @brief Log binary data
*
* Either choose \a ctx or \a file for logging
*
* @param[in] ctx (optional) Context for logging
* @param[in] type Debug level
* @param[in] file File name to be prepended
* @param[in] line Line to be prepended
* @param[in] func Function to be prepended
* @param[in] label label to prepend to the buffer
* @param[in] data binary data
* @param[in] len length of \a data
* @param[in] f (optional) File for printing
*/
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);
int print_avail(int verbose);
#endif

View File

@@ -1,98 +0,0 @@
/*
* 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/>.
*/
/**
* @file
*/
#ifndef _CCID_SM_H
#define _CCID_SM_H
#include <opensc/opensc.h>
#ifdef __cplusplus
extern "C" {
#endif
/** Padding indicator: use ISO/IEC 9797-1 padding method 2 */
#define SM_ISO_PADDING 0x01
/** Padding indicator: use no padding */
#define SM_NO_PADDING 0x02
/** Secure messaging context */
struct sm_ctx {
/** 1 if secure messaging is activated, 0 otherwise */
unsigned char active;
/** Padding-content indicator byte (ISO 7816-4 Table 30) */
u8 padding_indicator;
/** Pad to this block length */
size_t block_length;
/** Authentication context for the specific authentication implementation */
void *authentication_ctx;
/** Call back function for authentication of data */
int (*authenticate)(sc_card_t *card, const struct sm_ctx *ctx,
const u8 *data, size_t datalen, u8 **outdata);
/** Call back function for verifying authentication data */
int (*verify_authentication)(sc_card_t *card, const struct sm_ctx *ctx,
const u8 *mac, size_t maclen,
const u8 *macdata, size_t macdatalen);
/** Cipher context for the specific cipher implementation */
void *cipher_ctx;
/** Call back function for encryption of data */
int (*encrypt)(sc_card_t *card, const struct sm_ctx *ctx,
const u8 *data, size_t datalen, u8 **enc);
/** Call back function for decryption of data */
int (*decrypt)(sc_card_t *card, const struct sm_ctx *ctx,
const u8 *enc, size_t enclen, u8 **data);
/** Call back function for actions before encoding and encryption of \a apdu */
int (*pre_transmit)(sc_card_t *card, const struct sm_ctx *ctx,
sc_apdu_t *apdu);
/** Call back function for actions before decryption and decoding of \a sm_apdu */
int (*post_transmit)(sc_card_t *card, const struct sm_ctx *ctx,
sc_apdu_t *sm_apdu);
};
/**
* @brief Secure messaging wrapper for sc_transmit_apdu()
*
* If secure messaging (SM) is activated in \a sctx and \a apdu is not already
* SM protected, \a apdu is processed with the following steps:
* \li call to \a sctx->pre_transmit
* \li encrypt \a apdu calling \a sctx->encrypt
* \li authenticate \a apdu calling \a sctx->authenticate
* \li transmit SM protected \a apdu
* \li verify SM protected \a apdu calling \a sctx->verify_authentication
* \li decrypt SM protected \a apdu calling \a sctx->decrypt
* \li copy decrypted/authenticated data and status bytes to \a apdu
*
* @param[in] sctx (optional)
* @param[in] card
* @param[in,out] apdu
*
* @return \c SC_SUCCESS or error code if an error occurred
*/
int sm_transmit_apdu(struct sm_ctx *sctx, sc_card_t *card,
sc_apdu_t *apdu);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,69 +0,0 @@
/*
* 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);
}
}

View File

@@ -1,267 +0,0 @@
/*
* 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)
return 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 the first reader with a card: %s", (*reader)->name);
break;
}
}
if (reader_id >= reader_count) {
sc_debug(*ctx, "No card found, using the first reader.");
reader_id = 0;
}
}
if (reader_id >= reader_count)
return SC_ERROR_NO_READERS_FOUND;
*reader = sc_ctx_get_reader(*ctx, reader_id);
return 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)
return SC_ERROR_INVALID_ARGUMENTS;
len0 = len;
if (len < 4) {
sc_error(ctx, "APDU too short (must be at least 4 bytes)");
return 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) {
apdu->cse = SC_APDU_CASE_1;
} else {
if (*p == 0 && len >= 3) {
/* ...must be an extended APDU */
p++;
if (len == 3) {
apdu->le = (*p++)<<8;
apdu->le += *p++;
if (apdu->le == 0)
apdu->le = 0xffff+1;
len -= 3;
apdu->cse = SC_APDU_CASE_2_EXT;
} else {
/* len > 3 */
apdu->lc = (*p++)<<8;
apdu->lc += *p++;
len -= 3;
if (len < apdu->lc) {
sc_error(ctx, "APDU too short (need %lu more bytes)\n",
(unsigned long) apdu->lc - len);
return SC_ERROR_INVALID_DATA;
}
apdu->data = p;
apdu->datalen = apdu->lc;
len -= apdu->lc;
p += apdu->lc;
if (!len) {
apdu->cse = SC_APDU_CASE_3_EXT;
} else {
/* at this point the apdu has a Lc, so Le is on 2 bytes */
if (len < 2) {
sc_error(ctx, "APDU too short (need 2 more bytes)\n");
return SC_ERROR_INVALID_DATA;
}
apdu->le = (*p++)<<8;
apdu->le += *p++;
if (apdu->le == 0)
apdu->le = 0xffff+1;
len -= 2;
apdu->cse = SC_APDU_CASE_4_EXT;
}
}
} else {
/* ...must be a short APDU */
if (len == 1) {
apdu->le = *p++;
if (apdu->le == 0)
apdu->le = 0xff+1;
len--;
apdu->cse = SC_APDU_CASE_2_SHORT;
} else {
apdu->lc = *p++;
len--;
if (len < apdu->lc) {
sc_error(ctx, "APDU too short (need %lu more bytes)\n",
(unsigned long) apdu->lc - len);
return SC_ERROR_INVALID_DATA;
}
apdu->data = p;
apdu->datalen = apdu->lc;
len -= apdu->lc;
p += apdu->lc;
if (!len) {
apdu->cse = SC_APDU_CASE_3_SHORT;
} else {
apdu->le = *p++;
if (apdu->le == 0)
apdu->le = 0xff+1;
len--;
apdu->cse = SC_APDU_CASE_4_SHORT;
}
}
}
if (len) {
sc_error(ctx, "APDU too long (%lu bytes extra)\n",
(unsigned long) len);
return SC_ERROR_INVALID_DATA;
}
}
apdu->flags = SC_APDU_FLAGS_NO_GET_RESP|SC_APDU_FLAGS_NO_RETRY_WL;
sc_debug(ctx, "Case %d %s APDU, %lu bytes:\tins=%02x p1=%02x p2=%02x lc=%04x le=%04x",
apdu->cse & SC_APDU_SHORT_MASK,
(apdu->cse & SC_APDU_EXT) != 0 ? "extended" : "short",
(unsigned long) len0, apdu->ins, apdu->p1, apdu->p2, apdu->lc, apdu->le);
return SC_SUCCESS;
}
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)
{
/* this is the maximum supported by sc_do_log_va */
/* Flawfinder: ignore */
char buf[1800];
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",
label, len, len==1?"":"s", buf);
}
}
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;
}
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;
}
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;
}

1
ccid/src/scutil.c Symbolic link
View File

@@ -0,0 +1 @@
../../npa/src/scutil.c

View File

@@ -1,622 +0,0 @@
/*
* 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 "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 }
};
static 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)
/* Flawfinder: ignore */
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;
}
static 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;
/* Flawfinder: ignore */
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;
}
}
static 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:
len = datalen;
while (len) {
len--;
if (data[len])
break;
}
if (data[len] != 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 {
/* Flawfinder: ignore */
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) {
sc_error(card->ctx, "Could not add padding to data: %s",
sc_strerror(r));
goto err;
}
pad_data_len = r;
if (card->ctx->debug > SC_LOG_TYPE_DEBUG)
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;
}
if (card->ctx->debug > SC_LOG_TYPE_DEBUG)
bin_log(card->ctx, "Cryptogram", *formatted_data, r);
r = prefix_buf(ctx->padding_indicator, *formatted_data, r, formatted_data);
if (r < 0) {
sc_error(card->ctx, "Could not prepend padding indicator to formatted "
"data: %s", sc_strerror(r));
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);
}
return 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 = 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;
}
if (card->ctx->debug > SC_LOG_TYPE_DEBUG)
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;
}
}
if (card->ctx->debug > SC_LOG_TYPE_DEBUG)
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;
}
if (card->ctx->debug > SC_LOG_TYPE_DEBUG)
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;
}
if (card->ctx->debug > SC_LOG_TYPE_DEBUG)
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;
}
if (card->ctx->debug > SC_LOG_TYPE_DEBUG)
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;
}
if (card->ctx->debug > SC_LOG_TYPE_DEBUG)
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;
}
if (card->ctx->debug > SC_LOG_TYPE_DEBUG)
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;
/* Flawfinder: ignore */
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;
}
if (card->ctx->debug > SC_LOG_TYPE_DEBUG)
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);
if (card->ctx->debug > SC_LOG_TYPE_DEBUG)
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;
}
/* Flawfinder: ignore */
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;
if (card->ctx->debug >= SC_LOG_TYPE_DEBUG)
bin_log(card->ctx, "ASN.1 encoded encrypted 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);
return 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 = NULL, *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 {
sc_error(card->ctx, "Cryptographic Checksum missing");
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) {
sc_error(card->ctx, "Could not remove padding");
goto err;
}
if (apdu->resplen < r) {
sc_error(card->ctx, "Response of SM APDU too long");
r = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
/* Flawfinder: ignore */
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 {
sc_error(card->ctx, "Authenticated status bytes are missing");
r = SC_ERROR_ASN1_OBJECT_NOT_FOUND;
goto err;
}
if (card->ctx->debug >= SC_LOG_TYPE_DEBUG) {
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);
}
r = SC_SUCCESS;
err:
if (asn1) {
free(asn1);
}
if (mac_data) {
free(mac_data);
}
return r;
}
int sm_transmit_apdu(struct sm_ctx *sctx, sc_card_t *card,
sc_apdu_t *apdu)
{
sc_apdu_t sm_apdu;
u8 rbuf[0xffff], sbuf[0xffff];
sm_apdu.data = sbuf;
sm_apdu.datalen = sizeof sbuf;
sm_apdu.resp = rbuf;
sm_apdu.resplen = sizeof rbuf;
if (!sctx || !sctx->active) {
sc_debug(card->ctx, "Secure messaging disabled.");
return sc_transmit_apdu(card, apdu);
}
if ((apdu->cla & 0x0C) == 0x0C) {
sc_debug(card->ctx, "Given APDU is already protected with some secure messaging. Deactivating own SM context.");
sctx->active = 0;
return sc_transmit_apdu(card, apdu);
}
if (sctx->pre_transmit)
SC_TEST_RET(card->ctx, sctx->pre_transmit(card, sctx, apdu),
"Could not complete SM specific pre transmit routine");
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");
if (sctx->post_transmit)
SC_TEST_RET(card->ctx, sctx->post_transmit(card, sctx, &sm_apdu),
"Could not complete SM specific post transmit routine");
SC_TEST_RET(card->ctx, sm_decrypt(sctx, card, &sm_apdu, apdu),
"Could not decrypt APDU");
return SC_SUCCESS;
}

1
ccid/src/sm.c Symbolic link
View File

@@ -0,0 +1 @@
../../npa/src/sm.c