Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F36623232
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
92 KB
Subscribers
None
View Options
diff --git a/common/util.h b/common/util.h
index d5bb225a7..8895137ec 100644
--- a/common/util.h
+++ b/common/util.h
@@ -1,384 +1,387 @@
/* util.h - Utility functions for GnuPG
* Copyright (C) 2001, 2002, 2003, 2004, 2009 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute and/or modify this
* part of GnuPG under the terms of either
*
* - the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* or
*
* - the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* or both in parallel, as here.
*
* GnuPG 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 copies of the GNU General Public License
* and the GNU Lesser General Public License along with this program;
* if not, see <https://www.gnu.org/licenses/>.
*/
#ifndef GNUPG_COMMON_UTIL_H
#define GNUPG_COMMON_UTIL_H
#include <gcrypt.h> /* We need this for the memory function protos. */
#include <errno.h> /* We need errno. */
#include <gpg-error.h> /* We need gpg_error_t and estream. */
/* These error codes are used but not defined in the required
* libgpg-error version. Define them here.
* Example: (#if GPG_ERROR_VERSION_NUMBER < 0x011500 // 1.21)
*/
-
+#if GPG_ERROR_VERSION_NUMBER < 0x012400 /* 1.36 */
+#define GPG_ERR_NO_AUTH 314
+#define GPG_ERR_BAD_AUTH 315
+#endif /*GPG_ERROR_VERSION_NUMBER*/
/* Hash function used with libksba. */
#define HASH_FNC ((void (*)(void *, const void*,size_t))gcry_md_write)
/* The length of the keygrip. This is a SHA-1 hash of the key
* parameters as generated by gcry_pk_get_keygrip. */
#define KEYGRIP_LEN 20
/* Get all the stuff from jnlib. */
#include "../common/logging.h"
#include "../common/argparse.h"
#include "../common/stringhelp.h"
#include "../common/mischelp.h"
#include "../common/strlist.h"
#include "../common/dotlock.h"
#include "../common/utf8conv.h"
#include "../common/dynload.h"
#include "../common/fwddecl.h"
#include "../common/utilproto.h"
#include "gettime.h"
/* Redefine asprintf by our estream version which uses our own memory
allocator.. */
#define asprintf gpgrt_asprintf
#define vasprintf gpgrt_vasprintf
/* Due to a bug in mingw32's snprintf related to the 'l' modifier and
for increased portability we use our snprintf on all systems. */
#undef snprintf
#define snprintf gpgrt_snprintf
/* Replacements for macros not available with libgpg-error < 1.20. */
/* We need this type even if we are not using libreadline and or we
did not include libreadline in the current file. */
#ifndef GNUPG_LIBREADLINE_H_INCLUDED
typedef char **rl_completion_func_t (const char *, int, int);
#endif /*!GNUPG_LIBREADLINE_H_INCLUDED*/
/* Handy malloc macros - please use only them. */
#define xtrymalloc(a) gcry_malloc ((a))
#define xtrymalloc_secure(a) gcry_malloc_secure ((a))
#define xtrycalloc(a,b) gcry_calloc ((a),(b))
#define xtrycalloc_secure(a,b) gcry_calloc_secure ((a),(b))
#define xtryrealloc(a,b) gcry_realloc ((a),(b))
#define xtrystrdup(a) gcry_strdup ((a))
#define xfree(a) gcry_free ((a))
#define xfree_fnc gcry_free
#define xmalloc(a) gcry_xmalloc ((a))
#define xmalloc_secure(a) gcry_xmalloc_secure ((a))
#define xcalloc(a,b) gcry_xcalloc ((a),(b))
#define xcalloc_secure(a,b) gcry_xcalloc_secure ((a),(b))
#define xrealloc(a,b) gcry_xrealloc ((a),(b))
#define xstrdup(a) gcry_xstrdup ((a))
/* For compatibility with gpg 1.4 we also define these: */
#define xmalloc_clear(a) gcry_xcalloc (1, (a))
#define xmalloc_secure_clear(a) gcry_xcalloc_secure (1, (a))
/* The default error source of the application. This is different
from GPG_ERR_SOURCE_DEFAULT in that it does not depend on the
source file and thus is usable in code shared by applications.
Defined by init.c. */
extern gpg_err_source_t default_errsource;
/* Convenience function to return a gpg-error code for memory
allocation failures. This function makes sure that an error will
be returned even if accidentally ERRNO is not set. */
static inline gpg_error_t
out_of_core (void)
{
return gpg_error_from_syserror ();
}
/*-- yesno.c --*/
int answer_is_yes (const char *s);
int answer_is_yes_no_default (const char *s, int def_answer);
int answer_is_yes_no_quit (const char *s);
int answer_is_okay_cancel (const char *s, int def_answer);
/*-- xreadline.c --*/
ssize_t read_line (FILE *fp,
char **addr_of_buffer, size_t *length_of_buffer,
size_t *max_length);
/*-- b64enc.c and b64dec.c --*/
struct b64state
{
unsigned int flags;
int idx;
int quad_count;
FILE *fp;
estream_t stream;
char *title;
unsigned char radbuf[4];
u32 crc;
int stop_seen:1;
int invalid_encoding:1;
gpg_error_t lasterr;
};
gpg_error_t b64enc_start (struct b64state *state, FILE *fp, const char *title);
gpg_error_t b64enc_start_es (struct b64state *state, estream_t fp,
const char *title);
gpg_error_t b64enc_write (struct b64state *state,
const void *buffer, size_t nbytes);
gpg_error_t b64enc_finish (struct b64state *state);
gpg_error_t b64dec_start (struct b64state *state, const char *title);
gpg_error_t b64dec_proc (struct b64state *state, void *buffer, size_t length,
size_t *r_nbytes);
gpg_error_t b64dec_finish (struct b64state *state);
/*-- sexputil.c */
char *canon_sexp_to_string (const unsigned char *canon, size_t canonlen);
void log_printcanon (const char *text,
const unsigned char *sexp, size_t sexplen);
void log_printsexp (const char *text, gcry_sexp_t sexp);
gpg_error_t make_canon_sexp (gcry_sexp_t sexp,
unsigned char **r_buffer, size_t *r_buflen);
gpg_error_t make_canon_sexp_pad (gcry_sexp_t sexp, int secure,
unsigned char **r_buffer, size_t *r_buflen);
gpg_error_t keygrip_from_canon_sexp (const unsigned char *key, size_t keylen,
unsigned char *grip);
int cmp_simple_canon_sexp (const unsigned char *a, const unsigned char *b);
unsigned char *make_simple_sexp_from_hexstr (const char *line,
size_t *nscanned);
int hash_algo_from_sigval (const unsigned char *sigval);
unsigned char *make_canon_sexp_from_rsa_pk (const void *m, size_t mlen,
const void *e, size_t elen,
size_t *r_len);
gpg_error_t get_rsa_pk_from_canon_sexp (const unsigned char *keydata,
size_t keydatalen,
unsigned char const **r_n,
size_t *r_nlen,
unsigned char const **r_e,
size_t *r_elen);
int get_pk_algo_from_key (gcry_sexp_t key);
int get_pk_algo_from_canon_sexp (const unsigned char *keydata,
size_t keydatalen);
char *pubkey_algo_string (gcry_sexp_t s_pkey);
/*-- convert.c --*/
int hex2bin (const char *string, void *buffer, size_t length);
int hexcolon2bin (const char *string, void *buffer, size_t length);
char *bin2hex (const void *buffer, size_t length, char *stringbuf);
char *bin2hexcolon (const void *buffer, size_t length, char *stringbuf);
const char *hex2str (const char *hexstring,
char *buffer, size_t bufsize, size_t *buflen);
char *hex2str_alloc (const char *hexstring, size_t *r_count);
/*-- percent.c --*/
char *percent_plus_escape (const char *string);
char *percent_data_escape (int plus, const char *prefix,
const void *data, size_t datalen);
char *percent_plus_unescape (const char *string, int nulrepl);
char *percent_unescape (const char *string, int nulrepl);
size_t percent_plus_unescape_inplace (char *string, int nulrepl);
size_t percent_unescape_inplace (char *string, int nulrepl);
/*-- openpgp-oid.c --*/
gpg_error_t openpgp_oid_from_str (const char *string, gcry_mpi_t *r_mpi);
char *openpgp_oidbuf_to_str (const unsigned char *buf, size_t len);
char *openpgp_oid_to_str (gcry_mpi_t a);
int openpgp_oidbuf_is_ed25519 (const void *buf, size_t len);
int openpgp_oid_is_ed25519 (gcry_mpi_t a);
int openpgp_oidbuf_is_cv25519 (const void *buf, size_t len);
int openpgp_oid_is_cv25519 (gcry_mpi_t a);
const char *openpgp_curve_to_oid (const char *name, unsigned int *r_nbits);
const char *openpgp_oid_to_curve (const char *oid, int canon);
const char *openpgp_enum_curves (int *idxp);
const char *openpgp_is_curve_supported (const char *name,
int *r_algo, unsigned int *r_nbits);
/*-- homedir.c --*/
const char *standard_homedir (void);
const char *default_homedir (void);
void gnupg_set_homedir (const char *newdir);
const char *gnupg_homedir (void);
int gnupg_default_homedir_p (void);
const char *gnupg_daemon_rootdir (void);
const char *gnupg_socketdir (void);
const char *gnupg_sysconfdir (void);
const char *gnupg_bindir (void);
const char *gnupg_libexecdir (void);
const char *gnupg_libdir (void);
const char *gnupg_datadir (void);
const char *gnupg_localedir (void);
const char *gnupg_cachedir (void);
const char *dirmngr_socket_name (void);
char *_gnupg_socketdir_internal (int skip_checks, unsigned *r_info);
/* All module names. We also include gpg and gpgsm for the sake for
gpgconf. */
#define GNUPG_MODULE_NAME_AGENT 1
#define GNUPG_MODULE_NAME_PINENTRY 2
#define GNUPG_MODULE_NAME_SCDAEMON 3
#define GNUPG_MODULE_NAME_DIRMNGR 4
#define GNUPG_MODULE_NAME_PROTECT_TOOL 5
#define GNUPG_MODULE_NAME_CHECK_PATTERN 6
#define GNUPG_MODULE_NAME_GPGSM 7
#define GNUPG_MODULE_NAME_GPG 8
#define GNUPG_MODULE_NAME_CONNECT_AGENT 9
#define GNUPG_MODULE_NAME_GPGCONF 10
#define GNUPG_MODULE_NAME_DIRMNGR_LDAP 11
#define GNUPG_MODULE_NAME_GPGV 12
const char *gnupg_module_name (int which);
void gnupg_module_name_flush_some (void);
void gnupg_set_builddir (const char *newdir);
/* A list of constants to identify protocols. This is used by tools
* which need to distinguish between the different protocols
* implemented by GnuPG. May be used as bit flags. */
#define GNUPG_PROTOCOL_OPENPGP 1 /* The one and only (gpg). */
#define GNUPG_PROTOCOL_CMS 2 /* The core of S/MIME (gpgsm) */
#define GNUPG_PROTOCOL_SSH_AGENT 4 /* Out ssh-agent implementation */
/*-- gpgrlhelp.c --*/
void gnupg_rl_initialize (void);
/*-- helpfile.c --*/
char *gnupg_get_help_string (const char *key, int only_current_locale);
/*-- localename.c --*/
const char *gnupg_messages_locale_name (void);
/*-- miscellaneous.c --*/
/* This function is called at startup to tell libgcrypt to use our own
logging subsystem. */
void setup_libgcrypt_logging (void);
/* Print an out of core message and die. */
void xoutofcore (void);
/* Same as estream_asprintf but die on memory failure. */
char *xasprintf (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2);
/* This is now an alias to estream_asprintf. */
char *xtryasprintf (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2);
/* Replacement for gcry_cipher_algo_name. */
const char *gnupg_cipher_algo_name (int algo);
void obsolete_option (const char *configname, unsigned int configlineno,
const char *name);
const char *print_fname_stdout (const char *s);
const char *print_fname_stdin (const char *s);
void print_utf8_buffer3 (estream_t fp, const void *p, size_t n,
const char *delim);
void print_utf8_buffer2 (estream_t fp, const void *p, size_t n, int delim);
void print_utf8_buffer (estream_t fp, const void *p, size_t n);
void print_utf8_string (estream_t stream, const char *p);
void print_hexstring (FILE *fp, const void *buffer, size_t length,
int reserved);
char *try_make_printable_string (const void *p, size_t n, int delim);
char *make_printable_string (const void *p, size_t n, int delim);
char *decode_c_string (const char *src);
int is_file_compressed (const char *s, int *ret_rc);
int match_multistr (const char *multistr,const char *match);
int gnupg_compare_version (const char *a, const char *b);
struct debug_flags_s
{
unsigned int flag;
const char *name;
};
int parse_debug_flag (const char *string, unsigned int *debugvar,
const struct debug_flags_s *flags);
/*-- Simple replacement functions. */
/* We use the gnupg_ttyname macro to be safe not to run into conflicts
which an extisting but broken ttyname. */
#if !defined(HAVE_TTYNAME) || defined(HAVE_BROKEN_TTYNAME)
# define gnupg_ttyname(n) _gnupg_ttyname ((n))
/* Systems without ttyname (W32) will merely return NULL. */
static inline char *
_gnupg_ttyname (int fd)
{
(void)fd;
return NULL;
}
#else /*HAVE_TTYNAME*/
# define gnupg_ttyname(n) ttyname ((n))
#endif /*HAVE_TTYNAME */
#ifdef HAVE_W32CE_SYSTEM
#define getpid() GetCurrentProcessId ()
char *_gnupg_getenv (const char *name); /* See sysutils.c */
#define getenv(a) _gnupg_getenv ((a))
char *_gnupg_setenv (const char *name); /* See sysutils.c */
#define setenv(a,b,c) _gnupg_setenv ((a),(b),(c))
int _gnupg_isatty (int fd);
#define gnupg_isatty(a) _gnupg_isatty ((a))
#else
#define gnupg_isatty(a) isatty ((a))
#endif
/*-- Macros to replace ctype ones to avoid locale problems. --*/
#define spacep(p) (*(p) == ' ' || *(p) == '\t')
#define digitp(p) (*(p) >= '0' && *(p) <= '9')
#define alphap(p) ((*(p) >= 'A' && *(p) <= 'Z') \
|| (*(p) >= 'a' && *(p) <= 'z'))
#define alnump(p) (alphap (p) || digitp (p))
#define hexdigitp(a) (digitp (a) \
|| (*(a) >= 'A' && *(a) <= 'F') \
|| (*(a) >= 'a' && *(a) <= 'f'))
/* Note this isn't identical to a C locale isspace() without \f and
\v, but works for the purposes used here. */
#define ascii_isspace(a) ((a)==' ' || (a)=='\n' || (a)=='\r' || (a)=='\t')
/* The atoi macros assume that the buffer has only valid digits. */
#define atoi_1(p) (*(p) - '0' )
#define atoi_2(p) ((atoi_1(p) * 10) + atoi_1((p)+1))
#define atoi_4(p) ((atoi_2(p) * 100) + atoi_2((p)+2))
#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \
*(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1))
#define xtoi_4(p) ((xtoi_2(p) * 256) + xtoi_2((p)+2))
#endif /*GNUPG_COMMON_UTIL_H*/
diff --git a/scd/app-piv.c b/scd/app-piv.c
index 1d70db51c..36086f546 100644
--- a/scd/app-piv.c
+++ b/scd/app-piv.c
@@ -1,2679 +1,2692 @@
/* app-piv.c - The OpenPGP card application.
* Copyright (C) 2019 g10 Code GmbH
*
* This file is part of GnuPG.
*
* GnuPG 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.
*
* GnuPG 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 <https://www.gnu.org/licenses/>.
*/
/* Some notes:
* - Specs for PIV are at http://dx.doi.org/10.6028/NIST.SP.800-73-4
*
* - Access control matrix:
* | Action | 9B | PIN | PUK | |
* |--------------+-----+-----+-----+------------------------------|
* | Generate key | yes | | | |
* | Change 9B | yes | | | |
* | Change retry | yes | yes | | Yubikey only |
* | Import key | yes | | | |
* | Import cert | yes | | | |
* | Change CHUID | yes | | | |
* | Reset card | | | | PIN and PUK in blocked state |
* | Verify PIN | | yes | | |
* | Sign data | | yes | | |
* | Decrypt data | | yes | | |
* | Change PIN | | yes | | |
* | Change PUK | | | yes | |
* | Unblock PIN | | | yes | New PIN required |
* |---------------------------------------------------------------|
* (9B indicates the 24 byte PIV Card Application Administration Key)
*
* - When generating a key we store the created public key in the
* corresponding data object, so that gpg and gpgsm are able to get
* the public key, create a certificate and store that then in that
* data object. That is not standard compliant but due to the use
* of other tags, it should not harm. See do_genkey for the actual
* used tag structure.
*/
#include <config.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include "scdaemon.h"
#include "../common/util.h"
#include "../common/i18n.h"
#include "iso7816.h"
#include "app-common.h"
#include "../common/tlv.h"
#include "../common/host2net.h"
#include "apdu.h" /* We use apdu_send_direct. */
#define PIV_ALGORITHM_3DES_ECB_0 0x00
#define PIV_ALGORITHM_2DES_ECB 0x01
#define PIV_ALGORITHM_2DES_CBC 0x02
#define PIV_ALGORITHM_3DES_ECB 0x03
#define PIV_ALGORITHM_3DES_CBC 0x04
#define PIV_ALGORITHM_RSA 0x07
#define PIV_ALGORITHM_AES128_ECB 0x08
#define PIV_ALGORITHM_AES128_CBC 0x09
#define PIV_ALGORITHM_AES192_ECB 0x0A
#define PIV_ALGORITHM_AES192_CBC 0x0B
#define PIV_ALGORITHM_AES256_ECB 0x0C
#define PIV_ALGORITHM_AES256_CBC 0x0D
#define PIV_ALGORITHM_ECC_P256 0x11
#define PIV_ALGORITHM_ECC_P384 0x14
/* A table describing the DOs of a PIV card. */
struct data_object_s
{
unsigned int tag;
unsigned int mandatory:1;
unsigned int acr_contact:2; /* 0=always, 1=VCI, 2=PIN, 3=PINorOCC */
unsigned int acr_contactless:2; /* 0=always, 1=VCI, 2=VCIandPIN,
3=VCIand(PINorOCC) */
unsigned int binary:1; /* Data is not human readable. */
unsigned int dont_cache:1; /* Data item will not be cached. */
unsigned int flush_on_error:1; /* Flush cached item on error. */
unsigned int keypair:1; /* Has a public key for a keypair. */
char keyref[3]; /* The key reference. */
char *oidsuffix; /* Suffix of the OID, prefix is "2.16.840.1.101.3.7." */
char *desc; /* Description of the DO. */
};
typedef struct data_object_s *data_object_t;
static struct data_object_s data_objects[] = {
{ 0x5FC107, 1, 0,1, 1, 0,0, 0, "", "1.219.0", "Card Capability Container"},
{ 0x5FC102, 1, 0,0, 1, 0,0, 0, "", "2.48.0", "Cardholder Unique Id" },
{ 0x5FC105, 1, 0,1, 1, 0,0, 1, "9A", "2.1.1", "Cert PIV Authentication" },
{ 0x5FC103, 1, 2,2, 1, 0,0, 0, "", "2.96.16", "Cardholder Fingerprints" },
{ 0x5FC106, 1, 0,1, 1, 0,0, 0, "", "2.144.0", "Security Object" },
{ 0x5FC108, 1, 2,2, 1, 0,0, 0, "", "2.96.48", "Cardholder Facial Image" },
{ 0x5FC101, 1, 0,0, 1, 0,0, 1, "9E", "2.5.0", "Cert Card Authentication"},
{ 0x5FC10A, 0, 0,1, 1, 0,0, 1, "9C", "2.1.0", "Cert Digital Signature" },
{ 0x5FC10B, 0, 0,1, 1, 0,0, 1, "9D", "2.1.2", "Cert Key Management" },
{ 0x5FC109, 0, 3,3, 0, 0,0, 0, "", "2.48.1", "Printed Information" },
{ 0x7E, 0, 0,0, 1, 0,0, 0, "", "2.96.80", "Discovery Object" },
{ 0x5FC10C, 0, 0,1, 1, 0,0, 0, "", "2.96.96", "Key History Object" },
{ 0x5FC10D, 0, 0,1, 1, 0,0, 0, "82", "2.16.1", "Retired Cert Key Mgm 1" },
{ 0x5FC10E, 0, 0,1, 1, 0,0, 0, "83", "2.16.2", "Retired Cert Key Mgm 2" },
{ 0x5FC10F, 0, 0,1, 1, 0,0, 0, "84", "2.16.3", "Retired Cert Key Mgm 3" },
{ 0x5FC110, 0, 0,1, 1, 0,0, 0, "85", "2.16.4", "Retired Cert Key Mgm 4" },
{ 0x5FC111, 0, 0,1, 1, 0,0, 0, "86", "2.16.5", "Retired Cert Key Mgm 5" },
{ 0x5FC112, 0, 0,1, 1, 0,0, 0, "87", "2.16.6", "Retired Cert Key Mgm 6" },
{ 0x5FC113, 0, 0,1, 1, 0,0, 0, "88", "2.16.7", "Retired Cert Key Mgm 7" },
{ 0x5FC114, 0, 0,1, 1, 0,0, 0, "89", "2.16.8", "Retired Cert Key Mgm 8" },
{ 0x5FC115, 0, 0,1, 1, 0,0, 0, "8A", "2.16.9", "Retired Cert Key Mgm 9" },
{ 0x5FC116, 0, 0,1, 1, 0,0, 0, "8B", "2.16.10", "Retired Cert Key Mgm 10" },
{ 0x5FC117, 0, 0,1, 1, 0,0, 0, "8C", "2.16.11", "Retired Cert Key Mgm 11" },
{ 0x5FC118, 0, 0,1, 1, 0,0, 0, "8D", "2.16.12", "Retired Cert Key Mgm 12" },
{ 0x5FC119, 0, 0,1, 1, 0,0, 0, "8E", "2.16.13", "Retired Cert Key Mgm 13" },
{ 0x5FC11A, 0, 0,1, 1, 0,0, 0, "8F", "2.16.14", "Retired Cert Key Mgm 14" },
{ 0x5FC11B, 0, 0,1, 1, 0,0, 0, "90", "2.16.15", "Retired Cert Key Mgm 15" },
{ 0x5FC11C, 0, 0,1, 1, 0,0, 0, "91", "2.16.16", "Retired Cert Key Mgm 16" },
{ 0x5FC11D, 0, 0,1, 1, 0,0, 0, "92", "2.16.17", "Retired Cert Key Mgm 17" },
{ 0x5FC11E, 0, 0,1, 1, 0,0, 0, "93", "2.16.18", "Retired Cert Key Mgm 18" },
{ 0x5FC11F, 0, 0,1, 1, 0,0, 0, "94", "2.16.19", "Retired Cert Key Mgm 19" },
{ 0x5FC120, 0, 0,1, 1, 0,0, 0, "95", "2.16.20", "Retired Cert Key Mgm 20" },
{ 0x5FC121, 0, 2,2, 1, 0,0, 0, "", "2.16.21", "Cardholder Iris Images" },
{ 0x7F61, 0, 0,0, 1, 0,0, 0, "", "2.16.22", "BIT Group Template" },
{ 0x5FC122, 0, 0,0, 1, 0,0, 0, "", "2.16.23", "SM Cert Signer" },
{ 0x5FC123, 0, 3,3, 1, 0,0, 0, "", "2.16.24", "Pairing Code Ref Data" },
{ 0 }
/* Other key reference values without a data object:
* "00" Global PIN (not cleared by application switching)
* "04" PIV Secure Messaging Key
* "80" PIV Application PIN
* "81" PIN Unblocking Key
* "96" Primary Finger OCC
* "97" Secondary Finger OCC
* "98" Pairing Code
* "9B" PIV Card Application Administration Key
*/
};
/* One cache item for DOs. */
struct cache_s {
struct cache_s *next;
int tag;
size_t length;
unsigned char data[1];
};
/* Object with application specific data. */
struct app_local_s {
/* A linked list with cached DOs. */
struct cache_s *cache;
/* Various flags. */
struct
{
unsigned int yubikey:1; /* This is on a Yubikey. */
} flags;
};
/***** Local prototypes *****/
static gpg_error_t get_keygrip_by_tag (app_t app, unsigned int tag,
char **r_keygripstr, int *got_cert);
static gpg_error_t genkey_parse_rsa (const unsigned char *data, size_t datalen,
gcry_sexp_t *r_sexp);
static gpg_error_t genkey_parse_ecc (const unsigned char *data, size_t datalen,
int mechanism, gcry_sexp_t *r_sexp);
/* Deconstructor. */
static void
do_deinit (app_t app)
{
if (app && app->app_local)
{
struct cache_s *c, *c2;
for (c = app->app_local->cache; c; c = c2)
{
c2 = c->next;
xfree (c);
}
xfree (app->app_local);
app->app_local = NULL;
}
}
/* Wrapper around iso7816_get_data which first tries to get the data
* from the cache. With GET_IMMEDIATE passed as true, the cache is
* bypassed. The tag-53 container is also removed. */
static gpg_error_t
get_cached_data (app_t app, int tag,
unsigned char **result, size_t *resultlen,
int get_immediate)
{
gpg_error_t err;
int i;
unsigned char *p;
const unsigned char *s;
size_t len, n;
struct cache_s *c;
*result = NULL;
*resultlen = 0;
if (!get_immediate)
{
for (c=app->app_local->cache; c; c = c->next)
if (c->tag == tag)
{
if(c->length)
{
p = xtrymalloc (c->length);
if (!p)
return gpg_error_from_syserror ();
memcpy (p, c->data, c->length);
*result = p;
}
*resultlen = c->length;
return 0;
}
}
err = iso7816_get_data_odd (app->slot, 0, tag, &p, &len);
if (err)
return err;
/* Unless the Discovery Object or the BIT Group Template is
* requested, remove the outer container.
* (SP800-73.4 Part 2, section 3.1.2) */
if (tag == 0x7E || tag == 0x7F61)
;
else if (len && *p == 0x53 && (s = find_tlv (p, len, 0x53, &n)))
{
memmove (p, s, n);
len = n;
}
if (len)
*result = p;
*resultlen = len;
/* Check whether we should cache this object. */
if (get_immediate)
return 0;
for (i=0; data_objects[i].tag; i++)
if (data_objects[i].tag == tag)
{
if (data_objects[i].dont_cache)
return 0;
break;
}
/* Okay, cache it. */
for (c=app->app_local->cache; c; c = c->next)
log_assert (c->tag != tag);
c = xtrymalloc (sizeof *c + len);
if (c)
{
if (len)
memcpy (c->data, p, len);
else
xfree (p);
c->length = len;
c->tag = tag;
c->next = app->app_local->cache;
app->app_local->cache = c;
}
return 0;
}
/* Remove data object described by TAG from the cache. If TAG is 0
* all cache iterms are flushed. */
static void
flush_cached_data (app_t app, int tag)
{
struct cache_s *c, *cprev;
for (c=app->app_local->cache, cprev=NULL; c; cprev=c, c = c->next)
if (c->tag == tag || !tag)
{
if (cprev)
cprev->next = c->next;
else
app->app_local->cache = c->next;
xfree (c);
for (c=app->app_local->cache; c ; c = c->next)
{
log_assert (c->tag != tag); /* Oops: duplicated entry. */
}
return;
}
}
/* Get the DO identified by TAG from the card in SLOT and return a
* buffer with its content in RESULT and NBYTES. The return value is
* NULL if not found or a pointer which must be used to release the
* buffer holding value. */
static void *
get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes,
int *r_err)
{
gpg_error_t err;
int i;
unsigned char *buffer;
size_t buflen;
unsigned char *value;
size_t valuelen;
gpg_error_t dummyerr;
if (!r_err)
r_err = &dummyerr;
*result = NULL;
*nbytes = 0;
*r_err = 0;
for (i=0; data_objects[i].tag && data_objects[i].tag != tag; i++)
;
value = NULL;
err = gpg_error (GPG_ERR_ENOENT);
if (!value) /* Not in a constructed DO, try simple. */
{
err = get_cached_data (app, tag, &buffer, &buflen,
data_objects[i].dont_cache);
if (!err)
{
value = buffer;
valuelen = buflen;
}
}
if (!err)
{
*nbytes = valuelen;
*result = value;
return buffer;
}
*r_err = err;
return NULL;
}
static void
dump_all_do (int slot)
{
gpg_error_t err;
int i;
unsigned char *buffer;
size_t buflen;
for (i=0; data_objects[i].tag; i++)
{
/* We don't try extended length APDU because such large DO would
be pretty useless in a log file. */
err = iso7816_get_data_odd (slot, 0, data_objects[i].tag,
&buffer, &buflen);
if (err)
{
if (gpg_err_code (err) == GPG_ERR_ENOENT
&& !data_objects[i].mandatory)
;
else
log_info ("DO '%s' not available: %s\n",
data_objects[i].desc, gpg_strerror (err));
}
else
{
if (data_objects[i].binary)
{
log_info ("DO '%s': ", data_objects[i].desc);
if (buflen > 16 && opt.verbose < 2)
{
log_printhex (buffer, 16, NULL);
log_printf ("[...]\n");
}
else
log_printhex (buffer, buflen, "");
}
else
log_info ("DO '%s': '%.*s'\n",
data_objects[i].desc,
(int)buflen, buffer);
}
xfree (buffer); buffer = NULL;
}
}
/* Create a TLV tag and value and store it at BUFFER. Return the
* length of tag and length. A LENGTH greater than 65535 is
* truncated. TAG must be less or equal to 2^16. If BUFFER is NULL,
* only the required length is computed. */
static size_t
add_tlv (unsigned char *buffer, unsigned int tag, size_t length)
{
if (length > 0xffff)
length = 0xffff;
if (buffer)
{
unsigned char *p = buffer;
if (tag > 0xff)
*p++ = tag >> 8;
*p++ = tag;
if (length < 128)
*p++ = length;
else if (length < 256)
{
*p++ = 0x81;
*p++ = length;
}
else
{
*p++ = 0x82;
*p++ = length >> 8;
*p++ = length;
}
return p - buffer;
}
else
{
size_t n = 0;
if (tag > 0xff)
n++;
n++;
if (length < 128)
n++;
else if (length < 256)
n += 2;
else
n += 3;
return n;
}
}
/* Function to build a list of TLV and return the result in a mallcoed
* buffer. The varargs are tuples of (int,size_t,void) each with the
* tag, the length and the actual data. A (0,0,NULL) tuple terminates
* the list. Up to 10 tuples are supported. */
static gpg_error_t
concat_tlv_list (unsigned char **r_result, size_t *r_resultlen, ...)
{
gpg_error_t err;
va_list arg_ptr;
struct {
int tag;
unsigned int len;
unsigned int contlen;
const void *data;
} argv[10];
int i, j, argc;
unsigned char *data = NULL;
size_t datalen;
unsigned char *p;
size_t n;
*r_result = NULL;
*r_resultlen = 0;
/* Collect all args. Check that length is <= 2^16 to match the
* behaviour of add_tlv. */
va_start (arg_ptr, r_resultlen);
argc = 0;
while (((argv[argc].tag = va_arg (arg_ptr, int))))
{
argv[argc].len = va_arg (arg_ptr, size_t);
argv[argc].contlen = 0;
argv[argc].data = va_arg (arg_ptr, const void *);
if (argc >= DIM (argv)-1 || argv[argc].len > 0xffff)
{
va_end (arg_ptr);
err = gpg_error (GPG_ERR_EINVAL);
goto leave;
}
argc++;
}
va_end (arg_ptr);
/* Compute the required buffer length and allocate the buffer. */
datalen = 0;
for (i=0; i < argc; i++)
{
if (!argv[i].len && !argv[i].data)
{
/* Constructed tag. Compute its length. Note that we
* currently allow only one constructed tag in the list. */
for (n=0, j = i + 1; j < argc; j++)
{
log_assert (!(!argv[j].len && !argv[j].data));
n += add_tlv (NULL, argv[j].tag, argv[j].len);
n += argv[j].len;
}
argv[i].contlen = n;
datalen += add_tlv (NULL, argv[i].tag, n);
}
else
{
datalen += add_tlv (NULL, argv[i].tag, argv[i].len);
datalen += argv[i].len;
}
}
data = xtrymalloc (datalen);
if (!data)
{
err = gpg_error_from_syserror ();
goto leave;
}
/* Copy that data to the buffer. */
p = data;
for (i=0; i < argc; i++)
{
if (!argv[i].len && !argv[i].data)
{
/* Constructed tag. */
p += add_tlv (p, argv[i].tag, argv[i].contlen);
}
else
{
p += add_tlv (p, argv[i].tag, argv[i].len);
memcpy (p, argv[i].data, argv[i].len);
p += argv[i].len;
}
}
log_assert ( data + datalen == p );
*r_result = data;
data = NULL;
*r_resultlen = datalen;
err = 0;
leave:
xfree (data);
return err;
}
/* Wrapper around iso7816_put_data_odd which also sets the tag into
* the '5C' data object. The varargs are tuples of (int,size_t,void)
* with the tag, the length and the actual data. A (0,0,NULL) tuple
* terminates the list. Up to 10 tuples are supported. */
static gpg_error_t
put_data (int slot, unsigned int tag, ...)
{
gpg_error_t err;
va_list arg_ptr;
struct {
int tag;
size_t len;
const void *data;
} argv[10];
int i, argc;
unsigned char data5c[5];
size_t data5clen;
unsigned char *data = NULL;
size_t datalen;
unsigned char *p;
size_t n;
/* Collect all args. Check that length is <= 2^16 to match the
* behaviour of add_tlv. */
va_start (arg_ptr, tag);
argc = 0;
while (((argv[argc].tag = va_arg (arg_ptr, int))))
{
argv[argc].len = va_arg (arg_ptr, size_t);
argv[argc].data = va_arg (arg_ptr, const void *);
if (argc >= DIM (argv)-1 || argv[argc].len > 0xffff)
{
va_end (arg_ptr);
return GPG_ERR_EINVAL;
}
argc++;
}
va_end (arg_ptr);
/* Build the TLV with the tag to be updated. */
data5c[0] = 0x5c; /* Tag list */
if (tag <= 0xff)
{
data5c[1] = 1;
data5c[2] = tag;
data5clen = 3;
}
else if (tag <= 0xffff)
{
data5c[1] = 2;
data5c[2] = (tag >> 8);
data5c[3] = tag;
data5clen = 4;
}
else
{
data5c[1] = 3;
data5c[2] = (tag >> 16);
data5c[3] = (tag >> 8);
data5c[4] = tag;
data5clen = 5;
}
/* Compute the required buffer length and allocate the buffer. */
n = 0;
for (i=0; i < argc; i++)
{
n += add_tlv (NULL, argv[i].tag, argv[i].len);
n += argv[i].len;
}
datalen = data5clen + add_tlv (NULL, 0x53, n) + n;
data = xtrymalloc (datalen);
if (!data)
{
err = gpg_error_from_syserror ();
goto leave;
}
/* Copy that data to the buffer. */
p = data;
memcpy (p, data5c, data5clen);
p += data5clen;
p += add_tlv (p, 0x53, n);
for (i=0; i < argc; i++)
{
p += add_tlv (p, argv[i].tag, argv[i].len);
memcpy (p, argv[i].data, argv[i].len);
p += argv[i].len;
}
log_assert ( data + datalen == p );
err = iso7816_put_data_odd (slot, -1 /* use command chaining */,
0x3fff, data, datalen);
leave:
xfree (data);
return err;
}
/* Parse the key reference KEYREFSTR which is expected to hold a key
* reference for a CHV object. Return the one octet keyref or -1 for
* an invalid reference. */
static int
parse_chv_keyref (const char *keyrefstr)
{
if (!keyrefstr)
return -1;
else if (!ascii_strcasecmp (keyrefstr, "PIV.00"))
return 0x00;
else if (!ascii_strcasecmp (keyrefstr, "PIV.80"))
return 0x80;
else if (!ascii_strcasecmp (keyrefstr, "PIV.81"))
return 0x81;
else
return -1;
}
/* Return an allocated string with the serial number in a format to be
* show to the user. With FAILMODE is true return NULL if such an
* abbreviated S/N is not available, else return the full serial
* number as a hex string. May return NULL on malloc problem. */
static char *
get_dispserialno (app_t app, int failmode)
{
char *result;
if (app->serialno && app->serialnolen == 3+1+4
&& !memcmp (app->serialno, "\xff\x02\x00", 3))
{
/* This is a 4 byte S/N of a Yubikey which seems to be printed
* on the token in decimal. Maybe they will print larger S/N
* also in decimal but we can't be sure, thus do it only for
* these 32 bit numbers. */
unsigned long sn;
sn = app->serialno[4] * 16777216;
sn += app->serialno[5] * 65536;
sn += app->serialno[6] * 256;
sn += app->serialno[7];
result = xtryasprintf ("yk-%lu", sn);
}
else if (failmode)
result = NULL; /* No Abbreviated S/N. */
else
result = app_get_serialno (app);
return result;
}
/* The verify command can be used to retrieve the security status of
* the card. Given the PIN name (e.g. "PIV.80" for thge application
* pin, a status is returned:
*
* -1 = Error retrieving the data,
* -2 = No such PIN,
* -3 = PIN blocked,
* -5 = Verify still valid,
* n >= 0 = Number of verification attempts left.
*/
static int
get_chv_status (app_t app, const char *keyrefstr)
{
unsigned char apdu[4];
unsigned int sw;
int result;
int keyref;
keyref = parse_chv_keyref (keyrefstr);
if (!keyrefstr)
return -1;
apdu[0] = 0x00;
apdu[1] = ISO7816_VERIFY;
apdu[2] = 0x00;
apdu[3] = keyref;
if (!iso7816_apdu_direct (app->slot, apdu, 4, 0, &sw, NULL, NULL))
result = -5; /* No need to verification. */
else if (sw == 0x6a88 || sw == 0x6a80)
result = -2; /* No such PIN. */
else if (sw == 0x6983)
result = -3; /* PIN is blocked. */
else if ((sw & 0xfff0) == 0x63C0)
result = (sw & 0x000f);
else
result = -1; /* Error. */
return result;
}
/* Implementation of the GETATTR command. This is similar to the
* LEARN command but returns only one value via status lines. */
static gpg_error_t
do_getattr (app_t app, ctrl_t ctrl, const char *name)
{
static struct {
const char *name;
int tag;
int special;
} table[] = {
{ "SERIALNO", 0x0000, -1 },
{ "$AUTHKEYID", 0x0000, -2 }, /* Default key for ssh. */
{ "$DISPSERIALNO",0x0000, -3 },
{ "CHV-STATUS", 0x0000, -4 },
{ "CHV-USAGE", 0x007E, -5 }
};
gpg_error_t err = 0;
int idx;
void *relptr;
unsigned char *value;
size_t valuelen;
const unsigned char *s;
size_t n;
for (idx=0; (idx < DIM (table)
&& ascii_strcasecmp (table[idx].name, name)); idx++)
;
if (!(idx < DIM (table)))
err = gpg_error (GPG_ERR_INV_NAME);
else if (table[idx].special == -1)
{
char *serial = app_get_serialno (app);
if (serial)
{
send_status_direct (ctrl, "SERIALNO", serial);
xfree (serial);
}
}
else if (table[idx].special == -2)
{
char const tmp[] = "PIV.9A"; /* Cert PIV Authenticate. */
send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
}
else if (table[idx].special == -3)
{
char *tmp = get_dispserialno (app, 1);
if (tmp)
{
send_status_info (ctrl, table[idx].name,
tmp, strlen (tmp),
NULL, (size_t)0);
xfree (tmp);
}
else
err = gpg_error (GPG_ERR_INV_NAME); /* No Abbreviated S/N. */
}
else if (table[idx].special == -4) /* CHV-STATUS */
{
int tmp[4];
tmp[0] = get_chv_status (app, "PIV.00");
tmp[1] = get_chv_status (app, "PIV.80");
tmp[2] = get_chv_status (app, "PIV.81");
err = send_status_printf (ctrl, table[idx].name, "%d %d %d",
tmp[0], tmp[1], tmp[2]);
}
else if (table[idx].special == -5) /* CHV-USAGE (aka PIN Usage Policy) */
{
/* We return 2 hex bytes or nothing in case the discovery object
* is not supported. */
relptr = get_one_do (app, table[idx].tag, &value, &valuelen, &err);
if (relptr)
{
s = find_tlv (value, valuelen, 0x7E, &n);
if (s && n && (s = find_tlv (s, n, 0x5F2F, &n)) && n >=2 )
err = send_status_printf (ctrl, table[idx].name, "%02X %02X",
s[0], s[1]);
xfree (relptr);
}
}
else
{
relptr = get_one_do (app, table[idx].tag, &value, &valuelen, &err);
if (relptr)
{
send_status_info (ctrl, table[idx].name, value, valuelen, NULL, 0);
xfree (relptr);
}
}
return err;
}
/* Authenticate the card using the Card Application Administration
* Key. (VALUE,VALUELEN) has that 24 byte key. */
static gpg_error_t
auth_adm_key (app_t app, const unsigned char *value, size_t valuelen)
{
gpg_error_t err;
unsigned char tmpl[4+24];
size_t tmpllen;
unsigned char *outdata = NULL;
size_t outdatalen;
const unsigned char *s;
char witness[8];
size_t n;
gcry_cipher_hd_t cipher = NULL;
/* Prepare decryption. */
err = gcry_cipher_open (&cipher, GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_ECB, 0);
if (err)
goto leave;
err = gcry_cipher_setkey (cipher, value, valuelen);
if (err)
goto leave;
/* Request a witness. */
tmpl[0] = 0x7c;
tmpl[1] = 0x02;
tmpl[2] = 0x80;
tmpl[3] = 0; /* (Empty witness requests a witness.) */
tmpllen = 4;
err = iso7816_general_authenticate (app->slot, 0,
PIV_ALGORITHM_3DES_ECB_0, 0x9B,
tmpl, tmpllen, 0,
&outdata, &outdatalen);
+ if (gpg_err_code (err) == GPG_ERR_BAD_PIN)
+ err = gpg_error (GPG_ERR_BAD_AUTH);
if (err)
goto leave;
if (!(outdatalen && *outdata == 0x7c
&& (s = find_tlv (outdata, outdatalen, 0x80, &n))
&& n == 8))
{
err = gpg_error (GPG_ERR_CARD);
log_error ("piv: improper witness received\n");
goto leave;
}
err = gcry_cipher_decrypt (cipher, witness, 8, s, 8);
if (err)
goto leave;
/* Return decrypted witness and send our challenge. */
tmpl[0] = 0x7c;
tmpl[1] = 22;
tmpl[2] = 0x80;
tmpl[3] = 8;
memcpy (tmpl+4, witness, 8);
tmpl[12] = 0x81;
tmpl[13] = 8;
gcry_create_nonce (tmpl+14, 8);
tmpl[22] = 0x80;
tmpl[23] = 0;
tmpllen = 24;
xfree (outdata);
err = iso7816_general_authenticate (app->slot, 0,
PIV_ALGORITHM_3DES_ECB_0, 0x9B,
tmpl, tmpllen, 0,
&outdata, &outdatalen);
+ if (gpg_err_code (err) == GPG_ERR_BAD_PIN)
+ err = gpg_error (GPG_ERR_BAD_AUTH);
if (err)
goto leave;
if (!(outdatalen && *outdata == 0x7c
&& (s = find_tlv (outdata, outdatalen, 0x82, &n))
&& n == 8))
{
err = gpg_error (GPG_ERR_CARD);
log_error ("piv: improper challenge received\n");
goto leave;
}
/* (We reuse the witness buffer.) */
err = gcry_cipher_decrypt (cipher, witness, 8, s, 8);
if (err)
goto leave;
if (memcmp (witness, tmpl+14, 8))
{
- err = gpg_error (GPG_ERR_BAD_SIGNATURE);
+ err = gpg_error (GPG_ERR_BAD_AUTH);
goto leave;
}
leave:
xfree (outdata);
gcry_cipher_close (cipher);
return err;
}
/* Set a new admin key. */
static gpg_error_t
set_adm_key (app_t app, const unsigned char *value, size_t valuelen)
{
gpg_error_t err;
unsigned char apdu[8+24];
unsigned int sw;
/* Check whether it is a weak key and that it is of proper length. */
{
gcry_cipher_hd_t cipher;
err = gcry_cipher_open (&cipher, GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_ECB, 0);
if (!err)
{
err = gcry_cipher_setkey (cipher, value, valuelen);
gcry_cipher_close (cipher);
}
if (err)
goto leave;
}
if (app->app_local->flags.yubikey)
{
/* This is a Yubikey. */
if (valuelen != 24)
{
err = gpg_error (GPG_ERR_INV_LENGTH);
goto leave;
}
/* We use a proprietary Yubikey command. */
apdu[0] = 0;
apdu[1] = 0xff;
apdu[2] = 0xff;
apdu[3] = 0xff; /* touch policy: 0xff=never, 0xfe = always. */
apdu[4] = 3 + 24;
apdu[5] = PIV_ALGORITHM_3DES_ECB;
apdu[6] = 0x9b;
apdu[7] = 24;
memcpy (apdu+8, value, 24);
err = iso7816_apdu_direct (app->slot, apdu, 8+24, 0, &sw, NULL, NULL);
wipememory (apdu+8, 24);
if (err)
log_error ("piv: setting admin key failed; sw=%04x\n", sw);
+ /* A PIN is not required, thus use a better error code. */
+ if (gpg_err_code (err) == GPG_ERR_BAD_PIN)
+ err = gpg_error (GPG_ERR_NO_AUTH);
}
else
err = gpg_error (GPG_ERR_NOT_SUPPORTED);
leave:
return err;
}
/* Handle the SETATTR operation. All arguments are already basically
* checked. */
static gpg_error_t
do_setattr (app_t app, const char *name,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg,
const unsigned char *value, size_t valuelen)
{
gpg_error_t err;
static struct {
const char *name;
unsigned short tag;
unsigned short flush_tag; /* The tag which needs to be flushed or 0. */
int special; /* Special mode to use for thus NAME. */
} table[] = {
/* Authenticate using the PIV Card Application Administration Key
* (0x0B). Note that Yubico calls this key the "management key"
* which we don't do because that term is too similar to "Cert
* Management Key" (0x9D). */
{ "AUTH-ADM-KEY", 0x0000, 0x0000, 1 },
{ "SET-ADM-KEY", 0x0000, 0x0000, 2 }
};
int idx;
(void)pincb;
(void)pincb_arg;
for (idx=0; (idx < DIM (table)
&& ascii_strcasecmp (table[idx].name, name)); idx++)
;
if (!(idx < DIM (table)))
return gpg_error (GPG_ERR_INV_NAME);
/* Flush the cache before writing it, so that the next get operation
* will reread the data from the card and thus get synced in case of
* errors (e.g. data truncated by the card). */
if (table[idx].tag)
flush_cached_data (app, table[idx].flush_tag? table[idx].flush_tag
/* */ : table[idx].tag);
switch (table[idx].special)
{
case 1:
err = auth_adm_key (app, value, valuelen);
break;
case 2:
err = set_adm_key (app, value, valuelen);
break;
default:
err = gpg_error (GPG_ERR_BUG);
break;
}
return err;
}
/* Send the KEYPAIRINFO back. DOBJ describes the data object carrying
* the key. This is used by the LEARN command. */
static gpg_error_t
send_keypair_and_cert_info (app_t app, ctrl_t ctrl, data_object_t dobj,
int only_keypair)
{
gpg_error_t err = 0;
char *keygripstr = NULL;
int got_cert;
char idbuf[50];
err = get_keygrip_by_tag (app, dobj->tag, &keygripstr, &got_cert);
if (err)
goto leave;
snprintf (idbuf, sizeof idbuf, "PIV.%s", dobj->keyref);
send_status_info (ctrl, "KEYPAIRINFO",
keygripstr, strlen (keygripstr),
idbuf, strlen (idbuf),
NULL, (size_t)0);
if (!only_keypair && got_cert)
{
/* All certificates are of type 100 (Regular X.509 Cert). */
send_status_info (ctrl, "CERTINFO",
"100", 3,
idbuf, strlen (idbuf),
NULL, (size_t)0);
}
leave:
xfree (keygripstr);
return err;
}
/* Handle the LEARN command. */
static gpg_error_t
do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
{
int i;
(void)flags;
do_getattr (app, ctrl, "CHV-USAGE");
do_getattr (app, ctrl, "CHV-STATUS");
for (i=0; data_objects[i].tag; i++)
if (data_objects[i].keypair)
send_keypair_and_cert_info (app, ctrl, data_objects + i, !!(flags & 1));
return 0;
}
/* Core of do_readcert which fetches the certificate based on the
* given tag and returns it in a freshly allocated buffer stored at
* R_CERT and the length of the certificate stored at R_CERTLEN. If
* on success a non-zero value is stored at R_MECHANISM, the returned
* data is not certificate but a public key (in the format used by the
* container '7f49'. */
static gpg_error_t
readcert_by_tag (app_t app, unsigned int tag,
unsigned char **r_cert, size_t *r_certlen, int *r_mechanism)
{
gpg_error_t err;
unsigned char *buffer;
size_t buflen;
void *relptr;
const unsigned char *s, *s2;
size_t n, n2;
*r_cert = NULL;
*r_certlen = 0;
*r_mechanism = 0;
relptr = get_one_do (app, tag, &buffer, &buflen, NULL);
if (!relptr || !buflen)
{
err = gpg_error (GPG_ERR_NOT_FOUND);
goto leave;
}
s = find_tlv (buffer, buflen, 0x71, &n);
if (!s)
{
/* No certificate; check whether a public key has been stored
* using our own scheme. */
s = find_tlv (buffer, buflen, 0x7f49, &n);
if (!s || !n)
{
log_error ("piv: No public key in 0x%X\n", tag);
err = gpg_error (GPG_ERR_NO_PUBKEY);
goto leave;
}
s2 = find_tlv (buffer, buflen, 0x80, &n2);
if (!s2 || n2 != 1 || !*s2)
{
log_error ("piv: No mechanism for public key in 0x%X\n", tag);
err = gpg_error (GPG_ERR_NO_PUBKEY);
goto leave;
}
*r_mechanism = *s2;
}
else
{
if (n != 1)
{
log_error ("piv: invalid CertInfo in 0x%X\n", tag);
err = gpg_error (GPG_ERR_INV_CERT_OBJ);
goto leave;
}
if (*s == 0x01)
{
log_error ("piv: gzip compression not yet supported (tag 0x%X)\n",
tag);
err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING);
goto leave;
}
if (*s)
{
log_error ("piv: invalid CertInfo 0x%02x in 0x%X\n", *s, tag);
err = gpg_error (GPG_ERR_INV_CERT_OBJ);
goto leave;
}
/* Note: We don't check that the LRC octet has a length of zero
* as required by the specs. */
/* Get the cert from the container. */
s = find_tlv (buffer, buflen, 0x70, &n);
if (!s || !n)
{
err = gpg_error (GPG_ERR_NOT_FOUND);
goto leave;
}
}
/* The next is common for certificate and public key. */
if (!(*r_cert = xtrymalloc (n)))
{
err = gpg_error_from_syserror ();
goto leave;
}
memcpy (*r_cert, s, n);
*r_certlen = n;
err = 0;
leave:
xfree (relptr);
return err;
}
/* Get the keygrip in hex format of a key from the certificate stored
* at TAG. Caller must free the string at R_KEYGRIPSTR. */
static gpg_error_t
get_keygrip_by_tag (app_t app, unsigned int tag,
char **r_keygripstr, int *r_got_cert)
{
gpg_error_t err;
unsigned char *certbuf = NULL;
size_t certbuflen;
int mechanism;
gcry_sexp_t s_pkey = NULL;
ksba_cert_t cert = NULL;
unsigned char grip[KEYGRIP_LEN];
*r_got_cert = 0;
*r_keygripstr = xtrymalloc (2*KEYGRIP_LEN+1);
if (!r_keygripstr)
{
err = gpg_error_from_syserror ();
goto leave;
}
/* We need to get the public key from the certificate. */
err = readcert_by_tag (app, tag, &certbuf, &certbuflen, &mechanism);
if (err)
goto leave;
if (mechanism) /* Compute keygrip from public key. */
{
if (mechanism == PIV_ALGORITHM_RSA)
err = genkey_parse_rsa (certbuf, certbuflen, &s_pkey);
else if (mechanism == PIV_ALGORITHM_ECC_P256
|| mechanism == PIV_ALGORITHM_ECC_P384)
err = genkey_parse_ecc (certbuf, certbuflen, mechanism, &s_pkey);
else
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
if (err)
goto leave;
if (!gcry_pk_get_keygrip (s_pkey, grip))
{
log_error ("piv: error computing keygrip\n");
err = gpg_error (GPG_ERR_GENERAL);
goto leave;
}
bin2hex (grip, sizeof grip, *r_keygripstr);
}
else /* Compute keygrip from certificate. */
{
*r_got_cert = 0;
err = ksba_cert_new (&cert);
if (err)
goto leave;
err = ksba_cert_init_from_mem (cert, certbuf, certbuflen);
if (err)
goto leave;
err = app_help_get_keygrip_string (cert, *r_keygripstr);
}
leave:
gcry_sexp_release (s_pkey);
ksba_cert_release (cert);
xfree (certbuf);
if (err)
{
xfree (*r_keygripstr);
*r_keygripstr = NULL;
}
return err;
}
/* Locate the data object from the given KEYREF. The KEYREF may also
* be the corresponding OID of the key object. Returns the data
* object or NULL if not found. */
static data_object_t
find_dobj_by_keyref (app_t app, const char *keyref)
{
int i;
(void)app;
if (!ascii_strncasecmp (keyref, "PIV.", 4))
{
keyref += 4;
for (i=0; data_objects[i].tag; i++)
if (*data_objects[i].keyref
&& !ascii_strcasecmp (keyref, data_objects[i].keyref))
{
return data_objects + i;
}
}
else if (!strncmp (keyref, "2.16.840.1.101.3.7.", 19))
{
keyref += 19;
for (i=0; data_objects[i].tag; i++)
if (*data_objects[i].keyref
&& !strcmp (keyref, data_objects[i].oidsuffix))
{
return data_objects + i;
}
}
return NULL;
}
/* Return the keyref from DOBJ as an integer. If it does not exist,
* return -1. */
static int
keyref_from_dobj (data_object_t dobj)
{
if (!dobj || !hexdigitp (dobj->keyref) || !hexdigitp (dobj->keyref+1))
return -1;
return xtoi_2 (dobj->keyref);
}
/* Read a certificate from the card and returned in a freshly
* allocated buffer stored at R_CERT and the length of the certificate
* stored at R_CERTLEN. CERTID is either the OID of the cert's
* container or of the form "PIV.<two_hexdigit_keyref>" */
static gpg_error_t
do_readcert (app_t app, const char *certid,
unsigned char **r_cert, size_t *r_certlen)
{
gpg_error_t err;
data_object_t dobj;
int mechanism;
*r_cert = NULL;
*r_certlen = 0;
dobj = find_dobj_by_keyref (app, certid);
if (!dobj)
return gpg_error (GPG_ERR_INV_ID);
err = readcert_by_tag (app, dobj->tag, r_cert, r_certlen, &mechanism);
if (!err && mechanism)
{
/* Well, no certificate but a public key - we don't want it. */
xfree (*r_cert);
*r_cert = NULL;
*r_certlen = 0;
err = gpg_error (GPG_ERR_NOT_FOUND);
}
return err;
}
/* Return a public key in a freshly allocated buffer. This will only
* work for a freshly generated key as long as no reset of the
* application has been performed. This is because we return a cached
* result from key generation. If no cached result is available, the
* error GPG_ERR_UNSUPPORTED_OPERATION is returned so that the higher
* layer can then to get the key by reading the matching certificate.
* On success a canonical encoded s-expression with the public key is
* stored at (R_PK,R_PKLEN); the caller must release that buffer. On
* error R_PK and R_PKLEN are not changed and an error code is
* returned.
*/
static gpg_error_t
do_readkey (app_t app, int advanced, const char *keyrefstr,
unsigned char **r_pk, size_t *r_pklen)
{
gpg_error_t err;
data_object_t dobj;
int keyref;
unsigned char *cert = NULL;
size_t certlen;
int mechanism;
gcry_sexp_t s_pkey = NULL;
unsigned char *pk = NULL;
size_t pklen;
dobj = find_dobj_by_keyref (app, keyrefstr);
if ((keyref = keyref_from_dobj (dobj)) == -1)
{
err = gpg_error (GPG_ERR_INV_ID);
goto leave;
}
err = readcert_by_tag (app, dobj->tag, &cert, &certlen, &mechanism);
if (err)
goto leave;
if (!mechanism)
{
/* We got a certificate. Let the upper layer handle the
* extraction of the key. FIXME: It would be better to have a
* shared fucntion to dothis here. */
err = gpg_error (GPG_ERR_NOT_FOUND);
goto leave;
}
/* Convert the public key into the expected s-expression. */
if (mechanism == PIV_ALGORITHM_RSA)
err = genkey_parse_rsa (cert, certlen, &s_pkey);
else if (mechanism == PIV_ALGORITHM_ECC_P256
|| mechanism == PIV_ALGORITHM_ECC_P384)
err = genkey_parse_ecc (cert, certlen, mechanism, &s_pkey);
else
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
if (err)
goto leave;
err = make_canon_sexp (s_pkey, &pk, &pklen);
if (err)
goto leave;
if (advanced)
{
/* FIXME: How ugly - we should move that to command.c */
char *p = canon_sexp_to_string (pk, pklen);
if (!p)
{
err = gpg_error_from_syserror ();
goto leave;
}
xfree (pk);
pk = p;
pklen = strlen (pk);
}
*r_pk = pk;
pk = NULL;
*r_pklen = pklen;
leave:
gcry_sexp_release (s_pkey);
xfree (pk);
xfree (cert);
return err;
}
/* Given a data object DOBJ return the corresponding PIV algorithm and
* store it at R_ALGO. The algorithm is taken from the corresponding
* certificate or from a cache. */
static gpg_error_t
get_key_algorithm_by_dobj (app_t app, data_object_t dobj, int *r_mechanism)
{
gpg_error_t err;
unsigned char *certbuf = NULL;
size_t certbuflen;
int mechanism;
ksba_cert_t cert = NULL;
ksba_sexp_t k_pkey = NULL;
gcry_sexp_t s_pkey = NULL;
gcry_sexp_t l1 = NULL;
char *algoname = NULL;
int algo;
size_t n;
const char *curve_name;
*r_mechanism = 0;
err = readcert_by_tag (app, dobj->tag, &certbuf, &certbuflen, &mechanism);
if (err)
goto leave;
if (mechanism)
{
/* A public key was found. That makes it easy. */
switch (mechanism)
{
case PIV_ALGORITHM_RSA:
case PIV_ALGORITHM_ECC_P256:
case PIV_ALGORITHM_ECC_P384:
*r_mechanism = mechanism;
break;
default:
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
log_error ("piv: unknown mechanism %d in public key at %s\n",
mechanism, dobj->keyref);
break;
}
goto leave;
}
err = ksba_cert_new (&cert);
if (err)
goto leave;
err = ksba_cert_init_from_mem (cert, certbuf, certbuflen);
if (err)
{
log_error ("piv: failed to parse the certificate %s: %s\n",
dobj->keyref, gpg_strerror (err));
goto leave;
}
xfree (certbuf);
certbuf = NULL;
k_pkey = ksba_cert_get_public_key (cert);
if (!k_pkey)
{
err = gpg_error (GPG_ERR_NO_PUBKEY);
goto leave;
}
n = gcry_sexp_canon_len (k_pkey, 0, NULL, NULL);
err = gcry_sexp_new (&s_pkey, k_pkey, n, 0);
if (err)
goto leave;
l1 = gcry_sexp_find_token (s_pkey, "public-key", 0);
if (!l1)
{
err = gpg_error (GPG_ERR_NO_PUBKEY);
goto leave;
}
{
gcry_sexp_t l_tmp = gcry_sexp_cadr (l1);
gcry_sexp_release (l1);
l1 = l_tmp;
}
algoname = gcry_sexp_nth_string (l1, 0);
if (!algoname)
{
err = gpg_error_from_syserror ();
goto leave;
}
algo = gcry_pk_map_name (algoname);
switch (algo)
{
case GCRY_PK_RSA:
algo = PIV_ALGORITHM_RSA;
break;
case GCRY_PK_ECC:
case GCRY_PK_ECDSA:
case GCRY_PK_ECDH:
curve_name = gcry_pk_get_curve (s_pkey, 0, NULL);
if (curve_name && !strcmp (curve_name, "NIST P-256"))
algo = PIV_ALGORITHM_ECC_P256;
else if (curve_name && !strcmp (curve_name, "NIST P-384"))
algo = PIV_ALGORITHM_ECC_P384;
else
{
err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
log_error ("piv: certificate %s, curve '%s': %s\n",
dobj->keyref, curve_name, gpg_strerror (err));
goto leave;
}
break;
default:
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
log_error ("piv: certificate %s, pubkey algo '%s': %s\n",
dobj->keyref, algoname, gpg_strerror (err));
goto leave;
}
*r_mechanism = algo;
leave:
gcry_free (algoname);
gcry_sexp_release (l1);
gcry_sexp_release (s_pkey);
ksba_free (k_pkey);
xfree (certbuf);
return err;
}
/* Return an allocated string to be used as prompt. Returns NULL on
* malloc error. */
static char *
make_prompt (app_t app, int remaining, const char *firstline)
{
char *serial, *tmpbuf, *result;
serial = get_dispserialno (app, 0);
if (!serial)
return NULL;
/* TRANSLATORS: Put a \x1f right before a colon. This can be
* used by pinentry to nicely align the names and values. Keep
* the %s at the start and end of the string. */
result = xtryasprintf (_("%s"
"Number\x1f: %s%%0A"
"Holder\x1f: %s"
"%s"),
"\x1e",
serial,
"Unknown", /* Fixme */
"");
xfree (serial);
/* Append a "remaining attempts" info if needed. */
if (remaining != -1 && remaining < 3)
{
char *rembuf;
/* TRANSLATORS: This is the number of remaining attempts to
* enter a PIN. Use %%0A (double-percent,0A) for a linefeed. */
rembuf = xtryasprintf (_("Remaining attempts: %d"), remaining);
if (rembuf)
{
tmpbuf = strconcat (firstline, "%0A%0A", result,
"%0A%0A", rembuf, NULL);
xfree (rembuf);
}
else
tmpbuf = NULL;
xfree (result);
result = tmpbuf;
}
else
{
tmpbuf = strconcat (firstline, "%0A%0A", result, NULL);
xfree (result);
result = tmpbuf;
}
return result;
}
/* Helper for verify_chv to ask for the PIN and to prepare/pad it. On
* success the result is stored at (R_PIN,R_PINLEN). */
static gpg_error_t
ask_and_prepare_chv (app_t app, int keyref, int ask_new, int remaining,
gpg_error_t (*pincb)(void*,const char *,char **),
void *pincb_arg, char **r_pin, unsigned int *r_pinlen)
{
gpg_error_t err;
const char *label;
char *prompt;
char *pinvalue = NULL;
unsigned int pinlen;
char *pinbuffer = NULL;
int minlen, maxlen, padding, onlydigits;
*r_pin = NULL;
*r_pinlen = 0;
if (ask_new)
remaining = -1;
if (remaining != -1)
log_debug ("piv: CHV %02X has %d attempts left\n", keyref, remaining);
switch (keyref)
{
case 0x00:
minlen = 6;
maxlen = 8;
padding = 1;
onlydigits = 1;
label = (ask_new? _("|N|Please enter the new Global-PIN")
/**/ : _("||Please enter the Global-PIN of your PIV card"));
break;
case 0x80:
minlen = 6;
maxlen = 8;
padding = 1;
onlydigits = 1;
label = (ask_new? _("|N|Please enter the new PIN")
/**/ : _("||Please enter the PIN of your PIV card"));
break;
case 0x81:
minlen = 8;
maxlen = 8;
padding = 0;
onlydigits = 0;
label = (ask_new? _("|N|Please enter the new Unblocking Key")
/**/ :_("||Please enter the Unblocking Key of your PIV card"));
break;
case 0x96:
case 0x97:
case 0x98:
case 0x9B:
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
default:
return gpg_error (GPG_ERR_INV_ID);
}
/* Ask for the PIN. */
prompt = make_prompt (app, remaining, label);
err = pincb (pincb_arg, prompt, &pinvalue);
xfree (prompt);
prompt = NULL;
if (err)
{
log_info (_("PIN callback returned error: %s\n"), gpg_strerror (err));
return err;
}
pinlen = pinvalue? strlen (pinvalue) : 0;
if (pinlen < minlen)
{
log_error (_("PIN for is too short; minimum length is %d\n"), minlen);
if (pinvalue)
wipememory (pinvalue, pinlen);
xfree (pinvalue);
return gpg_error (GPG_ERR_BAD_PIN);
}
if (pinlen > maxlen)
{
log_error (_("PIN for is too long; maximum length is %d\n"), maxlen);
wipememory (pinvalue, pinlen);
xfree (pinvalue);
return gpg_error (GPG_ERR_BAD_PIN);
}
if (onlydigits && strspn (pinvalue, "0123456789") != pinlen)
{
log_error (_("PIN has invalid characters; only digits are allowed\n"));
wipememory (pinvalue, pinlen);
xfree (pinvalue);
return gpg_error (GPG_ERR_BAD_PIN);
}
pinbuffer = xtrymalloc_secure (maxlen);
if (!pinbuffer)
{
err = gpg_error_from_syserror ();
wipememory (pinvalue, pinlen);
xfree (pinvalue);
return err;
}
memcpy (pinbuffer, pinvalue, pinlen);
wipememory (pinvalue, pinlen);
xfree (pinvalue);
if (padding)
{
memset (pinbuffer + pinlen, 0xff, maxlen - pinlen);
pinlen = maxlen;
}
*r_pin = pinbuffer;
*r_pinlen = pinlen;
return 0;
}
/* Verify the card holder verification identified by KEYREF. This is
* either the Appication PIN or the Global PIN. */
static gpg_error_t
verify_chv (app_t app, int keyref,
gpg_error_t (*pincb)(void*,const char *,char **), void *pincb_arg)
{
gpg_error_t err;
unsigned char apdu[4];
unsigned int sw;
int remaining;
char *pin = NULL;
unsigned int pinlen;
/* First check whether a verify is at all needed. This is done with
* P1 being 0 and no Lc and command data send. */
apdu[0] = 0x00;
apdu[1] = ISO7816_VERIFY;
apdu[2] = 0x00;
apdu[3] = keyref;
if (!iso7816_apdu_direct (app->slot, apdu, 4, 0, &sw, NULL, NULL))
{
/* No need to verification. */
return 0; /* All fine. */
}
if ((sw & 0xfff0) == 0x63C0)
remaining = (sw & 0x000f); /* PIN has REMAINING tries left. */
else
remaining = -1;
err = ask_and_prepare_chv (app, keyref, 0, remaining, pincb, pincb_arg,
&pin, &pinlen);
if (err)
return err;
err = iso7816_verify (app->slot, keyref, pin, pinlen);
wipememory (pin, pinlen);
xfree (pin);
if (err)
log_error ("CHV %02X verification failed: %s\n",
keyref, gpg_strerror (err));
return err;
}
/* Handle the PASSWD command. Valid values for PWIDSTR are
* key references related to PINs; in particular:
* PIV.00 - The Global PIN
* PIV.80 - The Application PIN
* PIV.81 - The PIN Unblocking key
* The supported flags are:
* APP_CHANGE_FLAG_CLEAR Clear the PIN verification state.
* APP_CHANGE_FLAG_RESET Reset a PIN using the PUK. Only
* allowed with PIV.80.
*/
static gpg_error_t
do_change_chv (app_t app, ctrl_t ctrl, const char *pwidstr,
unsigned int flags,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg)
{
gpg_error_t err;
int keyref, targetkeyref;
unsigned char apdu[4];
unsigned int sw;
int remaining;
char *oldpin = NULL;
unsigned int oldpinlen;
char *newpin = NULL;
unsigned int newpinlen;
(void)ctrl;
/* Check for unknown flags. */
if ((flags & ~(APP_CHANGE_FLAG_CLEAR|APP_CHANGE_FLAG_RESET)))
{
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
goto leave;
}
/* Parse the keyref. */
targetkeyref = keyref = parse_chv_keyref (pwidstr);
if (keyref == -1)
{
err = gpg_error (GPG_ERR_INV_ID);
goto leave;
}
/* First see whether the special --clear mode has been requested. */
if ((flags & APP_CHANGE_FLAG_CLEAR))
{
apdu[0] = 0x00;
apdu[1] = ISO7816_VERIFY;
apdu[2] = 0xff;
apdu[3] = keyref;
err = iso7816_apdu_direct (app->slot, apdu, 4, 0, NULL, NULL, NULL);
goto leave;
}
/* Prepare reset mode. */
if ((flags & APP_CHANGE_FLAG_RESET))
{
if (keyref == 0x81)
{
err = gpg_error (GPG_ERR_INV_ID); /* Can't reset the PUK. */
goto leave;
}
/* Set the keyref to the PUK and keep the TARGETKEYREF. */
keyref = 0x81;
}
/* Get the remaining tries count. This is done by using the check
* for verified state feature. */
apdu[0] = 0x00;
apdu[1] = ISO7816_VERIFY;
apdu[2] = 0x00;
apdu[3] = keyref;
if (!iso7816_apdu_direct (app->slot, apdu, 4, 0, &sw, NULL, NULL))
remaining = -1; /* Already verified, thus full number of tries. */
else if ((sw & 0xfff0) == 0x63C0)
remaining = (sw & 0x000f); /* PIN has REMAINING tries left. */
else
remaining = -1;
/* Ask for the old pin or puk. */
err = ask_and_prepare_chv (app, keyref, 0, remaining, pincb, pincb_arg,
&oldpin, &oldpinlen);
if (err)
return err;
/* Verify the old pin so that we don't prompt for the new pin if the
* old is wrong. This is not possible for the PUK, though. */
if (keyref != 0x81)
{
err = iso7816_verify (app->slot, keyref, oldpin, oldpinlen);
if (err)
{
log_error ("CHV %02X verification failed: %s\n",
keyref, gpg_strerror (err));
goto leave;
}
}
/* Ask for the new pin. */
err = ask_and_prepare_chv (app, targetkeyref, 1, -1, pincb, pincb_arg,
&newpin, &newpinlen);
if (err)
return err;
if ((flags & APP_CHANGE_FLAG_RESET))
{
char *buf = xtrymalloc_secure (oldpinlen + newpinlen);
if (!buf)
{
err = gpg_error_from_syserror ();
goto leave;
}
memcpy (buf, oldpin, oldpinlen);
memcpy (buf+oldpinlen, newpin, newpinlen);
err = iso7816_reset_retry_counter_with_rc (app->slot, targetkeyref,
buf, oldpinlen+newpinlen);
xfree (buf);
if (err)
log_error ("resetting CHV %02X using CHV %02X failed: %s\n",
targetkeyref, keyref, gpg_strerror (err));
}
else
{
err = iso7816_change_reference_data (app->slot, keyref,
oldpin, oldpinlen,
newpin, newpinlen);
if (err)
log_error ("CHV %02X changing PIN failed: %s\n",
keyref, gpg_strerror (err));
}
leave:
xfree (oldpin);
xfree (newpin);
return err;
}
/* Perform a simple verify operation for the PIN specified by PWIDSTR.
* For valid values see do_change_chv. */
static gpg_error_t
do_check_chv (app_t app, const char *pwidstr,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg)
{
int keyref;
keyref = parse_chv_keyref (pwidstr);
if (keyref == -1)
return gpg_error (GPG_ERR_INV_ID);
return verify_chv (app, keyref, pincb, pincb_arg);
}
/* Compute a digital signature using the GENERAL AUTHENTICATE command
* on INDATA which is expected to be the raw message digest. The
* KEYIDSTR has the key reference or its OID (e.g. "PIV.9A"). The
* result is stored at (R_OUTDATA,R_OUTDATALEN); on error (NULL,0) is
* stored there and an error code returned. For ECDSA the result is
* the simple concatenation of R and S without any DER encoding. R
* and S are left extended with zeroes to make sure they have an equal
* length. If HASHALGO is not zero, the function prepends the hash's
* OID to the indata or checks that it is consistent.
*/
static gpg_error_t
do_sign (app_t app, const char *keyidstr, int hashalgo,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg,
const void *indata_arg, size_t indatalen,
unsigned char **r_outdata, size_t *r_outdatalen)
{
const unsigned char *indata = indata_arg;
gpg_error_t err;
data_object_t dobj;
unsigned char oidbuf[64];
size_t oidbuflen;
unsigned char *outdata = NULL;
size_t outdatalen;
const unsigned char *s;
size_t n;
int keyref, mechanism;
unsigned char *indata_buffer = NULL; /* Malloced helper. */
unsigned char *apdudata = NULL;
size_t apdudatalen;
if (!keyidstr || !*keyidstr)
{
err = gpg_error (GPG_ERR_INV_VALUE);
goto leave;
}
dobj = find_dobj_by_keyref (app, keyidstr);
if ((keyref = keyref_from_dobj (dobj)) == -1)
{
err = gpg_error (GPG_ERR_INV_ID);
goto leave;
}
err = get_key_algorithm_by_dobj (app, dobj, &mechanism);
if (err)
goto leave;
/* For ECC we need to remove the ASN.1 prefix from INDATA. For RSA
* we need to add the padding and possible also the ASN.1 prefix. */
if (mechanism == PIV_ALGORITHM_ECC_P256
|| mechanism == PIV_ALGORITHM_ECC_P384)
{
int need_algo, need_digestlen;
if (mechanism == PIV_ALGORITHM_ECC_P256)
{
need_algo = GCRY_MD_SHA256;
need_digestlen = 32;
}
else
{
need_algo = GCRY_MD_SHA384;
need_digestlen = 48;
}
if (hashalgo && hashalgo != need_algo)
{
err = gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
log_error ("piv: hash algo %d does not match mechanism %d\n",
need_algo, mechanism);
goto leave;
}
if (indatalen > need_digestlen)
{
oidbuflen = sizeof oidbuf;
err = gcry_md_get_asnoid (need_algo, &oidbuf, &oidbuflen);
if (err)
{
err = gpg_error (GPG_ERR_INTERNAL);
log_debug ("piv: no OID for hash algo %d\n", need_algo);
goto leave;
}
if (indatalen != oidbuflen + need_digestlen
|| memcmp (indata, oidbuf, oidbuflen))
{
err = gpg_error (GPG_ERR_INV_VALUE);
log_error ("piv: bad input for signing with mechanism %d\n",
mechanism);
goto leave;
}
indata += oidbuflen;
indatalen -= oidbuflen;
}
}
else if (mechanism == PIV_ALGORITHM_RSA)
{
/* PIV requires 2048 bit RSA. */
unsigned int framelen = 2048 / 8;
unsigned char *frame;
int i;
oidbuflen = sizeof oidbuf;
if (!hashalgo)
{
/* We assume that indata already has the required
* digestinfo; thus merely prepend the padding below. */
}
else if ((err = gcry_md_get_asnoid (hashalgo, &oidbuf, &oidbuflen)))
{
log_debug ("piv: no OID for hash algo %d\n", hashalgo);
goto leave;
}
else
{
unsigned int digestlen = gcry_md_get_algo_dlen (hashalgo);
if (indatalen == digestlen)
{
/* Plain hash in INDATA; prepend the digestinfo. */
indata_buffer = xtrymalloc (oidbuflen + indatalen);
if (!indata_buffer)
{
err = gpg_error_from_syserror ();
goto leave;
}
memcpy (indata_buffer, oidbuf, oidbuflen);
memcpy (indata_buffer+oidbuflen, indata, indatalen);
indata = indata_buffer;
indatalen = oidbuflen + indatalen;
}
else if (indatalen == oidbuflen + digestlen
&& !memcmp (indata, oidbuf, oidbuflen))
; /* Correct prefix. */
else
{
err = gpg_error (GPG_ERR_INV_VALUE);
log_error ("piv: bad input for signing with RSA and hash %d\n",
hashalgo);
goto leave;
}
}
/* Now prepend the pkcs#v1.5 padding. We require at least 8
* byte of padding and 3 extra bytes for the prefix and the
* delimiting nul. */
if (!indatalen || indatalen + 8 + 4 > framelen)
{
err = gpg_error (GPG_ERR_INV_VALUE);
log_error ("piv: input does not fit into a %u bit PKCS#v1.5 frame\n",
8*framelen);
goto leave;
}
frame = xtrymalloc (framelen);
if (!frame)
{
err = gpg_error_from_syserror ();
goto leave;
}
n = 0;
frame[n++] = 0;
frame[n++] = 1; /* Block type. */
i = framelen - indatalen - 3 ;
memset (frame+n, 0xff, i);
n += i;
frame[n++] = 0; /* Delimiter. */
memcpy (frame+n, indata, indatalen);
n += indatalen;
log_assert (n == framelen);
/* And now put it into the indata_buffer. */
xfree (indata_buffer);
indata_buffer = frame;
indata = indata_buffer;
indatalen = framelen;
}
else
{
err = gpg_error (GPG_ERR_INTERNAL);
log_debug ("piv: unknown PIV mechanism %d while signing\n", mechanism);
goto leave;
}
/* Now verify the Application PIN. */
err = verify_chv (app, 0x80, pincb, pincb_arg);
if (err)
return err;
/* Build the Dynamic Authentication Template. */
err = concat_tlv_list (&apdudata, &apdudatalen,
(int)0x7c, (size_t)0, NULL, /* Constructed. */
(int)0x82, (size_t)0, "",
(int)0x81, (size_t)indatalen, indata,
(int)0, (size_t)0, NULL);
if (err)
goto leave;
/* Note: the -1 requests command chaining. */
err = iso7816_general_authenticate (app->slot, -1,
mechanism, keyref,
apdudata, (int)apdudatalen, 0,
&outdata, &outdatalen);
if (err)
goto leave;
/* Parse the response. */
if (outdatalen && *outdata == 0x7c
&& (s = find_tlv (outdata, outdatalen, 0x82, &n)))
{
if (mechanism == PIV_ALGORITHM_RSA)
{
memmove (outdata, outdata + (s - outdata), n);
outdatalen = n;
}
else /* ECC */
{
const unsigned char *rval, *sval;
size_t rlen, rlenx, slen, slenx, resultlen;
char *result;
/* The result of an ECDSA signature is
* SEQUENCE { r INTEGER, s INTEGER }
* We re-pack that by concatenating R and S and making sure
* that both have the same length. We simplify parsing by
* using find_tlv and not a proper DER parser. */
s = find_tlv (s, n, 0x30, &n);
if (!s)
goto bad_der;
rval = find_tlv (s, n, 0x02, &rlen);
if (!rval)
goto bad_der;
log_assert (n >= (rval-s)+rlen);
sval = find_tlv (rval+rlen, n-((rval-s)+rlen), 0x02, &slen);
if (!rval)
goto bad_der;
rlenx = slenx = 0;
if (rlen > slen)
slenx = rlen - slen;
else if (slen > rlen)
rlenx = slen - rlen;
resultlen = rlen + rlenx + slen + slenx;
result = xtrycalloc (1, resultlen);
if (!result)
{
err = gpg_error_from_syserror ();
goto leave;
}
memcpy (result + rlenx, rval, rlen);
memcpy (result + rlenx + rlen + slenx, sval, slen);
xfree (outdata);
outdata = result;
outdatalen = resultlen;
}
}
else
{
bad_der:
err = gpg_error (GPG_ERR_CARD);
log_error ("piv: response does not contain a proper result\n");
goto leave;
}
leave:
if (err)
{
xfree (outdata);
*r_outdata = NULL;
*r_outdatalen = 0;
}
else
{
*r_outdata = outdata;
*r_outdatalen = outdatalen;
}
xfree (apdudata);
xfree (indata_buffer);
return err;
}
/* AUTH for PIV cards is actually the same as SIGN. The difference
* between AUTH and SIGN is that AUTH expects that pkcs#1.5 padding
* for RSA has already been done (digestInfo part w/o the padding)
* whereas SIGN may accept a plain digest and does the padding if
* needed. This is also the reason why SIGN takes a hashalgo. */
static gpg_error_t
do_auth (app_t app, const char *keyidstr,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg,
const void *indata, size_t indatalen,
unsigned char **r_outdata, size_t *r_outdatalen)
{
return do_sign (app, keyidstr, 0, pincb, pincb_arg, indata, indatalen,
r_outdata, r_outdatalen);
}
/* Check whether a key for DOBJ already exists. We detect this by
* reading the certificate described by DOBJ. If FORCE is TRUE a
* diagnositic will be printed but no error returned if the key
* already exists. The flag GENERATING is used to select a
* diagnositic. */
static gpg_error_t
does_key_exist (app_t app, data_object_t dobj, int generating, int force)
{
void *relptr;
unsigned char *buffer;
size_t buflen;
int found;
relptr = get_one_do (app, dobj->tag, &buffer, &buflen, NULL);
found = (relptr && buflen);
xfree (relptr);
if (found && !force)
{
log_error (_("key already exists\n"));
return gpg_error (GPG_ERR_EEXIST);
}
if (found)
log_info (_("existing key will be replaced\n"));
else if (generating)
log_info (_("generating new key\n"));
else
log_info (_("writing new key\n"));
return 0;
}
/* Parse an RSA response object, consisting of the content of tag
* 0x7f49, into a gcrypt s-expression object and store that R_SEXP.
* On error NULL is stored at R_SEXP. */
static gpg_error_t
genkey_parse_rsa (const unsigned char *data, size_t datalen,
gcry_sexp_t *r_sexp)
{
gpg_error_t err;
const unsigned char *m, *e;
unsigned char *mbuf = NULL;
unsigned char *ebuf = NULL;
size_t mlen, elen;
*r_sexp = NULL;
m = find_tlv (data, datalen, 0x0081, &mlen);
if (!m)
{
log_error (_("response does not contain the RSA modulus\n"));
err = gpg_error (GPG_ERR_CARD);
goto leave;
}
e = find_tlv (data, datalen, 0x0082, &elen);
if (!e)
{
log_error (_("response does not contain the RSA public exponent\n"));
err = gpg_error (GPG_ERR_CARD);
goto leave;
}
for (; mlen && !*m; mlen--, m++) /* Strip leading zeroes */
;
for (; elen && !*e; elen--, e++) /* Strip leading zeroes */
;
mbuf = xtrymalloc (mlen + 1);
if (!mbuf)
{
err = gpg_error_from_syserror ();
goto leave;
}
/* Prepend numbers with a 0 if needed. */
if (mlen && (*m & 0x80))
{
*mbuf = 0;
memcpy (mbuf+1, m, mlen);
mlen++;
}
else
memcpy (mbuf, m, mlen);
ebuf = xtrymalloc (elen + 1);
if (!ebuf)
{
err = gpg_error_from_syserror ();
goto leave;
}
/* Prepend numbers with a 0 if needed. */
if (elen && (*e & 0x80))
{
*ebuf = 0;
memcpy (ebuf+1, e, elen);
elen++;
}
else
memcpy (ebuf, e, elen);
err = gcry_sexp_build (r_sexp, NULL, "(public-key(rsa(n%b)(e%b)))",
(int)mlen, mbuf, (int)elen, ebuf);
leave:
xfree (mbuf);
xfree (ebuf);
return err;
}
/* Parse an ECC response object, consisting of the content of tag
* 0x7f49, into a gcrypt s-expression object and store that R_SEXP.
* On error NULL is stored at R_SEXP. MECHANISM specifies the
* curve. */
static gpg_error_t
genkey_parse_ecc (const unsigned char *data, size_t datalen, int mechanism,
gcry_sexp_t *r_sexp)
{
gpg_error_t err;
const unsigned char *ecc_q;
size_t ecc_qlen;
const char *curve;
*r_sexp = NULL;
ecc_q = find_tlv (data, datalen, 0x0086, &ecc_qlen);
if (!ecc_q)
{
log_error (_("response does not contain the EC public key\n"));
err = gpg_error (GPG_ERR_CARD);
goto leave;
}
if (mechanism == PIV_ALGORITHM_ECC_P256)
curve = "nistp256";
else if (mechanism == PIV_ALGORITHM_ECC_P384)
curve = "nistp384";
else
{
err = gpg_error (GPG_ERR_BUG); /* Call with wrong parameters. */
goto leave;
}
err = gcry_sexp_build (r_sexp, NULL, "(public-key(ecc(curve%s)(q%b)))",
curve, (int)ecc_qlen, ecc_q);
leave:
return err;
}
/* Create a new keypair for KEYREF. If KEYTYPE is NULL a default
* keytype is selected, else it may be one of the strings:
* "rsa2048", "nistp256, or "nistp384".
*
* Supported FLAGS are:
* APP_GENKEY_FLAG_FORCE Overwrite existing key.
*
* Note that CREATETIME is not used for PIV cards.
*
* Because there seems to be no way to read the public key we need to
* retrieve it from a certificate. The GnuPG system however requires
* the use of app_readkey to fetch the public key from the card to
* create the certificate; to support this we temporary store the
* generated public key in the local context for use by app_readkey.
*/
static gpg_error_t
do_genkey (app_t app, ctrl_t ctrl, const char *keyrefstr, const char *keytype,
unsigned int flags, time_t createtime,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg)
{
gpg_error_t err;
data_object_t dobj;
unsigned char *buffer = NULL;
size_t buflen;
int force = !!(flags & APP_GENKEY_FLAG_FORCE);
int mechanism;
time_t start_at;
int keyref;
unsigned char tmpl[5];
size_t tmpllen;
const unsigned char *keydata;
size_t keydatalen;
(void)ctrl;
(void)createtime;
(void)pincb;
(void)pincb_arg;
if (!keytype)
keytype = "rsa2048";
if (!strcmp (keytype, "rsa2048"))
mechanism = PIV_ALGORITHM_RSA;
else if (!strcmp (keytype, "nistp256"))
mechanism = PIV_ALGORITHM_ECC_P256;
else if (!strcmp (keytype, "nistp384"))
mechanism = PIV_ALGORITHM_ECC_P384;
else
return gpg_error (GPG_ERR_UNKNOWN_CURVE);
/* We flush the cache to increase the I/O traffic before a key
* generation. This _might_ help the card to gather more entropy
* and is anyway a prerequisite for does_key_exist. */
flush_cached_data (app, 0);
/* Check whether a key already exists. */
dobj = find_dobj_by_keyref (app, keyrefstr);
if ((keyref = keyref_from_dobj (dobj)) == -1)
{
err = gpg_error (GPG_ERR_INV_ID);
goto leave;
}
err = does_key_exist (app, dobj, 1, force);
if (err)
goto leave;
/* FIXME: Check that the authentication has already been done. */
/* Create the key. */
log_info (_("please wait while key is being generated ...\n"));
start_at = time (NULL);
tmpl[0] = 0xac;
tmpl[1] = 3;
tmpl[2] = 0x80;
tmpl[3] = 1;
tmpl[4] = mechanism;
tmpllen = 5;
err = iso7816_generate_keypair (app->slot, 0, 0, keyref,
tmpl, tmpllen, 0, &buffer, &buflen);
if (err)
{
+ /* A PIN is not required, thus use a better error code. */
+ if (gpg_err_code (err) == GPG_ERR_BAD_PIN)
+ err = gpg_error (GPG_ERR_NO_AUTH);
log_error (_("generating key failed\n"));
return err;
}
{
int nsecs = (int)(time (NULL) - start_at);
log_info (ngettext("key generation completed (%d second)\n",
"key generation completed (%d seconds)\n",
nsecs), nsecs);
}
/* Parse the result and store it as an s-expression in a dedicated
* cache for later retrieval by app_readkey. */
keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen);
if (!keydata || !keydatalen)
{
err = gpg_error (GPG_ERR_CARD);
log_error (_("response does not contain the public key data\n"));
goto leave;
}
tmpl[0] = mechanism;
flush_cached_data (app, dobj->tag);
err = put_data (app->slot, dobj->tag,
(int)0x80, (size_t)1, tmpl,
(int)0x7f49, (size_t)keydatalen, keydata,
(int)0, (size_t)0, NULL);
if (err)
{
log_error ("piv: failed to write key to the cert DO %s: %s\n",
dobj->keyref, gpg_strerror (err));
goto leave;
}
leave:
xfree (buffer);
return err;
}
/* Write the certificate (CERT,CERTLEN) to the card at CERTREFSTR.
* CERTREFSTR is either the OID of the certificate's container data
* object or of the form "PIV.<two_hexdigit_keyref>". */
static gpg_error_t
do_writecert (app_t app, ctrl_t ctrl,
const char *certrefstr,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg,
const unsigned char *cert, size_t certlen)
{
gpg_error_t err;
data_object_t dobj;
(void)ctrl;
(void)pincb; /* Not used; instead authentication is needed. */
(void)pincb_arg;
dobj = find_dobj_by_keyref (app, certrefstr);
if (!dobj || !*dobj->keyref)
return gpg_error (GPG_ERR_INV_ID);
/* FIXME: Check that the authentication has already been done. */
/* FIXME: Check that the public key parameters from the certificate
* match an already stored key. */
flush_cached_data (app, dobj->tag);
err = put_data (app->slot, dobj->tag,
(int)0x70, (size_t)certlen, cert,/* Certificate */
(int)0x71, (size_t)1, "", /* No compress */
(int)0xfe, (size_t)0, "", /* Empty LRC. */
(int)0, (size_t)0, NULL);
+ /* A PIN is not required, thus use a better error code. */
+ if (gpg_err_code (err) == GPG_ERR_BAD_PIN)
+ err = gpg_error (GPG_ERR_NO_AUTH);
if (err)
log_error ("piv: failed to write cert to %s: %s\n",
dobj->keyref, gpg_strerror (err));
return err;
}
/* Select the PIV application on the card in SLOT. This function must
* be used before any other PIV application functions. */
gpg_error_t
app_select_piv (app_t app)
{
static char const aid[] = { 0xA0, 0x00, 0x00, 0x03, 0x08, /* RID=NIST */
0x00, 0x00, 0x10, 0x00 /* PIX=PIV */ };
int slot = app->slot;
gpg_error_t err;
unsigned char *apt = NULL;
size_t aptlen;
const unsigned char *s;
size_t n;
/* Note that we select using the AID without the 2 octet version
* number. This allows for better reporting of future specs. We
* need to use the use-zero-for-P2-flag. */
err = iso7816_select_application_ext (slot, aid, sizeof aid, 0x0001,
&apt, &aptlen);
if (err)
goto leave;
app->apptype = "PIV";
app->did_chv1 = 0;
app->did_chv2 = 0;
app->did_chv3 = 0;
app->app_local = NULL;
/* Check the Application Property Template. */
if (opt.verbose)
{
/* We use a separate log_info to avoid the "DBG:" prefix. */
log_info ("piv: APT=");
log_printhex (apt, aptlen, "");
}
s = find_tlv (apt, aptlen, 0x4F, &n);
if (!s || n != 6 || memcmp (s, aid+5, 4))
{
/* The PIX does not match. */
log_error ("piv: missing or invalid DO 0x4F in APT\n");
err = gpg_error (GPG_ERR_CARD);
goto leave;
}
if (s[4] != 1 || s[5] != 0)
{
log_error ("piv: unknown PIV version %u.%u\n", s[4], s[5]);
err = gpg_error (GPG_ERR_CARD);
goto leave;
}
app->card_version = ((s[4] << 8) | s[5]);
s = find_tlv (apt, aptlen, 0x79, &n);
if (!s || n < 7)
{
log_error ("piv: missing or invalid DO 0x79 in APT\n");
err = gpg_error (GPG_ERR_CARD);
goto leave;
}
s = find_tlv (s, n, 0x4F, &n);
if (!s || n != 5 || memcmp (s, aid, 5))
{
/* The RID does not match. */
log_error ("piv: missing or invalid DO 0x79.4F in APT\n");
err = gpg_error (GPG_ERR_CARD);
goto leave;
}
app->app_local = xtrycalloc (1, sizeof *app->app_local);
if (!app->app_local)
{
err = gpg_error_from_syserror ();
goto leave;
}
if (app->cardtype && !strcmp (app->cardtype, "yubikey"))
app->app_local->flags.yubikey = 1;
/* FIXME: Parse the optional and conditional DOs in the APT. */
if (opt.verbose)
dump_all_do (slot);
app->fnc.deinit = do_deinit;
app->fnc.learn_status = do_learn_status;
app->fnc.readcert = do_readcert;
app->fnc.readkey = do_readkey;
app->fnc.getattr = do_getattr;
app->fnc.setattr = do_setattr;
app->fnc.writecert = do_writecert;
/* app->fnc.writekey = do_writekey; */
app->fnc.genkey = do_genkey;
app->fnc.sign = do_sign;
app->fnc.auth = do_auth;
/* app->fnc.decipher = do_decipher; */
app->fnc.change_pin = do_change_chv;
app->fnc.check_pin = do_check_chv;
leave:
xfree (apt);
if (err)
do_deinit (app);
return err;
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Feb 26, 6:43 PM (6 h, 7 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
f5/63/d8e805bb149eda6c02843b8e107f
Attached To
rG GnuPG
Event Timeline
Log In to Comment