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:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
1563
ccid/src/pace.c
1563
ccid/src/pace.c
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
1
ccid/src/scutil.c
Symbolic link
@@ -0,0 +1 @@
|
||||
../../npa/src/scutil.c
|
||||
622
ccid/src/sm.c
622
ccid/src/sm.c
@@ -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
1
ccid/src/sm.c
Symbolic link
@@ -0,0 +1 @@
|
||||
../../npa/src/sm.c
|
||||
Reference in New Issue
Block a user