From f19405fdb175ed0bccd91f1ebc6746641beda121 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Mon, 16 Jun 2014 09:24:18 +0200 Subject: [PATCH 01/71] move the cli stuff to subdir tool --- .gitignore | 1 + Makefile.am | 29 +--------- configure.ac | 1 + tool/Makefile.am | 54 +++++++++++++++++++ cmdline.ggo => tool/cmdline.ggo | 0 yubico-piv-tool.c => tool/yubico-piv-tool.c | 0 .../yubico-piv-tool.h2m | 0 7 files changed, 57 insertions(+), 28 deletions(-) create mode 100644 tool/Makefile.am rename cmdline.ggo => tool/cmdline.ggo (100%) rename yubico-piv-tool.c => tool/yubico-piv-tool.c (100%) rename yubico-piv-tool.h2m => tool/yubico-piv-tool.h2m (100%) diff --git a/.gitignore b/.gitignore index 9e7d978..55ca5d3 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ yubico-piv-tool.o *.c~ *.h~ ChangeLog +build-aux/compile build-aux/config.guess build-aux/config.sub build-aux/depcomp diff --git a/Makefile.am b/Makefile.am index 3f688eb..3ff81f3 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 = tool tests 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/configure.ac b/configure.ac index f231157..dd587c0 100644 --- a/configure.ac +++ b/configure.ac @@ -136,6 +136,7 @@ fi AC_CONFIG_FILES([ Makefile + tool/Makefile tests/Makefile ]) AC_OUTPUT diff --git a/tool/Makefile.am b/tool/Makefile.am new file mode 100644 index 0000000..b41525c --- /dev/null +++ b/tool/Makefile.am @@ -0,0 +1,54 @@ +# 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. + +AM_CFLAGS = $(WERROR_CFLAGS) $(WARN_CFLAGS) +AM_CPPFLAGS = $(OPENSSL_CFLAGS) $(PCSC_CFLAGS) + +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=$(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/yubico-piv-tool.c b/tool/yubico-piv-tool.c similarity index 100% rename from yubico-piv-tool.c rename to tool/yubico-piv-tool.c 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 From 1a4287061524e992aea56af01ee3550d9aa9495d Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Mon, 16 Jun 2014 10:22:05 +0200 Subject: [PATCH 02/71] start of the ykpiv library --- Makefile.am | 2 +- configure.ac | 14 ++++ lib/Makefile.am | 49 ++++++++++++++ lib/version.c | 142 ++++++++++++++++++++++++++++++++++++++++ lib/ykpiv-version.h.in | 91 +++++++++++++++++++++++++ lib/ykpiv.c | 28 ++++++++ lib/ykpiv.h | 42 ++++++++++++ lib/ykpiv.map | 34 ++++++++++ lib/ykpiv.pc.in | 12 ++++ m4/ld-version-script.m4 | 53 +++++++++++++++ 10 files changed, 466 insertions(+), 1 deletion(-) create mode 100644 lib/Makefile.am create mode 100644 lib/version.c create mode 100644 lib/ykpiv-version.h.in create mode 100644 lib/ykpiv.c create mode 100644 lib/ykpiv.h create mode 100644 lib/ykpiv.map create mode 100644 lib/ykpiv.pc.in create mode 100644 m4/ld-version-script.m4 diff --git a/Makefile.am b/Makefile.am index 3ff81f3..566d588 100644 --- a/Makefile.am +++ b/Makefile.am @@ -24,7 +24,7 @@ # 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 = tool tests +SUBDIRS = lib tool tests ACLOCAL_AMFLAGS = -I m4 diff --git a/configure.ac b/configure.ac index dd587c0..6c7b113 100644 --- a/configure.ac +++ b/configure.ac @@ -28,9 +28,18 @@ AC_INIT([yubico-piv-tool], [0.0.4]) 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 @@ -39,6 +48,8 @@ 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'])], @@ -136,8 +147,11 @@ fi AC_CONFIG_FILES([ Makefile + lib/Makefile tool/Makefile 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..2fddc41 --- /dev/null +++ b/lib/Makefile.am @@ -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. + +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 +libykpiv_la_includedir = $(includedir)/ykpiv +libykpiv_la_include_HEADERS = ykpiv.h ykpiv-version.h + +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/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..312531b --- /dev/null +++ b/lib/ykpiv.c @@ -0,0 +1,28 @@ + /* + * 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. + * + */ diff --git a/lib/ykpiv.h b/lib/ykpiv.h new file mode 100644 index 0000000..b4f2a8c --- /dev/null +++ b/lib/ykpiv.h @@ -0,0 +1,42 @@ + /* + * 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 + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/ykpiv.map b/lib/ykpiv.map new file mode 100644 index 0000000..a4a66a4 --- /dev/null +++ b/lib/ykpiv.map @@ -0,0 +1,34 @@ +# 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; + +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 < Date: Mon, 16 Jun 2014 10:22:23 +0200 Subject: [PATCH 03/71] ignore more --- .gitignore | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 55ca5d3..7a20b20 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,10 @@ 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 @@ -42,3 +37,19 @@ yubico-piv-tool-*-mac.zip.sig tests/basic.sh.log tests/basic.sh.trs tests/test-suite.log +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/ + From 39d9a81a34e121223316f034a9eee70f749219fa Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Mon, 16 Jun 2014 10:44:34 +0200 Subject: [PATCH 04/71] let the tool link with the library --- tool/Makefile.am | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tool/Makefile.am b/tool/Makefile.am index b41525c..6a46735 100644 --- a/tool/Makefile.am +++ b/tool/Makefile.am @@ -26,12 +26,14 @@ AM_CFLAGS = $(WERROR_CFLAGS) $(WARN_CFLAGS) AM_CPPFLAGS = $(OPENSSL_CFLAGS) $(PCSC_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_SOURCES += cmdline.ggo cmdline.c cmdline.h yubico_piv_tool_LDADD = $(OPENSSL_LIBS) $(PCSC_LIBS) yubico_piv_tool_LDADD += $(LTLIBWINSCARD) $(PCSC_MACOSX_LIBS) +yubico_piv_tool_LDADD += ../lib/libykpiv.la cmdline.c cmdline.h: cmdline.ggo Makefile.am gengetopt --input $^ From f10e83c76c6e9ce8281c0fbfb61676d9841f8d4f Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Mon, 16 Jun 2014 10:44:48 +0200 Subject: [PATCH 05/71] let configure.ac try to find gengetopt --- configure.ac | 1 + tool/Makefile.am | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 6c7b113..5f72fe5 100644 --- a/configure.ac +++ b/configure.ac @@ -44,6 +44,7 @@ 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) diff --git a/tool/Makefile.am b/tool/Makefile.am index 6a46735..f36d4e0 100644 --- a/tool/Makefile.am +++ b/tool/Makefile.am @@ -36,7 +36,7 @@ yubico_piv_tool_LDADD += $(LTLIBWINSCARD) $(PCSC_MACOSX_LIBS) yubico_piv_tool_LDADD += ../lib/libykpiv.la cmdline.c cmdline.h: cmdline.ggo Makefile.am - gengetopt --input $^ + $(GENGETOPT) --input $^ BUILT_SOURCES = cmdline.c cmdline.h MAINTAINERCLEANFILES = $(BUILT_SOURCES) From 980c8656e9d20b9e2a9cb6917f8402c6a4765c9a Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Mon, 16 Jun 2014 10:46:57 +0200 Subject: [PATCH 06/71] bump version properly with librarisation --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 5f72fe5..225bc03 100644 --- a/configure.ac +++ b/configure.ac @@ -24,7 +24,7 @@ # 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]) From fe5632e60345f374efc97c31c30c90730ce12ae5 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Mon, 16 Jun 2014 12:35:28 +0200 Subject: [PATCH 07/71] start library structure --- lib/Makefile.am | 2 +- lib/internal.h | 38 ++++++++++++++++++++++++++++++++++++++ lib/ykpiv.c | 30 ++++++++++++++++++++++++++++++ lib/ykpiv.h | 5 +++++ lib/ykpiv.map | 2 ++ 5 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 lib/internal.h diff --git a/lib/Makefile.am b/lib/Makefile.am index 2fddc41..72b2229 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -29,7 +29,7 @@ AM_CPPFLAGS = $(OPENSSL_CFLAGS) $(PCSC_CFLAGS) lib_LTLIBRARIES = libykpiv.la -libykpiv_la_SOURCES = ykpiv.c version.c ykpiv.pc.in ykpiv.map +libykpiv_la_SOURCES = ykpiv.c version.c ykpiv.pc.in ykpiv.map internal.h libykpiv_la_includedir = $(includedir)/ykpiv libykpiv_la_include_HEADERS = ykpiv.h ykpiv-version.h diff --git a/lib/internal.h b/lib/internal.h new file mode 100644 index 0000000..d213456 --- /dev/null +++ b/lib/internal.h @@ -0,0 +1,38 @@ + /* + * 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 + +struct ykpiv_state { + SCARDCONTEXT card; + SCARDHANDLE cardHandle; +}; + +#endif diff --git a/lib/ykpiv.c b/lib/ykpiv.c index 312531b..3467b0c 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -26,3 +26,33 @@ * for the parts of OpenSSL used as well as that of the covered work. * */ + +#include +#include + +#if BACKEND_PCSC +#if defined HAVE_PCSC_WINSCARD_H +# include +# include +#else +# include +#endif +#endif + +#include "internal.h" +#include "ykpiv.h" + +int ykpiv_init(ykpiv_state **state) { + ykpiv_state *s = malloc(sizeof(ykpiv_state)); + if(s == NULL) { + return -1; + } + memset(s, 0, sizeof(ykpiv_state)); + *state = s; + return 0; +} + +int ykpiv_done(ykpiv_state *state) { + free(state); + return 0; +} diff --git a/lib/ykpiv.h b/lib/ykpiv.h index b4f2a8c..6b74800 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -35,6 +35,11 @@ extern "C" { #endif + typedef struct ykpiv_state ykpiv_state; + + int ykpiv_init(ykpiv_state **state); + int ykpiv_done(ykpiv_state *state); + #ifdef __cplusplus } #endif diff --git a/lib/ykpiv.map b/lib/ykpiv.map index a4a66a4..c9a2ae7 100644 --- a/lib/ykpiv.map +++ b/lib/ykpiv.map @@ -28,6 +28,8 @@ YKPIV_0.1.0 { global: ykpiv_check_version; + ykpiv_init; + ykpiv_map; local: *; From 0d4dd2fea128b5c8e72e9d8f49aabad3cf548a85 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Mon, 16 Jun 2014 13:19:48 +0200 Subject: [PATCH 08/71] start moving code to the library, and add error codes --- .gitignore | 2 + configure.ac | 1 + lib/Makefile.am | 1 + lib/error.c | 95 +++++++++++++++++++++++++++++++++++++++++ lib/internal.h | 20 ++++++++- lib/ykpiv.c | 109 +++++++++++++++++++++++++++++++++++++++--------- lib/ykpiv.h | 16 ++++++- lib/ykpiv.map | 2 + 8 files changed, 222 insertions(+), 24 deletions(-) create mode 100644 lib/error.c diff --git a/.gitignore b/.gitignore index 7a20b20..a4cf1e3 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,8 @@ yubico-piv-tool-*-mac.zip.sig tests/basic.sh.log tests/basic.sh.trs tests/test-suite.log +lib/error.lo +lib/error.o lib/libykpiv.la lib/version.lo lib/version.o diff --git a/configure.ac b/configure.ac index 225bc03..9184a28 100644 --- a/configure.ac +++ b/configure.ac @@ -136,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]) diff --git a/lib/Makefile.am b/lib/Makefile.am index 72b2229..ee448ab 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -30,6 +30,7 @@ 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 diff --git a/lib/error.c b/lib/error.c new file mode 100644 index 0000000..12da084 --- /dev/null +++ b/lib/error.c @@ -0,0 +1,95 @@ + /* + * 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"), +}; + +/** + * 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 index d213456..605c470 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -30,9 +30,25 @@ #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 card; - SCARDHANDLE cardHandle; + SCARDCONTEXT context; + SCARDHANDLE card; + int verbose; +}; + +unsigned const char aid[] = { + 0xa0, 0x00, 0x00, 0x03, 0x08 }; #endif diff --git a/lib/ykpiv.c b/lib/ykpiv.c index 3467b0c..86cd9aa 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -29,30 +29,99 @@ #include #include - -#if BACKEND_PCSC -#if defined HAVE_PCSC_WINSCARD_H -# include -# include -#else -# include -#endif -#endif +#include #include "internal.h" #include "ykpiv.h" -int ykpiv_init(ykpiv_state **state) { - ykpiv_state *s = malloc(sizeof(ykpiv_state)); - if(s == NULL) { - return -1; - } - memset(s, 0, sizeof(ykpiv_state)); - *state = s; - return 0; +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; } -int ykpiv_done(ykpiv_state *state) { - free(state); - return 0; +ykpiv_rc ykpiv_done(ykpiv_state *state) { + free(state); + 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; + } + + return YKPIV_OK; } diff --git a/lib/ykpiv.h b/lib/ykpiv.h index 6b74800..bb504ce 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -37,8 +37,20 @@ extern "C" typedef struct ykpiv_state ykpiv_state; - int ykpiv_init(ykpiv_state **state); - int ykpiv_done(ykpiv_state *state); + typedef enum { + YKPIV_OK = 0, + YKPIV_MEMORY_ERROR = -1, + YKPIV_PCSC_ERROR = -2, + } 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); + + #ifdef __cplusplus } diff --git a/lib/ykpiv.map b/lib/ykpiv.map index c9a2ae7..1207e64 100644 --- a/lib/ykpiv.map +++ b/lib/ykpiv.map @@ -28,6 +28,8 @@ YKPIV_0.1.0 { global: ykpiv_check_version; + ykpiv_strerror_name; + ykpiv_strerror; ykpiv_init; ykpiv_map; From f1c5302407bef049550a81aee4911128985d845f Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Mon, 16 Jun 2014 15:00:52 +0200 Subject: [PATCH 09/71] break out connect and send data to the library exporting ykpiv_connect() ykpiv_send_data() ykpiv_transfer_data() --- lib/error.c | 1 + lib/ykpiv.c | 123 +++++++++++++++++++ lib/ykpiv.h | 12 +- lib/ykpiv.map | 5 +- tool/yubico-piv-tool.c | 267 ++++++++++++++--------------------------- 5 files changed, 232 insertions(+), 176 deletions(-) diff --git a/lib/error.c b/lib/error.c index 12da084..c2e1c78 100644 --- a/lib/error.c +++ b/lib/error.c @@ -44,6 +44,7 @@ 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"), }; /** diff --git a/lib/ykpiv.c b/lib/ykpiv.c index 86cd9aa..86d736f 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -30,10 +30,32 @@ #include #include #include +#include #include "internal.h" #include "ykpiv.h" +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(const unsigned char *buf, unsigned int len) { + unsigned int i; + for (i = 0; i < len; i++) { + fprintf(stderr, "%02x ", buf[i]); + } +} + ykpiv_rc ykpiv_init(ykpiv_state **state, int verbose) { ykpiv_state *s = malloc(sizeof(ykpiv_state)); if(s == NULL) { @@ -125,3 +147,104 @@ ykpiv_rc ykpiv_connect(ykpiv_state *state, const char *wanted) { return YKPIV_OK; } + +ykpiv_rc ykpiv_transfer_data(ykpiv_state *state, uint32_t template, + unsigned char *in_data, long in_len, + unsigned char *out_data, unsigned long *out_len, int *sw) { + unsigned char *in_ptr = in_data; + unsigned long max_out = *out_len; + ykpiv_rc res; + *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)); + YKPIV_APDU_UNPACK(apdu.raw, template); + 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 = ykpiv_send_data(state, apdu.raw, 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(*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 = ykpiv_send_data(state, apdu.raw, 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; +} + +ykpiv_rc ykpiv_send_data(ykpiv_state *state, unsigned char *apdu, + unsigned char *data, unsigned long *recv_len, int *sw) { + long rc; + unsigned int send_len = (unsigned int)(apdu[4] + 5); /* magic numbers.. */ + + if(state->verbose > 1) { + fprintf(stderr, "> "); + dump_hex(apdu, send_len); + fprintf(stderr, "\n"); + } + rc = SCardTransmit(state->card, SCARD_PCI_T1, apdu, 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; +} diff --git a/lib/ykpiv.h b/lib/ykpiv.h index bb504ce..f19de7a 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -30,6 +30,8 @@ #ifndef YKPIV_H #define YKPIV_H +#include + #ifdef __cplusplus extern "C" { @@ -41,6 +43,7 @@ extern "C" YKPIV_OK = 0, YKPIV_MEMORY_ERROR = -1, YKPIV_PCSC_ERROR = -2, + YKPIV_SIZE_ERROR = -3, } ykpiv_rc; const char *ykpiv_strerror(ykpiv_rc err); @@ -49,8 +52,15 @@ extern "C" 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_transfer_data(ykpiv_state *state, uint32_t template, + unsigned char *in_data, long in_len, + unsigned char *out_data, unsigned long *out_len, int *sw); + ykpiv_rc ykpiv_send_data(ykpiv_state *state, unsigned char *apdu, + unsigned char *data, unsigned long *recv_len, int *sw); - +#define YKPIV_APDU_TEMPLATE(i,j,k,l) ((i & 0xff) << 24 | (j & 0xff) << 16 | (k & 0xff) << 8 | (l & 0xff)) +#define YKPIV_APDU_UNPACK(c, t) (c[0] = ((t >> 24) & 0xff), \ + c[1] = ((t >> 16) & 0xff), c[2] = ((t >> 8) & 0xff), c[3] = (t & 0xff)) #ifdef __cplusplus } diff --git a/lib/ykpiv.map b/lib/ykpiv.map index 1207e64..1554d62 100644 --- a/lib/ykpiv.map +++ b/lib/ykpiv.map @@ -31,7 +31,10 @@ global: ykpiv_strerror_name; ykpiv_strerror; ykpiv_init; - ykpiv_map; + ykpiv_done; + ykpiv_connect; + ykpiv_send_data; + ykpiv_transfer_data; local: *; diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 8339df8..3f821d8 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -32,6 +32,8 @@ #include #include +#include "ykpiv.h" + #if BACKEND_PCSC #if defined HAVE_PCSC_WINSCARD_H # include @@ -93,15 +95,12 @@ union u_APDU { 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, +static bool sign_data(ykpiv_state*, unsigned char*, int, unsigned char, unsigned char, ASN1_BIT_STRING*, int); static int get_object_id(enum enum_slot slot); @@ -171,7 +170,7 @@ static bool connect_reader(SCARDHANDLE *card, SCARDCONTEXT *context, const char return true; } -static bool select_applet(SCARDHANDLE *card, int verbose) { +static bool select_applet(ykpiv_state *state, int verbose) { APDU apdu; unsigned char data[0xff]; unsigned long recv_len = sizeof(data); @@ -183,15 +182,16 @@ static bool select_applet(SCARDHANDLE *card, int verbose) { apdu.st.lc = sizeof(aid); memcpy(apdu.st.data, aid, sizeof(aid)); - sw = send_data(card, &apdu, data, &recv_len, verbose); - if(sw == 0x9000) { + if(ykpiv_send_data(state, apdu.raw, data, &recv_len, &sw) != YKPIV_OK) { + return false; + } else if(sw == 0x9000) { return true; } return false; } -static bool authenticate(SCARDHANDLE *card, unsigned const char *key, int verbose) { +static bool authenticate(ykpiv_state *state, unsigned const char *key, int verbose) { APDU apdu; unsigned char data[0xff]; DES_cblock challenge; @@ -221,8 +221,9 @@ static bool authenticate(SCARDHANDLE *card, unsigned const char *key, int verbos 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) { + if(ykpiv_send_data(state, apdu.raw, data, &recv_len, &sw) != YKPIV_OK) { + return false; + } else if(sw != 0x9000) { return false; } memcpy(challenge, data + 4, 8); @@ -254,8 +255,9 @@ static bool authenticate(SCARDHANDLE *card, unsigned const char *key, int verbos 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) { + if(ykpiv_send_data(state, apdu.raw, data, &recv_len, &sw) != YKPIV_OK) { + return false; + } else if(sw != 0x9000) { return false; } } @@ -272,7 +274,7 @@ static bool authenticate(SCARDHANDLE *card, unsigned const char *key, int verbos } } -static void print_version(SCARDHANDLE *card, int verbose) { +static void print_version(ykpiv_state *state, int verbose) { APDU apdu; unsigned char data[0xff]; unsigned long recv_len = sizeof(data); @@ -280,17 +282,17 @@ static void print_version(SCARDHANDLE *card, int verbose) { memset(apdu.raw, 0, sizeof(apdu)); apdu.st.ins = 0xfd; - sw = send_data(card, &apdu, data, &recv_len, verbose); - if(sw == 0x9000) { + if(ykpiv_send_data(state, apdu.raw, data, &recv_len, &sw) != YKPIV_OK) { + printf("Failed to retreive apple version.\n"); + } else if(sw == 0x9000) { printf("Applet version %d.%d.%d found.\n", data[0], data[1], data[2]); } else { printf("Applet version not found. Status code: %x\n", sw); } } -static bool generate_key(SCARDHANDLE *card, const char *slot, enum enum_algorithm algorithm, +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, int verbose) { - APDU apdu; unsigned char in_data[5]; unsigned char data[1024]; unsigned long recv_len = sizeof(data); @@ -313,9 +315,6 @@ static bool generate_key(SCARDHANDLE *card, const char *slot, enum enum_algorith 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; @@ -335,9 +334,10 @@ static bool generate_key(SCARDHANDLE *card, const char *slot, enum enum_algorith 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, YKPIV_APDU_TEMPLATE(0, 0x47, 0, key), 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,7 +436,7 @@ generate_out: return ret; } -static bool set_mgm_key(SCARDHANDLE *card, unsigned const char *new_key, int verbose) { +static bool set_mgm_key(ykpiv_state *state, unsigned const char *new_key, int verbose) { APDU apdu; unsigned char data[0xff]; unsigned long recv_len = sizeof(data); @@ -463,15 +463,15 @@ static bool set_mgm_key(SCARDHANDLE *card, unsigned const char *new_key, int ver 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) { + if(ykpiv_send_data(state, apdu.raw, data, &recv_len, &sw) != YKPIV_OK) { + return false; + } else if(sw == 0x9000) { return true; } return false; } -static bool reset(SCARDHANDLE *card, int verbose) { +static bool reset(ykpiv_state *state, int verbose) { APDU apdu; unsigned char data[0xff]; unsigned long recv_len = sizeof(data); @@ -480,15 +480,15 @@ static bool reset(SCARDHANDLE *card, int verbose) { 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_send_data(state, apdu.raw, 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) { +static bool set_pin_retries(ykpiv_state *state, int pin_retries, int puk_retries, int verbose) { APDU apdu; unsigned char data[0xff]; unsigned long recv_len = sizeof(data); @@ -507,15 +507,15 @@ static bool set_pin_retries(SCARDHANDLE *card, int pin_retries, int puk_retries, 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_send_data(state, apdu.raw, 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, +static bool import_key(ykpiv_state *state, enum enum_key_format key_format, const char *input_file_name, const char *slot, char *password, int verbose) { int key = 0; FILE *input_file = NULL; @@ -600,11 +600,12 @@ static bool import_key(SCARDHANDLE *card, enum enum_key_format key_format, 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, YKPIV_APDU_TEMPLATE(0x00, 0xfe, algorithm, key), 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,7 +625,7 @@ import_out: return ret; } -static bool import_cert(SCARDHANDLE *card, enum enum_key_format cert_format, +static bool import_cert(ykpiv_state *state, enum enum_key_format cert_format, const char *input_file_name, enum enum_slot slot, char *password, int verbose) { bool ret = false; FILE *input_file = NULL; @@ -703,8 +704,9 @@ static bool import_cert(SCARDHANDLE *card, enum enum_key_format cert_format, apdu.st.p1 = 0x3f; apdu.st.p2 = 0xff; - sw = transfer_data(card, &apdu, certdata, certptr - certdata, data, &recv_len, verbose); - if(sw != 0x9000) { + if(ykpiv_transfer_data(state, YKPIV_APDU_TEMPLATE(0, 0xdb, 0x3f, 0xff), certdata, certptr - certdata, data, &recv_len, &sw) != YKPIV_OK) { + fprintf(stderr, "Failed commands with device.\n"); + } else if(sw != 0x9000) { fprintf(stderr, "Failed loading certificate to device with code %x.\n", sw); } else { ret = true; @@ -728,7 +730,7 @@ import_cert_out: return ret; } -static bool set_chuid(SCARDHANDLE *card, int verbose) { +static bool set_chuid(ykpiv_state *state, int verbose) { APDU apdu; unsigned char data[0xff]; unsigned char *dataptr = apdu.st.data; @@ -751,15 +753,17 @@ static bool set_chuid(SCARDHANDLE *card, int verbose) { 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) { + if(ykpiv_send_data(state, apdu.raw, data, &recv_len, &sw) != YKPIV_OK) { + fprintf(stderr, "Failed communicating with device.\n"); + return false; + } else if(sw != 0x9000) { fprintf(stderr, "Failed setting CHUID.\n"); 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) { X509_REQ *req = NULL; @@ -848,7 +852,7 @@ static bool request_certificate(SCARDHANDLE *card, enum enum_key_format key_form fprintf(stderr, "Unsupported algorithm %x.\n", algorithm); goto request_out; } - if(sign_data(card, signinput, len, algorithm, key, req->signature, + if(sign_data(state, signinput, len, algorithm, key, req->signature, verbose) == false) { goto request_out; } @@ -879,7 +883,7 @@ 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) { FILE *input_file = NULL; @@ -979,7 +983,7 @@ static bool selfsign_certificate(SCARDHANDLE *card, enum enum_key_format key_for fprintf(stderr, "Unsupported algorithm %x.\n", algorithm); goto selfsign_out; } - if(sign_data(card, signinput, len, algorithm, key, x509->signature, + if(sign_data(state, signinput, len, algorithm, key, x509->signature, verbose) == false) { goto selfsign_out; } @@ -1010,7 +1014,7 @@ selfsign_out: return ret; } -static bool verify_pin(SCARDHANDLE *card, const char *pin, int verbose) { +static bool verify_pin(ykpiv_state *state, const char *pin, int verbose) { APDU apdu; unsigned char data[0xff]; unsigned long recv_len = sizeof(data); @@ -1031,8 +1035,9 @@ static bool verify_pin(SCARDHANDLE *card, const char *pin, int verbose) { if(len < 8) { memset(apdu.st.data + len, 0xff, 8 - len); } - sw = send_data(card, &apdu, data, &recv_len, verbose); - if(sw == 0x9000) { + if(ykpiv_send_data(state, apdu.raw, data, &recv_len, &sw) != YKPIV_OK) { + return false; + } else if(sw == 0x9000) { return true; } else if((sw >> 8) == 0x63) { fprintf(stderr, "Pin verification failed, %d tries left before pin is blocked.\n", sw & 0xff); @@ -1046,7 +1051,7 @@ static bool verify_pin(SCARDHANDLE *card, const char *pin, int verbose) { /* 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, +static bool change_pin(ykpiv_state *state, enum enum_action action, const char *pin, const char *new_pin, int verbose) { APDU apdu; unsigned char data[0xff]; @@ -1072,8 +1077,9 @@ static bool change_pin(SCARDHANDLE *card, enum enum_action action, const char *p if(new_len < 8) { memset(apdu.st.data + 8 + new_len, 0xff, 16 - new_len); } - sw = send_data(card, &apdu, data, &recv_len, verbose); - if(sw != 0x9000) { + if(ykpiv_send_data(state, apdu.raw, 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,7 +1098,7 @@ 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) { +static bool delete_certificate(ykpiv_state *state, enum enum_slot slot, int verbose) { APDU apdu; unsigned char objdata[7]; unsigned char *ptr = objdata; @@ -1115,16 +1121,17 @@ static bool delete_certificate(SCARDHANDLE *card, enum enum_slot slot, int verbo 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); + if(ykpiv_transfer_data(state, YKPIV_APDU_TEMPLATE(0, 0xdb, 0x3f, 0xff), objdata, 7, data, &recv_len, &sw) != YKPIV_OK) { + return false; + } else if(sw != 0x9000) { + fprintf(stderr, "Failed deleting 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, +static bool sign_data(ykpiv_state *state, 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; @@ -1157,8 +1164,10 @@ static bool sign_data(SCARDHANDLE *card, unsigned char *signinput, int 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) { + if(ykpiv_transfer_data(state, YKPIV_APDU_TEMPLATE(0, 0x87, algorithm, key), indata, dataptr - indata, data, &recv_len, &sw) != YKPIV_OK) { + fprintf(stderr, "Sign command failed to communicate.\n"); + return false; + } else if(sw != 0x9000) { fprintf(stderr, "Failed sign command with code %x.\n", sw); return false; } @@ -1273,100 +1282,6 @@ parse_err: 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++) { @@ -1454,8 +1369,7 @@ static int get_object_id(enum enum_slot slot) { 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; @@ -1472,17 +1386,22 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - if(connect_reader(&card, &context, args_info.reader_arg, verbosity) == false) { + if(ykpiv_init(&state, verbosity) != YKPIV_OK) { + fprintf(stderr, "Failed initializing library.\n"); + return EXIT_FAILURE; + } + + 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) { + if(select_applet(state, verbosity) == false) { fprintf(stderr, "Failed to select applet.\n"); return EXIT_FAILURE; } - if(authenticate(&card, key, verbosity) == false) { + if(authenticate(state, key, verbosity) == false) { fprintf(stderr, "Failed authentication with the applet.\n"); return EXIT_FAILURE; } @@ -1500,11 +1419,11 @@ int main(int argc, char *argv[]) { } switch(action) { case action_arg_version: - print_version(&card, verbosity); + print_version(state, verbosity); 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, verbosity) == false) { ret = EXIT_FAILURE; } } else { @@ -1517,7 +1436,7 @@ int main(int argc, char *argv[]) { unsigned char new_key[KEY_LEN]; if(parse_key(args_info.new_key_arg, new_key, verbosity) == false) { ret = EXIT_FAILURE; - } else if(set_mgm_key(&card, new_key, verbosity) == false) { + } else if(set_mgm_key(state, new_key, verbosity) == false) { ret = EXIT_FAILURE; } else { printf("Successfully set new management key.\n"); @@ -1528,7 +1447,7 @@ int main(int argc, char *argv[]) { } break; case action_arg_reset: - if(reset(&card, verbosity) == false) { + if(reset(state, verbosity) == false) { ret = EXIT_FAILURE; } else { printf("Successfully reset the applet.\n"); @@ -1536,7 +1455,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 +1468,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, verbosity) == false) { ret = EXIT_FAILURE; } else { printf("Successfully imported a new private key.\n"); @@ -1561,7 +1480,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, verbosity) == false) { ret = EXIT_FAILURE; } else { printf("Successfully imported a new certificate.\n"); @@ -1572,7 +1491,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,7 +1505,7 @@ 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, + 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, verbosity) == false) { ret = EXIT_FAILURE; } @@ -1594,7 +1513,7 @@ int main(int argc, char *argv[]) { 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, verbosity)) { printf("Successfully verified PIN.\n"); } else { ret = EXIT_FAILURE; @@ -1608,7 +1527,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, verbosity)) { if(action == action_arg_unblockMINUS_pin) { printf("Successfully unblocked the pin code.\n"); } else { @@ -1633,7 +1552,7 @@ 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, + 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, verbosity) == false) { ret = EXIT_FAILURE; } @@ -1644,7 +1563,7 @@ int main(int argc, char *argv[]) { 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) { + if(delete_certificate(state, args_info.slot_arg, verbosity) == false) { ret = EXIT_FAILURE; } } From 2d2f14b9f1584f946af0748280b36bf318fb1db1 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 17 Jun 2014 08:31:58 +0200 Subject: [PATCH 10/71] fix the test with new directory structure --- tests/basic.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/basic.sh b/tests/basic.sh index f29f4c1..5a58b81 100755 --- a/tests/basic.sh +++ b/tests/basic.sh @@ -31,10 +31,12 @@ set -e -HELP_OUTPUT=$(../yubico-piv-tool$EXEEXT --help) +BIN="../tool/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 From 03c028a4ef8b0a9e191bff3155af717d2f925cb7 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 17 Jun 2014 09:08:56 +0200 Subject: [PATCH 11/71] get rid of the YKPIV_APDU_* macros transport the starting 4 bytes as an array instead --- lib/ykpiv.c | 4 ++-- lib/ykpiv.h | 6 +----- tool/yubico-piv-tool.c | 43 +++++++++++++++++++----------------------- 3 files changed, 22 insertions(+), 31 deletions(-) diff --git a/lib/ykpiv.c b/lib/ykpiv.c index 86d736f..0391ec4 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -148,7 +148,7 @@ ykpiv_rc ykpiv_connect(ykpiv_state *state, const char *wanted) { return YKPIV_OK; } -ykpiv_rc ykpiv_transfer_data(ykpiv_state *state, uint32_t template, +ykpiv_rc ykpiv_transfer_data(ykpiv_state *state, unsigned char *templ, unsigned char *in_data, long in_len, unsigned char *out_data, unsigned long *out_len, int *sw) { unsigned char *in_ptr = in_data; @@ -163,7 +163,7 @@ ykpiv_rc ykpiv_transfer_data(ykpiv_state *state, uint32_t template, APDU apdu; memset(apdu.raw, 0, sizeof(apdu.raw)); - YKPIV_APDU_UNPACK(apdu.raw, template); + memcpy(apdu.raw, templ, 4); if(in_ptr + 0xff < in_data + in_len) { apdu.st.cla = 0x10; } else { diff --git a/lib/ykpiv.h b/lib/ykpiv.h index f19de7a..a8111e5 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -52,16 +52,12 @@ extern "C" 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_transfer_data(ykpiv_state *state, uint32_t template, + ykpiv_rc ykpiv_transfer_data(ykpiv_state *state, unsigned char *templ, unsigned char *in_data, long in_len, unsigned char *out_data, unsigned long *out_len, int *sw); ykpiv_rc ykpiv_send_data(ykpiv_state *state, unsigned char *apdu, unsigned char *data, unsigned long *recv_len, int *sw); -#define YKPIV_APDU_TEMPLATE(i,j,k,l) ((i & 0xff) << 24 | (j & 0xff) << 16 | (k & 0xff) << 8 | (l & 0xff)) -#define YKPIV_APDU_UNPACK(c, t) (c[0] = ((t >> 24) & 0xff), \ - c[1] = ((t >> 16) & 0xff), c[2] = ((t >> 8) & 0xff), c[3] = (t & 0xff)) - #ifdef __cplusplus } #endif diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 3f821d8..0a34c08 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -291,10 +291,12 @@ static void print_version(ykpiv_state *state, int verbose) { } } -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, int verbose) { +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, int verbose) { unsigned char in_data[5]; unsigned char data[1024]; + unsigned char templ[] = {0, 0x47, 0, 0}; unsigned long recv_len = sizeof(data); unsigned long received = 0; int sw; @@ -309,6 +311,7 @@ static bool generate_key(ykpiv_state *state, const char *slot, enum enum_algorit EC_POINT *point = NULL; sscanf(slot, "%x", &key); + templ[3] = key; output_file = open_file(output_file_name, OUTPUT); if(!output_file) { @@ -334,7 +337,8 @@ static bool generate_key(ykpiv_state *state, const char *slot, enum enum_algorit fprintf(stderr, "Unexepcted algorithm.\n"); goto generate_out; } - if(ykpiv_transfer_data(state, YKPIV_APDU_TEMPLATE(0, 0x47, 0, key), in_data, sizeof(in_data), data, &recv_len, &sw) != YKPIV_OK) { + 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) { @@ -559,11 +563,11 @@ static bool import_key(ykpiv_state *state, 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, 0xfe, algorithm, key}; int sw; if(algorithm == 0x06 || algorithm == 0x07) { RSA *rsa_private_key = EVP_PKEY_get1_RSA(private_key); @@ -596,11 +600,8 @@ static bool import_key(ykpiv_state *state, 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; - if(ykpiv_transfer_data(state, YKPIV_APDU_TEMPLATE(0x00, 0xfe, algorithm, key), in_data, in_ptr - in_data, data, &recv_len, &sw) != YKPIV_OK) { + 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); @@ -662,10 +663,10 @@ static bool import_cert(ykpiv_state *state, enum enum_key_format cert_format, } { - APDU apdu; unsigned char certdata[2100]; unsigned char *certptr = certdata; unsigned char data[0xff]; + unsigned char templ[] = {0, 0xdb, 0x3f, 0xff}; unsigned long recv_len = sizeof(data); int cert_len = i2d_X509(cert, NULL); int bytes; @@ -699,12 +700,8 @@ static bool import_cert(ykpiv_state *state, 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; - - if(ykpiv_transfer_data(state, YKPIV_APDU_TEMPLATE(0, 0xdb, 0x3f, 0xff), certdata, certptr - certdata, data, &recv_len, &sw) != YKPIV_OK) { + if(ykpiv_transfer_data(state, templ, certdata, certptr - certdata, data, + &recv_len, &sw) != YKPIV_OK) { fprintf(stderr, "Failed commands with device.\n"); } else if(sw != 0x9000) { fprintf(stderr, "Failed loading certificate to device with code %x.\n", sw); @@ -1104,6 +1101,7 @@ static bool delete_certificate(ykpiv_state *state, enum enum_slot slot, int verb unsigned char *ptr = objdata; unsigned char data[0xff]; unsigned long recv_len = sizeof(data); + unsigned char templ[] = {0, 0xdb, 0x3f, 0xff}; int sw; bool ret = false; int object = get_object_id(slot); @@ -1121,7 +1119,8 @@ static bool delete_certificate(ykpiv_state *state, enum enum_slot slot, int verb apdu.st.p1 = 0x3f; apdu.st.p2 = 0xff; - if(ykpiv_transfer_data(state, YKPIV_APDU_TEMPLATE(0, 0xdb, 0x3f, 0xff), objdata, 7, data, &recv_len, &sw) != YKPIV_OK) { + if(ykpiv_transfer_data(state, templ, objdata, 7, data, &recv_len, &sw) + != YKPIV_OK) { return false; } else if(sw != 0x9000) { fprintf(stderr, "Failed deleting certificate to device with code %x.\n", sw); @@ -1136,17 +1135,12 @@ static bool sign_data(ykpiv_state *state, unsigned char *signinput, int in_len, unsigned char indata[1024]; unsigned char *dataptr = indata; unsigned char data[1024]; + unsigned char templ[] = {0, 0x87, algorithm, key}; 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) { @@ -1164,7 +1158,8 @@ static bool sign_data(ykpiv_state *state, unsigned char *signinput, int in_len, memcpy(dataptr, signinput, (size_t)in_len); dataptr += in_len; - if(ykpiv_transfer_data(state, YKPIV_APDU_TEMPLATE(0, 0x87, algorithm, key), indata, dataptr - indata, data, &recv_len, &sw) != YKPIV_OK) { + if(ykpiv_transfer_data(state, templ, indata, dataptr - indata, data, + &recv_len, &sw) != YKPIV_OK) { fprintf(stderr, "Sign command failed to communicate.\n"); return false; } else if(sw != 0x9000) { From fc0fac1730f7790fe0f23ba1c666b6720b4051f0 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 17 Jun 2014 09:09:53 +0200 Subject: [PATCH 12/71] drop the connect_reader() function --- tool/yubico-piv-tool.c | 66 ------------------------------------------ 1 file changed, 66 deletions(-) diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 0a34c08..275bc9d 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -104,72 +104,6 @@ static bool sign_data(ykpiv_state*, unsigned char*, int, unsigned char, unsigned 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(ykpiv_state *state, int verbose) { APDU apdu; unsigned char data[0xff]; From 7f80de659a71cbb537cc0ad557b4abd2f11d9de3 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 17 Jun 2014 09:14:14 +0200 Subject: [PATCH 13/71] drop verbose parameter to alot of functions since it's stored in state now --- tool/yubico-piv-tool.c | 62 ++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 275bc9d..334562e 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -101,10 +101,10 @@ static X509_NAME *parse_name(char*); static unsigned char get_algorithm(EVP_PKEY*); static FILE *open_file(const char*, int); static bool sign_data(ykpiv_state*, unsigned char*, int, unsigned char, unsigned char, - ASN1_BIT_STRING*, int); + ASN1_BIT_STRING*); static int get_object_id(enum enum_slot slot); -static bool select_applet(ykpiv_state *state, int verbose) { +static bool select_applet(ykpiv_state *state) { APDU apdu; unsigned char data[0xff]; unsigned long recv_len = sizeof(data); @@ -125,7 +125,7 @@ static bool select_applet(ykpiv_state *state, int verbose) { return false; } -static bool authenticate(ykpiv_state *state, unsigned const char *key, int verbose) { +static bool authenticate(ykpiv_state *state, unsigned const char *key) { APDU apdu; unsigned char data[0xff]; DES_cblock challenge; @@ -208,7 +208,7 @@ static bool authenticate(ykpiv_state *state, unsigned const char *key, int verbo } } -static void print_version(ykpiv_state *state, int verbose) { +static void print_version(ykpiv_state *state) { APDU apdu; unsigned char data[0xff]; unsigned long recv_len = sizeof(data); @@ -227,7 +227,7 @@ static void print_version(ykpiv_state *state, int verbose) { 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, int verbose) { + enum enum_key_format key_format) { unsigned char in_data[5]; unsigned char data[1024]; unsigned char templ[] = {0, 0x47, 0, 0}; @@ -374,7 +374,7 @@ generate_out: return ret; } -static bool set_mgm_key(ykpiv_state *state, unsigned const char *new_key, int verbose) { +static bool set_mgm_key(ykpiv_state *state, unsigned const char *new_key) { APDU apdu; unsigned char data[0xff]; unsigned long recv_len = sizeof(data); @@ -409,7 +409,7 @@ static bool set_mgm_key(ykpiv_state *state, unsigned const char *new_key, int ve return false; } -static bool reset(ykpiv_state *state, int verbose) { +static bool reset(ykpiv_state *state) { APDU apdu; unsigned char data[0xff]; unsigned long recv_len = sizeof(data); @@ -454,7 +454,7 @@ static bool set_pin_retries(ykpiv_state *state, int pin_retries, int puk_retries } static bool import_key(ykpiv_state *state, enum enum_key_format key_format, - const char *input_file_name, const char *slot, char *password, int verbose) { + const char *input_file_name, const char *slot, char *password) { int key = 0; FILE *input_file = NULL; EVP_PKEY *private_key = NULL; @@ -561,7 +561,7 @@ import_out: } static bool import_cert(ykpiv_state *state, enum enum_key_format cert_format, - const char *input_file_name, enum enum_slot slot, char *password, int verbose) { + const char *input_file_name, enum enum_slot slot, char *password) { bool ret = false; FILE *input_file = NULL; X509 *cert = NULL; @@ -696,7 +696,7 @@ static bool set_chuid(ykpiv_state *state, int verbose) { 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; @@ -783,8 +783,7 @@ static bool request_certificate(ykpiv_state *state, enum enum_key_format key_for fprintf(stderr, "Unsupported algorithm %x.\n", algorithm); goto request_out; } - if(sign_data(state, signinput, len, algorithm, key, req->signature, - verbose) == false) { + if(sign_data(state, signinput, len, algorithm, key, req->signature) == false) { goto request_out; } @@ -816,7 +815,7 @@ request_out: 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; @@ -914,8 +913,7 @@ static bool selfsign_certificate(ykpiv_state *state, enum enum_key_format key_fo fprintf(stderr, "Unsupported algorithm %x.\n", algorithm); goto selfsign_out; } - if(sign_data(state, signinput, len, algorithm, key, x509->signature, - verbose) == false) { + if(sign_data(state, signinput, len, algorithm, key, x509->signature)) { goto selfsign_out; } @@ -945,7 +943,7 @@ selfsign_out: return ret; } -static bool verify_pin(ykpiv_state *state, const char *pin, int verbose) { +static bool verify_pin(ykpiv_state *state, const char *pin) { APDU apdu; unsigned char data[0xff]; unsigned long recv_len = sizeof(data); @@ -983,7 +981,7 @@ static bool verify_pin(ykpiv_state *state, const char *pin, int verbose) { /* 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(ykpiv_state *state, enum enum_action action, const char *pin, - const char *new_pin, int verbose) { + const char *new_pin) { APDU apdu; unsigned char data[0xff]; unsigned long recv_len = sizeof(data); @@ -1029,7 +1027,7 @@ static bool change_pin(ykpiv_state *state, enum enum_action action, const char * return true; } -static bool delete_certificate(ykpiv_state *state, enum enum_slot slot, int verbose) { +static bool delete_certificate(ykpiv_state *state, enum enum_slot slot) { APDU apdu; unsigned char objdata[7]; unsigned char *ptr = objdata; @@ -1065,7 +1063,7 @@ static bool delete_certificate(ykpiv_state *state, enum enum_slot slot, int verb } static bool sign_data(ykpiv_state *state, unsigned char *signinput, int in_len, - unsigned char algorithm, unsigned char key, ASN1_BIT_STRING *sig, int verbose) { + unsigned char algorithm, unsigned char key, ASN1_BIT_STRING *sig) { unsigned char indata[1024]; unsigned char *dataptr = indata; unsigned char data[1024]; @@ -1325,12 +1323,12 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - if(select_applet(state, verbosity) == false) { + if(select_applet(state) == false) { fprintf(stderr, "Failed to select applet.\n"); return EXIT_FAILURE; } - if(authenticate(state, key, verbosity) == false) { + if(authenticate(state, key) == false) { fprintf(stderr, "Failed authentication with the applet.\n"); return EXIT_FAILURE; } @@ -1348,11 +1346,11 @@ int main(int argc, char *argv[]) { } switch(action) { case action_arg_version: - print_version(state, verbosity); + print_version(state); break; case action_arg_generate: if(args_info.slot_arg != slot__NULL) { - if(generate_key(state, 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 { @@ -1365,7 +1363,7 @@ int main(int argc, char *argv[]) { unsigned char new_key[KEY_LEN]; if(parse_key(args_info.new_key_arg, new_key, verbosity) == false) { ret = EXIT_FAILURE; - } else if(set_mgm_key(state, new_key, verbosity) == false) { + } else if(set_mgm_key(state, new_key) == false) { ret = EXIT_FAILURE; } else { printf("Successfully set new management key.\n"); @@ -1376,7 +1374,7 @@ int main(int argc, char *argv[]) { } break; case action_arg_reset: - if(reset(state, verbosity) == false) { + if(reset(state) == false) { ret = EXIT_FAILURE; } else { printf("Successfully reset the applet.\n"); @@ -1397,7 +1395,7 @@ int main(int argc, char *argv[]) { break; case action_arg_importMINUS_key: if(args_info.slot_arg != slot__NULL) { - if(import_key(state, 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"); @@ -1409,7 +1407,7 @@ int main(int argc, char *argv[]) { break; case action_arg_importMINUS_certificate: if(args_info.slot_arg != slot__NULL) { - if(import_cert(state, 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"); @@ -1435,14 +1433,14 @@ int main(int argc, char *argv[]) { ret = EXIT_FAILURE; } else { 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, verbosity) == false) { + 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(state, args_info.pin_arg, verbosity)) { + if(verify_pin(state, args_info.pin_arg)) { printf("Successfully verified PIN.\n"); } else { ret = EXIT_FAILURE; @@ -1456,7 +1454,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(state, 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 { @@ -1482,7 +1480,7 @@ int main(int argc, char *argv[]) { ret = EXIT_FAILURE; } else { 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, verbosity) == false) { + args_info.slot_orig, args_info.subject_arg, args_info.output_arg) == false) { ret = EXIT_FAILURE; } } @@ -1492,7 +1490,7 @@ int main(int argc, char *argv[]) { 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, verbosity) == false) { + if(delete_certificate(state, args_info.slot_arg) == false) { ret = EXIT_FAILURE; } } From 56bee46ed3d150f6a7cd7433a5460b7571a90375 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 17 Jun 2014 09:15:47 +0200 Subject: [PATCH 14/71] call ykpiv_done() before exiting --- tool/yubico-piv-tool.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 334562e..b88cc9b 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -1505,6 +1505,7 @@ int main(int argc, char *argv[]) { } } + ykpiv_done(state); EVP_cleanup(); return ret; } From 9eb5c7fda15b3850742ec4841859e029c1a01f5e Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 17 Jun 2014 09:16:51 +0200 Subject: [PATCH 15/71] drop direct PCSC dependency for the tool --- tool/Makefile.am | 5 ++--- tool/yubico-piv-tool.c | 9 --------- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/tool/Makefile.am b/tool/Makefile.am index f36d4e0..6dd4dba 100644 --- a/tool/Makefile.am +++ b/tool/Makefile.am @@ -25,14 +25,13 @@ # for the parts of OpenSSL used as well as that of the covered work. AM_CFLAGS = $(WERROR_CFLAGS) $(WARN_CFLAGS) -AM_CPPFLAGS = $(OPENSSL_CFLAGS) $(PCSC_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_SOURCES += cmdline.ggo cmdline.c cmdline.h -yubico_piv_tool_LDADD = $(OPENSSL_LIBS) $(PCSC_LIBS) -yubico_piv_tool_LDADD += $(LTLIBWINSCARD) $(PCSC_MACOSX_LIBS) +yubico_piv_tool_LDADD = $(OPENSSL_LIBS) yubico_piv_tool_LDADD += ../lib/libykpiv.la cmdline.c cmdline.h: cmdline.ggo Makefile.am diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index b88cc9b..d3106bb 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -34,15 +34,6 @@ #include "ykpiv.h" -#if BACKEND_PCSC -#if defined HAVE_PCSC_WINSCARD_H -# include -# include -#else -# include -#endif -#endif - #include #include #include From 6dcb6798e61d00db022b9c54ee61f45b5ab31251 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 17 Jun 2014 09:20:32 +0200 Subject: [PATCH 16/71] let the ykpiv_connect() function select as well --- lib/error.c | 1 + lib/ykpiv.c | 22 ++++++++++++++++++++++ lib/ykpiv.h | 1 + tool/yubico-piv-tool.c | 31 ------------------------------- 4 files changed, 24 insertions(+), 31 deletions(-) diff --git a/lib/error.c b/lib/error.c index c2e1c78..a4dd16f 100644 --- a/lib/error.c +++ b/lib/error.c @@ -45,6 +45,7 @@ static const err_t errors[] = { 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"), }; /** diff --git a/lib/ykpiv.c b/lib/ykpiv.c index 0391ec4..0db5dca 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -145,6 +145,28 @@ ykpiv_rc ykpiv_connect(ykpiv_state *state, const char *wanted) { 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 = ykpiv_send_data(state, apdu.raw, data, &recv_len, &sw) != YKPIV_OK)) { + return res; + } else if(sw == 0x9000) { + return YKPIV_OK; + } + + return YKPIV_APPLET_ERROR; + } + return YKPIV_OK; } diff --git a/lib/ykpiv.h b/lib/ykpiv.h index a8111e5..5e65243 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -44,6 +44,7 @@ extern "C" YKPIV_MEMORY_ERROR = -1, YKPIV_PCSC_ERROR = -2, YKPIV_SIZE_ERROR = -3, + YKPIV_APPLET_ERROR = -4, } ykpiv_rc; const char *ykpiv_strerror(ykpiv_rc err); diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index d3106bb..6c73355 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -41,11 +41,6 @@ #include "cmdline.h" -unsigned const char aid[] = { - 0xa0, 0x00, 0x00, 0x03, 0x08 -}; - - /* FASC-N containing S9999F9999F999999F0F1F0000000000300001E encoded in * 4-bit BCD with 1 bit parity. run through the tools/fasc.pl script to get * bytes. */ @@ -95,27 +90,6 @@ static bool sign_data(ykpiv_state*, unsigned char*, int, unsigned char, unsigned ASN1_BIT_STRING*); static int get_object_id(enum enum_slot slot); -static bool select_applet(ykpiv_state *state) { - 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)); - - if(ykpiv_send_data(state, apdu.raw, data, &recv_len, &sw) != YKPIV_OK) { - return false; - } else if(sw == 0x9000) { - return true; - } - - return false; -} - static bool authenticate(ykpiv_state *state, unsigned const char *key) { APDU apdu; unsigned char data[0xff]; @@ -1314,11 +1288,6 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - if(select_applet(state) == false) { - fprintf(stderr, "Failed to select applet.\n"); - return EXIT_FAILURE; - } - if(authenticate(state, key) == false) { fprintf(stderr, "Failed authentication with the applet.\n"); return EXIT_FAILURE; From cb60c782f52fed2a339e393d2c96214c17a53ab2 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 17 Jun 2014 09:33:46 +0200 Subject: [PATCH 17/71] move authenticate to library as ykpiv_authenticate() --- lib/error.c | 2 + lib/ykpiv.c | 89 ++++++++++++++++++++++++++++++++++++++++++ lib/ykpiv.h | 3 ++ lib/ykpiv.map | 1 + tool/yubico-piv-tool.c | 85 +--------------------------------------- 5 files changed, 96 insertions(+), 84 deletions(-) diff --git a/lib/error.c b/lib/error.c index a4dd16f..6505ab6 100644 --- a/lib/error.c +++ b/lib/error.c @@ -46,6 +46,8 @@ static const err_t errors[] = { 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"), }; /** diff --git a/lib/ykpiv.c b/lib/ykpiv.c index 0db5dca..de2a434 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -32,6 +32,9 @@ #include #include +#include +#include + #include "internal.h" #include "ykpiv.h" @@ -270,3 +273,89 @@ ykpiv_rc ykpiv_send_data(ykpiv_state *state, unsigned char *apdu, } 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 = ykpiv_send_data(state, apdu.raw, 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 = 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) { + 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 = ykpiv_send_data(state, apdu.raw, 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; + } + } +} diff --git a/lib/ykpiv.h b/lib/ykpiv.h index 5e65243..7253198 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -45,6 +45,8 @@ extern "C" YKPIV_PCSC_ERROR = -2, YKPIV_SIZE_ERROR = -3, YKPIV_APPLET_ERROR = -4, + YKPIV_AUTHENTICATION_ERROR = -5, + YKPIV_RANDOMNESS_ERROR = -6, } ykpiv_rc; const char *ykpiv_strerror(ykpiv_rc err); @@ -58,6 +60,7 @@ extern "C" unsigned char *out_data, unsigned long *out_len, int *sw); ykpiv_rc ykpiv_send_data(ykpiv_state *state, unsigned char *apdu, unsigned char *data, unsigned long *recv_len, int *sw); + ykpiv_rc ykpiv_authenticate(ykpiv_state *state, const unsigned char *key); #ifdef __cplusplus } diff --git a/lib/ykpiv.map b/lib/ykpiv.map index 1554d62..a6812ce 100644 --- a/lib/ykpiv.map +++ b/lib/ykpiv.map @@ -35,6 +35,7 @@ global: ykpiv_connect; ykpiv_send_data; ykpiv_transfer_data; + ykpiv_authenticate; local: *; diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 6c73355..3bbcd37 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -90,89 +90,6 @@ static bool sign_data(ykpiv_state*, unsigned char*, int, unsigned char, unsigned ASN1_BIT_STRING*); static int get_object_id(enum enum_slot slot); -static bool 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; - - 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(ykpiv_send_data(state, apdu.raw, data, &recv_len, &sw) != YKPIV_OK) { - return false; - } else 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; - if(ykpiv_send_data(state, apdu.raw, data, &recv_len, &sw) != YKPIV_OK) { - return false; - } else 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(ykpiv_state *state) { APDU apdu; unsigned char data[0xff]; @@ -1288,7 +1205,7 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - if(authenticate(state, key) == false) { + if(ykpiv_authenticate(state, key) != YKPIV_OK) { fprintf(stderr, "Failed authentication with the applet.\n"); return EXIT_FAILURE; } From 1f8cff5711c450e1d3fe078b32865567cb556b38 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 17 Jun 2014 09:35:58 +0200 Subject: [PATCH 18/71] move apdu structure to internal.h --- lib/internal.h | 14 ++++++++++++++ lib/ykpiv.c | 14 -------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/internal.h b/lib/internal.h index 605c470..b506233 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -47,6 +47,20 @@ struct ykpiv_state { 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 }; diff --git a/lib/ykpiv.c b/lib/ykpiv.c index de2a434..a6f5572 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -38,20 +38,6 @@ #include "internal.h" #include "ykpiv.h" -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(const unsigned char *buf, unsigned int len) { unsigned int i; for (i = 0; i < len; i++) { From d62430c2d5e1a7b9fba2a7f7915453f95144f3c9 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 17 Jun 2014 09:37:37 +0200 Subject: [PATCH 19/71] more constness --- lib/ykpiv.c | 6 +++--- lib/ykpiv.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/ykpiv.c b/lib/ykpiv.c index a6f5572..d588e19 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -159,10 +159,10 @@ ykpiv_rc ykpiv_connect(ykpiv_state *state, const char *wanted) { return YKPIV_OK; } -ykpiv_rc ykpiv_transfer_data(ykpiv_state *state, unsigned char *templ, - unsigned char *in_data, long in_len, +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) { - unsigned char *in_ptr = in_data; + const unsigned char *in_ptr = in_data; unsigned long max_out = *out_len; ykpiv_rc res; *out_len = 0; diff --git a/lib/ykpiv.h b/lib/ykpiv.h index 7253198..662cb6d 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -55,8 +55,8 @@ extern "C" 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_transfer_data(ykpiv_state *state, unsigned char *templ, - unsigned char *in_data, long in_len, + 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_send_data(ykpiv_state *state, unsigned char *apdu, unsigned char *data, unsigned long *recv_len, int *sw); From 8eb955bd13d92b5acad9dc4cbb10f9035899d3ff Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 17 Jun 2014 09:58:37 +0200 Subject: [PATCH 20/71] make constants for algorithms, keys and instructions --- lib/ykpiv.c | 6 +++--- lib/ykpiv.h | 13 +++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/lib/ykpiv.c b/lib/ykpiv.c index d588e19..7aa8b3b 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -307,9 +307,9 @@ ykpiv_rc ykpiv_authenticate(ykpiv_state *state, unsigned const char *key) { 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 */ + 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; diff --git a/lib/ykpiv.h b/lib/ykpiv.h index 662cb6d..01d1136 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -62,6 +62,19 @@ extern "C" unsigned char *data, unsigned long *recv_len, int *sw); ykpiv_rc ykpiv_authenticate(ykpiv_state *state, const unsigned char *key); +#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_INS_AUTHENTICATE 0x87; + #ifdef __cplusplus } #endif From 01c844905a49998d28e9930dcaea0bf3cdfbbbb6 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 17 Jun 2014 09:58:55 +0200 Subject: [PATCH 21/71] add ykpiv_set_mgmkey() --- lib/error.c | 1 + lib/ykpiv.c | 38 ++++++++++++++++++++++++++++++++++++++ lib/ykpiv.h | 5 +++++ lib/ykpiv.map | 1 + tool/yubico-piv-tool.c | 37 +------------------------------------ 5 files changed, 46 insertions(+), 36 deletions(-) diff --git a/lib/error.c b/lib/error.c index 6505ab6..4f5aa9a 100644 --- a/lib/error.c +++ b/lib/error.c @@ -48,6 +48,7 @@ static const err_t errors[] = { 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."), }; /** diff --git a/lib/ykpiv.c b/lib/ykpiv.c index 7aa8b3b..62851aa 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -345,3 +345,41 @@ ykpiv_rc ykpiv_authenticate(ykpiv_state *state, unsigned const char *key) { } } } + +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 = ykpiv_send_data(state, apdu.raw, data, &recv_len, &sw)) != YKPIV_OK) { + return res; + } else if(sw == 0x9000) { + return YKPIV_OK; + } + return YKPIV_GENERIC_ERROR; +} diff --git a/lib/ykpiv.h b/lib/ykpiv.h index 01d1136..7d8924a 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -47,6 +47,7 @@ extern "C" YKPIV_APPLET_ERROR = -4, YKPIV_AUTHENTICATION_ERROR = -5, YKPIV_RANDOMNESS_ERROR = -6, + YKPIV_GENERIC_ERROR = -7, } ykpiv_rc; const char *ykpiv_strerror(ykpiv_rc err); @@ -61,6 +62,7 @@ extern "C" ykpiv_rc ykpiv_send_data(ykpiv_state *state, unsigned char *apdu, unsigned char *data, unsigned long *recv_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); #define YKPIV_ALGO_3DES 0x03; #define YKPIV_ALGO_RSA1024 0x06; @@ -75,6 +77,9 @@ extern "C" #define YKPIV_INS_AUTHENTICATE 0x87; + /* Yubico vendor specific instructions */ +#define YKPIV_INS_SET_MGMKEY 0xff; + #ifdef __cplusplus } #endif diff --git a/lib/ykpiv.map b/lib/ykpiv.map index a6812ce..20e07d9 100644 --- a/lib/ykpiv.map +++ b/lib/ykpiv.map @@ -36,6 +36,7 @@ global: ykpiv_send_data; ykpiv_transfer_data; ykpiv_authenticate; + ykpiv_set_mgmkey; local: *; diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 3bbcd37..86f4d0c 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -256,41 +256,6 @@ generate_out: return ret; } -static bool set_mgm_key(ykpiv_state *state, unsigned const char *new_key) { - 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); - if(ykpiv_send_data(state, apdu.raw, data, &recv_len, &sw) != YKPIV_OK) { - return false; - } else if(sw == 0x9000) { - return true; - } - return false; -} - static bool reset(ykpiv_state *state) { APDU apdu; unsigned char data[0xff]; @@ -1240,7 +1205,7 @@ int main(int argc, char *argv[]) { unsigned char new_key[KEY_LEN]; if(parse_key(args_info.new_key_arg, new_key, verbosity) == false) { ret = EXIT_FAILURE; - } else if(set_mgm_key(state, new_key) == false) { + } else if(ykpiv_set_mgmkey(state, new_key) != YKPIV_OK) { ret = EXIT_FAILURE; } else { printf("Successfully set new management key.\n"); From d1b20627216f187747a88d96d05c3fe220cc9f0d Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 17 Jun 2014 10:07:49 +0200 Subject: [PATCH 22/71] add ykpiv_parse_key() --- lib/error.c | 1 + lib/ykpiv.c | 30 ++++++++++++++++++++++++++++++ lib/ykpiv.h | 3 +++ lib/ykpiv.map | 1 + tool/yubico-piv-tool.c | 35 +++++------------------------------ 5 files changed, 40 insertions(+), 30 deletions(-) diff --git a/lib/error.c b/lib/error.c index 4f5aa9a..af3454f 100644 --- a/lib/error.c +++ b/lib/error.c @@ -49,6 +49,7 @@ static const err_t errors[] = { 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"), }; /** diff --git a/lib/ykpiv.c b/lib/ykpiv.c index 62851aa..ac8a357 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -383,3 +383,33 @@ ykpiv_rc ykpiv_set_mgmkey(ykpiv_state *state, const unsigned char *new_key) { } 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; +} diff --git a/lib/ykpiv.h b/lib/ykpiv.h index 7d8924a..847667c 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -48,6 +48,7 @@ extern "C" YKPIV_AUTHENTICATION_ERROR = -5, YKPIV_RANDOMNESS_ERROR = -6, YKPIV_GENERIC_ERROR = -7, + YKPIV_KEY_ERROR = -8, } ykpiv_rc; const char *ykpiv_strerror(ykpiv_rc err); @@ -63,6 +64,8 @@ extern "C" unsigned char *data, unsigned long *recv_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); #define YKPIV_ALGO_3DES 0x03; #define YKPIV_ALGO_RSA1024 0x06; diff --git a/lib/ykpiv.map b/lib/ykpiv.map index 20e07d9..34ab3fd 100644 --- a/lib/ykpiv.map +++ b/lib/ykpiv.map @@ -37,6 +37,7 @@ global: ykpiv_transfer_data; ykpiv_authenticate; ykpiv_set_mgmkey; + ykpiv_parse_key; local: *; diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 86f4d0c..051a765 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -1063,31 +1063,6 @@ static void dump_hex(const unsigned char *buf, unsigned int len) { } } -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]; @@ -1156,10 +1131,6 @@ 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) { - return EXIT_FAILURE; - } - if(ykpiv_init(&state, verbosity) != YKPIV_OK) { fprintf(stderr, "Failed initializing library.\n"); return EXIT_FAILURE; @@ -1170,6 +1141,10 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } + if(ykpiv_parse_key(state, args_info.key_arg, key) != YKPIV_OK) { + return EXIT_FAILURE; + } + if(ykpiv_authenticate(state, key) != YKPIV_OK) { fprintf(stderr, "Failed authentication with the applet.\n"); return EXIT_FAILURE; @@ -1203,7 +1178,7 @@ 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(ykpiv_set_mgmkey(state, new_key) != YKPIV_OK) { ret = EXIT_FAILURE; From 931cc5f35a195f2f0dff761ce56d66bd5463f305 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 17 Jun 2014 10:34:05 +0200 Subject: [PATCH 23/71] include ykpiv-version.h in ykpiv.h --- lib/ykpiv.h | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/ykpiv.h b/lib/ykpiv.h index 847667c..87433a0 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -31,6 +31,7 @@ #define YKPIV_H #include +#include #ifdef __cplusplus extern "C" From 9d0d163f620ca184aa2a7a964732d050bb032456 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 17 Jun 2014 10:34:51 +0200 Subject: [PATCH 24/71] some tests for the library --- .gitignore | 8 +++++ tests/Makefile.am | 11 ++++-- tests/basic.c | 74 ++++++++++++++++++++++++++++++++++++++ tests/parse_key.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 182 insertions(+), 2 deletions(-) create mode 100644 tests/basic.c create mode 100644 tests/parse_key.c diff --git a/.gitignore b/.gitignore index a4cf1e3..a224e0e 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,14 @@ yubico-piv-tool-*-mac.zip.sig tests/basic.sh.log tests/basic.sh.trs tests/test-suite.log +tests/basic +tests/basic.log +tests/basic.o +tests/basic.trs +tests/parse_key +tests/parse_key.log +tests/parse_key.o +tests/parse_key.trs lib/error.lo lib/error.o lib/libykpiv.la diff --git a/tests/Makefile.am b/tests/Makefile.am index 9eaf2e7..1c47722 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -24,6 +24,13 @@ # 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 - TESTS_ENVIRONMENT = export VERSION=$(VERSION); export EXEEXT=$(EXEEXT); + +AM_CFLAGS = $(WARN_CFLAGS) +AM_CPPFLAGS = -I$(top_srcdir)/lib -I$(top_builddir)/lib + +AM_LDFLAGS = -no-install +LDADD = ../lib/libykpiv.la + +check_PROGRAMS = basic parse_key +TESTS = basic.sh $(check_PROGRAMS) diff --git a/tests/basic.c b/tests/basic.c new file mode 100644 index 0000000..b7c64ae --- /dev/null +++ b/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/tests/parse_key.c b/tests/parse_key.c new file mode 100644 index 0000000..564352d --- /dev/null +++ b/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; +} From f4681463119d02df5200a343588500277b49d151 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 17 Jun 2014 10:55:46 +0200 Subject: [PATCH 25/71] add ykpiv_disconnect() --- lib/ykpiv.c | 15 +++++++++++++++ lib/ykpiv.h | 1 + lib/ykpiv.map | 1 + 3 files changed, 17 insertions(+) diff --git a/lib/ykpiv.c b/lib/ykpiv.c index ac8a357..0e40c7d 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -57,10 +57,25 @@ ykpiv_rc ykpiv_init(ykpiv_state **state, int verbose) { } 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; diff --git a/lib/ykpiv.h b/lib/ykpiv.h index 87433a0..054accf 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -58,6 +58,7 @@ extern "C" 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); diff --git a/lib/ykpiv.map b/lib/ykpiv.map index 34ab3fd..1cbdfb9 100644 --- a/lib/ykpiv.map +++ b/lib/ykpiv.map @@ -33,6 +33,7 @@ global: ykpiv_init; ykpiv_done; ykpiv_connect; + ykpiv_disconnect; ykpiv_send_data; ykpiv_transfer_data; ykpiv_authenticate; From d36eb37cf9f14d615c57eaa4ff4ab2f3b7ae698b Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 17 Jun 2014 11:01:01 +0200 Subject: [PATCH 26/71] no EXTRA_DIST in tool --- tool/Makefile.am | 2 -- 1 file changed, 2 deletions(-) diff --git a/tool/Makefile.am b/tool/Makefile.am index 6dd4dba..ab6503d 100644 --- a/tool/Makefile.am +++ b/tool/Makefile.am @@ -45,8 +45,6 @@ MAINTAINERCLEANFILES = $(BUILT_SOURCES) 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 \ From e3995346513c3c2836ac0bbfea5240e8fdd24081 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 17 Jun 2014 13:48:13 +0200 Subject: [PATCH 27/71] make a small temp library to avoid warnings on ggo file --- .gitignore | 3 +++ tool/Makefile.am | 8 +++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index a224e0e..4c1cce0 100644 --- a/.gitignore +++ b/.gitignore @@ -62,4 +62,7 @@ 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 diff --git a/tool/Makefile.am b/tool/Makefile.am index ab6503d..60f13f4 100644 --- a/tool/Makefile.am +++ b/tool/Makefile.am @@ -30,9 +30,11 @@ 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_SOURCES += cmdline.ggo cmdline.c cmdline.h -yubico_piv_tool_LDADD = $(OPENSSL_LIBS) -yubico_piv_tool_LDADD += ../lib/libykpiv.la +yubico_piv_tool_LDADD = $(OPENSSL_LIBS) ../lib/libykpiv.la libpiv_cmd.la + +noinst_LTLIBRARIES = libpiv_cmd.la +libpiv_cmd_la_SOURCES = cmdline.ggo cmdline.c cmdline.h +libpiv_cmd_la_CFLAGS = cmdline.c cmdline.h: cmdline.ggo Makefile.am $(GENGETOPT) --input $^ From 7b2fecb8fd962327556812097640e0e761236bf9 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 17 Jun 2014 14:28:16 +0200 Subject: [PATCH 28/71] include windows.h on windows --- tool/yubico-piv-tool.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 051a765..6b2b29f 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -34,6 +34,10 @@ #include "ykpiv.h" +#ifdef _WIN32 +#include +#endif + #include #include #include From 880c8a00614da391dbb82f1afa831eb8be427a2d Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 17 Jun 2014 15:11:02 +0200 Subject: [PATCH 29/71] move sign_data() function to library --- lib/Makefile.am | 1 + lib/error.c | 1 + lib/ykpiv.c | 107 +++++++++++++++++++++++++++++++++++++++++ lib/ykpiv.h | 26 +++++----- lib/ykpiv.map | 1 + tool/yubico-piv-tool.c | 76 ++++++----------------------- 6 files changed, 141 insertions(+), 71 deletions(-) diff --git a/lib/Makefile.am b/lib/Makefile.am index ee448ab..729b57d 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -33,6 +33,7 @@ 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) diff --git a/lib/error.c b/lib/error.c index af3454f..4411665 100644 --- a/lib/error.c +++ b/lib/error.c @@ -50,6 +50,7 @@ static const err_t errors[] = { 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"), }; /** diff --git a/lib/ykpiv.c b/lib/ykpiv.c index 0e40c7d..6c76f62 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -45,6 +45,36 @@ static void dump_hex(const unsigned char *buf, unsigned int len) { } } +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_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; +} + ykpiv_rc ykpiv_init(ykpiv_state **state, int verbose) { ykpiv_state *s = malloc(sizeof(ykpiv_state)); if(s == NULL) { @@ -428,3 +458,80 @@ ykpiv_rc ykpiv_parse_key(ykpiv_state *state, } return YKPIV_OK; } + +ykpiv_rc ykpiv_sign_data(ykpiv_state *state, + const unsigned char *sign_in, int in_len, + unsigned char *sign_out, int *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); + int sw; + int bytes; + int len = 0; + ykpiv_rc res; + + if(in_len > 1000) { + return YKPIV_SIZE_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; +} diff --git a/lib/ykpiv.h b/lib/ykpiv.h index 054accf..e0b1067 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -50,6 +50,7 @@ extern "C" YKPIV_RANDOMNESS_ERROR = -6, YKPIV_GENERIC_ERROR = -7, YKPIV_KEY_ERROR = -8, + YKPIV_PARSE_ERROR = -9, } ykpiv_rc; const char *ykpiv_strerror(ykpiv_rc err); @@ -68,22 +69,25 @@ extern "C" 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, + int in_len,unsigned char *sign_out, int *out_len, + unsigned char algorithm, unsigned char key); -#define YKPIV_ALGO_3DES 0x03; -#define YKPIV_ALGO_RSA1024 0x06; -#define YKPIV_ALGO_RSA2048 0x07; -#define YKPIV_ALGO_ECCP256 0x11; +#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_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_INS_AUTHENTICATE 0x87; +#define YKPIV_INS_AUTHENTICATE 0x87 /* Yubico vendor specific instructions */ -#define YKPIV_INS_SET_MGMKEY 0xff; +#define YKPIV_INS_SET_MGMKEY 0xff #ifdef __cplusplus } diff --git a/lib/ykpiv.map b/lib/ykpiv.map index 1cbdfb9..eed8a19 100644 --- a/lib/ykpiv.map +++ b/lib/ykpiv.map @@ -39,6 +39,7 @@ global: ykpiv_authenticate; ykpiv_set_mgmkey; ykpiv_parse_key; + ykpiv_sign_data; local: *; diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 6b2b29f..6b73936 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -90,8 +90,6 @@ 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(ykpiv_state*, unsigned char*, int, unsigned char, unsigned char, - ASN1_BIT_STRING*); static int get_object_id(enum enum_slot slot); static void print_version(ykpiv_state *state) { @@ -634,8 +632,14 @@ static bool request_certificate(ykpiv_state *state, enum enum_key_format key_for fprintf(stderr, "Unsupported algorithm %x.\n", algorithm); goto request_out; } - if(sign_data(state, signinput, len, algorithm, key, req->signature) == false) { - goto request_out; + { + unsigned char signature[1024]; + int 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) { @@ -764,8 +768,14 @@ static bool selfsign_certificate(ykpiv_state *state, enum enum_key_format key_fo fprintf(stderr, "Unsupported algorithm %x.\n", algorithm); goto selfsign_out; } - if(sign_data(state, signinput, len, algorithm, key, x509->signature)) { - goto selfsign_out; + { + unsigned char signature[1024]; + int 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) { @@ -913,60 +923,6 @@ static bool delete_certificate(ykpiv_state *state, enum enum_slot slot) { return ret; } -static bool sign_data(ykpiv_state *state, unsigned char *signinput, int in_len, - unsigned char algorithm, unsigned char key, ASN1_BIT_STRING *sig) { - unsigned char indata[1024]; - unsigned char *dataptr = indata; - unsigned char data[1024]; - unsigned char templ[] = {0, 0x87, algorithm, key}; - unsigned long recv_len = sizeof(data); - int sw; - int bytes; - int len; - - 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; - - if(ykpiv_transfer_data(state, templ, indata, dataptr - indata, data, - &recv_len, &sw) != YKPIV_OK) { - fprintf(stderr, "Sign command failed to communicate.\n"); - return false; - } else if(sw != 0x9000) { - fprintf(stderr, "Failed sign command with code %x.\n", sw); - 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, "-")) { From a97010d5e3660e08ecc2c4825288a529169f7b0c Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 17 Jun 2014 15:26:48 +0200 Subject: [PATCH 30/71] add ykpiv_get_version() function --- lib/ykpiv.c | 22 ++++++++++++++++++++++ lib/ykpiv.h | 4 ++++ lib/ykpiv.map | 1 + tool/yubico-piv-tool.c | 16 ++++------------ 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/lib/ykpiv.c b/lib/ykpiv.c index 6c76f62..a35f515 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -535,3 +535,25 @@ ykpiv_rc ykpiv_sign_data(ykpiv_state *state, 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 = ykpiv_send_data(state, apdu.raw, 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; + } +} diff --git a/lib/ykpiv.h b/lib/ykpiv.h index e0b1067..bf035b4 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -31,6 +31,8 @@ #define YKPIV_H #include +#include + #include #ifdef __cplusplus @@ -72,6 +74,7 @@ extern "C" ykpiv_rc ykpiv_sign_data(ykpiv_state *state, const unsigned char *sign_in, int in_len,unsigned char *sign_out, int *out_len, unsigned char algorithm, unsigned char key); + ykpiv_rc ykpiv_get_version(ykpiv_state *state, char *version, size_t len); #define YKPIV_ALGO_3DES 0x03 #define YKPIV_ALGO_RSA1024 0x06 @@ -88,6 +91,7 @@ extern "C" /* Yubico vendor specific instructions */ #define YKPIV_INS_SET_MGMKEY 0xff +#define YKPIV_INS_GET_VERSION 0xfd #ifdef __cplusplus } diff --git a/lib/ykpiv.map b/lib/ykpiv.map index eed8a19..5aed2ae 100644 --- a/lib/ykpiv.map +++ b/lib/ykpiv.map @@ -40,6 +40,7 @@ global: ykpiv_set_mgmkey; ykpiv_parse_key; ykpiv_sign_data; + ykpiv_get_version; local: *; diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 6b73936..8aacddc 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -93,19 +93,11 @@ static FILE *open_file(const char*, int); static int get_object_id(enum enum_slot slot); static void print_version(ykpiv_state *state) { - APDU apdu; - unsigned char data[0xff]; - unsigned long recv_len = sizeof(data); - int sw; - - memset(apdu.raw, 0, sizeof(apdu)); - apdu.st.ins = 0xfd; - if(ykpiv_send_data(state, apdu.raw, data, &recv_len, &sw) != YKPIV_OK) { - printf("Failed to retreive apple version.\n"); - } else if(sw == 0x9000) { - printf("Applet version %d.%d.%d found.\n", data[0], data[1], data[2]); + 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"); } } From b82e64362cad88502bf447b141bdd1c62d015920 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Wed, 18 Jun 2014 10:22:00 +0200 Subject: [PATCH 31/71] set version correctly in NEWS --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) From 3c557ebbea08d21d492a7fbcdcbf6b83aec5f7f3 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Wed, 18 Jun 2014 13:21:05 +0200 Subject: [PATCH 32/71] use constants for algorithm ids --- tool/yubico-piv-tool.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 8aacddc..200ac78 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -134,13 +134,13 @@ static bool generate_key(ykpiv_state *state, const char *slot, 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: @@ -344,7 +344,7 @@ static bool import_key(ykpiv_state *state, enum enum_key_format key_format, unsigned char *in_ptr = in_data; unsigned char templ[] = {0, 0xfe, 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; @@ -366,7 +366,7 @@ static bool import_key(ykpiv_state *state, 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); @@ -606,16 +606,16 @@ static bool request_certificate(ykpiv_state *state, enum enum_key_format key_for } switch(algorithm) { - case 0x6: + case YKPIV_ALGO_RSA1024: len = 128; - case 0x7: + case YKPIV_ALGO_RSA2048: if(len == 0) { len = 256; } RSA_padding_add_PKCS1_type_1(signinput, len, digest, sizeof(digest)); req->sig_alg->algorithm = OBJ_nid2obj(NID_sha256WithRSAEncryption); break; - case 0x11: + case YKPIV_ALGO_ECCP256: req->sig_alg->algorithm = OBJ_nid2obj(NID_ecdsa_with_SHA256); len = DIGEST_LEN; memcpy(signinput, digest + sizeof(sha256oid), DIGEST_LEN); @@ -742,16 +742,16 @@ static bool selfsign_certificate(ykpiv_state *state, enum enum_key_format key_fo goto selfsign_out; } switch(algorithm) { - case 0x6: + case YKPIV_ALGO_RSA1024: len = 128; - case 0x7: + case YKPIV_ALGO_RSA2048: if(len == 0) { len = 256; } RSA_padding_add_PKCS1_type_1(signinput, len, digest, sizeof(digest)); x509->sig_alg->algorithm = OBJ_nid2obj(NID_sha256WithRSAEncryption); break; - case 0x11: + case YKPIV_ALGO_ECCP256: x509->sig_alg->algorithm = OBJ_nid2obj(NID_ecdsa_with_SHA256); len = DIGEST_LEN; memcpy(signinput, digest + sizeof(sha256oid), DIGEST_LEN); @@ -937,9 +937,9 @@ static unsigned char get_algorithm(EVP_PKEY *key) { RSA *rsa = EVP_PKEY_get1_RSA(key); int size = RSA_size(rsa); if(size == 256) { - return 0x7; + return YKPIV_ALGO_RSA2048; } else if(size == 128) { - return 0x6; + return YKPIV_ALGO_RSA1024; } else { fprintf(stderr, "Unuseable key of %d bits, only 1024 and 2048 is supported.\n", size * 8); return 0; @@ -951,7 +951,7 @@ static unsigned char get_algorithm(EVP_PKEY *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; + return YKPIV_ALGO_ECCP256; } else { fprintf(stderr, "Unknown EC curve %d\n", curve); return 0; From 052b80830a70c69a1897b90c9dafff4401d51fe6 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Wed, 18 Jun 2014 13:28:28 +0200 Subject: [PATCH 33/71] make constants for more instructions --- lib/ykpiv.h | 9 +++++++++ tool/yubico-piv-tool.c | 21 +++++++++++---------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/lib/ykpiv.h b/lib/ykpiv.h index bf035b4..1239df3 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -87,11 +87,20 @@ extern "C" #define YKPIV_KEY_KEYMGM 0x9d #define YKPIV_KEY_CARDAUTH 0x9e +#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 } diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 200ac78..903866c 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -106,7 +106,7 @@ static bool generate_key(ykpiv_state *state, const char *slot, enum enum_key_format key_format) { unsigned char in_data[5]; unsigned char data[1024]; - unsigned char templ[] = {0, 0x47, 0, 0}; + unsigned char templ[] = {0, YKPIV_INS_GENERATE_ASYMMERTRIC, 0, 0}; unsigned long recv_len = sizeof(data); unsigned long received = 0; int sw; @@ -258,7 +258,7 @@ static bool reset(ykpiv_state *state) { memset(apdu.raw, 0, sizeof(apdu)); /* note: the reset function is only available when both pins are blocked. */ - apdu.st.ins = 0xfb; + apdu.st.ins = YKPIV_INS_RESET; if(ykpiv_send_data(state, apdu.raw, data, &recv_len, &sw) != YKPIV_OK) { return false; } else if(sw == 0x9000) { @@ -283,7 +283,7 @@ static bool set_pin_retries(ykpiv_state *state, int pin_retries, int puk_retries } memset(apdu.raw, 0, sizeof(apdu)); - apdu.st.ins = 0xfa; + apdu.st.ins = YKPIV_INS_SET_PIN_RETRIES; apdu.st.p1 = pin_retries; apdu.st.p2 = puk_retries; if(ykpiv_send_data(state, apdu.raw, data, &recv_len, &sw) != YKPIV_OK) { @@ -342,7 +342,7 @@ static bool import_key(ykpiv_state *state, enum enum_key_format key_format, unsigned long recv_len = sizeof(data); unsigned char in_data[1024]; unsigned char *in_ptr = in_data; - unsigned char templ[] = {0, 0xfe, algorithm, key}; + unsigned char templ[] = {0, YKPIV_INS_IMPORT_KEY, algorithm, key}; int sw; if(algorithm == YKPIV_ALGO_RSA1024 || algorithm == YKPIV_ALGO_RSA2048) { RSA *rsa_private_key = EVP_PKEY_get1_RSA(private_key); @@ -441,7 +441,7 @@ static bool import_cert(ykpiv_state *state, enum enum_key_format cert_format, unsigned char certdata[2100]; unsigned char *certptr = certdata; unsigned char data[0xff]; - unsigned char templ[] = {0, 0xdb, 0x3f, 0xff}; + unsigned char templ[] = {0, YKPIV_INS_PUT_DATA, 0x3f, 0xff}; unsigned long recv_len = sizeof(data); int cert_len = i2d_X509(cert, NULL); int bytes; @@ -521,7 +521,7 @@ static bool set_chuid(ykpiv_state *state, int verbose) { dump_hex(dataptr, 0x10); fprintf(stderr, "\n"); } - apdu.st.ins = 0xdb; + apdu.st.ins = YKPIV_INS_PUT_DATA; apdu.st.p1 = 0x3f; apdu.st.p2 = 0xff; apdu.st.lc = sizeof(chuid_tmpl); @@ -809,7 +809,7 @@ static bool verify_pin(ykpiv_state *state, const char *pin) { } memset(apdu.raw, 0, sizeof(apdu.raw)); - apdu.st.ins = 0x20; + apdu.st.ins = YKPIV_INS_VERIFY; apdu.st.p1 = 0x00; apdu.st.p2 = 0x80; apdu.st.lc = 0x08; @@ -848,7 +848,8 @@ static bool change_pin(ykpiv_state *state, enum enum_action action, const char * } memset(apdu.raw, 0, sizeof(apdu.raw)); - apdu.st.ins = action == action_arg_unblockMINUS_pin ? 0x2c : 0x24; + apdu.st.ins = action == action_arg_unblockMINUS_pin ? + YKPIV_INS_RESET_RETRY : YKPIV_INS_CHANGE_REFERENCE; apdu.st.p2 = action == action_arg_changeMINUS_puk ? 0x81 : 0x80; apdu.st.lc = 0x10; memcpy(apdu.st.data, pin, pin_len); @@ -886,7 +887,7 @@ static bool delete_certificate(ykpiv_state *state, enum enum_slot slot) { unsigned char *ptr = objdata; unsigned char data[0xff]; unsigned long recv_len = sizeof(data); - unsigned char templ[] = {0, 0xdb, 0x3f, 0xff}; + unsigned char templ[] = {0, YKPIV_INS_PUT_DATA, 0x3f, 0xff}; int sw; bool ret = false; int object = get_object_id(slot); @@ -900,7 +901,7 @@ static bool delete_certificate(ykpiv_state *state, enum enum_slot slot) { *ptr++ = 0x00; /* length 0 means we'll delete the object */ memset(apdu.raw, 0, sizeof(apdu.raw)); - apdu.st.ins = 0xdb; + apdu.st.ins = YKPIV_INS_PUT_DATA; apdu.st.p1 = 0x3f; apdu.st.p2 = 0xff; From 31d9d0d6805a9b859fe70ae8fc468a74985155e0 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Mon, 23 Jun 2014 09:57:10 +0200 Subject: [PATCH 34/71] add ykpiv_verify to the library --- lib/ykpiv.c | 45 ++++++++++++++++++++++++++++++++++++++++++ lib/ykpiv.h | 2 ++ lib/ykpiv.map | 1 + tool/yubico-piv-tool.c | 35 +++++++++++--------------------- 4 files changed, 60 insertions(+), 23 deletions(-) diff --git a/lib/ykpiv.c b/lib/ykpiv.c index a35f515..1322a9e 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -557,3 +557,48 @@ ykpiv_rc ykpiv_get_version(ykpiv_state *state, char *version, size_t len) { 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 = ykpiv_send_data(state, apdu.raw, 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; + } +} diff --git a/lib/ykpiv.h b/lib/ykpiv.h index 1239df3..0207f13 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -53,6 +53,7 @@ extern "C" YKPIV_GENERIC_ERROR = -7, YKPIV_KEY_ERROR = -8, YKPIV_PARSE_ERROR = -9, + YKPIV_WRONG_PIN = -10, } ykpiv_rc; const char *ykpiv_strerror(ykpiv_rc err); @@ -75,6 +76,7 @@ extern "C" int in_len,unsigned char *sign_out, int *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); #define YKPIV_ALGO_3DES 0x03 #define YKPIV_ALGO_RSA1024 0x06 diff --git a/lib/ykpiv.map b/lib/ykpiv.map index 5aed2ae..1e156ac 100644 --- a/lib/ykpiv.map +++ b/lib/ykpiv.map @@ -41,6 +41,7 @@ global: ykpiv_parse_key; ykpiv_sign_data; ykpiv_get_version; + ykpiv_verify; local: *; diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 903866c..d15c672 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -797,36 +797,25 @@ selfsign_out: } static bool verify_pin(ykpiv_state *state, const char *pin) { - APDU apdu; - unsigned char data[0xff]; - unsigned long recv_len = sizeof(data); - int sw; - size_t len = strlen(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 = 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(ykpiv_send_data(state, apdu.raw, data, &recv_len, &sw) != YKPIV_OK) { - return false; - } else 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; } From ab3083dc1b866cb06dccc3bad26d5077d133bc7b Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 24 Jun 2014 09:37:03 +0200 Subject: [PATCH 35/71] split out util functions from yubico-piv-tool.c --- .gitignore | 3 + tool/Makefile.am | 7 +- tool/internal.h | 58 ++++++++++++ tool/util.c | 197 +++++++++++++++++++++++++++++++++++++++++ tool/yubico-piv-tool.c | 179 +------------------------------------ 5 files changed, 264 insertions(+), 180 deletions(-) create mode 100644 tool/internal.h create mode 100644 tool/util.c diff --git a/.gitignore b/.gitignore index 4c1cce0..9363123 100644 --- a/.gitignore +++ b/.gitignore @@ -65,4 +65,7 @@ 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 diff --git a/tool/Makefile.am b/tool/Makefile.am index 60f13f4..657f09e 100644 --- a/tool/Makefile.am +++ b/tool/Makefile.am @@ -30,12 +30,15 @@ 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 libpiv_cmd.la +yubico_piv_tool_LDADD = $(OPENSSL_LIBS) ../lib/libykpiv.la +yubico_piv_tool_LDADD += libpiv_cmd.la libpiv_util.la -noinst_LTLIBRARIES = libpiv_cmd.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 internal.h + cmdline.c cmdline.h: cmdline.ggo Makefile.am $(GENGETOPT) --input $^ diff --git a/tool/internal.h b/tool/internal.h new file mode 100644 index 0000000..03e2ca2 --- /dev/null +++ b/tool/internal.h @@ -0,0 +1,58 @@ + /* + * 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 + +#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; + +void dump_hex(unsigned const char*, unsigned int); +int set_length(unsigned char*, int); +int get_length(unsigned char*, int*); +X509_NAME *parse_name(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/tool/util.c b/tool/util.c new file mode 100644 index 0000000..6c99119 --- /dev/null +++ b/tool/util.c @@ -0,0 +1,197 @@ + /* + * 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 "cmdline.h" +#include "internal.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(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; +} + +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(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 = 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; +} diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index d15c672..15e04e5 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -44,6 +44,7 @@ #include #include "cmdline.h" +#include "internal.h" /* FASC-N containing S9999F9999F999999F0F1F0000000000300001E encoded in * 4-bit BCD with 1 bit parity. run through the tools/fasc.pl script to get @@ -67,31 +68,6 @@ 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 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 int get_object_id(enum enum_slot slot); - static void print_version(ykpiv_state *state) { char version[7]; if(ykpiv_get_version(state, version, sizeof(version)) == YKPIV_OK) { @@ -905,159 +881,6 @@ static bool delete_certificate(ykpiv_state *state, enum enum_slot slot) { return ret; } -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; - } - } - 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 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; - } -} - -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 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 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; ykpiv_state *state; From 72f271ae7303f5c6ef8a3ed4f48ea05d9192e624 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 24 Jun 2014 09:50:11 +0200 Subject: [PATCH 36/71] move basic.sh test to subdir of tool --- .gitignore | 6 +++--- configure.ac | 1 + tests/Makefile.am | 4 +--- tool/Makefile.am | 2 ++ tool/tests/Makefile.am | 29 +++++++++++++++++++++++++++++ {tests => tool/tests}/basic.sh | 2 +- 6 files changed, 37 insertions(+), 7 deletions(-) create mode 100644 tool/tests/Makefile.am rename {tests => tool/tests}/basic.sh (97%) diff --git a/.gitignore b/.gitignore index 9363123..6bfdce8 100644 --- a/.gitignore +++ b/.gitignore @@ -34,8 +34,6 @@ 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 tests/basic tests/basic.log @@ -68,4 +66,6 @@ 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 diff --git a/configure.ac b/configure.ac index 9184a28..a3fc0dc 100644 --- a/configure.ac +++ b/configure.ac @@ -151,6 +151,7 @@ AC_CONFIG_FILES([ Makefile lib/Makefile tool/Makefile + tool/tests/Makefile tests/Makefile lib/ykpiv-version.h lib/ykpiv.pc diff --git a/tests/Makefile.am b/tests/Makefile.am index 1c47722..ba7b69c 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -24,8 +24,6 @@ # 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 @@ -33,4 +31,4 @@ AM_LDFLAGS = -no-install LDADD = ../lib/libykpiv.la check_PROGRAMS = basic parse_key -TESTS = basic.sh $(check_PROGRAMS) +TESTS = $(check_PROGRAMS) diff --git a/tool/Makefile.am b/tool/Makefile.am index 657f09e..a3d609f 100644 --- a/tool/Makefile.am +++ b/tool/Makefile.am @@ -24,6 +24,8 @@ # 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 diff --git a/tool/tests/Makefile.am b/tool/tests/Makefile.am new file mode 100644 index 0000000..88b2b85 --- /dev/null +++ b/tool/tests/Makefile.am @@ -0,0 +1,29 @@ +# 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); + +TESTS = basic.sh diff --git a/tests/basic.sh b/tool/tests/basic.sh similarity index 97% rename from tests/basic.sh rename to tool/tests/basic.sh index 5a58b81..daf3c33 100755 --- a/tests/basic.sh +++ b/tool/tests/basic.sh @@ -31,7 +31,7 @@ set -e -BIN="../tool/yubico-piv-tool${EXEEXT}" +BIN="../yubico-piv-tool${EXEEXT}" HELP_OUTPUT=$($BIN --help) From b048ea85e5f436436a15fe7cc80321836ef0843b Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 24 Jun 2014 09:52:10 +0200 Subject: [PATCH 37/71] move lib tests under lib --- .gitignore | 18 +++++++++--------- Makefile.am | 2 +- configure.ac | 2 +- lib/Makefile.am | 2 ++ {tests => lib/tests}/Makefile.am | 2 +- {tests => lib/tests}/basic.c | 0 {tests => lib/tests}/parse_key.c | 0 7 files changed, 14 insertions(+), 12 deletions(-) rename {tests => lib/tests}/Makefile.am (98%) rename {tests => lib/tests}/basic.c (100%) rename {tests => lib/tests}/parse_key.c (100%) diff --git a/.gitignore b/.gitignore index 6bfdce8..a3d3236 100644 --- a/.gitignore +++ b/.gitignore @@ -34,15 +34,15 @@ yubico-piv-tool-*.tar.gz yubico-piv-tool-*.tar.gz.sig yubico-piv-tool-*-mac.zip yubico-piv-tool-*-mac.zip.sig -tests/test-suite.log -tests/basic -tests/basic.log -tests/basic.o -tests/basic.trs -tests/parse_key -tests/parse_key.log -tests/parse_key.o -tests/parse_key.trs +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 diff --git a/Makefile.am b/Makefile.am index 566d588..0f442c2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -24,7 +24,7 @@ # 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 = lib tool tests +SUBDIRS = lib tool ACLOCAL_AMFLAGS = -I m4 diff --git a/configure.ac b/configure.ac index a3fc0dc..80e04d6 100644 --- a/configure.ac +++ b/configure.ac @@ -150,9 +150,9 @@ fi AC_CONFIG_FILES([ Makefile lib/Makefile + lib/tests/Makefile tool/Makefile tool/tests/Makefile - tests/Makefile lib/ykpiv-version.h lib/ykpiv.pc ]) diff --git a/lib/Makefile.am b/lib/Makefile.am index 729b57d..7a02a82 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -24,6 +24,8 @@ # 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) diff --git a/tests/Makefile.am b/lib/tests/Makefile.am similarity index 98% rename from tests/Makefile.am rename to lib/tests/Makefile.am index ba7b69c..6fefb8f 100644 --- a/tests/Makefile.am +++ b/lib/tests/Makefile.am @@ -28,7 +28,7 @@ AM_CFLAGS = $(WARN_CFLAGS) AM_CPPFLAGS = -I$(top_srcdir)/lib -I$(top_builddir)/lib AM_LDFLAGS = -no-install -LDADD = ../lib/libykpiv.la +LDADD = ../libykpiv.la check_PROGRAMS = basic parse_key TESTS = $(check_PROGRAMS) diff --git a/tests/basic.c b/lib/tests/basic.c similarity index 100% rename from tests/basic.c rename to lib/tests/basic.c diff --git a/tests/parse_key.c b/lib/tests/parse_key.c similarity index 100% rename from tests/parse_key.c rename to lib/tests/parse_key.c From 92db159c4f291f39b67dfbde4ae31deee5deeb51 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 24 Jun 2014 09:57:21 +0200 Subject: [PATCH 38/71] change internal.h to util.h --- tool/Makefile.am | 2 +- tool/util.c | 2 +- tool/{internal.h => util.h} | 0 tool/yubico-piv-tool.c | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename tool/{internal.h => util.h} (100%) diff --git a/tool/Makefile.am b/tool/Makefile.am index a3d609f..5d5027b 100644 --- a/tool/Makefile.am +++ b/tool/Makefile.am @@ -39,7 +39,7 @@ 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 internal.h +libpiv_util_la_SOURCES = util.c util.h cmdline.c cmdline.h: cmdline.ggo Makefile.am $(GENGETOPT) --input $^ diff --git a/tool/util.c b/tool/util.c index 6c99119..b297aa1 100644 --- a/tool/util.c +++ b/tool/util.c @@ -41,7 +41,7 @@ #include #include "cmdline.h" -#include "internal.h" +#include "util.h" FILE *open_file(const char *file_name, int mode) { FILE *file; diff --git a/tool/internal.h b/tool/util.h similarity index 100% rename from tool/internal.h rename to tool/util.h diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 15e04e5..2c628c2 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -44,7 +44,7 @@ #include #include "cmdline.h" -#include "internal.h" +#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 From 81ddeb047f1f3a38f192d5f3955a03ee4ec61d74 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 24 Jun 2014 10:27:01 +0200 Subject: [PATCH 39/71] let parse_name() take a const char* instead --- tool/util.c | 10 +++++++++- tool/util.h | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tool/util.c b/tool/util.c index b297aa1..af78260 100644 --- a/tool/util.c +++ b/tool/util.c @@ -91,10 +91,18 @@ unsigned char get_algorithm(EVP_PKEY *key) { } } -X509_NAME *parse_name(char *name) { +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; diff --git a/tool/util.h b/tool/util.h index 03e2ca2..33c89d4 100644 --- a/tool/util.h +++ b/tool/util.h @@ -50,7 +50,7 @@ typedef union u_APDU APDU; void dump_hex(unsigned const char*, unsigned int); int set_length(unsigned char*, int); int get_length(unsigned char*, int*); -X509_NAME *parse_name(char*); +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); From cdcbec814025d87bb468d1fdd3e4ad0664b02825 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 24 Jun 2014 10:27:52 +0200 Subject: [PATCH 40/71] test for parse_name() --- .gitignore | 4 +++ tool/tests/Makefile.am | 12 ++++++++- tool/tests/parse_name.c | 60 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 tool/tests/parse_name.c diff --git a/.gitignore b/.gitignore index a3d3236..baea115 100644 --- a/.gitignore +++ b/.gitignore @@ -69,3 +69,7 @@ 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/tool/tests/Makefile.am b/tool/tests/Makefile.am index 88b2b85..a25a3f1 100644 --- a/tool/tests/Makefile.am +++ b/tool/tests/Makefile.am @@ -26,4 +26,14 @@ TESTS_ENVIRONMENT = export VERSION=$(VERSION); export EXEEXT=$(EXEEXT); -TESTS = basic.sh +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/tool/tests/parse_name.c b/tool/tests/parse_name.c new file mode 100644 index 0000000..e3319a2 --- /dev/null +++ b/tool/tests/parse_name.c @@ -0,0 +1,60 @@ + /* + * 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 + +#ifdef _WIN32 +#include +#endif + +#include + +#include "util.h" + +static void test_name(char *name, char *expected) { + char buf[1024]; + X509_NAME *parsed = parse_name(name); + BIO *bio = BIO_new(BIO_s_mem()); + const char none[] = {0}; + + 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"); + test_name("/CN=test/OU=bar/O=EXAMPLE/", "CN = test, OU = bar, O = EXAMPLE"); + return EXIT_SUCCESS; +} From 5cdad1d7857b4445434d432ed67db6e6d931ae0f Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 24 Jun 2014 10:39:17 +0200 Subject: [PATCH 41/71] constify --- tool/tests/parse_name.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tool/tests/parse_name.c b/tool/tests/parse_name.c index e3319a2..ab80bfc 100644 --- a/tool/tests/parse_name.c +++ b/tool/tests/parse_name.c @@ -36,7 +36,7 @@ #include "util.h" -static void test_name(char *name, char *expected) { +static void test_name(const char *name, const char *expected) { char buf[1024]; X509_NAME *parsed = parse_name(name); BIO *bio = BIO_new(BIO_s_mem()); From 5588368d557220a37349b07e36c2822f3e3886a0 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 24 Jun 2014 10:39:23 +0200 Subject: [PATCH 42/71] link the util lib with openssl --- tool/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/tool/Makefile.am b/tool/Makefile.am index 5d5027b..0e5af5e 100644 --- a/tool/Makefile.am +++ b/tool/Makefile.am @@ -40,6 +40,7 @@ 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 $^ From 33c71344defcb854c45516016e057e4f0eaf37e2 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 24 Jun 2014 10:42:50 +0200 Subject: [PATCH 43/71] let util.h include cmdline.h since it's using declarations from it.. --- tool/util.c | 1 - tool/util.h | 2 ++ tool/yubico-piv-tool.c | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tool/util.c b/tool/util.c index af78260..7e7421a 100644 --- a/tool/util.c +++ b/tool/util.c @@ -40,7 +40,6 @@ #include -#include "cmdline.h" #include "util.h" FILE *open_file(const char *file_name, int mode) { diff --git a/tool/util.h b/tool/util.h index 33c89d4..fe314fc 100644 --- a/tool/util.h +++ b/tool/util.h @@ -30,6 +30,8 @@ #ifndef YUBICO_PIV_TOOL_INTERNAL_H #define YUBICO_PIV_TOOL_INTERNAL_H +#include "cmdline.h" + #define INPUT 1 #define OUTPUT 2 diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 2c628c2..3044a50 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -43,7 +43,6 @@ #include #include -#include "cmdline.h" #include "util.h" /* FASC-N containing S9999F9999F999999F0F1F0000000000300001E encoded in From a46cbc55b62181699218a65b43426ae8954b8149 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 24 Jun 2014 13:58:47 +0200 Subject: [PATCH 44/71] fix indentation --- tool/yubico-piv-tool.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 3044a50..95c2c7c 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -1067,15 +1067,15 @@ int main(int argc, char *argv[]) { } 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(state, args_info.slot_arg) == 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); From d5222c10b716b36dad1657c5b0267419d6392c1d Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 24 Jun 2014 14:07:15 +0200 Subject: [PATCH 45/71] add macros for piv objects --- lib/ykpiv.h | 14 ++++++++++++++ tool/util.c | 8 ++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/lib/ykpiv.h b/lib/ykpiv.h index 0207f13..ebdca28 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -89,6 +89,20 @@ extern "C" #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 diff --git a/tool/util.c b/tool/util.c index 7e7421a..79097cc 100644 --- a/tool/util.c +++ b/tool/util.c @@ -185,16 +185,16 @@ int get_object_id(enum enum_slot slot) { switch(slot) { case slot_arg_9a: - object = 0x5fc105; + object = YKPIV_OBJ_AUTHENTICATION; break; case slot_arg_9c: - object = 0x5fc10a; + object = YKPIV_OBJ_SIGNATURE; break; case slot_arg_9d: - object = 0x5fc10b; + object = YKPIV_OBJ_KEY_MANAGEMENT; break; case slot_arg_9e: - object = 0x5fc101; + object = YKPIV_OBJ_CARD_AUTH; break; case slot__NULL: default: From 05a30e1da5bd2090d64c5fc48f7c625826186c4a Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 24 Jun 2014 14:35:59 +0200 Subject: [PATCH 46/71] add ykpiv_fetch_object() to library --- lib/ykpiv.c | 24 ++++++++++++++++++++++++ lib/ykpiv.h | 3 +++ lib/ykpiv.map | 1 + 3 files changed, 28 insertions(+) diff --git a/lib/ykpiv.c b/lib/ykpiv.c index 1322a9e..3921f48 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -602,3 +602,27 @@ ykpiv_rc ykpiv_verify(ykpiv_state *state, const char *pin, int *tries) { 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 templ[] = {0, YKPIV_INS_GET_DATA, 0x3b, 0xff}; + long inlen = 5; + + indata[0] = 0x5c; + if(object_id == YKPIV_OBJ_DISCOVERY) { + indata[1] = 1; + indata[2] = YKPIV_OBJ_DISCOVERY; + inlen = 3; + } else if(object_id > 0xffff && object_id <= 0xffffff) { + indata[1] = 3; + indata[2] = (object_id >> 16) & 0xff; + indata[3] = (object_id >> 8) & 0xff; + indata[4] = object_id & 0xff; + } else { + return YKPIV_INVALID_OBJECT; + } + + return ykpiv_transfer_data(state, templ, indata, inlen, data, len, &sw); +} diff --git a/lib/ykpiv.h b/lib/ykpiv.h index ebdca28..6c4dc54 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -54,6 +54,7 @@ extern "C" YKPIV_KEY_ERROR = -8, YKPIV_PARSE_ERROR = -9, YKPIV_WRONG_PIN = -10, + YKPIV_INVALID_OBJECT = -11, } ykpiv_rc; const char *ykpiv_strerror(ykpiv_rc err); @@ -77,6 +78,8 @@ extern "C" 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); #define YKPIV_ALGO_3DES 0x03 #define YKPIV_ALGO_RSA1024 0x06 diff --git a/lib/ykpiv.map b/lib/ykpiv.map index 1e156ac..532439e 100644 --- a/lib/ykpiv.map +++ b/lib/ykpiv.map @@ -42,6 +42,7 @@ global: ykpiv_sign_data; ykpiv_get_version; ykpiv_verify; + ykpiv_fetch_object; local: *; From c5696d4b452b8d0180c96e35c58467fae53b2ed0 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 24 Jun 2014 15:02:26 +0200 Subject: [PATCH 47/71] rework some stuff to size_t --- lib/ykpiv.c | 9 +++++---- lib/ykpiv.h | 2 +- tool/yubico-piv-tool.c | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/ykpiv.c b/lib/ykpiv.c index 3921f48..beaceab 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -61,7 +61,7 @@ static int set_length(unsigned char *buffer, int length) { } } -static int get_length(unsigned char *buffer, int *len) { +static int get_length(unsigned char *buffer, size_t *len) { if(buffer[0] < 0x81) { *len = buffer[0]; return 1; @@ -69,7 +69,8 @@ static int get_length(unsigned char *buffer, int *len) { *len = buffer[1]; return 2; } else if((*buffer & 0x7f) == 2) { - *len = (buffer[1] << 8) + buffer[2]; + size_t tmp = buffer[1]; + *len = (tmp << 8) + buffer[2]; return 3; } return 0; @@ -461,7 +462,7 @@ ykpiv_rc ykpiv_parse_key(ykpiv_state *state, ykpiv_rc ykpiv_sign_data(ykpiv_state *state, const unsigned char *sign_in, int in_len, - unsigned char *sign_out, int *out_len, + unsigned char *sign_out, size_t *out_len, unsigned char algorithm, unsigned char key) { unsigned char indata[1024]; @@ -471,7 +472,7 @@ ykpiv_rc ykpiv_sign_data(ykpiv_state *state, unsigned long recv_len = sizeof(data); int sw; int bytes; - int len = 0; + size_t len = 0; ykpiv_rc res; if(in_len > 1000) { diff --git a/lib/ykpiv.h b/lib/ykpiv.h index 6c4dc54..b093051 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -74,7 +74,7 @@ extern "C" 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, - int in_len,unsigned char *sign_out, int *out_len, + int 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); diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 95c2c7c..aafa811 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -601,7 +601,7 @@ static bool request_certificate(ykpiv_state *state, enum enum_key_format key_for } { unsigned char signature[1024]; - int sig_len = sizeof(signature); + size_t sig_len = sizeof(signature); if(ykpiv_sign_data(state, signinput, len, signature, &sig_len, algorithm, key) != YKPIV_OK) { goto request_out; @@ -737,7 +737,7 @@ static bool selfsign_certificate(ykpiv_state *state, enum enum_key_format key_fo } { unsigned char signature[1024]; - int sig_len = sizeof(signature); + size_t sig_len = sizeof(signature); if(ykpiv_sign_data(state, signinput, len, signature, &sig_len, algorithm, key) != YKPIV_OK) { goto selfsign_out; From 2e8170aa316c71b5bfadaad465db46a10836cebd Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 24 Jun 2014 15:02:36 +0200 Subject: [PATCH 48/71] only return the actual data in ykpiv_fetch_data() --- lib/ykpiv.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/ykpiv.c b/lib/ykpiv.c index beaceab..c182054 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -610,6 +610,7 @@ ykpiv_rc ykpiv_fetch_object(ykpiv_state *state, int object_id, unsigned char indata[5]; unsigned char templ[] = {0, YKPIV_INS_GET_DATA, 0x3b, 0xff}; long inlen = 5; + ykpiv_rc res; indata[0] = 0x5c; if(object_id == YKPIV_OBJ_DISCOVERY) { @@ -625,5 +626,16 @@ ykpiv_rc ykpiv_fetch_object(ykpiv_state *state, int object_id, return YKPIV_INVALID_OBJECT; } - return ykpiv_transfer_data(state, templ, indata, inlen, data, len, &sw); + if((res = ykpiv_transfer_data(state, templ, indata, inlen, 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; + } + return YKPIV_OK; } From 7338dcae8a73e0cd7be9ad6266ccd50fe65fa40c Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 24 Jun 2014 15:22:33 +0200 Subject: [PATCH 49/71] some negative tests of parse_name() --- tool/tests/parse_name.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/tool/tests/parse_name.c b/tool/tests/parse_name.c index ab80bfc..a3fe30d 100644 --- a/tool/tests/parse_name.c +++ b/tool/tests/parse_name.c @@ -27,6 +27,7 @@ */ #include +#include #ifdef _WIN32 #include @@ -36,11 +37,21 @@ #include "util.h" -static void test_name(const char *name, const char *expected) { +static void test_name(const char *name, const char *expected, bool fail) { char buf[1024]; - X509_NAME *parsed = parse_name(name); - BIO *bio = BIO_new(BIO_s_mem()); + 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); @@ -54,7 +65,11 @@ static void test_name(const char *name, const char *expected) { } int main(void) { - test_name("/CN=test foo/", "CN = test foo"); - test_name("/CN=test/OU=bar/O=EXAMPLE/", "CN = test, OU = bar, O = EXAMPLE"); + 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; } From d17c0650a97f88225f051615f9456238eefa06c3 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 24 Jun 2014 16:18:01 +0200 Subject: [PATCH 50/71] 3f ff for object --- lib/ykpiv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ykpiv.c b/lib/ykpiv.c index c182054..614846e 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -608,7 +608,7 @@ 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 templ[] = {0, YKPIV_INS_GET_DATA, 0x3b, 0xff}; + unsigned char templ[] = {0, YKPIV_INS_GET_DATA, 0x3f, 0xff}; long inlen = 5; ykpiv_rc res; From 9b5ede8530cac323612200c66cab1c20f4a280e9 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Wed, 25 Jun 2014 10:40:44 +0200 Subject: [PATCH 51/71] change around and let ykpiv_sign_data() pad --- lib/ykpiv.c | 27 ++++++++++++++++++++++++--- lib/ykpiv.h | 1 + tool/yubico-piv-tool.c | 26 ++++++++++---------------- 3 files changed, 35 insertions(+), 19 deletions(-) diff --git a/lib/ykpiv.c b/lib/ykpiv.c index 614846e..468d9ef 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -461,7 +461,7 @@ ykpiv_rc ykpiv_parse_key(ykpiv_state *state, } ykpiv_rc ykpiv_sign_data(ykpiv_state *state, - const unsigned char *sign_in, int in_len, + const unsigned char *raw_in, int in_len, unsigned char *sign_out, size_t *out_len, unsigned char algorithm, unsigned char key) { @@ -470,13 +470,34 @@ ykpiv_rc ykpiv_sign_data(ykpiv_state *state, 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; int bytes; size_t len = 0; ykpiv_rc res; - if(in_len > 1000) { - return YKPIV_SIZE_ERROR; + 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) { diff --git a/lib/ykpiv.h b/lib/ykpiv.h index b093051..6022a1a 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -55,6 +55,7 @@ extern "C" 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); diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index aafa811..bf9b0ef 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -523,7 +523,7 @@ static bool request_certificate(ykpiv_state *state, enum enum_key_format key_for unsigned int digest_len = DIGEST_LEN; unsigned char algorithm; int key = 0; - unsigned char signinput[256]; + unsigned char *signinput; int len = 0; sscanf(slot, "%x", &key); @@ -582,18 +582,15 @@ static bool request_certificate(ykpiv_state *state, enum enum_key_format key_for switch(algorithm) { case YKPIV_ALGO_RSA1024: - len = 128; case YKPIV_ALGO_RSA2048: - if(len == 0) { - len = 256; - } - RSA_padding_add_PKCS1_type_1(signinput, len, digest, sizeof(digest)); + signinput = digest; + len = sizeof(digest); req->sig_alg->algorithm = OBJ_nid2obj(NID_sha256WithRSAEncryption); break; case YKPIV_ALGO_ECCP256: - req->sig_alg->algorithm = OBJ_nid2obj(NID_ecdsa_with_SHA256); + 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); @@ -648,7 +645,7 @@ static bool selfsign_certificate(ykpiv_state *state, enum enum_key_format key_fo unsigned int digest_len = DIGEST_LEN; unsigned char algorithm; int key = 0; - unsigned char signinput[256]; + unsigned char *signinput; int len = 0; sscanf(slot, "%x", &key); @@ -718,18 +715,15 @@ static bool selfsign_certificate(ykpiv_state *state, enum enum_key_format key_fo } switch(algorithm) { case YKPIV_ALGO_RSA1024: - len = 128; case YKPIV_ALGO_RSA2048: - if(len == 0) { - len = 256; - } - RSA_padding_add_PKCS1_type_1(signinput, len, digest, sizeof(digest)); + signinput = digest; + len = sizeof(digest); x509->sig_alg->algorithm = OBJ_nid2obj(NID_sha256WithRSAEncryption); break; case YKPIV_ALGO_ECCP256: - x509->sig_alg->algorithm = OBJ_nid2obj(NID_ecdsa_with_SHA256); + 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); From 58e078af2f63914bcad58ad9953180fb6efd413a Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Wed, 25 Jun 2014 10:41:00 +0200 Subject: [PATCH 52/71] add error messages --- lib/error.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/error.c b/lib/error.c index 4411665..1f8952a 100644 --- a/lib/error.c +++ b/lib/error.c @@ -51,6 +51,9 @@ static const err_t errors[] = { 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"), }; /** From 1ded0f6496da59a10392f4e73f7801fcb6e14241 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Wed, 25 Jun 2014 14:14:23 +0200 Subject: [PATCH 53/71] set version on the selfsigned cert --- tool/yubico-piv-tool.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index bf9b0ef..39a63e0 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -676,6 +676,10 @@ static bool selfsign_certificate(ykpiv_state *state, enum enum_key_format key_fo 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; From c4d8492682141e5a4e53e623d503714ec9b27af6 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Wed, 25 Jun 2014 15:10:18 +0200 Subject: [PATCH 54/71] make get_length() take a const buffer --- lib/ykpiv.c | 2 +- tool/util.c | 2 +- tool/util.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ykpiv.c b/lib/ykpiv.c index 468d9ef..460dc96 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -61,7 +61,7 @@ static int set_length(unsigned char *buffer, int length) { } } -static int get_length(unsigned char *buffer, size_t *len) { +static int get_length(const unsigned char *buffer, size_t *len) { if(buffer[0] < 0x81) { *len = buffer[0]; return 1; diff --git a/tool/util.c b/tool/util.c index 79097cc..6cebd65 100644 --- a/tool/util.c +++ b/tool/util.c @@ -150,7 +150,7 @@ void dump_hex(const unsigned char *buf, unsigned int len) { } } -int get_length(unsigned char *buffer, int *len) { +int get_length(const unsigned char *buffer, int *len) { if(buffer[0] < 0x81) { *len = buffer[0]; return 1; diff --git a/tool/util.h b/tool/util.h index fe314fc..acb5811 100644 --- a/tool/util.h +++ b/tool/util.h @@ -51,7 +51,7 @@ typedef union u_APDU APDU; void dump_hex(unsigned const char*, unsigned int); int set_length(unsigned char*, int); -int get_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); From 00343b9b6cd860a1d43810ae07d699d15af2d3e8 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Wed, 25 Jun 2014 15:17:27 +0200 Subject: [PATCH 55/71] refactor transfer data so it can send 0 bytes --- lib/ykpiv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ykpiv.c b/lib/ykpiv.c index 460dc96..a57d0e2 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -213,7 +213,7 @@ ykpiv_rc ykpiv_transfer_data(ykpiv_state *state, const unsigned char *templ, ykpiv_rc res; *out_len = 0; - while(in_ptr < in_data + in_len) { + do { size_t this_size = 0xff; unsigned long recv_len = 0xff; unsigned char data[0xff]; @@ -247,7 +247,7 @@ ykpiv_rc ykpiv_transfer_data(ykpiv_state *state, const unsigned char *templ, 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; From 28ab285d925f35aeb70c4fe9bab11966744ed87c Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Wed, 25 Jun 2014 15:22:04 +0200 Subject: [PATCH 56/71] refactor reset to use ykpiv_transfer_data() instead --- tool/yubico-piv-tool.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 39a63e0..1ed19e9 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -226,15 +226,13 @@ generate_out: } static bool reset(ykpiv_state *state) { - APDU apdu; + 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 = YKPIV_INS_RESET; - if(ykpiv_send_data(state, apdu.raw, data, &recv_len, &sw) != YKPIV_OK) { + if(ykpiv_transfer_data(state, templ, NULL, 0, data, &recv_len, &sw) != YKPIV_OK) { return false; } else if(sw == 0x9000) { return true; @@ -954,6 +952,7 @@ int main(int argc, char *argv[]) { break; case action_arg_reset: if(reset(state) == false) { + fprintf(stderr, "Reset failed, are pincodes blocked?\n"); ret = EXIT_FAILURE; } else { printf("Successfully reset the applet.\n"); From a60096addf4c9c704b693d7b86077928d789e58e Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Wed, 25 Jun 2014 15:24:40 +0200 Subject: [PATCH 57/71] refactor pin-retries to use ykpiv_transfer_data() --- tool/yubico-piv-tool.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 1ed19e9..0b95c50 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -241,7 +241,7 @@ static bool reset(ykpiv_state *state) { } static bool set_pin_retries(ykpiv_state *state, int pin_retries, int puk_retries, int verbose) { - APDU apdu; + 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; @@ -255,11 +255,7 @@ static bool set_pin_retries(ykpiv_state *state, 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 = YKPIV_INS_SET_PIN_RETRIES; - apdu.st.p1 = pin_retries; - apdu.st.p2 = puk_retries; - if(ykpiv_send_data(state, apdu.raw, data, &recv_len, &sw) != YKPIV_OK) { + if(ykpiv_transfer_data(state, templ, NULL, 0, data, &recv_len, &sw) != YKPIV_OK) { return false; } else if(sw == 0x9000) { return true; From b508f8bfea8b00d821b76392d9adc90c6c9c3d6d Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Wed, 25 Jun 2014 15:28:44 +0200 Subject: [PATCH 58/71] refactor to ykpiv_transfer_data() for chuid setting --- tool/yubico-piv-tool.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 0b95c50..d3f5c06 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -472,14 +472,14 @@ import_cert_out: } static bool set_chuid(ykpiv_state *state, int verbose) { - APDU apdu; + unsigned char templ[] = {0, YKPIV_INS_PUT_DATA, 0x3f, 0xff}; unsigned char data[0xff]; - unsigned char *dataptr = apdu.st.data; + unsigned char chuid[sizeof(chuid_tmpl)]; + unsigned char *dataptr = chuid; unsigned long recv_len = sizeof(data); int sw; - memset(apdu.raw, 0, sizeof(apdu)); - memcpy(apdu.st.data, chuid_tmpl, sizeof(chuid_tmpl)); + memcpy(chuid, chuid_tmpl, sizeof(chuid)); dataptr += CHUID_GUID_OFFS; if(RAND_pseudo_bytes(dataptr, 0x10) == -1) { fprintf(stderr, "error: no randomness.\n"); @@ -490,11 +490,7 @@ static bool set_chuid(ykpiv_state *state, int verbose) { dump_hex(dataptr, 0x10); fprintf(stderr, "\n"); } - apdu.st.ins = YKPIV_INS_PUT_DATA; - apdu.st.p1 = 0x3f; - apdu.st.p2 = 0xff; - apdu.st.lc = sizeof(chuid_tmpl); - if(ykpiv_send_data(state, apdu.raw, data, &recv_len, &sw) != YKPIV_OK) { + if(ykpiv_transfer_data(state, templ, chuid, sizeof(chuid), data, &recv_len, &sw) != YKPIV_OK) { fprintf(stderr, "Failed communicating with device.\n"); return false; } else if(sw != 0x9000) { From b0ff83ac9c696057f9f538799369781269892568 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Wed, 25 Jun 2014 15:36:33 +0200 Subject: [PATCH 59/71] refactor change_pin() to use ykpiv_transfer_data() --- tool/yubico-piv-tool.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index d3f5c06..3421716 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -787,7 +787,8 @@ static bool verify_pin(ykpiv_state *state, const char *pin) { * since they're very similar in what data they use. */ static bool change_pin(ykpiv_state *state, enum enum_action action, const char *pin, const char *new_pin) { - APDU apdu; + 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; @@ -799,20 +800,21 @@ static bool change_pin(ykpiv_state *state, enum enum_action action, const char * return false; } - memset(apdu.raw, 0, sizeof(apdu.raw)); - apdu.st.ins = action == action_arg_unblockMINUS_pin ? - YKPIV_INS_RESET_RETRY : YKPIV_INS_CHANGE_REFERENCE; - 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); } - if(ykpiv_send_data(state, apdu.raw, data, &recv_len, &sw) != YKPIV_OK) { + 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) { From ba9ee75b3f8b1e7cee1492047e18afef8f25e392 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Wed, 25 Jun 2014 15:38:53 +0200 Subject: [PATCH 60/71] stop exporting ykpiv_send_data() --- lib/ykpiv.c | 5 ++++- lib/ykpiv.h | 2 -- lib/ykpiv.map | 1 - 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/ykpiv.c b/lib/ykpiv.c index a57d0e2..fac90dd 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -38,6 +38,9 @@ #include "internal.h" #include "ykpiv.h" +static ykpiv_rc ykpiv_send_data(ykpiv_state *state, unsigned char *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++) { @@ -275,7 +278,7 @@ ykpiv_rc ykpiv_transfer_data(ykpiv_state *state, const unsigned char *templ, return YKPIV_OK; } -ykpiv_rc ykpiv_send_data(ykpiv_state *state, unsigned char *apdu, +static ykpiv_rc ykpiv_send_data(ykpiv_state *state, unsigned char *apdu, unsigned char *data, unsigned long *recv_len, int *sw) { long rc; unsigned int send_len = (unsigned int)(apdu[4] + 5); /* magic numbers.. */ diff --git a/lib/ykpiv.h b/lib/ykpiv.h index 6022a1a..a019ff9 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -68,8 +68,6 @@ extern "C" 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_send_data(ykpiv_state *state, unsigned char *apdu, - unsigned char *data, unsigned long *recv_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, diff --git a/lib/ykpiv.map b/lib/ykpiv.map index 532439e..619ea09 100644 --- a/lib/ykpiv.map +++ b/lib/ykpiv.map @@ -34,7 +34,6 @@ global: ykpiv_done; ykpiv_connect; ykpiv_disconnect; - ykpiv_send_data; ykpiv_transfer_data; ykpiv_authenticate; ykpiv_set_mgmkey; From b8ecc6cda29c2f0561ec72f075660d0d55d1a3ae Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Wed, 25 Jun 2014 15:41:20 +0200 Subject: [PATCH 61/71] refactor so ykpiv_sign_data() takes size_t input --- lib/ykpiv.c | 2 +- lib/ykpiv.h | 2 +- tool/yubico-piv-tool.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/ykpiv.c b/lib/ykpiv.c index fac90dd..dca2c3d 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -464,7 +464,7 @@ ykpiv_rc ykpiv_parse_key(ykpiv_state *state, } ykpiv_rc ykpiv_sign_data(ykpiv_state *state, - const unsigned char *raw_in, int in_len, + const unsigned char *raw_in, size_t in_len, unsigned char *sign_out, size_t *out_len, unsigned char algorithm, unsigned char key) { diff --git a/lib/ykpiv.h b/lib/ykpiv.h index a019ff9..03715c0 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -73,7 +73,7 @@ extern "C" 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, - int in_len,unsigned char *sign_out, size_t *out_len, + 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); diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 3421716..9b16696 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -514,7 +514,7 @@ static bool request_certificate(ykpiv_state *state, enum enum_key_format key_for unsigned char algorithm; int key = 0; unsigned char *signinput; - int len = 0; + size_t len = 0; sscanf(slot, "%x", &key); @@ -636,7 +636,7 @@ static bool selfsign_certificate(ykpiv_state *state, enum enum_key_format key_fo unsigned char algorithm; int key = 0; unsigned char *signinput; - int len = 0; + size_t len = 0; sscanf(slot, "%x", &key); From 1f567f62ebeeec69871f72b273881dacf5274e82 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Wed, 25 Jun 2014 15:44:00 +0200 Subject: [PATCH 62/71] drop unused code from delete_cert --- tool/yubico-piv-tool.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 9b16696..c13d4b6 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -836,7 +836,6 @@ static bool change_pin(ykpiv_state *state, enum enum_action action, const char * } static bool delete_certificate(ykpiv_state *state, enum enum_slot slot) { - APDU apdu; unsigned char objdata[7]; unsigned char *ptr = objdata; unsigned char data[0xff]; @@ -854,11 +853,6 @@ static bool delete_certificate(ykpiv_state *state, enum enum_slot slot) { *ptr++ = 0x53; *ptr++ = 0x00; /* length 0 means we'll delete the object */ - memset(apdu.raw, 0, sizeof(apdu.raw)); - apdu.st.ins = YKPIV_INS_PUT_DATA; - apdu.st.p1 = 0x3f; - apdu.st.p2 = 0xff; - if(ykpiv_transfer_data(state, templ, objdata, 7, data, &recv_len, &sw) != YKPIV_OK) { return false; From 07d831c50c855d7cbd99f743da62d5b4c0849d47 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Wed, 25 Jun 2014 15:44:21 +0200 Subject: [PATCH 63/71] drop apdu structure from tool --- tool/util.h | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/tool/util.h b/tool/util.h index acb5811..7a76466 100644 --- a/tool/util.h +++ b/tool/util.h @@ -35,20 +35,6 @@ #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; - void dump_hex(unsigned const char*, unsigned int); int set_length(unsigned char*, int); int get_length(const unsigned char*, int*); From 82e054d244546bfed28776f63207e7b1c9e2a01e Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Wed, 25 Jun 2014 15:47:56 +0200 Subject: [PATCH 64/71] rename ykpiv_send_data() to send_data() since it's local --- lib/ykpiv.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/ykpiv.c b/lib/ykpiv.c index dca2c3d..0a6b146 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -38,7 +38,7 @@ #include "internal.h" #include "ykpiv.h" -static ykpiv_rc ykpiv_send_data(ykpiv_state *state, unsigned char *apdu, +static ykpiv_rc send_data(ykpiv_state *state, unsigned char *apdu, unsigned char *data, unsigned long *recv_len, int *sw); static void dump_hex(const unsigned char *buf, unsigned int len) { @@ -196,7 +196,7 @@ ykpiv_rc ykpiv_connect(ykpiv_state *state, const char *wanted) { apdu.st.lc = sizeof(aid); memcpy(apdu.st.data, aid, sizeof(aid)); - if((res = ykpiv_send_data(state, apdu.raw, data, &recv_len, &sw) != YKPIV_OK)) { + if((res = send_data(state, apdu.raw, data, &recv_len, &sw) != YKPIV_OK)) { return res; } else if(sw == 0x9000) { return YKPIV_OK; @@ -234,7 +234,7 @@ ykpiv_rc ykpiv_transfer_data(ykpiv_state *state, const unsigned char *templ, } apdu.st.lc = this_size; memcpy(apdu.st.data, in_ptr, this_size); - res = ykpiv_send_data(state, apdu.raw, data, &recv_len, sw); + res = send_data(state, apdu.raw, data, &recv_len, sw); if(res != YKPIV_OK) { return res; } else if(*sw != 0x9000 && *sw >> 8 != 0x61) { @@ -262,7 +262,7 @@ ykpiv_rc ykpiv_transfer_data(ykpiv_state *state, const unsigned char *templ, memset(apdu.raw, 0, sizeof(apdu.raw)); apdu.st.ins = 0xc0; - res = ykpiv_send_data(state, apdu.raw, data, &recv_len, sw); + res = send_data(state, apdu.raw, data, &recv_len, sw); if(res != YKPIV_OK) { return res; } else if(*sw != 0x9000 && *sw >> 8 != 0x61) { @@ -278,7 +278,7 @@ ykpiv_rc ykpiv_transfer_data(ykpiv_state *state, const unsigned char *templ, return YKPIV_OK; } -static ykpiv_rc ykpiv_send_data(ykpiv_state *state, unsigned char *apdu, +static ykpiv_rc send_data(ykpiv_state *state, unsigned char *apdu, unsigned char *data, unsigned long *recv_len, int *sw) { long rc; unsigned int send_len = (unsigned int)(apdu[4] + 5); /* magic numbers.. */ @@ -340,7 +340,7 @@ ykpiv_rc ykpiv_authenticate(ykpiv_state *state, unsigned const char *key) { apdu.st.data[0] = 0x7c; apdu.st.data[1] = 0x02; apdu.st.data[2] = 0x80; - if((res = ykpiv_send_data(state, apdu.raw, data, &recv_len, &sw)) != YKPIV_OK) { + if((res = send_data(state, apdu.raw, data, &recv_len, &sw)) != YKPIV_OK) { return res; } else if(sw != 0x9000) { return YKPIV_AUTHENTICATION_ERROR; @@ -376,7 +376,7 @@ ykpiv_rc ykpiv_authenticate(ykpiv_state *state, unsigned const char *key) { memcpy(challenge, dataptr, 8); dataptr += 8; apdu.st.lc = dataptr - apdu.st.data; - if((res = ykpiv_send_data(state, apdu.raw, data, &recv_len, &sw)) != YKPIV_OK) { + if((res = send_data(state, apdu.raw, data, &recv_len, &sw)) != YKPIV_OK) { return res; } else if(sw != 0x9000) { return YKPIV_AUTHENTICATION_ERROR; @@ -425,7 +425,7 @@ ykpiv_rc ykpiv_set_mgmkey(ykpiv_state *state, const unsigned char *new_key) { 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 = ykpiv_send_data(state, apdu.raw, data, &recv_len, &sw)) != YKPIV_OK) { + if((res = send_data(state, apdu.raw, data, &recv_len, &sw)) != YKPIV_OK) { return res; } else if(sw == 0x9000) { return YKPIV_OK; @@ -570,7 +570,7 @@ ykpiv_rc ykpiv_get_version(ykpiv_state *state, char *version, size_t len) { memset(apdu.raw, 0, sizeof(apdu)); apdu.st.ins = YKPIV_INS_GET_VERSION; - if((res = ykpiv_send_data(state, apdu.raw, data, &recv_len, &sw)) != YKPIV_OK) { + if((res = send_data(state, apdu.raw, 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]); @@ -604,7 +604,7 @@ ykpiv_rc ykpiv_verify(ykpiv_state *state, const char *pin, int *tries) { if(len < 8) { memset(apdu.st.data + len, 0xff, 8 - len); } - if((res = ykpiv_send_data(state, apdu.raw, data, &recv_len, &sw)) != YKPIV_OK) { + if((res = send_data(state, apdu.raw, data, &recv_len, &sw)) != YKPIV_OK) { return res; } else if(sw == 0x9000) { return YKPIV_OK; From ea88f94fd4bb3d067d23f72ad58ef5d1c9cbce10 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Wed, 25 Jun 2014 15:49:07 +0200 Subject: [PATCH 65/71] add openssl/rsa.h for ykpiv.c --- lib/ykpiv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/ykpiv.c b/lib/ykpiv.c index 0a6b146..5f288a7 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -34,6 +34,7 @@ #include #include +#include #include "internal.h" #include "ykpiv.h" From 0d754893f4168876c46be3b9b785eec1ad5ef4eb Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Wed, 25 Jun 2014 15:52:01 +0200 Subject: [PATCH 66/71] let set_length take a size_t --- lib/ykpiv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ykpiv.c b/lib/ykpiv.c index 5f288a7..436fd6b 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -49,7 +49,7 @@ static void dump_hex(const unsigned char *buf, unsigned int len) { } } -static int set_length(unsigned char *buffer, int length) { +static int set_length(unsigned char *buffer, size_t length) { if(length < 0x80) { *buffer++ = length; return 1; @@ -477,7 +477,7 @@ ykpiv_rc ykpiv_sign_data(ykpiv_state *state, unsigned char sign_in[256]; size_t pad_len = 0; int sw; - int bytes; + size_t bytes; size_t len = 0; ykpiv_rc res; From b0eec2a6ea9da28d65e718bff485ed65652f2e90 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Thu, 26 Jun 2014 07:34:25 +0200 Subject: [PATCH 67/71] refactor to let send_data take an APDU* for data --- lib/ykpiv.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/ykpiv.c b/lib/ykpiv.c index 436fd6b..0102298 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -39,7 +39,7 @@ #include "internal.h" #include "ykpiv.h" -static ykpiv_rc send_data(ykpiv_state *state, unsigned char *apdu, +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) { @@ -197,7 +197,7 @@ ykpiv_rc ykpiv_connect(ykpiv_state *state, const char *wanted) { apdu.st.lc = sizeof(aid); memcpy(apdu.st.data, aid, sizeof(aid)); - if((res = send_data(state, apdu.raw, data, &recv_len, &sw) != YKPIV_OK)) { + if((res = send_data(state, &apdu, data, &recv_len, &sw) != YKPIV_OK)) { return res; } else if(sw == 0x9000) { return YKPIV_OK; @@ -235,7 +235,7 @@ ykpiv_rc ykpiv_transfer_data(ykpiv_state *state, const unsigned char *templ, } apdu.st.lc = this_size; memcpy(apdu.st.data, in_ptr, this_size); - res = send_data(state, apdu.raw, data, &recv_len, sw); + res = send_data(state, &apdu, data, &recv_len, sw); if(res != YKPIV_OK) { return res; } else if(*sw != 0x9000 && *sw >> 8 != 0x61) { @@ -263,7 +263,7 @@ ykpiv_rc ykpiv_transfer_data(ykpiv_state *state, const unsigned char *templ, memset(apdu.raw, 0, sizeof(apdu.raw)); apdu.st.ins = 0xc0; - res = send_data(state, apdu.raw, data, &recv_len, sw); + res = send_data(state, &apdu, data, &recv_len, sw); if(res != YKPIV_OK) { return res; } else if(*sw != 0x9000 && *sw >> 8 != 0x61) { @@ -279,17 +279,17 @@ ykpiv_rc ykpiv_transfer_data(ykpiv_state *state, const unsigned char *templ, return YKPIV_OK; } -static ykpiv_rc send_data(ykpiv_state *state, unsigned char *apdu, +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[4] + 5); /* magic numbers.. */ + unsigned int send_len = (unsigned int)apdu->st.lc + 5; if(state->verbose > 1) { fprintf(stderr, "> "); - dump_hex(apdu, send_len); + dump_hex(apdu->raw, send_len); fprintf(stderr, "\n"); } - rc = SCardTransmit(state->card, SCARD_PCI_T1, apdu, send_len, NULL, data, recv_len); + 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); @@ -341,7 +341,7 @@ ykpiv_rc ykpiv_authenticate(ykpiv_state *state, unsigned const char *key) { apdu.st.data[0] = 0x7c; apdu.st.data[1] = 0x02; apdu.st.data[2] = 0x80; - if((res = send_data(state, apdu.raw, data, &recv_len, &sw)) != YKPIV_OK) { + if((res = send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) { return res; } else if(sw != 0x9000) { return YKPIV_AUTHENTICATION_ERROR; @@ -377,7 +377,7 @@ ykpiv_rc ykpiv_authenticate(ykpiv_state *state, unsigned const char *key) { memcpy(challenge, dataptr, 8); dataptr += 8; apdu.st.lc = dataptr - apdu.st.data; - if((res = send_data(state, apdu.raw, data, &recv_len, &sw)) != YKPIV_OK) { + if((res = send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) { return res; } else if(sw != 0x9000) { return YKPIV_AUTHENTICATION_ERROR; @@ -426,7 +426,7 @@ ykpiv_rc ykpiv_set_mgmkey(ykpiv_state *state, const unsigned char *new_key) { 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.raw, data, &recv_len, &sw)) != YKPIV_OK) { + if((res = send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) { return res; } else if(sw == 0x9000) { return YKPIV_OK; @@ -571,7 +571,7 @@ ykpiv_rc ykpiv_get_version(ykpiv_state *state, char *version, size_t len) { memset(apdu.raw, 0, sizeof(apdu)); apdu.st.ins = YKPIV_INS_GET_VERSION; - if((res = send_data(state, apdu.raw, data, &recv_len, &sw)) != YKPIV_OK) { + 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]); @@ -605,7 +605,7 @@ ykpiv_rc ykpiv_verify(ykpiv_state *state, const char *pin, int *tries) { if(len < 8) { memset(apdu.st.data + len, 0xff, 8 - len); } - if((res = send_data(state, apdu.raw, data, &recv_len, &sw)) != YKPIV_OK) { + if((res = send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) { return res; } else if(sw == 0x9000) { return YKPIV_OK; From a1c2e4e8d158555867d9ad130639ed002699ef95 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Thu, 26 Jun 2014 08:02:44 +0200 Subject: [PATCH 68/71] refactor writing object to it's own library function ykpiv_save_object(), use that for writing certs --- lib/ykpiv.c | 71 +++++++++++++++++++++++++++++++++--------- lib/ykpiv.h | 2 ++ lib/ykpiv.map | 1 + tool/yubico-piv-tool.c | 29 +++-------------- 4 files changed, 64 insertions(+), 39 deletions(-) diff --git a/lib/ykpiv.c b/lib/ykpiv.c index 0102298..568f447 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -80,6 +80,19 @@ static int get_length(const unsigned char *buffer, size_t *len) { return 0; } +static unsigned char *set_object(int object_id, unsigned char *buffer) { + 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) { @@ -633,25 +646,17 @@ 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}; - long inlen = 5; ykpiv_rc res; - indata[0] = 0x5c; - if(object_id == YKPIV_OBJ_DISCOVERY) { - indata[1] = 1; - indata[2] = YKPIV_OBJ_DISCOVERY; - inlen = 3; - } else if(object_id > 0xffff && object_id <= 0xffffff) { - indata[1] = 3; - indata[2] = (object_id >> 16) & 0xff; - indata[3] = (object_id >> 8) & 0xff; - indata[4] = object_id & 0xff; - } else { + *inptr++ = 0x5c; + inptr = set_object(object_id, inptr); + if(inptr == NULL) { return YKPIV_INVALID_OBJECT; } - if((res = ykpiv_transfer_data(state, templ, indata, inlen, data, len, &sw)) + if((res = ykpiv_transfer_data(state, templ, indata, inptr - indata, data, len, &sw)) != YKPIV_OK) { return res; } @@ -662,5 +667,43 @@ ykpiv_rc ykpiv_fetch_object(ykpiv_state *state, int object_id, memmove(data, data + 1 + offs, outlen); *len = outlen; } - return YKPIV_OK; + 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; + + if(len > sizeof(data) - 9) { + return YKPIV_SIZE_ERROR; + } + *dataptr++ = 0x5c; + 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, 0, + &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 index 03715c0..afb64a2 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -79,6 +79,8 @@ extern "C" 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 diff --git a/lib/ykpiv.map b/lib/ykpiv.map index 619ea09..fc97ac0 100644 --- a/lib/ykpiv.map +++ b/lib/ykpiv.map @@ -42,6 +42,7 @@ global: ykpiv_get_version; ykpiv_verify; ykpiv_fetch_object; + ykpiv_save_object; local: *; diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index c13d4b6..000c717 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -377,7 +377,6 @@ static bool import_cert(ykpiv_state *state, enum enum_key_format cert_format, 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) { @@ -409,31 +408,14 @@ static bool import_cert(ykpiv_state *state, enum enum_key_format cert_format, { unsigned char certdata[2100]; unsigned char *certptr = certdata; - unsigned char data[0xff]; - unsigned char templ[] = {0, YKPIV_INS_PUT_DATA, 0x3f, 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.. */ @@ -444,11 +426,8 @@ static bool import_cert(ykpiv_state *state, enum enum_key_format cert_format, *certptr++ = 0xfe; /* LRC */ *certptr++ = 0; - if(ykpiv_transfer_data(state, templ, certdata, certptr - certdata, data, - &recv_len, &sw) != YKPIV_OK) { - fprintf(stderr, "Failed commands with device.\n"); - } else 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; } From 75a5cf74d2042d637df888f5b46fed03add17e91 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Thu, 26 Jun 2014 08:08:59 +0200 Subject: [PATCH 69/71] refactor set_chuid() to use ykpiv_save_object() --- tool/yubico-piv-tool.c | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 000c717..4e507f6 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -50,14 +50,13 @@ * 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, @@ -451,31 +450,24 @@ import_cert_out: } static bool set_chuid(ykpiv_state *state, int verbose) { - unsigned char templ[] = {0, YKPIV_INS_PUT_DATA, 0x3f, 0xff}; - unsigned char data[0xff]; unsigned char chuid[sizeof(chuid_tmpl)]; - unsigned char *dataptr = chuid; - unsigned long recv_len = sizeof(data); - int sw; + ykpiv_rc res; memcpy(chuid, chuid_tmpl, sizeof(chuid)); - dataptr += CHUID_GUID_OFFS; - if(RAND_pseudo_bytes(dataptr, 0x10) == -1) { + 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"); } - if(ykpiv_transfer_data(state, templ, chuid, sizeof(chuid), data, &recv_len, &sw) != YKPIV_OK) { - fprintf(stderr, "Failed communicating with device.\n"); - return false; - } else 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; } From ac79b0809de98fb786eba0898155ee99e40f1a50 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Thu, 26 Jun 2014 08:24:34 +0200 Subject: [PATCH 70/71] refactor delete action to use ykpiv_save_object() --- lib/ykpiv.c | 3 ++- tool/yubico-piv-tool.c | 25 ++++--------------------- 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/lib/ykpiv.c b/lib/ykpiv.c index 568f447..3e2e534 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -682,6 +682,7 @@ ykpiv_rc ykpiv_save_object(ykpiv_state *state, int object_id, 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; @@ -696,7 +697,7 @@ ykpiv_rc ykpiv_save_object(ykpiv_state *state, int object_id, memcpy(dataptr, indata, len); dataptr += len; - if((res = ykpiv_transfer_data(state, templ, data, dataptr - data, NULL, 0, + if((res = ykpiv_transfer_data(state, templ, data, dataptr - data, NULL, &outlen, &sw)) != YKPIV_OK) { return res; } diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 4e507f6..12c4ac5 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -807,32 +807,15 @@ static bool change_pin(ykpiv_state *state, enum enum_action action, const char * } static bool delete_certificate(ykpiv_state *state, enum enum_slot slot) { - unsigned char objdata[7]; - unsigned char *ptr = objdata; - unsigned char data[0xff]; - unsigned long recv_len = sizeof(data); - unsigned char templ[] = {0, YKPIV_INS_PUT_DATA, 0x3f, 0xff}; - int sw; - bool ret = false; 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 */ - - if(ykpiv_transfer_data(state, templ, objdata, 7, data, &recv_len, &sw) - != YKPIV_OK) { + if(ykpiv_save_object(state, object, NULL, 0) != YKPIV_OK) { + fprintf(stderr, "Failed deleting object.\n"); return false; - } else if(sw != 0x9000) { - fprintf(stderr, "Failed deleting certificate to device with code %x.\n", sw); } else { - ret = true; + fprintf(stdout, "Certificate deleted.\n"); + return true; } - return ret; } int main(int argc, char *argv[]) { From b34db10086ce22ec8203e6e092c0deb8c85d557a Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Thu, 26 Jun 2014 08:28:38 +0200 Subject: [PATCH 71/71] let set_object set tag as well, not only lv --- lib/ykpiv.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/ykpiv.c b/lib/ykpiv.c index 3e2e534..9cda463 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -81,6 +81,7 @@ static int get_length(const unsigned char *buffer, size_t *len) { } 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; @@ -650,7 +651,6 @@ ykpiv_rc ykpiv_fetch_object(ykpiv_state *state, int object_id, unsigned char templ[] = {0, YKPIV_INS_GET_DATA, 0x3f, 0xff}; ykpiv_rc res; - *inptr++ = 0x5c; inptr = set_object(object_id, inptr); if(inptr == NULL) { return YKPIV_INVALID_OBJECT; @@ -687,7 +687,6 @@ ykpiv_rc ykpiv_save_object(ykpiv_state *state, int object_id, if(len > sizeof(data) - 9) { return YKPIV_SIZE_ERROR; } - *dataptr++ = 0x5c; dataptr = set_object(object_id, dataptr); if(dataptr == NULL) { return YKPIV_INVALID_OBJECT;