From 7145984d5f1a4135175bb7977f8ef913f7a38495 Mon Sep 17 00:00:00 2001 From: Frank Morgner Date: Tue, 18 Sep 2012 02:54:19 +0200 Subject: [PATCH] libnpa: Implementation of TA --- npa/src/npa.c | 489 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 409 insertions(+), 80 deletions(-) diff --git a/npa/src/npa.c b/npa/src/npa.c index 28bce44..80da19e 100644 --- a/npa/src/npa.c +++ b/npa/src/npa.c @@ -38,41 +38,42 @@ #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 */ -typedef struct npa_mse_set_at_cd_st { +typedef struct npa_mse_cd_st { ASN1_OBJECT *cryptographic_mechanism_reference; ASN1_OCTET_STRING *key_reference1; ASN1_OCTET_STRING *key_reference2; ASN1_OCTET_STRING *eph_pub_key; CVC_DISCRETIONARY_DATA_TEMPLATES *auxiliary_data; CVC_CHAT *chat; -} NPA_MSE_SET_AT_C; -ASN1_SEQUENCE(NPA_MSE_SET_AT_C) = { +} NPA_MSE_C; +ASN1_SEQUENCE(NPA_MSE_C) = { /* 0x80 * Cryptographic mechanism reference */ - ASN1_IMP_OPT(NPA_MSE_SET_AT_C, cryptographic_mechanism_reference, ASN1_OBJECT, 0), + ASN1_IMP_OPT(NPA_MSE_C, cryptographic_mechanism_reference, ASN1_OBJECT, 0), /* 0x83 * Reference of a public key / secret key */ - ASN1_IMP_OPT(NPA_MSE_SET_AT_C, key_reference1, ASN1_OCTET_STRING, 3), + ASN1_IMP_OPT(NPA_MSE_C, key_reference1, ASN1_OCTET_STRING, 3), /* 0x84 * Reference of a private key / Reference for computing a session key */ - ASN1_IMP_OPT(NPA_MSE_SET_AT_C, key_reference2, ASN1_OCTET_STRING, 4), + ASN1_IMP_OPT(NPA_MSE_C, key_reference2, ASN1_OCTET_STRING, 4), /* 0x91 * Ephemeral Public Key */ - ASN1_IMP_OPT(NPA_MSE_SET_AT_C, eph_pub_key, ASN1_OCTET_STRING, 0x11), + ASN1_IMP_OPT(NPA_MSE_C, eph_pub_key, ASN1_OCTET_STRING, 0x11), /* 0x67 * Auxiliary authenticated data */ - ASN1_APP_IMP_OPT(NPA_MSE_SET_AT_C, auxiliary_data, CVC_DISCRETIONARY_DATA_TEMPLATES, 7), - /*ASN1_APP_IMP_OPT(NPA_MSE_SET_AT_C, auxiliary_data, ASN1_OCTET_STRING, 7),*/ + ASN1_APP_IMP_OPT(NPA_MSE_C, auxiliary_data, CVC_DISCRETIONARY_DATA_TEMPLATES, 7), + /*ASN1_APP_IMP_OPT(NPA_MSE_C, auxiliary_data, ASN1_OCTET_STRING, 7),*/ /* Certificate Holder Authorization Template */ - ASN1_OPT(NPA_MSE_SET_AT_C, chat, CVC_CHAT), -} ASN1_SEQUENCE_END(NPA_MSE_SET_AT_C) -DECLARE_ASN1_FUNCTIONS(NPA_MSE_SET_AT_C) -IMPLEMENT_ASN1_FUNCTIONS(NPA_MSE_SET_AT_C) + ASN1_OPT(NPA_MSE_C, chat, CVC_CHAT), +} ASN1_SEQUENCE_END(NPA_MSE_C) +DECLARE_ASN1_FUNCTIONS(NPA_MSE_C) +IMPLEMENT_ASN1_FUNCTIONS(NPA_MSE_C) /* @@ -177,6 +178,7 @@ struct npa_sm_ctx { /* included in OpenPACE, but not propagated */ +extern BUF_MEM *BUF_MEM_create(size_t len); extern BUF_MEM *BUF_MEM_create_init(const void *buf, size_t len); @@ -339,69 +341,146 @@ err: return r; } -static int npa_mse_set_at(struct sm_ctx *oldnpactx, sc_card_t *card, - int protocol, int secret_key, const CVC_CHAT *chat, u8 *sw1, u8 *sw2) +static int format_mse_cdata(struct sc_context *ctx, int protocol, + const unsigned char *key_reference1, size_t key_reference1_len, + const unsigned char *key_reference2, size_t key_reference2_len, + const unsigned char *eph_pub_key, size_t eph_pub_key_len, + const unsigned char *auxiliary_data, size_t auxiliary_data_len, + const CVC_CHAT *chat, unsigned char **cdata) { - sc_apdu_t apdu; - unsigned char *d = NULL; - NPA_MSE_SET_AT_C *data = NULL; - int r, tries, class, tag; + NPA_MSE_C *data = NULL; + unsigned char *data_sequence; + const unsigned char *data_no_sequence, *p; long length; - const unsigned char *p; + int r, class, tag; - memset(&apdu, 0, sizeof apdu); - - if (!card || !sw1 || !sw2) { + if (!cdata) { r = SC_ERROR_INVALID_ARGUMENTS; goto err; } - apdu.ins = 0x22; - apdu.p1 = 0xc1; - apdu.p2 = 0xa4; - apdu.cse = SC_APDU_CASE_3_SHORT; - apdu.flags = SC_APDU_FLAGS_NO_GET_RESP|SC_APDU_FLAGS_NO_RETRY_WL; - - - data = NPA_MSE_SET_AT_C_new(); + data = NPA_MSE_C_new(); if (!data) { - r = SC_ERROR_OUT_OF_MEMORY; goto err; } - data->cryptographic_mechanism_reference = OBJ_nid2obj(protocol); - data->key_reference1 = ASN1_INTEGER_new(); - - if (!data->cryptographic_mechanism_reference - || !data->key_reference1) { - r = SC_ERROR_OUT_OF_MEMORY; - goto err; + if (protocol) { + data->cryptographic_mechanism_reference = OBJ_nid2obj(protocol); + if (!data->cryptographic_mechanism_reference) { + sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "Error setting Cryptographic mechanism reference of MSE:Set AT data"); + r = SC_ERROR_INTERNAL; + goto err; + } } - if (!ASN1_INTEGER_set(data->key_reference1, secret_key)) { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Error setting key reference 1 of MSE:Set AT data"); - ssl_error(card->ctx); - r = SC_ERROR_INTERNAL; - goto err; + if (key_reference1 && key_reference1_len) { + data->key_reference1 = ASN1_OCTET_STRING_new(); + if (!data->key_reference1 + || !M_ASN1_OCTET_STRING_set( + data->key_reference1, key_reference1, key_reference1_len)) { + sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "Error setting key reference 1 of MSE:Set AT data"); + r = SC_ERROR_INTERNAL; + goto err; + } + } + + if (key_reference2 && key_reference2_len) { + data->key_reference2 = ASN1_OCTET_STRING_new(); + if (!data->key_reference2 + || !M_ASN1_OCTET_STRING_set( + data->key_reference2, key_reference2, key_reference2_len)) { + sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "Error setting key reference 2 of MSE:Set AT data"); + r = SC_ERROR_INTERNAL; + goto err; + } + } + + if (eph_pub_key && eph_pub_key_len) { + data->eph_pub_key = ASN1_OCTET_STRING_new(); + if (!data->eph_pub_key + || !M_ASN1_OCTET_STRING_set( + data->eph_pub_key, eph_pub_key, eph_pub_key_len)) { + sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "Error setting ephemeral Public Key of MSE:Set AT data"); + r = SC_ERROR_INTERNAL; + goto err; + } + } + + if (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; + goto err; + } } data->chat = (CVC_CHAT *) chat; - r = i2d_NPA_MSE_SET_AT_C(data, &d); - p = d; - if (r < 0 - || (0x80 & ASN1_get_object(&p, &length, &tag, &class, r))) { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Error encoding MSE:Set AT APDU data"); - ssl_error(card->ctx); + length = i2d_NPA_MSE_C(data, &data_sequence); + data_no_sequence = data_sequence; + if (length < 0 + || (0x80 & ASN1_get_object(&data_no_sequence, &length, &tag, &class, length))) { + sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "Error encoding MSE:Set AT APDU data"); r = SC_ERROR_INTERNAL; goto err; } - apdu.data = p; - apdu.datalen = length; - apdu.lc = length; + if (length < 0) { + r = SC_ERROR_INTERNAL; + goto err; + } + bin_log(ctx, SC_LOG_DEBUG_NORMAL, "MSE command data", data_no_sequence, length); + + + p = realloc(*cdata, length); + if (!p) { + r = SC_ERROR_OUT_OF_MEMORY; + goto err; + } + memcpy(*cdata, data_no_sequence, length); + r = length; + +err: + if (data) { + /* do not free the functions parameter chat */ + data->chat = NULL; + NPA_MSE_C_free(data); + } + if (data_sequence) + free(data_sequence); + + return r; +} + +static int npa_mse(struct sm_ctx *oldnpactx, sc_card_t *card, unsigned char p1, + int protocol, const unsigned char *secret_key, size_t secret_key_len, + const CVC_CHAT *chat, u8 *sw1, u8 *sw2) +{ + sc_apdu_t apdu; + unsigned char *d = NULL; + int r, tries, class, tag; + + memset(&apdu, 0, sizeof apdu); + + if (!card) { + r = SC_ERROR_INVALID_ARGUMENTS; + goto err; + } + + apdu.ins = 0x22; + apdu.p1 = p1; + apdu.p2 = 0xa4; + apdu.cse = SC_APDU_CASE_3_SHORT; + apdu.flags = SC_APDU_FLAGS_NO_GET_RESP|SC_APDU_FLAGS_NO_RETRY_WL; + + r = format_mse_cdata(card->ctx, protocol, secret_key, secret_key_len, NULL, + 0, NULL, 0, NULL, 0, chat, &d); + if (r < 0) + goto err; + apdu.data = d; + apdu.datalen = r; + apdu.lc = r; - bin_log(card->ctx, SC_LOG_DEBUG_NORMAL, "MSE:Set AT command data", apdu.data, apdu.datalen); if (oldnpactx) r = sm_transmit_apdu(oldnpactx, card, &apdu); @@ -417,12 +496,30 @@ static int npa_mse_set_at(struct sm_ctx *oldnpactx, sc_card_t *card, goto err; } - *sw1 = apdu.sw1; - *sw2 = apdu.sw2; + if (sw1) + *sw1 = apdu.sw1; + if (sw2) + *sw2 = apdu.sw2; - if (apdu.sw1 == 0x63) { - if ((apdu.sw2 & 0xc0) == 0xc0) { - tries = apdu.sw2 & 0x0f; +err: + if (d) + free(d); + + return r; +} + +static int npa_mse_set_at_pace(struct sm_ctx *oldnpactx, sc_card_t *card, + int protocol, enum s_type secret_key, const CVC_CHAT *chat, u8 *sw1, + u8 *sw2) +{ + int r, tries; + char key = secret_key; + + r = npa_mse(oldnpactx, card, 0xC1, protocol, &key, sizeof key, chat, sw1, sw2); + + if (*sw1 == 0x63) { + if ((*sw2 & 0xc0) == 0xc0) { + tries = *sw2 & 0x0f; if (tries <= 1) { /* this is only a warning... */ sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Remaining tries: %d (%s must be %s)\n", @@ -432,32 +529,20 @@ static int npa_mse_set_at(struct sm_ctx *oldnpactx, sc_card_t *card, r = SC_SUCCESS; } else { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Unknown status bytes: SW1=%02X, SW2=%02X\n", - apdu.sw1, apdu.sw2); + *sw1, *sw2); r = SC_ERROR_CARD_CMD_FAILED; - goto err; } - } else if (apdu.sw1 == 0x62 && apdu.sw2 == 0x83) { + } else if (*sw1 == 0x62 && *sw2 == 0x83) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Password is deactivated\n"); r = SC_ERROR_AUTH_METHOD_BLOCKED; - goto err; } else { - r = sc_check_sw(card, apdu.sw1, apdu.sw2); + r = sc_check_sw(card, *sw1, *sw2); } -err: - if (apdu.resp) - free(apdu.resp); - if (data) { - /* do not free the functions parameter chat */ - data->chat = NULL; - NPA_MSE_SET_AT_C_free(data); - } - if (d) - free(d); - return r; } + static int npa_gen_auth_1_encrypted_nonce(struct sm_ctx *oldnpactx, sc_card_t *card, u8 **enc_nonce, size_t *enc_nonce_len) { @@ -1098,13 +1183,15 @@ int EstablishPACEChannel(struct sm_ctx *oldnpactx, sc_card_t *card, eac_ctx->tr_version = tr_version; - r = npa_mse_set_at(oldnpactx, card, eac_ctx->pace_ctx->protocol, pace_input.pin_id, - chat, &pace_output->mse_set_at_sw1, &pace_output->mse_set_at_sw2); + r = npa_mse_set_at_pace(oldnpactx, card, eac_ctx->pace_ctx->protocol, + pace_input.pin_id, chat, &pace_output->mse_set_at_sw1, + &pace_output->mse_set_at_sw2); if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not select protocol proberties " "(MSE: Set AT failed)."); goto err; } + enc_nonce = BUF_MEM_new(); if (!enc_nonce) { ssl_error(card->ctx); @@ -1317,6 +1404,248 @@ err: SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } +static int npa_mse_set_dst_ta(struct sm_ctx *npactx, sc_card_t *card, + const unsigned char *car, size_t car_len) +{ + return npa_mse(npactx, card, 0x81, 0, car, car_len, NULL, + NULL, NULL); +} + +static int npa_mse_set_at_ta(struct sm_ctx *npactx, sc_card_t *card, + const unsigned char *chr, size_t chr_len) +{ + return npa_mse(npactx, card, 0xa1, 0, chr, chr_len, NULL, + NULL, NULL); +} + +static int npa_get_challenge(struct sm_ctx *npactx, sc_card_t *card, + unsigned char *challenge, size_t len) +{ + sc_apdu_t apdu; + int r, class, tag; + long int length; + + memset(&apdu, 0, sizeof apdu); + + if (!card) { + r = SC_ERROR_INVALID_ARGUMENTS; + goto err; + } + + apdu.ins = 0x84; + apdu.cse = SC_APDU_CASE_2_SHORT; + apdu.flags = SC_APDU_FLAGS_NO_GET_RESP|SC_APDU_FLAGS_NO_RETRY_WL; + apdu.resplen = len; + apdu.resp = challenge; + + if (npactx) + r = sm_transmit_apdu(npactx, card, &apdu); + else + r = sc_transmit_apdu(card, &apdu); + if (r < 0) + goto err; + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + +err: + return r; +} + +static int npa_verify(struct sm_ctx *npactx, sc_card_t *card, + const unsigned char *cert, size_t cert_len) +{ + sc_apdu_t apdu; + int r, class, tag; + long int length; + + memset(&apdu, 0, sizeof apdu); + + if (!card) { + r = SC_ERROR_INVALID_ARGUMENTS; + goto err; + } + + apdu.ins = 0x2A; + apdu.p1 = 0x00; + apdu.p2 = 0xbe; + apdu.cse = SC_APDU_CASE_4_EXT; + apdu.flags = SC_APDU_FLAGS_NO_GET_RESP|SC_APDU_FLAGS_NO_RETRY_WL; + + apdu.data = cert; + if (0x80 & ASN1_get_object(&apdu.data, &length, &tag, + &class, cert_len)) { + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Error decoding Certificate"); + ssl_error(card->ctx); + r = SC_ERROR_INTERNAL; + goto err; + } + apdu.datalen = length; + apdu.lc = length; + + if (npactx) + r = sm_transmit_apdu(npactx, card, &apdu); + else + r = sc_transmit_apdu(card, &apdu); + if (r < 0) + goto err; + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + +err: + return r; +} + +static int npa_external_authenticate(struct sm_ctx *npactx, sc_card_t *card, + const unsigned char *signature, size_t signature_len) +{ + int r; + sc_apdu_t apdu; + memset(&apdu, 0, sizeof apdu); + + if (!card) { + r = SC_ERROR_INVALID_ARGUMENTS; + goto err; + } + + apdu.ins = 0x82; + apdu.cse = SC_APDU_CASE_4_SHORT; + apdu.flags = SC_APDU_FLAGS_NO_GET_RESP|SC_APDU_FLAGS_NO_RETRY_WL; + + apdu.data = signature; + apdu.datalen = signature_len; + apdu.lc = signature_len; + + if (npactx) + r = sm_transmit_apdu(npactx, card, &apdu); + else + r = sc_transmit_apdu(card, &apdu); + if (r < 0) + goto err; + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + +err: + return r; +} + +int perform_terminal_authentication(struct sm_ctx *ctx, sc_card_t *card, + const unsigned char **certs, const size_t *certs_lens, + const unsigned char *privkey, size_t privkey_len, + const unsigned char *auxiliary_data, size_t auxiliary_data_len) +{ + int r; + const unsigned char *cert; + size_t cert_len, num_certs = 0; + CVC_CERT *cvc_cert = NULL; + BUF_MEM *my_ta_comp_eph_pubkey = NULL, *nonce = NULL, *signature = NULL; + + if (!card || !ctx->priv_data || !certs_lens || !certs) { + r = SC_ERROR_INVALID_ARGUMENTS; + goto err; + } + struct npa_sm_ctx *eacsmctx = ctx->priv_data; + + eacsmctx->flags = 0; + + + cert = *certs; + cert_len = *certs_lens; + while (cert && cert_len) { + if (!CVC_d2i_CVC_CERT(&cvc_cert, &cert, cert_len) || !cvc_cert + || !cvc_cert->body || !cvc_cert->body->certificate_authority_reference + || !cvc_cert->body->certificate_holder_reference) { + r = SC_ERROR_INVALID_DATA; + goto err; + } + + r = npa_mse_set_dst_ta(ctx, card, + cvc_cert->body->certificate_authority_reference->data, + cvc_cert->body->certificate_authority_reference->length); + if (r < 0) { + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not select protocol proberties " + "(MSE: Set AT failed)."); + goto err; + } + + r = npa_verify(ctx, card, cert, cert_len); + if (r < 0) + goto err; + + cert++; + cert_len++; + CVC_CERT_free(cvc_cert); + cvc_cert = NULL; + } + + + if (!EAC_CTX_init_ta(eacsmctx->ctx, privkey, privkey_len, cert, cert_len, + NULL, 0)) { + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not initialize TA."); + ssl_error(card->ctx); + r = SC_ERROR_INTERNAL; + goto err; + } + my_ta_comp_eph_pubkey = TA_STEP3_generate_ephemeral_key(eacsmctx->ctx); + if (!my_ta_comp_eph_pubkey) { + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not generate CA ephemeral key."); + ssl_error(card->ctx); + r = SC_ERROR_INTERNAL; + goto err; + } + + + r = npa_mse_set_at_ta(ctx, card, + cvc_cert->body->certificate_holder_reference->data, + cvc_cert->body->certificate_holder_reference->length); + if (r < 0) { + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not select protocol proberties " + "(MSE: Set AT failed)."); + goto err; + } + + nonce = BUF_MEM_create(8); + if (!nonce) { + ssl_error(card->ctx); + r = SC_ERROR_INTERNAL; + goto err; + } + r = npa_get_challenge(ctx, card, nonce->data, nonce->length); + if (r < 0) { + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not get nonce for TA."); + goto err; + } + if (!TA_STEP4_set_nonce(eacsmctx->ctx, nonce)) { + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not set nonce for TA."); + ssl_error(card->ctx); + r = SC_ERROR_INTERNAL; + goto err; + } + + eacsmctx->auxiliary_data = BUF_MEM_create_init(auxiliary_data, + auxiliary_data_len); + signature = TA_STEP5_sign(eacsmctx->ctx, my_ta_comp_eph_pubkey, + eacsmctx->id_icc, eacsmctx->auxiliary_data); + if (!signature) { + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not generate signature."); + ssl_error(card->ctx); + r = SC_ERROR_INTERNAL; + goto err; + } + r = npa_external_authenticate(ctx, card, signature->data, signature->length); + +err: + if (cvc_cert) + CVC_CERT_free(cvc_cert); + if (my_ta_comp_eph_pubkey) + BUF_MEM_free(my_ta_comp_eph_pubkey); + if (nonce) + BUF_MEM_free(nonce); + if (signature) + BUF_MEM_free(signature); + + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); +} + static const char *MRZ_name = "MRZ"; static const char *PIN_name = "PIN"; static const char *PUK_name = "PUK"; @@ -1594,7 +1923,7 @@ npa_sm_pre_transmit(sc_card_t *card, const struct sm_ctx *ctx, long int llen; BUF_MEM *signature = NULL; unsigned char *sequence = NULL, *templates = NULL; - NPA_MSE_SET_AT_C *msesetat = NULL; + NPA_MSE_C *msesetat = NULL; const unsigned char *p; if (!card) @@ -1681,7 +2010,7 @@ npa_sm_pre_transmit(sc_card_t *card, const struct sm_ctx *ctx, len = add_tag(&sequence, 1, V_ASN1_SEQUENCE, V_ASN1_UNIVERSAL, apdu->data, apdu->datalen); p = sequence; - if (len < 0 || !d2i_NPA_MSE_SET_AT_C(&msesetat, &p, len)) { + if (len < 0 || !d2i_NPA_MSE_C(&msesetat, &p, len)) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not parse MSE:Set AT."); ssl_error(card->ctx); r = SC_ERROR_INTERNAL; @@ -1705,7 +2034,7 @@ npa_sm_pre_transmit(sc_card_t *card, const struct sm_ctx *ctx, * item template with the correct tag. Due to limitations of * OpenSSL it is not possible to *encode* an optional item template * (such as APDU_DISCRETIONARY_DATA_TEMPLATES) in an other item - * template (such as NPA_MSE_SET_AT_C). So what we have to do here + * template (such as NPA_MSE_C). So what we have to do here * is manually adding the correct tag to the saved * CVC_DISCRETIONARY_DATA_TEMPLATES. * See also openssl/crypto/asn1/tasn_dec.c:183