card holder certificate support
This commit is contained in:
19
ChangeLog
19
ChangeLog
@@ -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
2
NEWS
@@ -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
|
||||
|
||||
78
src/flash.c
78
src/flash.c
@@ -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
|
||||
};
|
||||
@@ -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");
|
||||
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);
|
||||
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);
|
||||
*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;
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 = .;
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -1200,27 +1200,40 @@ copy_do (const struct do_table_entry *do_p, int with_tag)
|
||||
void
|
||||
gpg_do_get_data (uint16_t tag, int with_tag)
|
||||
{
|
||||
const struct do_table_entry *do_p = get_do_entry (tag);
|
||||
|
||||
res_p = res_APDU;
|
||||
|
||||
DEBUG_INFO (" ");
|
||||
DEBUG_SHORT (tag);
|
||||
|
||||
if (do_p)
|
||||
if (tag == GPG_DO_CH_CERTIFICATE)
|
||||
{
|
||||
if (copy_do (do_p, with_tag) < 0)
|
||||
/* Overwriting partially written result */
|
||||
GPG_SECURITY_FAILURE ();
|
||||
else
|
||||
res_APDU_pointer = &ch_certificate_start;
|
||||
res_APDU_size = (res_APDU_pointer[2] << 8) | res_APDU_pointer[3];
|
||||
if (res_APDU_size == 0xffff)
|
||||
{
|
||||
*res_p++ = 0x90;
|
||||
*res_p++ = 0x00;
|
||||
res_APDU_size = res_p - res_APDU;
|
||||
res_APDU_pointer = NULL;
|
||||
GPG_NO_RECORD ();
|
||||
}
|
||||
}
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
memmove (res_APDU, res_APDU+ICC_RESPONSE_MSG_DATA_SIZE,
|
||||
res_APDU_size);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user