added verification of age/validity/community
This commit is contained in:
@@ -21,10 +21,10 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "cmdline.h"
|
#include "cmdline.h"
|
||||||
#include "config.h"
|
|
||||||
#include <eac/pace.h>
|
#include <eac/pace.h>
|
||||||
#include <libopensc/log.h>
|
#include <libopensc/log.h>
|
||||||
#include <libopensc/opensc.h>
|
#include <libopensc/opensc.h>
|
||||||
|
#include <npa/iso-sm.h>
|
||||||
#include <npa/npa.h>
|
#include <npa/npa.h>
|
||||||
#include <npa/scutil.h>
|
#include <npa/scutil.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -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);
|
r = sc_hex_to_bin(dg_hex, dg, &dg_len);
|
||||||
if (r < 0) {
|
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));
|
sfid, dg_str, sc_strerror(r));
|
||||||
} else {
|
} else {
|
||||||
r = write_binary_rec(card, sfid, dg, dg_len);
|
r = write_binary_rec(card, sfid, dg, dg_len);
|
||||||
if (r < 0)
|
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));
|
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)
|
int npa_translate_apdus(sc_card_t *card, FILE *input)
|
||||||
{
|
{
|
||||||
u8 buf[4 + 3 + 0xffff + 3];
|
u8 buf[4 + 3 + 0xffff + 3];
|
||||||
@@ -184,6 +207,65 @@ int npa_translate_apdus(sc_card_t *card, FILE *input)
|
|||||||
return r;
|
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
|
int
|
||||||
main (int argc, char **argv)
|
main (int argc, char **argv)
|
||||||
{
|
{
|
||||||
@@ -201,14 +283,18 @@ main (int argc, char **argv)
|
|||||||
size_t privkey_len = 0;
|
size_t privkey_len = 0;
|
||||||
unsigned char auxiliary_data[0xff];
|
unsigned char auxiliary_data[0xff];
|
||||||
size_t auxiliary_data_len = 0;
|
size_t auxiliary_data_len = 0;
|
||||||
unsigned char new_dg[0xff];
|
unsigned char *verify_age_data = NULL;
|
||||||
size_t new_dg_len;
|
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_context_t *ctx = NULL;
|
||||||
sc_card_t *card = NULL;
|
sc_card_t *card = NULL;
|
||||||
sc_reader_t *reader;
|
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_input pace_input;
|
||||||
struct establish_pace_channel_output pace_output;
|
struct establish_pace_channel_output pace_output;
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
@@ -218,6 +304,7 @@ main (int argc, char **argv)
|
|||||||
unsigned char *certs_chat = NULL;
|
unsigned char *certs_chat = NULL;
|
||||||
unsigned char *dg = NULL;
|
unsigned char *dg = NULL;
|
||||||
size_t dg_len = 0;
|
size_t dg_len = 0;
|
||||||
|
CVC_DISCRETIONARY_DATA_TEMPLATES *templates = NULL;
|
||||||
|
|
||||||
struct gengetopt_args_info cmdline;
|
struct gengetopt_args_info cmdline;
|
||||||
|
|
||||||
@@ -494,6 +581,44 @@ main (int argc, char **argv)
|
|||||||
fprintf(stderr, "Could not parse auxiliary data.\n");
|
fprintf(stderr, "Could not parse auxiliary data.\n");
|
||||||
exit(2);
|
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)
|
if (cmdline.write_dg21_given)
|
||||||
write_dg(card, 21, "Optional Data", cmdline.write_dg21_arg);
|
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 (cmdline.translate_given) {
|
||||||
if (strncmp(cmdline.translate_arg, "stdin", strlen("stdin")) == 0)
|
if (strncmp(cmdline.translate_arg, "stdin", strlen("stdin")) == 0)
|
||||||
input = stdin;
|
input = stdin;
|
||||||
@@ -652,6 +790,8 @@ err:
|
|||||||
if (cvc_cert)
|
if (cvc_cert)
|
||||||
CVC_CERT_free(cvc_cert);
|
CVC_CERT_free(cvc_cert);
|
||||||
free(dg);
|
free(dg);
|
||||||
|
if (templates)
|
||||||
|
CVC_DISCRETIONARY_DATA_TEMPLATES_free(templates);
|
||||||
|
|
||||||
sm_stop(card);
|
sm_stop(card);
|
||||||
sc_reset(card, 1);
|
sc_reset(card, 1);
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ option "chat" -
|
|||||||
typestr="HEX_STRING"
|
typestr="HEX_STRING"
|
||||||
optional
|
optional
|
||||||
option "auxiliary-data" A
|
option "auxiliary-data" A
|
||||||
"Terminal's auxiliary data."
|
"Terminal's auxiliary data (default is determined by age verification and community id verification)."
|
||||||
string
|
string
|
||||||
typestr="HEX_STRING"
|
typestr="HEX_STRING"
|
||||||
optional
|
optional
|
||||||
@@ -169,6 +169,20 @@ option "write-dg21" -
|
|||||||
typestr="HEX_STRING"
|
typestr="HEX_STRING"
|
||||||
optional
|
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"
|
section "Special options, not always useful"
|
||||||
option "break" b
|
option "break" b
|
||||||
"Brute force PIN, CAN or PUK"
|
"Brute force PIN, CAN or PUK"
|
||||||
|
|||||||
@@ -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_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)
|
#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
|
* MSE:Set AT
|
||||||
@@ -321,7 +320,6 @@ int read_binary_rec(sc_card_t *card, unsigned char sfid,
|
|||||||
size_t read = maxresp - 8;
|
size_t read = maxresp - 8;
|
||||||
sc_apdu_t apdu;
|
sc_apdu_t apdu;
|
||||||
u8 *p;
|
u8 *p;
|
||||||
struct iso_sm_ctx *iso_sm_ctx = card->sm_ctx.info.cmd_data;
|
|
||||||
|
|
||||||
if (!card || !ef || !ef_len) {
|
if (!card || !ef || !ef_len) {
|
||||||
r = SC_ERROR_INVALID_ARGUMENTS;
|
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) {
|
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)) {
|
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");
|
sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "Error setting auxiliary authenticated data of MSE:Set AT data");
|
||||||
r = SC_ERROR_INTERNAL;
|
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)
|
const u8 *macdata, size_t macdatalen)
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
char *p;
|
|
||||||
BUF_MEM *inbuf = NULL, *my_mac = NULL;
|
BUF_MEM *inbuf = NULL, *my_mac = NULL;
|
||||||
|
|
||||||
if (!card || !ctx || !ctx->priv_data) {
|
if (!card || !ctx || !ctx->priv_data) {
|
||||||
|
|||||||
@@ -155,7 +155,8 @@ int perform_pace(sc_card_t *card,
|
|||||||
* @param[in] privkey The terminal's private key
|
* @param[in] privkey The terminal's private key
|
||||||
* @param[in] privkey_len length of \a privkey
|
* @param[in] privkey_len length of \a privkey
|
||||||
* @param[in] auxiliary_data auxiliary data for age/validity/community ID
|
* @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
|
* @param[in] auxiliary_data_len length of \a auxiliary_data
|
||||||
*
|
*
|
||||||
* @return \c SC_SUCCESS or error code if an error occurred
|
* @return \c SC_SUCCESS or error code if an error occurred
|
||||||
|
|||||||
Reference in New Issue
Block a user