diff --git a/.gitignore b/.gitignore index 9e7d978..baea115 100644 --- a/.gitignore +++ b/.gitignore @@ -3,9 +3,6 @@ Makefile Makefile.in aclocal.m4 autom4te.cache/ -cmdline.c -cmdline.h -cmdline.o config.log config.status configure @@ -15,12 +12,11 @@ m4/ltoptions.m4 m4/ltsugar.m4 m4/ltversion.m4 m4/lt~obsolete.m4 -yubico-piv-tool -yubico-piv-tool.1 -yubico-piv-tool.o *.c~ *.h~ ChangeLog +build-aux/ar-lib +build-aux/compile build-aux/config.guess build-aux/config.sub build-aux/depcomp @@ -38,6 +34,42 @@ yubico-piv-tool-*.tar.gz yubico-piv-tool-*.tar.gz.sig yubico-piv-tool-*-mac.zip yubico-piv-tool-*-mac.zip.sig -tests/basic.sh.log -tests/basic.sh.trs -tests/test-suite.log +lib/tests/basic +lib/tests/basic.log +lib/tests/basic.o +lib/tests/basic.trs +lib/tests/parse_key +lib/tests/parse_key.log +lib/tests/parse_key.o +lib/tests/parse_key.trs +lib/tests/test-suite.log +lib/error.lo +lib/error.o +lib/libykpiv.la +lib/version.lo +lib/version.o +lib/ykpiv-version.h +lib/ykpiv.lo +lib/ykpiv.o +lib/ykpiv.pc +lib/.libs/ +tool/cmdline.c +tool/cmdline.h +tool/cmdline.o +tool/yubico-piv-tool +tool/yubico-piv-tool.1 +tool/yubico-piv-tool.o +tool/.libs/ +tool/libpiv_cmd.la +tool/libpiv_cmd_la-cmdline.lo +tool/libpiv_cmd_la-cmdline.o +tool/libpiv_util.la +tool/util.lo +tool/util.o +tool/tests/basic.sh.log +tool/tests/basic.sh.trs +tool/tests/test-suite.log +tool/tests/parse_name +tool/tests/parse_name.log +tool/tests/parse_name.o +tool/tests/parse_name.trs diff --git a/Makefile.am b/Makefile.am index 3f688eb..0f442c2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -24,39 +24,12 @@ # non-source form of such a combination shall include the source code # for the parts of OpenSSL used as well as that of the covered work. -SUBDIRS = . tests - -AM_CFLAGS = $(WERROR_CFLAGS) $(WARN_CFLAGS) -AM_CPPFLAGS = $(OPENSSL_CFLAGS) $(PCSC_CFLAGS) +SUBDIRS = lib tool ACLOCAL_AMFLAGS = -I m4 -bin_PROGRAMS = yubico-piv-tool -yubico_piv_tool_SOURCES = yubico-piv-tool.c yubico-piv-tool.h2m -yubico_piv_tool_SOURCES += cmdline.ggo cmdline.c cmdline.h -yubico_piv_tool_LDADD = $(OPENSSL_LIBS) $(PCSC_LIBS) -yubico_piv_tool_LDADD += $(LTLIBWINSCARD) $(PCSC_MACOSX_LIBS) - -cmdline.c cmdline.h: cmdline.ggo Makefile.am - gengetopt --input $^ - -BUILT_SOURCES = cmdline.c cmdline.h -MAINTAINERCLEANFILES = $(BUILT_SOURCES) - -# Doc. - -dist_man_MANS = yubico-piv-tool.1 -MAINTAINERCLEANFILES += $(dist_man_MANS) - EXTRA_DIST = windows.mk mac.mk tests/basic.sh -yubico-piv-tool.1: $(yubico_piv_tool_SOURCES) \ - $(top_srcdir)/configure.ac - $(HELP2MAN) --no-info \ - --name="Yubico PIV tool" \ - --include=$(top_srcdir)/yubico-piv-tool.h2m \ - --output=$@ $(top_builddir)/yubico-piv-tool$(EXEEXT) - # Maintainer rules. ChangeLog: diff --git a/NEWS b/NEWS index 02ec01f..6b38415 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,6 @@ yubico-piv-tool NEWS -- History of user-visible changes. -*- outline -*- -* Version 0.0.4 (unreleased) +* Version 0.1.0 (unreleased) * Version 0.0.3 (released 2014-05-26) diff --git a/configure.ac b/configure.ac index f231157..80e04d6 100644 --- a/configure.ac +++ b/configure.ac @@ -24,21 +24,33 @@ # non-source form of such a combination shall include the source code # for the parts of OpenSSL used as well as that of the covered work. -AC_INIT([yubico-piv-tool], [0.0.4]) +AC_INIT([yubico-piv-tool], [0.1.0]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) +# Library code modified: REVISION++ +# Interfaces changed/added/removed: CURRENT++ REVISION=0 +# Interfaces added: AGE++ +# Interfaces removed: AGE=0 +AC_SUBST([LT_CURRENT], 0) +AC_SUBST([LT_REVISION], 0) +AC_SUBST([LT_AGE], 0) + AM_INIT_AUTOMAKE([-Wall -Werror foreign]) AM_SILENT_RULES([yes]) AC_PROG_CC +m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) AC_LIBTOOL_WIN32_DLL AC_PROG_LIBTOOL AM_MISSING_PROG(HELP2MAN, help2man, $missing_dir) +AM_MISSING_PROG(GENGETOPT, gengetopt, $missing_dir) PKG_PROG_PKG_CONFIG PKG_CHECK_MODULES(OPENSSL, openssl) +gl_LD_VERSION_SCRIPT + AC_ARG_WITH([backend], [AS_HELP_STRING([--with-backend=ARG], [use specific backend/linkage; 'pcsc', 'macscard' or 'winscard'])], @@ -124,6 +136,7 @@ if test "$gl_gcc_warnings" = yes; then nw="$nw -Wtraditional-conversion" # Too many warnings for now nw="$nw -Wconversion" # Too many warnings for now nw="$nw -Wsuggest-attribute=pure" # Is it worth using attributes? + nw="$nw -Wsuggest-attribute=const" # Is it worth using attributes? gl_MANYWARN_ALL_GCC([ws]) gl_MANYWARN_COMPLEMENT(ws, [$ws], [$nw]) @@ -136,7 +149,12 @@ fi AC_CONFIG_FILES([ Makefile - tests/Makefile + lib/Makefile + lib/tests/Makefile + tool/Makefile + tool/tests/Makefile + lib/ykpiv-version.h + lib/ykpiv.pc ]) AC_OUTPUT diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 0000000..7a02a82 --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,53 @@ +# Copyright (c) 2014 Yubico AB +# All rights reserved. +# +# This program 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. +# +# This program 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 . +# +# Additional permission under GNU GPL version 3 section 7 +# +# If you modify this program, or any covered work, by linking or +# combining it with the OpenSSL project's OpenSSL library (or a +# modified version of that library), containing parts covered by the +# terms of the OpenSSL or SSLeay licenses, We grant you additional +# permission to convey the resulting work. Corresponding Source for a +# non-source form of such a combination shall include the source code +# for the parts of OpenSSL used as well as that of the covered work. + +SUBDIRS = . tests + +AM_CFLAGS = $(WERROR_CFLAGS) $(WARN_CFLAGS) +AM_CPPFLAGS = $(OPENSSL_CFLAGS) $(PCSC_CFLAGS) + +lib_LTLIBRARIES = libykpiv.la + +libykpiv_la_SOURCES = ykpiv.c version.c ykpiv.pc.in ykpiv.map internal.h +libykpiv_la_SOURCES += error.c +libykpiv_la_includedir = $(includedir)/ykpiv +libykpiv_la_include_HEADERS = ykpiv.h ykpiv-version.h +EXTRA_libykpiv_la_DEPENDENCIES = ykpiv.map + +libykpiv_la_LIBADD = $(OPENSSL_LIBS) $(PCSC_LIBS) +libykpiv_la_LIBADD += $(LTLIBWINSCARD) $(PCSC_MACOSX_LIBS) + +libykpiv_la_LDFLAGS = -no-undefined +libykpiv_la_LDFLAGS += -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) + +if HAVE_LD_VERSION_SCRIPT +libykpiv_la_LDFLAGS += -Wl,--version-script=$(srcdir)/ykpiv.map +else +libykpiv_la_LDFLAGS += -export-symbols-regex '^ykpiv_.*' +endif + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = ykpiv.pc diff --git a/lib/error.c b/lib/error.c new file mode 100644 index 0000000..1f8952a --- /dev/null +++ b/lib/error.c @@ -0,0 +1,105 @@ + /* + * Copyright (c) 2014 Yubico AB + * All rights reserved. + * + * This program 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. + * + * This program 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 . + * + * Additional permission under GNU GPL version 3 section 7 + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, We grant you additional + * permission to convey the resulting work. Corresponding Source for a + * non-source form of such a combination shall include the source code + * for the parts of OpenSSL used as well as that of the covered work. + * + */ + +#include "ykpiv.h" + +#include + +#define ERR(name, desc) { name, #name, desc } + +typedef struct +{ + ykpiv_rc rc; + const char *name; + const char *description; +} err_t; + +static const err_t errors[] = { + ERR (YKPIV_OK, "Successful return"), + ERR (YKPIV_MEMORY_ERROR, "Error allocating memory"), + ERR (YKPIV_PCSC_ERROR, "Error in PCSC call"), + ERR (YKPIV_SIZE_ERROR, "Wrong buffer size"), + ERR (YKPIV_APPLET_ERROR, "No PIV applet found"), + ERR (YKPIV_AUTHENTICATION_ERROR, "Error during authentication"), + ERR (YKPIV_RANDOMNESS_ERROR, "Error getting randomness"), + ERR (YKPIV_GENERIC_ERROR, "Something went wrong."), + ERR (YKPIV_KEY_ERROR, "Error in key"), + ERR (YKPIV_PARSE_ERROR, "Parse error"), + ERR (YKPIV_WRONG_PIN, "Wrong PIN code"), + ERR (YKPIV_INVALID_OBJECT, "Object invalid"), + ERR (YKPIV_ALGORITHM_ERROR, "Algorithm error"), +}; + +/** + * ykpiv_strerror: + * @err: error code + * + * Convert return code to human readable string explanation of the + * reason for the particular error code. + * + * This string can be used to output a diagnostic message to the user. + * + * Return value: Returns a pointer to a statically allocated string + * containing an explanation of the error code @err. + **/ +const char *ykpiv_strerror(ykpiv_rc err) { + static const char *unknown = "Unknown ykpiv error"; + const char *p; + + if (-err < 0 || -err >= (int) (sizeof (errors) / sizeof (errors[0]))) + return unknown; + + p = errors[-err].description; + if (!p) + p = unknown; + + return p; +} + + +/** + * ykpiv_strerror_name: + * @err: error code + * + * Convert return code to human readable string representing the error + * code symbol itself. For example, ykpiv_strerror_name(%YKPIV_OK) + * returns the string "YKPIV_OK". + * + * This string can be used to output a diagnostic message to the user. + * + * Return value: Returns a pointer to a statically allocated string + * containing a string version of the error code @err, or NULL if + * the error code is not known. + **/ +const char *ykpiv_strerror_name(ykpiv_rc err) { + if (-err < 0 || -err >= (int) (sizeof (errors) / sizeof (errors[0]))) + return NULL; + + return errors[-err].name; +} diff --git a/lib/internal.h b/lib/internal.h new file mode 100644 index 0000000..b506233 --- /dev/null +++ b/lib/internal.h @@ -0,0 +1,68 @@ + /* + * Copyright (c) 2014 Yubico AB + * All rights reserved. + * + * This program 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. + * + * This program 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 . + * + * Additional permission under GNU GPL version 3 section 7 + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, We grant you additional + * permission to convey the resulting work. Corresponding Source for a + * non-source form of such a combination shall include the source code + * for the parts of OpenSSL used as well as that of the covered work. + * + */ + +#ifndef YKPIV_INTERNAL_H +#define YKPIV_INTERNAL_H + +#include + +#if BACKEND_PCSC +#if defined HAVE_PCSC_WINSCARD_H +# include +# include +#else +# include +#endif +#endif + +struct ykpiv_state { + SCARDCONTEXT context; + SCARDHANDLE card; + int verbose; +}; + +union u_APDU { + struct { + unsigned char cla; + unsigned char ins; + unsigned char p1; + unsigned char p2; + unsigned char lc; + unsigned char data[0xff]; + } st; + unsigned char raw[0xff + 5]; +}; + +typedef union u_APDU APDU; + +unsigned const char aid[] = { + 0xa0, 0x00, 0x00, 0x03, 0x08 +}; + +#endif diff --git a/tests/Makefile.am b/lib/tests/Makefile.am similarity index 86% rename from tests/Makefile.am rename to lib/tests/Makefile.am index 9eaf2e7..6fefb8f 100644 --- a/tests/Makefile.am +++ b/lib/tests/Makefile.am @@ -24,6 +24,11 @@ # non-source form of such a combination shall include the source code # for the parts of OpenSSL used as well as that of the covered work. -TESTS = basic.sh +AM_CFLAGS = $(WARN_CFLAGS) +AM_CPPFLAGS = -I$(top_srcdir)/lib -I$(top_builddir)/lib -TESTS_ENVIRONMENT = export VERSION=$(VERSION); export EXEEXT=$(EXEEXT); +AM_LDFLAGS = -no-install +LDADD = ../libykpiv.la + +check_PROGRAMS = basic parse_key +TESTS = $(check_PROGRAMS) diff --git a/lib/tests/basic.c b/lib/tests/basic.c new file mode 100644 index 0000000..b7c64ae --- /dev/null +++ b/lib/tests/basic.c @@ -0,0 +1,74 @@ + /* + * Copyright (c) 2014 Yubico AB + * All rights reserved. + * + * This program 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. + * + * This program 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 . + * + * Additional permission under GNU GPL version 3 section 7 + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, We grant you additional + * permission to convey the resulting work. Corresponding Source for a + * non-source form of such a combination shall include the source code + * for the parts of OpenSSL used as well as that of the covered work. + * + */ + +#include "ykpiv.h" + +#include +#include +#include +#include + +int main(void) +{ + if(strcmp(YKPIV_VERSION_STRING, ykpiv_check_version (NULL)) != 0) { + printf("version mismatch %s != %s\n", YKPIV_VERSION_STRING, + ykpiv_check_version(NULL)); + return EXIT_FAILURE; + } + + if(ykpiv_check_version(YKPIV_VERSION_STRING) == NULL) { + printf("version NULL?\n"); + return EXIT_FAILURE; + } + + if(ykpiv_check_version("99.99.99") != NULL) { + printf ("version not NULL?\n"); + return EXIT_FAILURE; + } + + printf ("ykpiv version: header %s library %s\n", + YKPIV_VERSION_STRING, ykpiv_check_version (NULL)); + + + if(ykpiv_strerror(YKPIV_OK) == NULL) { + printf ("ykpiv_strerror NULL\n"); + return EXIT_FAILURE; + } + + { + const char *s; + s = ykpiv_strerror_name(YKPIV_OK); + if(s == NULL || strcmp(s, "YKPIV_OK") != 0) { + printf("ykpiv_strerror_name %s\n", s); + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +} diff --git a/lib/tests/parse_key.c b/lib/tests/parse_key.c new file mode 100644 index 0000000..564352d --- /dev/null +++ b/lib/tests/parse_key.c @@ -0,0 +1,91 @@ + /* + * Copyright (c) 2014 Yubico AB + * All rights reserved. + * + * This program 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. + * + * This program 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 . + * + * Additional permission under GNU GPL version 3 section 7 + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, We grant you additional + * permission to convey the resulting work. Corresponding Source for a + * non-source form of such a combination shall include the source code + * for the parts of OpenSSL used as well as that of the covered work. + * + */ + +#include +#include +#include + +#include "ykpiv.h" + +struct key { + const char text[49]; + const unsigned char formatted[24]; + int valid; +}; + +struct key keys[] = { + {"010203040506070801020304050607080102030405060708", + {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}, + 1}, + {"a1a2a3a4a5a6a7a8a1a2a3a4a5a6a7a8a1a2a3a4a5a6a7a8", + {0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8,0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8}, + 1} +}; + +static int parse_key(ykpiv_state *state, const char *text, const unsigned char *expected, int valid) { + unsigned char key[24]; + ykpiv_rc res = ykpiv_parse_key(state, text, key); + if(res != YKPIV_OK && valid == 1) { + printf("key check failed for %s!\n", text); + return EXIT_FAILURE; + } else if(res != YKPIV_OK && valid == 0) { + return EXIT_SUCCESS; + } + + if(memcmp(expected, key, 24) != 0) { + printf("keys not matching for %s!\n", text); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +int main(void) { + ykpiv_state *state; + size_t i; + + if(ykpiv_init(&state, 0) != YKPIV_OK) { + printf("Failed initializing library!\n"); + return EXIT_FAILURE; + } + + for(i = 0; i < sizeof(keys) / sizeof(struct key); i++) { + int res = parse_key(state, keys[i].text, keys[i].formatted, keys[i].valid); + if(res != EXIT_SUCCESS) { + return res; + } + } + + if(ykpiv_done(state) != YKPIV_OK) { + printf("Failed de-initializing library!\n"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/lib/version.c b/lib/version.c new file mode 100644 index 0000000..50071d1 --- /dev/null +++ b/lib/version.c @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2014 Yubico AB + * All rights reserved. + * + * This program 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. + * + * This program 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 . + * + * Additional permission under GNU GPL version 3 section 7 + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, We grant you additional + * permission to convey the resulting work. Corresponding Source for a + * non-source form of such a combination shall include the source code + * for the parts of OpenSSL used as well as that of the covered work. + * + */ + +#include "ykpiv-version.h" + +#include + +#include + +/* From http://article.gmane.org/gmane.os.freebsd.devel.hackers/23606 */ +static int my_strverscmp (const char *s1, const char *s2) +{ + static const char *digits = "0123456789"; + int ret, lz1, lz2; + size_t p1, p2; + + p1 = strcspn (s1, digits); + p2 = strcspn (s2, digits); + while (p1 == p2 && s1[p1] != '\0' && s2[p2] != '\0') { + /* Different prefix */ + if ((ret = strncmp (s1, s2, p1)) != 0) + return ret; + + s1 += p1; + s2 += p2; + + lz1 = lz2 = 0; + if (*s1 == '0') + lz1 = 1; + if (*s2 == '0') + lz2 = 1; + + if (lz1 > lz2) + return -1; + else if (lz1 < lz2) + return 1; + else if (lz1 == 1) { + /* + * If the common prefix for s1 and s2 consists only of zeros, then the + * "longer" number has to compare less. Otherwise the comparison needs + * to be numerical (just fallthrough). See + * http://refspecs.freestandards.org/LSB_2.0.1/LSB-generic/ + * LSB-generic/baselib-strverscmp.html + */ + while (*s1 == '0' && *s2 == '0') { + ++s1; + ++s2; + } + + p1 = strspn (s1, digits); + p2 = strspn (s2, digits); + + /* Catch empty strings */ + if (p1 == 0 && p2 > 0) + return 1; + else if (p2 == 0 && p1 > 0) + return -1; + + /* Prefixes are not same */ + if (*s1 != *s2 && *s1 != '0' && *s2 != '0') { + if (p1 < p2) + return 1; + else if (p1 > p2) + return -1; + } else { + if (p1 < p2) + ret = strncmp (s1, s2, p1); + else if (p1 > p2) + ret = strncmp (s1, s2, p2); + if (ret != 0) + return ret; + } + } + + p1 = strspn (s1, digits); + p2 = strspn (s2, digits); + + if (p1 < p2) + return -1; + else if (p1 > p2) + return 1; + else if ((ret = strncmp (s1, s2, p1)) != 0) + return ret; + + /* Numbers are equal or not present, try with next ones. */ + s1 += p1; + s2 += p2; + p1 = strcspn (s1, digits); + p2 = strcspn (s2, digits); + } + + return strcmp (s1, s2); +} + +/** + * ykpiv_check_version: + * @req_version: Required version number, or NULL. + * + * Check that the version of the library is at minimum the requested + * one and return the version string; return NULL if the condition is + * not satisfied. If a NULL is passed to this function, no check is + * done, but the version string is simply returned. + * + * See %YKPIV_VERSION_STRING for a suitable @req_version string. + * + * Return value: Version string of run-time library, or NULL if the + * run-time library does not meet the required version number. + */ +const char * ykpiv_check_version (const char *req_version) +{ + if (!req_version + || my_strverscmp (req_version, YKPIV_VERSION_STRING) <= 0) + return YKPIV_VERSION_STRING; + + return NULL; +} diff --git a/lib/ykpiv-version.h.in b/lib/ykpiv-version.h.in new file mode 100644 index 0000000..acf8af5 --- /dev/null +++ b/lib/ykpiv-version.h.in @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2014 Yubico AB + * All rights reserved. + * + * This program 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. + * + * This program 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 . + * + * Additional permission under GNU GPL version 3 section 7 + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, We grant you additional + * permission to convey the resulting work. Corresponding Source for a + * non-source form of such a combination shall include the source code + * for the parts of OpenSSL used as well as that of the covered work. + * + */ + +#ifndef YKPIV_VERSION_H +#define YKPIV_VERSION_H + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * YKPIV_VERSION_STRING + * + * Pre-processor symbol with a string that describe the header file + * version number. Used together with ykneomgr_check_version() to verify + * header file and run-time library consistency. + */ +#define YKPIV_VERSION_STRING "@VERSION@" + + /** + * YKPIV_VERSION_NUMBER + * + * Pre-processor symbol with a hexadecimal value describing the header + * file version number. For example, when the header version is 1.2.3 + * this symbol will have the value 0x01020300. The last two digits + * are only used between public releases, and will otherwise be 00. + */ +#define YKPIV_VERSION_NUMBER @YKPIV_VERSION_NUMBER@ + + /** + * YKPIV_VERSION_MAJOR + * + * Pre-processor symbol with a decimal value that describe the major + * level of the header file version number. For example, when the + * header version is 1.2.3 this symbol will be 1. + */ +#define YKPIV_VERSION_MAJOR @YKPIV_VERSION_MAJOR@ + + /** + * YKPIV_VERSION_MINOR + * + * Pre-processor symbol with a decimal value that describe the minor + * level of the header file version number. For example, when the + * header version is 1.2.3 this symbol will be 2. + */ +#define YKPIV_VERSION_MINOR @YKPIV_VERSION_MINOR@ + + /** + * YKPIV_VERSION_PATCH + * + * Pre-processor symbol with a decimal value that describe the patch + * level of the header file version number. For example, when the + * header version is 1.2.3 this symbol will be 3. + */ +#define YKPIV_VERSION_PATCH @YKPIV_VERSION_PATCH@ + + const char *ykpiv_check_version (const char *req_version); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/ykpiv.c b/lib/ykpiv.c new file mode 100644 index 0000000..9cda463 --- /dev/null +++ b/lib/ykpiv.c @@ -0,0 +1,709 @@ + /* + * Copyright (c) 2014 Yubico AB + * All rights reserved. + * + * This program 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. + * + * This program 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 . + * + * Additional permission under GNU GPL version 3 section 7 + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, We grant you additional + * permission to convey the resulting work. Corresponding Source for a + * non-source form of such a combination shall include the source code + * for the parts of OpenSSL used as well as that of the covered work. + * + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "internal.h" +#include "ykpiv.h" + +static ykpiv_rc send_data(ykpiv_state *state, APDU *apdu, + unsigned char *data, unsigned long *recv_len, int *sw); + +static void dump_hex(const unsigned char *buf, unsigned int len) { + unsigned int i; + for (i = 0; i < len; i++) { + fprintf(stderr, "%02x ", buf[i]); + } +} + +static int set_length(unsigned char *buffer, size_t length) { + if(length < 0x80) { + *buffer++ = length; + return 1; + } else if(length < 0xff) { + *buffer++ = 0x81; + *buffer++ = length; + return 2; + } else { + *buffer++ = 0x82; + *buffer++ = (length >> 8) & 0xff; + *buffer++ = length & 0xff; + return 3; + } +} + +static int get_length(const unsigned char *buffer, size_t *len) { + if(buffer[0] < 0x81) { + *len = buffer[0]; + return 1; + } else if((*buffer & 0x7f) == 1) { + *len = buffer[1]; + return 2; + } else if((*buffer & 0x7f) == 2) { + size_t tmp = buffer[1]; + *len = (tmp << 8) + buffer[2]; + return 3; + } + return 0; +} + +static unsigned char *set_object(int object_id, unsigned char *buffer) { + *buffer++ = 0x5c; + if(object_id == YKPIV_OBJ_DISCOVERY) { + *buffer++ = 1; + *buffer++ = YKPIV_OBJ_DISCOVERY; + } else if(object_id > 0xffff && object_id <= 0xffffff) { + *buffer++ = 3; + *buffer++ = (object_id >> 16) & 0xff; + *buffer++ = (object_id >> 8) & 0xff; + *buffer++ = object_id & 0xff; + } + return buffer; +} + +ykpiv_rc ykpiv_init(ykpiv_state **state, int verbose) { + ykpiv_state *s = malloc(sizeof(ykpiv_state)); + if(s == NULL) { + return YKPIV_MEMORY_ERROR; + } + memset(s, 0, sizeof(ykpiv_state)); + s->verbose = verbose; + *state = s; + return YKPIV_OK; +} + +ykpiv_rc ykpiv_done(ykpiv_state *state) { + ykpiv_disconnect(state); + free(state); + return YKPIV_OK; +} + +ykpiv_rc ykpiv_disconnect(ykpiv_state *state) { + if(state->card) { + SCardDisconnect(state->card, SCARD_RESET_CARD); + state->card = 0; + } + + if(state->context) { + SCardReleaseContext(state->context); + state->context = 0; + } + + return YKPIV_OK; +} + +ykpiv_rc ykpiv_connect(ykpiv_state *state, const char *wanted) { + unsigned long num_readers = 0; + unsigned long active_protocol; + char reader_buf[1024]; + long rc; + char *reader_ptr; + + rc = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &state->context); + if (rc != SCARD_S_SUCCESS) { + if(state->verbose) { + fprintf (stderr, "error: SCardEstablishContext failed, rc=%08lx\n", rc); + } + return YKPIV_PCSC_ERROR; + } + + rc = SCardListReaders(state->context, NULL, NULL, &num_readers); + if (rc != SCARD_S_SUCCESS) { + if(state->verbose) { + fprintf (stderr, "error: SCardListReaders failed, rc=%08lx\n", rc); + } + SCardReleaseContext(state->context); + return YKPIV_PCSC_ERROR; + } + + if (num_readers > sizeof(reader_buf)) { + num_readers = sizeof(reader_buf); + } + + rc = SCardListReaders(state->context, NULL, reader_buf, &num_readers); + if (rc != SCARD_S_SUCCESS) + { + if(state->verbose) { + fprintf (stderr, "error: SCardListReaders failed, rc=%08lx\n", rc); + } + SCardReleaseContext(state->context); + return YKPIV_PCSC_ERROR; + } + + reader_ptr = reader_buf; + if(wanted) { + while(*reader_ptr != '\0') { + if(strstr(reader_ptr, wanted)) { + if(state->verbose) { + fprintf(stderr, "using reader '%s' matching '%s'.\n", reader_ptr, wanted); + } + break; + } else { + if(state->verbose) { + fprintf(stderr, "skipping reader '%s' since it doesn't match.\n", reader_ptr); + } + reader_ptr += strlen(reader_ptr) + 1; + } + } + } + if(*reader_ptr == '\0') { + if(state->verbose) { + fprintf(stderr, "error: no useable reader found.\n"); + } + SCardReleaseContext(state->context); + return YKPIV_PCSC_ERROR; + } + + rc = SCardConnect(state->context, reader_ptr, SCARD_SHARE_SHARED, + SCARD_PROTOCOL_T1, &state->card, &active_protocol); + if(rc != SCARD_S_SUCCESS) + { + if(state->verbose) { + fprintf(stderr, "error: SCardConnect failed, rc=%08lx\n", rc); + } + SCardReleaseContext(state->context); + return YKPIV_PCSC_ERROR; + } + + { + APDU apdu; + unsigned char data[0xff]; + unsigned long recv_len = sizeof(data); + int sw; + ykpiv_rc res; + + memset(apdu.raw, 0, sizeof(apdu)); + apdu.st.ins = 0xa4; + apdu.st.p1 = 0x04; + apdu.st.lc = sizeof(aid); + memcpy(apdu.st.data, aid, sizeof(aid)); + + if((res = send_data(state, &apdu, data, &recv_len, &sw) != YKPIV_OK)) { + return res; + } else if(sw == 0x9000) { + return YKPIV_OK; + } + + return YKPIV_APPLET_ERROR; + } + + return YKPIV_OK; +} + +ykpiv_rc ykpiv_transfer_data(ykpiv_state *state, const unsigned char *templ, + const unsigned char *in_data, long in_len, + unsigned char *out_data, unsigned long *out_len, int *sw) { + const unsigned char *in_ptr = in_data; + unsigned long max_out = *out_len; + ykpiv_rc res; + *out_len = 0; + + do { + size_t this_size = 0xff; + unsigned long recv_len = 0xff; + unsigned char data[0xff]; + APDU apdu; + + memset(apdu.raw, 0, sizeof(apdu.raw)); + memcpy(apdu.raw, templ, 4); + if(in_ptr + 0xff < in_data + in_len) { + apdu.st.cla = 0x10; + } else { + this_size = (size_t)((in_data + in_len) - in_ptr); + } + if(state->verbose > 2) { + fprintf(stderr, "Going to send %lu bytes in this go.\n", (unsigned long)this_size); + } + apdu.st.lc = this_size; + memcpy(apdu.st.data, in_ptr, this_size); + res = send_data(state, &apdu, data, &recv_len, sw); + if(res != YKPIV_OK) { + return res; + } else if(*sw != 0x9000 && *sw >> 8 != 0x61) { + return YKPIV_OK; + } + if(*out_len + recv_len - 2 > max_out) { + if(state->verbose) { + fprintf(stderr, "Output buffer to small, wanted to write %lu, max was %lu.\n", *out_len + recv_len - 2, max_out); + } + return YKPIV_SIZE_ERROR; + } + memcpy(out_data, data, recv_len - 2); + out_data += recv_len - 2; + *out_len += recv_len - 2; + in_ptr += this_size; + } while(in_ptr < in_data + in_len); + while(*sw >> 8 == 0x61) { + APDU apdu; + unsigned long recv_len = 0xff; + unsigned char data[0xff]; + + if(state->verbose > 2) { + fprintf(stderr, "The card indicates there is %d bytes more data for us.\n", *sw & 0xff); + } + + memset(apdu.raw, 0, sizeof(apdu.raw)); + apdu.st.ins = 0xc0; + res = send_data(state, &apdu, data, &recv_len, sw); + if(res != YKPIV_OK) { + return res; + } else if(*sw != 0x9000 && *sw >> 8 != 0x61) { + return YKPIV_OK; + } + if(*out_len + recv_len - 2 > max_out) { + fprintf(stderr, "Output buffer to small, wanted to write %lu, max was %lu.", *out_len + recv_len - 2, max_out); + } + memcpy(out_data, data, recv_len - 2); + out_data += recv_len - 2; + *out_len += recv_len - 2; + } + return YKPIV_OK; +} + +static ykpiv_rc send_data(ykpiv_state *state, APDU *apdu, + unsigned char *data, unsigned long *recv_len, int *sw) { + long rc; + unsigned int send_len = (unsigned int)apdu->st.lc + 5; + + if(state->verbose > 1) { + fprintf(stderr, "> "); + dump_hex(apdu->raw, send_len); + fprintf(stderr, "\n"); + } + rc = SCardTransmit(state->card, SCARD_PCI_T1, apdu->raw, send_len, NULL, data, recv_len); + if(rc != SCARD_S_SUCCESS) { + if(state->verbose) { + fprintf (stderr, "error: SCardTransmit failed, rc=%08lx\n", rc); + } + return YKPIV_PCSC_ERROR; + } + + if(state->verbose > 1) { + fprintf(stderr, "< "); + dump_hex(data, *recv_len); + fprintf(stderr, "\n"); + } + if(*recv_len >= 2) { + *sw = (data[*recv_len - 2] << 8) | data[*recv_len - 1]; + } else { + *sw = 0; + } + return YKPIV_OK; +} + +ykpiv_rc ykpiv_authenticate(ykpiv_state *state, unsigned const char *key) { + APDU apdu; + unsigned char data[0xff]; + DES_cblock challenge; + unsigned long recv_len = sizeof(data); + int sw; + ykpiv_rc res; + + DES_key_schedule ks1, ks2, ks3; + + /* set up our key */ + { + const_DES_cblock key_tmp; + memcpy(key_tmp, key, 8); + DES_set_key_unchecked(&key_tmp, &ks1); + memcpy(key_tmp, key + 8, 8); + DES_set_key_unchecked(&key_tmp, &ks2); + memcpy(key_tmp, key + 16, 8); + DES_set_key_unchecked(&key_tmp, &ks3); + } + + /* get a challenge from the card */ + { + memset(apdu.raw, 0, sizeof(apdu)); + apdu.st.ins = 0x87; + apdu.st.p1 = 0x03; /* triple des */ + apdu.st.p2 = 0x9b; /* management key */ + apdu.st.lc = 0x04; + apdu.st.data[0] = 0x7c; + apdu.st.data[1] = 0x02; + apdu.st.data[2] = 0x80; + if((res = send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) { + return res; + } else if(sw != 0x9000) { + return YKPIV_AUTHENTICATION_ERROR; + } + memcpy(challenge, data + 4, 8); + } + + /* send a response to the cards challenge and a challenge of our own. */ + { + unsigned char *dataptr = apdu.st.data; + DES_cblock response; + DES_ecb3_encrypt(&challenge, &response, &ks1, &ks2, &ks3, 0); + + recv_len = 0xff; + memset(apdu.raw, 0, sizeof(apdu)); + apdu.st.ins = YKPIV_INS_AUTHENTICATE; + apdu.st.p1 = YKPIV_ALGO_3DES; /* triple des */ + apdu.st.p2 = YKPIV_KEY_CARDMGM; /* management key */ + *dataptr++ = 0x7c; + *dataptr++ = 20; /* 2 + 8 + 2 +8 */ + *dataptr++ = 0x80; + *dataptr++ = 8; + memcpy(dataptr, response, 8); + dataptr += 8; + *dataptr++ = 0x81; + *dataptr++ = 8; + if(RAND_pseudo_bytes(dataptr, 8) == -1) { + if(state->verbose) { + fprintf(stderr, "Failed getting randomness for authentication.\n"); + } + return YKPIV_RANDOMNESS_ERROR; + } + memcpy(challenge, dataptr, 8); + dataptr += 8; + apdu.st.lc = dataptr - apdu.st.data; + if((res = send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) { + return res; + } else if(sw != 0x9000) { + return YKPIV_AUTHENTICATION_ERROR; + } + } + + /* compare the response from the card with our challenge */ + { + DES_cblock response; + DES_ecb3_encrypt(&challenge, &response, &ks1, &ks2, &ks3, 1); + if(memcmp(response, data + 4, 8) == 0) { + return YKPIV_OK; + } else { + return YKPIV_AUTHENTICATION_ERROR; + } + } +} + +ykpiv_rc ykpiv_set_mgmkey(ykpiv_state *state, const unsigned char *new_key) { + APDU apdu; + unsigned char data[0xff]; + unsigned long recv_len = sizeof(data); + int sw; + size_t i; + ykpiv_rc res; + + for(i = 0; i < 3; i++) { + const_DES_cblock key_tmp; + memcpy(key_tmp, new_key + i * 8, 8); + if(DES_is_weak_key(&key_tmp) == 1) { + if(state->verbose) { + fprintf(stderr, "Won't set new key '"); + dump_hex(new_key + i, 8); + fprintf(stderr, "' since it's considered weak.\n"); + } + return YKPIV_GENERIC_ERROR; + } + } + + memset(apdu.raw, 0, sizeof(apdu)); + apdu.st.ins = YKPIV_INS_SET_MGMKEY; + apdu.st.p1 = 0xff; + apdu.st.p2 = 0xff; + apdu.st.lc = DES_KEY_SZ * 3 + 3; + apdu.st.data[0] = YKPIV_ALGO_3DES; + apdu.st.data[1] = YKPIV_KEY_CARDMGM; + apdu.st.data[2] = DES_KEY_SZ * 3; + memcpy(apdu.st.data + 3, new_key, DES_KEY_SZ * 3); + if((res = send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) { + return res; + } else if(sw == 0x9000) { + return YKPIV_OK; + } + return YKPIV_GENERIC_ERROR; +} + +ykpiv_rc ykpiv_parse_key(ykpiv_state *state, + const char *key_in, unsigned char *key_out) { + unsigned int i; + char key_part[4] = {0}; + int key_len = strlen(key_in); + + if(key_len != DES_KEY_SZ * 3 * 2) { + if(state->verbose) { + fprintf(stderr, "Wrong key size, should be %lu characters (was %d).\n", DES_KEY_SZ * 3 * 2, key_len); + } + return YKPIV_SIZE_ERROR; + } + for(i = 0; i < DES_KEY_SZ * 3; i++) { + key_part[0] = *key_in++; + key_part[1] = *key_in++; + if(sscanf(key_part, "%hhx", &key_out[i]) != 1) { + if(state->verbose) { + fprintf(stderr, "Failed parsing key at position %d.\n", i); + } + return YKPIV_KEY_ERROR; + } + } + if(state->verbose > 1) { + fprintf(stderr, "parsed key: "); + dump_hex(key_out, DES_KEY_SZ * 3); + fprintf(stderr, "\n"); + } + return YKPIV_OK; +} + +ykpiv_rc ykpiv_sign_data(ykpiv_state *state, + const unsigned char *raw_in, size_t in_len, + unsigned char *sign_out, size_t *out_len, + unsigned char algorithm, unsigned char key) { + + unsigned char indata[1024]; + unsigned char *dataptr = indata; + unsigned char data[1024]; + unsigned char templ[] = {0, YKPIV_INS_AUTHENTICATE, algorithm, key}; + unsigned long recv_len = sizeof(data); + unsigned char sign_in[256]; + size_t pad_len = 0; + int sw; + size_t bytes; + size_t len = 0; + ykpiv_rc res; + + switch(algorithm) { + case YKPIV_ALGO_RSA1024: + pad_len = 128; + case YKPIV_ALGO_RSA2048: + if(pad_len == 0) { + pad_len = 256; + } + if(in_len > pad_len) { + return YKPIV_SIZE_ERROR; + } + RSA_padding_add_PKCS1_type_1(sign_in, pad_len, raw_in, in_len); + in_len = pad_len; + break; + case YKPIV_ALGO_ECCP256: + if(in_len > 32) { + return YKPIV_SIZE_ERROR; + } + memcpy(sign_in, raw_in, in_len); + break; + default: + return YKPIV_ALGORITHM_ERROR; + } + + if(in_len < 0x80) { + bytes = 1; + } else if(in_len < 0xff) { + bytes = 2; + } else { + bytes = 3; + } + + *dataptr++ = 0x7c; + dataptr += set_length(dataptr, in_len + bytes + 3); + *dataptr++ = 0x82; + *dataptr++ = 0x00; + *dataptr++ = 0x81; + dataptr += set_length(dataptr, in_len); + memcpy(dataptr, sign_in, (size_t)in_len); + dataptr += in_len; + + if((res = ykpiv_transfer_data(state, templ, indata, dataptr - indata, data, + &recv_len, &sw)) != YKPIV_OK) { + if(state->verbose) { + fprintf(stderr, "Sign command failed to communicate.\n"); + } + return res; + } else if(sw != 0x9000) { + if(state->verbose) { + fprintf(stderr, "Failed sign command with code %x.\n", sw); + } + return YKPIV_GENERIC_ERROR; + } + /* skip the first 7c tag */ + if(data[0] != 0x7c) { + if(state->verbose) { + fprintf(stderr, "Failed parsing signature reply.\n"); + } + return YKPIV_PARSE_ERROR; + } + dataptr = data + 1; + dataptr += get_length(dataptr, &len); + /* skip the 82 tag */ + if(*dataptr != 0x82) { + if(state->verbose) { + fprintf(stderr, "Failed parsing signature reply.\n"); + } + return YKPIV_PARSE_ERROR; + } + dataptr++; + dataptr += get_length(dataptr, &len); + if(len > *out_len) { + if(state->verbose) { + fprintf(stderr, "Wrong size on output buffer.\n"); + } + return YKPIV_SIZE_ERROR; + } + *out_len = len; + memcpy(sign_out, dataptr, len); + return YKPIV_OK; +} + +ykpiv_rc ykpiv_get_version(ykpiv_state *state, char *version, size_t len) { + APDU apdu; + unsigned char data[0xff]; + unsigned long recv_len = sizeof(data); + int sw; + ykpiv_rc res; + + memset(apdu.raw, 0, sizeof(apdu)); + apdu.st.ins = YKPIV_INS_GET_VERSION; + if((res = send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) { + return res; + } else if(sw == 0x9000) { + int result = snprintf(version, len, "%d.%d.%d", data[0], data[1], data[2]); + if(result < 0) { + return YKPIV_SIZE_ERROR; + } + return YKPIV_OK; + } else { + return YKPIV_GENERIC_ERROR; + } +} + +ykpiv_rc ykpiv_verify(ykpiv_state *state, const char *pin, int *tries) { + APDU apdu; + unsigned char data[0xff]; + unsigned long recv_len = sizeof(data); + int sw; + size_t len = strlen(pin); + ykpiv_rc res; + + if(len > 8) { + return YKPIV_SIZE_ERROR; + } + + memset(apdu.raw, 0, sizeof(apdu.raw)); + apdu.st.ins = YKPIV_INS_VERIFY; + apdu.st.p1 = 0x00; + apdu.st.p2 = 0x80; + apdu.st.lc = 0x08; + memcpy(apdu.st.data, pin, len); + if(len < 8) { + memset(apdu.st.data + len, 0xff, 8 - len); + } + if((res = send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) { + return res; + } else if(sw == 0x9000) { + return YKPIV_OK; + } else if((sw >> 8) == 0x63) { + if(state->verbose) { + fprintf(stderr, "Pin verification failed, %d tries left before pin is blocked.\n", sw & 0xff); + } + *tries = (sw & 0xff); + return YKPIV_WRONG_PIN; + } else if(sw == 0x6983) { + if(state->verbose) { + fprintf(stderr, "Pin code blocked, use unblock-pin action to unblock.\n"); + } + *tries = 0; + return YKPIV_WRONG_PIN; + } else { + if(state->verbose) { + fprintf(stderr, "Pin code verification failed with code %x.\n", sw); + } + return YKPIV_GENERIC_ERROR; + } +} + +ykpiv_rc ykpiv_fetch_object(ykpiv_state *state, int object_id, + unsigned char *data, unsigned long *len) { + int sw; + unsigned char indata[5]; + unsigned char *inptr = indata; + unsigned char templ[] = {0, YKPIV_INS_GET_DATA, 0x3f, 0xff}; + ykpiv_rc res; + + inptr = set_object(object_id, inptr); + if(inptr == NULL) { + return YKPIV_INVALID_OBJECT; + } + + if((res = ykpiv_transfer_data(state, templ, indata, inptr - indata, data, len, &sw)) + != YKPIV_OK) { + return res; + } + + { + size_t outlen; + int offs = get_length(data + 1, &outlen); + memmove(data, data + 1 + offs, outlen); + *len = outlen; + } + if(sw == 0x9000) { + return YKPIV_OK; + } else { + return YKPIV_GENERIC_ERROR; + } +} + +ykpiv_rc ykpiv_save_object(ykpiv_state *state, int object_id, + unsigned char *indata, size_t len) { + + unsigned char data[2048]; + unsigned char *dataptr = data; + unsigned char templ[] = {0, YKPIV_INS_PUT_DATA, 0x3f, 0xff}; + int sw; + ykpiv_rc res; + unsigned long outlen = 0; + + if(len > sizeof(data) - 9) { + return YKPIV_SIZE_ERROR; + } + dataptr = set_object(object_id, dataptr); + if(dataptr == NULL) { + return YKPIV_INVALID_OBJECT; + } + *dataptr++ = 0x53; + dataptr += set_length(dataptr, len); + memcpy(dataptr, indata, len); + dataptr += len; + + if((res = ykpiv_transfer_data(state, templ, data, dataptr - data, NULL, &outlen, + &sw)) != YKPIV_OK) { + return res; + } + + if(sw == 0x9000) { + return YKPIV_OK; + } else { + return YKPIV_GENERIC_ERROR; + } +} diff --git a/lib/ykpiv.h b/lib/ykpiv.h new file mode 100644 index 0000000..afb64a2 --- /dev/null +++ b/lib/ykpiv.h @@ -0,0 +1,129 @@ + /* + * Copyright (c) 2014 Yubico AB + * All rights reserved. + * + * This program 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. + * + * This program 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 . + * + * Additional permission under GNU GPL version 3 section 7 + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, We grant you additional + * permission to convey the resulting work. Corresponding Source for a + * non-source form of such a combination shall include the source code + * for the parts of OpenSSL used as well as that of the covered work. + * + */ + +#ifndef YKPIV_H +#define YKPIV_H + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct ykpiv_state ykpiv_state; + + typedef enum { + YKPIV_OK = 0, + YKPIV_MEMORY_ERROR = -1, + YKPIV_PCSC_ERROR = -2, + YKPIV_SIZE_ERROR = -3, + YKPIV_APPLET_ERROR = -4, + YKPIV_AUTHENTICATION_ERROR = -5, + YKPIV_RANDOMNESS_ERROR = -6, + YKPIV_GENERIC_ERROR = -7, + YKPIV_KEY_ERROR = -8, + YKPIV_PARSE_ERROR = -9, + YKPIV_WRONG_PIN = -10, + YKPIV_INVALID_OBJECT = -11, + YKPIV_ALGORITHM_ERROR = -12, + } ykpiv_rc; + + const char *ykpiv_strerror(ykpiv_rc err); + const char *ykpiv_strerror_name(ykpiv_rc err); + + ykpiv_rc ykpiv_init(ykpiv_state **state, int verbose); + ykpiv_rc ykpiv_done(ykpiv_state *state); + ykpiv_rc ykpiv_connect(ykpiv_state *state, const char *wanted); + ykpiv_rc ykpiv_disconnect(ykpiv_state *state); + ykpiv_rc ykpiv_transfer_data(ykpiv_state *state, const unsigned char *templ, + const unsigned char *in_data, long in_len, + unsigned char *out_data, unsigned long *out_len, int *sw); + ykpiv_rc ykpiv_authenticate(ykpiv_state *state, const unsigned char *key); + ykpiv_rc ykpiv_set_mgmkey(ykpiv_state *state, const unsigned char *new_key); + ykpiv_rc ykpiv_parse_key(ykpiv_state *state, + const char *key_in, unsigned char *key_out); + ykpiv_rc ykpiv_sign_data(ykpiv_state *state, const unsigned char *sign_in, + size_t in_len,unsigned char *sign_out, size_t *out_len, + unsigned char algorithm, unsigned char key); + ykpiv_rc ykpiv_get_version(ykpiv_state *state, char *version, size_t len); + ykpiv_rc ykpiv_verify(ykpiv_state *state, const char *pin, int *tries); + ykpiv_rc ykpiv_fetch_object(ykpiv_state *state, int object_id, + unsigned char *data, unsigned long *len); + ykpiv_rc ykpiv_save_object(ykpiv_state *state, int object_id, + unsigned char *indata, size_t len); + +#define YKPIV_ALGO_3DES 0x03 +#define YKPIV_ALGO_RSA1024 0x06 +#define YKPIV_ALGO_RSA2048 0x07 +#define YKPIV_ALGO_ECCP256 0x11 + +#define YKPIV_KEY_AUTHENTICATION 0x9a +#define YKPIV_KEY_CARDMGM 0x9b +#define YKPIV_KEY_SIGNATURE 0x9c +#define YKPIV_KEY_KEYMGM 0x9d +#define YKPIV_KEY_CARDAUTH 0x9e + +#define YKPIV_OBJ_CAPABILITY 0x5fc107 +#define YKPIV_OBJ_CHUID 0x5fc102 +#define YKPIV_OBJ_AUTHENTICATION 0x5fc105 /* cert for 9a key */ +#define YKPIV_OBJ_FINGERPRINTS 0x5fc103 +#define YKPIV_OBJ_SECURITY 0x5fc106 +#define YKPIV_OBJ_FACIAL 0x5fc108 +#define YKPIV_OBJ_PRINTED 0x5fc109 +#define YKPIV_OBJ_SIGNATURE 0x5fc10a /* cert for 9c key */ +#define YKPIV_OBJ_KEY_MANAGEMENT 0x5fc10b /* cert for 9d key */ +#define YKPIV_OBJ_CARD_AUTH 0x5fc101 /* cert for 9e key */ +#define YKPIV_OBJ_DISCOVERY 0x7e +#define YKPIV_OBJ_KEY_HISTORY 0x5fc10c +#define YKPIV_OBJ_IRIS 0x5fc121 + +#define YKPIV_INS_VERIFY 0x20 +#define YKPIV_INS_CHANGE_REFERENCE 0x24 +#define YKPIV_INS_RESET_RETRY 0x2c +#define YKPIV_INS_GENERATE_ASYMMERTRIC 0x47 +#define YKPIV_INS_AUTHENTICATE 0x87 +#define YKPIV_INS_GET_DATA 0xcb +#define YKPIV_INS_PUT_DATA 0xdb + + /* Yubico vendor specific instructions */ +#define YKPIV_INS_SET_MGMKEY 0xff +#define YKPIV_INS_IMPORT_KEY 0xfe +#define YKPIV_INS_GET_VERSION 0xfd +#define YKPIV_INS_RESET 0xfb +#define YKPIV_INS_SET_PIN_RETRIES 0xfa + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/ykpiv.map b/lib/ykpiv.map new file mode 100644 index 0000000..fc97ac0 --- /dev/null +++ b/lib/ykpiv.map @@ -0,0 +1,49 @@ +# Copyright (c) 2014 Yubico AB +# All rights reserved. +# +# This program 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. +# +# This program 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 . +# +# Additional permission under GNU GPL version 3 section 7 +# +# If you modify this program, or any covered work, by linking or +# combining it with the OpenSSL project's OpenSSL library (or a +# modified version of that library), containing parts covered by the +# terms of the OpenSSL or SSLeay licenses, We grant you additional +# permission to convey the resulting work. Corresponding Source for a +# non-source form of such a combination shall include the source code +# for the parts of OpenSSL used as well as that of the covered work. + +YKPIV_0.1.0 +{ +global: + ykpiv_check_version; + ykpiv_strerror_name; + ykpiv_strerror; + ykpiv_init; + ykpiv_done; + ykpiv_connect; + ykpiv_disconnect; + ykpiv_transfer_data; + ykpiv_authenticate; + ykpiv_set_mgmkey; + ykpiv_parse_key; + ykpiv_sign_data; + ykpiv_get_version; + ykpiv_verify; + ykpiv_fetch_object; + ykpiv_save_object; + +local: + *; +}; diff --git a/lib/ykpiv.pc.in b/lib/ykpiv.pc.in new file mode 100644 index 0000000..fc8e8c3 --- /dev/null +++ b/lib/ykpiv.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: @PACKAGE@ +Description: Yubico PIV C Library +URL: https://www.yubico.com/ +Version: @VERSION@ +Libs: -L${libdir} -lykpiv +Cflags: -I${includedir}/ykpiv + diff --git a/m4/ld-version-script.m4 b/m4/ld-version-script.m4 new file mode 100644 index 0000000..5ed93ef --- /dev/null +++ b/m4/ld-version-script.m4 @@ -0,0 +1,53 @@ +# ld-version-script.m4 serial 3 +dnl Copyright (C) 2008-2012 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Simon Josefsson + +# FIXME: The test below returns a false positive for mingw +# cross-compiles, 'local:' statements does not reduce number of +# exported symbols in a DLL. Use --disable-ld-version-script to work +# around the problem. + +# gl_LD_VERSION_SCRIPT +# -------------------- +# Check if LD supports linker scripts, and define automake conditional +# HAVE_LD_VERSION_SCRIPT if so. +AC_DEFUN([gl_LD_VERSION_SCRIPT], +[ + AC_ARG_ENABLE([ld-version-script], + AS_HELP_STRING([--enable-ld-version-script], + [enable linker version script (default is enabled when possible)]), + [have_ld_version_script=$enableval], []) + if test -z "$have_ld_version_script"; then + AC_MSG_CHECKING([if LD -Wl,--version-script works]) + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS -Wl,--version-script=conftest.map" + cat > conftest.map < conftest.map <. +# +# Additional permission under GNU GPL version 3 section 7 +# +# If you modify this program, or any covered work, by linking or +# combining it with the OpenSSL project's OpenSSL library (or a +# modified version of that library), containing parts covered by the +# terms of the OpenSSL or SSLeay licenses, We grant you additional +# permission to convey the resulting work. Corresponding Source for a +# non-source form of such a combination shall include the source code +# for the parts of OpenSSL used as well as that of the covered work. + +SUBDIRS = . tests + +AM_CFLAGS = $(WERROR_CFLAGS) $(WARN_CFLAGS) +AM_CPPFLAGS = $(OPENSSL_CFLAGS) +AM_CPPFLAGS += -I$(top_srcdir)/lib -I$(top_builddir)/lib + +bin_PROGRAMS = yubico-piv-tool +yubico_piv_tool_SOURCES = yubico-piv-tool.c yubico-piv-tool.h2m +yubico_piv_tool_LDADD = $(OPENSSL_LIBS) ../lib/libykpiv.la +yubico_piv_tool_LDADD += libpiv_cmd.la libpiv_util.la + +noinst_LTLIBRARIES = libpiv_cmd.la libpiv_util.la +libpiv_cmd_la_SOURCES = cmdline.ggo cmdline.c cmdline.h +libpiv_cmd_la_CFLAGS = + +libpiv_util_la_SOURCES = util.c util.h +libpiv_util_la_LIBADD = $(OPENSSL_LIBS) + +cmdline.c cmdline.h: cmdline.ggo Makefile.am + $(GENGETOPT) --input $^ + +BUILT_SOURCES = cmdline.c cmdline.h +MAINTAINERCLEANFILES = $(BUILT_SOURCES) + +# Doc. + +dist_man_MANS = yubico-piv-tool.1 +MAINTAINERCLEANFILES += $(dist_man_MANS) + +yubico-piv-tool.1: $(yubico_piv_tool_SOURCES) \ + $(top_srcdir)/configure.ac + $(HELP2MAN) --no-info \ + --name="Yubico PIV tool" \ + --include=$(srcdir)/yubico-piv-tool.h2m \ + --output=$@ $(builddir)/yubico-piv-tool$(EXEEXT) diff --git a/cmdline.ggo b/tool/cmdline.ggo similarity index 100% rename from cmdline.ggo rename to tool/cmdline.ggo diff --git a/tool/tests/Makefile.am b/tool/tests/Makefile.am new file mode 100644 index 0000000..a25a3f1 --- /dev/null +++ b/tool/tests/Makefile.am @@ -0,0 +1,39 @@ +# Copyright (c) 2014 Yubico AB +# All rights reserved. +# +# This program 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. +# +# This program 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 . +# +# Additional permission under GNU GPL version 3 section 7 +# +# If you modify this program, or any covered work, by linking or +# combining it with the OpenSSL project's OpenSSL library (or a +# modified version of that library), containing parts covered by the +# terms of the OpenSSL or SSLeay licenses, We grant you additional +# permission to convey the resulting work. Corresponding Source for a +# non-source form of such a combination shall include the source code +# for the parts of OpenSSL used as well as that of the covered work. + +TESTS_ENVIRONMENT = export VERSION=$(VERSION); export EXEEXT=$(EXEEXT); + +AM_CFLAGS = $(WARN_CFLAGS) +AM_CPPFLAGS = -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CPPFLAGS += -I$(top_srcdir)/tool -I$(top_builddir)/tool +AM_CPPFLAGS += $(OPENSSL_CFLAGS) + +AM_LDFLAGS = -no-install + +parse_name_LDADD = ../libpiv_util.la $(OPENSSL_LIBS) + +check_PROGRAMS = parse_name +TESTS = basic.sh $(check_PROGRAMS) diff --git a/tests/basic.sh b/tool/tests/basic.sh similarity index 92% rename from tests/basic.sh rename to tool/tests/basic.sh index f29f4c1..daf3c33 100755 --- a/tests/basic.sh +++ b/tool/tests/basic.sh @@ -31,10 +31,12 @@ set -e -HELP_OUTPUT=$(../yubico-piv-tool$EXEEXT --help) +BIN="../yubico-piv-tool${EXEEXT}" + +HELP_OUTPUT=$($BIN --help) expected="yubico-piv-tool $VERSION" -VERSION_OUTPUT=$(../yubico-piv-tool$EXEEXT --version | sed 's/\r//') +VERSION_OUTPUT=$($BIN --version | sed 's/\r//') if [ "x$VERSION_OUTPUT" != "x$expected" ]; then echo "Version ($VERSION_OUTPUT) not matching expected output $expected." exit 1 diff --git a/tool/tests/parse_name.c b/tool/tests/parse_name.c new file mode 100644 index 0000000..a3fe30d --- /dev/null +++ b/tool/tests/parse_name.c @@ -0,0 +1,75 @@ + /* + * Copyright (c) 2014 Yubico AB + * All rights reserved. + * + * This program 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. + * + * This program 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 . + * + * Additional permission under GNU GPL version 3 section 7 + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, We grant you additional + * permission to convey the resulting work. Corresponding Source for a + * non-source form of such a combination shall include the source code + * for the parts of OpenSSL used as well as that of the covered work. + */ + +#include +#include + +#ifdef _WIN32 +#include +#endif + +#include + +#include "util.h" + +static void test_name(const char *name, const char *expected, bool fail) { + char buf[1024]; + BIO *bio; + const char none[] = {0}; + X509_NAME *parsed = parse_name(name); + if(parsed == NULL) { + if(fail) { + return; + } else { + printf("Failed parsing of '%s'!\n", name); + exit(EXIT_FAILURE); + } + } + + bio = BIO_new(BIO_s_mem()); + + X509_NAME_print_ex(bio, parsed, 0, XN_FLAG_ONELINE); + BIO_write(bio, none, 1); + BIO_read(bio, buf, 1024); + BIO_free(bio); + X509_NAME_free(parsed); + if(strcmp(buf, expected) != 0) { + printf("Names not matching: '%s' != '%s'\n", expected, buf); + exit(EXIT_FAILURE); + } +} + +int main(void) { + test_name("/CN=test foo/", "CN = test foo", false); + test_name("/CN=test/OU=bar/O=EXAMPLE/", "CN = test, OU = bar, O = EXAMPLE", false); + test_name("/foo/", "", true); + test_name("/CN=test/foobar/", "", true); + test_name("/CN=test/foo=bar/", "", true); + + return EXIT_SUCCESS; +} diff --git a/tool/util.c b/tool/util.c new file mode 100644 index 0000000..6cebd65 --- /dev/null +++ b/tool/util.c @@ -0,0 +1,204 @@ + /* + * Copyright (c) 2014 Yubico AB + * All rights reserved. + * + * This program 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. + * + * This program 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 . + * + * Additional permission under GNU GPL version 3 section 7 + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, We grant you additional + * permission to convey the resulting work. Corresponding Source for a + * non-source form of such a combination shall include the source code + * for the parts of OpenSSL used as well as that of the covered work. + * + */ + +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#endif + +#include + +#include + +#include "util.h" + +FILE *open_file(const char *file_name, int mode) { + FILE *file; + if(!strcmp(file_name, "-")) { + file = mode == INPUT ? stdin : stdout; + } else { + file = fopen(file_name, mode == INPUT ? "r" : "w"); + if(!file) { + fprintf(stderr, "Failed opening '%s'!\n", file_name); + return NULL; + } + } + return file; +} + +unsigned char get_algorithm(EVP_PKEY *key) { + int type = EVP_PKEY_type(key->type); + switch(type) { + case EVP_PKEY_RSA: + { + RSA *rsa = EVP_PKEY_get1_RSA(key); + int size = RSA_size(rsa); + if(size == 256) { + return YKPIV_ALGO_RSA2048; + } else if(size == 128) { + return YKPIV_ALGO_RSA1024; + } else { + fprintf(stderr, "Unuseable key of %d bits, only 1024 and 2048 is supported.\n", size * 8); + return 0; + } + } + case EVP_PKEY_EC: + { + EC_KEY *ec = EVP_PKEY_get1_EC_KEY(key); + const EC_GROUP *group = EC_KEY_get0_group(ec); + int curve = EC_GROUP_get_curve_name(group); + if(curve == NID_X9_62_prime256v1) { + return YKPIV_ALGO_ECCP256; + } else { + fprintf(stderr, "Unknown EC curve %d\n", curve); + return 0; + } + } + default: + fprintf(stderr, "Unknown algorithm %d.\n", type); + return 0; + } +} + +X509_NAME *parse_name(const char *orig_name) { + char name[1025]; + X509_NAME *parsed = NULL; + char *ptr = name; + char *part; + + if(strlen(orig_name) > 1024) { + fprintf(stderr, "Name is to long!\n"); + return NULL; + } + strcpy(name, orig_name); + + if(*name != '/') { + fprintf(stderr, "Name does not start with '/'!\n"); + return NULL; + } + parsed = X509_NAME_new(); + if(!parsed) { + fprintf(stderr, "Failed to allocate memory\n"); + return NULL; + } + while((part = strtok(ptr, "/"))) { + char *key; + char *value; + char *equals = strchr(part, '='); + if(!equals) { + fprintf(stderr, "The part '%s' doesn't seem to contain a =.\n", part); + goto parse_err; + } + *equals++ = '\0'; + value = equals; + key = part; + + ptr = NULL; + if(!key) { + fprintf(stderr, "Malformed name (%s)\n", part); + goto parse_err; + } + if(!value) { + fprintf(stderr, "Malformed name (%s)\n", part); + goto parse_err; + } + if(!X509_NAME_add_entry_by_txt(parsed, key, MBSTRING_UTF8, (unsigned char*)value, -1, -1, 0)) { + fprintf(stderr, "Failed adding %s=%s to name.\n", key, value); + goto parse_err; + } + } + return parsed; +parse_err: + X509_NAME_free(parsed); + return NULL; +} + +void dump_hex(const unsigned char *buf, unsigned int len) { + unsigned int i; + for (i = 0; i < len; i++) { + fprintf(stderr, "%02x ", buf[i]); + } +} + +int get_length(const unsigned char *buffer, int *len) { + if(buffer[0] < 0x81) { + *len = buffer[0]; + return 1; + } else if((*buffer & 0x7f) == 1) { + *len = buffer[1]; + return 2; + } else if((*buffer & 0x7f) == 2) { + *len = (buffer[1] << 8) + buffer[2]; + return 3; + } + return 0; +} + +int set_length(unsigned char *buffer, int length) { + if(length < 0x80) { + *buffer++ = length; + return 1; + } else if(length < 0xff) { + *buffer++ = 0x81; + *buffer++ = length; + return 2; + } else { + *buffer++ = 0x82; + *buffer++ = (length >> 8) & 0xff; + *buffer++ = length & 0xff; + return 3; + } +} + +int get_object_id(enum enum_slot slot) { + int object; + + switch(slot) { + case slot_arg_9a: + object = YKPIV_OBJ_AUTHENTICATION; + break; + case slot_arg_9c: + object = YKPIV_OBJ_SIGNATURE; + break; + case slot_arg_9d: + object = YKPIV_OBJ_KEY_MANAGEMENT; + break; + case slot_arg_9e: + object = YKPIV_OBJ_CARD_AUTH; + break; + case slot__NULL: + default: + object = 0; + } + return object; +} diff --git a/tool/util.h b/tool/util.h new file mode 100644 index 0000000..7a76466 --- /dev/null +++ b/tool/util.h @@ -0,0 +1,46 @@ + /* + * Copyright (c) 2014 Yubico AB + * All rights reserved. + * + * This program 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. + * + * This program 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 . + * + * Additional permission under GNU GPL version 3 section 7 + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, We grant you additional + * permission to convey the resulting work. Corresponding Source for a + * non-source form of such a combination shall include the source code + * for the parts of OpenSSL used as well as that of the covered work. + * + */ + +#ifndef YUBICO_PIV_TOOL_INTERNAL_H +#define YUBICO_PIV_TOOL_INTERNAL_H + +#include "cmdline.h" + +#define INPUT 1 +#define OUTPUT 2 + +void dump_hex(unsigned const char*, unsigned int); +int set_length(unsigned char*, int); +int get_length(const unsigned char*, int*); +X509_NAME *parse_name(const char*); +unsigned char get_algorithm(EVP_PKEY*); +FILE *open_file(const char*, int); +int get_object_id(enum enum_slot slot); + +#endif diff --git a/yubico-piv-tool.c b/tool/yubico-piv-tool.c similarity index 51% rename from yubico-piv-tool.c rename to tool/yubico-piv-tool.c index 8339df8..12c4ac5 100644 --- a/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -32,13 +32,10 @@ #include #include -#if BACKEND_PCSC -#if defined HAVE_PCSC_WINSCARD_H -# include -# include -#else -# include -#endif +#include "ykpiv.h" + +#ifdef _WIN32 +#include #endif #include @@ -46,26 +43,20 @@ #include #include -#include "cmdline.h" - -unsigned const char aid[] = { - 0xa0, 0x00, 0x00, 0x03, 0x08 -}; - +#include "util.h" /* FASC-N containing S9999F9999F999999F0F1F0000000000300001E encoded in * 4-bit BCD with 1 bit parity. run through the tools/fasc.pl script to get * bytes. */ /* this CHUID has an expiry of 2030-01-01, maybe that should be variable.. */ unsigned const char chuid_tmpl[] = { - 0x5c, 0x03, 0x5f, 0xc1, 0x02, 0x53, 0x3b, 0x30, 0x19, 0xd4, 0xe7, 0x39, 0xda, - 0x73, 0x9c, 0xed, 0x39, 0xce, 0x73, 0x9d, 0x83, 0x68, 0x58, 0x21, 0x08, 0x42, - 0x10, 0x84, 0x21, 0x38, 0x42, 0x10, 0xc3, 0xf5, 0x34, 0x10, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x35, 0x08, 0x32, 0x30, 0x33, 0x30, 0x30, 0x31, 0x30, 0x31, 0x3e, 0x00, 0xfe, - 0x00, + 0x30, 0x19, 0xd4, 0xe7, 0x39, 0xda, 0x73, 0x9c, 0xed, 0x39, 0xce, 0x73, 0x9d, + 0x83, 0x68, 0x58, 0x21, 0x08, 0x42, 0x10, 0x84, 0x21, 0x38, 0x42, 0x10, 0xc3, + 0xf5, 0x34, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x08, 0x32, 0x30, 0x33, 0x30, 0x30, + 0x31, 0x30, 0x31, 0x3e, 0x00, 0xfe, 0x00, }; -#define CHUID_GUID_OFFS 36 +#define CHUID_GUID_OFFS 28 unsigned const char sha256oid[] = { 0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, @@ -75,224 +66,21 @@ unsigned const char sha256oid[] = { #define KEY_LEN 24 -#define INPUT 1 -#define OUTPUT 2 - -union u_APDU { - struct { - unsigned char cla; - unsigned char ins; - unsigned char p1; - unsigned char p2; - unsigned char lc; - unsigned char data[0xff]; - } st; - unsigned char raw[0xff + 5]; -}; - -typedef union u_APDU APDU; - -static void dump_hex(unsigned const char*, unsigned int); -static int transfer_data(SCARDHANDLE*, APDU*, unsigned char*, long, - unsigned char*, unsigned long*, int verbose); -static int send_data(SCARDHANDLE*, APDU*, unsigned char*, unsigned long*, int); -static int set_length(unsigned char*, int); -static int get_length(unsigned char*, int*); -static X509_NAME *parse_name(char*); -static unsigned char get_algorithm(EVP_PKEY*); -static FILE *open_file(const char*, int); -static bool sign_data(SCARDHANDLE*, unsigned char*, int, unsigned char, unsigned char, - ASN1_BIT_STRING*, int); -static int get_object_id(enum enum_slot slot); - -static bool connect_reader(SCARDHANDLE *card, SCARDCONTEXT *context, const char *wanted, int verbose) { - unsigned long num_readers = 0; - unsigned long active_protocol; - char reader_buf[1024]; - long rc; - char *reader_ptr; - - rc = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, context); - if (rc != SCARD_S_SUCCESS) { - fprintf (stderr, "error: SCardEstablishContext failed, rc=%08lx\n", rc); - return false; - } - - rc = SCardListReaders(*context, NULL, NULL, &num_readers); - if (rc != SCARD_S_SUCCESS) { - fprintf (stderr, "error: SCardListReaders failed, rc=%08lx\n", rc); - SCardReleaseContext(*context); - return false; - } - - if (num_readers > sizeof(reader_buf)) { - num_readers = sizeof(reader_buf); - } - - rc = SCardListReaders(*context, NULL, reader_buf, &num_readers); - if (rc != SCARD_S_SUCCESS) - { - fprintf (stderr, "error: SCardListReaders failed, rc=%08lx\n", rc); - SCardReleaseContext(*context); - return false; - } - - reader_ptr = reader_buf; - if(wanted) { - while(*reader_ptr != '\0') { - if(strstr(reader_ptr, wanted)) { - if(verbose) { - fprintf(stderr, "using reader '%s' matching '%s'.\n", reader_ptr, wanted); - } - break; - } else { - if(verbose) { - fprintf(stderr, "skipping reader '%s' since it doesn't match.\n", reader_ptr); - } - reader_ptr += strlen(reader_ptr) + 1; - } - } - } - if(*reader_ptr == '\0') { - fprintf(stderr, "error: no useable reader found.\n"); - SCardReleaseContext(*context); - return false; - } - - rc = SCardConnect(*context, reader_ptr, SCARD_SHARE_SHARED, - SCARD_PROTOCOL_T1, card, &active_protocol); - if(rc != SCARD_S_SUCCESS) - { - fprintf(stderr, "error: SCardConnect failed, rc=%08lx\n", rc); - SCardReleaseContext(*context); - return false; - } - - return true; -} - -static bool select_applet(SCARDHANDLE *card, int verbose) { - APDU apdu; - unsigned char data[0xff]; - unsigned long recv_len = sizeof(data); - int sw; - - memset(apdu.raw, 0, sizeof(apdu)); - apdu.st.ins = 0xa4; - apdu.st.p1 = 0x04; - apdu.st.lc = sizeof(aid); - memcpy(apdu.st.data, aid, sizeof(aid)); - - sw = send_data(card, &apdu, data, &recv_len, verbose); - if(sw == 0x9000) { - return true; - } - - return false; -} - -static bool authenticate(SCARDHANDLE *card, unsigned const char *key, int verbose) { - APDU apdu; - unsigned char data[0xff]; - DES_cblock challenge; - unsigned long recv_len = sizeof(data); - int sw; - - DES_key_schedule ks1, ks2, ks3; - - /* set up our key */ - { - const_DES_cblock key_tmp; - memcpy(key_tmp, key, 8); - DES_set_key_unchecked(&key_tmp, &ks1); - memcpy(key_tmp, key + 8, 8); - DES_set_key_unchecked(&key_tmp, &ks2); - memcpy(key_tmp, key + 16, 8); - DES_set_key_unchecked(&key_tmp, &ks3); - } - - /* get a challenge from the card */ - { - memset(apdu.raw, 0, sizeof(apdu)); - apdu.st.ins = 0x87; - apdu.st.p1 = 0x03; /* triple des */ - apdu.st.p2 = 0x9b; /* management key */ - apdu.st.lc = 0x04; - apdu.st.data[0] = 0x7c; - apdu.st.data[1] = 0x02; - apdu.st.data[2] = 0x80; - sw = send_data(card, &apdu, data, &recv_len, verbose); - if(sw != 0x9000) { - return false; - } - memcpy(challenge, data + 4, 8); - } - - /* send a response to the cards challenge and a challenge of our own. */ - { - unsigned char *dataptr = apdu.st.data; - DES_cblock response; - DES_ecb3_encrypt(&challenge, &response, &ks1, &ks2, &ks3, 0); - - recv_len = 0xff; - memset(apdu.raw, 0, sizeof(apdu)); - apdu.st.ins = 0x87; - apdu.st.p1 = 0x03; /* triple des */ - apdu.st.p2 = 0x9b; /* management key */ - *dataptr++ = 0x7c; - *dataptr++ = 20; /* 2 + 8 + 2 +8 */ - *dataptr++ = 0x80; - *dataptr++ = 8; - memcpy(dataptr, response, 8); - dataptr += 8; - *dataptr++ = 0x81; - *dataptr++ = 8; - if(RAND_pseudo_bytes(dataptr, 8) == -1) { - fprintf(stderr, "Failed getting randomness for authentication.\n"); - return false; - } - memcpy(challenge, dataptr, 8); - dataptr += 8; - apdu.st.lc = dataptr - apdu.st.data; - sw = send_data(card, &apdu, data, &recv_len, verbose); - if(sw != 0x9000) { - return false; - } - } - - /* compare the response from the card with our challenge */ - { - DES_cblock response; - DES_ecb3_encrypt(&challenge, &response, &ks1, &ks2, &ks3, 1); - if(memcmp(response, data + 4, 8) == 0) { - return true; - } else { - return false; - } - } -} - -static void print_version(SCARDHANDLE *card, int verbose) { - APDU apdu; - unsigned char data[0xff]; - unsigned long recv_len = sizeof(data); - int sw; - - memset(apdu.raw, 0, sizeof(apdu)); - apdu.st.ins = 0xfd; - sw = send_data(card, &apdu, data, &recv_len, verbose); - if(sw == 0x9000) { - printf("Applet version %d.%d.%d found.\n", data[0], data[1], data[2]); +static void print_version(ykpiv_state *state) { + char version[7]; + if(ykpiv_get_version(state, version, sizeof(version)) == YKPIV_OK) { + printf("Applet version %s found.\n", version); } else { - printf("Applet version not found. Status code: %x\n", sw); + printf("Failed to retreive apple version.\n"); } } -static bool generate_key(SCARDHANDLE *card, const char *slot, enum enum_algorithm algorithm, - const char *output_file_name, enum enum_key_format key_format, int verbose) { - APDU apdu; +static bool generate_key(ykpiv_state *state, const char *slot, + enum enum_algorithm algorithm, const char *output_file_name, + enum enum_key_format key_format) { unsigned char in_data[5]; unsigned char data[1024]; + unsigned char templ[] = {0, YKPIV_INS_GENERATE_ASYMMERTRIC, 0, 0}; unsigned long recv_len = sizeof(data); unsigned long received = 0; int sw; @@ -307,37 +95,37 @@ static bool generate_key(SCARDHANDLE *card, const char *slot, enum enum_algorith EC_POINT *point = NULL; sscanf(slot, "%x", &key); + templ[3] = key; output_file = open_file(output_file_name, OUTPUT); if(!output_file) { return false; } - memset(apdu.raw, 0, sizeof(apdu)); - apdu.st.ins = 0x47; - apdu.st.p2 = key; in_data[0] = 0xac; in_data[1] = 3; in_data[2] = 0x80; in_data[3] = 1; switch(algorithm) { case algorithm_arg_RSA2048: - in_data[4] = 0x07; + in_data[4] = YKPIV_ALGO_RSA2048; break; case algorithm_arg_RSA1024: - in_data[4] = 0x06; + in_data[4] = YKPIV_ALGO_RSA1024; break; case algorithm_arg_ECCP256: - in_data[4] = 0x11; + in_data[4] = YKPIV_ALGO_ECCP256; break; case algorithm__NULL: default: fprintf(stderr, "Unexepcted algorithm.\n"); goto generate_out; } - sw = transfer_data(card, &apdu, in_data, sizeof(in_data), data, &recv_len, verbose); - - if(sw != 0x9000) { + if(ykpiv_transfer_data(state, templ, in_data, sizeof(in_data), data, + &recv_len, &sw) != YKPIV_OK) { + fprintf(stderr, "Failed to communicate.\n"); + goto generate_out; + } else if(sw != 0x9000) { fprintf(stderr, "Failed to generate new key.\n"); goto generate_out; } @@ -436,60 +224,23 @@ generate_out: return ret; } -static bool set_mgm_key(SCARDHANDLE *card, unsigned const char *new_key, int verbose) { - APDU apdu; - unsigned char data[0xff]; - unsigned long recv_len = sizeof(data); - int sw; - size_t i; - - for(i = 0; i < KEY_LEN; i += 8) { - const_DES_cblock key_tmp; - memcpy(key_tmp, new_key + i, 8); - if(DES_is_weak_key(&key_tmp) == 1) { - fprintf(stderr, "Won't set new key '"); - dump_hex(new_key + i, 8); - fprintf(stderr, "' since it's considered weak.\n"); - return false; - } - } - - memset(apdu.raw, 0, sizeof(apdu)); - apdu.st.ins = 0xff; - apdu.st.p1 = 0xff; - apdu.st.p2 = 0xff; - apdu.st.lc = KEY_LEN + 3; - apdu.st.data[0] = 0x03; /* 3-DES */ - apdu.st.data[1] = 0x9b; - apdu.st.data[2] = KEY_LEN; - memcpy(apdu.st.data + 3, new_key, KEY_LEN); - sw = send_data(card, &apdu, data, &recv_len, verbose); - - if(sw == 0x9000) { - return true; - } - return false; -} - -static bool reset(SCARDHANDLE *card, int verbose) { - APDU apdu; +static bool reset(ykpiv_state *state) { + unsigned char templ[] = {0, YKPIV_INS_RESET, 0, 0}; unsigned char data[0xff]; unsigned long recv_len = sizeof(data); int sw; - memset(apdu.raw, 0, sizeof(apdu)); /* note: the reset function is only available when both pins are blocked. */ - apdu.st.ins = 0xfb; - sw = send_data(card, &apdu, data, &recv_len, verbose); - - if(sw == 0x9000) { + if(ykpiv_transfer_data(state, templ, NULL, 0, data, &recv_len, &sw) != YKPIV_OK) { + return false; + } else if(sw == 0x9000) { return true; } return false; } -static bool set_pin_retries(SCARDHANDLE *card, int pin_retries, int puk_retries, int verbose) { - APDU apdu; +static bool set_pin_retries(ykpiv_state *state, int pin_retries, int puk_retries, int verbose) { + unsigned char templ[] = {0, YKPIV_INS_SET_PIN_RETRIES, pin_retries, puk_retries}; unsigned char data[0xff]; unsigned long recv_len = sizeof(data); int sw; @@ -503,20 +254,16 @@ static bool set_pin_retries(SCARDHANDLE *card, int pin_retries, int puk_retries, fprintf(stderr, "Setting pin retries to %d and puk retries to %d.\n", pin_retries, puk_retries); } - memset(apdu.raw, 0, sizeof(apdu)); - apdu.st.ins = 0xfa; - apdu.st.p1 = pin_retries; - apdu.st.p2 = puk_retries; - sw = send_data(card, &apdu, data, &recv_len, verbose); - - if(sw == 0x9000) { + if(ykpiv_transfer_data(state, templ, NULL, 0, data, &recv_len, &sw) != YKPIV_OK) { + return false; + } else if(sw == 0x9000) { return true; } return false; } -static bool import_key(SCARDHANDLE *card, enum enum_key_format key_format, - const char *input_file_name, const char *slot, char *password, int verbose) { +static bool import_key(ykpiv_state *state, enum enum_key_format key_format, + const char *input_file_name, const char *slot, char *password) { int key = 0; FILE *input_file = NULL; EVP_PKEY *private_key = NULL; @@ -559,13 +306,13 @@ static bool import_key(SCARDHANDLE *card, enum enum_key_format key_format, goto import_out; } { - APDU apdu; unsigned char data[0xff]; unsigned long recv_len = sizeof(data); unsigned char in_data[1024]; unsigned char *in_ptr = in_data; + unsigned char templ[] = {0, YKPIV_INS_IMPORT_KEY, algorithm, key}; int sw; - if(algorithm == 0x06 || algorithm == 0x07) { + if(algorithm == YKPIV_ALGO_RSA1024 || algorithm == YKPIV_ALGO_RSA2048) { RSA *rsa_private_key = EVP_PKEY_get1_RSA(private_key); *in_ptr++ = 0x01; @@ -587,7 +334,7 @@ static bool import_key(SCARDHANDLE *card, enum enum_key_format key_format, *in_ptr++ = 0x05; in_ptr += set_length(in_ptr, BN_num_bytes(rsa_private_key->iqmp)); in_ptr += BN_bn2bin(rsa_private_key->iqmp, in_ptr); - } else if(algorithm == 0x11) { + } else if(algorithm == YKPIV_ALGO_ECCP256) { EC_KEY *ec = EVP_PKEY_get1_EC_KEY(private_key); const BIGNUM *s = EC_KEY_get0_private_key(ec); @@ -596,15 +343,13 @@ static bool import_key(SCARDHANDLE *card, enum enum_key_format key_format, in_ptr += BN_bn2bin(s, in_ptr); } - memset(apdu.raw, 0, sizeof(apdu.raw)); - apdu.st.ins = 0xfe; - apdu.st.p1 = algorithm; - apdu.st.p2 = key; - sw = transfer_data(card, &apdu, in_data, in_ptr - in_data, data, &recv_len, verbose); - if(sw != 0x9000) { + if(ykpiv_transfer_data(state, templ, in_data, in_ptr - in_data, data, + &recv_len, &sw) != YKPIV_OK) { + return false; + } else if(sw != 0x9000) { fprintf(stderr, "Failed import command with code %x.", sw); } else { - ret = true; + ret = true; } } } @@ -624,14 +369,13 @@ import_out: return ret; } -static bool import_cert(SCARDHANDLE *card, enum enum_key_format cert_format, - const char *input_file_name, enum enum_slot slot, char *password, int verbose) { +static bool import_cert(ykpiv_state *state, enum enum_key_format cert_format, + const char *input_file_name, enum enum_slot slot, char *password) { bool ret = false; FILE *input_file = NULL; X509 *cert = NULL; PKCS12 *p12 = NULL; EVP_PKEY *private_key = NULL; - int object = get_object_id(slot); input_file = open_file(input_file_name, INPUT); if(!input_file) { @@ -661,33 +405,16 @@ static bool import_cert(SCARDHANDLE *card, enum enum_key_format cert_format, } { - APDU apdu; unsigned char certdata[2100]; unsigned char *certptr = certdata; - unsigned char data[0xff]; - unsigned long recv_len = sizeof(data); + int object = get_object_id(slot); int cert_len = i2d_X509(cert, NULL); - int bytes; - int sw; + ykpiv_rc res; if(cert_len > 2048) { fprintf(stderr, "Certificate to large, maximum 2048 bytes (was %d bytes).\n", cert_len); goto import_cert_out; } - *certptr++ = 0x5c; - *certptr++ = 0x03; - *certptr++ = (object >> 16) & 0xff; - *certptr++ = (object >> 8) & 0xff; - *certptr++ = object & 0xff; - *certptr++ = 0x53; - if(cert_len < 0x80) { - bytes = 1; - } else if(cert_len < 0xff) { - bytes = 2; - } else { - bytes = 3; - } - certptr += set_length(certptr, cert_len + bytes + 6); *certptr++ = 0x70; certptr += set_length(certptr, cert_len); /* i2d_X509 increments certptr here.. */ @@ -698,14 +425,8 @@ static bool import_cert(SCARDHANDLE *card, enum enum_key_format cert_format, *certptr++ = 0xfe; /* LRC */ *certptr++ = 0; - memset(apdu.raw, 0, sizeof(apdu.raw)); - apdu.st.ins = 0xdb; - apdu.st.p1 = 0x3f; - apdu.st.p2 = 0xff; - - sw = transfer_data(card, &apdu, certdata, certptr - certdata, data, &recv_len, verbose); - if(sw != 0x9000) { - fprintf(stderr, "Failed loading certificate to device with code %x.\n", sw); + if((res = ykpiv_save_object(state, object, certdata, (size_t)(certptr - certdata))) != YKPIV_OK) { + fprintf(stderr, "Failed commands with device: %s\n", ykpiv_strerror(res)); } else { ret = true; } @@ -728,40 +449,31 @@ import_cert_out: return ret; } -static bool set_chuid(SCARDHANDLE *card, int verbose) { - APDU apdu; - unsigned char data[0xff]; - unsigned char *dataptr = apdu.st.data; - unsigned long recv_len = sizeof(data); - int sw; +static bool set_chuid(ykpiv_state *state, int verbose) { + unsigned char chuid[sizeof(chuid_tmpl)]; + ykpiv_rc res; - memset(apdu.raw, 0, sizeof(apdu)); - memcpy(apdu.st.data, chuid_tmpl, sizeof(chuid_tmpl)); - dataptr += CHUID_GUID_OFFS; - if(RAND_pseudo_bytes(dataptr, 0x10) == -1) { + memcpy(chuid, chuid_tmpl, sizeof(chuid)); + if(RAND_pseudo_bytes(chuid + CHUID_GUID_OFFS, 0x10) == -1) { fprintf(stderr, "error: no randomness.\n"); return false; } if(verbose) { fprintf(stderr, "Setting the GUID to: "); - dump_hex(dataptr, 0x10); + dump_hex(chuid, sizeof(chuid)); fprintf(stderr, "\n"); } - apdu.st.ins = 0xdb; - apdu.st.p1 = 0x3f; - apdu.st.p2 = 0xff; - apdu.st.lc = sizeof(chuid_tmpl); - sw = send_data(card, &apdu, data, &recv_len, verbose); - if(sw != 0x9000) { - fprintf(stderr, "Failed setting CHUID.\n"); + if((res = ykpiv_save_object(state, YKPIV_OBJ_CHUID, chuid, sizeof(chuid))) != YKPIV_OK) { + fprintf(stderr, "Failed communicating with device: %s\n", ykpiv_strerror(res)); return false; } + return true; } -static bool request_certificate(SCARDHANDLE *card, enum enum_key_format key_format, +static bool request_certificate(ykpiv_state *state, enum enum_key_format key_format, const char *input_file_name, const char *slot, char *subject, - const char *output_file_name, int verbose) { + const char *output_file_name) { X509_REQ *req = NULL; X509_NAME *name = NULL; FILE *input_file = NULL; @@ -772,8 +484,8 @@ static bool request_certificate(SCARDHANDLE *card, enum enum_key_format key_form unsigned int digest_len = DIGEST_LEN; unsigned char algorithm; int key = 0; - unsigned char signinput[256]; - int len = 0; + unsigned char *signinput; + size_t len = 0; sscanf(slot, "%x", &key); @@ -830,27 +542,29 @@ static bool request_certificate(SCARDHANDLE *card, enum enum_key_format key_form } switch(algorithm) { - case 0x6: - len = 128; - case 0x7: - if(len == 0) { - len = 256; - } - RSA_padding_add_PKCS1_type_1(signinput, len, digest, sizeof(digest)); + case YKPIV_ALGO_RSA1024: + case YKPIV_ALGO_RSA2048: + signinput = digest; + len = sizeof(digest); req->sig_alg->algorithm = OBJ_nid2obj(NID_sha256WithRSAEncryption); break; - case 0x11: - req->sig_alg->algorithm = OBJ_nid2obj(NID_ecdsa_with_SHA256); + case YKPIV_ALGO_ECCP256: + signinput = digest + sizeof(sha256oid); len = DIGEST_LEN; - memcpy(signinput, digest + sizeof(sha256oid), DIGEST_LEN); + req->sig_alg->algorithm = OBJ_nid2obj(NID_ecdsa_with_SHA256); break; default: fprintf(stderr, "Unsupported algorithm %x.\n", algorithm); goto request_out; } - if(sign_data(card, signinput, len, algorithm, key, req->signature, - verbose) == false) { - goto request_out; + { + unsigned char signature[1024]; + size_t sig_len = sizeof(signature); + if(ykpiv_sign_data(state, signinput, len, signature, &sig_len, algorithm, key) + != YKPIV_OK) { + goto request_out; + } + M_ASN1_BIT_STRING_set(req->signature, signature, sig_len); } if(key_format == key_format_arg_PEM) { @@ -879,9 +593,9 @@ request_out: return ret; } -static bool selfsign_certificate(SCARDHANDLE *card, enum enum_key_format key_format, +static bool selfsign_certificate(ykpiv_state *state, enum enum_key_format key_format, const char *input_file_name, const char *slot, char *subject, - const char *output_file_name, int verbose) { + const char *output_file_name) { FILE *input_file = NULL; FILE *output_file = NULL; bool ret = false; @@ -892,8 +606,8 @@ static bool selfsign_certificate(SCARDHANDLE *card, enum enum_key_format key_for unsigned int digest_len = DIGEST_LEN; unsigned char algorithm; int key = 0; - unsigned char signinput[256]; - int len = 0; + unsigned char *signinput; + size_t len = 0; sscanf(slot, "%x", &key); @@ -923,6 +637,10 @@ static bool selfsign_certificate(SCARDHANDLE *card, enum enum_key_format key_for fprintf(stderr, "Failed to allocate certificate structure.\n"); goto selfsign_out; } + if(!X509_set_version(x509, 2)) { + fprintf(stderr, "Failed to set certificate version.\n"); + goto selfsign_out; + } if(!X509_set_pubkey(x509, public_key)) { fprintf(stderr, "Failed to set the certificate public key.\n"); goto selfsign_out; @@ -961,27 +679,29 @@ static bool selfsign_certificate(SCARDHANDLE *card, enum enum_key_format key_for goto selfsign_out; } switch(algorithm) { - case 0x6: - len = 128; - case 0x7: - if(len == 0) { - len = 256; - } - RSA_padding_add_PKCS1_type_1(signinput, len, digest, sizeof(digest)); + case YKPIV_ALGO_RSA1024: + case YKPIV_ALGO_RSA2048: + signinput = digest; + len = sizeof(digest); x509->sig_alg->algorithm = OBJ_nid2obj(NID_sha256WithRSAEncryption); break; - case 0x11: - x509->sig_alg->algorithm = OBJ_nid2obj(NID_ecdsa_with_SHA256); + case YKPIV_ALGO_ECCP256: + signinput = digest + sizeof(sha256oid); len = DIGEST_LEN; - memcpy(signinput, digest + sizeof(sha256oid), DIGEST_LEN); + x509->sig_alg->algorithm = OBJ_nid2obj(NID_ecdsa_with_SHA256); break; default: fprintf(stderr, "Unsupported algorithm %x.\n", algorithm); goto selfsign_out; } - if(sign_data(card, signinput, len, algorithm, key, x509->signature, - verbose) == false) { - goto selfsign_out; + { + unsigned char signature[1024]; + size_t sig_len = sizeof(signature); + if(ykpiv_sign_data(state, signinput, len, signature, &sig_len, algorithm, key) + != YKPIV_OK) { + goto selfsign_out; + } + M_ASN1_BIT_STRING_set(x509->signature, signature, sig_len); } if(key_format == key_format_arg_PEM) { @@ -1010,45 +730,36 @@ selfsign_out: return ret; } -static bool verify_pin(SCARDHANDLE *card, const char *pin, int verbose) { - APDU apdu; - unsigned char data[0xff]; - unsigned long recv_len = sizeof(data); - int sw; - size_t len = strlen(pin); +static bool verify_pin(ykpiv_state *state, const char *pin) { + int tries = -1; + ykpiv_rc res; + int len = strlen(pin); if(len > 8) { fprintf(stderr, "Maximum 8 digits of PIN supported.\n"); - return false; } - memset(apdu.raw, 0, sizeof(apdu.raw)); - apdu.st.ins = 0x20; - apdu.st.p1 = 0x00; - apdu.st.p2 = 0x80; - apdu.st.lc = 0x08; - memcpy(apdu.st.data, pin, len); - if(len < 8) { - memset(apdu.st.data + len, 0xff, 8 - len); - } - sw = send_data(card, &apdu, data, &recv_len, verbose); - if(sw == 0x9000) { + res = ykpiv_verify(state, pin, &tries); + if(res == YKPIV_OK) { return true; - } else if((sw >> 8) == 0x63) { - fprintf(stderr, "Pin verification failed, %d tries left before pin is blocked.\n", sw & 0xff); - } else if(sw == 0x6983) { - fprintf(stderr, "Pin code blocked, use unblock-pin action to unblock.\n"); + } else if(res == YKPIV_WRONG_PIN) { + if(tries > 0) { + fprintf(stderr, "Pin verification failed, %d tries left before pin is blocked.\n", tries); + } else { + fprintf(stderr, "Pin code blocked, use unblock-pin action to unblock.\n"); + } } else { - fprintf(stderr, "Pin code verification failed with code %x.\n", sw); + fprintf(stderr, "Pin code verification failed: '%s'\n", ykpiv_strerror(res)); } return false; } /* this function is called for all three of change-pin, change-puk and unblock pin * since they're very similar in what data they use. */ -static bool change_pin(SCARDHANDLE *card, enum enum_action action, const char *pin, - const char *new_pin, int verbose) { - APDU apdu; +static bool change_pin(ykpiv_state *state, enum enum_action action, const char *pin, + const char *new_pin) { + unsigned char templ[] = {0, YKPIV_INS_CHANGE_REFERENCE, 0, 0x81}; + unsigned char indata[0x10]; unsigned char data[0xff]; unsigned long recv_len = sizeof(data); int sw; @@ -1060,20 +771,23 @@ static bool change_pin(SCARDHANDLE *card, enum enum_action action, const char *p return false; } - memset(apdu.raw, 0, sizeof(apdu.raw)); - apdu.st.ins = action == action_arg_unblockMINUS_pin ? 0x2c : 0x24; - apdu.st.p2 = action == action_arg_changeMINUS_puk ? 0x81 : 0x80; - apdu.st.lc = 0x10; - memcpy(apdu.st.data, pin, pin_len); + if(action == action_arg_unblockMINUS_pin) { + templ[1] = YKPIV_INS_RESET_RETRY; + } + else if(action == action_arg_changeMINUS_pin) { + templ[3] = 0x80; + } + memcpy(indata, pin, pin_len); if(pin_len < 8) { - memset(apdu.st.data + pin_len, 0xff, 8 - pin_len); + memset(indata + pin_len, 0xff, 8 - pin_len); } - memcpy(apdu.st.data + 8, new_pin, new_len); + memcpy(indata + 8, new_pin, new_len); if(new_len < 8) { - memset(apdu.st.data + 8 + new_len, 0xff, 16 - new_len); + memset(indata + 8 + new_len, 0xff, 16 - new_len); } - sw = send_data(card, &apdu, data, &recv_len, verbose); - if(sw != 0x9000) { + if(ykpiv_transfer_data(state, templ, indata, sizeof(indata), data, &recv_len, &sw) != YKPIV_OK) { + return false; + } else if(sw != 0x9000) { if((sw >> 8) == 0x63) { int tries = sw & 0xff; fprintf(stderr, "Failed verifying %s code, now %d tries left before blocked.\n", @@ -1092,370 +806,21 @@ static bool change_pin(SCARDHANDLE *card, enum enum_action action, const char *p return true; } -static bool delete_certificate(SCARDHANDLE *card, enum enum_slot slot, int verbose) { - APDU apdu; - unsigned char objdata[7]; - unsigned char *ptr = objdata; - unsigned char data[0xff]; - unsigned long recv_len = sizeof(data); - int sw; - bool ret = false; +static bool delete_certificate(ykpiv_state *state, enum enum_slot slot) { int object = get_object_id(slot); - *ptr++ = 0x5c; - *ptr++ = 0x03; - *ptr++ = (object >> 16) & 0xff; - *ptr++ = (object >> 8) & 0xff; - *ptr++ = object & 0xff; - *ptr++ = 0x53; - *ptr++ = 0x00; /* length 0 means we'll delete the object */ - - memset(apdu.raw, 0, sizeof(apdu.raw)); - apdu.st.ins = 0xdb; - apdu.st.p1 = 0x3f; - apdu.st.p2 = 0xff; - - sw = transfer_data(card, &apdu, objdata, 7, data, &recv_len, verbose); - if(sw != 0x9000) { - fprintf(stderr, "Failed loading certificate to device with code %x.\n", sw); - } else { - ret = true; - } - return ret; -} - -static bool sign_data(SCARDHANDLE *card, unsigned char *signinput, int in_len, - unsigned char algorithm, unsigned char key, ASN1_BIT_STRING *sig, int verbose) { - unsigned char indata[1024]; - unsigned char *dataptr = indata; - unsigned char data[1024]; - unsigned long recv_len = sizeof(data); - int sw; - int bytes; - APDU apdu; - int len; - - memset(apdu.raw, 0, sizeof(apdu.raw)); - apdu.st.ins = 0x87; - apdu.st.p1 = algorithm; - apdu.st.p2 = key; - - if(in_len < 0x80) { - bytes = 1; - } else if(in_len < 0xff) { - bytes = 2; - } else { - bytes = 3; - } - - *dataptr++ = 0x7c; - dataptr += set_length(dataptr, in_len + bytes + 3); - *dataptr++ = 0x82; - *dataptr++ = 0x00; - *dataptr++ = 0x81; - dataptr += set_length(dataptr, in_len); - memcpy(dataptr, signinput, (size_t)in_len); - dataptr += in_len; - - sw = transfer_data(card, &apdu, indata, dataptr - indata, data, &recv_len, verbose); - if(sw != 0x9000) { - fprintf(stderr, "Failed sign command with code %x.\n", sw); + if(ykpiv_save_object(state, object, NULL, 0) != YKPIV_OK) { + fprintf(stderr, "Failed deleting object.\n"); return false; - } - /* skip the first 7c tag */ - if(data[0] != 0x7c) { - fprintf(stderr, "Failed parsing signature reply.\n"); - return false; - } - dataptr = data + 1; - dataptr += get_length(dataptr, &len); - /* skip the 82 tag */ - if(*dataptr != 0x82) { - fprintf(stderr, "Failed parsing signature reply.\n"); - return false; - } - dataptr++; - dataptr += get_length(dataptr, &len); - M_ASN1_BIT_STRING_set(sig, dataptr, len); - return true; -} - -static FILE *open_file(const char *file_name, int mode) { - FILE *file; - if(!strcmp(file_name, "-")) { - file = mode == INPUT ? stdin : stdout; } else { - file = fopen(file_name, mode == INPUT ? "r" : "w"); - if(!file) { - fprintf(stderr, "Failed opening '%s'!\n", file_name); - return NULL; - } + fprintf(stdout, "Certificate deleted.\n"); + return true; } - return file; -} - -static unsigned char get_algorithm(EVP_PKEY *key) { - int type = EVP_PKEY_type(key->type); - switch(type) { - case EVP_PKEY_RSA: - { - RSA *rsa = EVP_PKEY_get1_RSA(key); - int size = RSA_size(rsa); - if(size == 256) { - return 0x7; - } else if(size == 128) { - return 0x6; - } else { - fprintf(stderr, "Unuseable key of %d bits, only 1024 and 2048 is supported.\n", size * 8); - return 0; - } - } - case EVP_PKEY_EC: - { - EC_KEY *ec = EVP_PKEY_get1_EC_KEY(key); - const EC_GROUP *group = EC_KEY_get0_group(ec); - int curve = EC_GROUP_get_curve_name(group); - if(curve == NID_X9_62_prime256v1) { - return 0x11; - } else { - fprintf(stderr, "Unknown EC curve %d\n", curve); - return 0; - } - } - default: - fprintf(stderr, "Unknown algorithm %d.\n", type); - return 0; - } -} - -static X509_NAME *parse_name(char *name) { - X509_NAME *parsed = NULL; - char *ptr = name; - char *part; - if(*name != '/') { - fprintf(stderr, "Name does not start with '/'!\n"); - return NULL; - } - parsed = X509_NAME_new(); - if(!parsed) { - fprintf(stderr, "Failed to allocate memory\n"); - return NULL; - } - while((part = strtok(ptr, "/"))) { - char *key; - char *value; - char *equals = strchr(part, '='); - if(!equals) { - fprintf(stderr, "The part '%s' doesn't seem to contain a =.\n", part); - goto parse_err; - } - *equals++ = '\0'; - value = equals; - key = part; - - ptr = NULL; - if(!key) { - fprintf(stderr, "Malformed name (%s)\n", part); - goto parse_err; - } - if(!value) { - fprintf(stderr, "Malformed name (%s)\n", part); - goto parse_err; - } - if(!X509_NAME_add_entry_by_txt(parsed, key, MBSTRING_UTF8, (unsigned char*)value, -1, -1, 0)) { - fprintf(stderr, "Failed adding %s=%s to name.\n", key, value); - goto parse_err; - } - } - return parsed; -parse_err: - X509_NAME_free(parsed); - return NULL; -} - -static int transfer_data(SCARDHANDLE *card, APDU *apdu_tmpl, unsigned char *in_data, - long in_len, unsigned char *out_data, unsigned long *out_len, - int verbose) { - unsigned char *in_ptr = in_data; - unsigned long max_out = *out_len; - int sw = 0; - *out_len = 0; - - while(in_ptr < in_data + in_len) { - size_t this_size = 0xff; - unsigned long recv_len = 0xff; - unsigned char data[0xff]; - APDU apdu; - - memset(apdu.raw, 0, sizeof(apdu.raw)); - memcpy(apdu.raw, apdu_tmpl->raw, 4); - if(in_ptr + 0xff < in_data + in_len) { - apdu.st.cla = 0x10; - } else { - this_size = (size_t)((in_data + in_len) - in_ptr); - } - if(verbose > 2) { - fprintf(stderr, "Going to send %lu bytes in this go.\n", (unsigned long)this_size); - } - apdu.st.lc = this_size; - memcpy(apdu.st.data, in_ptr, this_size); - sw = send_data(card, &apdu, data, &recv_len, verbose); - if(sw != 0x9000 && sw >> 8 != 0x61) { - return sw; - } - if(*out_len + recv_len - 2 > max_out) { - fprintf(stderr, "Output buffer to small, wanted to write %lu, max was %lu.\n", *out_len + recv_len - 2, max_out); - return 0; - } - memcpy(out_data, data, recv_len - 2); - out_data += recv_len - 2; - *out_len += recv_len - 2; - in_ptr += this_size; - } - while(sw >> 8 == 0x61) { - APDU apdu; - unsigned long recv_len = 0xff; - unsigned char data[0xff]; - - if(verbose > 2) { - fprintf(stderr, "The card indicates there is %d bytes more data for us.\n", sw & 0xff); - } - - memset(apdu.raw, 0, sizeof(apdu.raw)); - apdu.st.ins = 0xc0; - sw = send_data(card, &apdu, data, &recv_len, verbose); - if(sw != 0x9000 && sw >> 8 != 0x61) { - return sw; - } - if(*out_len + recv_len - 2 > max_out) { - fprintf(stderr, "Output buffer to small, wanted to write %lu, max was %lu.", *out_len + recv_len - 2, max_out); - } - memcpy(out_data, data, recv_len - 2); - out_data += recv_len - 2; - *out_len += recv_len - 2; - } - return sw; -} - -static int send_data(SCARDHANDLE *card, APDU *apdu, unsigned char *data, - unsigned long *recv_len, int verbose) { - long rc; - int sw; - unsigned int send_len = (unsigned int)(apdu->st.lc + 5); - - if(verbose > 1) { - fprintf(stderr, "> "); - dump_hex(apdu->raw, send_len); - fprintf(stderr, "\n"); - } - rc = SCardTransmit(*card, SCARD_PCI_T1, apdu->raw, send_len, NULL, data, recv_len); - if(rc != SCARD_S_SUCCESS) { - fprintf (stderr, "error: SCardTransmit failed, rc=%08lx\n", rc); - return 0; - } - - if(verbose > 1) { - fprintf(stderr, "< "); - dump_hex(data, *recv_len); - fprintf(stderr, "\n"); - } - if(*recv_len >= 2) { - sw = (data[*recv_len - 2] << 8) | data[*recv_len - 1]; - } else { - sw = 0; - } - return sw; -} - -static void dump_hex(const unsigned char *buf, unsigned int len) { - unsigned int i; - for (i = 0; i < len; i++) { - fprintf(stderr, "%02x ", buf[i]); - } -} - -static bool parse_key(char *key_arg, unsigned char *key, int verbose) { - int i; - char key_part[4] = {0}; - int key_len = strlen(key_arg); - - if(key_len != KEY_LEN * 2) { - fprintf(stderr, "Wrong key size, should be %d characters (was %d).\n", KEY_LEN * 2, key_len); - return false; - } - for(i = 0; i < KEY_LEN; i++) { - key_part[0] = *key_arg++; - key_part[1] = *key_arg++; - if(sscanf(key_part, "%hhx", &key[i]) != 1) { - fprintf(stderr, "Failed parsing key at position %d.\n", i); - return false; - } - } - if(verbose > 1) { - fprintf(stderr, "parsed key: "); - dump_hex(key, KEY_LEN); - fprintf(stderr, "\n"); - } - return true; -} - -static int get_length(unsigned char *buffer, int *len) { - if(buffer[0] < 0x81) { - *len = buffer[0]; - return 1; - } else if((*buffer & 0x7f) == 1) { - *len = buffer[1]; - return 2; - } else if((*buffer & 0x7f) == 2) { - *len = (buffer[1] << 8) + buffer[2]; - return 3; - } - return 0; -} - -static int set_length(unsigned char *buffer, int length) { - if(length < 0x80) { - *buffer++ = length; - return 1; - } else if(length < 0xff) { - *buffer++ = 0x81; - *buffer++ = length; - return 2; - } else { - *buffer++ = 0x82; - *buffer++ = (length >> 8) & 0xff; - *buffer++ = length & 0xff; - return 3; - } -} - -static int get_object_id(enum enum_slot slot) { - int object; - - switch(slot) { - case slot_arg_9a: - object = 0x5fc105; - break; - case slot_arg_9c: - object = 0x5fc10a; - break; - case slot_arg_9d: - object = 0x5fc10b; - break; - case slot_arg_9e: - object = 0x5fc101; - break; - case slot__NULL: - default: - object = 0; - } - return object; } int main(int argc, char *argv[]) { struct gengetopt_args_info args_info; - SCARDHANDLE card; - SCARDCONTEXT context; + ykpiv_state *state; unsigned char key[KEY_LEN]; int verbosity; enum enum_action action; @@ -1468,21 +833,21 @@ int main(int argc, char *argv[]) { verbosity = args_info.verbose_arg + (int)args_info.verbose_given; - if(parse_key(args_info.key_arg, key, verbosity) == false) { + if(ykpiv_init(&state, verbosity) != YKPIV_OK) { + fprintf(stderr, "Failed initializing library.\n"); return EXIT_FAILURE; } - if(connect_reader(&card, &context, args_info.reader_arg, verbosity) == false) { + if(ykpiv_connect(state, args_info.reader_arg) != YKPIV_OK) { fprintf(stderr, "Failed to connect to reader.\n"); return EXIT_FAILURE; } - if(select_applet(&card, verbosity) == false) { - fprintf(stderr, "Failed to select applet.\n"); + if(ykpiv_parse_key(state, args_info.key_arg, key) != YKPIV_OK) { return EXIT_FAILURE; } - if(authenticate(&card, key, verbosity) == false) { + if(ykpiv_authenticate(state, key) != YKPIV_OK) { fprintf(stderr, "Failed authentication with the applet.\n"); return EXIT_FAILURE; } @@ -1500,11 +865,11 @@ int main(int argc, char *argv[]) { } switch(action) { case action_arg_version: - print_version(&card, verbosity); + print_version(state); break; case action_arg_generate: if(args_info.slot_arg != slot__NULL) { - if(generate_key(&card, args_info.slot_orig, args_info.algorithm_arg, args_info.output_arg, args_info.key_format_arg, verbosity) == false) { + if(generate_key(state, args_info.slot_orig, args_info.algorithm_arg, args_info.output_arg, args_info.key_format_arg) == false) { ret = EXIT_FAILURE; } } else { @@ -1515,9 +880,9 @@ int main(int argc, char *argv[]) { case action_arg_setMINUS_mgmMINUS_key: if(args_info.new_key_arg) { unsigned char new_key[KEY_LEN]; - if(parse_key(args_info.new_key_arg, new_key, verbosity) == false) { + if(ykpiv_parse_key(state, args_info.new_key_arg, new_key) != YKPIV_OK) { ret = EXIT_FAILURE; - } else if(set_mgm_key(&card, new_key, verbosity) == false) { + } else if(ykpiv_set_mgmkey(state, new_key) != YKPIV_OK) { ret = EXIT_FAILURE; } else { printf("Successfully set new management key.\n"); @@ -1528,7 +893,8 @@ int main(int argc, char *argv[]) { } break; case action_arg_reset: - if(reset(&card, verbosity) == false) { + if(reset(state) == false) { + fprintf(stderr, "Reset failed, are pincodes blocked?\n"); ret = EXIT_FAILURE; } else { printf("Successfully reset the applet.\n"); @@ -1536,7 +902,7 @@ int main(int argc, char *argv[]) { break; case action_arg_pinMINUS_retries: if(args_info.pin_retries_arg && args_info.puk_retries_arg) { - if(set_pin_retries(&card, args_info.pin_retries_arg, args_info.puk_retries_arg, verbosity) == false) { + if(set_pin_retries(state, args_info.pin_retries_arg, args_info.puk_retries_arg, verbosity) == false) { ret = EXIT_FAILURE; } else { printf("Successfully changed pin retries to %d and puk retries to %d, both codes have been reset to default now.\n", @@ -1549,7 +915,7 @@ int main(int argc, char *argv[]) { break; case action_arg_importMINUS_key: if(args_info.slot_arg != slot__NULL) { - if(import_key(&card, args_info.key_format_arg, args_info.input_arg, args_info.slot_orig, args_info.password_arg, verbosity) == false) { + if(import_key(state, args_info.key_format_arg, args_info.input_arg, args_info.slot_orig, args_info.password_arg) == false) { ret = EXIT_FAILURE; } else { printf("Successfully imported a new private key.\n"); @@ -1561,7 +927,7 @@ int main(int argc, char *argv[]) { break; case action_arg_importMINUS_certificate: if(args_info.slot_arg != slot__NULL) { - if(import_cert(&card, args_info.key_format_arg, args_info.input_arg, args_info.slot_arg, args_info.password_arg, verbosity) == false) { + if(import_cert(state, args_info.key_format_arg, args_info.input_arg, args_info.slot_arg, args_info.password_arg) == false) { ret = EXIT_FAILURE; } else { printf("Successfully imported a new certificate.\n"); @@ -1572,7 +938,7 @@ int main(int argc, char *argv[]) { } break; case action_arg_setMINUS_chuid: - if(set_chuid(&card, verbosity) == false) { + if(set_chuid(state, verbosity) == false) { ret = EXIT_FAILURE; } else { printf("Successfully set new CHUID.\n"); @@ -1586,15 +952,15 @@ int main(int argc, char *argv[]) { fprintf(stderr, "The request-certificate action needs a subject (-S) to operate on.\n"); ret = EXIT_FAILURE; } else { - if(request_certificate(&card, args_info.key_format_arg, args_info.input_arg, - args_info.slot_orig, args_info.subject_arg, args_info.output_arg, verbosity) == false) { + if(request_certificate(state, args_info.key_format_arg, args_info.input_arg, + args_info.slot_orig, args_info.subject_arg, args_info.output_arg) == false) { ret = EXIT_FAILURE; } } break; case action_arg_verifyMINUS_pin: if(args_info.pin_arg) { - if(verify_pin(&card, args_info.pin_arg, verbosity)) { + if(verify_pin(state, args_info.pin_arg)) { printf("Successfully verified PIN.\n"); } else { ret = EXIT_FAILURE; @@ -1608,7 +974,7 @@ int main(int argc, char *argv[]) { case action_arg_changeMINUS_puk: case action_arg_unblockMINUS_pin: if(args_info.pin_arg && args_info.new_pin_arg) { - if(change_pin(&card, action, args_info.pin_arg, args_info.new_pin_arg, verbosity)) { + if(change_pin(state, action, args_info.pin_arg, args_info.new_pin_arg)) { if(action == action_arg_unblockMINUS_pin) { printf("Successfully unblocked the pin code.\n"); } else { @@ -1633,22 +999,22 @@ int main(int argc, char *argv[]) { fprintf(stderr, "The selfsign-certificate action needs a subject (-S) to operate on.\n"); ret = EXIT_FAILURE; } else { - if(selfsign_certificate(&card, args_info.key_format_arg, args_info.input_arg, - args_info.slot_orig, args_info.subject_arg, args_info.output_arg, verbosity) == false) { + if(selfsign_certificate(state, args_info.key_format_arg, args_info.input_arg, + args_info.slot_orig, args_info.subject_arg, args_info.output_arg) == false) { ret = EXIT_FAILURE; } } break; case action_arg_deleteMINUS_certificate: - if(args_info.slot_arg == slot__NULL) { - fprintf(stderr, "The delete-certificate action needs a slot (-s) to operate on.\n"); - ret = EXIT_FAILURE; - } else { - if(delete_certificate(&card, args_info.slot_arg, verbosity) == false) { - ret = EXIT_FAILURE; - } - } - break; + if(args_info.slot_arg == slot__NULL) { + fprintf(stderr, "The delete-certificate action needs a slot (-s) to operate on.\n"); + ret = EXIT_FAILURE; + } else { + if(delete_certificate(state, args_info.slot_arg) == false) { + ret = EXIT_FAILURE; + } + } + break; case action__NULL: default: fprintf(stderr, "Wrong action. %d.\n", action); @@ -1659,6 +1025,7 @@ int main(int argc, char *argv[]) { } } + ykpiv_done(state); EVP_cleanup(); return ret; } diff --git a/yubico-piv-tool.h2m b/tool/yubico-piv-tool.h2m similarity index 100% rename from yubico-piv-tool.h2m rename to tool/yubico-piv-tool.h2m