2010-09-03 15:42:36 +00:00
|
|
|
/*
|
|
|
|
* openpgp.c -- OpenPGP card protocol support
|
|
|
|
*
|
|
|
|
* 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/>.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2010-09-04 04:48:26 +00:00
|
|
|
#include "config.h"
|
2010-09-03 15:42:36 +00:00
|
|
|
#include "ch.h"
|
|
|
|
#include "hal.h"
|
|
|
|
#include "gnuk.h"
|
|
|
|
#include "openpgp.h"
|
|
|
|
#include "polarssl/config.h"
|
|
|
|
#include "polarssl/sha1.h"
|
|
|
|
|
|
|
|
#define INS_VERIFY 0x20
|
|
|
|
#define INS_CHANGE_REFERENCE_DATA 0x24
|
|
|
|
#define INS_PSO 0x2a
|
|
|
|
#define INS_RESET_RETRY_COUNTER 0x2c
|
|
|
|
#define INS_PGP_GENERATE_ASYMMETRIC_KEY_PAIR 0x47
|
2010-10-16 00:22:18 +00:00
|
|
|
#define INS_INTERNAL_AUTHENTICATE 0x88
|
2010-09-03 15:42:36 +00:00
|
|
|
#define INS_SELECT_FILE 0xa4
|
|
|
|
#define INS_READ_BINARY 0xb0
|
|
|
|
#define INS_GET_DATA 0xca
|
|
|
|
#define INS_PUT_DATA 0xda
|
|
|
|
#define INS_PUT_DATA_ODD 0xdb /* For key import */
|
|
|
|
|
2010-09-05 16:55:29 +00:00
|
|
|
static const uint8_t
|
2010-09-04 09:52:25 +00:00
|
|
|
select_file_TOP_result[] __attribute__ ((aligned (1))) = {
|
|
|
|
0x00, 0x00, /* unused */
|
|
|
|
0x0b, 0x10, /* number of bytes in this directory */
|
|
|
|
0x3f, 0x00, /* field of selected file: MF, 3f00 */
|
|
|
|
0x38, /* it's DF */
|
|
|
|
0xff, /* unused */
|
|
|
|
0xff, 0x44, 0x44, /* access conditions */
|
|
|
|
0x01, /* status of the selected file (OK, unblocked) */
|
|
|
|
0x05, /* number of bytes of data follow */
|
|
|
|
0x03, /* Features: unused */
|
|
|
|
0x01, /* number of subdirectories (OpenPGP) */
|
|
|
|
0x01, /* number of elementary files (SerialNo) */
|
|
|
|
0x00, /* number of secret codes */
|
|
|
|
0x00, /* Unused */
|
|
|
|
0x00, 0x00 /* PIN status: OK, PIN blocked?: No */
|
|
|
|
};
|
|
|
|
|
2010-09-03 15:42:36 +00:00
|
|
|
void
|
|
|
|
write_res_apdu (const uint8_t *p, int len, uint8_t sw1, uint8_t sw2)
|
|
|
|
{
|
|
|
|
res_APDU_size = 2 + len;
|
|
|
|
if (len)
|
|
|
|
memcpy (res_APDU, p, len);
|
|
|
|
res_APDU[len] = sw1;
|
|
|
|
res_APDU[len+1] = sw2;
|
|
|
|
}
|
|
|
|
|
2010-10-22 08:18:06 +00:00
|
|
|
#define FILE_NONE 0
|
|
|
|
#define FILE_DF_OPENPGP 1
|
|
|
|
#define FILE_MF 2
|
|
|
|
#define FILE_EF_DIR 3
|
|
|
|
#define FILE_EF_SERIAL 4
|
2010-09-03 15:42:36 +00:00
|
|
|
|
2010-10-22 08:18:06 +00:00
|
|
|
static uint8_t file_selection = FILE_NONE;
|
2010-09-03 15:42:36 +00:00
|
|
|
|
|
|
|
static void
|
|
|
|
cmd_verify (void)
|
|
|
|
{
|
|
|
|
int len;
|
|
|
|
uint8_t p2 = cmd_APDU[3];
|
|
|
|
int r;
|
2010-09-05 16:55:29 +00:00
|
|
|
int data_start = 5;
|
2010-09-03 15:42:36 +00:00
|
|
|
|
|
|
|
DEBUG_INFO (" - VERIFY\r\n");
|
2010-09-05 09:10:54 +00:00
|
|
|
DEBUG_BYTE (p2);
|
2010-09-03 15:42:36 +00:00
|
|
|
|
|
|
|
len = cmd_APDU[4];
|
2010-09-05 16:55:29 +00:00
|
|
|
if (len == 0) /* extended length */
|
|
|
|
{
|
|
|
|
len = (cmd_APDU[5]<<8) | cmd_APDU[6];
|
|
|
|
data_start = 7;
|
|
|
|
}
|
|
|
|
|
2010-09-03 15:42:36 +00:00
|
|
|
if (p2 == 0x81)
|
2010-09-05 16:55:29 +00:00
|
|
|
r = verify_pso_cds (&cmd_APDU[data_start], len);
|
2010-09-03 15:42:36 +00:00
|
|
|
else if (p2 == 0x82)
|
2010-09-05 16:55:29 +00:00
|
|
|
r = verify_pso_other (&cmd_APDU[data_start], len);
|
2010-09-03 15:42:36 +00:00
|
|
|
else
|
2010-09-05 16:55:29 +00:00
|
|
|
r = verify_admin (&cmd_APDU[data_start], len);
|
2010-09-03 15:42:36 +00:00
|
|
|
|
|
|
|
if (r < 0)
|
2010-09-05 09:10:54 +00:00
|
|
|
{
|
|
|
|
DEBUG_INFO ("failed\r\n");
|
|
|
|
GPG_SECURITY_FAILURE ();
|
|
|
|
}
|
2010-09-03 15:42:36 +00:00
|
|
|
else if (r == 0)
|
2010-09-05 09:10:54 +00:00
|
|
|
{
|
|
|
|
DEBUG_INFO ("blocked\r\n");
|
|
|
|
GPG_SECURITY_AUTH_BLOCKED ();
|
|
|
|
}
|
2010-09-03 15:42:36 +00:00
|
|
|
else
|
2010-09-05 09:10:54 +00:00
|
|
|
{
|
|
|
|
DEBUG_INFO ("good\r\n");
|
|
|
|
GPG_SUCCESS ();
|
|
|
|
}
|
2010-09-03 15:42:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
gpg_change_keystring (int who_old, const uint8_t *old_ks,
|
|
|
|
int who_new, const uint8_t *new_ks)
|
|
|
|
{
|
2010-09-09 08:50:34 +00:00
|
|
|
int r;
|
|
|
|
int prv_keys_exist = 0;
|
|
|
|
|
|
|
|
r = gpg_do_load_prvkey (GPG_KEY_FOR_SIGNING, who_old, old_ks);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (r > 0)
|
|
|
|
prv_keys_exist++;
|
|
|
|
|
|
|
|
r = gpg_do_chks_prvkey (GPG_KEY_FOR_SIGNING, who_old, old_ks,
|
|
|
|
who_new, new_ks);
|
|
|
|
if (r < 0)
|
|
|
|
return -2;
|
2010-09-03 15:42:36 +00:00
|
|
|
|
2010-09-09 08:50:34 +00:00
|
|
|
r = gpg_do_load_prvkey (GPG_KEY_FOR_DECRYPTION, who_old, old_ks);
|
|
|
|
if (r < 0)
|
2010-09-03 15:42:36 +00:00
|
|
|
return r;
|
|
|
|
|
2010-09-09 08:50:34 +00:00
|
|
|
if (r > 0)
|
|
|
|
prv_keys_exist++;
|
|
|
|
|
|
|
|
r = gpg_do_chks_prvkey (GPG_KEY_FOR_DECRYPTION, who_old, old_ks,
|
2010-09-03 15:42:36 +00:00
|
|
|
who_new, new_ks);
|
|
|
|
if (r < 0)
|
|
|
|
return -2;
|
|
|
|
|
2010-11-05 08:43:01 +00:00
|
|
|
r = gpg_do_load_prvkey (GPG_KEY_FOR_AUTHENTICATION, who_old, old_ks);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (r > 0)
|
|
|
|
prv_keys_exist++;
|
|
|
|
|
|
|
|
r = gpg_do_chks_prvkey (GPG_KEY_FOR_AUTHENTICATION, who_old, old_ks,
|
|
|
|
who_new, new_ks);
|
|
|
|
if (r < 0)
|
|
|
|
return -2;
|
|
|
|
|
2010-09-09 08:50:34 +00:00
|
|
|
if (prv_keys_exist)
|
|
|
|
return 1;
|
|
|
|
else
|
|
|
|
return 0;
|
2010-09-03 15:42:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
cmd_change_password (void)
|
|
|
|
{
|
|
|
|
uint8_t old_ks[KEYSTRING_MD_SIZE];
|
|
|
|
uint8_t new_ks0[KEYSTRING_MD_SIZE+1];
|
|
|
|
uint8_t *new_ks = &new_ks0[1];
|
|
|
|
uint8_t p2 = cmd_APDU[3];
|
|
|
|
int len = cmd_APDU[4];
|
|
|
|
const uint8_t *pw = &cmd_APDU[5];
|
|
|
|
const uint8_t *newpw;
|
|
|
|
int pw_len, newpw_len;
|
|
|
|
int who = p2 - 0x80;
|
|
|
|
int r;
|
|
|
|
|
2010-09-05 09:10:54 +00:00
|
|
|
DEBUG_INFO ("Change PW\r\n");
|
|
|
|
DEBUG_BYTE (who);
|
|
|
|
|
2010-09-05 16:55:29 +00:00
|
|
|
if (len == 0) /* extended length */
|
|
|
|
{
|
|
|
|
len = (cmd_APDU[5]<<8) | cmd_APDU[6];
|
|
|
|
pw += 2;
|
|
|
|
}
|
|
|
|
|
2010-10-16 00:22:18 +00:00
|
|
|
if (who == BY_USER) /* PW1 */
|
2010-09-03 15:42:36 +00:00
|
|
|
{
|
2010-09-05 09:10:54 +00:00
|
|
|
const uint8_t *pk = gpg_do_read_simple (NR_DO_KEYSTRING_PW1);
|
2010-09-03 15:42:36 +00:00
|
|
|
|
|
|
|
if (pk == NULL)
|
|
|
|
{
|
2010-09-09 08:50:34 +00:00
|
|
|
if (len < (int)strlen (OPENPGP_CARD_INITIAL_PW1))
|
2010-09-03 15:42:36 +00:00
|
|
|
{
|
2010-09-05 09:10:54 +00:00
|
|
|
DEBUG_INFO ("permission denied.\r\n");
|
2010-09-03 15:42:36 +00:00
|
|
|
GPG_SECURITY_FAILURE ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-09-09 08:50:34 +00:00
|
|
|
pw_len = strlen (OPENPGP_CARD_INITIAL_PW1);
|
2010-09-03 15:42:36 +00:00
|
|
|
newpw = pw + pw_len;
|
|
|
|
newpw_len = len - pw_len;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pw_len = pk[0];
|
|
|
|
newpw = pw + pw_len;
|
|
|
|
newpw_len = len - pw_len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else /* PW3 (0x83) */
|
|
|
|
{
|
|
|
|
pw_len = verify_admin_0 (pw, len, -1);
|
|
|
|
|
|
|
|
if (pw_len < 0)
|
|
|
|
{
|
2010-09-05 09:10:54 +00:00
|
|
|
DEBUG_INFO ("permission denied.\r\n");
|
2010-09-03 15:42:36 +00:00
|
|
|
GPG_SECURITY_FAILURE ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (pw_len == 0)
|
|
|
|
{
|
2010-09-05 09:10:54 +00:00
|
|
|
DEBUG_INFO ("blocked.\r\n");
|
2010-09-03 15:42:36 +00:00
|
|
|
GPG_SECURITY_AUTH_BLOCKED ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
newpw = pw + pw_len;
|
|
|
|
newpw_len = len - pw_len;
|
|
|
|
gpg_set_pw3 (newpw, newpw_len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sha1 (pw, pw_len, old_ks);
|
|
|
|
sha1 (newpw, newpw_len, new_ks);
|
|
|
|
new_ks0[0] = newpw_len;
|
|
|
|
|
|
|
|
r = gpg_change_keystring (who, old_ks, who, new_ks);
|
|
|
|
if (r < -2)
|
2010-09-05 09:10:54 +00:00
|
|
|
{
|
|
|
|
DEBUG_INFO ("memory error.\r\n");
|
|
|
|
GPG_MEMORY_FAILURE ();
|
|
|
|
}
|
2010-09-03 15:42:36 +00:00
|
|
|
else if (r < 0)
|
2010-09-05 09:10:54 +00:00
|
|
|
{
|
|
|
|
DEBUG_INFO ("security error.\r\n");
|
|
|
|
GPG_SECURITY_FAILURE ();
|
|
|
|
}
|
2010-10-16 00:22:18 +00:00
|
|
|
else if (r == 0 && who == BY_USER) /* no prvkey */
|
2010-09-03 15:42:36 +00:00
|
|
|
{
|
2010-09-05 09:10:54 +00:00
|
|
|
gpg_do_write_simple (NR_DO_KEYSTRING_PW1, new_ks0, KEYSTRING_SIZE_PW1);
|
2010-09-04 09:44:01 +00:00
|
|
|
ac_reset_pso_cds ();
|
2010-11-05 07:42:17 +00:00
|
|
|
gpg_reset_pw_err_counter (PW_ERR_PW1);
|
2010-09-09 08:50:34 +00:00
|
|
|
DEBUG_INFO ("Changed DO_KEYSTRING_PW1.\r\n");
|
2010-11-08 00:40:31 +00:00
|
|
|
GPG_SUCCESS ();
|
2010-09-03 15:42:36 +00:00
|
|
|
}
|
2010-10-16 00:22:18 +00:00
|
|
|
else if (r > 0 && who == BY_USER)
|
2010-09-03 15:42:36 +00:00
|
|
|
{
|
2010-09-05 09:10:54 +00:00
|
|
|
gpg_do_write_simple (NR_DO_KEYSTRING_PW1, new_ks0, 1);
|
2010-09-04 09:44:01 +00:00
|
|
|
ac_reset_pso_cds ();
|
2010-11-05 07:42:17 +00:00
|
|
|
gpg_reset_pw_err_counter (PW_ERR_PW1);
|
2010-09-09 08:50:34 +00:00
|
|
|
DEBUG_INFO ("Changed length of DO_KEYSTRING_PW1.\r\n");
|
2010-11-08 00:40:31 +00:00
|
|
|
GPG_SUCCESS ();
|
2010-09-03 15:42:36 +00:00
|
|
|
}
|
2010-10-16 00:22:18 +00:00
|
|
|
else /* r >= 0 && who == BY_ADMIN */
|
2010-09-04 09:44:01 +00:00
|
|
|
{
|
2010-09-05 09:10:54 +00:00
|
|
|
DEBUG_INFO ("done.\r\n");
|
2010-11-05 07:42:17 +00:00
|
|
|
gpg_reset_pw_err_counter (PW_ERR_PW3);
|
2010-09-04 09:44:01 +00:00
|
|
|
GPG_SUCCESS ();
|
|
|
|
}
|
2010-09-03 15:42:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
cmd_reset_user_password (void)
|
|
|
|
{
|
|
|
|
uint8_t p1 = cmd_APDU[2];
|
2010-09-05 16:55:29 +00:00
|
|
|
int len = cmd_APDU[4];
|
|
|
|
const uint8_t *pw = &cmd_APDU[5];
|
2010-09-03 15:42:36 +00:00
|
|
|
const uint8_t *newpw;
|
|
|
|
int pw_len, newpw_len;
|
|
|
|
int r;
|
2010-09-05 16:55:29 +00:00
|
|
|
uint8_t new_ks0[KEYSTRING_MD_SIZE+1];
|
|
|
|
uint8_t *new_ks = &new_ks0[1];
|
2010-09-03 15:42:36 +00:00
|
|
|
|
2010-09-05 09:10:54 +00:00
|
|
|
DEBUG_INFO ("Reset PW1\r\n");
|
|
|
|
DEBUG_BYTE (p1);
|
|
|
|
|
2010-09-05 16:55:29 +00:00
|
|
|
if (len == 0) /* extended length */
|
|
|
|
{
|
|
|
|
len = (cmd_APDU[5]<<8) | cmd_APDU[6];
|
|
|
|
pw += 2;
|
|
|
|
}
|
|
|
|
|
2010-09-03 15:42:36 +00:00
|
|
|
if (p1 == 0x00) /* by User with Reseting Code */
|
|
|
|
{
|
2010-09-05 09:10:54 +00:00
|
|
|
const uint8_t *ks_rc = gpg_do_read_simple (NR_DO_KEYSTRING_RC);
|
2010-09-03 15:42:36 +00:00
|
|
|
uint8_t old_ks[KEYSTRING_MD_SIZE];
|
|
|
|
|
2010-11-05 07:42:17 +00:00
|
|
|
if (gpg_passwd_locked (PW_ERR_RC))
|
2010-09-03 15:42:36 +00:00
|
|
|
{
|
2010-09-05 09:10:54 +00:00
|
|
|
DEBUG_INFO ("blocked.\r\n");
|
2010-09-03 15:42:36 +00:00
|
|
|
GPG_SECURITY_AUTH_BLOCKED ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ks_rc == NULL)
|
|
|
|
{
|
2010-09-05 09:10:54 +00:00
|
|
|
DEBUG_INFO ("security error.\r\n");
|
2010-09-03 15:42:36 +00:00
|
|
|
GPG_SECURITY_FAILURE ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pw_len = ks_rc[0];
|
|
|
|
newpw = pw + pw_len;
|
|
|
|
newpw_len = len - pw_len;
|
|
|
|
sha1 (pw, pw_len, old_ks);
|
|
|
|
sha1 (newpw, newpw_len, new_ks);
|
|
|
|
new_ks0[0] = newpw_len;
|
2010-10-16 00:22:18 +00:00
|
|
|
r = gpg_change_keystring (BY_RESETCODE, old_ks, BY_USER, new_ks);
|
2010-09-03 15:42:36 +00:00
|
|
|
if (r < -2)
|
2010-09-05 09:10:54 +00:00
|
|
|
{
|
|
|
|
DEBUG_INFO ("memory error.\r\n");
|
|
|
|
GPG_MEMORY_FAILURE ();
|
|
|
|
}
|
2010-09-03 15:42:36 +00:00
|
|
|
else if (r < 0)
|
|
|
|
{
|
|
|
|
sec_fail:
|
2010-09-05 09:10:54 +00:00
|
|
|
DEBUG_INFO ("failed.\r\n");
|
2010-11-05 07:42:17 +00:00
|
|
|
gpg_increment_pw_err_counter (PW_ERR_RC);
|
2010-09-03 15:42:36 +00:00
|
|
|
GPG_SECURITY_FAILURE ();
|
|
|
|
}
|
|
|
|
else if (r == 0)
|
|
|
|
{
|
|
|
|
if (memcmp (ks_rc+1, old_ks, KEYSTRING_MD_SIZE) != 0)
|
|
|
|
goto sec_fail;
|
2010-11-05 07:42:17 +00:00
|
|
|
DEBUG_INFO ("done (no prvkey).\r\n");
|
2010-09-05 09:10:54 +00:00
|
|
|
gpg_do_write_simple (NR_DO_KEYSTRING_PW1, new_ks0, KEYSTRING_SIZE_PW1);
|
2010-09-04 09:44:01 +00:00
|
|
|
ac_reset_pso_cds ();
|
2010-11-05 07:42:17 +00:00
|
|
|
gpg_reset_pw_err_counter (PW_ERR_RC);
|
|
|
|
gpg_reset_pw_err_counter (PW_ERR_PW1);
|
2010-11-08 00:40:31 +00:00
|
|
|
GPG_SUCCESS ();
|
2010-09-03 15:42:36 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-09-05 09:10:54 +00:00
|
|
|
DEBUG_INFO ("done.\r\n");
|
2010-09-04 09:44:01 +00:00
|
|
|
ac_reset_pso_cds ();
|
2010-11-05 07:42:17 +00:00
|
|
|
gpg_reset_pw_err_counter (PW_ERR_RC);
|
|
|
|
gpg_reset_pw_err_counter (PW_ERR_PW1);
|
2010-09-03 15:42:36 +00:00
|
|
|
GPG_SUCCESS ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else /* by Admin (p1 == 0x02) */
|
|
|
|
{
|
2010-09-05 16:55:29 +00:00
|
|
|
const uint8_t *old_ks = keystring_md_pw3;
|
|
|
|
|
2010-09-03 15:42:36 +00:00
|
|
|
if (!ac_check_status (AC_ADMIN_AUTHORIZED))
|
2010-09-05 09:10:54 +00:00
|
|
|
{
|
|
|
|
DEBUG_INFO ("permission denied.\r\n");
|
|
|
|
GPG_SECURITY_FAILURE ();
|
2010-09-05 16:55:29 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
newpw_len = len;
|
|
|
|
newpw = pw;
|
|
|
|
sha1 (newpw, newpw_len, new_ks);
|
|
|
|
new_ks0[0] = newpw_len;
|
2010-10-16 00:22:18 +00:00
|
|
|
r = gpg_change_keystring (BY_ADMIN, old_ks, BY_USER, new_ks);
|
2010-09-05 16:55:29 +00:00
|
|
|
if (r < -2)
|
|
|
|
{
|
|
|
|
DEBUG_INFO ("memory error.\r\n");
|
|
|
|
GPG_MEMORY_FAILURE ();
|
|
|
|
}
|
|
|
|
else if (r < 0)
|
|
|
|
{
|
|
|
|
DEBUG_INFO ("security error.\r\n");
|
|
|
|
GPG_SECURITY_FAILURE ();
|
|
|
|
}
|
|
|
|
else if (r == 0)
|
|
|
|
{
|
|
|
|
DEBUG_INFO ("done (no privkey).\r\n");
|
|
|
|
gpg_do_write_simple (NR_DO_KEYSTRING_PW1, new_ks0, KEYSTRING_SIZE_PW1);
|
|
|
|
ac_reset_pso_cds ();
|
2010-11-05 07:42:17 +00:00
|
|
|
gpg_reset_pw_err_counter (PW_ERR_PW1);
|
2010-11-08 00:40:31 +00:00
|
|
|
GPG_SUCCESS ();
|
2010-09-05 09:10:54 +00:00
|
|
|
}
|
2010-09-03 15:42:36 +00:00
|
|
|
else
|
|
|
|
{
|
2010-09-05 16:55:29 +00:00
|
|
|
DEBUG_INFO ("done.\r\n");
|
|
|
|
ac_reset_pso_cds ();
|
2010-11-05 07:42:17 +00:00
|
|
|
gpg_reset_pw_err_counter (PW_ERR_PW1);
|
2010-09-05 16:55:29 +00:00
|
|
|
GPG_SUCCESS ();
|
2010-09-03 15:42:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
cmd_put_data (void)
|
|
|
|
{
|
|
|
|
uint8_t *data;
|
|
|
|
uint16_t tag;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
DEBUG_INFO (" - PUT DATA\r\n");
|
|
|
|
|
|
|
|
if (file_selection != FILE_DF_OPENPGP)
|
|
|
|
GPG_NO_RECORD();
|
|
|
|
|
|
|
|
tag = ((cmd_APDU[2]<<8) | cmd_APDU[3]);
|
|
|
|
len = cmd_APDU_size - 5;
|
|
|
|
data = &cmd_APDU[5];
|
|
|
|
if (len >= 256)
|
|
|
|
/* extended Lc */
|
|
|
|
{
|
|
|
|
data += 2;
|
|
|
|
len -= 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
gpg_do_put_data (tag, data, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
cmd_pgp_gakp (void)
|
|
|
|
{
|
|
|
|
DEBUG_INFO (" - Generate Asymmetric Key Pair\r\n");
|
2010-09-05 09:10:54 +00:00
|
|
|
DEBUG_BYTE (cmd_APDU[2]);
|
2010-09-03 15:42:36 +00:00
|
|
|
|
|
|
|
if (cmd_APDU[2] == 0x81)
|
|
|
|
/* Get public key */
|
2010-09-05 16:55:29 +00:00
|
|
|
gpg_do_public_key (cmd_APDU[7]);
|
2010-09-03 15:42:36 +00:00
|
|
|
else
|
|
|
|
{ /* Generate key pair */
|
|
|
|
if (!ac_check_status (AC_ADMIN_AUTHORIZED))
|
|
|
|
GPG_SECURITY_FAILURE ();
|
|
|
|
|
|
|
|
/* XXX: Not yet supported */
|
2010-09-05 09:10:54 +00:00
|
|
|
GPG_ERROR ();
|
2010-09-03 15:42:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
cmd_read_binary (void)
|
|
|
|
{
|
|
|
|
DEBUG_INFO (" - Read binary\r\n");
|
|
|
|
|
|
|
|
if (file_selection == FILE_EF_SERIAL)
|
|
|
|
{
|
|
|
|
if (cmd_APDU[3] >= 6)
|
|
|
|
GPG_BAD_P0_P1 ();
|
|
|
|
else
|
2010-09-09 08:50:34 +00:00
|
|
|
{
|
|
|
|
int len = openpgpcard_aid[0];
|
|
|
|
|
|
|
|
res_APDU[0] = 0x5a;
|
|
|
|
memcpy (res_APDU+1, openpgpcard_aid, len);
|
|
|
|
res_APDU[len+1] = 0x90;
|
|
|
|
res_APDU[len+2] = 0x00;
|
|
|
|
res_APDU_size = len + 3;
|
|
|
|
}
|
2010-09-03 15:42:36 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
GPG_NO_RECORD();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
cmd_select_file (void)
|
|
|
|
{
|
|
|
|
if (cmd_APDU[2] == 4) /* Selection by DF name */
|
|
|
|
{
|
|
|
|
DEBUG_INFO (" - select DF by name\r\n");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* P2 == 0, LC=6, name = D2 76 00 01 24 01
|
|
|
|
*/
|
|
|
|
|
|
|
|
file_selection = FILE_DF_OPENPGP;
|
|
|
|
GPG_SUCCESS ();
|
|
|
|
}
|
|
|
|
else if (cmd_APDU[4] == 2
|
|
|
|
&& cmd_APDU[5] == 0x2f
|
|
|
|
&& cmd_APDU[6] == 0x02)
|
|
|
|
{
|
|
|
|
DEBUG_INFO (" - select 0x2f02 EF\r\n");
|
|
|
|
/*
|
|
|
|
* MF.EF-GDO -- Serial number of the card and name of the owner
|
|
|
|
*/
|
|
|
|
GPG_SUCCESS ();
|
|
|
|
file_selection = FILE_EF_SERIAL;
|
|
|
|
}
|
|
|
|
else if (cmd_APDU[4] == 2
|
|
|
|
&& cmd_APDU[5] == 0x3f
|
|
|
|
&& cmd_APDU[6] == 0x00)
|
|
|
|
{
|
|
|
|
DEBUG_INFO (" - select ROOT MF\r\n");
|
|
|
|
if (cmd_APDU[3] == 0x0c)
|
|
|
|
{
|
|
|
|
GPG_SUCCESS ();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-09-04 09:52:25 +00:00
|
|
|
write_res_apdu (select_file_TOP_result,
|
2010-09-03 15:42:36 +00:00
|
|
|
sizeof (select_file_TOP_result), 0x90, 0x00);
|
2010-09-13 02:47:21 +00:00
|
|
|
res_APDU[2] = (data_objects_number_of_bytes & 0xff);
|
|
|
|
res_APDU[3] = (data_objects_number_of_bytes >> 8);
|
2010-09-03 15:42:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
file_selection = FILE_MF;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DEBUG_INFO (" - select ?? \r\n");
|
|
|
|
|
|
|
|
file_selection = FILE_NONE;
|
|
|
|
GPG_NO_FILE();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
cmd_get_data (void)
|
|
|
|
{
|
|
|
|
uint16_t tag = ((cmd_APDU[2]<<8) | cmd_APDU[3]);
|
|
|
|
|
|
|
|
DEBUG_INFO (" - Get Data\r\n");
|
|
|
|
|
|
|
|
if (file_selection != FILE_DF_OPENPGP)
|
|
|
|
GPG_NO_RECORD();
|
|
|
|
|
|
|
|
gpg_do_get_data (tag);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
cmd_pso (void)
|
|
|
|
{
|
2010-09-08 05:24:12 +00:00
|
|
|
int len = cmd_APDU[4];
|
|
|
|
int data_start = 5;
|
2010-09-05 09:10:54 +00:00
|
|
|
int r;
|
|
|
|
|
2010-09-08 05:24:12 +00:00
|
|
|
if (len == 0)
|
|
|
|
{
|
|
|
|
len = (cmd_APDU[5]<<8) | cmd_APDU[6];
|
|
|
|
data_start = 7;
|
|
|
|
}
|
|
|
|
|
|
|
|
DEBUG_INFO (" - PSO: ");
|
|
|
|
DEBUG_WORD ((uint32_t)&r);
|
2010-09-03 15:42:36 +00:00
|
|
|
|
2010-09-05 09:10:54 +00:00
|
|
|
if (cmd_APDU[2] == 0x9e && cmd_APDU[3] == 0x9a)
|
2010-09-03 15:42:36 +00:00
|
|
|
{
|
|
|
|
if (!ac_check_status (AC_PSO_CDS_AUTHORIZED))
|
|
|
|
{
|
2010-09-05 09:10:54 +00:00
|
|
|
DEBUG_INFO ("security error.");
|
2010-09-03 15:42:36 +00:00
|
|
|
GPG_SECURITY_FAILURE ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cmd_APDU_size != 8 + 35 && cmd_APDU_size != 8 + 35 + 1)
|
|
|
|
/* Extended Lc: 3-byte */
|
|
|
|
{
|
|
|
|
DEBUG_INFO (" wrong length: ");
|
|
|
|
DEBUG_SHORT (cmd_APDU_size);
|
2010-09-08 05:24:12 +00:00
|
|
|
GPG_ERROR ();
|
2010-09-03 15:42:36 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-09-08 05:24:12 +00:00
|
|
|
DEBUG_SHORT (len); /* Should be cmd_APDU_size - 6 */
|
2010-09-03 15:42:36 +00:00
|
|
|
|
2010-09-08 05:24:12 +00:00
|
|
|
r = rsa_sign (&cmd_APDU[data_start], res_APDU, len);
|
2010-09-03 15:42:36 +00:00
|
|
|
if (r < 0)
|
2010-09-09 08:50:34 +00:00
|
|
|
{
|
|
|
|
ac_reset_pso_cds ();
|
|
|
|
GPG_ERROR ();
|
|
|
|
}
|
2010-09-03 15:42:36 +00:00
|
|
|
else
|
|
|
|
{ /* Success */
|
2010-11-05 07:42:17 +00:00
|
|
|
if (gpg_get_pw1_lifetime ())
|
2010-09-04 09:44:01 +00:00
|
|
|
ac_reset_pso_cds ();
|
2010-09-03 15:42:36 +00:00
|
|
|
|
2010-11-05 07:42:17 +00:00
|
|
|
gpg_increment_digital_signature_counter ();
|
2010-09-03 15:42:36 +00:00
|
|
|
}
|
|
|
|
}
|
2010-09-05 09:10:54 +00:00
|
|
|
}
|
|
|
|
else if (cmd_APDU[2] == 0x80 && cmd_APDU[3] == 0x86)
|
|
|
|
{
|
2010-10-16 00:22:18 +00:00
|
|
|
DEBUG_SHORT (len);
|
|
|
|
|
2010-11-05 07:42:17 +00:00
|
|
|
if (gpg_passwd_locked (PW_ERR_PW1)
|
2010-10-16 00:22:18 +00:00
|
|
|
|| !ac_check_status (AC_PSO_OTHER_AUTHORIZED))
|
2010-09-05 09:10:54 +00:00
|
|
|
{
|
|
|
|
DEBUG_INFO ("security error.");
|
|
|
|
GPG_SECURITY_FAILURE ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-10-16 00:22:18 +00:00
|
|
|
if ((r = gpg_do_load_prvkey (GPG_KEY_FOR_DECRYPTION, BY_USER,
|
|
|
|
pw1_keystring + 1)) < 0)
|
|
|
|
{
|
2010-11-05 07:42:17 +00:00
|
|
|
gpg_increment_pw_err_counter (PW_ERR_PW1);
|
2010-10-16 00:22:18 +00:00
|
|
|
GPG_SECURITY_FAILURE ();
|
|
|
|
return;
|
|
|
|
}
|
2010-11-05 07:42:17 +00:00
|
|
|
else
|
|
|
|
/* Reset counter as it's success now */
|
|
|
|
gpg_reset_pw_err_counter (PW_ERR_PW1);
|
2010-09-05 09:10:54 +00:00
|
|
|
|
2010-09-09 08:50:34 +00:00
|
|
|
ac_reset_pso_other ();
|
|
|
|
|
2010-09-08 05:24:12 +00:00
|
|
|
/* Skip padding 0x00 */
|
|
|
|
data_start++;
|
|
|
|
len--;
|
|
|
|
r = rsa_decrypt (&cmd_APDU[data_start], res_APDU, len);
|
2010-09-05 09:10:54 +00:00
|
|
|
if (r < 0)
|
|
|
|
GPG_ERROR ();
|
2010-09-03 15:42:36 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{ /* XXX: not yet supported */
|
|
|
|
DEBUG_INFO (" - ??");
|
|
|
|
DEBUG_BYTE (cmd_APDU[2]);
|
|
|
|
DEBUG_INFO (" - ??");
|
|
|
|
DEBUG_BYTE (cmd_APDU[3]);
|
2010-09-08 05:24:12 +00:00
|
|
|
GPG_ERROR ();
|
2010-09-03 15:42:36 +00:00
|
|
|
}
|
2010-09-05 09:10:54 +00:00
|
|
|
|
|
|
|
DEBUG_INFO ("PSO done.\r\n");
|
2010-09-03 15:42:36 +00:00
|
|
|
}
|
|
|
|
|
2010-10-16 00:22:18 +00:00
|
|
|
static void
|
|
|
|
cmd_internal_authenticate (void)
|
|
|
|
{
|
|
|
|
int len = cmd_APDU[4];
|
|
|
|
int data_start = 5;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
if (len == 0)
|
|
|
|
{
|
|
|
|
len = (cmd_APDU[5]<<8) | cmd_APDU[6];
|
|
|
|
data_start = 7;
|
|
|
|
}
|
|
|
|
|
|
|
|
DEBUG_INFO (" - INTERNAL AUTHENTICATE\r\n");
|
|
|
|
|
|
|
|
if (cmd_APDU[2] == 0x00 && cmd_APDU[3] == 0x00)
|
|
|
|
{
|
|
|
|
DEBUG_SHORT (len);
|
|
|
|
|
2010-11-05 07:42:17 +00:00
|
|
|
if (gpg_passwd_locked (PW_ERR_PW1)
|
2010-10-16 00:22:18 +00:00
|
|
|
|| !ac_check_status (AC_PSO_OTHER_AUTHORIZED))
|
|
|
|
{
|
|
|
|
DEBUG_INFO ("security error.");
|
|
|
|
GPG_SECURITY_FAILURE ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((r = gpg_do_load_prvkey (GPG_KEY_FOR_AUTHENTICATION, BY_USER,
|
|
|
|
pw1_keystring + 1)) < 0)
|
|
|
|
{
|
2010-11-05 07:42:17 +00:00
|
|
|
gpg_increment_pw_err_counter (PW_ERR_PW1);
|
2010-10-16 00:22:18 +00:00
|
|
|
GPG_SECURITY_FAILURE ();
|
|
|
|
return;
|
|
|
|
}
|
2010-11-05 07:42:17 +00:00
|
|
|
else
|
|
|
|
/* Reset counter as it's success now */
|
|
|
|
gpg_reset_pw_err_counter (PW_ERR_PW1);
|
2010-10-16 00:22:18 +00:00
|
|
|
|
|
|
|
ac_reset_pso_other ();
|
|
|
|
|
|
|
|
r = rsa_sign (&cmd_APDU[data_start], res_APDU, len);
|
|
|
|
if (r < 0)
|
|
|
|
GPG_ERROR ();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DEBUG_INFO (" - ??");
|
|
|
|
DEBUG_BYTE (cmd_APDU[2]);
|
|
|
|
DEBUG_INFO (" - ??");
|
|
|
|
DEBUG_BYTE (cmd_APDU[3]);
|
|
|
|
GPG_ERROR ();
|
|
|
|
}
|
|
|
|
|
|
|
|
DEBUG_INFO ("INTERNAL AUTHENTICATE done.\r\n");
|
|
|
|
}
|
|
|
|
|
2010-09-03 15:42:36 +00:00
|
|
|
struct command
|
|
|
|
{
|
|
|
|
uint8_t command;
|
|
|
|
void (*cmd_handler) (void);
|
|
|
|
};
|
|
|
|
|
2010-09-04 04:48:26 +00:00
|
|
|
const struct command cmds[] = {
|
2010-09-03 15:42:36 +00:00
|
|
|
{ INS_VERIFY, cmd_verify },
|
|
|
|
{ INS_CHANGE_REFERENCE_DATA, cmd_change_password },
|
|
|
|
{ INS_PSO, cmd_pso },
|
|
|
|
{ INS_RESET_RETRY_COUNTER, cmd_reset_user_password },
|
|
|
|
{ INS_PGP_GENERATE_ASYMMETRIC_KEY_PAIR, cmd_pgp_gakp },
|
2010-10-16 00:22:18 +00:00
|
|
|
{ INS_INTERNAL_AUTHENTICATE, cmd_internal_authenticate },
|
2010-09-03 15:42:36 +00:00
|
|
|
{ INS_SELECT_FILE, cmd_select_file },
|
|
|
|
{ INS_READ_BINARY, cmd_read_binary },
|
|
|
|
{ INS_GET_DATA, cmd_get_data },
|
|
|
|
{ INS_PUT_DATA, cmd_put_data },
|
|
|
|
{ INS_PUT_DATA_ODD, cmd_put_data },
|
|
|
|
};
|
|
|
|
#define NUM_CMDS ((int)(sizeof (cmds) / sizeof (struct command)))
|
|
|
|
|
|
|
|
static void
|
|
|
|
process_command_apdu (void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
uint8_t cmd = cmd_APDU[1];
|
|
|
|
|
|
|
|
for (i = 0; i < NUM_CMDS; i++)
|
|
|
|
if (cmds[i].command == cmd)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (i < NUM_CMDS)
|
|
|
|
cmds[i].cmd_handler ();
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DEBUG_INFO (" - ??");
|
|
|
|
DEBUG_BYTE (cmd);
|
|
|
|
GPG_NO_INS ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Thread *gpg_thread;
|
|
|
|
|
|
|
|
msg_t
|
|
|
|
GPGthread (void *arg)
|
|
|
|
{
|
|
|
|
(void)arg;
|
|
|
|
|
|
|
|
gpg_thread = chThdSelf ();
|
|
|
|
chEvtClear (ALL_EVENTS);
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
eventmask_t m;
|
|
|
|
|
|
|
|
m = chEvtWaitOne (ALL_EVENTS);
|
|
|
|
|
2010-09-08 05:24:12 +00:00
|
|
|
DEBUG_INFO ("GPG!: ");
|
|
|
|
DEBUG_WORD ((uint32_t)&m);
|
2010-09-03 15:42:36 +00:00
|
|
|
|
|
|
|
process_command_apdu ();
|
|
|
|
|
|
|
|
chEvtSignal (icc_thread, EV_EXEC_FINISHED);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|