diff --git a/npa/src/npa-tool.c b/npa/src/npa-tool.c index e00a854..fda6c1d 100644 --- a/npa/src/npa-tool.c +++ b/npa/src/npa-tool.c @@ -21,10 +21,10 @@ #endif #include "cmdline.h" -#include "config.h" #include #include #include +#include #include #include #include @@ -109,16 +109,39 @@ static void write_dg(sc_card_t *card, unsigned char sfid, const char *dg_str, r = sc_hex_to_bin(dg_hex, dg, &dg_len); if (r < 0) { - fprintf(stderr, "Coult not parse DG %02u %s (%s)\n", + fprintf(stderr, "Could 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", + fprintf(stderr, "Could not write DG %02u %s (%s)\n", sfid, dg_str, sc_strerror(r)); + else + printf("Wrote DG %02u %s\n", sfid, dg_str); } } +#define ISO_VERIFY 0x20 +static void verify(sc_card_t *card, const char *verify_str, + const unsigned char *data, size_t data_len) +{ + sc_apdu_t apdu; + int r; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, ISO_VERIFY, 0x80, 0); + apdu.cla = 0x80; + apdu.data = data; + apdu.datalen = data_len; + apdu.lc = data_len; + + r = sc_transmit_apdu(card, &apdu); + if (r < 0) + fprintf(stderr, "Coult not verify %s (%s)\n", + verify_str, sc_strerror(r)); + else + printf("Verified %s\n", verify_str); +} + int npa_translate_apdus(sc_card_t *card, FILE *input) { u8 buf[4 + 3 + 0xffff + 3]; @@ -184,6 +207,65 @@ int npa_translate_apdus(sc_card_t *card, FILE *input) return r; } +static int add_to_CVC_DISCRETIONARY_DATA_TEMPLATES( + CVC_DISCRETIONARY_DATA_TEMPLATES **templates, + int nid, const unsigned char *data, size_t data_len) +{ + int r; + CVC_DISCRETIONARY_DATA_TEMPLATE **template; + + if (!templates) { + r = SC_ERROR_INVALID_ARGUMENTS; + goto err; + } + + if (!*templates) { + *templates = CVC_DISCRETIONARY_DATA_TEMPLATES_new(); + if (!*templates) { + r = SC_ERROR_INTERNAL; + goto err; + } + } + + if (!(*templates)->template1) + template = &(*templates)->template1; + else if (!(*templates)->template2) + template = &(*templates)->template2; + else { + fprintf(stderr, + "Not enough space in auxiliary data for that many data templates"); + r = SC_ERROR_INVALID_ARGUMENTS; + goto err; + } + + *template = CVC_DISCRETIONARY_DATA_TEMPLATE_new(); + if (!*template) { + r = SC_ERROR_INTERNAL; + goto err; + } + + (*template)->type = OBJ_nid2obj(nid); + if (!(*template)->type) { + r = SC_ERROR_INTERNAL; + goto err; + } + + if (data && data_len) { + (*template)->discretionary_data1 = ASN1_OCTET_STRING_new(); + if (!(*template)->discretionary_data1 + || !M_ASN1_OCTET_STRING_set( + (*template)->discretionary_data1, data, data_len)) { + r = SC_ERROR_INTERNAL; + goto err; + } + } + + r = SC_SUCCESS; + +err: + return r; +} + int main (int argc, char **argv) { @@ -201,14 +283,18 @@ main (int argc, char **argv) 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; + unsigned char *verify_age_data = NULL; + size_t verify_age_len = 0; + unsigned char *verify_community_data = NULL; + size_t verify_community_len = 0; + unsigned char *verify_validity_data = NULL; + size_t verify_validity_len = 0; 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; + int r, tr_version = EAC_TR_VERSION_2_02; struct establish_pace_channel_input pace_input; struct establish_pace_channel_output pace_output; struct timeval tv; @@ -218,6 +304,7 @@ main (int argc, char **argv) unsigned char *certs_chat = NULL; unsigned char *dg = NULL; size_t dg_len = 0; + CVC_DISCRETIONARY_DATA_TEMPLATES *templates = NULL; struct gengetopt_args_info cmdline; @@ -494,6 +581,44 @@ main (int argc, char **argv) fprintf(stderr, "Could not parse auxiliary data.\n"); exit(2); } + } else { + if (cmdline.older_than_given) { + r = add_to_CVC_DISCRETIONARY_DATA_TEMPLATES(&templates, + NID_id_DateOfBirth, cmdline.older_than_arg, + strlen(cmdline.older_than_arg)); + if (r < 0) { + fprintf(stderr, "Error formatting age verification data template.\n"); + goto err; + } + } + if (cmdline.verify_validity_given) { + r = add_to_CVC_DISCRETIONARY_DATA_TEMPLATES(&templates, + NID_id_DateOfExpiry, NULL, 0); + if (r < 0) { + fprintf(stderr, "Error formatting validity verification data template.\n"); + goto err; + } + } + if (cmdline.verify_community_given) { + r = add_to_CVC_DISCRETIONARY_DATA_TEMPLATES(&templates, + NID_id_CommunityID, cmdline.verify_community_arg, + strlen(cmdline.verify_community_arg)); + if (r < 0) { + fprintf(stderr, "Error formatting community ID verification data template.\n"); + goto err; + } + } + unsigned char *p = NULL; + auxiliary_data_len = i2d_CVC_DISCRETIONARY_DATA_TEMPLATES( + templates, &p); + if (0 >= (int) auxiliary_data_len + || auxiliary_data_len > sizeof auxiliary_data) { + free(p); + r = SC_ERROR_INTERNAL; + goto err; + } + memcpy(auxiliary_data, p, auxiliary_data_len); + free(p); } } @@ -611,6 +736,19 @@ main (int argc, char **argv) if (cmdline.write_dg21_given) write_dg(card, 21, "Optional Data", cmdline.write_dg21_arg); + if (cmdline.older_than_given) { + unsigned char id_DateOfBirth[] = {6, 9, 4, 0, 127, 0, 7, 3, 1, 4, 1}; + verify(card, "age", id_DateOfBirth, sizeof id_DateOfBirth); + } + if (cmdline.verify_validity_given) { + unsigned char id_DateOfExpiry[] = {6, 9, 4, 0, 127, 0, 7, 3, 1, 4, 2}; + verify(card, "validity", id_DateOfExpiry, sizeof id_DateOfExpiry); + } + if (cmdline.verify_community_given) { + unsigned char id_CommunityID[] = {6, 9, 4, 0, 127, 0, 7, 3, 1, 4, 3}; + verify(card, "community ID", id_CommunityID, sizeof id_CommunityID); + } + if (cmdline.translate_given) { if (strncmp(cmdline.translate_arg, "stdin", strlen("stdin")) == 0) input = stdin; @@ -652,6 +790,8 @@ err: if (cvc_cert) CVC_CERT_free(cvc_cert); free(dg); + if (templates) + CVC_DISCRETIONARY_DATA_TEMPLATES_free(templates); sm_stop(card); sc_reset(card, 1); diff --git a/npa/src/npa-tool.ggo.in b/npa/src/npa-tool.ggo.in index 41861ef..c94945d 100644 --- a/npa/src/npa-tool.ggo.in +++ b/npa/src/npa-tool.ggo.in @@ -57,7 +57,7 @@ option "chat" - typestr="HEX_STRING" optional option "auxiliary-data" A - "Terminal's auxiliary data." + "Terminal's auxiliary data (default is determined by age verification and community id verification)." string typestr="HEX_STRING" optional @@ -169,6 +169,20 @@ option "write-dg21" - typestr="HEX_STRING" optional +section "nPA operations" +option "older-than" - + "Verify age with a reference date" + string + typestr="YYYYMMDD" + optional +option "verify-validity" - + "Verify document validity with the chips current date" + flag off +option "verify-community" - + "Verify community ID with a reference ID" + 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 dfb2692..6ee4da9 100644 --- a/npa/src/npa.c +++ b/npa/src/npa.c @@ -43,7 +43,6 @@ #define ASN1_APP_IMP_OPT(stname, field, type, tag) ASN1_EX_TYPE(ASN1_TFLG_IMPTAG|ASN1_TFLG_APPLICATION|ASN1_TFLG_OPTIONAL, tag, stname, field, type) #define ASN1_APP_IMP(stname, field, type, tag) ASN1_EX_TYPE(ASN1_TFLG_IMPTAG|ASN1_TFLG_APPLICATION, tag, stname, field, type) -IMPLEMENT_ASN1_FUNCTIONS(CVC_DISCRETIONARY_DATA_TEMPLATES) /* * MSE:Set AT @@ -321,7 +320,6 @@ int read_binary_rec(sc_card_t *card, unsigned char sfid, size_t read = maxresp - 8; sc_apdu_t apdu; u8 *p; - struct iso_sm_ctx *iso_sm_ctx = card->sm_ctx.info.cmd_data; if (!card || !ef || !ef_len) { r = SC_ERROR_INVALID_ARGUMENTS; @@ -514,6 +512,7 @@ static int format_mse_cdata(struct sc_context *ctx, int protocol, } if (auxiliary_data && auxiliary_data_len) { + bin_log(ctx, SC_LOG_DEBUG_NORMAL, "test", auxiliary_data, auxiliary_data_len); if (!d2i_CVC_DISCRETIONARY_DATA_TEMPLATES(&data->auxiliary_data, &auxiliary_data, auxiliary_data_len)) { sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "Error setting auxiliary authenticated data of MSE:Set AT data"); r = SC_ERROR_INTERNAL; @@ -2131,7 +2130,6 @@ npa_sm_verify_authentication(sc_card_t *card, const struct iso_sm_ctx *ctx, const u8 *macdata, size_t macdatalen) { int r; - char *p; BUF_MEM *inbuf = NULL, *my_mac = NULL; if (!card || !ctx || !ctx->priv_data) { diff --git a/npa/src/npa/npa.h b/npa/src/npa/npa.h index 671f422..a619219 100644 --- a/npa/src/npa/npa.h +++ b/npa/src/npa/npa.h @@ -155,7 +155,8 @@ int perform_pace(sc_card_t *card, * @param[in] privkey The terminal's private key * @param[in] privkey_len length of \a privkey * @param[in] auxiliary_data auxiliary data for age/validity/community ID - * verification + * verification. Should be formatted as buffer of + * \c CVC_DISCRETIONARY_DATA_TEMPLATES * @param[in] auxiliary_data_len length of \a auxiliary_data * * @return \c SC_SUCCESS or error code if an error occurred