- implemented passing a CHAT to the card.

- note that the type PACE_CHAT should be integrated into OpenPACE
- added parameters for passing CHAT and certificate description to pace-tool


git-svn-id: https://vsmartcard.svn.sourceforge.net/svnroot/vsmartcard@146 96b47cad-a561-4643-ad3b-153ac7d7599c
This commit is contained in:
frankmorgner
2010-06-10 11:14:39 +00:00
parent a1fdced1f5
commit c64e9ac527
2 changed files with 130 additions and 38 deletions

View File

@@ -19,6 +19,7 @@
#include "binutil.h" #include "binutil.h"
#include "pace.h" #include "pace.h"
#include "scutil.h" #include "scutil.h"
#include <asm/byteorder.h>
#include <opensc/log.h> #include <opensc/log.h>
#include <openssl/pace.h> #include <openssl/pace.h>
#include <stdint.h> #include <stdint.h>
@@ -43,6 +44,10 @@ static const char *can = NULL;
static u8 usecan = 0; static u8 usecan = 0;
static const char *mrz = NULL; static const char *mrz = NULL;
static u8 usemrz = 0; static u8 usemrz = 0;
static u8 chat[0xff];
static size_t chatlen = 0;
static u8 desc[0xffff];
static size_t desclen = 0;
static const char *cdriver = NULL; static const char *cdriver = NULL;
static sc_context_t *ctx = NULL; static sc_context_t *ctx = NULL;
@@ -55,6 +60,8 @@ static sc_reader_t *reader;
#define OPT_PUK 'u' #define OPT_PUK 'u'
#define OPT_CAN 'a' #define OPT_CAN 'a'
#define OPT_MRZ 'z' #define OPT_MRZ 'z'
#define OPT_CHAT 'C'
#define OPT_CERTDESC 'D'
#define OPT_CHANGE_PIN 'N' #define OPT_CHANGE_PIN 'N'
#define OPT_RESUME_PIN 'R' #define OPT_RESUME_PIN 'R'
#define OPT_TRANSLATE 't' #define OPT_TRANSLATE 't'
@@ -70,6 +77,8 @@ static const struct option options[] = {
{ "puk", optional_argument, NULL, OPT_PUK }, { "puk", optional_argument, NULL, OPT_PUK },
{ "can", optional_argument, NULL, OPT_CAN }, { "can", optional_argument, NULL, OPT_CAN },
{ "mrz", optional_argument, NULL, OPT_MRZ }, { "mrz", optional_argument, NULL, OPT_MRZ },
{ "chat", required_argument, NULL, OPT_CHAT },
{ "cert-desc", required_argument, NULL, OPT_CERTDESC },
{ "new-pin", optional_argument, NULL, OPT_CHANGE_PIN }, { "new-pin", optional_argument, NULL, OPT_CHANGE_PIN },
{ "resume-pin", no_argument, NULL, OPT_RESUME_PIN }, { "resume-pin", no_argument, NULL, OPT_RESUME_PIN },
{ "translate", no_argument, NULL, OPT_TRANSLATE }, { "translate", no_argument, NULL, OPT_TRANSLATE },
@@ -81,10 +90,12 @@ static const char *option_help[] = {
"Print help and exit", "Print help and exit",
"Number of reader to use (default: auto-detect)", "Number of reader to use (default: auto-detect)",
"Which card driver to use (default: auto-detect)", "Which card driver to use (default: auto-detect)",
"Run PACE with PIN", "Run PACE with (transport) PIN",
"Run PACE with PUK", "Run PACE with PUK",
"Run PACE with CAN", "Run PACE with CAN",
"Run PACE with MRZ (insert MRZ without newlines)", "Run PACE with MRZ (insert MRZ without newlines)",
"Card holder authorization template to use (hex string)",
"Certificate description to use (hex string)",
"Install a new PIN", "Install a new PIN",
"Resume PIN (uses CAN to activate last retry)", "Resume PIN (uses CAN to activate last retry)",
"Send plaintext APDUs through the PACE SM channel", "Send plaintext APDUs through the PACE SM channel",
@@ -94,9 +105,11 @@ static const char *option_help[] = {
int pace_get_channel(struct sm_ctx *oldpacectx, sc_card_t *card, int pace_get_channel(struct sm_ctx *oldpacectx, sc_card_t *card,
enum s_type pin_id, const char *pin, size_t pinlen, enum s_type pin_id, const char *pin, size_t pinlen,
const char *chat, size_t chatlen, const char *desc, size_t desclen,
__u8 **out, size_t *outlen, struct sm_ctx *sctx) __u8 **out, size_t *outlen, struct sm_ctx *sctx)
{ {
u8 buf[0xff + 5]; u8 buf[0xff + 0xff + 0xffff + 5];
__le16 word;
switch (pin_id) { switch (pin_id) {
case PACE_MRZ: case PACE_MRZ:
@@ -109,17 +122,38 @@ int pace_get_channel(struct sm_ctx *oldpacectx, sc_card_t *card,
return SC_ERROR_INVALID_ARGUMENTS; return SC_ERROR_INVALID_ARGUMENTS;
} }
if (pinlen > sizeof(buf) - 5) { if (pinlen > 0xff) {
sc_error(card->ctx, "%s too long (maximal %u bytes supported)", sc_error(card->ctx, "%s "
"too long (maximal %u bytes supported)",
pace_secret_name(pin_id), pace_secret_name(pin_id),
sizeof(buf) - 5); 0xff);
return SC_ERROR_INVALID_ARGUMENTS;
} }
if (chatlen > 0xff) {
sc_error(card->ctx, "CHAT "
"too long (maximal %u bytes supported)",
0xff);
return SC_ERROR_INVALID_ARGUMENTS;
}
if (desclen > 0xffff) {
sc_error(card->ctx, "Certificate description "
"too long (maximal %u bytes supported)",
0xffff);
return SC_ERROR_INVALID_ARGUMENTS;
}
buf[0] = pin_id; buf[0] = pin_id;
buf[1] = 0; // length_chat
buf[2] = pinlen; // length_pin buf[1] = chatlen;
memcpy(&buf[3], pin, pinlen); memcpy(&buf[2], chat, chatlen);
buf[3 + pinlen] = 0; // length_cert_desc
buf[4 + pinlen]= 0; // length_cert_desc buf[2 + chatlen] = pinlen;
memcpy(&buf[3 + chatlen], pin, pinlen);
word = __cpu_to_le16(desclen);
memcpy(&buf[3 + chatlen + pinlen], &word, sizeof word);
memcpy(&buf[3 + chatlen + pinlen + sizeof word],
desc, desclen);
return EstablishPACEChannel(oldpacectx, card, buf, out, outlen, sctx); return EstablishPACEChannel(oldpacectx, card, buf, out, outlen, sctx);
} }
@@ -195,7 +229,7 @@ main (int argc, char **argv)
memset(&tmpctx, 0, sizeof(tmpctx)); memset(&tmpctx, 0, sizeof(tmpctx));
while (1) { while (1) {
i = getopt_long(argc, argv, "hr:i:u:a:z:N::Rtvoc:", options, &oindex); i = getopt_long(argc, argv, "hr:i::u::a::z::C:D:N::Rtvoc:", options, &oindex);
if (i == -1) if (i == -1)
break; break;
switch (i) { switch (i) {
@@ -242,6 +276,20 @@ main (int argc, char **argv)
if (!mrz) if (!mrz)
can = getenv("MRZ"); can = getenv("MRZ");
break; break;
case OPT_CHAT:
chatlen = sizeof chat;
if (sc_hex_to_bin(optarg, chat, &chatlen) < 0) {
parse_error(argv[0], options, option_help, optarg, oindex);
exit(2);
}
break;
case OPT_CERTDESC:
desclen = sizeof desc;
if (sc_hex_to_bin(optarg, desc, &desclen) < 0) {
parse_error(argv[0], options, option_help, optarg, oindex);
exit(2);
}
break;
case OPT_CHANGE_PIN: case OPT_CHANGE_PIN:
dochangepin = 1; dochangepin = 1;
newpin = optarg; newpin = optarg;
@@ -300,6 +348,7 @@ main (int argc, char **argv)
t_start = time(NULL); t_start = time(NULL);
i = pace_get_channel(NULL, card, i = pace_get_channel(NULL, card,
PACE_CAN, can, can ? strlen(can) : 0, PACE_CAN, can, can ? strlen(can) : 0,
chat, chatlen, desc, desclen,
&channeldata, &channeldatalen, &tmpctx); &channeldata, &channeldatalen, &tmpctx);
if (i < 0) if (i < 0)
goto err; goto err;
@@ -309,6 +358,7 @@ main (int argc, char **argv)
i = pace_get_channel(&tmpctx, card, i = pace_get_channel(&tmpctx, card,
PACE_PIN, pin, pin ? strlen(pin) : 0, PACE_PIN, pin, pin ? strlen(pin) : 0,
chat, chatlen, desc, desclen,
&channeldata, &channeldatalen, &sctx); &channeldata, &channeldatalen, &sctx);
if (i < 0) if (i < 0)
goto err; goto err;
@@ -321,6 +371,7 @@ main (int argc, char **argv)
t_start = time(NULL); t_start = time(NULL);
i = pace_get_channel(NULL, card, i = pace_get_channel(NULL, card,
PACE_PIN, pin, pin ? strlen(pin) : 0, PACE_PIN, pin, pin ? strlen(pin) : 0,
chat, chatlen, desc, desclen,
&channeldata, &channeldatalen, &sctx); &channeldata, &channeldatalen, &sctx);
if (i < 0) if (i < 0)
goto err; goto err;
@@ -357,6 +408,7 @@ main (int argc, char **argv)
t_start = time(NULL); t_start = time(NULL);
i = pace_get_channel(NULL, card, id, s, s ? strlen(s) : 0, i = pace_get_channel(NULL, card, id, s, s ? strlen(s) : 0,
chat, chatlen, desc, desclen,
&channeldata, &channeldatalen, &sctx); &channeldata, &channeldatalen, &sctx);
if (i < 0) if (i < 0)
goto err; goto err;

View File

@@ -33,18 +33,31 @@
#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)
/* /*
* MSE:Set AT * MSE:Set AT
*/ */
/* XXX should be part of OpenPACE */
typedef struct pace_chat_st {
ASN1_OBJECT *terminal_type;
ASN1_OCTET_STRING *relative_authorization;
} PACE_CHAT;
ASN1_SEQUENCE(PACE_CHAT) = {
ASN1_SIMPLE(PACE_CHAT, terminal_type, ASN1_OBJECT),
/* tag: 0x53*/
ASN1_APP_IMP(PACE_CHAT, relative_authorization, ASN1_OCTET_STRING, 0x13) /* discretionary data */
} ASN1_SEQUENCE_END(PACE_CHAT)
IMPLEMENT_ASN1_FUNCTIONS(PACE_CHAT)
typedef struct pace_mse_set_at_cd_st { typedef struct pace_mse_set_at_cd_st {
ASN1_OBJECT *cryptographic_mechanism_reference; ASN1_OBJECT *cryptographic_mechanism_reference;
ASN1_INTEGER *key_reference1; ASN1_INTEGER *key_reference1;
ASN1_INTEGER *key_reference2; ASN1_INTEGER *key_reference2;
ASN1_OCTET_STRING *auxiliary_data; ASN1_OCTET_STRING *auxiliary_data;
ASN1_OCTET_STRING *eph_pub_key; ASN1_OCTET_STRING *eph_pub_key;
ASN1_OCTET_STRING *cha_template; PACE_CHAT *cha_template;
} PACE_MSE_SET_AT_C; } PACE_MSE_SET_AT_C;
ASN1_SEQUENCE(PACE_MSE_SET_AT_C) = { ASN1_SEQUENCE(PACE_MSE_SET_AT_C) = {
/* 0x80 /* 0x80
@@ -64,7 +77,7 @@ ASN1_SEQUENCE(PACE_MSE_SET_AT_C) = {
ASN1_IMP_OPT(PACE_MSE_SET_AT_C, eph_pub_key, ASN1_OCTET_STRING, 0x11), ASN1_IMP_OPT(PACE_MSE_SET_AT_C, eph_pub_key, ASN1_OCTET_STRING, 0x11),
/* 0x7F4C /* 0x7F4C
* Certificate Holder Authorization Template */ * Certificate Holder Authorization Template */
ASN1_APP_IMP_OPT(PACE_MSE_SET_AT_C, cha_template, ASN1_OCTET_STRING, 0x4c), ASN1_APP_IMP_OPT(PACE_MSE_SET_AT_C, cha_template, PACE_CHAT, 0x4c),
} ASN1_SEQUENCE_END(PACE_MSE_SET_AT_C) } ASN1_SEQUENCE_END(PACE_MSE_SET_AT_C)
IMPLEMENT_ASN1_FUNCTIONS(PACE_MSE_SET_AT_C) IMPLEMENT_ASN1_FUNCTIONS(PACE_MSE_SET_AT_C)
@@ -108,8 +121,8 @@ typedef struct pace_gen_auth_rapdu_body_st {
ASN1_OCTET_STRING *mapping_data; ASN1_OCTET_STRING *mapping_data;
ASN1_OCTET_STRING *eph_pub_key; ASN1_OCTET_STRING *eph_pub_key;
ASN1_OCTET_STRING *auth_token; ASN1_OCTET_STRING *auth_token;
ASN1_OCTET_STRING *cert_auth1; ASN1_OCTET_STRING *cur_car;
ASN1_OCTET_STRING *cert_auth2; ASN1_OCTET_STRING *prev_car;
} PACE_GEN_AUTH_R_BODY; } PACE_GEN_AUTH_R_BODY;
ASN1_SEQUENCE(PACE_GEN_AUTH_R_BODY) = { ASN1_SEQUENCE(PACE_GEN_AUTH_R_BODY) = {
/* 0x80 /* 0x80
@@ -125,11 +138,11 @@ ASN1_SEQUENCE(PACE_GEN_AUTH_R_BODY) = {
* Authentication Token */ * Authentication Token */
ASN1_IMP_OPT(PACE_GEN_AUTH_R_BODY, auth_token, ASN1_OCTET_STRING, 6), ASN1_IMP_OPT(PACE_GEN_AUTH_R_BODY, auth_token, ASN1_OCTET_STRING, 6),
/* 0x87 /* 0x87
* Certification Authority Reference */ * Most recent Certification Authority Reference */
ASN1_IMP_OPT(PACE_GEN_AUTH_R_BODY, cert_auth1, ASN1_OCTET_STRING, 7), ASN1_IMP_OPT(PACE_GEN_AUTH_R_BODY, cur_car, ASN1_OCTET_STRING, 7),
/* 0x88 /* 0x88
* Certification Authority Reference */ * Previous Certification Authority Reference */
ASN1_IMP_OPT(PACE_GEN_AUTH_R_BODY, cert_auth2, ASN1_OCTET_STRING, 8), ASN1_IMP_OPT(PACE_GEN_AUTH_R_BODY, prev_car, ASN1_OCTET_STRING, 8),
} ASN1_SEQUENCE_END(PACE_GEN_AUTH_R_BODY) } ASN1_SEQUENCE_END(PACE_GEN_AUTH_R_BODY)
IMPLEMENT_ASN1_FUNCTIONS(PACE_GEN_AUTH_R_BODY) IMPLEMENT_ASN1_FUNCTIONS(PACE_GEN_AUTH_R_BODY)
@@ -240,7 +253,8 @@ err:
} }
static int pace_mse_set_at(const struct sm_ctx *oldpacectx, sc_card_t *card, static int pace_mse_set_at(const struct sm_ctx *oldpacectx, sc_card_t *card,
int protocol, int secret_key, int reference, u8 *sw1, u8 *sw2) int protocol, int secret_key, const u8 *chat,
size_t length_chat, u8 *sw1, u8 *sw2)
{ {
sc_apdu_t apdu; sc_apdu_t apdu;
unsigned char *d = NULL; unsigned char *d = NULL;
@@ -259,34 +273,43 @@ static int pace_mse_set_at(const struct sm_ctx *oldpacectx, sc_card_t *card,
apdu.cse = SC_APDU_CASE_3_SHORT; apdu.cse = SC_APDU_CASE_3_SHORT;
apdu.flags = SC_APDU_FLAGS_NO_GET_RESP|SC_APDU_FLAGS_NO_RETRY_WL; apdu.flags = SC_APDU_FLAGS_NO_GET_RESP|SC_APDU_FLAGS_NO_RETRY_WL;
data = PACE_MSE_SET_AT_C_new(); data = PACE_MSE_SET_AT_C_new();
if (!data) { if (!data) {
r = SC_ERROR_OUT_OF_MEMORY; r = SC_ERROR_OUT_OF_MEMORY;
goto err; goto err;
} }
data->cryptographic_mechanism_reference = OBJ_nid2obj(protocol); data->cryptographic_mechanism_reference = OBJ_nid2obj(protocol);
data->key_reference1 = ASN1_INTEGER_new(); data->key_reference1 = ASN1_INTEGER_new();
//data->key_reference2 = ASN1_INTEGER_new();
if (!data->cryptographic_mechanism_reference if (!data->cryptographic_mechanism_reference
|| !data->key_reference1 || !data->key_reference1) {
//|| !data->key_reference2
) {
r = SC_ERROR_OUT_OF_MEMORY; r = SC_ERROR_OUT_OF_MEMORY;
goto err; goto err;
} }
if (!ASN1_INTEGER_set(data->key_reference1, secret_key)
//|| !ASN1_INTEGER_set(data->key_reference2, reference) if (!ASN1_INTEGER_set(data->key_reference1, secret_key)) {
) {
r = SC_ERROR_INTERNAL; r = SC_ERROR_INTERNAL;
goto err; goto err;
} }
if (length_chat) {
if (!d2i_PACE_CHAT(&data->cha_template, (const unsigned char **) &chat,
length_chat)) {
r = SC_ERROR_INTERNAL;
goto err;
}
}
r = i2d_PACE_MSE_SET_AT_C(data, &d); r = i2d_PACE_MSE_SET_AT_C(data, &d);
if (r < 0) { if (r < 0) {
r = SC_ERROR_INTERNAL; r = SC_ERROR_INTERNAL;
goto err; goto err;
} }
/* The tag/length for the sequence (0x30) is omitted in the command apdu. */ /* The tag/length for the sequence (0x30) is omitted in the command apdu. */
/* FIXME is there a OpenSSL way to get the value only or even a define for /* XXX is there a OpenSSL way to get the value only or even a define for
* the tag? */ * the tag? */
apdu.data = sc_asn1_find_tag(card->ctx, d, r, 0x30, &apdu.datalen); apdu.data = sc_asn1_find_tag(card->ctx, d, r, 0x30, &apdu.datalen);
apdu.lc = apdu.datalen; apdu.lc = apdu.datalen;
@@ -448,7 +471,9 @@ static int pace_gen_auth(const struct sm_ctx *oldpacectx, sc_card_t *card,
if (!r_data->enc_nonce if (!r_data->enc_nonce
|| r_data->mapping_data || r_data->mapping_data
|| r_data->eph_pub_key || r_data->eph_pub_key
|| r_data->auth_token) { || r_data->auth_token
|| r_data->cur_car
|| r_data->prev_car) {
sc_error(card->ctx, "Response data of general authenticate for " sc_error(card->ctx, "Response data of general authenticate for "
"step %d should (only) contain the " "step %d should (only) contain the "
"encrypted nonce.", step); "encrypted nonce.", step);
@@ -462,7 +487,9 @@ static int pace_gen_auth(const struct sm_ctx *oldpacectx, sc_card_t *card,
if (r_data->enc_nonce if (r_data->enc_nonce
|| !r_data->mapping_data || !r_data->mapping_data
|| r_data->eph_pub_key || r_data->eph_pub_key
|| r_data->auth_token) { || r_data->auth_token
|| r_data->cur_car
|| r_data->prev_car) {
sc_error(card->ctx, "Response data of general authenticate for " sc_error(card->ctx, "Response data of general authenticate for "
"step %d should (only) contain the " "step %d should (only) contain the "
"mapping data.", step); "mapping data.", step);
@@ -476,7 +503,9 @@ static int pace_gen_auth(const struct sm_ctx *oldpacectx, sc_card_t *card,
if (r_data->enc_nonce if (r_data->enc_nonce
|| r_data->mapping_data || r_data->mapping_data
|| !r_data->eph_pub_key || !r_data->eph_pub_key
|| r_data->auth_token) { || r_data->auth_token
|| r_data->cur_car
|| r_data->prev_car) {
sc_error(card->ctx, "Response data of general authenticate for " sc_error(card->ctx, "Response data of general authenticate for "
"step %d should (only) contain the " "step %d should (only) contain the "
"ephemeral public key.", step); "ephemeral public key.", step);
@@ -499,6 +528,13 @@ static int pace_gen_auth(const struct sm_ctx *oldpacectx, sc_card_t *card,
} }
p = r_data->auth_token->data; p = r_data->auth_token->data;
l = r_data->auth_token->length; 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; break;
default: default:
r = SC_ERROR_INVALID_ARGUMENTS; r = SC_ERROR_INVALID_ARGUMENTS;
@@ -515,7 +551,7 @@ static int pace_gen_auth(const struct sm_ctx *oldpacectx, sc_card_t *card,
err: err:
if (c_data) { if (c_data) {
/* FIXME /* XXX
if (c_data->mapping_data) if (c_data->mapping_data)
ASN1_OCTET_STRING_free(c_data->mapping_data); ASN1_OCTET_STRING_free(c_data->mapping_data);
if (c_data->eph_pub_key) if (c_data->eph_pub_key)
@@ -527,7 +563,7 @@ err:
if (d) if (d)
free(d); free(d);
if (r_data) { if (r_data) {
/* FIXME /* XXX
if (r_data->mapping_data) if (r_data->mapping_data)
ASN1_OCTET_STRING_free(r_data->mapping_data); ASN1_OCTET_STRING_free(r_data->mapping_data);
if (r_data->eph_pub_key) if (r_data->eph_pub_key)
@@ -667,6 +703,10 @@ int EstablishPACEChannel(const struct sm_ctx *oldpacectx, sc_card_t *card,
in++; in++;
chat = in; chat = in;
in += length_chat; in += length_chat;
/* XXX make this human readable */
if (length_chat)
bin_log(card->ctx, "Card holder authorization template",
chat, length_chat);
length_pin = *in; length_pin = *in;
in++; in++;
@@ -677,6 +717,10 @@ int EstablishPACEChannel(const struct sm_ctx *oldpacectx, sc_card_t *card,
length_cert_desc = __le16_to_cpu(word); length_cert_desc = __le16_to_cpu(word);
in += sizeof word; in += sizeof word;
certificate_description = in; certificate_description = in;
/* XXX make this human readable */
if (length_cert_desc)
bin_log(card->ctx, "Certificate description",
certificate_description, length_cert_desc);
if (!*out) { if (!*out) {
@@ -694,10 +738,6 @@ int EstablishPACEChannel(const struct sm_ctx *oldpacectx, sc_card_t *card,
word = __cpu_to_le16(length_ef_cardaccess); word = __cpu_to_le16(length_ef_cardaccess);
memcpy(*out + 2, &word, sizeof word); memcpy(*out + 2, &word, sizeof word);
memcpy(*out + 4, ef_cardaccess, length_ef_cardaccess); memcpy(*out + 4, ef_cardaccess, length_ef_cardaccess);
/* XXX CAR */
/*(*out)[length_ef_cardaccess + 2] = 0;*/
/* XXX CARprev */
/*(*out)[length_ef_cardaccess + 3] = 0;*/
} else { } else {
second_execution = 1; second_execution = 1;
memcpy(&word, *out + 2, sizeof word); memcpy(&word, *out + 2, sizeof word);
@@ -714,7 +754,7 @@ int EstablishPACEChannel(const struct sm_ctx *oldpacectx, sc_card_t *card,
goto err; goto err;
} }
r = pace_mse_set_at(oldpacectx, card, info->protocol, pin_id, 1, *out, *out+1); r = pace_mse_set_at(oldpacectx, card, info->protocol, pin_id, chat, length_chat, *out, *out+1);
if (r < 0) { if (r < 0) {
sc_error(card->ctx, "Could not select protocol proberties " sc_error(card->ctx, "Could not select protocol proberties "
"(MSE: Set AT)."); "(MSE: Set AT).");