card holder certificate support

This commit is contained in:
NIIBE Yutaka
2011-01-27 18:17:01 +09:00
parent f59637700a
commit 15c87a2fd1
9 changed files with 237 additions and 32 deletions

View File

@@ -1,7 +1,22 @@
2011-01-27 NIIBE Yutaka <gniibe@fsij.org>
* src/openpgp-do.c (gpg_do_table): Exclude GPG_DO_CH_CERTIFICATE
for now.
* src/usb-icc.c (res_APDU_pointer): New.
(icc_handle_data, USBthread): Handle res_APDU_pointer.
* src/openpgp.h (GPG_COMMAND_NOT_ALLOWED): New.
* src/openpgp.c (INS_UPDATE_BINARY, FILE_EF_CH_CERTIFICATE)
(FILE_EF_RANDOM, cmd_update_binary): New.
(process_command_apdu): Initialize res_APDU_pointer.
* src/openpgp-do.c (gpg_do_get_data): Handle GPG_DO_CH_CERTIFICATE.
* src/gnuk.ld.in (.gnuk_ch_certificate): New.
* src/flash.c (flash_check_blank, flash_erase_binary)
(flash_write_binary): New.
* src/openpgp-do.c (gpg_do_table): Exclude GPG_DO_CH_CERTIFICATE.
* src/openpgp.c (cmd_reset_user_password): Add PINPAD_SUPPORT.

2
NEWS
View File

@@ -4,6 +4,8 @@ Gnuk NEWS - User visible changes
Released 2011-01-XX, by NIIBE Yutaka
** Card Holder Certificate is supported.
** Better interoperability to OpenSC.
Gnuk is not yet supported by OpenSC, but it could be. With the
changes in Gnuk, it could be relatively easily possible to support

View File

@@ -1,7 +1,7 @@
/*
* flash.c -- Data Objects (DO) and GPG Key handling on Flash ROM
*
* Copyright (C) 2010 Free Software Initiative of Japan
* Copyright (C) 2010, 2011 Free Software Initiative of Japan
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Gnuk, a GnuPG USB Token implementation.
@@ -25,8 +25,8 @@
* We assume single DO size is less than 256.
*
* NOTE: When we will support "Card holder certificate" (which size is
* larger than 256), it will not be put into data pool, but will
* be implemented by its own flash page.
* larger than 256), it is not put into data pool, but is
* implemented by its own flash page(s).
*/
#include "config.h"
@@ -167,7 +167,7 @@ static const uint8_t *keystore_pool;
static uint8_t *last_p;
static const uint8_t *keystore;
/* The first halfward is generation for the data page (little endian) */
/* The first halfword is generation for the data page (little endian) */
const uint8_t const flash_data[4] __attribute__ ((section (".gnuk_data"))) = {
0x01, 0x00, 0xff, 0xff
};
@@ -581,3 +581,71 @@ flash_cnt123_clear (const uint8_t **addr_p)
flash_program_halfword ((uint32_t)p, 0);
*addr_p = NULL;
}
static int
flash_check_blank (const uint8_t *page, int size)
{
const uint8_t *p;
for (p = page; p < page + size; p++)
if (*p != 0xff)
return 0;
return 1;
}
#define FLASH_CH_CERTIFICATE_SIZE 2048
int
flash_erase_binary (uint8_t file_id)
{
const uint8_t *p = &ch_certificate_start;
if (file_id == FILEID_CH_CERTIFICATE)
{
if (flash_check_blank (p, FLASH_CH_CERTIFICATE_SIZE) == 0)
{
flash_erase_page ((uint32_t)p);
#if FLASH_CH_CERTIFICATE_SIZE > FLASH_PAGE_SIZE
flash_erase_page ((uint32_t)p + FLASH_PAGE_SIZE);
#endif
}
return 0;
}
else
return -1;
}
int
flash_write_binary (uint8_t file_id, const uint8_t *data,
uint16_t len, uint16_t offset)
{
if (file_id == FILEID_CH_CERTIFICATE)
{
if (offset + len > FLASH_CH_CERTIFICATE_SIZE || (offset&1) || (len&1))
return -1;
else
{
const uint8_t *p = &ch_certificate_start;
uint16_t hw;
uint32_t addr;
int i;
addr = (uint32_t)p + offset;
for (i = 0; i < len/2; i++)
{
hw = data[i*2] | (data[i*2+1]<<8);
if (flash_program_halfword (addr, hw) != FLASH_COMPLETE)
flash_warning ("DO WRITE ERROR");
addr += 2;
}
return 0;
}
}
else
return -1;
}

View File

@@ -36,6 +36,7 @@ extern void *memmove(void *dest, const void *src, size_t n);
extern int icc_data_size;
#define cmd_APDU_size icc_data_size
extern int res_APDU_size;
extern uint8_t *res_APDU_pointer;
/* USB buffer size of LL (Low-level): size of single Bulk transaction */
#define USB_LL_BUF_SIZE 64
@@ -115,6 +116,14 @@ extern void flash_clear_halfword (uint32_t addr);
extern void flash_increment_counter (uint8_t counter_tag_nr);
extern void flash_reset_counter (uint8_t counter_tag_nr);
#define FILEID_CH_CERTIFICATE 0
#define FILEID_RANDOM 1
extern int flash_erase_binary (uint8_t file_id);
extern int flash_write_binary (uint8_t file_id, const uint8_t *data, uint16_t len, uint16_t offset);
/* Linker set this symbol */
extern uint8_t ch_certificate_start;
#define KEY_MAGIC_LEN 8
#define KEY_CONTENT_LEN 256 /* p and q */
#define GNUK_MAGIC "Gnuk KEY"

View File

@@ -121,6 +121,14 @@ SECTIONS
. = ALIGN (@FLASH_PAGE_SIZE@);
} > flash =0xffffffff
.gnuk_ch_certificate :
{
. = ALIGN (@FLASH_PAGE_SIZE@);
ch_certificate_start = .;
. += 1920;
. = ALIGN (@FLASH_PAGE_SIZE@);
} > flash =0xffffffff
.gnuk_flash :
{
_data_pool = .;

View File

@@ -929,8 +929,8 @@ gpg_do_table[] = {
{ GPG_DO_KEY_IMPORT, DO_PROC_WRITE, AC_NEVER, AC_ADMIN_AUTHORIZED,
proc_key_import },
#if 0
/* Card holder certificate: Not supported yet */
{ GPG_DO_CH_CERTIFICATE, DO_PROC_READWRITE, AC_NEVER, AC_NEVER, NULL },
/* Card holder certificate */
{ GPG_DO_CH_CERTIFICATE, DO_PROC_READ, AC_ALWAYS, AC_NEVER, NULL },
#endif
};
@@ -1199,6 +1199,18 @@ copy_do (const struct do_table_entry *do_p, int with_tag)
*/
void
gpg_do_get_data (uint16_t tag, int with_tag)
{
if (tag == GPG_DO_CH_CERTIFICATE)
{
res_APDU_pointer = &ch_certificate_start;
res_APDU_size = (res_APDU_pointer[2] << 8) | res_APDU_pointer[3];
if (res_APDU_size == 0xffff)
{
res_APDU_pointer = NULL;
GPG_NO_RECORD ();
}
}
else
{
const struct do_table_entry *do_p = get_do_entry (tag);
@@ -1222,6 +1234,7 @@ gpg_do_get_data (uint16_t tag, int with_tag)
else
GPG_NO_RECORD ();
}
}
void
gpg_do_put_data (uint16_t tag, const uint8_t *data, int len)

View File

@@ -39,6 +39,7 @@
#define INS_SELECT_FILE 0xa4
#define INS_READ_BINARY 0xb0
#define INS_GET_DATA 0xca
#define INS_UPDATE_BINARY 0xd6
#define INS_PUT_DATA 0xda
#define INS_PUT_DATA_ODD 0xdb /* For key import */
@@ -75,6 +76,8 @@ write_res_apdu (const uint8_t *p, int len, uint8_t sw1, uint8_t sw2)
#define FILE_MF 2
#define FILE_EF_DIR 3
#define FILE_EF_SERIAL 4
#define FILE_EF_CH_CERTIFICATE 5
#define FILE_EF_RANDOM 6
static uint8_t file_selection;
@@ -632,7 +635,7 @@ cmd_read_binary (void)
static void
cmd_select_file (void)
{
if (cmd_APDU[2] == 4) /* Selection by DF name */
if (cmd_APDU[2] == 4) /* Selection by DF name: it must be OpenPGP card */
{
DEBUG_INFO (" - select DF by name\r\n");
@@ -654,8 +657,7 @@ cmd_select_file (void)
}
}
else if (cmd_APDU[4] == 2
&& cmd_APDU[5] == 0x2f
&& cmd_APDU[6] == 0x02)
&& cmd_APDU[5] == 0x2f && cmd_APDU[6] == 0x02)
{
DEBUG_INFO (" - select 0x2f02 EF\r\n");
/*
@@ -665,8 +667,7 @@ cmd_select_file (void)
file_selection = FILE_EF_SERIAL;
}
else if (cmd_APDU[4] == 2
&& cmd_APDU[5] == 0x3f
&& cmd_APDU[6] == 0x00)
&& cmd_APDU[5] == 0x3f && cmd_APDU[6] == 0x00)
{
DEBUG_INFO (" - select ROOT MF\r\n");
if (cmd_APDU[3] == 0x0c)
@@ -682,6 +683,7 @@ cmd_select_file (void)
}
file_selection = FILE_MF;
ac_fini (); /* Reset authentication */
}
else
{
@@ -838,6 +840,77 @@ cmd_internal_authenticate (void)
DEBUG_INFO ("INTERNAL AUTHENTICATE done.\r\n");
}
static void
cmd_update_binary (void)
{
int len = cmd_APDU[4];
int data_start = 5;
uint16_t offset;
int r;
if (len == 0)
{
len = (cmd_APDU[5]<<8) | cmd_APDU[6];
data_start = 7;
}
DEBUG_INFO (" - UPDATE BINARY\r\n");
if (gpg_passwd_locked (PW_ERR_PW3) || !ac_check_status (AC_ADMIN_AUTHORIZED))
{
DEBUG_INFO ("security error.");
GPG_SECURITY_FAILURE ();
return;
}
if ((cmd_APDU[2] & 0x80))
if ((cmd_APDU[2] & 0x7f) <= 0x01)
{
file_selection = FILE_EF_CH_CERTIFICATE + (cmd_APDU[2] & 0x7f);
r = flash_erase_binary (file_selection - FILE_EF_CH_CERTIFICATE);
if (r < 0)
{
DEBUG_INFO ("memory error.\r\n");
GPG_MEMORY_FAILURE ();
return;
}
offset = 0;
}
else
{
GPG_NO_FILE ();
return;
}
else
{
if (file_selection != FILE_EF_CH_CERTIFICATE
&& file_selection != FILE_EF_RANDOM)
{
GPG_COMMAND_NOT_ALLOWED ();
return;
}
offset = (cmd_APDU[2] << 8) | cmd_APDU[3];
}
DEBUG_SHORT (len);
DEBUG_SHORT (offset);
r = flash_write_binary (file_selection - FILE_EF_CH_CERTIFICATE,
&cmd_APDU[data_start], len, offset);
if (r < 0)
{
DEBUG_INFO ("memory error.\r\n");
GPG_MEMORY_FAILURE ();
return;
}
DEBUG_INFO ("UPDATE BINARY done.\r\n");
}
struct command
{
uint8_t command;
@@ -854,6 +927,7 @@ const struct command cmds[] = {
{ INS_SELECT_FILE, cmd_select_file },
{ INS_READ_BINARY, cmd_read_binary },
{ INS_GET_DATA, cmd_get_data },
{ INS_UPDATE_BINARY, cmd_update_binary }, /* Not in OpenPGP card protocol */
{ INS_PUT_DATA, cmd_put_data },
{ INS_PUT_DATA_ODD, cmd_put_data },
};
@@ -869,6 +943,8 @@ process_command_apdu (void)
if (cmds[i].command == cmd)
break;
res_APDU_pointer = NULL; /* default */
if (i < NUM_CMDS)
cmds[i].cmd_handler ();
else

View File

@@ -1,6 +1,7 @@
#define GPG_MEMORY_FAILURE() write_res_apdu (NULL, 0, 0x65, 0x81)
#define GPG_SECURITY_FAILURE() write_res_apdu (NULL, 0, 0x69, 0x82)
#define GPG_SECURITY_AUTH_BLOCKED() write_res_apdu (NULL, 0, 0x69, 0x83)
#define GPG_COMMAND_NOT_ALLOWED() write_res_apdu (NULL, 0, 0x69, 0x86)
#define GPG_NO_FILE() write_res_apdu (NULL, 0, 0x6a, 0x82)
#define GPG_NO_RECORD() write_res_apdu (NULL, 0, 0x6a, 0x88)
#define GPG_BAD_P0_P1() write_res_apdu (NULL, 0, 0x6b, 0x00)

View File

@@ -385,6 +385,7 @@ icc_power_off (void)
}
int res_APDU_size;
uint8_t *res_APDU_pointer;
static void
icc_send_data_block (int len, uint8_t status, uint8_t chain)
@@ -608,8 +609,16 @@ icc_handle_data (void)
{
if (icc_header->param == 0x10)
{
if (res_APDU_pointer != NULL)
{
memcpy (res_APDU, res_APDU_pointer,
ICC_RESPONSE_MSG_DATA_SIZE);
res_APDU_pointer += ICC_RESPONSE_MSG_DATA_SIZE;
}
else
memmove (res_APDU, res_APDU+ICC_RESPONSE_MSG_DATA_SIZE,
res_APDU_size);
if (res_APDU_size <= ICC_RESPONSE_MSG_DATA_SIZE)
{
icc_send_data_block (res_APDU_size, 0, 0x02);
@@ -689,6 +698,9 @@ USBthread (void *arg)
{
if (icc_state == ICC_STATE_EXECUTE)
{
if (res_APDU_pointer != NULL)
memcpy (res_APDU, res_APDU_pointer, ICC_RESPONSE_MSG_DATA_SIZE);
if (res_APDU_size <= ICC_RESPONSE_MSG_DATA_SIZE)
{
icc_send_data_block (res_APDU_size, 0, 0);
@@ -698,6 +710,7 @@ USBthread (void *arg)
{
icc_send_data_block (ICC_RESPONSE_MSG_DATA_SIZE, 0, 0x01);
res_APDU_size -= ICC_RESPONSE_MSG_DATA_SIZE;
res_APDU_pointer += ICC_RESPONSE_MSG_DATA_SIZE;
icc_state = ICC_STATE_SEND;
}
}