opensc/src/libopensc/cwa14890.c

2137 lines
63 KiB
C

/**
* cwa14890.c: Implementation of Secure Messaging according CWA-14890-1 and CWA-14890-2 standards.
*
* Copyright (C) 2010 Juan Antonio Martinez <jonsito@terra.es>
*
* This work is derived from many sources at OpenSC Project site,
* (see references) and the information made public by Spanish
* Direccion General de la Policia y de la Guardia Civil
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define __CWA14890_C__
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef ENABLE_OPENSSL /* empty file without openssl */
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "opensc.h"
#include "cardctl.h"
#include "internal.h"
#include <openssl/x509.h>
#include <openssl/des.h>
#include <openssl/rand.h>
#include "cwa-dnie.h"
#include "cwa14890.h"
/*********************** utility functions ************************/
/**
* Tool for create a string dump of a provided buffer.
*
* When buffer length is longer than 16384 bytes, output is cut
*
* @param buff Buffer to be printed
* @param len Buffer len
* @return a char buffer with data dump in hex+ascii format
*/
static char *cwa_hexdump(const u8 * buf, size_t len)
{
size_t j;
size_t count = 0;
static char res[16384];
memset(res, 0, sizeof(res));
len = MIN(len, sizeof(res));
for (count = 0; count < len; count += 16) {
size_t nitems = MIN(16, len - count);
for (j = 0; j < nitems; j++)
sprintf(res, "%s%02X ", res, 0xff & *(buf + count + j));
for (; j < 16; j++)
sprintf(res, "%s ", res);
for (j = 0; j < nitems; j++) {
char c = (char)*(buf + count + j);
sprintf(res, "%s%c", res, (isprint(c) ? c : '.'));
}
for (; j < 16; j++)
sprintf(res, "%s ", res);
sprintf(res, "%s\n", res);
}
return res;
}
/**
* Dump an APDU before SM translation.
*
* This is mainly for debugging purposes. programmer should disable
* this function in a production environment, as APDU will be shown
* in text-plain on debug traces
*
* @param card Pointer to card driver data structure
* @param apdu APDU to be encoded, or APDU response after decoded
* @param flag 0: APDU is to be encoded: 1; APDU decoded response
*/
static void cwa_trace_apdu(sc_card_t * card, sc_apdu_t * apdu, int flag)
{
char *buf = NULL;
/* set to 0 in production */
#if 1
if (!card || !card->ctx || !apdu)
return;
if (flag == 0) { /* apdu command */
if (apdu->datalen > 0) { /* apdu data to show */
buf = cwa_hexdump(apdu->data, apdu->datalen);
sc_log(card->ctx,
"\nAPDU before encode: ==================================================\nCLA: %02X INS: %02X P1: %02X P2: %02X Lc: %02X Le: %02X DATA: [%5u bytes]\n%s======================================================================\n",
apdu->cla, apdu->ins, apdu->p1, apdu->p2,
apdu->lc, apdu->le, apdu->datalen, buf);
} else { /* apdu data field is empty */
sc_log(card->ctx,
"\nAPDU before encode: ==================================================\nCLA: %02X INS: %02X P1: %02X P2: %02X Lc: %02X Le: %02X (NO DATA)\n======================================================================\n",
apdu->cla, apdu->ins, apdu->p1, apdu->p2,
apdu->lc, apdu->le);
}
} else { /* apdu response */
buf = cwa_hexdump(apdu->resp, apdu->resplen);
sc_log(card->ctx,
"\nAPDU response after decode: ==========================================\nSW1: %02X SW2: %02X RESP: [%5u bytes]\n%s======================================================================\n",
apdu->sw1, apdu->sw2, apdu->resplen, buf);
}
#endif
}
/**
* Increase send sequence counter SSC.
*
* @param card smart card info structure
* @param sm Secure Message session handling data structure
* @return SC_SUCCESS if ok; else error code
*
* TODO: to further study: what about using bignum arithmetics?
*/
static int cwa_increase_ssc(sc_card_t * card, cwa_sm_session_t * sm)
{
int n;
/* preliminary checks */
if (!card || !card->ctx )
return SC_ERROR_INVALID_ARGUMENTS;
if (!sm )
return SC_ERROR_SM_NOT_INITIALIZED;
LOG_FUNC_CALLED(card->ctx);
/* u8 arithmetic; exit loop if no carry */
sc_log(card->ctx, "Curr SSC: '%s'", sc_dump_hex(sm->ssc, 8));
for (n = 7; n >= 0; n--) {
sm->ssc[n]++;
if ((sm->ssc[n]) != 0x00)
break;
}
sc_log(card->ctx, "Next SSC: '%s'", sc_dump_hex(sm->ssc, 8));
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
/**
* ISO 7816 padding.
*
* Adds an 0x80 at the end of buffer and as many zeroes to get len
* multiple of 8
* Buffer must be long enougth to store additional bytes
*
* @param buffer where to compose data
* @param len pointer to buffer length
*/
static void cwa_iso7816_padding(u8 * buf, size_t * buflen)
{
buf[*buflen] = 0x80;
(*buflen)++;
for (; *buflen & 0x07; (*buflen)++)
buf[*buflen] = 0x00;
}
/**
* compose a BER-TLV data in provided buffer.
*
* Multybyte tag id are not supported
* Also multibyte id 0x84 is unhandled
*
* Notice that TLV is composed starting at offset lenght from
* the buffer. Consecutive calls to cwa_add_tlv, appends a new
* TLV at the end of the buffer
*
* @param card card info structure
* @param tag tag id
* @param len data length
* @param value data buffer
* @param out pointer to dest data
* @param outlen length of composed tlv data
* @return SC_SUCCESS if ok; else error
*/
static int cwa_compose_tlv(sc_card_t * card,
u8 tag,
size_t len, u8 * data, u8 ** out, size_t * outlen)
{
u8 *pt;
size_t size;
sc_context_t *ctx;
/* preliminary checks */
if (!card || !card->ctx || !out || !outlen)
return SC_ERROR_INVALID_ARGUMENTS;
/* comodity vars */
ctx = card->ctx;
LOG_FUNC_CALLED(ctx);
pt = *out;
size = *outlen;
/* assume tag id is not multibyte */
*(pt + size++) = tag;
/* evaluate tag length value according iso7816-4 sect 5.2.2 */
if (len < 0x80) {
*(pt + size++) = len;
} else if (len < 0x00000100) {
*(pt + size++) = 0x81;
*(pt + size++) = 0xff & len;
} else if (len < 0x00010000) {
*(pt + size++) = 0x82;
*(pt + size++) = 0xff & (len >> 8);
*(pt + size++) = 0xff & len;
} else if (len < 0x01000000) {
*(pt + size++) = 0x83;
*(pt + size++) = 0xff & (len >> 16);
*(pt + size++) = 0xff & (len >> 8);
*(pt + size++) = 0xff & len;
} else { /* do not handle tag length 0x84 */
LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
}
/* copy remaining data to buffer */
if (len != 0)
memcpy(pt + size, data, len);
size += len;
*outlen = size;
LOG_FUNC_RETURN(ctx, SC_SUCCESS);
}
/**
* Parse and APDU Response and extract specific BER-TLV data.
*
* NOTICE that iso7816 sect 5.2.2 states that Tag length may be 1 to n bytes
* length. In this code we'll assume allways tag lenght = 1 byte
*
* @param card card info structure
* @param data Buffer to look for tlv into
* @param datalen Buffer len
* @param tlv array of TLV structure to store results into
* @return SC_SUCCESS if OK; else error code
*/
static int cwa_parse_tlv(sc_card_t * card,
u8 * data, size_t datalen, cwa_tlv_t tlv_array[]
)
{
size_t n = 0;
size_t next = 0;
sc_context_t *ctx = NULL;
u8 *buffer = NULL;
/* preliminary checks */
if (!card || !card->ctx)
return SC_ERROR_INVALID_ARGUMENTS;
/* comodity vars */
ctx = card->ctx;
LOG_FUNC_CALLED(ctx);
if (!data || !tlv_array)
LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
/* create buffer and copy data into */
buffer = calloc(datalen, sizeof(u8));
if (!buffer)
LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY);
memcpy(buffer, data, datalen);
for (n = 0; n < datalen; n += next) {
cwa_tlv_t *tlv = NULL; /* pointer to TLV structure to store info */
size_t j = 2; /* TLV has at least two bytes */
switch (*(buffer + n)) {
case CWA_SM_PLAIN_TAG:
tlv = &tlv_array[0];
break; /* 0x81 Plain */
case CWA_SM_CRYPTO_TAG:
tlv = &tlv_array[1];
break; /* 0x87 Crypto */
case CWA_SM_MAC_TAG:
tlv = &tlv_array[2];
break; /* 0x8E MAC CC */
case CWA_SM_STATUS_TAG:
tlv = &tlv_array[3];
break; /* 0x99 Status */
default: /* CWA_SM_LE_TAG (0x97) is not valid here */
sc_log(ctx, "Invalid TLV Tag type: '0x%02X'",
*(buffer + n));
LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA);
}
tlv->buf = buffer + n;
tlv->tag = 0xff & *(buffer + n);
tlv->len = 0; /* temporary */
/* evaluate len and start of data */
switch (0xff & *(buffer + n + 1)) {
case 0x84:
tlv->len = (0xff & *(buffer + n + j++));
case 0x83:
tlv->len =
(tlv->len << 8) + (0xff & *(buffer + n + j++));
case 0x82:
tlv->len =
(tlv->len << 8) + (0xff & *(buffer + n + j++));
case 0x81:
tlv->len =
(tlv->len << 8) + (0xff & *(buffer + n + j++));
break;
/* case 0x80 is not standard, but official code uses it */
case 0x80:
tlv->len =
(tlv->len << 8) + (0xff & *(buffer + n + j++));
break;
default:
if ((*(buffer + n + 1) & 0xff) < 0x80) {
tlv->len = 0xff & *(buffer + n + 1);
} else {
sc_log(ctx, "Invalid tag length indicator: %d",
*(buffer + n + 1));
LOG_FUNC_RETURN(ctx, SC_ERROR_WRONG_LENGTH);
}
}
tlv->data = buffer + n + j;
tlv->buflen = j + tlv->len;;
sc_log(ctx, "Found Tag: '0x%02X': Length: '%d 'Value:\n%s",
tlv->tag, tlv->len, sc_dump_hex(tlv->data, tlv->len));
/* set index to next Tag to jump to */
next = tlv->buflen;
}
LOG_FUNC_RETURN(ctx, SC_SUCCESS); /* mark no error */
}
/*********************** authentication routines *******************/
/**
* Verify certificates provided by card.
*
* This routine uses Root CA public key data From Annex III of manual
* to verify intermediate CA icc certificate provided by card
* if verify sucess, then extract public keys from intermediate CA
* and verify icc certificate
*
* @param card pointer to sc_card_contex
* @param sub_ca_cert icc intermediate CA certificate readed from card
* @param icc_ca icc certificate from card
* @return SC_SUCCESS if verification is ok; else error code
*/
static int cwa_verify_icc_certificates(sc_card_t * card,
cwa_provider_t * provider,
X509 * sub_ca_cert, X509 * icc_cert)
{
char *msg;
int res = SC_SUCCESS;
EVP_PKEY *root_ca_key = NULL;
EVP_PKEY *sub_ca_key = NULL;
sc_context_t *ctx = NULL;
/* safety check */
if (!card || !card->ctx || !provider)
return SC_ERROR_INVALID_ARGUMENTS;
ctx = card->ctx;
LOG_FUNC_CALLED(ctx);
if (!sub_ca_cert || !icc_cert) /* check received arguments */
LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
/* retrieve root ca pkey from provider */
res = provider->cwa_get_root_ca_pubkey(card, &root_ca_key);
if (res != SC_SUCCESS) {
msg = "Cannot get root CA public key";
res = SC_ERROR_INTERNAL;
goto verify_icc_certificates_end;
}
/* verify sub_ca_cert against root_ca_key */
res = X509_verify(sub_ca_cert, root_ca_key);
if (!res) {
msg = "Cannot verify icc Sub-CA certificate";
res = SC_ERROR_SM_AUTHENTICATION_FAILED;
goto verify_icc_certificates_end;
}
/* extract sub_ca_key from sub_ca_cert */
sub_ca_key = X509_get_pubkey(sub_ca_cert);
/* verify icc_cert against sub_ca_key */
res = X509_verify(icc_cert, sub_ca_key);
if (!res) {
msg = "Cannot verify icc certificate";
res = SC_ERROR_SM_AUTHENTICATION_FAILED;
goto verify_icc_certificates_end;
}
/* arriving here means certificate verification success */
res = SC_SUCCESS;
verify_icc_certificates_end:
if (root_ca_key)
EVP_PKEY_free(root_ca_key);
if (sub_ca_key)
EVP_PKEY_free(sub_ca_key);
if (res != SC_SUCCESS)
sc_log(ctx, msg);
LOG_FUNC_RETURN(ctx, res);
}
/**
* Verify CVC certificates in SM establishment process.
*
* This is done by mean of 00 2A 00 AE
* (Perform Security Operation: Verify Certificate )
*
* @param card pointer to card data
* @param cert Certificate in CVC format
* @param len length of CVC certificate
* @return SC_SUCCESS if ok; else error code
*/
static int cwa_verify_cvc_certificate(sc_card_t * card,
const u8 * cert, size_t len)
{
sc_apdu_t apdu;
int result = SC_SUCCESS;
sc_context_t *ctx = NULL;
/* safety check */
if (!card || !card->ctx)
return SC_ERROR_INVALID_ARGUMENTS;
ctx = card->ctx;
LOG_FUNC_CALLED(ctx);
if (!cert || (len <= 0)) /* check received arguments */
LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
/* compose apdu for Perform Security Operation (Verify cert) cmd */
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x2A, 0x00, 0xAE);
apdu.data = cert;
apdu.datalen = len;
apdu.lc = len;
apdu.le = 0;
apdu.resplen = 0;
apdu.resp = NULL;
/* send composed apdu and parse result */
result = dnie_transmit_apdu(card, &apdu);
LOG_TEST_RET(ctx, result, "Verify CVC certificate failed");
result = sc_check_sw(card, apdu.sw1, apdu.sw2);
LOG_FUNC_RETURN(ctx, result);
}
/**
* Alternate implementation for set_security environment.
*
* Used to handle raw apdu data in set_security_env() on SM stblishment
* Standard set_securiy_env() method has sc_security_env->buffer limited
* to 8 bytes; so cannot send some of required SM commands.
*
* @param card pointer to card data
* @param p1 apdu P1 parameter
* @param p2 apdu P2 parameter
* @param buffer raw data to be inserted in apdu
* @param length size of buffer
* @return SC_SUCCESS if ok; else error code
*/
static int cwa_set_security_env(sc_card_t * card,
u8 p1, u8 p2, u8 * buffer, size_t length)
{
sc_apdu_t apdu;
int result = SC_SUCCESS;
sc_context_t *ctx = NULL;
/* safety check */
if (!card || !card->ctx)
return SC_ERROR_INVALID_ARGUMENTS;
ctx = card->ctx;
LOG_FUNC_CALLED(ctx);
if (!buffer || (length <= 0)) /* check received arguments */
LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
/* compose apdu for Manage Security Environment cmd */
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, p1, p2);
apdu.data = buffer;
apdu.datalen = length;
apdu.lc = length;
apdu.resp = NULL;
apdu.resplen = 0;
apdu.le = 0;
/* send composed apdu and parse result */
result = dnie_transmit_apdu(card, &apdu);
LOG_TEST_RET(ctx, result, "SM Set Security Environment failed");
result = sc_check_sw(card, apdu.sw1, apdu.sw2);
LOG_FUNC_RETURN(ctx, result);
}
/**
* SM internal authenticate.
*
* Internal (Card) authentication (let the card verify sent ifd certs)
*
* @param card pointer to card data
* @param sm secure message data pointer
* @param data data to be sent in apdu
* @param datalen length of data to send
* @return SC_SUCCESS if OK: else error code
*/
static int cwa_internal_auth(sc_card_t * card,
cwa_sm_status_t * sm, u8 * data, size_t datalen)
{
sc_apdu_t apdu;
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
int result = SC_SUCCESS;
sc_context_t *ctx = NULL;
/* safety check */
if (!card || !card->ctx)
return SC_ERROR_INVALID_ARGUMENTS;
ctx = card->ctx;
LOG_FUNC_CALLED(ctx);
if (!data || (datalen <= 0)) /* check received arguments */
LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
/* compose apdu for Internal Authenticate cmd */
sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x88, 0x00, 0x00);
apdu.data = data;
apdu.datalen = datalen;
apdu.lc = datalen;
apdu.le = 0x80; /* expected 1024 bits response */
apdu.resp = rbuf;
apdu.resplen = sizeof(rbuf);
/* send composed apdu and parse result */
result = dnie_transmit_apdu(card, &apdu);
LOG_TEST_RET(ctx, result, "SM internal auth failed");
result = sc_check_sw(card, apdu.sw1, apdu.sw2);
LOG_TEST_RET(ctx, result, "SM internal auth invalid response");
if (apdu.resplen != sizeof(sm->sig)) /* invalid number of bytes received */
LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED);
memcpy(sm->sig, apdu.resp, apdu.resplen); /* copy result to buffer */
LOG_FUNC_RETURN(ctx, SC_SUCCESS);
}
/**
* Compose signature data for external auth according CWA-14890.
*
* This code prepares data to be sent to ICC for external
* authentication procedure
*
* Store resulting data into sm->sig
*
* @param card pointer to st_card_t card data information
* @param icc_pubkey public key of card
* @param ifd_privkey private RSA key of ifd
* @param sn_icc card serial number
* @param sm pointer to cwa_internal_t data
* @return SC_SUCCESS if ok; else errorcode
*/
static int cwa_prepare_external_auth(sc_card_t * card,
RSA * icc_pubkey,
RSA * ifd_privkey,
u8 * sn_icc, cwa_sm_status_t * sm)
{
/* we have to compose following message:
data = E[PK.ICC.AUT](SIGMIN)
SIGMIN = min ( SIG, N.IFD-SIG )
SIG= DS[SK.IFD.AUT] (
0x6A || - padding according iso 9796-2
PRND2 || - (74 bytes) random data to make buffer 128 bytes length
Kifd || - (32 bytes)- ifd random generated key
sha1_hash(
PRND2 ||
Kifd ||
RND.ICC || - (8 bytes) response to get_challenge() cmd
SN.ICC - (8 bytes) serial number from get_serialnr() cmd
) ||
0xBC - iso 9796-2 padding
) - total: 128 bytes
then, we should encrypt with our private key and then with icc pub key
returning resulting data
*/
char *msg; /* to store error messages */
int res = SC_SUCCESS;
u8 *buf1; /* where to encrypt with icc pub key */
u8 *buf2; /* where to encrypt with ifd pub key */
u8 *buf3; /* where to compose message to be encrypted */
int len1, len2, len3;
u8 *sha_buf; /* to compose message to be sha'd */
u8 *sha_data; /* sha signature data */
BIGNUM *bn = NULL;
BIGNUM *bnsub = NULL;
BIGNUM *bnres = NULL;
sc_context_t *ctx = NULL;
/* safety check */
if (!card || !card->ctx)
return SC_ERROR_INVALID_ARGUMENTS;
ctx = card->ctx;
LOG_FUNC_CALLED(ctx);
/* check received arguments */
if (!icc_pubkey || !ifd_privkey || !sn_icc || !sm)
LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
buf1 = calloc(128, sizeof(u8));
buf2 = calloc(128, sizeof(u8));
buf3 = calloc(128, sizeof(u8));
sha_buf = calloc(74 + 32 + 8 + 8, sizeof(u8));
sha_data = calloc(SHA_DIGEST_LENGTH, sizeof(u8));
/* alloc() resources */
if (!buf1 || !buf2 || !buf3 || !sha_buf || !sha_data) {
msg = "prepare external auth: calloc error";
res = SC_ERROR_OUT_OF_MEMORY;
goto prepare_external_auth_end;
}
/* compose buffer data */
buf3[0] = 0x6A; /* iso padding */
RAND_bytes(buf3 + 1, 74); /* pRND */
RAND_bytes(sm->kifd, 32); /* Kifd */
memcpy(buf3 + 1 + 74, sm->kifd, 32); /* copy Kifd into buffer */
/* prepare data to be hashed */
memcpy(sha_buf, buf3 + 1, 74); /* copy pRND into sha_buf */
memcpy(sha_buf + 74, buf3 + 1 + 74, 32); /* copy kifd into sha_buf */
memcpy(sha_buf + 74 + 32, sm->rndicc, 8); /* copy 8 byte icc challenge */
memcpy(sha_buf + 74 + 32 + 8, sn_icc, 8); /* copy serialnr, 8 bytes */
SHA1(sha_buf, 74 + 32 + 8 + 8, sha_data);
/* copy hashed data into buffer */
memcpy(buf3 + 1 + 74 + 32, sha_data, SHA_DIGEST_LENGTH);
buf3[127] = 0xBC; /* iso padding */
/* encrypt with ifd private key */
len2 =
RSA_private_decrypt(128, buf3, buf2, ifd_privkey, RSA_NO_PADDING);
if (len2 < 0) {
msg = "Prepare external auth: ifd_privk encrypt failed";
res = SC_ERROR_SM_ENCRYPT_FAILED;
goto prepare_external_auth_end;
}
/* evaluate value of minsig and store into buf3 */
bn = BN_bin2bn(buf2, len2, NULL);
bnsub = BN_new();
if (!bn || !bnsub) {
msg = "Prepare external auth: BN creation failed";
res = SC_ERROR_INTERNAL;
goto prepare_external_auth_end;
}
res = BN_sub(bnsub, ifd_privkey->n, bn); /* eval N.IFD-SIG */
if (res == 0) { /* 1:success 0 fail */
msg = "Prepare external auth: BN sigmin evaluation failed";
res = SC_ERROR_INTERNAL;
goto prepare_external_auth_end;
}
bnres = (BN_cmp(bn, bnsub) < 0) ? bn : bnsub; /* choose min(SIG,N.IFD-SIG) */
if (BN_num_bytes(bnres) > 128) {
msg = "Prepare external auth: BN sigmin result is too big";
res = SC_ERROR_INTERNAL;
goto prepare_external_auth_end;
}
len3 = BN_bn2bin(bnres, buf3); /* convert result back into buf3 */
if (len3 <= 0) {
msg = "Prepare external auth: BN to buffer conversion failed";
res = SC_ERROR_INTERNAL;
goto prepare_external_auth_end;
}
/* re-encrypt result with icc public key */
len1 = RSA_public_encrypt(len3, buf3, buf1, icc_pubkey, RSA_NO_PADDING);
if (len1 <= 0) {
msg = "Prepare external auth: icc_pubk encrypt failed";
res = SC_ERROR_SM_ENCRYPT_FAILED;
goto prepare_external_auth_end;
}
/* process done: copy result into cwa_internal buffer and return success */
memcpy(sm->sig, buf1, len1);
res = SC_SUCCESS;
prepare_external_auth_end:
if (bn)
BN_free(bn);
if (bnsub)
BN_free(bnsub);
if (buf1) {
memset(buf1, 0, 128);
free(buf1);
}
if (buf2) {
memset(buf2, 0, 128);
free(buf2);
}
if (buf3) {
memset(buf3, 0, 128);
free(buf3);
}
if (sha_buf) {
memset(sha_buf, 0, 74 + 32 + 8 + 1 + 7);
free(sha_buf);
}
if (sha_data) {
memset(sha_data, 0, SHA_DIGEST_LENGTH);
free(sha_data);
}
if (res != SC_SUCCESS)
sc_log(ctx, msg);
LOG_FUNC_RETURN(ctx, res);
}
/**
* SM external authenticate.
*
* Perform external (IFD) authenticate procedure (8.4.1.2)
*
* @param data apdu signature content
* @param datalen signature length (128)
* @return SC_SUCCESS if OK: else error code
*/
static int cwa_external_auth(sc_card_t * card, cwa_sm_status_t * sm)
{
sc_apdu_t apdu;
int result = SC_SUCCESS;
sc_context_t *ctx = NULL;
/* safety check */
if (!card || !card->ctx)
return SC_ERROR_INVALID_ARGUMENTS;
ctx = card->ctx;
LOG_FUNC_CALLED(ctx);
/* compose apdu for External Authenticate cmd */
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x82, 0x00, 0x00);
apdu.data = sm->sig;
apdu.datalen = sizeof(sm->sig);
apdu.lc = sizeof(sm->sig);
apdu.le = 0;
apdu.resp = NULL;
apdu.resplen = 0;
/* send composed apdu and parse result */
result = dnie_transmit_apdu(card, &apdu);
LOG_TEST_RET(ctx, result, "SM external auth failed");
result = sc_check_sw(card, apdu.sw1, apdu.sw2);
LOG_TEST_RET(ctx, result, "SM external auth invalid response");
LOG_FUNC_RETURN(ctx, SC_SUCCESS);
}
/**
* SM creation of session keys.
*
* Compute Kenc,Kmac, and SSC and store it into sm data
*
* @param card pointer to sc_card_t data
* @param sm pointer to cwa_internal_t data
* @return SC_SUCCESS if ok; else error code
*/
static int cwa_compute_session_keys(sc_card_t * card, cwa_sm_status_t * sm)
{
char *msg = NULL;
int n = 0;
int res = SC_SUCCESS;
u8 *kseed; /* to compose kifd ^ kicc */
u8 *data; /* to compose kenc and kmac to be hashed */
u8 *sha_data; /* to store hash result */
u8 kenc[4] = { 0x00, 0x00, 0x00, 0x01 };
u8 kmac[4] = { 0x00, 0x00, 0x00, 0x02 };
sc_context_t *ctx = NULL;
/* safety check */
if (!card || !card->ctx)
return SC_ERROR_INVALID_ARGUMENTS;
ctx = card->ctx;
LOG_FUNC_CALLED(ctx);
/* Just a literal transcription of cwa14890-1 sections 8.7.2 to 8.9 */
kseed = calloc(32, sizeof(u8));
data = calloc(32 + 4, sizeof(u8));
sha_data = calloc(SHA_DIGEST_LENGTH, sizeof(u8));
if (!kseed || !data || !sha_data) {
msg = "Compute Session Keys: calloc() failed";
res = SC_ERROR_OUT_OF_MEMORY;
goto compute_session_keys_end;
}
/* compose kseed (cwa-14890-1 sect 8.7.2) */
for (n = 0; n < 32; n++)
*(kseed + n) = sm->kicc[n] ^ sm->kifd[n];
/* evaluate kenc (cwa-14890-1 sect 8.8) */
memcpy(data, kseed, 32);
memcpy(data + 32, kenc, 4);
SHA1(data, 32 + 4, sha_data);
memcpy(sm->session.kenc, sha_data, 16); /* kenc=16 fsb sha((kifd^kicc)||00000001) */
/* evaluate kmac */
memset(data, 0, 32 + 4);
memset(sha_data, 0, SHA_DIGEST_LENGTH); /* clear buffers */
memcpy(data, kseed, 32);
memcpy(data + 32, kmac, 4);
SHA1(data, 32 + 4, sha_data);
memcpy(sm->session.kmac, sha_data, 16); /* kmac=16 fsb sha((kifd^kicc)||00000002) */
/* evaluate send sequence counter (cwa-14890-1 sect 8.9 & 9.6 */
memcpy(sm->session.ssc, sm->rndicc + 4, 4); /* 4 least significant bytes of rndicc */
memcpy(sm->session.ssc + 4, sm->rndifd + 4, 4); /* 4 least significant bytes of rndifd */
/* arriving here means process ok */
res = SC_SUCCESS;
compute_session_keys_end:
if (kseed) {
memset(kseed, 0, 32);
free(kseed);
}
if (data) {
memset(data, 0, 32 + 4);
free(data);
}
if (sha_data) {
memset(sha_data, 0, SHA_DIGEST_LENGTH);
free(sha_data);
}
if (res != SC_SUCCESS)
sc_log(ctx, msg);
else {
sc_log(ctx, "Kenc: %s", sc_dump_hex(sm->session.kenc, 16));
sc_log(ctx, "Kmac: %s", sc_dump_hex(sm->session.kmac, 16));
sc_log(ctx, "SSC: %s", sc_dump_hex(sm->session.ssc, 8));
}
LOG_FUNC_RETURN(ctx, res);
}
/*
* Compare signature for internal auth procedure.
*
* @param data Received data to be checked
* @param dlen data length
* @param expected results
* @return SC_SUCCESS or error code
*/
static int cwa_compare_signature(u8 * data, size_t dlen, u8 * ifd_data)
{
u8 *buf = calloc(74 + 32 + 32, sizeof(u8));
u8 *sha = calloc(SHA_DIGEST_LENGTH, sizeof(u8));
int res = SC_SUCCESS;
if (!buf || !sha) {
res = SC_ERROR_OUT_OF_MEMORY;
goto compare_signature_end;
}
res = SC_ERROR_INVALID_DATA;
if (dlen != 128)
goto compare_signature_end; /* check length */
if (data[0] != 0x6a)
goto compare_signature_end; /* iso 9796-2 padding */
if (data[127] != 0xBC)
goto compare_signature_end; /* iso 9796-2 padding */
memcpy(buf, data + 1, 74 + 32);
memcpy(buf + 74 + 32, ifd_data, 16);
SHA1(buf, 74 + 32 + 16, sha);
if (memcmp(data + 127 - SHA_DIGEST_LENGTH, sha, SHA_DIGEST_LENGTH) == 0)
res = SC_SUCCESS;
compare_signature_end:
if (buf)
free(buf);
if (sha)
free(sha);
return res;
}
/**
* check the result of internal_authenticate operation.
*
* Checks icc received data from internal auth procedure against
* expected results
*
* @param card Pointer to sc_card_t data
* @param icc_pubkey icc public key
* @param ifd_privkey ifd private key
* @param ifdbuf buffer containing ( RND.IFD || SN.IFD )
* @param ifdlen buffer length; should be 16
* @param sm secure messaging internal data
* @return SC_SUCCESS if ok; else error code
*/
static int cwa_verify_internal_auth(sc_card_t * card,
RSA * icc_pubkey,
RSA * ifd_privkey,
u8 * ifdbuf,
size_t ifdlen, cwa_sm_status_t * sm)
{
int res = SC_SUCCESS;
char *msg = NULL;
u8 *buf1 = NULL; /* to decrypt with our private key */
u8 *buf2 = NULL; /* to try SIGNUM==SIG */
u8 *buf3 = NULL; /* to try SIGNUM==N.ICC-SIG */
int len1 = 0;
int len2 = 0;
int len3 = 0;
BIGNUM *bn = NULL;
BIGNUM *sigbn = NULL;
sc_context_t *ctx = NULL;
if (!card || !card->ctx)
return SC_ERROR_INVALID_ARGUMENTS;
ctx = card->ctx;
LOG_FUNC_CALLED(ctx);
if (!ifdbuf || (ifdlen != 16)) {
res = SC_ERROR_INVALID_ARGUMENTS;
msg = "Null buffers received as parameters";
goto verify_internal_done;
}
if (!icc_pubkey || !ifd_privkey) {
res = SC_ERROR_SM_NO_SESSION_KEYS;
msg = "Either provided icc_pubk or ifd_privk are null";
goto verify_internal_done;
}
buf1 = (u8 *) calloc(128, sizeof(u8)); /* 128: RSA key len in bytes */
buf2 = (u8 *) calloc(128, sizeof(u8));
buf3 = (u8 *) calloc(128, sizeof(u8));
if (!buf1 || !buf2 || !buf3) {
msg = "Verify Signature: calloc() error";
res = SC_ERROR_OUT_OF_MEMORY;
goto verify_internal_done;
}
/*
We have received data with this format:
sigbuf = E[PK.IFD.AUT](SIGMIN)
SIGMIN = min ( SIG, N.ICC-SIG )
SIG= DS[SK.ICC.AUT] (
0x6A ||
PRND1 ||
Kicc ||
sha1_hash(PRND1 || Kicc || RND.IFD || SN.IFD) ||
0xBC
)
So we should reverse the process and try to get valid results
*/
/* decrypt data with our ifd priv key */
len1 =
RSA_private_decrypt(sizeof(sm->sig), sm->sig, buf1, ifd_privkey,
RSA_NO_PADDING);
if (len1 <= 0) {
msg = "Verify Signature: decrypt with ifd privk failed";
res = SC_ERROR_SM_ENCRYPT_FAILED;
goto verify_internal_done;
}
/* OK: now we have SIGMIN in buf1 */
/* check if SIGMIN data matches SIG or N.ICC-SIG */
/* evaluate DS[SK.ICC.AUTH](SIG) trying to decrypt with icc pubk */
len3 = RSA_public_encrypt(len1, buf1, buf3, icc_pubkey, RSA_NO_PADDING);
if (len3 <= 0)
goto verify_nicc_sig; /* evaluate N.ICC-SIG and retry */
res = cwa_compare_signature(buf3, len3, ifdbuf);
if (res == SC_SUCCESS)
goto verify_internal_ok;
verify_nicc_sig:
/*
* Arriving here means need to evaluate N.ICC-SIG
* So convert buffers to bignums to operate
*/
bn = BN_bin2bn(buf1, len1, NULL); /* create BN data */
sigbn = BN_new();
if (!bn || !sigbn) {
msg = "Verify Signature: cannot bignums creation error";
res = SC_ERROR_OUT_OF_MEMORY;
goto verify_internal_done;
}
res = BN_sub(sigbn, icc_pubkey->n, bn); /* eval N.ICC-SIG */
if (!res) {
msg = "Verify Signature: evaluation of N.ICC-SIG failed";
res = SC_ERROR_INTERNAL;
goto verify_internal_done;
}
len2 = BN_bn2bin(sigbn, buf2); /* copy result to buffer */
if (len2 <= 0) {
msg = "Verify Signature: cannot conver bignum to buffer";
res = SC_ERROR_INTERNAL;
goto verify_internal_done;
}
/* ok: check again with new data */
/* evaluate DS[SK.ICC.AUTH](I.ICC-SIG) trying to decrypt with icc pubk */
len3 = RSA_public_encrypt(len2, buf2, buf3, icc_pubkey, RSA_NO_PADDING);
if (len3 <= 0) {
msg = "Verify Signature: cannot get valid SIG data";
res = SC_ERROR_INVALID_DATA;
goto verify_internal_done;
}
res = cwa_compare_signature(buf3, len3, ifdbuf);
if (res != SC_SUCCESS) {
msg = "Verify Signature: cannot get valid SIG data";
res = SC_ERROR_INVALID_DATA;
goto verify_internal_done;
}
/* arriving here means OK: complete data structures */
verify_internal_ok:
memcpy(sm->kicc, buf3 + 1 + 74, 32); /* extract Kicc from buf3 */
res = SC_SUCCESS;
verify_internal_done:
if (buf1)
free(buf1);
if (buf2)
free(buf2);
if (buf3)
free(buf3);
if (bn)
BN_free(bn);
if (sigbn)
BN_free(sigbn);
if (res != SC_SUCCESS)
sc_log(ctx, msg);
LOG_FUNC_RETURN(ctx, res);
}
/**
* Create Secure Messaging channel.
*
* This is the main entry point for CWA14890 SM chanel creation.
* It closely follows cwa standard, with a minor modification:
* - ICC serial number is taken at the begining of SM creation
* - ICC and IFD certificate agreement process is reversed, to allow
* card to retain key references on further proccess (this behavior
* is also defined in standard)
*
* Based on Several documents:
* - "Understanding the DNIe"
* - "Manual de comandos del DNIe"
* - ISO7816-4 and CWA14890-{1,2}
*
* @param card card info structure
* @param provider cwa14890 info provider
* @param flag requested init method ( OFF, COLD, WARM )
* @return SC_SUCCESS if OK; else error code
*/
int cwa_create_secure_channel(sc_card_t * card,
cwa_provider_t * provider, int flag)
{
u8 *cert;
size_t certlen;
int res = SC_SUCCESS;
char *msg = "Success";
u8 *sn_icc;
/* data to get and parse certificates */
X509 *icc_cert = NULL;
X509 *ca_cert = NULL;
EVP_PKEY *icc_pubkey = NULL;
EVP_PKEY *ifd_privkey = NULL;
sc_context_t *ctx = NULL;
cwa_sm_status_t *sm = NULL;
/* several buffer and buffer pointers */
u8 *buffer;
size_t bufferlen;
u8 *tlv = NULL; /* buffer to compose TLV messages */
size_t tlvlen = 0;
u8 *rndbuf=NULL;
/* preliminary checks */
if (!card || !card->ctx )
return SC_ERROR_INVALID_ARGUMENTS;
if (!provider)
return SC_ERROR_SM_NOT_INITIALIZED;
/* comodity vars */
ctx = card->ctx;
sm = &(provider->status);
LOG_FUNC_CALLED(ctx);
/* check requested initialization method */
switch (flag) {
case CWA_SM_OFF: /* disable SM */
provider->status.session.state = CWA_SM_NONE; /* just mark channel inactive */
sc_log(ctx, "Setting CWA SM status to none");
LOG_FUNC_RETURN(ctx, SC_SUCCESS);
case CWA_SM_WARM: /* only initialize if not already done */
if (provider->status.session.state != CWA_SM_NONE) {
sc_log(ctx,
"Warm CWA SM requested: already in SM state");
LOG_FUNC_RETURN(ctx, SC_SUCCESS);
}
case CWA_SM_COLD: /* force sm initialization process */
sc_log(ctx, "CWA SM initialization requested");
break;
default:
sc_log(ctx, "Invalid provided SM initialization flag");
LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
}
/* OK: lets start process */
/* reset card (warm reset, do not unpower card) */
sc_log(ctx, "Resseting card");
sc_reset(card, 0);
/* mark SM status as in progress */
provider->status.session.state = CWA_SM_INPROGRESS;
/* call provider pre-operation method */
sc_log(ctx, "CreateSecureChannel pre-operations");
if (provider->cwa_create_pre_ops) {
res = provider->cwa_create_pre_ops(card, provider);
if (res != SC_SUCCESS) {
msg = "Create SM: provider pre_ops() failed";
sc_log(ctx, msg);
goto csc_end;
}
}
/* retrieve icc serial number */
sc_log(ctx, "Retrieve ICC serial number");
if (provider->cwa_get_sn_icc) {
res = provider->cwa_get_sn_icc(card, &sn_icc);
if (res != SC_SUCCESS) {
msg = "Retrieve ICC failed";
sc_log(ctx, msg);
goto csc_end;
}
} else {
msg = "Don't know how to obtain ICC serial number";
sc_log(ctx, msg);
res = SC_ERROR_INTERNAL;
goto csc_end;
}
/*
* Notice that this code inverts ICC and IFD certificate standard
* checking sequence.
*/
/* Read Intermediate CA from card */
if (!provider->cwa_get_icc_intermediate_ca_cert) {
sc_log(ctx,
"Step 8.4.1.6: Skip Retrieveing ICC intermediate CA");
ca_cert = NULL;
} else {
sc_log(ctx, "Step 8.4.1.7: Retrieving ICC intermediate CA");
res =
provider->cwa_get_icc_intermediate_ca_cert(card, &ca_cert);
if (res != SC_SUCCESS) {
msg =
"Cannot get ICC intermediate CA certificate from provider";
goto csc_end;
}
}
/* Read ICC certificate from card */
sc_log(ctx, "Step 8.4.1.8: Retrieve ICC certificate");
res = provider->cwa_get_icc_cert(card, &icc_cert);
if (res != SC_SUCCESS) {
msg = "Cannot get ICC certificate from provider";
goto csc_end;
}
/* Verify icc Card certificate chain */
/* Notice that Some implementations doesn't verify cert chain
* but simply verifies that icc_cert is a valid certificate */
if (ca_cert) {
sc_log(ctx, "Verifying ICC certificate chain");
res =
cwa_verify_icc_certificates(card, provider, ca_cert,
icc_cert);
if (res != SC_SUCCESS) {
res = SC_ERROR_SM_AUTHENTICATION_FAILED;
msg = "Icc Certificates verification failed";
goto csc_end;
}
} else {
sc_log(ctx, "Cannot verify Certificate chain. skip step");
}
/* Extract public key from ICC certificate */
icc_pubkey = X509_get_pubkey(icc_cert);
/* Select Root CA in card for ifd certificate verification */
sc_log(ctx,
"Step 8.4.1.2: Select Root CA in card for IFD cert verification");
res = provider->cwa_get_root_ca_pubkey_ref(card, &buffer, &bufferlen);
if (res != SC_SUCCESS) {
msg = "Cannot get Root CA key reference from provider";
goto csc_end;
}
tlvlen = 0;
tlv = calloc(10 + bufferlen, sizeof(u8));
if (!tlv) {
msg = "calloc error";
res = SC_ERROR_OUT_OF_MEMORY;
goto csc_end;
}
res = cwa_compose_tlv(card, 0x83, bufferlen, buffer, &tlv, &tlvlen);
if (res != SC_SUCCESS) {
msg = "Cannot compose tlv for setting Root CA key reference";
goto csc_end;
}
res = cwa_set_security_env(card, 0x81, 0xB6, tlv, tlvlen);
if (res != SC_SUCCESS) {
msg = "Select Root CA key ref failed";
goto csc_end;
}
/* Send IFD intermediate CA in CVC format C_CV_CA */
sc_log(ctx,
"Step 8.4.1.3: Send CVC IFD intermediate CA Cert for ICC verification");
res = provider->cwa_get_cvc_ca_cert(card, &cert, &certlen);
if (res != SC_SUCCESS) {
msg = "Get CVC CA cert from provider failed";
goto csc_end;
}
res = cwa_verify_cvc_certificate(card, cert, certlen);
if (res != SC_SUCCESS) {
msg = "Verify CVC CA failed";
goto csc_end;
}
/* select public key reference for sent IFD intermediate CA certificate */
sc_log(ctx,
"Step 8.4.1.4: Select Intermediate CA pubkey ref for ICC verification");
res =
provider->cwa_get_intermediate_ca_pubkey_ref(card, &buffer,
&bufferlen);
if (res != SC_SUCCESS) {
msg = "Cannot get intermediate CA key reference from provider";
goto csc_end;
}
tlvlen = 0;
free(tlv);
tlv = calloc(10 + bufferlen, sizeof(u8));
if (!tlv) {
msg = "calloc error";
res = SC_ERROR_OUT_OF_MEMORY;
goto csc_end;
}
res = cwa_compose_tlv(card, 0x83, bufferlen, buffer, &tlv, &tlvlen);
if (res != SC_SUCCESS) {
msg =
"Cannot compose tlv for setting intermeditate CA key reference";
goto csc_end;
}
res = cwa_set_security_env(card, 0x81, 0xB6, tlv, tlvlen);
if (res != SC_SUCCESS) {
msg = "Select CVC CA pubk failed";
goto csc_end;
}
/* Send IFD certiticate in CVC format C_CV_IFD */
sc_log(ctx,
"Step 8.4.1.5: Send CVC IFD Certificate for ICC verification");
res = provider->cwa_get_cvc_ifd_cert(card, &cert, &certlen);
if (res != SC_SUCCESS) {
msg = "Get CVC IFD cert from provider failed";
goto csc_end;
}
res = cwa_verify_cvc_certificate(card, cert, certlen);
if (res != SC_SUCCESS) {
msg = "Verify CVC IFD failed";
goto csc_end;
}
/* remember that this code changes IFD and ICC Cert verification steps */
/* select public key of ifd certificate and icc private key */
sc_log(ctx,
"Step 8.4.1.9: Send IFD pubk and ICC privk key references for Internal Auth");
res = provider->cwa_get_ifd_pubkey_ref(card, &buffer, &bufferlen);
if (res != SC_SUCCESS) {
msg = "Cannot get ifd public key reference from provider";
goto csc_end;
}
tlvlen = 0;
free(tlv);
tlv = calloc(10 + bufferlen, sizeof(u8));
if (!tlv) {
msg = "calloc error";
res = SC_ERROR_OUT_OF_MEMORY;
goto csc_end;
}
res = cwa_compose_tlv(card, 0x83, bufferlen, buffer, &tlv, &tlvlen);
if (res != SC_SUCCESS) {
msg = "Cannot compose tlv for setting ifd pubkey reference";
goto csc_end;
}
res = provider->cwa_get_icc_privkey_ref(card, &buffer, &bufferlen);
if (res != SC_SUCCESS) {
msg = "Cannot get icc private key reference from provider";
goto csc_end;
}
/* add this tlv to old one; do not call calloc */
res = cwa_compose_tlv(card, 0x84, bufferlen, buffer, &tlv, &tlvlen);
if (res != SC_SUCCESS) {
msg = "Cannot compose tlv for setting ifd pubkey reference";
goto csc_end;
}
res = cwa_set_security_env(card, 0xC1, 0xA4, tlv, tlvlen);
if (res != SC_SUCCESS) {
msg = "Select CVC IFD pubk failed";
goto csc_end;
}
/* Internal (Card) authentication (let the card verify sent ifd certs)
SN.IFD equals 8 lsb bytes of ifd.pubk ref according cwa14890 sec 8.4.1 */
sc_log(ctx, "Step 8.4.1.10: Perform Internal authentication");
res = provider->cwa_get_sn_ifd(card, &buffer);
if (res != SC_SUCCESS) {
msg = "Cannot get ifd serial number from provider";
goto csc_end;
}
rndbuf = calloc(8 /*RND.IFD */ + 8 /*SN.IFD */ , sizeof(u8));
if (!rndbuf) {
msg = "Cannot calloc for RND.IFD+SN.IFD";
res = SC_ERROR_OUT_OF_MEMORY;
goto csc_end;
}
RAND_bytes(sm->rndifd, 8); /* generate 8 random bytes */
memcpy(rndbuf, sm->rndifd, 8); /* insert RND.IFD into rndbuf */
memcpy(rndbuf + 8, buffer, 8); /* insert SN.IFD into rndbuf */
res = cwa_internal_auth(card, sm, rndbuf, 16);
if (res != SC_SUCCESS) {
msg = "Internal auth cmd failed";
goto csc_end;
}
/* retrieve ifd private key from provider */
res = provider->cwa_get_ifd_privkey(card, &ifd_privkey);
if (res != SC_SUCCESS) {
msg = "Cannot retrieve IFD private key from provider";
res = SC_ERROR_SM_NO_SESSION_KEYS;
goto csc_end;
}
/* verify received signature */
sc_log(ctx, "Verify Internal Auth command response");
res = cwa_verify_internal_auth(card, icc_pubkey->pkey.rsa, /* evaluated icc public key */
ifd_privkey->pkey.rsa, /* evaluated from DGP's Manual Annex 3 Data */
rndbuf, /* RND.IFD || SN.IFD */
16, /* rndbuf length; should be 16 */
sm /* sm data */
);
if (res != SC_SUCCESS) {
msg = "Internal Auth Verify failed";
goto csc_end;
}
/* get challenge: retrieve 8 random bytes from card */
sc_log(ctx, "Step 8.4.1.11: Prepare External Auth: Get Challenge");
res = card->ops->get_challenge(card, sm->rndicc, sizeof(sm->rndicc));
if (res != SC_SUCCESS) {
msg = "Get Challenge failed";
goto csc_end;
}
/* compose signature data for external auth */
res = cwa_prepare_external_auth(card,
icc_pubkey->pkey.rsa,
ifd_privkey->pkey.rsa, sn_icc, sm);
if (res != SC_SUCCESS) {
msg = "Prepare external auth failed";
goto csc_end;
}
/* External (IFD) authentication */
sc_log(ctx, "Step 8.4.1.12: Perform External (IFD) Authentication");
res = cwa_external_auth(card, sm);
if (res != SC_SUCCESS) {
msg = "External auth cmd failed";
goto csc_end;
}
/* Session key generation */
sc_log(ctx, "Step 8.4.2: Compute Session Keys");
res = cwa_compute_session_keys(card, sm);
if (res != SC_SUCCESS) {
msg = "Session Key generation failed";
goto csc_end;
}
/* call provider post-operation method */
sc_log(ctx, "CreateSecureChannel post-operations");
if (provider->cwa_create_post_ops) {
res = provider->cwa_create_post_ops(card, provider);
if (res != SC_SUCCESS) {
sc_log(ctx, "Create SM: provider post_ops() failed");
goto csc_end;
}
}
/* arriving here means ok: cleanup */
res = SC_SUCCESS;
csc_end:
if (icc_pubkey)
EVP_PKEY_free(icc_pubkey);
if (ifd_privkey)
EVP_PKEY_free(ifd_privkey);
/* setup SM state according result */
if (res != SC_SUCCESS) {
sc_log(ctx, msg);
provider->status.session.state = CWA_SM_NONE;
} else {
provider->status.session.state = CWA_SM_ACTIVE;
}
LOG_FUNC_RETURN(ctx, res);
}
/******************* SM internal APDU encoding / decoding functions ******/
/**
* Encode an APDU.
*
* Calling this functions means that It's has been verified
* That source apdu needs encoding
* Based on section 9 of CWA-14890 and Sect 6 of iso7816-4 standards
* And DNIe's manual
*
* @param card card info structure
* @param sm Secure Messaging state information
* @param from APDU to be encoded
* @param to where to store encoded apdu
* @return SC_SUCCESS if ok; else error code
*/
int cwa_encode_apdu(sc_card_t * card,
cwa_provider_t * provider, sc_apdu_t * from, sc_apdu_t * to)
{
u8 *apdubuf; /* to store resulting apdu */
size_t apdulen;
u8 *ccbuf; /* where to store data to eval cryptographic checksum CC */
size_t cclen = 0;
u8 macbuf[8]; /* to store and compute CC */
DES_key_schedule k1;
DES_key_schedule k2;
char *msg = NULL;
size_t i, j; /* for xor loops */
int res = SC_SUCCESS;
sc_context_t *ctx = NULL;
cwa_sm_session_t *sm_session = NULL;
u8 *msgbuf = NULL; /* to encrypt apdu data */
u8 *cryptbuf = NULL;
/* reserve extra bytes for padding and tlv header */
msgbuf = calloc(12 + from->lc, sizeof(u8)); /* to encrypt apdu data */
cryptbuf = calloc(12 + from->lc, sizeof(u8));
/* mandatory check */
if (!card || !card->ctx || !provider)
return SC_ERROR_INVALID_ARGUMENTS;
ctx = card->ctx;
sm_session = &(provider->status.session);
LOG_FUNC_CALLED(ctx);
/* check remaining arguments */
if (!from || !to || !sm_session)
LOG_FUNC_RETURN(ctx, SC_ERROR_SM_NOT_INITIALIZED);
if (sm_session->state != CWA_SM_ACTIVE)
LOG_FUNC_RETURN(ctx, SC_ERROR_SM_INVALID_LEVEL);
if (!msgbuf || !cryptbuf)
LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY);
/* check if APDU is already encoded */
if ((from->cla & 0x0C) != 0) {
memcpy(to, from, sizeof(sc_apdu_t));
return SC_SUCCESS; /* already encoded */
}
if (from->ins == 0xC0) {
memcpy(to, from, sizeof(sc_apdu_t));
return SC_SUCCESS; /* dont encode GET Response cmd */
}
#if 0
/* For testing results according DNIe manual */
u8 kenc[16] =
{ 0x59, 0x8f, 0x26, 0xe3, 0x6e, 0x11, 0xa8, 0xec, 0x14, 0xb8, 0x1e,
0x19, 0xbd, 0xa2, 0x23, 0xca };
u8 kmac[16] =
{ 0x5d, 0xe2, 0x93, 0x9a, 0x1e, 0xa0, 0x3a, 0x93, 0x0b, 0x88, 0x20,
0x6d, 0x8f, 0x73, 0xe8, 0xa7 };
u8 ssc[8] = { 0xd3, 0x1a, 0xc8, 0xec, 0x7b, 0xa0, 0xfe, 0x74 };
memcpy(sm->kenc, kenc, 16);
memcpy(sm->kmac, kmac, 16);
memcpy(sm->ssc, ssc, 16);
from->le = 0;
/* para debugging end */
#endif
/* call provider pre-operation method */
if (provider->cwa_encode_pre_ops) {
res = provider->cwa_encode_pre_ops(card, provider, from, to);
if (res != SC_SUCCESS) {
msg = "Encode APDU: provider pre_ops() failed";
goto encode_end;
}
}
/* trace APDU before encoding process */
cwa_trace_apdu(card, from, 0);
/* reserve enougth space for apdulen+tlv bytes
* to-be-crypted buffer and result apdu buffer */
apdubuf =
calloc(MAX(SC_MAX_APDU_BUFFER_SIZE, 20 + from->datalen),
sizeof(u8));
ccbuf =
calloc(MAX(SC_MAX_APDU_BUFFER_SIZE, 20 + from->datalen),
sizeof(u8));
if (!apdubuf || !ccbuf)
LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY);
/* set up data on destination apdu */
to->cse = SC_APDU_CASE_3_SHORT;
to->cla = from->cla | 0x0C; /* mark apdu as encoded */
to->ins = from->ins;
to->p1 = from->p1;
to->p2 = from->p2;
to->le = from->le;
to->lc = 0; /* to be evaluated */
/* fill buffer with header info */
*(ccbuf + cclen++) = to->cla;
*(ccbuf + cclen++) = to->ins;
*(ccbuf + cclen++) = to->p1;
*(ccbuf + cclen++) = to->p2;
cwa_iso7816_padding(ccbuf, &cclen); /* pad header (4 bytes pad) */
/* if no data, skip data encryption step */
if (from->lc != 0) {
size_t dlen = from->lc;
/* prepare keys */
DES_cblock iv = { 0, 0, 0, 0, 0, 0, 0, 0 };
DES_set_key_unchecked((const_DES_cblock *) & (sm_session->kenc[0]),
&k1);
DES_set_key_unchecked((const_DES_cblock *) & (sm_session->kenc[8]),
&k2);
/* pad message */
memcpy(msgbuf, from->data, dlen);
cwa_iso7816_padding(msgbuf, &dlen);
/* start kriptbuff with iso padding indicator */
*cryptbuf = 0x01;
/* aply TDES + CBC with kenc and iv=(0,..,0) */
DES_ede3_cbc_encrypt(msgbuf, cryptbuf + 1, dlen, &k1, &k2, &k1,
&iv, DES_ENCRYPT);
/* compose data TLV and add to result buffer */
res =
cwa_compose_tlv(card, 0x87, dlen + 1, cryptbuf, &ccbuf,
&cclen);
if (res != SC_SUCCESS) {
msg = "Error in compose tag 8x87 TLV";
goto encode_end;
}
}
/* if le byte is declared, compose and add Le TLV */
/* TODO: study why original driver checks for le>=256? */
if (from->le > 0) {
u8 le = 0xff & from->le;
res = cwa_compose_tlv(card, 0x97, 1, &le, &ccbuf, &cclen);
if (res != SC_SUCCESS) {
msg = "Encode APDU compose_tlv(0x97) failed";
goto encode_end;
}
}
/* copy current data to apdu buffer (skip header and header padding) */
memcpy(apdubuf, ccbuf + 8, cclen - 8);
apdulen = cclen - 8;
/* pad again ccbuffer to compute CC */
cwa_iso7816_padding(ccbuf, &cclen);
/* sc_log(ctx,"data to compose mac: %s",sc_dump_hex(ccbuf,cclen)); */
/* compute MAC Cryptographic Checksum using kmac and increased SSC */
res = cwa_increase_ssc(card, sm_session); /* increase send sequence counter */
if (res != SC_SUCCESS) {
msg = "Error in computing SSC";
goto encode_end;
}
/* set up keys for mac computing */
DES_set_key_unchecked((const_DES_cblock *) & (sm_session->kmac[0]),&k1);
DES_set_key_unchecked((const_DES_cblock *) & (sm_session->kmac[8]),&k2);
memcpy(macbuf, sm_session->ssc, 8); /* start with computed SSC */
for (i = 0; i < cclen; i += 8) { /* divide data in 8 byte blocks */
/* compute DES */
DES_ecb_encrypt((const_DES_cblock *) macbuf,
(DES_cblock *) macbuf, &k1, DES_ENCRYPT);
/* XOR with next data and repeat */
for (j = 0; j < 8; j++)
macbuf[j] ^= ccbuf[i + j];
}
/* and apply 3DES to result */
DES_ecb2_encrypt((const_DES_cblock *) macbuf, (DES_cblock *) macbuf,
&k1, &k2, DES_ENCRYPT);
/* compose and add computed MAC TLV to result buffer */
res = cwa_compose_tlv(card, 0x8E, 4, macbuf, &apdubuf, &apdulen);
if (res != SC_SUCCESS) {
msg = "Encode APDU compose_tlv(0x87) failed";
goto encode_end;
}
/* rewrite resulting header */
to->lc = apdulen;
to->data = apdubuf;
to->datalen = apdulen;
/* call provider post-operation method */
if (provider->cwa_encode_post_ops) {
res = provider->cwa_encode_post_ops(card, provider, from, to);
if (res != SC_SUCCESS) {
msg = "Encode APDU: provider post_ops() failed";
goto encode_end;
}
}
/* that's all folks */
res = SC_SUCCESS;
encode_end:
if (msg)
sc_log(ctx, msg);
LOG_FUNC_RETURN(ctx, res);
}
/**
* Decode an APDU response.
*
* Calling this functions means that It's has been verified
* That apdu response comes in TLV encoded format and needs decoding
* Based on section 9 of CWA-14890 and Sect 6 of iso7816-4 standards
* And DNIe's manual
*
* @param card card info structure
* @param sm Secure Messaging state information
* @param from APDU with response to be decoded
* @param to where to store decoded apdu
* @return SC_SUCCESS if ok; else error code
*/
int cwa_decode_response(sc_card_t * card,
cwa_provider_t * provider,
sc_apdu_t * from, sc_apdu_t * to)
{
size_t i, j;
cwa_tlv_t tlv_array[4];
cwa_tlv_t *p_tlv = &tlv_array[0]; /* to store plain data (Tag 0x81) */
cwa_tlv_t *e_tlv = &tlv_array[1]; /* to store pad encoded data (Tag 0x87) */
cwa_tlv_t *m_tlv = &tlv_array[2]; /* to store mac CC (Tag 0x8E) */
cwa_tlv_t *s_tlv = &tlv_array[3]; /* to store sw1-sw2 status (Tag 0x99) */
u8 *ccbuf = NULL; /* buffer for mac CC calculation */
size_t cclen = 0; /* ccbuf len */
u8 macbuf[8]; /* where to calculate mac */
size_t resplen = 0; /* respbuf length */
DES_key_schedule k1;
DES_key_schedule k2;
int res = SC_SUCCESS;
char *msg = NULL; /* to store error messages */
sc_context_t *ctx = NULL;
cwa_sm_session_t *sm_session = NULL;
/* mandatory check */
if (!card || !card->ctx || !provider)
return SC_ERROR_INVALID_ARGUMENTS;
ctx = card->ctx;
sm_session = &(provider->status.session);
LOG_FUNC_CALLED(ctx);
/* check remaining arguments */
if ((from == NULL) || (to == NULL) || (sm_session == NULL))
LOG_FUNC_RETURN(ctx, SC_ERROR_SM_NOT_INITIALIZED);
if (sm_session->state != CWA_SM_ACTIVE)
LOG_FUNC_RETURN(ctx, SC_ERROR_SM_INVALID_LEVEL);
/* cwa14890 sect 9.3: check SW1 or SW2 for SM related errors */
if (from->sw1 == 0x69) {
if ((from->sw2 == 0x88) || (from->sw2 == 0x87)) {
msg = "SM related errors in APDU response";
res = SC_ERROR_SM_ENCRYPT_FAILED; /* tell driver to restart SM */
goto response_decode_end;
}
}
/* if response is null/empty assume unencoded apdu */
if (!from->resp || (from->resplen == 0)) {
sc_log(ctx, "Empty APDU response: assume not cwa encoded");
memcpy(to, from, sizeof(sc_apdu_t));
return SC_SUCCESS;
}
/* checks if apdu response needs decoding by checking tags in response */
switch (*from->resp) {
case CWA_SM_PLAIN_TAG:
case CWA_SM_CRYPTO_TAG:
case CWA_SM_MAC_TAG:
case CWA_SM_LE_TAG:
case CWA_SM_STATUS_TAG:
break; /* cwa tags found: continue decoding */
default: /* else apdu response seems not to be cwa encoded */
sc_log(card->ctx, "APDU Response seems not to be cwa encoded");
memcpy(to, from, sizeof(sc_apdu_t));
return SC_SUCCESS; /* let process continue */
}
/* call provider pre-operation method */
if (provider->cwa_decode_pre_ops) {
res = provider->cwa_decode_pre_ops(card, provider, from, to);
if (res != SC_SUCCESS) {
sc_log(ctx, "Decode APDU: provider pre_ops() failed");
LOG_FUNC_RETURN(ctx, res);
}
}
/* parse response to find TLV's data and check results */
memset(tlv_array, 0, 4 * sizeof(cwa_tlv_t));
res = cwa_parse_tlv(card, from->resp, from->resplen, tlv_array);
if (res != SC_SUCCESS) {
msg = "Error in TLV parsing";
goto response_decode_end;
}
/* check consistency of received TLV's */
if (p_tlv->buf && e_tlv->buf) {
msg =
"Plain and Encoded data are mutually exclusive in apdu response";
res = SC_ERROR_INVALID_DATA;
goto response_decode_end;
}
if (!m_tlv->buf) {
msg = "No MAC TAG found in apdu response";
res = SC_ERROR_INVALID_DATA;
goto response_decode_end;
}
if (m_tlv->len != 4) {
msg = "Invalid MAC TAG Length";
res = SC_ERROR_INVALID_DATA;
goto response_decode_end;
}
/* compose buffer to evaluate mac */
/* reserve enought space for data+status+padding */
ccbuf =
calloc(e_tlv->buflen + s_tlv->buflen + p_tlv->buflen + 8,
sizeof(u8));
if (!ccbuf) {
msg = "Cannot allocate space for mac checking";
res = SC_ERROR_OUT_OF_MEMORY;
goto response_decode_end;
}
/* copy data into buffer */
cclen = 0;
if (e_tlv->buf) { /* encoded data */
memcpy(ccbuf, e_tlv->buf, e_tlv->buflen);
cclen = e_tlv->buflen;
}
if (p_tlv->buf) { /* plain data */
memcpy(ccbuf, p_tlv->buf, p_tlv->buflen);
cclen += p_tlv->buflen;
}
if (s_tlv->buf) { /* response status */
if (s_tlv->len != 2) {
msg = "Invalid SW TAG length";
res = SC_ERROR_INVALID_DATA;
goto response_decode_end;
}
memcpy(ccbuf + cclen, s_tlv->buf, s_tlv->buflen);
cclen += s_tlv->buflen;
to->sw1 = s_tlv->data[0];
to->sw2 = s_tlv->data[1];
} else { /* if no response status tag, use sw1 and sw2 from apdu */
to->sw1 = from->sw1;
to->sw2 = from->sw2;
}
/* add iso7816 padding */
cwa_iso7816_padding(ccbuf, &cclen);
/* evaluate mac by mean of kmac and increased SendSequence Counter SSC */
/* increase SSC */
res = cwa_increase_ssc(card, sm_session); /* increase send sequence counter */
if (res != SC_SUCCESS) {
msg = "Error in computing SSC";
goto response_decode_end;
}
/* set up keys for mac computing */
DES_set_key_unchecked((const_DES_cblock *) & (sm_session->kmac[0]), &k1);
DES_set_key_unchecked((const_DES_cblock *) & (sm_session->kmac[8]), &k2);
memcpy(macbuf, sm_session->ssc, 8); /* start with computed SSC */
for (i = 0; i < cclen; i += 8) { /* divide data in 8 byte blocks */
/* compute DES */
DES_ecb_encrypt((const_DES_cblock *) macbuf,
(DES_cblock *) macbuf, &k1, DES_ENCRYPT);
/* XOR with data and repeat */
for (j = 0; j < 8; j++)
macbuf[j] ^= ccbuf[i + j];
}
/* finally apply 3DES to result */
DES_ecb2_encrypt((const_DES_cblock *) macbuf, (DES_cblock *) macbuf,
&k1, &k2, DES_ENCRYPT);
/* check evaluated mac with provided by apdu response */
res = memcmp(m_tlv->data, macbuf, 4); /* check first 4 bytes */
if (res != 0) {
msg = "Error in MAC CC checking: value doesn't match";
res = SC_ERROR_SM_ENCRYPT_FAILED;
goto response_decode_end;
}
/* allocate response buffer */
resplen = 10 + MAX(p_tlv->len, e_tlv->len); /* estimate response buflen */
if (to->resp) { /* if response apdu provides buffer, try to use it */
if (to->resplen < resplen) {
msg =
"Provided buffer has not enought size to store response";
res = SC_ERROR_OUT_OF_MEMORY;
goto response_decode_end;
}
} else { /* buffer not provided: create and assing to response apdu */
to->resp = calloc(resplen, sizeof(u8));
if (!to->resp) {
msg = "Cannot allocate buffer to store response";
res = SC_ERROR_OUT_OF_MEMORY;
goto response_decode_end;
}
}
to->resplen = resplen;
/* fill destination response apdu buffer with data */
/* if plain data, just copy TLV data into apdu response */
if (p_tlv->buf) { /* plain data */
memcpy(to->resp, p_tlv->data, p_tlv->len);
to->resplen = p_tlv->len;
}
/* if encoded data, decode and store into apdu response */
else if (e_tlv->buf) { /* encoded data */
DES_cblock iv = { 0, 0, 0, 0, 0, 0, 0, 0 };
/* check data len */
if ((e_tlv->len < 9) || ((e_tlv->len - 1) % 8) != 0) {
msg = "Invalid length for Encoded data TLV";
res = SC_ERROR_INVALID_DATA;
goto response_decode_end;
}
/* first byte is padding info; check value */
if (e_tlv->data[0] != 0x01) {
msg = "Encoded TLV: Invalid padding info value";
res = SC_ERROR_INVALID_DATA;
goto response_decode_end;
}
/* prepare keys to decode */
DES_set_key_unchecked((const_DES_cblock *) & (sm_session->kenc[0]),
&k1);
DES_set_key_unchecked((const_DES_cblock *) & (sm_session->kenc[8]),
&k2);
/* decrypt into response buffer
* by using 3DES CBC by mean of kenc and iv={0,...0} */
DES_ede3_cbc_encrypt(&e_tlv->data[1], to->resp, e_tlv->len - 1,
&k1, &k2, &k1, &iv, DES_DECRYPT);
to->resplen = e_tlv->len - 1;
/* remove iso padding from response length */
for (; (to->resplen > 0) && *(to->resp + to->resplen - 1) == 0x00; to->resplen--) ; /* empty loop */
if (*(to->resp + to->resplen - 1) != 0x80) { /* check padding byte */
msg =
"Decrypted TLV has no 0x80 iso padding indicator!";
res = SC_ERROR_INVALID_DATA;
goto response_decode_end;
}
/* everything ok: remove ending 0x80 from response */
to->resplen--;
}
else
to->resplen = 0; /* neither plain, nor encoded data */
/* call provider post-operation method */
if (provider->cwa_decode_post_ops) {
res = provider->cwa_decode_post_ops(card, provider, from, to);
if (res != SC_SUCCESS) {
sc_log(ctx, "Decode APDU: provider post_ops() failed");
LOG_FUNC_RETURN(ctx, res);
}
}
/* that's all folks */
res = SC_SUCCESS;
response_decode_end:
if (ccbuf)
free(ccbuf);
if (msg) {
sc_log(ctx, msg);
} else {
cwa_trace_apdu(card, to, 1);
} /* trace apdu response */
LOG_FUNC_RETURN(ctx, res);
}
/********************* default provider for cwa14890 ****************/
/* pre and post operations */
static int default_create_pre_ops(sc_card_t * card, cwa_provider_t * provider)
{
return SC_SUCCESS;
}
static int default_create_post_ops(sc_card_t * card, cwa_provider_t * provider)
{
return SC_SUCCESS;
}
static int default_get_root_ca_pubkey(sc_card_t * card, EVP_PKEY ** root_ca_key)
{
return SC_ERROR_NOT_SUPPORTED;
}
/* retrieve CVC intermediate CA certificate and length */
static int default_get_cvc_ca_cert(sc_card_t * card, u8 ** cert,
size_t * length)
{
return SC_ERROR_NOT_SUPPORTED;
}
/* retrieve CVC IFD certificate and length */
static int default_get_cvc_ifd_cert(sc_card_t * card, u8 ** cert,
size_t * length)
{
return SC_ERROR_NOT_SUPPORTED;
}
static int default_get_ifd_privkey(sc_card_t * card, EVP_PKEY ** ifd_privkey)
{
return SC_ERROR_NOT_SUPPORTED;
}
/* get ICC intermediate CA path */
static int default_get_icc_intermediate_ca_cert(sc_card_t * card, X509 ** cert)
{
return SC_ERROR_NOT_SUPPORTED;
}
/* get ICC certificate path */
static int default_get_icc_cert(sc_card_t * card, X509 ** cert)
{
return SC_ERROR_NOT_SUPPORTED;
}
/* Retrieve key reference for Root CA to validate CVC intermediate CA certs */
static int default_get_root_ca_pubkey_ref(sc_card_t * card, u8 ** buf,
size_t * len)
{
return SC_ERROR_NOT_SUPPORTED;
}
/* Retrieve key reference for intermediate CA to validate IFD certs */
static int default_get_intermediate_ca_pubkey_ref(sc_card_t * card, u8 ** buf,
size_t * len)
{
return SC_ERROR_NOT_SUPPORTED;
}
/* Retrieve key reference for IFD certificate */
static int default_get_ifd_pubkey_ref(sc_card_t * card, u8 ** buf, size_t * len)
{
return SC_ERROR_NOT_SUPPORTED;
}
/* Retrieve key reference for ICC privkey */
static int default_get_icc_privkey_ref(sc_card_t * card, u8 ** buf,
size_t * len)
{
return SC_ERROR_NOT_SUPPORTED;
}
/* Retrieve SN.IFD (8 bytes left padded with zeroes if needed) */
static int default_get_sn_ifd(sc_card_t * card, u8 ** buf)
{
return SC_ERROR_NOT_SUPPORTED;
}
/* Retrieve SN.ICC (8 bytes left padded with zeroes if needed) */
static int default_get_sn_icc(sc_card_t * card, u8 ** buf)
{
return SC_ERROR_NOT_SUPPORTED;
}
/************** operations related with APDU encoding ******************/
/* pre and post operations */
static int default_encode_pre_ops(sc_card_t * card, cwa_provider_t * provider,
sc_apdu_t * from, sc_apdu_t * to)
{
return SC_SUCCESS;
}
static int default_encode_post_ops(sc_card_t * card, cwa_provider_t * provider,
sc_apdu_t * from, sc_apdu_t * to)
{
return SC_SUCCESS;
}
/************** operations related APDU response decoding **************/
/* pre and post operations */
static int default_decode_pre_ops(sc_card_t * card, cwa_provider_t * provider,
sc_apdu_t * from, sc_apdu_t * to)
{
return SC_SUCCESS;
}
static int default_decode_post_ops(sc_card_t * card, cwa_provider_t * provider,
sc_apdu_t * from, sc_apdu_t * to)
{
return SC_SUCCESS;
}
static cwa_provider_t default_cwa_provider = {
/************ data related with SM operations *************************/
{
{ /* KICC */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{ /* KIFD */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{ /* RND.ICC */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{ /* RND.IFD */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{ /* SigBuf */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{ /* sm session */
CWA_SM_NONE, /* state */
{ /* Kenc */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{ /* Kmac */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{ /* SSC Send Sequence counter */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
}
},
/************ operations related with secure channel creation *********/
/* pre and post operations */
default_create_pre_ops,
default_create_post_ops,
/* Get ICC intermediate CA path */
default_get_icc_intermediate_ca_cert,
/* Get ICC certificate path */
default_get_icc_cert,
/* Obtain RSA public key from RootCA */
default_get_root_ca_pubkey,
/* Obtain RSA IFD private key */
default_get_ifd_privkey,
/* Retrieve CVC intermediate CA certificate and length */
default_get_cvc_ca_cert,
/* Retrieve CVC IFD certificate and length */
default_get_cvc_ifd_cert,
/* Get public key references for Root CA to validate intermediate CA cert */
default_get_root_ca_pubkey_ref,
/* Get public key reference for IFD intermediate CA certificate */
default_get_intermediate_ca_pubkey_ref,
/* Get public key reference for IFD CVC certificate */
default_get_ifd_pubkey_ref,
/* Get ICC private key reference */
default_get_icc_privkey_ref,
/* Get IFD Serial Number */
default_get_sn_ifd,
/* Get ICC Serial Number */
default_get_sn_icc,
/************** operations related with APDU encoding ******************/
/* pre and post operations */
default_encode_pre_ops,
default_encode_post_ops,
/************** operations related APDU response decoding **************/
/* pre and post operations */
default_decode_pre_ops,
default_decode_post_ops,
};
/**
* Get a copy of default cwa provider.
*
* @param card pointer to card info structure
* @return copy of default provider or null on error
*/
cwa_provider_t *cwa_get_default_provider(sc_card_t * card)
{
cwa_provider_t *res = NULL;
if (!card || !card->ctx)
return NULL;
LOG_FUNC_CALLED(card->ctx);
res = calloc(1, sizeof(cwa_provider_t));
if (!res) {
sc_log(card->ctx, "Cannot allocate space for cwa_provider");
return NULL;
}
memcpy(res, &default_cwa_provider, sizeof(cwa_provider_t));
return res;
}
/* end of cwa14890.c */
#undef __CWA14890_C__
#endif /* ENABLE_OPENSSL */