2137 lines
63 KiB
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 */
|