diff --git a/src/libopensc/card-setcos.c b/src/libopensc/card-setcos.c index d6052bc3..699cec24 100644 --- a/src/libopensc/card-setcos.c +++ b/src/libopensc/card-setcos.c @@ -3,6 +3,7 @@ * * Copyright (C) 2001, 2002 Juha Yrjölä * Copyright (C) 2005 Antti Tapaninen + * Copyright (C) 2005 Zetes * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,6 +21,7 @@ */ #include "internal.h" +#include "cardctl.h" #include #include @@ -41,9 +43,19 @@ static struct sc_atr_table setcos_atrs[] = { { "3b:64:00:00:80:62:00:51", "ff:ff:ff:ff:ff:ff:f0:ff", NULL, SC_CARD_TYPE_SETCOS_FINEID_V2, 0 }, /* FINEID 2264 (EIDApplet/7816-15, OPK/EMV/AVANT) */ { "3b:6e:00:00:00:62:00:00:57:41:56:41:4e:54:10:81:90:00", NULL, NULL, SC_CARD_TYPE_SETCOS_FINEID_V2, 0 }, + + /* Setcos 4.4.1 */ + { "3b:9f:94:80:1f:c3:00:68:11:44:05:01:46:49:53:45:31:c8:00:00:00:00", "ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00", NULL, SC_CARD_TYPE_SETCOS_44, 0 }, { NULL } }; +/* Setcos 4.4 Life Cycle Status Integer */ +#define SETEC_LCSI_CREATE 0x01 +#define SETEC_LCSI_INIT 0x03 +#define SETEC_LCSI_ACTIVATED 0x07 +#define SETEC_LCSI_DEACTIVATE 0x06 +#define SETEC_LCSI_TEMINATE 0x0F /* MF only */ + static struct sc_card_operations setcos_ops; static struct sc_card_driver setcos_drv = { "Setec cards", @@ -132,6 +144,11 @@ static int setcos_init(sc_card_t *card) if (card->flags & SC_CARD_FLAG_RNG) card->caps |= SC_CARD_CAP_RNG; break; + case SC_CARD_TYPE_SETCOS_44: + card->cla = 0x00; + card->caps |= SC_CARD_CAP_USE_FCI_AC; + card->caps |= SC_CARD_CAP_RNG; + break; default: /* XXX: Get SetCOS version */ card->cla = 0x80; /* SetCOS 4.3.x */ @@ -153,12 +170,125 @@ static int setcos_init(sc_card_t *card) _sc_card_add_rsa_alg(card, 1024, flags, 0); } break; + case SC_CARD_TYPE_SETCOS_44: + { + unsigned long flags; + + flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_PAD_PKCS1; + flags |= SC_ALGORITHM_RSA_HASH_NONE | SC_ALGORITHM_RSA_HASH_SHA1; + + _sc_card_add_rsa_alg(card, 1024, flags, 0); + } + break; } return 0; } static const struct sc_card_operations *iso_ops = NULL; +static int setcos_construct_fci_44(sc_card_t *card, const sc_file_t *file, u8 *out, size_t *outlen) +{ + u8 *p = out; + u8 buf[64]; + u8 *pin_key_info; + + /* Command */ + *p++ = 0x6F; + p++; + + /* Length */ + buf[0] = (file->size >> 8) & 0xFF; + buf[1] = file->size & 0xFF; + sc_asn1_put_tag(0x81, buf, 2, p, 16, &p); + + /* Type */ + if (file->type_attr_len) { + memcpy(buf, file->type_attr, file->type_attr_len); + sc_asn1_put_tag(0x82, buf, file->type_attr_len, p, 16, &p); + } else { + u8 bLen = 1; + + buf[0] = file->shareable ? 0x40 : 0; + switch (file->type) { + case SC_FILE_TYPE_INTERNAL_EF: /* RSA keyfile */ + buf[0] = 0x11; + break; + case SC_FILE_TYPE_WORKING_EF: + if (file->ef_structure == 0x22) { /* pin-file */ + /* ISF key-file, Setcos V4.4 specific */ + bLen = 5; + buf[0] = 0x0A; /* EF linear fixed EF for ISF keys */ + buf[1] = 0x41; /* fixed */ + buf[2] = file->record_length >> 8; /* 2 byte record length */ + buf[3] = file->record_length & 0xFF; + buf[4] = file->size / file->record_length; /* record count */ + } else { + buf[0] |= file->ef_structure & 7; /* set file-type, only for EF, not for DF objects */ + } + break; + case SC_FILE_TYPE_DF: + buf[0] = 0x38; + break; + default: + return SC_ERROR_NOT_SUPPORTED; + } + sc_asn1_put_tag(0x82, buf, bLen, p, 16, &p); + } + + /* File-id */ + buf[0] = (file->id >> 8) & 0xFF; + buf[1] = file->id & 0xFF; + sc_asn1_put_tag(0x83, buf, 2, p, 16, &p); + + /* file state */ + if (file->prop_attr_len) { + memcpy(buf, file->prop_attr, file->prop_attr_len); + sc_asn1_put_tag(0x8A, buf, file->prop_attr_len, p, 18, &p); + } + + /* Access control */ + memcpy(buf, file->sec_attr, file->sec_attr_len); + sc_asn1_put_tag(0x86, buf, file->sec_attr_len, p, 18, &p); + + switch (file->type) { + case SC_FILE_TYPE_DF: + if (file->name[0] != 0) + sc_asn1_put_tag(0x84, (u8 *) file->name, file->namelen, p, 18, &p); + else { /* Name required -> take the FID */ + buf[0] = (file->id >> 8) & 0xFF; + buf[1] = file->id & 0xFF; + sc_asn1_put_tag(0x84, buf, 2, p, 16, &p); + } + + /* Pin/key info: define 4 pins, no keys */ + if(file->path.len == 2) + pin_key_info = "\xC1\x04\x81\x82\x83\x84\xC2\x00"; /* root-MF: use local pin-file */ + else + pin_key_info = "\xC1\x04\x01\x02\x03\x04\xC2\x00"; /* sub-DF: use parent pin-file in root-MF */ + sc_asn1_put_tag(0xA5, pin_key_info, 8, p, 18, &p); + break; + + case SC_FILE_TYPE_INTERNAL_EF: /* RSA keyfiles */ + /* Get pin-number */ + /* Define AC for RSA-files */ + break; + } + + /* Length */ + out[1] = p - out - 2; + + *outlen = p - out; + return 0; +} + +static int setcos_construct_fci(sc_card_t *card, const sc_file_t *file, u8 *out, size_t *outlen) +{ + if (card->type == SC_CARD_TYPE_SETCOS_44) + return setcos_construct_fci_44(card, file, out, outlen); + else + return iso_ops->construct_fci(card, file, out, outlen); +} + static u8 acl_to_byte(const sc_acl_entry_t *e) { switch (e->method) { @@ -184,8 +314,160 @@ static u8 acl_to_byte(const sc_acl_entry_t *e) return 0x00; } +static u8 acl_to_byte_44(const struct sc_acl_entry *e, u8* p_bNumber) +{ + /* Handle special fixed values */ + if (e == (sc_acl_entry_t *) 1) /* SC_AC_NEVER */ + return SC_AC_NEVER; + else if ((e == (sc_acl_entry_t *) 2) || /* SC_AC_NONE */ + (e == (sc_acl_entry_t *) 3) || /* SC_AC_UNKNOWN */ + (e == (sc_acl_entry_t *) 0)) + return SC_AC_NONE; + + /* Handle standard values */ + *p_bNumber = e->key_ref; + return(e->method); +} + +/* If pin is present in the pins list, return it's index. + * If it's not yet present, add it to the list and return the index. */ +static int setcos_pin_index_44(int *pins, int len, int pin) +{ + int i; + for (i = 0; i < len; i++) { + if (pins[i] == pin) + return i; + if (pins[i] == -1) { + pins[i] = pin; + return i; + } + } + assert(i != len); /* Too much PINs, shouldn't happen */ + return 0; +} + +/* The ACs are allways for the SETEC_LCSI_ACTIVATED state, even if + * we have to create the file in the SC_FILE_STATUS_INITIALISATION state. */ +static int setcos_create_file_44(sc_card_t *card, sc_file_t *file) +{ + const u8 bFileStatus = file->status == SC_FILE_STATUS_CREATION ? + SETEC_LCSI_CREATE : SETEC_LCSI_ACTIVATED; + u8 bCommands_always = 0; + int pins[] = {-1, -1, -1, -1, -1, -1, -1}; + u8 bCommands_pin[sizeof(pins)]; + u8 bCommands_key = 0; + u8 bNumber = 0; + u8 bPinNumber = 0; + u8 bKeyNumber = 0; + u8 bMethod = 0; + + /* -1 means RFU */ + const int df_idx[8] = { /* byte 1 = OpenSC type of AC Bit0, byte 2 = OpenSC type of AC Bit1 ...*/ + SC_AC_OP_DELETE, SC_AC_OP_CREATE, SC_AC_OP_CREATE, + SC_AC_OP_INVALIDATE, SC_AC_OP_REHABILITATE, + SC_AC_OP_LOCK, SC_AC_OP_DELETE, -1}; + const int ef_idx[8] = { /* note: SC_AC_OP_SELECT to be ignored, actually RFU */ + SC_AC_OP_READ, SC_AC_OP_UPDATE, SC_AC_OP_WRITE, + SC_AC_OP_INVALIDATE, SC_AC_OP_REHABILITATE, + -1, SC_AC_OP_ERASE, -1}; + const int efi_idx[8] = { /* internal EF used for RSA keys */ + SC_AC_OP_READ, SC_AC_OP_ERASE, SC_AC_OP_UPDATE, + SC_AC_OP_INVALIDATE, SC_AC_OP_REHABILITATE, + -1, SC_AC_OP_ERASE, -1}; + + /* Set file creation status */ + sc_file_set_prop_attr(file, &bFileStatus, 1); + + /* Build ACI from local structure = get AC for each operation group */ + if (file->sec_attr_len == 0) { + const int* p_idx; + int i; + int len = 0; + u8 bBuf[32]; + + /* Get specific operation groups for specified file-type */ + switch (file->type){ + case SC_FILE_TYPE_DF: /* DF */ + p_idx = df_idx; + break; + case SC_FILE_TYPE_INTERNAL_EF: /* EF for RSA keys */ + p_idx = efi_idx; + break; + default: /* SC_FILE_TYPE_WORKING_EF */ + p_idx = ef_idx; + break; + } + + /* Get enabled commands + required Keys/Pins */ + memset(bCommands_pin, 0, sizeof(bCommands_pin)); + for (i = 7; i >= 0; i--) { /* for each AC Setcos operation */ + bCommands_always <<= 1; + bCommands_key <<= 1; + + if (p_idx[i] == -1) /* -1 means that bit is RFU -> set to 0 */ + continue; + + bMethod = acl_to_byte_44(file->acl[ p_idx[i] ], &bNumber); + /* Convert to OpenSc-index, convert to pin/key number */ + switch(bMethod){ + case SC_AC_NONE: /* always allowed */ + bCommands_always |= 1; + break; + case SC_AC_CHV: /* pin */ + if (bNumber == 0 || bNumber > 7) { + sc_error(card->ctx, "SetCOS 4.4 PIN refs can only be 1..7\n"); + return SC_ERROR_INVALID_ARGUMENTS; + } + bPinNumber = bNumber; + bCommands_pin[setcos_pin_index_44(pins, sizeof(pins), (int) bNumber)] |= 1 << i; + break; + case SC_AC_TERM: /* key */ + bKeyNumber = bNumber; /* There should be only 1 key */ + bCommands_key |= 1; + break; + } + } + + /* Add the commands that are allways allowed */ + if (bCommands_always) { + bBuf[len++] = 1; + bBuf[len++] = bCommands_always; + } + /* Add commands that require pins */ + for (i = 0; i < sizeof(bCommands_pin) && pins[i] != -1; i++) { + bBuf[len++] = 2; + bBuf[len++] = bCommands_pin[i]; + bBuf[len++] = pins[i] & 0x07; /* pin ref */ + } + /* Add ommands that require the key */ + if (bCommands_key) { + bBuf[len++] = 2 | 0x20; /* indicate keyNumber present */ + bBuf[len++] = bCommands_key; + bBuf[len++] = bKeyNumber; + } + /* RSA signing/decryption requires AC adaptive coding, can't be put + in AC simple coding. Only implemented for pins, not for a key. */ + bPinNumber = 0; + bKeyNumber = 0; + if ( (file->type == SC_FILE_TYPE_INTERNAL_EF) && + (acl_to_byte_44(file->acl[SC_AC_OP_CRYPTO], &bNumber) == SC_AC_CHV) ) { + bBuf[len++] = 0x83; + bBuf[len++] = 0x01; + bBuf[len++] = 0x2A; /* INS byte for the sign/decrypt APDU */ + bBuf[len++] = bNumber & 0x07; /* pin ref */ + } + + sc_file_set_sec_attr(file, bBuf, len); + } + + return iso_ops->create_file(card, file); +} + static int setcos_create_file(sc_card_t *card, sc_file_t *file) { + if (card->type == SC_CARD_TYPE_SETCOS_44) + return setcos_create_file_44(card, file); + if (file->prop_attr_len == 0) sc_file_set_prop_attr(file, (const u8 *) "\x03\x00\x00", 3); if (file->sec_attr_len == 0) { @@ -227,6 +509,18 @@ static int setcos_set_security_env2(sc_card_t *card, int r, locked = 0; assert(card != NULL && env != NULL); + + if (card->type == SC_CARD_TYPE_SETCOS_44) { + if (env->flags & SC_SEC_ENV_KEY_REF_ASYMMETRIC) { + sc_error(card->ctx, "asymmetric keyref not supported.\n"); + return SC_ERROR_NOT_SUPPORTED; + } + if (se_num > 0) { + sc_error(card->ctx, "restore security environment not supported.\n"); + return SC_ERROR_NOT_SUPPORTED; + } + } + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0, 0); switch (env->operation) { case SC_SEC_OPERATION_DECIPHER: @@ -236,7 +530,8 @@ static int setcos_set_security_env2(sc_card_t *card, break; case SC_SEC_OPERATION_SIGN: /* Should be 0x41 */ - apdu.p1 = (card->type == SC_CARD_TYPE_SETCOS_FINEID_V2) ? 0x41 : 0x81; + apdu.p1 = ((card->type == SC_CARD_TYPE_SETCOS_FINEID_V2) || + (card->type == SC_CARD_TYPE_SETCOS_44)) ? 0x41 : 0x81; apdu.p2 = 0xB6; break; default: @@ -316,6 +611,7 @@ static int setcos_set_security_env(sc_card_t *card, case SC_CARD_TYPE_SETCOS_PKI: case SC_CARD_TYPE_SETCOS_FINEID: case SC_CARD_TYPE_SETCOS_FINEID_V2: + case SC_CARD_TYPE_SETCOS_44: break; default: sc_error(card->ctx, "Card does not support RSA.\n"); @@ -392,6 +688,147 @@ static void parse_sec_attr(sc_file_t *file, const u8 * buf, size_t len) add_acl_entry(file, idx[i], buf[i]); } +static void parse_sec_attr_44(sc_file_t *file, const u8 *buf, size_t len) +{ + /* OpenSc Operation values for each command operation-type */ + const int df_idx[8] = { /* byte 1 = OpenSC type of AC Bit0, byte 2 = OpenSC type of AC Bit1 ...*/ + SC_AC_OP_DELETE, SC_AC_OP_CREATE, SC_AC_OP_CREATE, + SC_AC_OP_INVALIDATE, SC_AC_OP_REHABILITATE, + SC_AC_OP_LOCK, SC_AC_OP_DELETE, -1}; + const int ef_idx[8] = { + SC_AC_OP_READ, SC_AC_OP_UPDATE, SC_AC_OP_WRITE, + SC_AC_OP_INVALIDATE, SC_AC_OP_REHABILITATE, + -1, SC_AC_OP_ERASE, -1}; + const int efi_idx[8] = { /* internal EF used for RSA keys */ + SC_AC_OP_READ, SC_AC_OP_ERASE, SC_AC_OP_UPDATE, + SC_AC_OP_INVALIDATE, SC_AC_OP_REHABILITATE, + -1, SC_AC_OP_ERASE, -1}; + + u8 bValue; + int i; + int iKeyRef; + int iMethod; + int iPinCount; + int iOffset = 0; + int iOperation; + const int* p_idx; + + /* Check all sub-AC definitions whitin the total AC */ + while (len > 1) { /* minimum length = 2 */ + int iACLen = buf[iOffset] & 0x0F; + + iPinCount = -1; /* default no pin required */ + iMethod = SC_AC_NONE; /* default no authentication required */ + + if (buf[iOffset] & 0X80) { /* AC in adaptive coding */ + /* Evaluates only the command-byte, not the optional P1/P2/Option bytes */ + int iParmLen = 1; /* command-byte is always present */ + int iKeyLen = 0; /* Encryption key is optional */ + + if (buf[iOffset] & 0x20) iKeyLen++; + if (buf[iOffset+1] & 0x40) iParmLen++; + if (buf[iOffset+1] & 0x20) iParmLen++; + if (buf[iOffset+1] & 0x10) iParmLen++; + if (buf[iOffset+1] & 0x08) iParmLen++; + + /* Get KeyNumber if available */ + if(iKeyLen) { + int iSC = buf[iOffset+iACLen]; + + switch( (iSC>>5) & 0x03 ){ + case 0: + iMethod = SC_AC_TERM; /* key authentication */ + break; + case 1: + iMethod = SC_AC_AUT; /* key authentication */ + break; + case 2: + case 3: + iMethod = SC_AC_PRO; /* secure messaging */ + break; + } + iKeyRef = iSC & 0x1F; /* get key number */ + } + + /* Get PinNumber if available */ + if (iACLen > (1+iParmLen+iKeyLen)) { /* check via total length if pin is present */ + iKeyRef = buf[iOffset+1+1+iParmLen]; /* PTL + AM-header + parameter-bytes */ + iMethod = SC_AC_CHV; + } + + /* Convert SETCOS command to OpenSC command group */ + switch(buf[iOffset+2]){ + case 0x2A: /* crypto operation */ + iOperation = SC_AC_OP_CRYPTO; + break; + case 0x46: /* key-generation operation */ + iOperation = SC_AC_OP_UPDATE; + break; + default: + iOperation = SC_AC_OP_SELECT; + break; + } + sc_file_add_acl_entry(file, iOperation, iMethod, iKeyRef); + } + else { /* AC in simple coding */ + /* Initial AC is treated as an operational AC */ + + /* Get specific Cmd groups for specified file-type */ + switch (file->type) { + case SC_FILE_TYPE_DF: /* DF */ + p_idx = df_idx; + break; + case SC_FILE_TYPE_INTERNAL_EF: /* EF for RSA keys */ + p_idx = efi_idx; + break; + default: /* EF */ + p_idx = ef_idx; + break; + } + + /* Encryption key present ? */ + iPinCount = iACLen - 1; + + if (buf[iOffset] & 0x20) { + int iSC = buf[iOffset + iACLen]; + + switch( (iSC>>5) & 0x03 ) { + case 0: + iMethod = SC_AC_TERM; /* key authentication */ + break; + case 1: + iMethod = SC_AC_AUT; /* key authentication */ + break; + case 2: + case 3: + iMethod = SC_AC_PRO; /* secure messaging */ + break; + } + iKeyRef = iSC & 0x1F; /* get key number */ + + iPinCount--; /* one byte used for keyReference */ + } + + /* Pin present ? */ + if ( iPinCount > 0 ) { + iKeyRef = (buf[iOffset + 2] & 0x7); /* pin ref */ + iMethod = SC_AC_CHV; + } + + /* Add AC for each command-operationType into OpenSc structure */ + bValue = buf[iOffset + 1]; + for (i = 0; i < 8; i++) { + if((bValue & 1) && (p_idx[i] >= 0)) + sc_file_add_acl_entry(file, p_idx[i], iMethod, iKeyRef); + bValue >>= 1; + } + } + /* Current field treated, get next AC sub-field */ + iOffset += iACLen +1; /* AC + PTL-byte */ + len -= iACLen +1; + } +} + static int setcos_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file) { @@ -400,8 +837,12 @@ static int setcos_select_file(sc_card_t *card, r = iso_ops->select_file(card, in_path, file); if (r) return r; - if (file != NULL) - parse_sec_attr(*file, (*file)->sec_attr, (*file)->sec_attr_len); + if (file != NULL) { + if (card->type == SC_CARD_TYPE_SETCOS_44) + parse_sec_attr_44(*file, (*file)->sec_attr, (*file)->sec_attr_len); + else + parse_sec_attr(*file, (*file)->sec_attr, (*file)->sec_attr_len); + } return 0; } @@ -411,16 +852,194 @@ static int setcos_list_files(sc_card_t *card, u8 * buf, size_t buflen) int r; sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xAA, 0, 0); + if (card->type == SC_CARD_TYPE_SETCOS_44) + apdu.cla = 0x80; apdu.resp = buf; apdu.resplen = buflen; apdu.le = buflen > 256 ? 256 : buflen; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + if (card->type == SC_CARD_TYPE_SETCOS_44 && apdu.sw1 == 0x6A && apdu.sw2 == 0x82) + return 0; /* no files found */ if (apdu.resplen == 0) return sc_check_sw(card, apdu.sw1, apdu.sw2); return apdu.resplen; } +static int setcos_process_fci(sc_card_t *card, sc_file_t *file, + const u8 *buf, size_t buflen) +{ + int r = iso_ops->process_fci(card, file, buf, buflen); + + /* SetCOS 4.4: RSA key file is an internal EF but it's + * file descriptor doesn't seem to follow ISO7816. */ + if (r >= 0 && card->type == SC_CARD_TYPE_SETCOS_44) { + const u8 *tag; + size_t taglen = 1; + tag = (u8 *) sc_asn1_find_tag(card->ctx, buf, buflen, 0x82, &taglen); + if (tag != NULL && taglen == 1 && *tag == 0x11) + file->type = SC_FILE_TYPE_INTERNAL_EF; + } + + return r; +} + +/* Write internal data, e.g. add default pin-records to pin-file */ +setcos_putdata(struct sc_card *card, struct sc_cardctl_setcos_data_obj* data_obj) +{ + int r; + struct sc_apdu apdu; + + SC_FUNC_CALLED(card->ctx, 1); + + memset(&apdu, 0, sizeof(apdu)); + apdu.cse = SC_APDU_CASE_3_SHORT; + apdu.cla = 0x00; + apdu.ins = 0xDA; + apdu.p1 = data_obj->P1; + apdu.p2 = data_obj->P2; + apdu.lc = data_obj->DataLen; + apdu.datalen = data_obj->DataLen; + apdu.data = data_obj->Data; + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + SC_TEST_RET(card->ctx, r, "PUT_DATA returned error"); + + SC_FUNC_RETURN(card->ctx, 1, r); +} + +/* Read internal data, e.g. get RSA public key */ +setcos_getdata(struct sc_card *card, struct sc_cardctl_setcos_data_obj* data_obj) +{ + int r; + struct sc_apdu apdu; + + SC_FUNC_CALLED(card->ctx, 1); + + memset(&apdu, 0, sizeof(apdu)); + apdu.cse = SC_APDU_CASE_2_SHORT; + apdu.cla = 0x00; + apdu.ins = 0xCA; /* GET DATA */ + apdu.p1 = data_obj->P1; + apdu.p2 = data_obj->P2; + apdu.lc = 0; + apdu.datalen = 0; + apdu.data = data_obj->Data; + + apdu.le = 256; + apdu.resp = data_obj->Data; + apdu.resplen = data_obj->DataLen; + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + SC_TEST_RET(card->ctx, r, "GET_DATA returned error"); + + if (apdu.resplen > data_obj->DataLen) + r = SC_ERROR_WRONG_LENGTH; + else + data_obj->DataLen = apdu.resplen; + + SC_FUNC_RETURN(card->ctx, 1, r); +} + +/* Generate or store a key */ +static int setcos_generate_store_key(sc_card_t *card, + struct sc_cardctl_setcos_gen_store_key_info *data) +{ + struct sc_apdu apdu; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + int r, p2, len; + + SC_FUNC_CALLED(card->ctx, 1); + + /* Setup key-generation paramters */ + len = 0; + if (data->op_type == OP_TYPE_GENERATE) + sbuf[len++] = 0x92; /* algo ID: RSA CRT */ + else + sbuf[len++] = 0x9A; /* algo ID: EXTERNALLY GENERATED RSA CRT */ + sbuf[len++] = 0x00; + sbuf[len++] = data->mod_len / 256; /* 2 bytes for modulus bitlength */ + sbuf[len++] = data->mod_len % 256; + + sbuf[len++] = data->pubexp_len / 256; /* 2 bytes for pubexp bitlength */ + sbuf[len++] = data->pubexp_len % 256; + memcpy(sbuf + len, data->pubexp, (data->pubexp_len + 7) / 8); + len += (data->pubexp_len + 7) / 8; + + if (data->op_type == OP_TYPE_STORE) { + sbuf[len++] = data->primep_len / 256; + sbuf[len++] = data->primep_len % 256; + memcpy(sbuf + len, data->primep, (data->primep_len + 7) / 8); + len += (data->primep_len + 7) / 8; + sbuf[len++] = data->primeq_len / 256; + sbuf[len++] = data->primeq_len % 256; + memcpy(sbuf + len, data->primeq, (data->primeq_len + 7) / 8); + len += (data->primeq_len + 7) / 8; + } + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x46, 0x00, 0x00); + apdu.cla = 0x00; + apdu.data = sbuf; + apdu.datalen = len; + apdu.lc = len; + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + SC_TEST_RET(card->ctx, r, "STORE/GENERATE_KEY returned error"); + + SC_FUNC_RETURN(card->ctx, 1, r); +} + +static int setcos_activate_file(sc_card_t *card) +{ + int r; + u8 sbuf[2]; + sc_apdu_t apdu; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x44, 0x00, 0x00); + apdu.data = sbuf; + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + SC_TEST_RET(card->ctx, r, "ACTIVATE_FILE returned error"); + + SC_FUNC_RETURN(card->ctx, 1, r); +} + +static int setcos_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) +{ + if (card->type != SC_CARD_TYPE_SETCOS_44) + return SC_ERROR_NOT_SUPPORTED; + + switch(cmd) { + case SC_CARDCTL_SETCOS_PUTDATA: + return setcos_putdata(card, + (struct sc_cardctl_setcos_data_obj*) ptr); + break; + case SC_CARDCTL_SETCOS_GETDATA: + return setcos_getdata(card, + (struct sc_cardctl_setcos_data_obj*) ptr); + break; + case SC_CARDCTL_SETCOS_GENERATE_STORE_KEY: + return setcos_generate_store_key(card, + (struct sc_cardctl_setcos_gen_store_key_info *) ptr); + case SC_CARDCTL_SETCOS_ACTIVATE_FILE: + return setcos_activate_file(card); + } + + return SC_ERROR_NOT_SUPPORTED; +} + #if 0 static int setcos_logout(sc_card_t *card) { @@ -442,6 +1061,9 @@ static struct sc_card_driver *sc_get_driver(void) setcos_ops.set_security_env = setcos_set_security_env; setcos_ops.select_file = setcos_select_file; setcos_ops.list_files = setcos_list_files; + setcos_ops.process_fci = setcos_process_fci; + setcos_ops.construct_fci = setcos_construct_fci; + setcos_ops.card_ctl = setcos_card_ctl; #if 0 setcos_ops.logout = setcos_logout; #endif diff --git a/src/libopensc/cardctl.h b/src/libopensc/cardctl.h index b6b03dac..ef1d3e8d 100644 --- a/src/libopensc/cardctl.h +++ b/src/libopensc/cardctl.h @@ -101,6 +101,15 @@ enum { SC_CARDCTL_OBERTHUR_UPDATE_KEY, SC_CARDCTL_OBERTHUR_GENERATE_KEY, SC_CARDCTL_OBERTHUR_CREATE_PIN, + + /* + * Setcos specific calls + */ + SC_CARDCTL_SETCOS_BASE = _CTL_PREFIX('S', 'E', 'T'), + SC_CARDCTL_SETCOS_PUTDATA, + SC_CARDCTL_SETCOS_GETDATA, + SC_CARDCTL_SETCOS_GENERATE_STORE_KEY, + SC_CARDCTL_SETCOS_ACTIVATE_FILE, }; enum { @@ -287,6 +296,31 @@ struct sc_cardctl_oberthur_createpin_info { unsigned int puk_tries; }; +/* + * Setcos stuff + */ +struct sc_cardctl_setcos_data_obj { + int P1; + int P2; + u8 * Data; + size_t DataLen; + int LengthMax; +}; + +#define OP_TYPE_GENERATE 0 +#define OP_TYPE_STORE 1 + +struct sc_cardctl_setcos_gen_store_key_info { + int op_type; + unsigned int mod_len; /* in bits */ + unsigned int pubexp_len; /* in bits */ + unsigned char *pubexp; + unsigned int primep_len; /* in bits */ + unsigned char *primep; + unsigned int primeq_len; /* in bits */ + unsigned char *primeq; +}; + #ifdef __cplusplus } #endif diff --git a/src/libopensc/cards.h b/src/libopensc/cards.h index 6aa36d9f..b091b510 100644 --- a/src/libopensc/cards.h +++ b/src/libopensc/cards.h @@ -74,6 +74,7 @@ enum { SC_CARD_TYPE_SETCOS_PKI, SC_CARD_TYPE_SETCOS_FINEID, SC_CARD_TYPE_SETCOS_FINEID_V2, + SC_CARD_TYPE_SETCOS_44 = 6100, /* starcos driver */ SC_CARD_TYPE_STARCOS_BASE = 7000, diff --git a/src/pkcs15init/Makefile.am b/src/pkcs15init/Makefile.am index 5f134e82..5ea019ca 100644 --- a/src/pkcs15init/Makefile.am +++ b/src/pkcs15init/Makefile.am @@ -26,7 +26,7 @@ libpkcs15init_la_SOURCES = \ pkcs15-lib.c profile.c keycache.c \ pkcs15-gpk.c pkcs15-miocos.c pkcs15-cflex.c \ pkcs15-etoken.c pkcs15-jcop.c pkcs15-starcos.c \ - pkcs15-oberthur.c + pkcs15-oberthur.c pkcs15-setcos.c libpkcs15init_la_LDFLAGS = -version-info @OPENSC_LT_CURRENT@:@OPENSC_LT_REVISION@:@OPENSC_LT_AGE@ diff --git a/src/pkcs15init/Makefile.mak b/src/pkcs15init/Makefile.mak index a795d18f..9ea054f1 100644 --- a/src/pkcs15init/Makefile.mak +++ b/src/pkcs15init/Makefile.mak @@ -8,7 +8,7 @@ HEADERSDIR = $(TOPDIR)\src\include\opensc OBJECTS = profile.obj pkcs15-lib.obj keycache.obj \ pkcs15-miocos.obj pkcs15-gpk.obj pkcs15-cflex.obj \ pkcs15-etoken.obj pkcs15-jcop.obj pkcs15-starcos.obj \ - pkcs15-oberthur.obj + pkcs15-oberthur.obj pkcs15-setcos.obj all: install-headers $(TARGET) diff --git a/src/pkcs15init/pkcs15-init.h b/src/pkcs15init/pkcs15-init.h index 7c403a7a..777ebba0 100644 --- a/src/pkcs15init/pkcs15-init.h +++ b/src/pkcs15init/pkcs15-init.h @@ -368,6 +368,7 @@ extern struct sc_pkcs15init_operations *sc_pkcs15init_get_etoken_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_jcop_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_starcos_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_oberthur_ops(void); +extern struct sc_pkcs15init_operations *sc_pkcs15init_get_setcos_ops(void); #ifdef __cplusplus } diff --git a/src/pkcs15init/pkcs15-lib.c b/src/pkcs15init/pkcs15-lib.c index 104eeba1..0b4305ac 100644 --- a/src/pkcs15init/pkcs15-lib.c +++ b/src/pkcs15init/pkcs15-lib.c @@ -142,6 +142,7 @@ static struct profile_operations { { "jcop", (void *) sc_pkcs15init_get_jcop_ops }, { "starcos", (void *) sc_pkcs15init_get_starcos_ops }, { "oberthur", (void *) sc_pkcs15init_get_oberthur_ops }, + { "setcos", (void *) sc_pkcs15init_get_setcos_ops }, { NULL, NULL }, }; diff --git a/src/pkcs15init/pkcs15-setcos.c b/src/pkcs15init/pkcs15-setcos.c new file mode 100644 index 00000000..91348d92 --- /dev/null +++ b/src/pkcs15init/pkcs15-setcos.c @@ -0,0 +1,582 @@ +/* + * SetOCS 4.4 specific operations for PKCS15 initialization + * + * Copyright (C) 2003, 2005 Zetes + * + * 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 +#endif +#include +#include +#include +#include +#include +#include +#include "pkcs15-init.h" +#include "keycache.h" +#include "profile.h" + +#define SETCOS_MAX_PINS 7 + +unsigned char SETCOS_DEFAULT_PUBKEY[] = {0x01, 0x00, 0x01}; +#define SETCOS_DEFAULT_PUBKEY_LEN sizeof(SETCOS_DEFAULT_PUBKEY) + +static int setcos_generate_store_key( sc_profile_t *, sc_card_t *, + unsigned int, unsigned int, sc_pkcs15_pubkey_t *, sc_pkcs15_prkey_t *, + sc_pkcs15_prkey_info_t *); + +static int setcos_create_pin_internal(sc_profile_t *, sc_card_t *, + int, sc_pkcs15_pin_info_t *, const u8 *, size_t, const u8 *, size_t); + +static int setcos_puk_retries(sc_profile_t *, int); + +/* + * Erase the card. + */ +static int setcos_erase_card(sc_profile_t *profile, sc_card_t *card) +{ + sc_pkcs15_pin_info_t pin_info; + sc_path_t path; + int r; + + /* Just delete the entire MF */ + + /* The SO pin has pin reference 1 -- not that it matters much + * because pkcs15-init will ask to enter all pins, even if we + * did a --so-pin on the command line. */ + sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &pin_info); + sc_keycache_set_pin_name(NULL, pin_info.reference, SC_PKCS15INIT_SO_PIN); + + /* Select parent DF and verify PINs/key as necessary */ + r = sc_pkcs15init_authenticate(profile, card, + profile->mf_info->file, SC_AC_OP_DELETE); + if (r < 0) + return r == SC_ERROR_FILE_NOT_FOUND ? 0 : r; + + /* Empty path -> we have to to delete the current DF (= the MF) */ + memset(&path, 0, sizeof(sc_path_t)); + r = sc_delete_file(card, &path) ; + + return r; +} + +#if 0 /* New API, turned out to be more work wrt setting the + life cycle state to SC_FILE_STATUS_ACTIVATED. */ +/* + * Create the MF and global pin file if they don't exist. + */ +static int +setcos_init_card(sc_profile_t *profile, sc_card_t *card) +{ + sc_file_t *mf = profile->mf_info->file; + sc_file_t *pinfile; + int pin_ref; + int r; + + /* The SO pin in the keycache is only linked to the pkcs15 DF, + * we'll re-ink it to the MF. */ + pin_ref = sc_keycache_find_named_pin(&profile->df_info->file->path, + SC_PKCS15INIT_SO_PIN); + if (pin_ref >= 0) + sc_keycache_set_pin_name(&profile->mf_info->file->path, + pin_ref, SC_PKCS15INIT_SO_PIN); + + /* Create the MF if it doesn't exist yet */ + card->ctx->suppress_errors++; + r = sc_select_file(card, &mf->path, NULL); + card->ctx->suppress_errors--; + if (r == SC_ERROR_FILE_NOT_FOUND) { + sc_debug(card->ctx, "MF doesn't exist, creating now"); + + /* Fix up the file's ACLs */ + r = sc_pkcs15init_fixup_file(profile, mf); + if (r >= 0) + r = sc_create_file(card, mf); + } + if (r < 0) + return r; + + /* Create the global pin file if it doesn't exist yet */ + r = sc_profile_get_file(profile, "pinfile", &pinfile); + if (r < 0) + return r; + card->ctx->suppress_errors++; + r = sc_select_file(card, &pinfile->path, NULL); + card->ctx->suppress_errors--; + if (r == SC_ERROR_FILE_NOT_FOUND) { + sc_debug(card->ctx, "Global pin file doesn't exist, creating now"); + + /* Fix up the file's ACLs */ + r = sc_pkcs15init_fixup_file(profile, pinfile); + /* Set life cycle state to SC_FILE_STATUS_CREATION, + * which means that all ACs are ignored. */ + if (r >= 0) + r = sc_create_file(card, pinfile); + } + sc_file_free(pinfile); + + /* Re-link the SO-PIN back to the original DF (= the pkcs15 DF) */ + sc_keycache_set_pin_name(&profile->df_info->file->path, + pin_ref, SC_PKCS15INIT_SO_PIN); + + return r; +} + +/* + * Create a DF + */ +static int +setcos_create_dir(sc_profile_t *profile, sc_card_t *card, sc_file_t *df) +{ + return sc_pkcs15init_create_file(profile, card, df); +} +#endif + +/* + * Create the MF and global pin file if they don't exist. + */ +static int setcos_init_app(sc_profile_t *profile, sc_card_t *card, + sc_pkcs15_pin_info_t *pin_info, + const u8 *pin, size_t pin_len, + const u8 *puk, size_t puk_len) +{ + sc_file_t *mf = profile->mf_info->file; + sc_file_t *pinfile = NULL; + int pin_ref; + int r; + + /* The SO pin in the keycache is only linked to the pkcs15 DF, + * we'll re-link it to the MF. */ + pin_ref = sc_keycache_find_named_pin(&profile->df_info->file->path, + SC_PKCS15INIT_SO_PIN); + if (pin_ref >= 0) + sc_keycache_set_pin_name(&profile->mf_info->file->path, + pin_ref, SC_PKCS15INIT_SO_PIN); + + /* Create the MF if it doesn't exist yet */ + card->ctx->suppress_errors++; + r = sc_select_file(card, &mf->path, NULL); + card->ctx->suppress_errors--; + if (r == SC_ERROR_FILE_NOT_FOUND) { + sc_debug(card->ctx, "MF doesn't exist, creating now"); + /* Fix up the file's ACLs */ + if ((r = sc_pkcs15init_fixup_file(profile, mf)) >= 0) { + /* Set life cycle state to SC_FILE_STATUS_CREATION, + * which means that all ACs are ignored. */ + mf->status = SC_FILE_STATUS_CREATION; + r = sc_create_file(card, mf); + } + } + if (r < 0) + return r; + + /* Create the global pin file if it doesn't exist yet */ + if ((r = sc_profile_get_file(profile, "pinfile", &pinfile)) < 0) + goto done; + card->ctx->suppress_errors++; + r = sc_select_file(card, &pinfile->path, NULL); + card->ctx->suppress_errors--; + if (r == SC_ERROR_FILE_NOT_FOUND) { + sc_debug(card->ctx, "Global pin file doesn't exist, creating now"); + /* Fix up the file's ACLs */ + if ((r = sc_pkcs15init_fixup_file(profile, pinfile)) >= 0) { + /* Set life cycle state to SC_FILE_STATUS_CREATION */ + pinfile->status = SC_FILE_STATUS_CREATION; + r = sc_create_file(card, pinfile); + } + } + if (r < 0) + goto done; + + /* Set the SO pin/puk values into the pin file */ + r = setcos_create_pin_internal(profile, card, 1, pin_info, + pin, pin_len, puk, puk_len); + if (r < 0) + goto done; + + /* OK, now we can change the life cycle state to SC_FILE_STATUS_ACTIVATED + * so the normal ACs on the pinfile and MF apply. */ + if ((r = sc_select_file(card, &pinfile->path, NULL)) >= 0) /* pinfile */ + r = sc_card_ctl(card, SC_CARDCTL_SETCOS_ACTIVATE_FILE, NULL); + if (r < 0) + goto done; + if ((r = sc_select_file(card, &mf->path, NULL)) >= 0) /* MF */ + r = sc_card_ctl(card, SC_CARDCTL_SETCOS_ACTIVATE_FILE, NULL); + if (r < 0) + goto done; + +done: + if (pinfile) + free(pinfile); + + return r; +} + +/* + * Select the PIN reference + */ +static int +setcos_select_pin_reference(sc_profile_t *profile, sc_card_t *card, + sc_pkcs15_pin_info_t *pin_info) +{ + sc_pkcs15_pin_info_t pin_info_prof; + + pin_info_prof.reference = 1; /* Default SO PIN ref. */ + sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &pin_info_prof); + + /* For the SO pin, we take the first available pin reference = 1 */ + if (pin_info->flags & SC_PKCS15_PIN_FLAG_SO_PIN) + pin_info->reference = pin_info_prof.reference; + /* sc_pkcs15init_create_pin() starts checking if 0 is an acceptable + * pin reference, which isn't for the SetCOS cards. And since the + * value 1 has been assigned to the SO pin, we'll jump to 2. */ + else if (pin_info->reference == 0) + pin_info->reference = pin_info_prof.reference + 1; + + return 0; +} + +/* + * Create a new PIN + */ +static int +setcos_create_pin(sc_profile_t *profile, sc_card_t *card, + sc_file_t *df, + sc_pkcs15_object_t *pin_obj, + const u8 *pin, size_t pin_len, + const u8 *puk, size_t puk_len) +{ + return setcos_create_pin_internal(profile, card, 0, + (sc_pkcs15_pin_info_t *) pin_obj->data, + pin, pin_len, puk, puk_len); +} + +/* + * Setup file struct & path: get correct template from the profile, construct full path + */ +static int +setcos_new_file(sc_profile_t *profile, sc_card_t *card, + unsigned int type, + unsigned int num, /* number of objects of this type already on the card */ + sc_file_t **out) +{ + sc_file_t *file; + sc_path_t *p; + char name[64], *tag; + + if ((type & SC_PKCS15_TYPE_PRKEY_RSA) == SC_PKCS15_TYPE_PRKEY_RSA) + tag = "private-key"; + else if ((type & SC_PKCS15_TYPE_PRKEY) == SC_PKCS15_TYPE_PRKEY) + tag = "extractable-key"; + else if ((type & SC_PKCS15_TYPE_PUBKEY_RSA) == SC_PKCS15_TYPE_PUBKEY_RSA) + tag = "public-key"; + else if ((type & SC_PKCS15_TYPE_CERT) == SC_PKCS15_TYPE_CERT) + tag = "certificate"; + else if ((type & SC_PKCS15_TYPE_DATA_OBJECT) == SC_PKCS15_TYPE_DATA_OBJECT) + tag = "data"; + else { + sc_error(card->ctx, "Unsupported file type"); + return SC_ERROR_INVALID_ARGUMENTS; + } + + /* Get template from profile */ + snprintf(name, sizeof(name), "template-%s", tag); + if (sc_profile_get_file(profile, name, &file) < 0) { + sc_error(card->ctx, "Profile doesn't define %s", name); + return SC_ERROR_NOT_SUPPORTED; + } + + /* Auto-increment FID for next object */ + file->id += num; + p = &file->path; + *p = profile->df_info->file->path; + p->value[p->len++] = (u8) (file->id / 256); + p->value[p->len++] = (u8) (file->id % 256); + + *out = file; + return 0; +} + +static int +setcos_encode_private_key(sc_profile_t *profile, sc_card_t *card, + struct sc_pkcs15_prkey_rsa *rsa, + u8 *key, size_t *keysize, int key_ref) +{ + return 0; +} + +static int +setcos_encode_public_key(sc_profile_t *profile, sc_card_t *card, + struct sc_pkcs15_prkey_rsa *rsa, + u8 *key, size_t *keysize, int key_ref) +{ + return 0; +} + +/* + * Generate RSA key + */ +static int +setcos_old_generate_key(sc_profile_t *profile, sc_card_t *card, + unsigned int index, /* keyref: 0 for 1st key, ... */ + unsigned int keybits, + sc_pkcs15_pubkey_t *pubkey, + struct sc_pkcs15_prkey_info *info) +{ + return setcos_generate_store_key(profile, card, index, + keybits, pubkey, + NULL, info); +} + +/* + * Store RSA key + */ +static int +setcos_new_key(sc_profile_t *profile, sc_card_t *card, + struct sc_pkcs15_prkey *key, unsigned int index, + struct sc_pkcs15_prkey_info *info) +{ + return setcos_generate_store_key(profile, card, index, + key->u.rsa.modulus.len * 8, NULL, + key, info); +} + +/* + * Common code for generating or storing a private key. + * If pubkey == NULL and prkey != NULL, we have to store a private key + * In the oposite case, we have to generate a private key + */ +static int +setcos_generate_store_key(sc_profile_t *profile, sc_card_t *card, + unsigned int index, /* keynumber: 0 for 1st priv key, ... */ + unsigned int keybits, + sc_pkcs15_pubkey_t *pubkey, + sc_pkcs15_prkey_t *prkey, + sc_pkcs15_prkey_info_t *info) +{ + struct sc_cardctl_setcos_gen_store_key_info args; + struct sc_cardctl_setcos_data_obj data_obj; + unsigned char raw_pubkey[256]; + unsigned char pinbuf[12]; + int r, mod_len; + sc_file_t *prkf = NULL; + u8 bData[32]; + + /* Parameter check */ + if ( (keybits < 512) || (keybits > 1024) || (keybits & 0X7)) { + sc_error(card->ctx, "Unsupported key size [%u]: 512-1024 bit + 8-multiple\n", keybits); + return SC_ERROR_INVALID_ARGUMENTS; + } + + /* Get the private key file */ + r = setcos_new_file(profile, card, SC_PKCS15_TYPE_PRKEY_RSA, index, &prkf); + if (r < 0) + goto done; + + /* Take enough room for a 1024 bit key */ + if (prkf->size < 512) + prkf->size = 512; + + /* Now create the key file */ + r = sc_pkcs15init_create_file(profile, card, prkf); + if (r < 0) + goto done; + + /* Fill in data structure */ + memset(&args, 0, sizeof(args)); + args.mod_len = keybits; + if (prkey == NULL) { + args.op_type = OP_TYPE_GENERATE; + args.pubexp_len = SETCOS_DEFAULT_PUBKEY_LEN * 8; + args.pubexp = SETCOS_DEFAULT_PUBKEY; + } + else { + args.op_type = OP_TYPE_STORE; + args.pubexp_len = prkey->u.rsa.exponent.len * 8; + args.pubexp = prkey->u.rsa.exponent.data; + args.primep_len = prkey->u.rsa.p.len * 8; + args.primep = prkey->u.rsa.p.data; + args.primeq_len = prkey->u.rsa.q.len * 8; + args.primeq = prkey->u.rsa.q.data; + } + + /* Authenticate */ + r = sc_pkcs15init_authenticate(profile, card, prkf, SC_AC_OP_UPDATE); + if (r < 0) + goto done; + + /* Generate/store rsa key */ + r = sc_card_ctl(card, SC_CARDCTL_SETCOS_GENERATE_STORE_KEY, &args); + if (r < 0) + goto done; + + /* Keypair generation -> collect public key info */ + if (pubkey != NULL) { + pubkey->algorithm = SC_ALGORITHM_RSA; + pubkey->u.rsa.modulus.len = (keybits + 7) / 8; + pubkey->u.rsa.modulus.data = (u8 *) malloc(pubkey->u.rsa.modulus.len); + pubkey->u.rsa.exponent.len = SETCOS_DEFAULT_PUBKEY_LEN; + pubkey->u.rsa.exponent.data = (u8 *) malloc(SETCOS_DEFAULT_PUBKEY_LEN); + memcpy(pubkey->u.rsa.exponent.data, SETCOS_DEFAULT_PUBKEY, SETCOS_DEFAULT_PUBKEY_LEN); + + /* Get public key modulus */ + if ( (r = sc_select_file(card, &prkf->path, NULL)) < 0) + goto done; + + data_obj.P1 = 01; + data_obj.P2 = 01; + data_obj.Data = raw_pubkey; + data_obj.DataLen = sizeof(raw_pubkey); + + if ((r = sc_card_ctl(card, SC_CARDCTL_SETCOS_GETDATA, &data_obj)) < 0) + goto done; + + mod_len = ((raw_pubkey[0] * 256) + raw_pubkey[1]); /* modulus bit length */ + if (mod_len != keybits){ + sc_error(card->ctx, "key-size from card[%i] does not match[%i]\n", mod_len, keybits); + r = SC_ERROR_PKCS15INIT; + goto done; + } + memcpy (pubkey->u.rsa.modulus.data, &raw_pubkey[2], pubkey->u.rsa.modulus.len); + } + + info->key_reference = 0; + info->path = prkf->path; + +done: + if (prkf) + sc_file_free(prkf); + + return r; +} + +/* + * Create a new PIN + */ +static int +setcos_create_pin_internal(sc_profile_t *profile, sc_card_t *card, + int ignore_ac, sc_pkcs15_pin_info_t *pin_info, + const u8 *pin, size_t pin_len, + const u8 *puk, size_t puk_len) +{ + u8 data[32]; + int so_pin_ref; + int r; + int pin_type; + struct sc_cardctl_setcos_data_obj data_obj; + sc_file_t *pinfile = NULL; + + if (pin_info->reference >= SETCOS_MAX_PINS) + return SC_ERROR_INVALID_ARGUMENTS; + if (pin == NULL || puk == NULL || pin_len < 4 || puk_len < 4) + return SC_ERROR_INVALID_PIN_LENGTH; + + /* Verify required access rights if needed (i.e. if the + * pin file isn't in the CREATE life cycle state). */ + if (!ignore_ac) { + /* Re-ink the SO pin to the MF because there is the pin file */ + so_pin_ref = sc_keycache_find_named_pin(&profile->df_info->file->path, + SC_PKCS15INIT_SO_PIN); + if (so_pin_ref >= 0) + sc_keycache_set_pin_name(&profile->mf_info->file->path, + so_pin_ref, SC_PKCS15INIT_SO_PIN); + + r = sc_profile_get_file(profile, "pinfile", &pinfile); + if (r >= 0) + r = sc_pkcs15init_authenticate(profile, card, pinfile, SC_AC_OP_UPDATE); + sc_file_free(pinfile); + if (r < 0) + return r; + } + + /* Make command to add a pin-record */ + + data_obj.P1 = 01; + data_obj.P2 = 01; + + /* setcos pin number */ + data[0] = pin_info->reference; + + memset(&data[1], pin_info->pad_char, 16); /* padding */ + memcpy(&data[1], (u8 *)pin, pin_len); /* copy pin*/ + memcpy(&data[9], (u8 *)puk, puk_len); /* copy puk */ + + data[17] = pin_info->tries_left & 0x0F; + data[18] = pin_info->tries_left & 0x0F; + /* 0xF0: unlimited unblock tries */ + data[19] = 0xF0 | setcos_puk_retries(profile, pin_info->reference); + + /* Allow an unlimited number of signatures after a pin verification. + * If set to 1 or so, we would have a UserConsent PIN. */ + data[20] = 0x00; + + if (pin_info->type == 0) + data[21] = 0x01; /* BCD */ + else + data[21] = 0x00; /* ASCII */ + if ((pin_info->flags & 0x010) == 0) /* test for initial pin */ + data[21] |= 0x80; + + data[22] = 0x00; /* not used */ + data[23] = 0x00; /* not used */ + + data_obj.Data = data; + data_obj.DataLen = 24; + + r = sc_card_ctl(card, SC_CARDCTL_SETCOS_PUTDATA, &data_obj); + + return r; +} + +static int setcos_puk_retries(sc_profile_t *profile, int pin_ref) +{ + sc_pkcs15_pin_info_t pin_info; + + pin_info.reference = 1; /* Default SO PIN ref. */ + sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &pin_info); + + /* If pin_ref is the SO PIN, get the SO PUK info, otherwise the User PUK info */ + sc_profile_get_pin_info(profile, + pin_ref == pin_info.reference ? SC_PKCS15INIT_SO_PUK : SC_PKCS15INIT_USER_PUK, + &pin_info); + + if ((pin_info.tries_left < 0) || (pin_info.tries_left > 15)) + return 3; /* Little extra safety */ + return pin_info.tries_left; +} + +static struct sc_pkcs15init_operations sc_pkcs15init_setcos_operations; + +struct sc_pkcs15init_operations * +sc_pkcs15init_get_setcos_ops(void) +{ + struct sc_pkcs15init_operations *ops; + + ops = &sc_pkcs15init_setcos_operations; + ops->erase_card = setcos_erase_card; + ops->init_app = setcos_init_app; + ops->select_pin_reference = setcos_select_pin_reference; + ops->create_pin = setcos_create_pin; + ops->old_generate_key = setcos_old_generate_key; + ops->new_key = setcos_new_key; + ops->new_file = setcos_new_file; + ops->encode_private_key = setcos_encode_private_key; + ops->encode_public_key = setcos_encode_public_key; + + return ops; +} diff --git a/src/pkcs15init/setcos.profile b/src/pkcs15init/setcos.profile new file mode 100644 index 00000000..dd6140bd --- /dev/null +++ b/src/pkcs15init/setcos.profile @@ -0,0 +1,98 @@ +# +# General purpose PKCS15 profile for SetCOS4.4 cards +# +cardinfo { + max-pin-length = 8; + pin-encoding = ascii-numeric; + pin-pad-char = 0x00; +} + +# Define reasonable limits for PINs and PUK +PIN user-pin { + attempts = 3; +} +PIN user-puk { + attempts = 5; +} + +PIN so-pin { + reference = 1; +} + +# Additional filesystem info. +# This is added to the file system info specified in the +# main profile. +filesystem { + DF MF { + ACL = *=NONE, CREATE=$SOPIN; # Allow to delete the MF + size = 42; # size = 2 + 2*(number of sub-files) -> 20 sub-files + + # There's 1 pin/key file + EF pinfile { + file-id = 0080; # Recommended by Setec + structure = 0x22; # ISF key-file, Setcos V4.4 specific + record-length = 28; + size = 112; # 28 * 4 = 112 -> 1 SO + 3 user pins/puks + ACL = WRITE=$SOPIN, UPDATE=$SOPIN; # WATCH OUT IF YOU CHANGE THESE!! + } + + DF PKCS15-AppDF { + ACL = *=$SOPIN, SELECT=NONE, FILES=NONE, CREATE=NONE; + size = 82; # size = 2 + 2*(number of sub-files) -> 40 sub-files + + EF PKCS15-PrKDF { + file-id = 4402; + size = 512; + acl = *=$SOPIN, READ=NONE; + } + + EF PKCS15-PuKDF { + file-id = 4403; + size = 512; + acl = *=$SOPIN, READ=NONE; + } + + EF PKCS15-CDF { + file-id = 4404; + size = 1024; + acl = *=$SOPIN, READ=NONE; + } + + EF PKCS15-DODF { + file-id = 4405; + size = 512; + acl = *=$SOPIN, READ=NONE; + } + + EF template-private-key { + file-id = 5101; # incremented for following objects: 5102, 5103 ... + type = internal-ef; + size = 512; # 512bit=196, 768bit=410, 1024bit:512 + ACL = *=NEVER, READ=NONE, CRYPTO=$PIN, UPDATE=$SOPIN; # READ: only for public key + } + EF template-extractable-key { + file-id = 7000; # incremented for following objects: 5102, 5103 ... + type = internal-ef; + size = 512; # 512bit=196, 768bit=410, 1024bit:512 + ACL = *=NEVER, READ=$PIN, UPDATE=$SOPIN; + } + + EF template-public-key { + file-id = 5201; # incremented for following objects: 5202, 5203 ... + ACL = *=$SOPIN, READ=NONE; + } + + EF template-certificate { + file-id = 5501; # incremented for following objects: 5502, 5503 ... + ACL = *=$SOPIN, READ=NONE; + } + + EF template-data { + file-id = 5000; + structure = transparent; + size = 1000; + ACL = *=$SOPIN, *=NONE; + } + } + } +}