From e01eae9ce31397976864565afff5d318f17dab2e Mon Sep 17 00:00:00 2001 From: Frank Morgner Date: Tue, 29 Jan 2013 15:09:09 +0100 Subject: [PATCH] added read and write of data groups adds read_binary_rec and write_binary_rec to scutil.h --- npa/src/npa-tool.c | 147 +++++++++++++++++++++++++++++++--------- npa/src/npa-tool.ggo.in | 90 ++++++++++++++++++++++++ npa/src/npa.c | 88 ++++++++++++++++++------ npa/src/npa/scutil.h | 26 +++++++ 4 files changed, 300 insertions(+), 51 deletions(-) diff --git a/npa/src/npa-tool.c b/npa/src/npa-tool.c index c421a09..932fb7d 100644 --- a/npa/src/npa-tool.c +++ b/npa/src/npa-tool.c @@ -51,24 +51,6 @@ static ssize_t getline(char **lineptr, size_t *n, FILE *stream) } #endif -static const char *newpin = NULL; -static const char *pin = NULL; -static const char *puk = NULL; -static const char *can = NULL; -static const char *mrz = NULL; -static u8 chat[0xff]; -static u8 desc[0xffff]; -size_t *certs_lens = NULL; -static const unsigned char **certs = NULL; -static unsigned char *privkey = NULL; -static size_t privkey_len = 0; -static u8 auxiliary_data[0xff]; -static size_t auxiliary_data_len = 0; - -static sc_context_t *ctx = NULL; -static sc_card_t *card = NULL; -static sc_reader_t *reader; - int fread_to_eof(const unsigned char *file, unsigned char **buf, size_t *buflen) { FILE *input; @@ -107,6 +89,36 @@ err: return r; } +static void read_dg(sc_card_t *card, unsigned char sfid, const char *dg_str, + unsigned char **dg, size_t *dg_len) +{ + int r = read_binary_rec(card, sfid, dg, dg_len); + if (r < 0) + fprintf(stderr, "Coult not read DG %02u %s (%s)\n", + sfid, dg_str, sc_strerror(r)); + else + bin_print(stdout, dg_str, *dg, *dg_len); +} + +static void write_dg(sc_card_t *card, unsigned char sfid, const char *dg_str, + const char *dg_hex) +{ + unsigned char dg[0xff]; + size_t dg_len = sizeof dg; + int r; + + r = sc_hex_to_bin(dg_hex, dg, &dg_len); + if (r < 0) { + fprintf(stderr, "Coult not parse DG %02u %s (%s)\n", + sfid, dg_str, sc_strerror(r)); + } else { + r = write_binary_rec(card, sfid, dg, dg_len); + if (r < 0) + printf("Coult not write DG %02u %s (%s)\n", + sfid, dg_str, sc_strerror(r)); + } +} + int npa_translate_apdus(sc_card_t *card, FILE *input) { u8 buf[4 + 3 + 0xffff + 3]; @@ -147,7 +159,7 @@ int npa_translate_apdus(sc_card_t *card, FILE *input) r = sc_bytes2apdu(card->ctx, buf, apdulen, &apdu); if (r < 0) { - bin_log(ctx, SC_LOG_DEBUG_NORMAL, "Invalid C-APDU", buf, apdulen); + bin_log(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid C-APDU", buf, apdulen); continue; } @@ -175,8 +187,28 @@ int npa_translate_apdus(sc_card_t *card, FILE *input) int main (int argc, char **argv) { + const char *newpin = NULL; + const char *pin = NULL; + const char *puk = NULL; + const char *can = NULL; + const char *mrz = NULL; + + unsigned char chat[0xff]; + unsigned char desc[0xffff]; + unsigned char **certs = NULL; + size_t *certs_lens = NULL; + unsigned char *privkey = NULL; + size_t privkey_len = 0; + unsigned char auxiliary_data[0xff]; + size_t auxiliary_data_len = 0; + unsigned char new_dg[0xff]; + size_t new_dg_len; + + sc_context_t *ctx = NULL; + sc_card_t *card = NULL; + sc_reader_t *reader; + int r, oindex = 0, tr_version = EAC_TR_VERSION_2_02; - size_t channeldatalen; struct establish_pace_channel_input pace_input; struct establish_pace_channel_output pace_output; struct timeval tv; @@ -184,6 +216,8 @@ main (int argc, char **argv) FILE *input = NULL; CVC_CERT *cvc_cert = NULL; unsigned char *certs_chat = NULL; + unsigned char *dg = NULL; + size_t dg_len = 0; struct gengetopt_args_info cmdline; @@ -502,7 +536,8 @@ main (int argc, char **argv) npa_secret_name(pace_input.pin_id)); if (cmdline.cv_certificate_given || cmdline.private_key_given) { - r = perform_terminal_authentication(card, certs, certs_lens, + r = perform_terminal_authentication(card, + (const unsigned char **) certs, certs_lens, privkey, privkey_len, auxiliary_data, auxiliary_data_len); if (r < 0) goto err; @@ -514,6 +549,60 @@ main (int argc, char **argv) printf("Performed Chip Authentication.\n"); } + if (cmdline.read_dg1_flag) + read_dg(card, 1, "Document Type", &dg, &dg_len); + if (cmdline.read_dg2_flag) + read_dg(card, 2, "Issuing State", &dg, &dg_len); + if (cmdline.read_dg3_flag) + read_dg(card, 3, "Date of Expiry", &dg, &dg_len); + if (cmdline.read_dg4_flag) + read_dg(card, 4, "Given Names", &dg, &dg_len); + if (cmdline.read_dg5_flag) + read_dg(card, 5, "Family Names", &dg, &dg_len); + if (cmdline.read_dg6_flag) + read_dg(card, 6, "Religious/Artistic Name", &dg, &dg_len); + if (cmdline.read_dg7_flag) + read_dg(card, 7, "Academic Title", &dg, &dg_len); + if (cmdline.read_dg8_flag) + read_dg(card, 8, "Date of Birth", &dg, &dg_len); + if (cmdline.read_dg9_flag) + read_dg(card, 9, "Place of Birth", &dg, &dg_len); + if (cmdline.read_dg10_flag) + read_dg(card, 10, "Nationality", &dg, &dg_len); + if (cmdline.read_dg11_flag) + read_dg(card, 11, "Sex", &dg, &dg_len); + if (cmdline.read_dg12_flag) + read_dg(card, 12, "Optional Data", &dg, &dg_len); + if (cmdline.read_dg13_flag) + read_dg(card, 13, "", &dg, &dg_len); + if (cmdline.read_dg14_flag) + read_dg(card, 14, "", &dg, &dg_len); + if (cmdline.read_dg15_flag) + read_dg(card, 15, "", &dg, &dg_len); + if (cmdline.read_dg16_flag) + read_dg(card, 16, "", &dg, &dg_len); + if (cmdline.read_dg17_flag) + read_dg(card, 17, "Normal Place of Residence", &dg, &dg_len); + if (cmdline.read_dg18_flag) + read_dg(card, 18, "Community ID", &dg, &dg_len); + if (cmdline.read_dg19_flag) + read_dg(card, 19, "Residence Permit I", &dg, &dg_len); + if (cmdline.read_dg20_flag) + read_dg(card, 20, "Residence Permit II", &dg, &dg_len); + if (cmdline.read_dg21_flag) + read_dg(card, 21, "Optional Data", &dg, &dg_len); + + if (cmdline.write_dg17_given) + write_dg(card, 17, "Normal Place of Residence", cmdline.write_dg17_arg); + if (cmdline.write_dg18_given) + write_dg(card, 18, "Community ID", cmdline.write_dg18_arg); + if (cmdline.write_dg19_given) + write_dg(card, 19, "Residence Permit I", cmdline.write_dg19_arg); + if (cmdline.write_dg20_given) + write_dg(card, 20, "Residence Permit II", cmdline.write_dg20_arg); + if (cmdline.write_dg21_given) + write_dg(card, 21, "Optional Data", cmdline.write_dg21_arg); + if (cmdline.translate_given) { if (strncmp(cmdline.translate_arg, "stdin", strlen("stdin")) == 0) input = stdin; @@ -535,16 +624,11 @@ main (int argc, char **argv) err: cmdline_parser_free(&cmdline); - 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); + free(pace_output.ef_cardaccess); + free(pace_output.recent_car); + free(pace_output.previous_car); + free(pace_output.id_icc); + free(pace_output.id_pcd); if (input) fclose(input); if (certs) { @@ -559,6 +643,7 @@ err: free(certs_chat); if (cvc_cert) CVC_CERT_free(cvc_cert); + free(dg); sm_stop(card); sc_reset(card, 1); diff --git a/npa/src/npa-tool.ggo.in b/npa/src/npa-tool.ggo.in index 4e63e0a..41861ef 100644 --- a/npa/src/npa-tool.ggo.in +++ b/npa/src/npa-tool.ggo.in @@ -79,6 +79,96 @@ option "unblock" U "Unblock PIN (uses PUK to activate three more retries)" flag off +section "Read and Write Data Groups" +option "read-dg1" - + "Read DG 1 (Document Type)" + flag off +option "read-dg2" - + "Read DG 2 (Issuing State)" + flag off +option "read-dg3" - + "Read DG 3 (Date of Expiry)" + flag off +option "read-dg4" - + "Read DG 4 (Given Names)" + flag off +option "read-dg5" - + "Read DG 5 (Family Names)" + flag off +option "read-dg6" - + "Read DG 6 (Religious/Artistic Name)" + flag off +option "read-dg7" - + "Read DG 7 (Academic Title)" + flag off +option "read-dg8" - + "Read DG 8 (Date of Birth)" + flag off +option "read-dg9" - + "Read DG 9 (Place of Birth)" + flag off +option "read-dg10" - + "Read DG 10 (Nationality)" + flag off +option "read-dg11" - + "Read DG 11 (Sex)" + flag off +option "read-dg12" - + "Read DG 12 (Optional Data)" + flag off +option "read-dg13" - + "Read DG 13" + flag off +option "read-dg14" - + "Read DG 14" + flag off +option "read-dg15" - + "Read DG 15" + flag off +option "read-dg16" - + "Read DG 16" + flag off +option "read-dg17" - + "Read DG 17 (Normal Place of Residence)" + flag off +option "read-dg18" - + "Read DG 18 (Community ID)" + flag off +option "read-dg19" - + "Read DG 19 (Residence Permit I)" + flag off +option "read-dg20" - + "Read DG 20 (Residence Permit II)" + flag off +option "read-dg21" - + "Read DG 21 (Optional Data)" + flag off +option "write-dg17" - + "Write DG 17 (Normal Place of Residence)" + string + typestr="HEX_STRING" + optional +option "write-dg18" - + "Write DG 18 (Community ID)" + string + typestr="HEX_STRING" + optional +option "write-dg19" - + "Write DG 19 (Residence Permit I)" + string + typestr="HEX_STRING" + optional +option "write-dg20" - + "Write DG 20 (Residence Permit II)" + string + typestr="HEX_STRING" + optional +option "write-dg21" - + "Write DG 21 (Optional Data)" + string + typestr="HEX_STRING" + optional + section "Special options, not always useful" option "break" b "Brute force PIN, CAN or PUK" diff --git a/npa/src/npa.c b/npa/src/npa.c index 4ce566a..8fdc568 100644 --- a/npa/src/npa.c +++ b/npa/src/npa.c @@ -312,19 +312,14 @@ int get_pace_capabilities(u8 *bitmap) #define ISO_READ_BINARY 0xB0 #define ISO_P1_FLAG_SFID 0x80 -/** Read an EF. - * @note MF must be selected before calling this function. - * */ -static int get_ef(sc_card_t *card, unsigned char sfid, +int read_binary_rec(sc_card_t *card, unsigned char sfid, u8 **ef, size_t *ef_len) { int r; /* we read less bytes than possible. this is a workaround for acr 122, * which only supports apdus of max 250 bytes */ size_t read = maxresp - 8; - u8 p2; sc_apdu_t apdu; - sc_file_t *file = NULL; u8 *p; struct iso_sm_ctx *iso_sm_ctx = card->sm_ctx.info.cmd_data; @@ -386,27 +381,80 @@ static int get_ef(sc_card_t *card, unsigned char sfid, *ef + *ef_len, read, 0); } - /* test cards only return an empty FCI template, - * so we can't determine any file proberties */ - if (file && *ef_len < file->size) { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Actual filesize differs from the size in file " - "proberties (%u!=%u).", *ef_len, file->size); - r = SC_ERROR_FILE_TOO_SMALL; - goto err; - } - r = SC_SUCCESS; err: - free(file); - return r; } -int get_ef_card_access(sc_card_t *card, +#define ISO_WRITE_BINARY 0xD0 +int write_binary_rec(sc_card_t *card, unsigned char sfid, + u8 *ef, size_t ef_len) +{ + int r; + /* we write less bytes than possible. this is a workaround for acr 122, + * which only supports apdus of max 250 bytes */ + size_t write = maxresp - 8; + sc_apdu_t apdu; + sc_file_t *file = NULL; + u8 *p; + struct iso_sm_ctx *iso_sm_ctx = card->sm_ctx.info.cmd_data; + + if (!card) { + r = SC_ERROR_INVALID_ARGUMENTS; + goto err; + } + + if (write > SC_MAX_APDU_BUFFER_SIZE-2 + || (card->sm_ctx.sm_mode == SM_MODE_TRANSMIT + && write > (((SC_MAX_APDU_BUFFER_SIZE-2 + /* for encrypted APDUs we usually get authenticated status + * bytes (4B), a MAC (11B) and a cryptogram with padding + * indicator (3B without data). The cryptogram is always + * padded to the block size. */ + -18) / iso_sm_ctx->block_length) + * iso_sm_ctx->block_length - 1))) + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_EXT, + ISO_WRITE_BINARY, ISO_P1_FLAG_SFID|sfid, 0); + else + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, + ISO_WRITE_BINARY, ISO_P1_FLAG_SFID|sfid, 0); + + if (write > ef_len) { + apdu.datalen = ef_len; + apdu.lc = ef_len; + } else { + apdu.datalen = write; + apdu.lc = write; + } + + + r = sc_transmit_apdu(card, &apdu); + /* emulate the behaviour of sc_write_binary */ + if (r >= 0) + r = apdu.resplen; + + while (1) { + if (r < 0 || r > ef_len) { + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not write EF."); + goto err; + } + ef_len -= r; + ef += r; + if (!ef_len) + break; + + r = sc_write_binary(card, ef_len, ef, write, 0); + } + +err: + return r; +} + +static int get_ef_card_access(sc_card_t *card, u8 **ef_cardaccess, size_t *length_ef_cardaccess) { - return get_ef(card, SFID_EF_CARDACCESS, ef_cardaccess, length_ef_cardaccess); + return read_binary_rec(card, SFID_EF_CARDACCESS, ef_cardaccess, length_ef_cardaccess); } static int format_mse_cdata(struct sc_context *ctx, int protocol, @@ -1821,7 +1869,7 @@ err: int get_ef_card_security(sc_card_t *card, u8 **ef_security, size_t *length_ef_security) { - return get_ef(card, SFID_EF_CARDSECURITY, ef_security, length_ef_security); + return read_binary_rec(card, SFID_EF_CARDSECURITY, ef_security, length_ef_security); } int perform_chip_authentication(sc_card_t *card) diff --git a/npa/src/npa/scutil.h b/npa/src/npa/scutil.h index c35bf83..3f5d592 100644 --- a/npa/src/npa/scutil.h +++ b/npa/src/npa/scutil.h @@ -89,5 +89,31 @@ void _bin_log(sc_context_t *ctx, int type, const char *file, int line, */ int print_avail(int verbose); +/** Recursively read an EF by short file identifier. + * + * @param[in] card + * @param[in] sfid Short file identifier + * @param[in,out] ef Where to safe the file. the buffer will be allocated + * using \c realloc() and should be set to NULL, if + * empty. + * @param[in,out] ef_len Length of \a *ef + * + * @note The appropriate directory must be selected before calling this function. + * */ +int read_binary_rec(sc_card_t *card, unsigned char sfid, + u8 **ef, size_t *ef_len); + +/** Recursively write an EF by short file identifier. + * + * @param[in] card + * @param[in] sfid Short file identifier + * @param[in] ef Date to write + * @param[in] ef_len Length of \a ef + * + * @note The appropriate directory must be selected before calling this function. + * */ +int write_binary_rec(sc_card_t *card, unsigned char sfid, + u8 *ef, size_t ef_len); + #endif /* @} */