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