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> 2011-01-27 NIIBE Yutaka <gniibe@fsij.org>
* src/openpgp-do.c (gpg_do_table): Exclude GPG_DO_CH_CERTIFICATE * src/usb-icc.c (res_APDU_pointer): New.
for now. (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. * 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 Released 2011-01-XX, by NIIBE Yutaka
** Card Holder Certificate is supported.
** Better interoperability to OpenSC. ** Better interoperability to OpenSC.
Gnuk is not yet supported by OpenSC, but it could be. With the Gnuk is not yet supported by OpenSC, but it could be. With the
changes in Gnuk, it could be relatively easily possible to support 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 * 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> * Author: NIIBE Yutaka <gniibe@fsij.org>
* *
* This file is a part of Gnuk, a GnuPG USB Token implementation. * 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. * We assume single DO size is less than 256.
* *
* NOTE: When we will support "Card holder certificate" (which size is * NOTE: When we will support "Card holder certificate" (which size is
* larger than 256), it will not be put into data pool, but will * larger than 256), it is not put into data pool, but is
* be implemented by its own flash page. * implemented by its own flash page(s).
*/ */
#include "config.h" #include "config.h"
@@ -167,7 +167,7 @@ static const uint8_t *keystore_pool;
static uint8_t *last_p; static uint8_t *last_p;
static const uint8_t *keystore; 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"))) = { const uint8_t const flash_data[4] __attribute__ ((section (".gnuk_data"))) = {
0x01, 0x00, 0xff, 0xff 0x01, 0x00, 0xff, 0xff
}; };
@@ -300,7 +300,7 @@ flash_do_write_internal (const uint8_t *p, int nr, const uint8_t *data, int len)
flash_warning ("DO WRITE ERROR"); flash_warning ("DO WRITE ERROR");
addr += 2; addr += 2;
for (i = 0; i < len/2; i ++) for (i = 0; i < len/2; i++)
{ {
hw = data[i*2] | (data[i*2+1]<<8); hw = data[i*2] | (data[i*2+1]<<8);
if (flash_program_halfword (addr, hw) != FLASH_COMPLETE) if (flash_program_halfword (addr, hw) != FLASH_COMPLETE)
@@ -581,3 +581,71 @@ flash_cnt123_clear (const uint8_t **addr_p)
flash_program_halfword ((uint32_t)p, 0); flash_program_halfword ((uint32_t)p, 0);
*addr_p = NULL; *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; extern int icc_data_size;
#define cmd_APDU_size icc_data_size #define cmd_APDU_size icc_data_size
extern int res_APDU_size; extern int res_APDU_size;
extern uint8_t *res_APDU_pointer;
/* USB buffer size of LL (Low-level): size of single Bulk transaction */ /* USB buffer size of LL (Low-level): size of single Bulk transaction */
#define USB_LL_BUF_SIZE 64 #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_increment_counter (uint8_t counter_tag_nr);
extern void flash_reset_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_MAGIC_LEN 8
#define KEY_CONTENT_LEN 256 /* p and q */ #define KEY_CONTENT_LEN 256 /* p and q */
#define GNUK_MAGIC "Gnuk KEY" #define GNUK_MAGIC "Gnuk KEY"

View File

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

View File

@@ -929,8 +929,8 @@ gpg_do_table[] = {
{ GPG_DO_KEY_IMPORT, DO_PROC_WRITE, AC_NEVER, AC_ADMIN_AUTHORIZED, { GPG_DO_KEY_IMPORT, DO_PROC_WRITE, AC_NEVER, AC_ADMIN_AUTHORIZED,
proc_key_import }, proc_key_import },
#if 0 #if 0
/* Card holder certificate: Not supported yet */ /* Card holder certificate */
{ GPG_DO_CH_CERTIFICATE, DO_PROC_READWRITE, AC_NEVER, AC_NEVER, NULL }, { GPG_DO_CH_CERTIFICATE, DO_PROC_READ, AC_ALWAYS, AC_NEVER, NULL },
#endif #endif
}; };
@@ -1200,27 +1200,40 @@ copy_do (const struct do_table_entry *do_p, int with_tag)
void void
gpg_do_get_data (uint16_t tag, int with_tag) gpg_do_get_data (uint16_t tag, int with_tag)
{ {
const struct do_table_entry *do_p = get_do_entry (tag); if (tag == GPG_DO_CH_CERTIFICATE)
res_p = res_APDU;
DEBUG_INFO (" ");
DEBUG_SHORT (tag);
if (do_p)
{ {
if (copy_do (do_p, with_tag) < 0) res_APDU_pointer = &ch_certificate_start;
/* Overwriting partially written result */ res_APDU_size = (res_APDU_pointer[2] << 8) | res_APDU_pointer[3];
GPG_SECURITY_FAILURE (); if (res_APDU_size == 0xffff)
else
{ {
*res_p++ = 0x90; res_APDU_pointer = NULL;
*res_p++ = 0x00; GPG_NO_RECORD ();
res_APDU_size = res_p - res_APDU;
} }
} }
else else
GPG_NO_RECORD (); {
const struct do_table_entry *do_p = get_do_entry (tag);
res_p = res_APDU;
DEBUG_INFO (" ");
DEBUG_SHORT (tag);
if (do_p)
{
if (copy_do (do_p, with_tag) < 0)
/* Overwriting partially written result */
GPG_SECURITY_FAILURE ();
else
{
*res_p++ = 0x90;
*res_p++ = 0x00;
res_APDU_size = res_p - res_APDU;
}
}
else
GPG_NO_RECORD ();
}
} }
void void

View File

@@ -39,6 +39,7 @@
#define INS_SELECT_FILE 0xa4 #define INS_SELECT_FILE 0xa4
#define INS_READ_BINARY 0xb0 #define INS_READ_BINARY 0xb0
#define INS_GET_DATA 0xca #define INS_GET_DATA 0xca
#define INS_UPDATE_BINARY 0xd6
#define INS_PUT_DATA 0xda #define INS_PUT_DATA 0xda
#define INS_PUT_DATA_ODD 0xdb /* For key import */ #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_MF 2
#define FILE_EF_DIR 3 #define FILE_EF_DIR 3
#define FILE_EF_SERIAL 4 #define FILE_EF_SERIAL 4
#define FILE_EF_CH_CERTIFICATE 5
#define FILE_EF_RANDOM 6
static uint8_t file_selection; static uint8_t file_selection;
@@ -632,7 +635,7 @@ cmd_read_binary (void)
static void static void
cmd_select_file (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"); DEBUG_INFO (" - select DF by name\r\n");
@@ -654,8 +657,7 @@ cmd_select_file (void)
} }
} }
else if (cmd_APDU[4] == 2 else if (cmd_APDU[4] == 2
&& cmd_APDU[5] == 0x2f && cmd_APDU[5] == 0x2f && cmd_APDU[6] == 0x02)
&& cmd_APDU[6] == 0x02)
{ {
DEBUG_INFO (" - select 0x2f02 EF\r\n"); DEBUG_INFO (" - select 0x2f02 EF\r\n");
/* /*
@@ -665,8 +667,7 @@ cmd_select_file (void)
file_selection = FILE_EF_SERIAL; file_selection = FILE_EF_SERIAL;
} }
else if (cmd_APDU[4] == 2 else if (cmd_APDU[4] == 2
&& cmd_APDU[5] == 0x3f && cmd_APDU[5] == 0x3f && cmd_APDU[6] == 0x00)
&& cmd_APDU[6] == 0x00)
{ {
DEBUG_INFO (" - select ROOT MF\r\n"); DEBUG_INFO (" - select ROOT MF\r\n");
if (cmd_APDU[3] == 0x0c) if (cmd_APDU[3] == 0x0c)
@@ -682,6 +683,7 @@ cmd_select_file (void)
} }
file_selection = FILE_MF; file_selection = FILE_MF;
ac_fini (); /* Reset authentication */
} }
else else
{ {
@@ -838,6 +840,77 @@ cmd_internal_authenticate (void)
DEBUG_INFO ("INTERNAL AUTHENTICATE done.\r\n"); 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 struct command
{ {
uint8_t command; uint8_t command;
@@ -854,6 +927,7 @@ const struct command cmds[] = {
{ INS_SELECT_FILE, cmd_select_file }, { INS_SELECT_FILE, cmd_select_file },
{ INS_READ_BINARY, cmd_read_binary }, { INS_READ_BINARY, cmd_read_binary },
{ INS_GET_DATA, cmd_get_data }, { 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, cmd_put_data },
{ INS_PUT_DATA_ODD, cmd_put_data }, { INS_PUT_DATA_ODD, cmd_put_data },
}; };
@@ -869,6 +943,8 @@ process_command_apdu (void)
if (cmds[i].command == cmd) if (cmds[i].command == cmd)
break; break;
res_APDU_pointer = NULL; /* default */
if (i < NUM_CMDS) if (i < NUM_CMDS)
cmds[i].cmd_handler (); cmds[i].cmd_handler ();
else else

View File

@@ -1,6 +1,7 @@
#define GPG_MEMORY_FAILURE() write_res_apdu (NULL, 0, 0x65, 0x81) #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_FAILURE() write_res_apdu (NULL, 0, 0x69, 0x82)
#define GPG_SECURITY_AUTH_BLOCKED() write_res_apdu (NULL, 0, 0x69, 0x83) #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_FILE() write_res_apdu (NULL, 0, 0x6a, 0x82)
#define GPG_NO_RECORD() write_res_apdu (NULL, 0, 0x6a, 0x88) #define GPG_NO_RECORD() write_res_apdu (NULL, 0, 0x6a, 0x88)
#define GPG_BAD_P0_P1() write_res_apdu (NULL, 0, 0x6b, 0x00) #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; int res_APDU_size;
uint8_t *res_APDU_pointer;
static void static void
icc_send_data_block (int len, uint8_t status, uint8_t chain) 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 (icc_header->param == 0x10)
{ {
memmove (res_APDU, res_APDU+ICC_RESPONSE_MSG_DATA_SIZE, if (res_APDU_pointer != NULL)
res_APDU_size); {
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) if (res_APDU_size <= ICC_RESPONSE_MSG_DATA_SIZE)
{ {
icc_send_data_block (res_APDU_size, 0, 0x02); icc_send_data_block (res_APDU_size, 0, 0x02);
@@ -689,6 +698,9 @@ USBthread (void *arg)
{ {
if (icc_state == ICC_STATE_EXECUTE) 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) if (res_APDU_size <= ICC_RESPONSE_MSG_DATA_SIZE)
{ {
icc_send_data_block (res_APDU_size, 0, 0); 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); icc_send_data_block (ICC_RESPONSE_MSG_DATA_SIZE, 0, 0x01);
res_APDU_size -= ICC_RESPONSE_MSG_DATA_SIZE; res_APDU_size -= ICC_RESPONSE_MSG_DATA_SIZE;
res_APDU_pointer += ICC_RESPONSE_MSG_DATA_SIZE;
icc_state = ICC_STATE_SEND; icc_state = ICC_STATE_SEND;
} }
} }