/*
* openpgp.c -- OpenPGP card protocol support
*
- * Copyright (C) 2010, 2011, 2012 Free Software Initiative of Japan
+ * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017
+ * Free Software Initiative of Japan
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Gnuk, a GnuPG USB Token implementation.
*
*/
+#include <stdint.h>
+#include <string.h>
+#include <chopstx.h>
+#include <eventflag.h>
+
#include "config.h"
-#include "ch.h"
-#include "hal.h"
+
#include "gnuk.h"
#include "sys.h"
-#include "openpgp.h"
+#include "status-code.h"
#include "sha256.h"
+#include "random.h"
+
+static struct eventflag *openpgp_comm;
#define ADMIN_PASSWD_MINLEN 8
#define INS_CHANGE_REFERENCE_DATA 0x24
#define INS_PSO 0x2a
#define INS_RESET_RETRY_COUNTER 0x2c
+#define INS_ACTIVATE_FILE 0x44
#define INS_PGP_GENERATE_ASYMMETRIC_KEY_PAIR 0x47
#define INS_EXTERNAL_AUTHENTICATE 0x82
#define INS_GET_CHALLENGE 0x84
#define INS_UPDATE_BINARY 0xd6
#define INS_PUT_DATA 0xda
#define INS_PUT_DATA_ODD 0xdb /* For key import */
+#define INS_TERMINATE_DF 0xe6
-#define CHALLENGE_LEN 32
static const uint8_t *challenge; /* Random bytes */
static const uint8_t
#define FILE_EF_UPDATE_KEY_2 7
#define FILE_EF_UPDATE_KEY_3 8
#define FILE_EF_CH_CERTIFICATE 9
+#define FILE_CARD_TERMINATED_OPENPGP 254
+#define FILE_CARD_TERMINATED 255
-static uint8_t file_selection;
+uint8_t file_selection;
static void
gpg_init (void)
{
- const uint8_t *flash_data_start;
+ const uint8_t *flash_do_start;
+ const uint8_t *flash_do_end;
- file_selection = FILE_NONE;
- flash_data_start = flash_init ();
- gpg_data_scan (flash_data_start);
+ flash_init (&flash_do_start, &flash_do_end);
+
+ if (flash_do_start == NULL)
+ file_selection = FILE_CARD_TERMINATED;
+ else
+ file_selection = FILE_NONE;
+
+ gpg_data_scan (flash_do_start, flash_do_end);
+ flash_init_keys ();
}
static void
int r;
led_blink (LED_START_COMMAND);
- r = pinpad_getline (msg_code, MS2ST (8000));
+ r = pinpad_getline (msg_code, 8000000);
led_blink (LED_FINISH_COMMAND);
return r;
}
cmd_verify (void)
{
int len;
+ uint8_t p1 = P1 (apdu);
uint8_t p2 = P2 (apdu);
int r;
const uint8_t *pw;
len = apdu.cmd_apdu_data_len;
pw = apdu.cmd_apdu_data;
+ if (len == 0)
+ {
+ if (p1 == 0)
+ { /* This is to examine status. */
+ if (p2 == 0x81)
+ r = ac_check_status (AC_PSO_CDS_AUTHORIZED);
+ else if (p2 == 0x82)
+ r = ac_check_status (AC_OTHER_AUTHORIZED);
+ else
+ r = ac_check_status (AC_ADMIN_AUTHORIZED);
+
+ if (r)
+ GPG_SUCCESS (); /* If authentication done already, return success. */
+ else
+ { /* If not, return retry counter, encoded. */
+ r = gpg_pw_get_retry_counter (p2);
+ set_res_sw (0x63, 0xc0 | (r&0x0f));
+ }
+ }
+ else if (p1 == 0xff)
+ { /* Reset the status. */
+ if (p2 == 0x81)
+ ac_reset_pso_cds ();
+ else if (p2 == 0x82)
+ ac_reset_other ();
+ else
+ ac_reset_admin ();
+ GPG_SUCCESS ();
+ }
+ else
+ GPG_BAD_P1_P2 ();
+ return;
+ }
+
+ /* This is real authentication. */
if (p2 == 0x81)
r = verify_pso_cds (pw, len);
else if (p2 == 0x82)
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 new_ks0[KEYSTRING_SIZE];
+ uint8_t *new_salt = KS_GET_SALT (new_ks0);
+ int newsalt_len = SALT_SIZE;
+ uint8_t *new_ks = KS_GET_KEYSTRING (new_ks0);
uint8_t p1 = P1 (apdu); /* 0: change (old+new), 1: exchange (new) */
uint8_t p2 = P2 (apdu);
int len;
int who = p2 - 0x80;
int who_old;
int r;
+ int pw3_null = 0;
+ const uint8_t *salt;
+ int salt_len;
+ const uint8_t *ks_pw3;
DEBUG_INFO ("Change PW\r\n");
DEBUG_BYTE (who);
{
const uint8_t *ks_pw1 = gpg_do_read_simple (NR_DO_KEYSTRING_PW1);
- pw_len = verify_user_0 (AC_PSO_CDS_AUTHORIZED, pw, len, -1, ks_pw1);
who_old = who;
+ pw_len = verify_user_0 (AC_PSO_CDS_AUTHORIZED, pw, len, -1, ks_pw1, 0);
+
+ if (ks_pw1 == NULL)
+ {
+ salt = NULL;
+ salt_len = 0;
+ }
+ else
+ {
+ salt = KS_GET_SALT (ks_pw1);
+ salt_len = SALT_SIZE;
+ }
if (pw_len < 0)
{
}
else
{
- const uint8_t *ks_pw3 = gpg_do_read_simple (NR_DO_KEYSTRING_PW3);
-
newpw = pw + pw_len;
newpw_len = len - pw_len;
+ ks_pw3 = gpg_do_read_simple (NR_DO_KEYSTRING_PW3);
/* Check length of password for admin-less mode. */
if (ks_pw3 == NULL && newpw_len < ADMIN_PASSWD_MINLEN)
}
else /* PW3 (0x83) */
{
- pw_len = verify_admin_0 (pw, len, -1);
+ ks_pw3 = gpg_do_read_simple (NR_DO_KEYSTRING_PW3);
+ pw_len = verify_admin_0 (pw, len, -1, ks_pw3, 0);
+
+ if (ks_pw3 == NULL)
+ {
+ salt = NULL;
+ salt_len = 0;
+ }
+ else
+ {
+ salt = KS_GET_SALT (ks_pw3);
+ salt_len = SALT_SIZE;
+ }
if (pw_len < 0)
{
{
newpw_len = strlen (OPENPGP_CARD_INITIAL_PW3);
memcpy (newpw, OPENPGP_CARD_INITIAL_PW3, newpw_len);
- gpg_do_write_simple (NR_DO_KEYSTRING_PW3, NULL, 0);
+ newsalt_len = 0;
+ pw3_null = 1;
}
- else
- gpg_set_pw3 (newpw, newpw_len);
+
who_old = admin_authorized;
}
}
- s2k (who_old, pw, pw_len, old_ks);
- s2k (who, newpw, newpw_len, new_ks);
+ if (newsalt_len != 0)
+ random_get_salt (new_salt);
+ s2k (salt, salt_len, pw, pw_len, old_ks);
+ s2k (new_salt, newsalt_len, newpw, newpw_len, new_ks);
new_ks0[0] = newpw_len;
r = gpg_change_keystring (who_old, old_ks, who, new_ks);
}
else if (r == 0 && who == BY_USER) /* no prvkey */
{
- gpg_do_write_simple (NR_DO_KEYSTRING_PW1, new_ks0, KEYSTRING_SIZE_PW1);
- ac_reset_pso_cds ();
- ac_reset_other ();
- if (admin_authorized == BY_USER)
- ac_reset_admin ();
- DEBUG_INFO ("Changed DO_KEYSTRING_PW1.\r\n");
- GPG_SUCCESS ();
+ DEBUG_INFO ("user pass change not supported with no keys.\r\n");
+ GPG_CONDITION_NOT_SATISFIED ();
}
else if (r > 0 && who == BY_USER)
{
- gpg_do_write_simple (NR_DO_KEYSTRING_PW1, new_ks0, 1);
+ gpg_do_write_simple (NR_DO_KEYSTRING_PW1, new_ks0, KS_META_SIZE);
ac_reset_pso_cds ();
ac_reset_other ();
if (admin_authorized == BY_USER)
DEBUG_INFO ("Changed length of DO_KEYSTRING_PW1.\r\n");
GPG_SUCCESS ();
}
- else /* r >= 0 && who == BY_ADMIN */
+ else if (r > 0 && who == BY_ADMIN)
+ {
+ if (pw3_null)
+ gpg_do_write_simple (NR_DO_KEYSTRING_PW3, NULL, 0);
+ else
+ gpg_do_write_simple (NR_DO_KEYSTRING_PW3, new_ks0, KS_META_SIZE);
+
+ ac_reset_admin ();
+ DEBUG_INFO ("Changed length of DO_KEYSTRING_PW3.\r\n");
+ GPG_SUCCESS ();
+ }
+ else /* r == 0 && who == BY_ADMIN */ /* no prvkey */
{
- DEBUG_INFO ("done.\r\n");
+ if (pw3_null)
+ gpg_do_write_simple (NR_DO_KEYSTRING_PW3, NULL, 0);
+ else
+ {
+ new_ks0[0] |= PW_LEN_KEYSTRING_BIT;
+ gpg_do_write_simple (NR_DO_KEYSTRING_PW3, new_ks0, KEYSTRING_SIZE);
+ }
+ DEBUG_INFO ("Changed DO_KEYSTRING_PW3.\r\n");
ac_reset_admin ();
GPG_SUCCESS ();
}
}
-#define USER_S2K_MAGIC "\xffUSER\r\n"
-#define RESETCODE_S2K_MAGIC "\xffRESET\r\n"
-
+#ifndef S2KCOUNT
+/*
+ * OpenPGP uses the value 65535 for the key on disk.
+ * Given the condition that the access to flash ROM is harder than disk,
+ * that is, the threat model is different, we chose the default value 192.
+ */
+#define S2KCOUNT 192
+#endif
void
-s2k (int who, const unsigned char *input, unsigned int ilen,
- unsigned char output[32])
+s2k (const unsigned char *salt, size_t slen,
+ const unsigned char *input, size_t ilen, unsigned char output[32])
{
sha256_context ctx;
+ size_t count = S2KCOUNT;
+ const uint8_t *unique = unique_device_id ();
sha256_start (&ctx);
- sha256_update (&ctx, input, ilen);
- if (who == BY_USER)
- sha256_update (&ctx, (unsigned char *)USER_S2K_MAGIC,
- sizeof (USER_S2K_MAGIC));
- else if (who == BY_RESETCODE)
- sha256_update (&ctx, (unsigned char *)RESETCODE_S2K_MAGIC,
- sizeof (RESETCODE_S2K_MAGIC));
- /* Not add any for BY_ADMIN */
+
+ sha256_update (&ctx, unique, 12);
+
+ while (count > slen + ilen)
+ {
+ if (slen)
+ sha256_update (&ctx, salt, slen);
+ sha256_update (&ctx, input, ilen);
+ count -= slen + ilen;
+ }
+
+ if (count <= slen)
+ sha256_update (&ctx, salt, count);
+ else
+ {
+ if (slen)
+ {
+ sha256_update (&ctx, salt, slen);
+ count -= slen;
+ }
+ sha256_update (&ctx, input, count);
+ }
+
sha256_finish (&ctx, output);
}
const uint8_t *newpw;
int pw_len, newpw_len;
int r;
- uint8_t new_ks0[KEYSTRING_MD_SIZE+1];
- uint8_t *new_ks = &new_ks0[1];
+ uint8_t new_ks0[KEYSTRING_SIZE];
+ uint8_t *new_ks = KS_GET_KEYSTRING (new_ks0);
+ uint8_t *new_salt = KS_GET_SALT (new_ks0);
+ const uint8_t *salt;
+ int salt_len;
DEBUG_INFO ("Reset PW1\r\n");
DEBUG_BYTE (p1);
return;
}
- pw_len = ks_rc[0];
+ pw_len = ks_rc[0] & PW_LEN_MASK;
+ salt = KS_GET_SALT (ks_rc);
+ salt_len = SALT_SIZE;
newpw = pw + pw_len;
newpw_len = len - pw_len;
- s2k (BY_RESETCODE, pw, pw_len, old_ks);
- s2k (BY_USER, newpw, newpw_len, new_ks);
+ random_get_salt (new_salt);
+ s2k (salt, salt_len, pw, pw_len, old_ks);
+ s2k (new_salt, SALT_SIZE, newpw, newpw_len, new_ks);
new_ks0[0] = newpw_len;
r = gpg_change_keystring (BY_RESETCODE, old_ks, BY_USER, new_ks);
if (r <= -2)
}
else if (r < 0)
{
- sec_fail:
DEBUG_INFO ("failed.\r\n");
gpg_pw_increment_err_counter (PW_ERR_RC);
GPG_SECURITY_FAILURE ();
}
else if (r == 0)
{
- if (memcmp (ks_rc+1, old_ks, KEYSTRING_MD_SIZE) != 0)
- goto sec_fail;
- DEBUG_INFO ("done (no prvkey).\r\n");
- gpg_do_write_simple (NR_DO_KEYSTRING_PW1, new_ks0,
- KEYSTRING_SIZE_PW1);
- ac_reset_pso_cds ();
- ac_reset_other ();
- if (admin_authorized == BY_USER)
- ac_reset_admin ();
- gpg_pw_reset_err_counter (PW_ERR_RC);
- gpg_pw_reset_err_counter (PW_ERR_PW1);
- GPG_SUCCESS ();
+ DEBUG_INFO ("user pass change not supported with no keys.\r\n");
+ GPG_CONDITION_NOT_SATISFIED ();
}
else
{
DEBUG_INFO ("done.\r\n");
- gpg_do_write_simple (NR_DO_KEYSTRING_PW1, new_ks0, 1);
+ gpg_do_write_simple (NR_DO_KEYSTRING_PW1, new_ks0, KS_META_SIZE);
ac_reset_pso_cds ();
ac_reset_other ();
if (admin_authorized == BY_USER)
newpw_len = len;
newpw = pw;
- s2k (BY_USER, newpw, newpw_len, new_ks);
+ random_get_salt (new_salt);
+ s2k (new_salt, SALT_SIZE, newpw, newpw_len, new_ks);
new_ks0[0] = newpw_len;
r = gpg_change_keystring (admin_authorized, old_ks, BY_USER, new_ks);
if (r <= -2)
}
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 ();
- ac_reset_other ();
- if (admin_authorized == BY_USER)
- ac_reset_admin ();
- gpg_pw_reset_err_counter (PW_ERR_PW1);
- GPG_SUCCESS ();
+ DEBUG_INFO ("user pass change not supported with no keys.\r\n");
+ GPG_CONDITION_NOT_SATISFIED ();
}
else
{
DEBUG_INFO ("done.\r\n");
- gpg_do_write_simple (NR_DO_KEYSTRING_PW1, new_ks0, 1);
+ gpg_do_write_simple (NR_DO_KEYSTRING_PW1, new_ks0, KS_META_SIZE);
ac_reset_pso_cds ();
ac_reset_other ();
if (admin_authorized == BY_USER)
DEBUG_INFO (" - PUT DATA\r\n");
- if (file_selection != FILE_DF_OPENPGP)
- GPG_NO_RECORD();
-
tag = ((P1 (apdu)<<8) | P2 (apdu));
len = apdu.cmd_apdu_data_len;
data = apdu.cmd_apdu_data;
{
if (!ac_check_status (AC_ADMIN_AUTHORIZED))
GPG_SECURITY_FAILURE ();
-#ifdef KEYGEN_SUPPORT
- /* Generate key pair */
gpg_do_keygen (apdu.cmd_apdu_data[0]);
-#else
- GPG_FUNCTION_NOT_SUPPORTED ();
-#endif
}
}
extern uint8_t _updatekey_store;
const uint8_t *p;
- p = &_updatekey_store + keyno * KEY_CONTENT_LEN;
+ p = &_updatekey_store + keyno * FIRMWARE_UPDATE_KEY_CONTENT_LEN;
return p;
}
else
{
p = gpg_get_firmware_update_key (file_id - FILEID_UPDATE_KEY_0);
- res_APDU_size = KEY_CONTENT_LEN;
- memcpy (res_APDU, p, KEY_CONTENT_LEN);
+ res_APDU_size = FIRMWARE_UPDATE_KEY_CONTENT_LEN;
+ memcpy (res_APDU, p, FIRMWARE_UPDATE_KEY_CONTENT_LEN);
GPG_SUCCESS ();
}
}
return;
}
- file_selection = FILE_DF_OPENPGP;
- if ((P2 (apdu) & 0x0c) == 0x0c) /* No FCI */
- GPG_SUCCESS ();
- else
+ if (file_selection == FILE_CARD_TERMINATED)
{
- gpg_do_get_data (0x004f, 1); /* AID */
- memmove (res_APDU+2, res_APDU, res_APDU_size);
- res_APDU[0] = 0x6f;
- res_APDU[1] = 0x12;
- res_APDU[2] = 0x84; /* overwrite: DF name */
- res_APDU_size += 2;
- GPG_SUCCESS ();
+ file_selection = FILE_CARD_TERMINATED_OPENPGP;
+ GPG_APPLICATION_TERMINATED();
+ return;
}
+
+ file_selection = FILE_DF_OPENPGP;
+
+ /* Behave just like original OpenPGP card. */
+ GPG_SUCCESS ();
}
else if (apdu.cmd_apdu_data_len == 2
&& apdu.cmd_apdu_data[0] == 0x2f && apdu.cmd_apdu_data[1] == 0x02)
DEBUG_INFO (" - Get Data\r\n");
- if (file_selection != FILE_DF_OPENPGP)
- GPG_NO_RECORD ();
-
gpg_do_get_data (tag, 0);
}
+#define ECDSA_HASH_LEN 32
+#define ECDSA_SIGNATURE_LENGTH 64
+
+#define EDDSA_HASH_LEN_MAX 256
+#define EDDSA_SIGNATURE_LENGTH 64
+
+#define ECC_CIPHER_DO_HEADER_SIZE 7
+
static void
cmd_pso (void)
{
int len = apdu.cmd_apdu_data_len;
- int r;
+ int r = -1;
+ int attr;
+ int pubkey_len;
+ unsigned int result_len = 0;
+ int cs;
DEBUG_INFO (" - PSO: ");
DEBUG_WORD ((uint32_t)&r);
DEBUG_BINARY (apdu.cmd_apdu_data, apdu.cmd_apdu_data_len);
+ DEBUG_SHORT (len);
if (P1 (apdu) == 0x9e && P2 (apdu) == 0x9a)
{
+ attr = gpg_get_algo_attr (GPG_KEY_FOR_SIGNING);
+ pubkey_len = gpg_get_algo_attr_key_size (GPG_KEY_FOR_SIGNING,
+ GPG_KEY_PUBLIC);
+
if (!ac_check_status (AC_PSO_CDS_AUTHORIZED))
{
DEBUG_INFO ("security error.");
return;
}
- /* Check size of digestInfo */
- if (len != 34 /* MD5 */
- && len != 35 /* SHA1 / RIPEMD-160 */
- && len != 47 /* SHA224 */
- && len != 51 /* SHA256 */
- && len != 67 /* SHA384 */
- && len != 83) /* SHA512 */
+ if (attr == ALGO_RSA2K || attr == ALGO_RSA4K)
{
- DEBUG_INFO (" wrong length: ");
- DEBUG_SHORT (len);
- GPG_ERROR ();
+ /* Check size of digestInfo */
+ if (len != 34 /* MD5 */
+ && len != 35 /* SHA1 / RIPEMD-160 */
+ && len != 47 /* SHA224 */
+ && len != 51 /* SHA256 */
+ && len != 67 /* SHA384 */
+ && len != 83) /* SHA512 */
+ {
+ DEBUG_INFO (" wrong length");
+ GPG_CONDITION_NOT_SATISFIED ();
+ return;
+ }
+
+ DEBUG_BINARY (kd[GPG_KEY_FOR_SIGNING].data, pubkey_len);
+
+ result_len = pubkey_len;
+ r = rsa_sign (apdu.cmd_apdu_data, res_APDU, len,
+ &kd[GPG_KEY_FOR_SIGNING], pubkey_len);
}
- else
+ else if (attr == ALGO_NISTP256R1 || attr == ALGO_SECP256K1)
{
- DEBUG_SHORT (len);
- DEBUG_BINARY (&kd[GPG_KEY_FOR_SIGNING], KEY_CONTENT_LEN);
+ /* ECDSA with p256r1/p256k1 for signature */
+ if (len != ECDSA_HASH_LEN)
+ {
+ DEBUG_INFO (" wrong length");
+ GPG_CONDITION_NOT_SATISFIED ();
+ return;
+ }
- r = rsa_sign (apdu.cmd_apdu_data, res_APDU, len,
- &kd[GPG_KEY_FOR_SIGNING]);
- if (r < 0)
+ cs = chopstx_setcancelstate (0);
+ result_len = ECDSA_SIGNATURE_LENGTH;
+ if (attr == ALGO_NISTP256R1)
+ r = ecdsa_sign_p256r1 (apdu.cmd_apdu_data, res_APDU,
+ kd[GPG_KEY_FOR_SIGNING].data);
+ else /* ALGO_SECP256K1 */
+ r = ecdsa_sign_p256k1 (apdu.cmd_apdu_data, res_APDU,
+ kd[GPG_KEY_FOR_SIGNING].data);
+ chopstx_setcancelstate (cs);
+ }
+ else if (attr == ALGO_ED25519)
+ {
+ uint32_t output[64/4]; /* Require 4-byte alignment. */
+
+ if (len > EDDSA_HASH_LEN_MAX)
{
- ac_reset_pso_cds ();
- GPG_ERROR ();
+ DEBUG_INFO ("wrong hash length.");
+ GPG_CONDITION_NOT_SATISFIED ();
+ return;
}
- else
- /* Success */
- gpg_increment_digital_signature_counter ();
+
+ cs = chopstx_setcancelstate (0);
+ result_len = EDDSA_SIGNATURE_LENGTH;
+ r = eddsa_sign_25519 (apdu.cmd_apdu_data, len, output,
+ kd[GPG_KEY_FOR_SIGNING].data,
+ kd[GPG_KEY_FOR_SIGNING].data+32,
+ kd[GPG_KEY_FOR_SIGNING].pubkey);
+ chopstx_setcancelstate (cs);
+ memcpy (res_APDU, output, EDDSA_SIGNATURE_LENGTH);
}
+ else
+ {
+ DEBUG_INFO ("unknown algo.");
+ GPG_FUNCTION_NOT_SUPPORTED ();
+ return;
+ }
+
+ if (r == 0)
+ {
+ res_APDU_size = result_len;
+ gpg_increment_digital_signature_counter ();
+ }
+ else /* Failure */
+ ac_reset_pso_cds ();
}
else if (P1 (apdu) == 0x80 && P2 (apdu) == 0x86)
{
- DEBUG_SHORT (len);
- DEBUG_BINARY (&kd[GPG_KEY_FOR_DECRYPTION], KEY_CONTENT_LEN);
+ attr = gpg_get_algo_attr (GPG_KEY_FOR_DECRYPTION);
+ pubkey_len = gpg_get_algo_attr_key_size (GPG_KEY_FOR_DECRYPTION,
+ GPG_KEY_PUBLIC);
+
+ DEBUG_BINARY (kd[GPG_KEY_FOR_DECRYPTION].data, pubkey_len);
if (!ac_check_status (AC_OTHER_AUTHORIZED))
{
return;
}
- /* Skip padding 0x00 */
- len--;
- if (len != KEY_CONTENT_LEN)
- GPG_CONDITION_NOT_SATISFIED ();
- else
+ if (attr == ALGO_RSA2K || attr == ALGO_RSA4K)
{
+ /* Skip padding 0x00 */
+ len--;
+ if (len != pubkey_len)
+ {
+ GPG_CONDITION_NOT_SATISFIED ();
+ return;
+ }
r = rsa_decrypt (apdu.cmd_apdu_data+1, res_APDU, len,
- &kd[GPG_KEY_FOR_DECRYPTION]);
- if (r < 0)
- GPG_ERROR ();
+ &kd[GPG_KEY_FOR_DECRYPTION], &result_len);
+ }
+ else if (attr == ALGO_NISTP256R1 || attr == ALGO_SECP256K1)
+ {
+ int header = ECC_CIPHER_DO_HEADER_SIZE;
+
+ /* Format is in big endian MPI: 04 || x || y */
+ if (len != 65 + ECC_CIPHER_DO_HEADER_SIZE
+ || apdu.cmd_apdu_data[header] != 0x04)
+ {
+ GPG_CONDITION_NOT_SATISFIED ();
+ return;
+ }
+
+ cs = chopstx_setcancelstate (0);
+ result_len = 65;
+ if (attr == ALGO_NISTP256R1)
+ r = ecdh_decrypt_p256r1 (apdu.cmd_apdu_data + header, res_APDU,
+ kd[GPG_KEY_FOR_DECRYPTION].data);
+ else
+ r = ecdh_decrypt_p256k1 (apdu.cmd_apdu_data + header, res_APDU,
+ kd[GPG_KEY_FOR_DECRYPTION].data);
+ chopstx_setcancelstate (cs);
+ }
+ else if (attr == ALGO_CURVE25519)
+ {
+ int header = ECC_CIPHER_DO_HEADER_SIZE;
+
+ if (len != 32 + ECC_CIPHER_DO_HEADER_SIZE)
+ {
+ GPG_CONDITION_NOT_SATISFIED ();
+ return;
+ }
+
+ cs = chopstx_setcancelstate (0);
+ result_len = 32;
+ r = ecdh_decrypt_curve25519 (apdu.cmd_apdu_data + header, res_APDU,
+ kd[GPG_KEY_FOR_DECRYPTION].data);
+ chopstx_setcancelstate (cs);
+ }
+ else
+ {
+ DEBUG_INFO ("unknown algo.");
+ GPG_FUNCTION_NOT_SUPPORTED ();
+ return;
}
+
+ if (r == 0)
+ res_APDU_size = result_len;
}
- else
+
+ if (r < 0)
{
DEBUG_INFO (" - ??");
DEBUG_BYTE (P1 (apdu));
}
-#define MAX_DIGEST_INFO_LEN 102 /* 40% */
+#define MAX_RSA_DIGEST_INFO_LEN 102 /* 40% */
static void
cmd_internal_authenticate (void)
{
+ int attr = gpg_get_algo_attr (GPG_KEY_FOR_AUTHENTICATION);
+ int pubkey_len = gpg_get_algo_attr_key_size (GPG_KEY_FOR_AUTHENTICATION,
+ GPG_KEY_PUBLIC);
int len = apdu.cmd_apdu_data_len;
- int r;
+ int r = -1;
+ unsigned int result_len = 0;
+ int cs;
DEBUG_INFO (" - INTERNAL AUTHENTICATE\r\n");
- if (P1 (apdu) == 0x00 && P2 (apdu) == 0x00)
+ if (P1 (apdu) != 0x00 || P2 (apdu) != 0x00)
{
- DEBUG_SHORT (len);
+ DEBUG_INFO (" - ??");
+ DEBUG_BYTE (P1 (apdu));
+ DEBUG_INFO (" - ??");
+ DEBUG_BYTE (P2 (apdu));
+ GPG_CONDITION_NOT_SATISFIED ();
+ return;
+ }
- if (!ac_check_status (AC_OTHER_AUTHORIZED))
+ DEBUG_SHORT (len);
+ if (!ac_check_status (AC_OTHER_AUTHORIZED))
+ {
+ DEBUG_INFO ("security error.");
+ GPG_SECURITY_FAILURE ();
+ return;
+ }
+
+ if (attr == ALGO_RSA2K || attr == ALGO_RSA4K)
+ {
+ if (len > MAX_RSA_DIGEST_INFO_LEN)
{
- DEBUG_INFO ("security error.");
- GPG_SECURITY_FAILURE ();
+ DEBUG_INFO ("input is too long.");
+ GPG_CONDITION_NOT_SATISFIED ();
return;
}
- if (len > MAX_DIGEST_INFO_LEN)
+ result_len = pubkey_len;
+ r = rsa_sign (apdu.cmd_apdu_data, res_APDU, len,
+ &kd[GPG_KEY_FOR_AUTHENTICATION], pubkey_len);
+ }
+ else if (attr == ALGO_NISTP256R1)
+ {
+ if (len != ECDSA_HASH_LEN)
{
- DEBUG_INFO ("input is too long.");
+ DEBUG_INFO ("wrong hash length.");
GPG_CONDITION_NOT_SATISFIED ();
return;
}
- r = rsa_sign (apdu.cmd_apdu_data, res_APDU, len,
- &kd[GPG_KEY_FOR_AUTHENTICATION]);
- if (r < 0)
- GPG_ERROR ();
+ cs = chopstx_setcancelstate (0);
+ result_len = ECDSA_SIGNATURE_LENGTH;
+ r = ecdsa_sign_p256r1 (apdu.cmd_apdu_data, res_APDU,
+ kd[GPG_KEY_FOR_AUTHENTICATION].data);
+ chopstx_setcancelstate (cs);
}
- else
+ else if (attr == ALGO_SECP256K1)
{
- DEBUG_INFO (" - ??");
- DEBUG_BYTE (P1 (apdu));
- DEBUG_INFO (" - ??");
- DEBUG_BYTE (P2 (apdu));
- GPG_ERROR ();
+ if (len != ECDSA_HASH_LEN)
+ {
+ DEBUG_INFO ("wrong hash length.");
+ GPG_CONDITION_NOT_SATISFIED ();
+ return;
+ }
+
+ cs = chopstx_setcancelstate (0);
+ result_len = ECDSA_SIGNATURE_LENGTH;
+ r = ecdsa_sign_p256k1 (apdu.cmd_apdu_data, res_APDU,
+ kd[GPG_KEY_FOR_AUTHENTICATION].data);
+ chopstx_setcancelstate (cs);
+ }
+ else if (attr == ALGO_ED25519)
+ {
+ uint32_t output[64/4]; /* Require 4-byte alignment. */
+
+ if (len > EDDSA_HASH_LEN_MAX)
+ {
+ DEBUG_INFO ("wrong hash length.");
+ GPG_CONDITION_NOT_SATISFIED ();
+ return;
+ }
+
+ cs = chopstx_setcancelstate (0);
+ result_len = EDDSA_SIGNATURE_LENGTH;
+ r = eddsa_sign_25519 (apdu.cmd_apdu_data, len, output,
+ kd[GPG_KEY_FOR_AUTHENTICATION].data,
+ kd[GPG_KEY_FOR_AUTHENTICATION].data+32,
+ kd[GPG_KEY_FOR_AUTHENTICATION].pubkey);
+ chopstx_setcancelstate (cs);
+ memcpy (res_APDU, output, EDDSA_SIGNATURE_LENGTH);
}
+ if (r == 0)
+ res_APDU_size = result_len;
+ else
+ GPG_ERROR ();
+
DEBUG_INFO ("INTERNAL AUTHENTICATE done.\r\n");
}
+
#define MBD_OPRATION_WRITE 0
#define MBD_OPRATION_UPDATE 1
DEBUG_SHORT (len);
DEBUG_SHORT (offset);
+ if (file_id == FILEID_CH_CERTIFICATE && (len&1))
+ /* It's OK the size of last write is odd. */
+ apdu.cmd_apdu_data[len++] = 0xff;
+
r = flash_write_binary (file_id, apdu.cmd_apdu_data, len, offset);
if (r < 0)
{
return;
}
+ if (file_id >= FILEID_UPDATE_KEY_0 && file_id <= FILEID_UPDATE_KEY_3
+ && len == 0 && offset == 0)
+ {
+ int i;
+ const uint8_t *p;
+
+ for (i = 0; i < 4; i++)
+ {
+ p = gpg_get_firmware_update_key (i);
+ if (p[0] != 0x00 || p[1] != 0x00) /* still valid */
+ break;
+ }
+
+ if (i == 4) /* all update keys are removed */
+ {
+ p = gpg_get_firmware_update_key (0);
+ flash_erase_page ((uint32_t)p);
+ }
+ }
+
GPG_SUCCESS ();
}
cmd_write_binary (void)
{
int len = apdu.cmd_apdu_data_len;
- int i;
- const uint8_t *p;
DEBUG_INFO (" - WRITE BINARY\r\n");
modify_binary (MBD_OPRATION_WRITE, P1 (apdu), P2 (apdu), len);
-
- for (i = 0; i < 4; i++)
- {
- p = gpg_get_firmware_update_key (i);
- if (p[0] != 0x00 || p[1] != 0x00) /* still valid */
- break;
- }
-
- if (i == 4) /* all update keys are removed */
- {
- p = gpg_get_firmware_update_key (0);
- flash_erase_page ((uint32_t)p);
- }
-
DEBUG_INFO ("WRITE BINARY done.\r\n");
}
DEBUG_INFO (" - EXTERNAL AUTHENTICATE\r\n");
- if (keyno > 4)
+ if (keyno >= 4)
{
GPG_CONDITION_NOT_SATISFIED ();
return;
return;
}
- r = rsa_verify (pubkey, challenge, signature);
+ r = rsa_verify (pubkey, FIRMWARE_UPDATE_KEY_CONTENT_LEN,
+ challenge, signature);
random_bytes_free (challenge);
challenge = NULL;
return;
}
- chThdTerminate (chThdSelf ());
+ eventflag_signal (openpgp_comm, EV_EXIT); /* signal to self. */
set_res_sw (0xff, 0xff);
DEBUG_INFO ("EXTERNAL AUTHENTICATE done.\r\n");
}
static void
cmd_get_challenge (void)
{
+ int len = apdu.expected_res_size;
+
DEBUG_INFO (" - GET CHALLENGE\r\n");
+ if (len > CHALLENGE_LEN)
+ {
+ GPG_CONDITION_NOT_SATISFIED ();
+ return;
+ }
+ else if (len == 0)
+ /* Le is not specified. Return full-sized challenge by GET_RESPONSE. */
+ len = CHALLENGE_LEN;
+
if (challenge)
random_bytes_free (challenge);
challenge = random_bytes_get ();
- memcpy (res_APDU, challenge, CHALLENGE_LEN);
- res_APDU_size = CHALLENGE_LEN;
+ memcpy (res_APDU, challenge, len);
+ res_APDU_size = len;
GPG_SUCCESS ();
DEBUG_INFO ("GET CHALLENGE done.\r\n");
}
+#ifdef LIFE_CYCLE_MANAGEMENT_SUPPORT
+static void
+cmd_activate_file (void)
+{
+ if (file_selection != FILE_CARD_TERMINATED_OPENPGP)
+ {
+ GPG_NO_RECORD();
+ return;
+ }
+
+ flash_activate ();
+ file_selection = FILE_DF_OPENPGP;
+ GPG_SUCCESS ();
+}
+
+static void
+cmd_terminate_df (void)
+{
+ uint8_t p1 = P1 (apdu);
+ uint8_t p2 = P2 (apdu);
+
+ if (file_selection != FILE_DF_OPENPGP)
+ {
+ GPG_NO_RECORD();
+ return;
+ }
+
+ if (p1 != 0 || p2 != 0)
+ {
+ GPG_BAD_P1_P2();
+ return;
+ }
+
+ if (apdu.cmd_apdu_data_len != 0)
+ {
+ GPG_WRONG_LENGTH();
+ return;
+ }
+
+
+ if (!ac_check_status (AC_ADMIN_AUTHORIZED) && !gpg_pw_locked (PW_ERR_PW3))
+ {
+ /* Only allow the case admin authorized, or, admin pass is locked. */
+ GPG_SECURITY_FAILURE();
+ return;
+ }
+
+ ac_reset_admin ();
+ ac_reset_pso_cds ();
+ ac_reset_other ();
+ gpg_do_terminate ();
+ flash_terminate ();
+ file_selection = FILE_CARD_TERMINATED;
+ GPG_SUCCESS ();
+}
+#endif
+
+
struct command
{
uint8_t command;
{ INS_CHANGE_REFERENCE_DATA, cmd_change_password },
{ INS_PSO, cmd_pso },
{ INS_RESET_RETRY_COUNTER, cmd_reset_user_password },
+#ifdef LIFE_CYCLE_MANAGEMENT_SUPPORT
+ { INS_ACTIVATE_FILE, cmd_activate_file },
+#endif
{ INS_PGP_GENERATE_ASYMMETRIC_KEY_PAIR, cmd_pgp_gakp },
{ INS_EXTERNAL_AUTHENTICATE, /* Not in OpenPGP card protocol */
cmd_external_authenticate },
{ INS_GET_CHALLENGE, cmd_get_challenge }, /* Not in OpenPGP card protocol */
{ INS_INTERNAL_AUTHENTICATE, cmd_internal_authenticate },
{ INS_SELECT_FILE, cmd_select_file },
- { INS_READ_BINARY, cmd_read_binary },
+ { INS_READ_BINARY, cmd_read_binary }, /* Not in OpenPGP card protocol */
{ INS_GET_DATA, cmd_get_data },
{ INS_WRITE_BINARY, cmd_write_binary}, /* Not in OpenPGP card protocol */
#if defined(CERTDO_SUPPORT)
#endif
{ INS_PUT_DATA, cmd_put_data },
{ INS_PUT_DATA_ODD, cmd_put_data },
+#ifdef LIFE_CYCLE_MANAGEMENT_SUPPORT
+ { INS_TERMINATE_DF, cmd_terminate_df},
+#endif
};
#define NUM_CMDS ((int)(sizeof (cmds) / sizeof (struct command)))
break;
if (i < NUM_CMDS)
- cmds[i].cmd_handler ();
+ {
+ if (file_selection == FILE_CARD_TERMINATED
+ && cmd != INS_SELECT_FILE && cmd != INS_ACTIVATE_FILE
+ && cmd != INS_GET_CHALLENGE && cmd != INS_EXTERNAL_AUTHENTICATE)
+ GPG_APPLICATION_TERMINATED();
+ else if (file_selection != FILE_DF_OPENPGP
+ && cmd != INS_SELECT_FILE && cmd != INS_ACTIVATE_FILE
+ && cmd != INS_GET_CHALLENGE && cmd != INS_EXTERNAL_AUTHENTICATE
+ && cmd != INS_WRITE_BINARY && cmd != INS_UPDATE_BINARY
+ && cmd != INS_READ_BINARY)
+ GPG_NO_RECORD();
+ else
+ {
+ chopstx_setcancelstate (1);
+ cmds[i].cmd_handler ();
+ chopstx_setcancelstate (0);
+ }
+ }
else
{
DEBUG_INFO (" - ??");
}
}
-msg_t
-GPGthread (void *arg)
+void *
+openpgp_card_thread (void *arg)
{
- Thread *icc_thread = (Thread *)arg;
+ struct eventflag *ccid_comm = (struct eventflag *)arg;
- gpg_init ();
+ openpgp_comm = ccid_comm + 1;
- chEvtClear (ALL_EVENTS);
+ gpg_init ();
- while (!chThdShouldTerminate ())
+ while (1)
{
- eventmask_t m = chEvtWaitOne (ALL_EVENTS);
#if defined(PINPAD_SUPPORT)
int len, pw_len, newpw_len;
#endif
+ eventmask_t m = eventflag_wait (openpgp_comm);
DEBUG_INFO ("GPG!: ");
else if (m == EV_MODIFY_CMD_AVAILABLE)
{
#if defined(PINPAD_SUPPORT)
- uint8_t bConfirmPIN = apdu.cmd_apdu_data[5];
+ uint8_t bConfirmPIN = apdu.cmd_apdu_data[0];
uint8_t *p = apdu.cmd_apdu_data;
if (INS (apdu) != INS_CHANGE_REFERENCE_DATA
goto done;
#endif
}
- else if (m == EV_NOP)
- continue;
+ else if (m == EV_EXIT)
+ break;
led_blink (LED_START_COMMAND);
process_command_apdu ();
led_blink (LED_FINISH_COMMAND);
done:
- chEvtSignal (icc_thread, EV_EXEC_FINISHED);
+ eventflag_signal (ccid_comm, EV_EXEC_FINISHED);
}
gpg_fini ();
- return 0;
+ return NULL;
}