mirror of
https://salsa.debian.org/gnuk-team/gnuk/gnuk.git
synced 2024-09-21 11:20:08 +00:00
701 lines
16 KiB
C
701 lines
16 KiB
C
/*
|
|
* openpgp-do.c -- OpenPGP card Data Objects (DO) handling
|
|
*
|
|
* Copyright (C) 2010 Free Software Initiative of Japan
|
|
* Author: NIIBE Yutaka <gniibe@fsij.org>
|
|
*
|
|
* This file is a part of Gnuk, a GnuPG USB Token implementation.
|
|
*
|
|
* Gnuk is free software: you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Gnuk 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 General Public
|
|
* License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "ch.h"
|
|
#include "gnuk.h"
|
|
|
|
/*
|
|
* Compile time vars:
|
|
* AID, Historical Bytes (template), Extended Capabilities,
|
|
* and Algorithm Attributes
|
|
*/
|
|
|
|
/* AID */
|
|
static const uint8_t const aid[] __attribute__ ((aligned (1))) = {
|
|
16,
|
|
0xd2, 0x76, 0x00, 0x01, 0x24, 0x01,
|
|
0x02, 0x00, /* Version 2.0 */
|
|
0xf5, 0x17, /* Manufacturer (FSIJ) */
|
|
0x00, 0x00, 0x00, 0x01, /* Serial */
|
|
0x00, 0x00
|
|
};
|
|
|
|
/* Historical Bytes (template) */
|
|
static const uint8_t const historical_bytes[] __attribute__ ((aligned (1))) = {
|
|
10,
|
|
0x00,
|
|
0x31, 0x80, /* Full DF name */
|
|
0x73,
|
|
0x80, 0x01, 0x40, /* Full DF name */
|
|
/* 1-byte */
|
|
/* No command chaining */
|
|
/* Extended Lc and Le */
|
|
0x00, 0x90, 0x00 /* Status info (no life cycle management) */
|
|
};
|
|
|
|
/* Extended Capabilities */
|
|
static const uint8_t const extended_capabilities[] __attribute__ ((aligned (1))) = {
|
|
10,
|
|
0x30, /*
|
|
* No SM, No get challenge,
|
|
* Key import supported,
|
|
* PW status byte can be put,
|
|
* No private_use_DO,
|
|
* No algo change allowed
|
|
*/
|
|
0, /* Secure Messaging Algorithm: N/A (TDES=0, AES=1) */
|
|
0x00, 0x00, /* Max get challenge */
|
|
0x00, 0x00, /* max. length of cardholder certificate */
|
|
(MAX_CMD_APDU_SIZE>>8), (MAX_CMD_APDU_SIZE&0xff), /* Max. length of command data */
|
|
(MAX_RES_APDU_SIZE>>8), (MAX_RES_APDU_SIZE&0xff), /* Max. length of response data */
|
|
};
|
|
|
|
/* Algorithm Attributes */
|
|
static const uint8_t const algorithm_attr[] __attribute__ ((aligned (1))) = {
|
|
6,
|
|
0x01, /* RSA */
|
|
0x08, 0x00, /* Length modulus (in bit): 2048 */
|
|
0x00, 0x20, /* Length exponent (in bit): 32 */
|
|
0x00 /* 0: p&q , 3: CRT with N (not yet supported) */
|
|
};
|
|
|
|
|
|
|
|
#define SIZE_PW_STATUS_BYTES 7
|
|
#if 0
|
|
{
|
|
1, /* PW1 valid for several PSO:CDS commands */
|
|
127, 127, 127, /* max length of PW1, RC, and PW3 */
|
|
3, 0, 3 /* Error counter of PW1, RC, and PW3 */
|
|
};
|
|
#endif
|
|
|
|
#define SIZE_DIGITAL_SIGNATURE_COUNTER 3
|
|
#if 0
|
|
{
|
|
0, 0, 0 /* 3-byte binary */
|
|
};
|
|
#endif
|
|
|
|
#define SIZE_FINGER_PRINT 20
|
|
#define SIZE_KEYGEN_TIME 4 /* RFC4880 */
|
|
|
|
/* Runtime vars: PSO */
|
|
|
|
struct key_store {
|
|
uint8_t p[128];
|
|
uint8_t q[128];
|
|
};
|
|
|
|
static struct key_store key_sig, key_dec, key_aut;
|
|
|
|
#define HASH_SIZE 20
|
|
static uint8_t pw3_hash[HASH_SIZE];
|
|
|
|
enum do_type {
|
|
DO_FIXED,
|
|
DO_VAR,
|
|
DO_CN_READ,
|
|
DO_PROC_READ,
|
|
DO_PROC_WRITE,
|
|
DO_HASH,
|
|
DO_KEYPTR
|
|
};
|
|
|
|
struct do_table_entry {
|
|
uint16_t tag;
|
|
enum do_type do_type;
|
|
uint8_t ac_read;
|
|
uint8_t ac_write;
|
|
const void *obj;
|
|
};
|
|
|
|
static uint8_t *res_p;
|
|
static int with_tag;
|
|
|
|
static void copy_do_1 (uint16_t tag, const uint8_t *do_data);
|
|
static struct do_table_entry *get_do_entry (uint16_t tag);
|
|
|
|
#define GNUK_DO_KEYPTR_SIG 0xff01
|
|
#define GNUK_DO_KEYPTR_DEC 0xff02
|
|
#define GNUK_DO_KEYPTR_AUT 0xff03
|
|
#define GNUK_DO_HASH_PW3 0xff04
|
|
|
|
#define GPG_DO_AID 0x004f
|
|
#define GPG_DO_NAME 0x005b
|
|
#define GPG_DO_LOGIN_DATA 0x005e
|
|
#define GPG_DO_CH_DATA 0x0065
|
|
#define GPG_DO_APP_DATA 0x006e
|
|
/* XXX: 0x0073 ??? */
|
|
#define GPG_DO_SS_TEMP 0x007a
|
|
#define GPG_DO_DS_COUNT 0x0093
|
|
#define GPG_DO_EXTCAP 0x00c0
|
|
#define GPG_DO_ALG_SIG 0x00c1
|
|
#define GPG_DO_ALG_DEC 0x00c2
|
|
#define GPG_DO_ALG_AUT 0x00c3
|
|
#define GPG_DO_PW_STATUS 0x00c4
|
|
#define GPG_DO_FP_ALL 0x00c5
|
|
#define GPG_DO_CAFP_ALL 0x00c6
|
|
#define GPG_DO_FP_SIG 0x00c7
|
|
#define GPG_DO_FP_DEC 0x00c8
|
|
#define GPG_DO_FP_AUT 0x00c9
|
|
#define GPG_DO_CAFP_1 0x00ca
|
|
#define GPG_DO_CAFP_2 0x00cb
|
|
#define GPG_DO_CAFP_3 0x00cc
|
|
#define GPG_DO_KGTIME_ALL 0x00cd
|
|
#define GPG_DO_KGTIME_SIG 0x00ce
|
|
#define GPG_DO_KGTIME_DEC 0x00cf
|
|
#define GPG_DO_KGTIME_AUT 0x00d0
|
|
#define GPG_DO_RESETTING_CODE 0x00d3
|
|
#define GPG_DO_KEY_IMPORT 0x3fff
|
|
#define GPG_DO_LANGUAGE 0x5f2d
|
|
#define GPG_DO_SEX 0x5f35
|
|
#define GPG_DO_URL 0x5f50
|
|
#define GPG_DO_HIST_BYTES 0x5f52
|
|
#define GPG_DO_CH_CERTIFICATE 0x7f21
|
|
|
|
static void
|
|
copy_tag (uint16_t tag)
|
|
{
|
|
if (tag < 0x0100)
|
|
*res_p++ = (tag & 0xff);
|
|
else
|
|
{
|
|
*res_p++ = (tag >> 8);
|
|
*res_p++ = (tag & 0xff);
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_hist_bytes (uint16_t tag)
|
|
{
|
|
/* XXX: For now, no life cycle management, just return template as is. */
|
|
/* XXX: Supporing TERMINATE DF / ACTIVATE FILE, we need to fix here */
|
|
copy_do_1 (tag, historical_bytes);
|
|
}
|
|
|
|
#define SIZE_FP 20
|
|
#define SIZE_KGTIME 4
|
|
|
|
static void
|
|
do_fp_all (uint16_t tag)
|
|
{
|
|
struct do_table_entry *do_p;
|
|
const uint8_t *do_data;
|
|
|
|
if (with_tag)
|
|
{
|
|
copy_tag (tag);
|
|
*res_p++ = SIZE_FP*3;
|
|
}
|
|
|
|
do_p = get_do_entry (GPG_DO_FP_SIG);
|
|
do_data = (const uint8_t *)do_p->obj;
|
|
if (do_data)
|
|
memcpy (res_p, &do_data[1], SIZE_FP);
|
|
else
|
|
memset (res_p, 0, SIZE_FP);
|
|
res_p += SIZE_FP;
|
|
|
|
do_p = get_do_entry (GPG_DO_FP_DEC);
|
|
do_data = (const uint8_t *)do_p->obj;
|
|
if (do_data)
|
|
memcpy (res_p, &do_data[1], SIZE_FP);
|
|
else
|
|
memset (res_p, 0, SIZE_FP);
|
|
res_p += SIZE_FP;
|
|
|
|
do_p = get_do_entry (GPG_DO_FP_AUT);
|
|
do_data = (const uint8_t *)do_p->obj;
|
|
if (do_data)
|
|
memcpy (res_p, &do_data[1], SIZE_FP);
|
|
else
|
|
memset (res_p, 0, SIZE_FP);
|
|
res_p += SIZE_FP;
|
|
}
|
|
|
|
static void
|
|
do_cafp_all (uint16_t tag)
|
|
{
|
|
struct do_table_entry *do_p;
|
|
const uint8_t *do_data;
|
|
|
|
if (with_tag)
|
|
{
|
|
copy_tag (tag);
|
|
*res_p++ = SIZE_FP*3;
|
|
}
|
|
|
|
do_p = get_do_entry (GPG_DO_CAFP_1);
|
|
do_data = (const uint8_t *)do_p->obj;
|
|
if (do_data)
|
|
memcpy (res_p, &do_data[1], SIZE_FP);
|
|
else
|
|
memset (res_p, 0, SIZE_FP);
|
|
res_p += SIZE_FP;
|
|
|
|
do_p = get_do_entry (GPG_DO_CAFP_2);
|
|
do_data = (const uint8_t *)do_p->obj;
|
|
if (do_data)
|
|
memcpy (res_p, &do_data[1], SIZE_FP);
|
|
else
|
|
memset (res_p, 0, SIZE_FP);
|
|
res_p += SIZE_FP;
|
|
|
|
do_p = get_do_entry (GPG_DO_CAFP_3);
|
|
do_data = (const uint8_t *)do_p->obj;
|
|
if (do_data)
|
|
memcpy (res_p, &do_data[1], SIZE_FP);
|
|
else
|
|
memset (res_p, 0, SIZE_FP);
|
|
res_p += SIZE_FP;
|
|
}
|
|
|
|
static void
|
|
do_kgtime_all (uint16_t tag)
|
|
{
|
|
struct do_table_entry *do_p;
|
|
const uint8_t *do_data;
|
|
|
|
if (with_tag)
|
|
{
|
|
copy_tag (tag);
|
|
*res_p++ = SIZE_KGTIME*3;
|
|
}
|
|
|
|
do_p = get_do_entry (GPG_DO_KGTIME_SIG);
|
|
do_data = (const uint8_t *)do_p->obj;
|
|
if (do_data)
|
|
memcpy (res_p, &do_data[1], SIZE_KGTIME);
|
|
else
|
|
memset (res_p, 0, SIZE_KGTIME);
|
|
res_p += SIZE_KGTIME;
|
|
|
|
do_p = get_do_entry (GPG_DO_KGTIME_DEC);
|
|
do_data = (const uint8_t *)do_p->obj;
|
|
if (do_data)
|
|
memcpy (res_p, &do_data[1], SIZE_KGTIME);
|
|
else
|
|
memset (res_p, 0, SIZE_KGTIME);
|
|
res_p += SIZE_KGTIME;
|
|
|
|
do_p = get_do_entry (GPG_DO_KGTIME_AUT);
|
|
do_data = (const uint8_t *)do_p->obj;
|
|
if (do_data)
|
|
memcpy (res_p, &do_data[1], SIZE_KGTIME);
|
|
else
|
|
memset (res_p, 0, SIZE_KGTIME);
|
|
res_p += SIZE_KGTIME;
|
|
}
|
|
|
|
static void
|
|
put_hex (uint8_t nibble)
|
|
{
|
|
uint8_t c;
|
|
|
|
if (nibble < 0x0a)
|
|
c = '0' + nibble;
|
|
else
|
|
c = 'a' + nibble - 0x0a;
|
|
|
|
_write (&c, 1);
|
|
}
|
|
|
|
static void
|
|
o_put_byte (uint8_t b)
|
|
{
|
|
_write (" ", 1);
|
|
put_hex (b >> 4);
|
|
put_hex (b &0x0f);
|
|
}
|
|
|
|
/*
|
|
* 4d, xx, xx: Extended Header List
|
|
* b6 00 (SIG) / b8 00 (DEC) / a4 00 (AUT)
|
|
* 7f48, xx: cardholder private key template
|
|
* 91 xx
|
|
* 92 xx
|
|
* 93 xx
|
|
* 5f48, xx: cardholder privatge key
|
|
*/
|
|
static void
|
|
proc_key_import (uint16_t tag, uint8_t *data, int len)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < len; i++)
|
|
{
|
|
o_put_byte (data[i]);
|
|
if ((i & 0x0f) == 0x0f)
|
|
_write ("\r\n", 2);
|
|
}
|
|
_write ("\r\n", 2);
|
|
|
|
write_res_apdu (NULL, 0, 0x65, 0x81); /* memory failure */
|
|
}
|
|
|
|
static const uint16_t const cn_ch_data[] = {
|
|
3,
|
|
GPG_DO_NAME,
|
|
GPG_DO_LANGUAGE,
|
|
GPG_DO_SEX,
|
|
};
|
|
|
|
static const uint16_t const cn_app_data[] = {
|
|
10,
|
|
GPG_DO_AID,
|
|
GPG_DO_HIST_BYTES,
|
|
/* XXX Discretionary data objects 0x0073 ??? */
|
|
GPG_DO_EXTCAP,
|
|
GPG_DO_ALG_SIG, GPG_DO_ALG_DEC, GPG_DO_ALG_AUT,
|
|
GPG_DO_PW_STATUS,
|
|
GPG_DO_FP_ALL, GPG_DO_CAFP_ALL, GPG_DO_KGTIME_ALL
|
|
};
|
|
|
|
static const uint16_t const cn_ss_temp[] = { 1, GPG_DO_DS_COUNT };
|
|
|
|
static struct do_table_entry
|
|
gpg_do_table[] = {
|
|
/* Pseudo DO (private): not user accessible */
|
|
{ GNUK_DO_KEYPTR_SIG, DO_KEYPTR, AC_NEVER, AC_NEVER, NULL },
|
|
{ GNUK_DO_KEYPTR_DEC, DO_KEYPTR, AC_NEVER, AC_NEVER, NULL },
|
|
{ GNUK_DO_KEYPTR_AUT, DO_KEYPTR, AC_NEVER, AC_NEVER, NULL },
|
|
{ GNUK_DO_HASH_PW3, DO_HASH, AC_NEVER, AC_NEVER, NULL },
|
|
/* Pseudo DO: calculated */
|
|
{ GPG_DO_HIST_BYTES, DO_PROC_READ, AC_ALWAYS, AC_NEVER, do_hist_bytes },
|
|
{ GPG_DO_FP_ALL, DO_PROC_READ, AC_ALWAYS, AC_NEVER, do_fp_all },
|
|
{ GPG_DO_CAFP_ALL, DO_PROC_READ, AC_ALWAYS, AC_NEVER, do_cafp_all },
|
|
{ GPG_DO_KGTIME_ALL, DO_PROC_READ, AC_ALWAYS, AC_NEVER, do_kgtime_all },
|
|
/* Fixed data */
|
|
{ GPG_DO_AID, DO_FIXED, AC_ALWAYS, AC_NEVER, aid },
|
|
{ GPG_DO_EXTCAP, DO_FIXED, AC_ALWAYS, AC_NEVER, extended_capabilities },
|
|
{ GPG_DO_ALG_SIG, DO_FIXED, AC_ALWAYS, AC_NEVER, algorithm_attr },
|
|
{ GPG_DO_ALG_DEC, DO_FIXED, AC_ALWAYS, AC_NEVER, algorithm_attr },
|
|
{ GPG_DO_ALG_AUT, DO_FIXED, AC_ALWAYS, AC_NEVER, algorithm_attr },
|
|
/* Variable(s): Fixed size, not changeable by user */
|
|
{ GPG_DO_DS_COUNT, DO_VAR, AC_ALWAYS, AC_NEVER, NULL },
|
|
/* Variables: Fixed size */
|
|
{ GPG_DO_PW_STATUS, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, NULL },
|
|
{ GPG_DO_SEX, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, NULL },
|
|
{ GPG_DO_FP_SIG, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, NULL },
|
|
{ GPG_DO_FP_DEC, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, NULL },
|
|
{ GPG_DO_FP_AUT, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, NULL },
|
|
{ GPG_DO_CAFP_1, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, NULL },
|
|
{ GPG_DO_CAFP_2, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, NULL },
|
|
{ GPG_DO_CAFP_3, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, NULL },
|
|
{ GPG_DO_KGTIME_SIG, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, NULL },
|
|
{ GPG_DO_KGTIME_DEC, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, NULL },
|
|
{ GPG_DO_KGTIME_AUT, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, NULL },
|
|
/* Variables: Variable size */
|
|
{ GPG_DO_LOGIN_DATA, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, NULL },
|
|
{ GPG_DO_URL, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, NULL },
|
|
{ GPG_DO_NAME, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, NULL },
|
|
{ GPG_DO_LANGUAGE, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, NULL },
|
|
{ GPG_DO_CH_CERTIFICATE, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, NULL },
|
|
/* Variable(s): Variable size, write only by user */
|
|
{ GPG_DO_RESETTING_CODE, DO_VAR, AC_NEVER, AC_ADMIN_AUTHORIZED, NULL },
|
|
/* Compound data: Read access only */
|
|
{ GPG_DO_CH_DATA, DO_CN_READ, AC_ALWAYS, AC_NEVER, cn_ch_data },
|
|
{ GPG_DO_APP_DATA, DO_CN_READ, AC_ALWAYS, AC_NEVER, cn_app_data },
|
|
{ GPG_DO_SS_TEMP, DO_CN_READ, AC_ALWAYS, AC_NEVER, cn_ss_temp },
|
|
/* Compound data: Write access only*/
|
|
{ GPG_DO_KEY_IMPORT, DO_PROC_WRITE, AC_NEVER, AC_ADMIN_AUTHORIZED,
|
|
proc_key_import },
|
|
};
|
|
|
|
#define NUM_DO_ENTRIES (int)(sizeof (gpg_do_table) / sizeof (struct do_table_entry))
|
|
|
|
extern const uint8_t const do_5e[];
|
|
extern const uint8_t const do_c4[];
|
|
extern const uint8_t const do_c7[];
|
|
extern const uint8_t const do_ca[];
|
|
extern const uint8_t const do_ce[];
|
|
extern const uint8_t const do_5b[];
|
|
extern const uint8_t const do_5f2d[];
|
|
extern const uint8_t const do_5f35[];
|
|
extern const uint8_t const do_93[];
|
|
extern const uint8_t const do_5f50[];
|
|
|
|
/*
|
|
* Initialize GPG_DO_TABLE reading from Flash ROM
|
|
*/
|
|
int
|
|
gpg_do_table_init (void)
|
|
{
|
|
struct do_table_entry *do_p;
|
|
|
|
do_p = get_do_entry (GPG_DO_LOGIN_DATA);
|
|
do_p->obj = do_5e;
|
|
do_p = get_do_entry (GPG_DO_PW_STATUS);
|
|
do_p->obj = do_c4;
|
|
#if 0
|
|
do_p = get_do_entry (GPG_DO_FP_SIG);
|
|
do_p->obj = do_c7;
|
|
do_p = get_do_entry (GPG_DO_CAFP_1);
|
|
do_p->obj = do_ca;
|
|
do_p = get_do_entry (GPG_DO_KGTIME_SIG);
|
|
do_p->obj = do_ce;
|
|
#endif
|
|
do_p = get_do_entry (GPG_DO_NAME);
|
|
do_p->obj = do_5b;
|
|
do_p = get_do_entry (GPG_DO_LANGUAGE);
|
|
do_p->obj = do_5f2d;
|
|
do_p = get_do_entry (GPG_DO_SEX);
|
|
do_p->obj = do_5f35;
|
|
do_p = get_do_entry (GPG_DO_DS_COUNT);
|
|
do_p->obj = do_93;
|
|
do_p = get_do_entry (GPG_DO_URL);
|
|
do_p->obj = do_5f50;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct do_table_entry *
|
|
get_do_entry (uint16_t tag)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < NUM_DO_ENTRIES; i++)
|
|
if (gpg_do_table[i].tag == tag)
|
|
return &gpg_do_table[i];
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
copy_do_1 (uint16_t tag, const uint8_t *do_data)
|
|
{
|
|
int len;
|
|
|
|
if (with_tag)
|
|
{
|
|
copy_tag (tag);
|
|
|
|
if (do_data[0] < 127)
|
|
len = do_data[0] + 1;
|
|
else if (do_data[1] == 0x81)
|
|
len = do_data[1] + 2;
|
|
else /* 0x82 */
|
|
len = ((do_data[1] << 8) | do_data[2]) + 3;
|
|
}
|
|
else
|
|
{
|
|
if (do_data[0] < 127)
|
|
{
|
|
len = do_data[0];
|
|
do_data++;
|
|
}
|
|
else if (do_data[1] == 0x81)
|
|
{
|
|
len = do_data[1];
|
|
do_data += 2;
|
|
}
|
|
else /* 0x82 */
|
|
{
|
|
len = ((do_data[1] << 8) | do_data[2]);
|
|
do_data += 3;
|
|
}
|
|
}
|
|
|
|
memcpy (res_p, do_data, len);
|
|
res_p += len;
|
|
}
|
|
|
|
static int
|
|
copy_do (struct do_table_entry *do_p)
|
|
{
|
|
if (do_p == NULL)
|
|
return 0;
|
|
|
|
if (ac_check_status (do_p->ac_read) == 0)
|
|
return -1;
|
|
|
|
switch (do_p->do_type)
|
|
{
|
|
case DO_FIXED:
|
|
case DO_VAR:
|
|
{
|
|
const uint8_t *do_data = (const uint8_t *)do_p->obj;
|
|
if (do_data == NULL)
|
|
return 0;
|
|
else
|
|
copy_do_1 (do_p->tag, do_data);
|
|
break;
|
|
}
|
|
case DO_CN_READ:
|
|
{
|
|
int i;
|
|
const uint16_t *cn_data = (const uint16_t *)do_p->obj;
|
|
int num_components = cn_data[0];
|
|
uint8_t *len_p;
|
|
|
|
copy_tag (do_p->tag);
|
|
*res_p++ = 0x81;
|
|
len_p = res_p;
|
|
*res_p++ = 0; /* for now */
|
|
with_tag = 1;
|
|
|
|
for (i = 0; i < num_components; i++)
|
|
{
|
|
uint16_t tag0;
|
|
struct do_table_entry *do0_p;
|
|
|
|
tag0 = cn_data[i+1];
|
|
do0_p = get_do_entry (tag0);
|
|
if (copy_do (do0_p) < 0)
|
|
return -1;
|
|
}
|
|
|
|
*len_p = (res_p - len_p);
|
|
break;
|
|
}
|
|
case DO_PROC_READ:
|
|
{
|
|
void (*do_func)(uint16_t) = (void (*)(uint16_t))do_p->obj;
|
|
|
|
do_func (do_p->tag);
|
|
break;
|
|
}
|
|
case DO_PROC_WRITE:
|
|
case DO_HASH:
|
|
case DO_KEYPTR:
|
|
return -1;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Process GET_DATA request on Data Object specified by TAG
|
|
* Call write_res_adpu to fill data returned
|
|
*/
|
|
void
|
|
gpg_do_get_data (uint16_t tag)
|
|
{
|
|
struct do_table_entry *do_p = get_do_entry (tag);
|
|
|
|
res_p = res_APDU;
|
|
with_tag = 0;
|
|
|
|
#ifdef DEBUG
|
|
put_string (" ");
|
|
put_short (tag);
|
|
#endif
|
|
|
|
if (do_p)
|
|
{
|
|
if (copy_do (do_p) < 0)
|
|
/* Overwrite partially written result */
|
|
write_res_apdu (NULL, 0, 0x69, 0x82);
|
|
else
|
|
{
|
|
*res_p++ = 0x90;
|
|
*res_p++ = 0x00;
|
|
res_APDU_size = res_p - res_APDU;
|
|
}
|
|
}
|
|
else
|
|
/* No record */
|
|
write_res_apdu (NULL, 0, 0x6a, 0x88);
|
|
}
|
|
|
|
void
|
|
gpg_do_put_data (uint16_t tag, uint8_t *data, int len)
|
|
{
|
|
struct do_table_entry *do_p = get_do_entry (tag);
|
|
|
|
#ifdef DEBUG
|
|
put_string (" ");
|
|
put_short (tag);
|
|
#endif
|
|
|
|
if (do_p)
|
|
{
|
|
if (ac_check_status (do_p->ac_write) == 0)
|
|
{
|
|
write_res_apdu (NULL, 0, 0x69, 0x82);
|
|
return;
|
|
}
|
|
|
|
switch (do_p->do_type)
|
|
{
|
|
case DO_FIXED:
|
|
case DO_CN_READ:
|
|
case DO_PROC_READ:
|
|
case DO_HASH:
|
|
case DO_KEYPTR:
|
|
write_res_apdu (NULL, 0, 0x69, 0x82);
|
|
break;
|
|
case DO_VAR:
|
|
{
|
|
#if 0
|
|
const uint8_t *do_data = (const uint8_t *)do_p->obj;
|
|
|
|
flash_do_release (do_data);
|
|
#endif
|
|
if (tag == GPG_DO_PW_STATUS)
|
|
{
|
|
/* XXX: only the first byte can be changed */
|
|
}
|
|
else
|
|
{
|
|
if (len == 0)
|
|
/* make DO empty */
|
|
do_p->obj = NULL;
|
|
else
|
|
{
|
|
do_p->obj = flash_do_write (tag, data, len);
|
|
if (do_p->obj)
|
|
write_res_apdu (NULL, 0, 0x90, 0x00); /* success */
|
|
else
|
|
write_res_apdu (NULL, 0, 0x65, 0x81); /* memory failure */
|
|
}
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
if (tag == GPG_DO_RESETTING_CODE)
|
|
{
|
|
/* XXX: Changing Resetting code,
|
|
* we need to reset RC counter in GPG_DO_PW_STATUS */
|
|
}
|
|
break;
|
|
}
|
|
case DO_PROC_WRITE:
|
|
{
|
|
void (*proc_func)(uint16_t, uint8_t *, int)
|
|
= (void (*)(uint16_t, uint8_t *, int))do_p->obj;
|
|
|
|
proc_func (tag, data, len);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
/* No record */
|
|
write_res_apdu (NULL, 0, 0x6a, 0x88);
|
|
}
|