opensc/src/smm/sm-cwa14890.c

410 lines
13 KiB
C

/*
* sm-cwa14890.c: Procedures related to Secure Messaging according to the CWA-14890
*
* Copyright (C) 2010 Viktor Tarasov <vtarasov@opentrust.com>
* OpenTrust <www.opentrust.com>
*
* 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
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <ctype.h>
#include <sys/stat.h>
#include <openssl/des.h>
#include "libopensc/opensc.h"
#include "libopensc/sm.h"
#include "libopensc/log.h"
#include "libopensc/asn1.h"
#include "libopensc/iasecc.h"
#include "libopensc/iasecc-sdo.h"
#include "sm-module.h"
int
sm_cwa_get_mac(struct sc_context *ctx, unsigned char *key, DES_cblock *icv,
unsigned char *in, int in_len, DES_cblock *out, int force_padding)
{
unsigned char padding[8] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
unsigned char *buf;
LOG_FUNC_CALLED(ctx);
sc_debug(ctx, SC_LOG_DEBUG_SM, "sm_cwa_get_mac() data length %i", in_len);
buf = malloc(in_len + 8);
if (!buf)
LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY);
sc_debug(ctx, SC_LOG_DEBUG_SM, "sm_cwa_get_mac() in_data(%i) %s", in_len, sc_dump_hex(in, in_len));
memcpy(buf, in, in_len);
memcpy(buf + in_len, padding, 8);
if (force_padding)
in_len = ((in_len + 8) / 8) * 8;
else
in_len = ((in_len + 7) / 8) * 8;
sc_debug(ctx, SC_LOG_DEBUG_SM, "sm_cwa_get_mac() data to MAC(%i) %s", in_len, sc_dump_hex(buf, in_len));
sc_debug(ctx, SC_LOG_DEBUG_SM, "sm_cwa_get_mac() ICV %s", sc_dump_hex((unsigned char *)icv, 8));
DES_cbc_cksum_3des_emv96(buf, out, in_len, key, icv);
free(buf);
LOG_FUNC_RETURN(ctx, SC_SUCCESS);
}
static int
sm_cwa_encode_external_auth_data(struct sc_context *ctx, struct sm_cwa_session *session_data,
unsigned char *out, size_t out_len)
{
if (out_len < 16)
return SC_ERROR_BUFFER_TOO_SMALL;
sc_debug(ctx, SC_LOG_DEBUG_SM, "IFD.RND %s", sc_dump_hex(session_data->ifd.rnd, 8));
sc_debug(ctx, SC_LOG_DEBUG_SM, "IFD.SN %s", sc_dump_hex(session_data->ifd.sn, 8));
memcpy(out + 0, session_data->icc.rnd, 8);
memcpy(out + 8, session_data->icc.sn, 8);
return 16;
}
int
sm_cwa_encode_mutual_auth_data(struct sc_context *ctx, struct sm_cwa_session *session_data,
unsigned char *out, size_t out_len)
{
if (out_len < 64)
return SC_ERROR_BUFFER_TOO_SMALL;
sc_debug(ctx, SC_LOG_DEBUG_SM, "IFD.RND %s", sc_dump_hex(session_data->ifd.rnd, 8));
sc_debug(ctx, SC_LOG_DEBUG_SM, "IFD.SN %s", sc_dump_hex(session_data->ifd.sn, 8));
sc_debug(ctx, SC_LOG_DEBUG_SM, "IFD.K %s", sc_dump_hex(session_data->ifd.k, 32));
sc_debug(ctx, SC_LOG_DEBUG_SM, "ICC.RND %s", sc_dump_hex(session_data->icc.rnd, 8));
sc_debug(ctx, SC_LOG_DEBUG_SM, "ICC.SN %s", sc_dump_hex(session_data->icc.sn, 8));
memcpy(out + 0, session_data->ifd.rnd, 8);
memcpy(out + 8, session_data->ifd.sn, 8);
memcpy(out + 16, session_data->icc.rnd, 8);
memcpy(out + 24, session_data->icc.sn, 8);
memcpy(out + 32, session_data->ifd.k, 32);
return 64;
}
int
sm_cwa_decode_authentication_data(struct sc_context *ctx, struct sm_cwa_keyset *keyset,
struct sm_cwa_session *session_data, unsigned char *auth_data)
{
DES_cblock icv = {0, 0, 0, 0, 0, 0, 0, 0};
DES_cblock cblock;
unsigned char *decrypted = NULL;
size_t decrypted_len;
int rv;
LOG_FUNC_CALLED(ctx);
memset(icv, 0, sizeof(icv));
rv = sm_cwa_get_mac(ctx, keyset->mac, &icv, session_data->mdata, 0x40, &cblock, 1);
LOG_TEST_RET(ctx, rv, "Decode authentication data: sm_ecc_get_mac failed");
sc_debug(ctx, SC_LOG_DEBUG_SM, "MAC:%s", sc_dump_hex(cblock, sizeof(cblock)));
if(memcmp(session_data->mdata + 0x40, cblock, 8))
LOG_FUNC_RETURN(ctx, SC_ERROR_SM_AUTHENTICATION_FAILED);
rv = sm_decrypt_des_cbc3(ctx, keyset->enc, session_data->mdata, session_data->mdata_len, &decrypted, &decrypted_len);
LOG_TEST_RET(ctx, rv, "sm_ecc_decode_auth_data() DES CBC3 decrypt error");
sc_debug(ctx, SC_LOG_DEBUG_SM,
"sm_ecc_decode_auth_data() decrypted(%"SC_FORMAT_LEN_SIZE_T"u) %s",
decrypted_len, sc_dump_hex(decrypted, decrypted_len));
if (memcmp(decrypted, session_data->icc.rnd, 8)) {
free(decrypted);
LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED);
}
if (memcmp(decrypted + 8, session_data->icc.sn, 8)) {
free(decrypted);
LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED);
}
if (memcmp(decrypted + 16, session_data->ifd.rnd, 8)) {
free(decrypted);
LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED);
}
if (memcmp(decrypted + 24, session_data->ifd.sn, 8)) {
free(decrypted);
LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED);
}
memcpy(session_data->icc.k, decrypted + 32, 32);
free(decrypted);
LOG_FUNC_RETURN(ctx, SC_SUCCESS);
}
int
sm_cwa_init_session_keys(struct sc_context *ctx, struct sm_cwa_session *session_data,
unsigned char mechanism)
{
unsigned char xored[36];
unsigned char buff[SHA256_DIGEST_LENGTH];
int ii;
memset(xored, 0, sizeof(xored));
for (ii=0; ii<32; ii++)
xored[ii] = session_data->ifd.k[ii] ^ session_data->icc.k[ii];
sc_debug(ctx, SC_LOG_DEBUG_SM, "K_IFD %s", sc_dump_hex(session_data->ifd.k, 32));
sc_debug(ctx, SC_LOG_DEBUG_SM, "K_ICC %s", sc_dump_hex(session_data->icc.k, 32));
if (mechanism == IASECC_ALGORITHM_SYMMETRIC_SHA1) {
xored[35] = 0x01;
sc_debug(ctx, SC_LOG_DEBUG_SM, "XOR for SkEnc %s", sc_dump_hex(xored, 36));
SHA1(xored, 36, buff);
memcpy(&session_data->session_enc[0], buff, sizeof(session_data->session_enc));
xored[35] = 0x02;
sc_debug(ctx, SC_LOG_DEBUG_SM, "XOR for SkMac %s", sc_dump_hex(xored, 36));
SHA1(xored, 36, buff);
memcpy(&session_data->session_mac[0], buff, sizeof(session_data->session_mac));
}
else if (mechanism == IASECC_ALGORITHM_SYMMETRIC_SHA256) {
xored[35] = 0x01;
SHA256(xored, 36, buff);
memcpy(&session_data->session_enc[0], buff, sizeof(session_data->session_enc));
xored[35] = 0x02;
SHA256(xored, 36, buff);
memcpy(&session_data->session_mac[0], buff, sizeof(session_data->session_mac));
}
else {
return SC_ERROR_INVALID_ARGUMENTS;
}
memcpy(session_data->ssc + 0, session_data->icc.rnd + 4, 4);
memcpy(session_data->ssc + 4, session_data->ifd.rnd + 4, 4);
LOG_FUNC_RETURN(ctx, SC_SUCCESS);
}
int
sm_cwa_initialize(struct sc_context *ctx, struct sm_info *sm_info, struct sc_remote_data *rdata)
{
struct sm_cwa_session *cwa_session = &sm_info->session.cwa;
struct sm_cwa_keyset *cwa_keyset = &sm_info->session.cwa.cwa_keyset;
struct sc_serial_number sn = sm_info->serialnr;
size_t icc_sn_len = sizeof(cwa_session->icc.sn);
struct sc_remote_apdu *new_rapdu = NULL;
struct sc_apdu *apdu = NULL;
unsigned char buf[0x100], *encrypted = NULL;
size_t encrypted_len;
DES_cblock icv = {0, 0, 0, 0, 0, 0, 0, 0}, cblock;
int rv, offs;
LOG_FUNC_CALLED(ctx);
sc_debug(ctx, SC_LOG_DEBUG_SM, "SM IAS/ECC initialize: serial %s", sc_dump_hex(sm_info->serialnr.value, sm_info->serialnr.len));
sc_debug(ctx, SC_LOG_DEBUG_SM, "SM IAS/ECC initialize: card challenge %s", sc_dump_hex(cwa_session->card_challenge, 8));
sc_debug(ctx, SC_LOG_DEBUG_SM, "SM IAS/ECC initialize: current_df_path %s", sc_print_path(&sm_info->current_path_df));
sc_debug(ctx, SC_LOG_DEBUG_SM, "SM IAS/ECC initialize: CRT_AT reference 0x%X", cwa_session->params.crt_at.refs[0]);
if (!rdata || !rdata->alloc)
LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
rv = rdata->alloc(rdata, &new_rapdu);
LOG_TEST_RET(ctx, rv, "SM GP decode card answer: cannot allocate remote APDU");
apdu = &new_rapdu->apdu;
memcpy(&cwa_session->icc.rnd[0], cwa_session->card_challenge, 8);
if (sn.len > icc_sn_len)
memcpy(&cwa_session->icc.sn[0], &sn.value[sn.len - icc_sn_len], icc_sn_len);
else
memcpy(&cwa_session->icc.sn[icc_sn_len - sn.len], &sn.value[0], sn.len);
if (sm_info->cmd == SM_CMD_EXTERNAL_AUTH) {
offs = sm_cwa_encode_external_auth_data(ctx, cwa_session, buf, sizeof(buf));
if (offs != 0x10)
LOG_FUNC_RETURN(ctx, offs);
}
else {
offs = sm_cwa_encode_mutual_auth_data(ctx, cwa_session, buf, sizeof(buf));
if (offs != 0x40)
LOG_FUNC_RETURN(ctx, offs);
}
sc_debug(ctx, SC_LOG_DEBUG_SM, "S(%i) %s", offs, sc_dump_hex(buf, offs));
rv = sm_encrypt_des_cbc3(ctx, cwa_keyset->enc, buf, offs, &encrypted, &encrypted_len, 1);
LOG_TEST_RET(ctx, rv, "_encrypt_des_cbc3() failed");
sc_debug(ctx, SC_LOG_DEBUG_SM, "ENCed(%"SC_FORMAT_LEN_SIZE_T"u) %s", encrypted_len,
sc_dump_hex(encrypted, encrypted_len));
memcpy(buf, encrypted, encrypted_len);
offs = encrypted_len;
rv = sm_cwa_get_mac(ctx, cwa_keyset->mac, &icv, buf, offs, &cblock, 1);
LOG_TEST_GOTO_ERR(ctx, rv, "sm_ecc_get_mac() failed");
sc_debug(ctx, SC_LOG_DEBUG_SM, "MACed(%"SC_FORMAT_LEN_SIZE_T"u) %s", sizeof(cblock),
sc_dump_hex(cblock, sizeof(cblock)));
apdu->cse = SC_APDU_CASE_4_SHORT;
apdu->cla = 0x00;
apdu->ins = 0x82;
apdu->p1 = 0x00;
apdu->p2 = 0x00;
apdu->lc = encrypted_len + sizeof(cblock);
apdu->le = encrypted_len + sizeof(cblock);
apdu->datalen = encrypted_len + sizeof(cblock);
memcpy(new_rapdu->sbuf, encrypted, encrypted_len);
memcpy(new_rapdu->sbuf + encrypted_len, cblock, sizeof(cblock));
rv = SC_SUCCESS;
err:
free(encrypted);
LOG_FUNC_RETURN(ctx, rv);
}
int
sm_cwa_securize_apdu(struct sc_context *ctx, struct sm_info *sm_info, struct sc_remote_apdu *rapdu)
{
struct sm_cwa_session *session_data = &sm_info->session.cwa;
struct sc_apdu *apdu = &rapdu->apdu;
unsigned char sbuf[0x400];
DES_cblock cblock, icv;
unsigned char *encrypted = NULL, edfb_data[0x200], mac_data[0x200];
size_t encrypted_len, edfb_len = 0, mac_len = 0, offs;
int rv;
LOG_FUNC_CALLED(ctx);
sc_debug(ctx, SC_LOG_DEBUG_SM,
"securize APDU (cla:%X,ins:%X,p1:%X,p2:%X,data(%"SC_FORMAT_LEN_SIZE_T"u):%p)",
apdu->cla, apdu->ins, apdu->p1, apdu->p2, apdu->datalen,
apdu->data);
sm_incr_ssc(session_data->ssc, sizeof(session_data->ssc));
rv = sm_encrypt_des_cbc3(ctx, session_data->session_enc, apdu->data, apdu->datalen, &encrypted, &encrypted_len, 0);
LOG_TEST_RET(ctx, rv, "securize APDU: DES CBC3 encryption failed");
sc_debug(ctx, SC_LOG_DEBUG_SM, "encrypted data (len:%"SC_FORMAT_LEN_SIZE_T"u, %s)",
encrypted_len, sc_dump_hex(encrypted, encrypted_len));
offs = 0;
if (apdu->ins & 0x01) {
edfb_data[offs++] = IASECC_SM_DO_TAG_TCG_ODD_INS;
if (encrypted_len + 1 > 0x7F)
edfb_data[offs++] = 0x81;
edfb_data[offs++] = encrypted_len;
}
else {
edfb_data[offs++] = IASECC_SM_DO_TAG_TCG_EVEN_INS;
if (encrypted_len + 1 > 0x7F)
edfb_data[offs++] = 0x81;
edfb_data[offs++] = encrypted_len + 1;
edfb_data[offs++] = 0x01;
}
memcpy(edfb_data + offs, encrypted, encrypted_len);
offs += encrypted_len;
edfb_len = offs;
sc_debug(ctx, SC_LOG_DEBUG_SM, "securize APDU: EDFB(len:%"SC_FORMAT_LEN_SIZE_T"u,%s)",
edfb_len, sc_dump_hex(edfb_data, edfb_len));
free(encrypted);
encrypted = NULL;
offs = 0;
memcpy(mac_data + offs, session_data->ssc, 8);
offs += 8;
mac_data[offs++] = apdu->cla | 0x0C;
mac_data[offs++] = apdu->ins;
mac_data[offs++] = apdu->p1;
mac_data[offs++] = apdu->p2;
mac_data[offs++] = 0x80;
mac_data[offs++] = 0x00;
mac_data[offs++] = 0x00;
mac_data[offs++] = 0x00;
memcpy(mac_data + offs, edfb_data, edfb_len);
offs += edfb_len;
/* if (apdu->le) { */
mac_data[offs++] = IASECC_SM_DO_TAG_TLE;
mac_data[offs++] = 1;
mac_data[offs++] = apdu->le;
/* } */
mac_len = offs;
sc_debug(ctx, SC_LOG_DEBUG_SM, "securize APDU: MAC data(len:%"SC_FORMAT_LEN_SIZE_T"u,%s)",
mac_len, sc_dump_hex(mac_data, mac_len));
memset(icv, 0, sizeof(icv));
rv = sm_cwa_get_mac(ctx, session_data->session_mac, &icv, mac_data, mac_len, &cblock, 0);
LOG_TEST_RET(ctx, rv, "securize APDU: MAC calculation error");
sc_debug(ctx, SC_LOG_DEBUG_SM, "securize APDU: MAC:%s", sc_dump_hex(cblock, sizeof(cblock)));
offs = 0;
if (edfb_len) {
memcpy(sbuf + offs, edfb_data, edfb_len);
offs += edfb_len;
}
/* if (apdu->le) { */
sbuf[offs++] = IASECC_SM_DO_TAG_TLE;
sbuf[offs++] = 1;
sbuf[offs++] = apdu->le;
/* } */
sbuf[offs++] = IASECC_SM_DO_TAG_TCC;
sbuf[offs++] = 8;
memcpy(sbuf + offs, cblock, 8);
offs += 8;
sc_debug(ctx, SC_LOG_DEBUG_SM, "securize APDU: SM data(len:%"SC_FORMAT_LEN_SIZE_T"u,%s)",
offs, sc_dump_hex(sbuf, offs));
if (offs > sizeof(rapdu->sbuf))
LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "securize APDU: buffer too small for encrypted data");
apdu->cse = SC_APDU_CASE_4_SHORT;
apdu->cla |= 0x0C;
apdu->lc = offs;
apdu->datalen = offs;
memcpy((unsigned char *)apdu->data, sbuf, offs);
sm_incr_ssc(session_data->ssc, sizeof(session_data->ssc));
LOG_FUNC_RETURN(ctx, SC_SUCCESS);
}