opensc/src/libopensc/card-belpic.c

428 lines
14 KiB
C
Raw Blame History

/*
* card-belpic.c: Support for Belgium EID card
*
* Copyright (C) 2003, Zetes Belgium
*
* 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
*/
/* About the Belpic (Belgian Personal Identity Card) card
*
* The Belpic card is a Cyberflex Java card, so you normally communicate
* with an applet running on the card. In order to support a pkcs15 file
* structure, an applet (the Belpic applet) has been build that emulates
* this. So the card's behaviour is specific for this Belpic applet, that's
* why a separate driver has been made.
*
* The card contains the citizen's ID data (name, address, photo, ...) and
* her keys and certs. The ID data are in a separate directory on the card and
* are not handled by this software. For the cryptographic data (keys and certs)
* a pkcs#15 structure has been chosen and they can be accessed and used
* by the OpenSC software.
*
* The current situation about the cryptographic data is: there is 1 PIN
* that protects 2 private keys and corresponding certs. Then there is a
* CA cert and the root cert. The first key (Auth Key) can be used for
* authentication, the second one (NonRep Key) for non repudiation purposes
* (so it can be used as an alternative to manual signatures).
*
* There are some special things to note, which all have some consequences:
* (1) the SELECT FILE command doesn't return any FCI (file length, type, ...)
* (2) the NonRep key needs a VERIFY PIN before a signature can be done with it
* (3) pin pad readers had to be supported by a proprietary interface (as at
* that moment no other solution was known/available/ready)
* The consequences are:
*
* For (1): we let the SELECT FILE command return that the file length is
* a fixed large number and that each file is a transparent working EF
* (except the root dir 3F 00). This way however, there is a problem with the
* sc_read_binary() function that will only stop reading until it receives
* a 0. Therefore, we use the 'next_idx' trick. Or, if that might fail
* and so a READ BINARY past the end of file is done, length 0 is returned
* instead of an error code.
*
* For (2), we decided that a GUI for asking the PIN would be the best
* thing to do (another option would be to make 2 virtual slots but that
* causes other problems and is less user-friendly). A GUI being popped up
* by the pkcs11 lib before each NonRep signature has another important
* security advantage: applications that cache the PIN can't silently do
* a NonRep signature because there will always be the GUI.
*
* For (3), we link dynamically against a pin pad lib (DLL) that implements the
* proprietary API for a specific pin pad. For each pin pad reader (identified
* by it's PC/SC reader name), a pin pad lib corresponds. Some reader/lib
* name pairs are hardcoded, and others can be added in the config file.
* Note that there's also a GUI used in this case: if a signature with the
* NonRep key is done: a dialog box is shown that asks the user to enter
* her PIN on the pin pad reader in order to make a legally valid signature.
*
* Further the (current) Belpic card as quite some limitations:
* no key pair generation or update of data except after establishing a Secure
* Channel or CTV-authentication (which can only be done at the municipalities),
* no encryption. The result is that only a very limited amount of functions
* is/had to be implemented to get the pkcs11 library working.
*
* About the belpic_set_language: the RA-PC software (including the pkcs11 lib)
* in the Brussels' communities should be able to change the language of the GUI
* messages. So the language set by this function takes priority on all other
* language-selection functionality.
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include "internal.h"
#include "log.h"
/* To be removed */
#include <time.h>
static long t1, t2, tot_read = 0, tot_dur = 0, dur;
#define BELPIC_VERSION "1.4"
/* Most of the #defines here are also present in the pkcs15 files, but
* because this driver has no access to them, it's hardcoded here. If
* other Belpic cards with other 'settings' appear, we'll have to move
* these #defines to the struct belpic_priv_data */
#define BELPIC_MAX_FILE_SIZE 65535
#define BELPIC_PIN_BUF_SIZE 8
#define BELPIC_MIN_USER_PIN_LEN 4
#define BELPIC_MAX_USER_PIN_LEN 12
#define BELPIC_PIN_ENCODING SC_PIN_ENCODING_GLP
#define BELPIC_PAD_CHAR 0xFF
#define BELPIC_KEY_REF_NONREP 0x83
/* Data in the return value for the GET CARD DATA command:
* All fields are one byte, except when noted otherwise.
*
* See <20>6.9 in
* https://github.com/Fedict/eid-mw/blob/master/doc/sdk/documentation/Public_Belpic_Applet_v1%207_Ref_Manual%20-%20A01.pdf
* for the full documentation on the GET CARD DATA command.
*/
// Card serial number (16 bytes)
#define BELPIC_CARDDATA_OFF_SERIALNUM 0
// "Component code"
#define BELPIC_CARDDATA_OFF_COMPCODE 16
// "OS number"
#define BELPIC_CARDDATA_OFF_OSNUM 17
// "OS version"
#define BELPIC_CARDDATA_OFF_OSVER 18
// "Softmask number"
#define BELPIC_CARDDATA_OFF_SMNUM 19
// "Softmask version"
#define BELPIC_CARDDATA_OFF_SMVER 20
// Applet version
#define BELPIC_CARDDATA_OFF_APPLETVERS 21
// Global OS version (2 bytes)
#define BELPIC_CARDDATA_OFF_GL_OSVE 22
// Applet interface version
#define BELPIC_CARDDATA_OFF_APPINTVERS 24
// PKCS#1 support version
#define BELPIC_CARDDATA_OFF_PKCS1 25
// Key exchange version
#define BELPIC_CARDDATA_OFF_KEYX 26
// Applet life cycle (Should always be 0F for released cards, is 07 when not issued yet)
#define BELPIC_CARDDATA_OFF_APPLCYCLE 27
// Full length of reply
#define BELPIC_CARDDATA_RESP_LEN 28
/* Used for a trick in select file and read binary */
static size_t next_idx = (size_t)-1;
static struct sc_atr_table belpic_atrs[] = {
/* Applet V1.1 */
{ "3B:98:13:40:0A:A5:03:01:01:01:AD:13:11", NULL, NULL, SC_CARD_TYPE_BELPIC_EID, 0, NULL },
/* Applet V1.0 with new EMV-compatible ATR */
{ "3B:98:94:40:0A:A5:03:01:01:01:AD:13:10", NULL, NULL, SC_CARD_TYPE_BELPIC_EID, 0, NULL },
/* Applet beta 5 + V1.0 */
{ "3B:98:94:40:FF:A5:03:01:01:01:AD:13:10", NULL, NULL, SC_CARD_TYPE_BELPIC_EID, 0, NULL },
{ NULL, NULL, NULL, 0, 0, NULL }
};
static struct sc_card_operations belpic_ops;
static struct sc_card_driver belpic_drv = {
"Belpic cards",
"belpic",
&belpic_ops,
NULL, 0, NULL
};
static const struct sc_card_operations *iso_ops = NULL;
static int get_carddata(sc_card_t *card, u8* carddata_loc, unsigned int carddataloc_len)
{
sc_apdu_t apdu;
u8 carddata_cmd[] = { 0x80, 0xE4, 0x00, 0x00, 0x1C };
int r;
assert(carddataloc_len == BELPIC_CARDDATA_RESP_LEN);
r = sc_bytes2apdu(card->ctx, carddata_cmd, sizeof(carddata_cmd), &apdu);
if(r) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "bytes to APDU conversion failed: %d\n", r);
return r;
}
apdu.resp = carddata_loc;
apdu.resplen = carddataloc_len;
r = sc_transmit_apdu(card, &apdu);
if(r) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "GetCardData command failed: %d\n", r);
return r;
}
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
if(r) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "GetCardData: card returned %d\n", r);
return r;
}
if(apdu.resplen < carddataloc_len) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
"GetCardData: card returned %"SC_FORMAT_LEN_SIZE_T"u bytes rather than expected %d\n",
apdu.resplen, carddataloc_len);
return SC_ERROR_WRONG_LENGTH;
}
return 0;
}
static int belpic_match_card(sc_card_t *card)
{
int i;
i = _sc_match_atr(card, belpic_atrs, &card->type);
if (i < 0)
return 0;
return 1;
}
static int belpic_init(sc_card_t *card)
{
int key_size = 1024;
int r;
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Belpic V%s\n", BELPIC_VERSION);
if (card->type < 0)
card->type = SC_CARD_TYPE_BELPIC_EID; /* Unknown card: assume it's the Belpic Card */
card->cla = 0x00;
if (card->type == SC_CARD_TYPE_BELPIC_EID) {
u8 carddata[BELPIC_CARDDATA_RESP_LEN];
memset(carddata, 0, sizeof(carddata));
if((r = get_carddata(card, carddata, sizeof(carddata))) < 0) {
return r;
}
if (carddata[BELPIC_CARDDATA_OFF_APPLETVERS] >= 0x17) {
key_size = 2048;
}
_sc_card_add_rsa_alg(card, key_size,
SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE, 0);
}
/* State that we have an RNG */
card->caps |= SC_CARD_CAP_RNG;
card->max_pin_len = BELPIC_MAX_USER_PIN_LEN;
return 0;
}
static int belpic_select_file(sc_card_t *card,
const sc_path_t *in_path, sc_file_t **file_out)
{
sc_apdu_t apdu;
u8 pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf;
int r, pathlen;
sc_file_t *file = NULL;
assert(card != NULL && in_path != NULL);
memcpy(path, in_path->value, in_path->len);
pathlen = in_path->len;
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x08, 0x0C);
apdu.lc = pathlen;
apdu.data = path;
apdu.datalen = pathlen;
apdu.resplen = 0;
apdu.le = 0;
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Select File APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
if (r)
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
next_idx = (size_t)-1; /* reset */
if (file_out != NULL) {
file = sc_file_new();
file->path = *in_path;
if (pathlen >= 2)
file->id = (in_path->value[pathlen - 2] << 8) | in_path->value[pathlen - 1];
file->size = BELPIC_MAX_FILE_SIZE;
file->shareable = 1;
file->ef_structure = SC_FILE_EF_TRANSPARENT;
if (pathlen == 2 && memcmp("\x3F\x00", in_path->value, 2) == 0)
file->type = SC_FILE_TYPE_DF;
else
file->type = SC_FILE_TYPE_WORKING_EF;
*file_out = file;
}
return 0;
}
static int belpic_read_binary(sc_card_t *card,
unsigned int idx, u8 * buf, size_t count, unsigned long flags)
{
int r;
if (next_idx == idx)
return 0; /* File was already read entirely */
t1 = clock();
r = iso_ops->read_binary(card, idx, buf, count, flags);
t2 = clock();
/* If the 'next_idx trick' shouldn't work, we hope this error
* means that an attempt was made to read beyond the file's
* contents, so we'll return 0 to end the loop in sc_read_binary()*/
if (r == SC_ERROR_INCORRECT_PARAMETERS)
return 0;
if (r >= 0 && (size_t)r < count)
next_idx = idx + (size_t)r;
dur = t2 - t1;
tot_dur += dur;
tot_read += r;
return r;
}
static int belpic_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left)
{
data->pin1.encoding = data->pin2.encoding = BELPIC_PIN_ENCODING;
data->pin1.pad_char = data->pin2.pad_char = BELPIC_PAD_CHAR;
data->pin1.min_length = data->pin2.min_length = BELPIC_MIN_USER_PIN_LEN;
data->pin1.max_length = data->pin2.max_length = BELPIC_MAX_USER_PIN_LEN;
data->apdu = NULL;
return iso_ops->pin_cmd(card, data, tries_left);
}
static int belpic_set_security_env(sc_card_t *card,
const sc_security_env_t *env, int se_num)
{
sc_apdu_t apdu;
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
int r;
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "belpic_set_security_env(), keyRef = 0x%0x, algo = 0x%0x\n",
*env->key_ref, env->algorithm_flags);
assert(card != NULL && env != NULL);
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0, 0);
switch (env->operation) {
case SC_SEC_OPERATION_SIGN:
apdu.p1 = 0x41;
apdu.p2 = 0xB6;
sbuf[0] = 0x04; /* length of the following data */
sbuf[1] = 0x80; /* tag for algorithm reference */
if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1)
sbuf[2] = 0x01;
else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1)
sbuf[2] = 0x02;
else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_MD5)
sbuf[2] = 0x04;
else {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Set Sec Env: unsupported algo 0X%0X\n",
env->algorithm_flags);
return SC_ERROR_INVALID_ARGUMENTS;
}
sbuf[3] = 0x84; /* tag for private key reference */
sbuf[4] = *env->key_ref; /* key reference */
apdu.lc = 5;
apdu.datalen = 5;
break;
default:
return SC_ERROR_INVALID_ARGUMENTS;
}
apdu.le = 0;
apdu.data = sbuf;
apdu.resplen = 0;
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Set Security Env APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card's Set Security Env command returned error");
/* If a NonRep signature will be done, ask to enter a PIN. It would be more
* logical to put the code below into the compute signature function because
* a Verify Pin call must immediately precede a Compute Signature call.
* It's not done because the Compute Signature is completely ISO7816 compliant
* so we use the iso7816_compute_signature() function, and because this function
* doesn't know about the key reference.
* It's not a problem either, because this function is (for pkcs11) only called
* by sc_pkcs15_compute_signature(), where the card is already locked, and
* the next function to be executed will be the compute_signature function.
*/
if (*env->key_ref == BELPIC_KEY_REF_NONREP) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "No GUI for NonRep key present, signature cancelled\n");
return SC_ERROR_NOT_SUPPORTED;
}
return r;
}
static struct sc_card_driver *sc_get_driver(void)
{
if (iso_ops == NULL)
iso_ops = sc_get_iso7816_driver()->ops;
belpic_ops.match_card = belpic_match_card;
belpic_ops.init = belpic_init;
belpic_ops.update_binary = iso_ops->update_binary;
belpic_ops.select_file = belpic_select_file;
belpic_ops.read_binary = belpic_read_binary;
belpic_ops.pin_cmd = belpic_pin_cmd;
belpic_ops.set_security_env = belpic_set_security_env;
belpic_ops.compute_signature = iso_ops->compute_signature;
belpic_ops.get_challenge = iso_ops->get_challenge;
belpic_ops.get_response = iso_ops->get_response;
belpic_ops.check_sw = iso_ops->check_sw;
return &belpic_drv;
}
#if 1
struct sc_card_driver *sc_get_belpic_driver(void)
{
return sc_get_driver();
}
#endif