diff --git a/tools/card-tool-misc.c b/tools/card-tool-misc.c index 0f5fcc0a0..06fcb6705 100644 --- a/tools/card-tool-misc.c +++ b/tools/card-tool-misc.c @@ -42,3 +42,38 @@ find_kinfo (card_info_t info, const char *keyref) return kinfo; return NULL; } + + +/* Convert STRING into a newly allocated buffer while translating the + * hex numbers. Blanks and colons are allowed to separate pairs of + * hex digits. Returns NULL on error or a newly malloced buffer and + * its length in LENGTH. */ +void * +hex_to_buffer (const char *string, size_t *r_length) +{ + unsigned char *buffer; + const char *s; + size_t n; + + buffer = xtrymalloc (strlen (string)+1); + if (!buffer) + return NULL; + for (s=string, n=0; *s; s++) + { + if (ascii_isspace (*s) || *s == ':') + continue; + if (hexdigitp (s) && hexdigitp (s+1)) + { + buffer[n++] = xtoi_2 (s); + s++; + } + else + { + xfree (buffer); + gpg_err_set_errno (EINVAL); + return NULL; + } + } + *r_length = n; + return buffer; +} diff --git a/tools/card-tool.h b/tools/card-tool.h index 05d6ea47d..bea618a8c 100644 --- a/tools/card-tool.h +++ b/tools/card-tool.h @@ -189,6 +189,7 @@ gpg_error_t test_get_matching_keys (const char *hexgrip); /*-- card-tool-misc.c --*/ key_info_t find_kinfo (card_info_t info, const char *keyref); +void *hex_to_buffer (const char *string, size_t *r_length); /*-- card-call-scd.c --*/ diff --git a/tools/gpg-card-tool.c b/tools/gpg-card-tool.c index 07b8bc67b..3145229ad 100644 --- a/tools/gpg-card-tool.c +++ b/tools/gpg-card-tool.c @@ -326,8 +326,11 @@ main (int argc, char **argv) /* Read data from file FNAME up to MAX_GET_DATA_FROM_FILE characters. * On error return an error code and stores NULL at R_BUFFER; on - * success returns 0, stpres the number of bytes read at R_BUFLEN and - * the address of a newly allocated buffer at R_BUFFER. */ + * success returns 0 and stores the number of bytes read at R_BUFLEN + * and the address of a newly allocated buffer at R_BUFFER. A + * complementary nul byte is always appended to the data but not + * counted; this allows to pass NULL for R-BUFFER and consider the + * returned data as a string. */ static gpg_error_t get_data_from_file (const char *fname, char **r_buffer, size_t *r_buflen) { @@ -337,7 +340,8 @@ get_data_from_file (const char *fname, char **r_buffer, size_t *r_buflen) int n; *r_buffer = NULL; - *r_buflen = 0; + if (r_buflen) + *r_buflen = 0; fp = es_fopen (fname, "rb"); if (!fp) @@ -356,7 +360,7 @@ get_data_from_file (const char *fname, char **r_buffer, size_t *r_buflen) return err; } - n = es_fread (data, 1, MAX_GET_DATA_FROM_FILE, fp); + n = es_fread (data, 1, MAX_GET_DATA_FROM_FILE - 1, fp); es_fclose (fp); if (n < 0) { @@ -365,8 +369,11 @@ get_data_from_file (const char *fname, char **r_buffer, size_t *r_buflen) xfree (data); return err; } + data[n] = 0; + *r_buffer = data; - *r_buflen = n; + if (r_buflen) + *r_buflen = n; return 0; } @@ -951,6 +958,73 @@ cmd_verify (card_info_t info, char *argstr) } +static gpg_error_t +cmd_authenticate (card_info_t info, char *argstr) +{ + gpg_error_t err; + int opt_setkey; + int opt_raw; + char *string = NULL; + char *key = NULL; + size_t keylen; + + if (!info) + return print_help + ("AUTHENTICATE [--setkey] [--raw] [< FILE]|KEY\n\n" + "Perform a mutual autentication either by reading the key\n" + "from FILE or by taking it from the command line. Without\n" + "the option --raw the key is expected to be hex encoded.\n" + "To install a new administration key --setkey is used; this\n" + "requires a prior authentication with the old key.", + APP_TYPE_PIV, 0); + + if (info->apptype != APP_TYPE_PIV) + { + log_info ("Note: This is a PIV only command.\n"); + return gpg_error (GPG_ERR_NOT_SUPPORTED); + } + + opt_setkey = has_leading_option (argstr, "--setkey"); + opt_raw = has_leading_option (argstr, "--raw"); + argstr = skip_options (argstr); + + if (*argstr == '<') /* Read key from a file. */ + { + for (argstr++; spacep (argstr); argstr++) + ; + err = get_data_from_file (argstr, &string, NULL); + if (err) + goto leave; + } + + if (opt_raw) + { + key = string? string : xstrdup (argstr); + string = NULL; + keylen = strlen (key); + } + else + { + key = hex_to_buffer (string? string: argstr, &keylen); + if (!key) + { + err = gpg_error_from_syserror (); + goto leave; + } + } + err = scd_setattr (opt_setkey? "SET-ADM-KEY":"AUTH-ADM-KEY", key, keylen); + + leave: + if (key) + { + wipememory (key, keylen); + xfree (key); + } + xfree (string); + return err; +} + + /* Helper for cmd_name to qyery a part of name. */ static char * ask_one_name (const char *prompt) @@ -2610,7 +2684,7 @@ enum cmdids cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSALUT, cmdCAFPR, cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT, cmdREADCERT, cmdUNBLOCK, cmdFACTORYRESET, cmdKDFSETUP, - cmdKEYATTR, cmdUIF, + cmdKEYATTR, cmdUIF, cmdAUTHENTICATE, cmdINVCMD }; @@ -2641,6 +2715,7 @@ static struct { "passwd" , cmdPASSWD, 0, N_("menu to change or unblock the PIN")}, { "verify" , cmdVERIFY, 0, N_("verify the PIN and list all data")}, { "unblock" , cmdUNBLOCK,0, N_("unblock the PIN using a Reset Code")}, + { "authenticate",cmdAUTHENTICATE, 0,N_("authenticate to the card")}, { "reset" , cmdRESET, 0, N_("send a reset to the card daemon")}, { "factory-reset", cmdFACTORYRESET, 1, N_("destroy all keys and data")}, { "kdf-setup", cmdKDFSETUP, 1, N_("setup KDF for PIN authentication")}, @@ -2871,6 +2946,7 @@ interactive_loop (void) if (!err) redisplay = 1; break; + case cmdAUTHENTICATE: err = cmd_authenticate (info, argstr); break; case cmdNAME: err = cmd_name (info, argstr); break; case cmdURL: err = cmd_url (info, argstr); break; case cmdFETCH: err = cmd_fetch (info); break;