- added information how many bytes were actually transmitted via usb

- split general authenticate into multiple functions, one for every step of
  PACE.
- including recent and previous car in output buffer of EstablishPACEChannel
- ccid-emulator initiates EstablishPACEChannel always with a new sm_ctx


git-svn-id: https://vsmartcard.svn.sourceforge.net/svnroot/vsmartcard@192 96b47cad-a561-4643-ad3b-153ac7d7599c
This commit is contained in:
frankmorgner
2010-07-04 11:35:17 +00:00
parent 997fb84d9e
commit b84e3fbdba
3 changed files with 424 additions and 134 deletions

View File

@@ -382,8 +382,8 @@ err:
return r;
}
static int pace_gen_auth(const struct sm_ctx *oldpacectx, sc_card_t *card,
int step, const u8 *in, size_t in_len, u8 **out, size_t *out_len)
static int pace_gen_auth_1_encrypted_nonce(const struct sm_ctx *oldpacectx,
sc_card_t *card, u8 **enc_nonce, size_t *enc_nonce_len)
{
sc_apdu_t apdu;
PACE_GEN_AUTH_C *c_data = NULL;
@@ -402,41 +402,6 @@ static int pace_gen_auth(const struct sm_ctx *oldpacectx, sc_card_t *card,
r = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
switch (step) {
case 1:
break;
case 2:
c_data->mapping_data = ASN1_OCTET_STRING_new();
if (!c_data->mapping_data
|| !M_ASN1_OCTET_STRING_set(
c_data->mapping_data, in, in_len)) {
r = SC_ERROR_INTERNAL;
goto err;
}
break;
case 3:
c_data->eph_pub_key = ASN1_OCTET_STRING_new();
if (!c_data->eph_pub_key
|| !M_ASN1_OCTET_STRING_set(
c_data->eph_pub_key, in, in_len)) {
r = SC_ERROR_INTERNAL;
goto err;
}
break;
case 4:
apdu.cla = 0;
c_data->auth_token = ASN1_OCTET_STRING_new();
if (!c_data->auth_token
|| !M_ASN1_OCTET_STRING_set(
c_data->auth_token, in, in_len)) {
r = SC_ERROR_INTERNAL;
goto err;
}
break;
default:
r = SC_ERROR_INVALID_ARGUMENTS;
goto err;
}
r = i2d_PACE_GEN_AUTH_C(c_data, &d);
if (r < 0) {
r = SC_ERROR_INTERNAL;
@@ -447,7 +412,7 @@ static int pace_gen_auth(const struct sm_ctx *oldpacectx, sc_card_t *card,
apdu.lc = r;
if (card->ctx->debug > SC_LOG_TYPE_DEBUG)
bin_log(card->ctx, "General authenticate command data", apdu.data, apdu.datalen);
bin_log(card->ctx, "General authenticate (Encrypted Nonce) command data", apdu.data, apdu.datalen);
apdu.resplen = maxresp;
apdu.resp = malloc(apdu.resplen);
@@ -463,7 +428,7 @@ static int pace_gen_auth(const struct sm_ctx *oldpacectx, sc_card_t *card,
goto err;
if (card->ctx->debug > SC_LOG_TYPE_DEBUG)
bin_log(card->ctx, "General authenticate response data", apdu.resp, apdu.resplen);
bin_log(card->ctx, "General authenticate (Encrypted Nonce) response data", apdu.resp, apdu.resplen);
if (!d2i_PACE_GEN_AUTH_R(&r_data,
(const unsigned char **) &apdu.resp, apdu.resplen)) {
@@ -472,88 +437,395 @@ static int pace_gen_auth(const struct sm_ctx *oldpacectx, sc_card_t *card,
goto err;
}
switch (step) {
case 1:
if (!r_data->enc_nonce
|| r_data->mapping_data
|| r_data->eph_pub_key
|| r_data->auth_token
|| r_data->cur_car
|| r_data->prev_car) {
sc_error(card->ctx, "Response data of general authenticate for "
"step %d should (only) contain the "
"encrypted nonce.", step);
r = SC_ERROR_UNKNOWN_DATA_RECEIVED;
goto err;
}
p = r_data->enc_nonce->data;
l = r_data->enc_nonce->length;
break;
case 2:
if (r_data->enc_nonce
|| !r_data->mapping_data
|| r_data->eph_pub_key
|| r_data->auth_token
|| r_data->cur_car
|| r_data->prev_car) {
sc_error(card->ctx, "Response data of general authenticate for "
"step %d should (only) contain the "
"mapping data.", step);
r = SC_ERROR_UNKNOWN_DATA_RECEIVED;
goto err;
}
p = r_data->mapping_data->data;
l = r_data->mapping_data->length;
break;
case 3:
if (r_data->enc_nonce
|| r_data->mapping_data
|| !r_data->eph_pub_key
|| r_data->auth_token
|| r_data->cur_car
|| r_data->prev_car) {
sc_error(card->ctx, "Response data of general authenticate for "
"step %d should (only) contain the "
"ephemeral public key.", step);
r = SC_ERROR_UNKNOWN_DATA_RECEIVED;
goto err;
}
p = r_data->eph_pub_key->data;
l = r_data->eph_pub_key->length;
break;
case 4:
if (r_data->enc_nonce
|| r_data->mapping_data
|| r_data->eph_pub_key
|| !r_data->auth_token) {
sc_error(card->ctx, "Response data of general authenticate for "
"step %d should (only) contain the "
"authentication token.", step);
r = SC_ERROR_UNKNOWN_DATA_RECEIVED;
goto err;
}
p = r_data->auth_token->data;
l = r_data->auth_token->length;
/* XXX CAR sould be returned as result in some way */
if (r_data->cur_car)
bin_log(card->ctx, "Most recent Certificate Authority Reference",
r_data->cur_car->data, r_data->cur_car->length);
if (r_data->prev_car)
bin_log(card->ctx, "Previous Certificate Authority Reference",
r_data->prev_car->data, r_data->prev_car->length);
break;
default:
r = SC_ERROR_INVALID_ARGUMENTS;
goto err;
if (!r_data->enc_nonce
|| r_data->mapping_data
|| r_data->eph_pub_key
|| r_data->auth_token
|| r_data->cur_car
|| r_data->prev_car) {
sc_error(card->ctx, "Response data of general authenticate for "
"step 1 should (only) contain the encrypted nonce.");
r = SC_ERROR_UNKNOWN_DATA_RECEIVED;
goto err;
}
p = r_data->enc_nonce->data;
l = r_data->enc_nonce->length;
*out = malloc(l);
if (!*out) {
*enc_nonce = malloc(l);
if (!*enc_nonce) {
r = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
memcpy(*out, p, l);
*out_len = l;
memcpy(*enc_nonce, p, l);
*enc_nonce_len = l;
err:
if (c_data) {
/* XXX
if (c_data->mapping_data)
ASN1_OCTET_STRING_free(c_data->mapping_data);
if (c_data->eph_pub_key)
ASN1_OCTET_STRING_free(c_data->eph_pub_key);
if (c_data->auth_token)
ASN1_OCTET_STRING_free(c_data->auth_token);*/
PACE_GEN_AUTH_C_free(c_data);
}
if (d)
free(d);
if (r_data) {
/* XXX
if (r_data->mapping_data)
ASN1_OCTET_STRING_free(r_data->mapping_data);
if (r_data->eph_pub_key)
ASN1_OCTET_STRING_free(r_data->eph_pub_key);
if (r_data->auth_token)
ASN1_OCTET_STRING_free(r_data->auth_token);*/
PACE_GEN_AUTH_R_free(r_data);
}
/* XXX */
/*if (apdu.resp)*/
/*free(apdu.resp);*/
return r;
}
static int pace_gen_auth_2_map_nonce(const struct sm_ctx *oldpacectx,
sc_card_t *card, const u8 *in, size_t in_len, u8 **map_data_out,
size_t *map_data_out_len)
{
sc_apdu_t apdu;
PACE_GEN_AUTH_C *c_data = NULL;
PACE_GEN_AUTH_R *r_data = NULL;
unsigned char *d = NULL, *p;
int r, l;
memset(&apdu, 0, sizeof apdu);
apdu.cla = 0x10;
apdu.ins = 0x86;
apdu.cse = SC_APDU_CASE_4_SHORT;
apdu.flags = SC_APDU_FLAGS_NO_GET_RESP|SC_APDU_FLAGS_NO_RETRY_WL;
c_data = PACE_GEN_AUTH_C_new();
if (!c_data) {
r = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
c_data->mapping_data = ASN1_OCTET_STRING_new();
if (!c_data->mapping_data
|| !M_ASN1_OCTET_STRING_set(
c_data->mapping_data, in, in_len)) {
r = SC_ERROR_INTERNAL;
goto err;
}
r = i2d_PACE_GEN_AUTH_C(c_data, &d);
if (r < 0) {
r = SC_ERROR_INTERNAL;
goto err;
}
apdu.data = (const u8 *) d;
apdu.datalen = r;
apdu.lc = r;
if (card->ctx->debug > SC_LOG_TYPE_DEBUG)
bin_log(card->ctx, "General authenticate (Map Nonce) command data", apdu.data, apdu.datalen);
apdu.resplen = maxresp;
apdu.resp = malloc(apdu.resplen);
if (oldpacectx)
r = sm_transmit_apdu(oldpacectx, card, &apdu);
else
r = sc_transmit_apdu(card, &apdu);
if (r < 0)
goto err;
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
if (r < 0)
goto err;
if (card->ctx->debug > SC_LOG_TYPE_DEBUG)
bin_log(card->ctx, "General authenticate (Map Nonce) response data", apdu.resp, apdu.resplen);
if (!d2i_PACE_GEN_AUTH_R(&r_data,
(const unsigned char **) &apdu.resp, apdu.resplen)) {
sc_error(card->ctx, "Could not parse general authenticate response data.");
r = SC_ERROR_INTERNAL;
goto err;
}
if (r_data->enc_nonce
|| !r_data->mapping_data
|| r_data->eph_pub_key
|| r_data->auth_token
|| r_data->cur_car
|| r_data->prev_car) {
sc_error(card->ctx, "Response data of general authenticate for "
"step 2 should (only) contain the mapping data.");
r = SC_ERROR_UNKNOWN_DATA_RECEIVED;
goto err;
}
p = r_data->mapping_data->data;
l = r_data->mapping_data->length;
*map_data_out = malloc(l);
if (!*map_data_out) {
r = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
memcpy(*map_data_out, p, l);
*map_data_out_len = l;
err:
if (c_data) {
/* XXX
if (c_data->mapping_data)
ASN1_OCTET_STRING_free(c_data->mapping_data);
if (c_data->eph_pub_key)
ASN1_OCTET_STRING_free(c_data->eph_pub_key);
if (c_data->auth_token)
ASN1_OCTET_STRING_free(c_data->auth_token);*/
PACE_GEN_AUTH_C_free(c_data);
}
if (d)
free(d);
if (r_data) {
/* XXX
if (r_data->mapping_data)
ASN1_OCTET_STRING_free(r_data->mapping_data);
if (r_data->eph_pub_key)
ASN1_OCTET_STRING_free(r_data->eph_pub_key);
if (r_data->auth_token)
ASN1_OCTET_STRING_free(r_data->auth_token);*/
PACE_GEN_AUTH_R_free(r_data);
}
/* XXX */
/*if (apdu.resp)*/
/*free(apdu.resp);*/
return r;
}
static int pace_gen_auth_3_perform_key_agreement(
const struct sm_ctx *oldpacectx, sc_card_t *card,
const u8 *in, size_t in_len, u8 **eph_pub_key_out, size_t *eph_pub_key_out_len)
{
sc_apdu_t apdu;
PACE_GEN_AUTH_C *c_data = NULL;
PACE_GEN_AUTH_R *r_data = NULL;
unsigned char *d = NULL, *p;
int r, l;
memset(&apdu, 0, sizeof apdu);
apdu.cla = 0x10;
apdu.ins = 0x86;
apdu.cse = SC_APDU_CASE_4_SHORT;
apdu.flags = SC_APDU_FLAGS_NO_GET_RESP|SC_APDU_FLAGS_NO_RETRY_WL;
c_data = PACE_GEN_AUTH_C_new();
if (!c_data) {
r = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
c_data->eph_pub_key = ASN1_OCTET_STRING_new();
if (!c_data->eph_pub_key
|| !M_ASN1_OCTET_STRING_set(
c_data->eph_pub_key, in, in_len)) {
r = SC_ERROR_INTERNAL;
goto err;
}
r = i2d_PACE_GEN_AUTH_C(c_data, &d);
if (r < 0) {
r = SC_ERROR_INTERNAL;
goto err;
}
apdu.data = (const u8 *) d;
apdu.datalen = r;
apdu.lc = r;
if (card->ctx->debug > SC_LOG_TYPE_DEBUG)
bin_log(card->ctx, "General authenticate (Perform Key Agreement) command data", apdu.data, apdu.datalen);
apdu.resplen = maxresp;
apdu.resp = malloc(apdu.resplen);
if (oldpacectx)
r = sm_transmit_apdu(oldpacectx, card, &apdu);
else
r = sc_transmit_apdu(card, &apdu);
if (r < 0)
goto err;
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
if (r < 0)
goto err;
if (card->ctx->debug > SC_LOG_TYPE_DEBUG)
bin_log(card->ctx, "General authenticate (Perform Key Agreement) response data", apdu.resp, apdu.resplen);
if (!d2i_PACE_GEN_AUTH_R(&r_data,
(const unsigned char **) &apdu.resp, apdu.resplen)) {
sc_error(card->ctx, "Could not parse general authenticate response data.");
r = SC_ERROR_INTERNAL;
goto err;
}
if (r_data->enc_nonce
|| r_data->mapping_data
|| !r_data->eph_pub_key
|| r_data->auth_token
|| r_data->cur_car
|| r_data->prev_car) {
sc_error(card->ctx, "Response data of general authenticate for "
"step 3 should (only) contain the ephemeral public key.");
r = SC_ERROR_UNKNOWN_DATA_RECEIVED;
goto err;
}
p = r_data->eph_pub_key->data;
l = r_data->eph_pub_key->length;
*eph_pub_key_out = malloc(l);
if (!*eph_pub_key_out) {
r = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
memcpy(*eph_pub_key_out, p, l);
*eph_pub_key_out_len = l;
err:
if (c_data) {
/* XXX
if (c_data->mapping_data)
ASN1_OCTET_STRING_free(c_data->mapping_data);
if (c_data->eph_pub_key)
ASN1_OCTET_STRING_free(c_data->eph_pub_key);
if (c_data->auth_token)
ASN1_OCTET_STRING_free(c_data->auth_token);*/
PACE_GEN_AUTH_C_free(c_data);
}
if (d)
free(d);
if (r_data) {
/* XXX
if (r_data->mapping_data)
ASN1_OCTET_STRING_free(r_data->mapping_data);
if (r_data->eph_pub_key)
ASN1_OCTET_STRING_free(r_data->eph_pub_key);
if (r_data->auth_token)
ASN1_OCTET_STRING_free(r_data->auth_token);*/
PACE_GEN_AUTH_R_free(r_data);
}
/* XXX */
/*if (apdu.resp)*/
/*free(apdu.resp);*/
return r;
}
static int pace_gen_auth_4_mutual_authentication(
const struct sm_ctx *oldpacectx, sc_card_t *card,
const u8 *in, size_t in_len, u8 **auth_token_out,
size_t *auth_token_out_len, u8 **recent_car, size_t *recent_car_len,
u8 **prev_car, size_t *prev_car_len)
{
sc_apdu_t apdu;
PACE_GEN_AUTH_C *c_data = NULL;
PACE_GEN_AUTH_R *r_data = NULL;
unsigned char *d = NULL, *p;
int r, l;
memset(&apdu, 0, sizeof apdu);
apdu.cla = 0x10;
apdu.ins = 0x86;
apdu.cse = SC_APDU_CASE_4_SHORT;
apdu.flags = SC_APDU_FLAGS_NO_GET_RESP|SC_APDU_FLAGS_NO_RETRY_WL;
c_data = PACE_GEN_AUTH_C_new();
if (!c_data) {
r = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
apdu.cla = 0;
c_data->auth_token = ASN1_OCTET_STRING_new();
if (!c_data->auth_token
|| !M_ASN1_OCTET_STRING_set(
c_data->auth_token, in, in_len)) {
r = SC_ERROR_INTERNAL;
goto err;
}
r = i2d_PACE_GEN_AUTH_C(c_data, &d);
if (r < 0) {
r = SC_ERROR_INTERNAL;
goto err;
}
apdu.data = (const u8 *) d;
apdu.datalen = r;
apdu.lc = r;
if (card->ctx->debug > SC_LOG_TYPE_DEBUG)
bin_log(card->ctx, "General authenticate (Perform Key Agreement) command data", apdu.data, apdu.datalen);
apdu.resplen = maxresp;
apdu.resp = malloc(apdu.resplen);
if (oldpacectx)
r = sm_transmit_apdu(oldpacectx, card, &apdu);
else
r = sc_transmit_apdu(card, &apdu);
if (r < 0)
goto err;
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
if (r < 0)
goto err;
if (card->ctx->debug > SC_LOG_TYPE_DEBUG)
bin_log(card->ctx, "General authenticate (Perform Key Agreement) response data", apdu.resp, apdu.resplen);
if (!d2i_PACE_GEN_AUTH_R(&r_data,
(const unsigned char **) &apdu.resp, apdu.resplen)) {
sc_error(card->ctx, "Could not parse general authenticate response data.");
r = SC_ERROR_INTERNAL;
goto err;
}
if (r_data->enc_nonce
|| r_data->mapping_data
|| r_data->eph_pub_key
|| !r_data->auth_token) {
sc_error(card->ctx, "Response data of general authenticate for "
"step 4 should (only) contain the authentication token.");
r = SC_ERROR_UNKNOWN_DATA_RECEIVED;
goto err;
}
p = r_data->auth_token->data;
l = r_data->auth_token->length;
/* XXX CAR sould be returned as result in some way */
if (r_data->cur_car) {
bin_log(card->ctx, "Most recent Certificate Authority Reference",
r_data->cur_car->data, r_data->cur_car->length);
*recent_car = malloc(r_data->cur_car->length);
if (!*recent_car) {
r = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
memcpy(*recent_car, r_data->cur_car->data, r_data->cur_car->length);
*recent_car_len = r_data->cur_car->length;
} else
*recent_car_len = 0;
if (r_data->prev_car) {
bin_log(card->ctx, "Previous Certificate Authority Reference",
r_data->prev_car->data, r_data->prev_car->length);
*prev_car = malloc(r_data->prev_car->length);
if (!*prev_car) {
r = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
memcpy(*prev_car, r_data->prev_car->data, r_data->prev_car->length);
*prev_car_len = r_data->prev_car->length;
} else
*prev_car_len = 0;
*auth_token_out = malloc(l);
if (!*auth_token_out) {
r = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
memcpy(*auth_token_out, p, l);
*auth_token_out_len = l;
err:
if (c_data) {
@@ -689,9 +961,9 @@ int EstablishPACEChannel(const struct sm_ctx *oldpacectx, sc_card_t *card,
const __u8 *in, size_t inlen, __u8 **out, size_t *outlen, struct sm_ctx *sctx)
{
__u8 pin_id;
size_t length_chat, length_pin, length_cert_desc, length_ef_cardaccess;
size_t length_chat, length_pin, length_cert_desc, length_ef_cardaccess, recent_car_len, prev_car_len;
const __u8 *chat, *pin, *certificate_description;
__u8 *ef_cardaccess = NULL;
__u8 *ef_cardaccess = NULL, *p = NULL, *recent_car = NULL, *prev_car = NULL;
PACEInfo *info = NULL;
__le16 word;
PACEDomainParameterInfo *static_dp = NULL, *eph_dp = NULL;
@@ -770,20 +1042,21 @@ int EstablishPACEChannel(const struct sm_ctx *oldpacectx, sc_card_t *card,
goto err;
}
/* get enough memory for status bytes and EF.CardAccess */
*out = realloc(4 + length_ef_cardaccess);
if (!*out) {
p = realloc(*out, 6 + length_ef_cardaccess);
if (!p) {
r = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
*out = p;
word = __cpu_to_le16(length_ef_cardaccess);
memcpy(*out + 2, &word, sizeof word);
memcpy(*out + 4, ef_cardaccess, length_ef_cardaccess);
memcpy((*out) + 2, &word, sizeof word);
memcpy((*out) + 4, ef_cardaccess, length_ef_cardaccess);
} else {
/* PACE is executed the second time. EF.CardAccess should already be
* present */
memcpy(&word, *out + 2, sizeof word);
memcpy(&word, (*out) + 2, sizeof word);
length_ef_cardaccess = __le16_to_cpu(word);
ef_cardaccess = *out + 4;
ef_cardaccess = (*out) + 4;
}
*outlen = 4+length_ef_cardaccess;
bin_log(card->ctx, "EF.CardAccess", ef_cardaccess, length_ef_cardaccess);
@@ -796,7 +1069,7 @@ int EstablishPACEChannel(const struct sm_ctx *oldpacectx, sc_card_t *card,
goto err;
}
r = pace_mse_set_at(oldpacectx, card, info->protocol, pin_id, chat, length_chat, *out, *out+1);
r = pace_mse_set_at(oldpacectx, card, info->protocol, pin_id, chat, length_chat, *out, (*out)+1);
if (r < 0) {
sc_error(card->ctx, "Could not select protocol proberties "
"(MSE: Set AT).");
@@ -807,7 +1080,7 @@ int EstablishPACEChannel(const struct sm_ctx *oldpacectx, sc_card_t *card,
r = SC_ERROR_INTERNAL;
goto err;
}
r = pace_gen_auth(oldpacectx, card, 1, NULL, 0, (u8 **) &enc_nonce->data,
r = pace_gen_auth_1_encrypted_nonce(oldpacectx, card, (u8 **) &enc_nonce->data,
&enc_nonce->length);
if (r < 0) {
sc_error(card->ctx, "Could not get encrypted nonce from card "
@@ -842,7 +1115,7 @@ int EstablishPACEChannel(const struct sm_ctx *oldpacectx, sc_card_t *card,
r = SC_ERROR_INTERNAL;
goto err;
}
r = pace_gen_auth(oldpacectx, card, 2, (u8 *) mdata->data, mdata->length,
r = pace_gen_auth_2_map_nonce(oldpacectx, card, (u8 *) mdata->data, mdata->length,
(u8 **) &mdata_opp->data, &mdata_opp->length);
if (r < 0) {
sc_error(card->ctx, "Could not exchange mapping data with card "
@@ -863,7 +1136,7 @@ int EstablishPACEChannel(const struct sm_ctx *oldpacectx, sc_card_t *card,
r = SC_ERROR_INTERNAL;
goto err;
}
r = pace_gen_auth(oldpacectx, card, 3, (u8 *) pub->data, pub->length,
r = pace_gen_auth_3_perform_key_agreement(oldpacectx, card, (u8 *) pub->data, pub->length,
(u8 **) &pub_opp->data, &pub_opp->length);
if (r < 0) {
sc_error(card->ctx, "Could not exchange ephemeral public key with card "
@@ -892,8 +1165,25 @@ int EstablishPACEChannel(const struct sm_ctx *oldpacectx, sc_card_t *card,
r = SC_ERROR_INTERNAL;
goto err;
}
r = pace_gen_auth(oldpacectx, card, 4, (u8 *) token->data, token->length,
(u8 **) &token_opp->data, &token_opp->length);
r = pace_gen_auth_4_mutual_authentication(oldpacectx, card, (u8 *) token->data, token->length,
(u8 **) &token_opp->data, &token_opp->length, &recent_car, &recent_car_len,
&prev_car, &prev_car_len);
/* get enough memory for CARs */
p = realloc(*out, (*outlen) + 1 + recent_car_len + 1 + prev_car_len);
if (!p) {
r = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
*out = p;
(*out)[*outlen] = recent_car_len;
memcpy((*out) + (*outlen) + 1,
recent_car, recent_car_len);
(*out)[(*outlen) + 1 + recent_car_len] = prev_car_len;
memcpy((*out) + (*outlen) + 1 + recent_car_len + 1,
prev_car, prev_car_len);
*outlen += 1 + recent_car_len + 1 + prev_car_len;
if (r < 0) {
sc_error(card->ctx, "Could not exchange authentication token with card "
"(General Authenticate step 4 failed).");