Merge pull request #130 from Yubico/ykpiv_util

Refactor yubico-piv-tool/libykpiv with a ykpiv_util_* high-level API
This commit is contained in:
Trevor Bentley
2017-11-20 10:23:05 +01:00
committed by GitHub
40 changed files with 7969 additions and 801 deletions
+24 -2
View File
@@ -7,6 +7,7 @@ autom4te.cache/
config.log config.log
config.status config.status
configure configure
doxygen-doc/
libtool libtool
m4/libtool.m4 m4/libtool.m4
m4/ltoptions.m4 m4/ltoptions.m4
@@ -15,6 +16,8 @@ m4/ltversion.m4
m4/lt~obsolete.m4 m4/lt~obsolete.m4
*.c~ *.c~
*.h~ *.h~
*.plist
.libs
ChangeLog ChangeLog
build-aux/ar-lib build-aux/ar-lib
build-aux/compile build-aux/compile
@@ -25,6 +28,9 @@ build-aux/install-sh
build-aux/ltmain.sh build-aux/ltmain.sh
build-aux/missing build-aux/missing
build-aux/test-driver build-aux/test-driver
GPATH
GRTAGS
GTAGS
tmp32/ tmp32/
tmp64/ tmp64/
yubico-piv-tool-*-win32.zip yubico-piv-tool-*-win32.zip
@@ -35,6 +41,10 @@ yubico-piv-tool-*.tar.gz
yubico-piv-tool-*.tar.gz.sig yubico-piv-tool-*.tar.gz.sig
yubico-piv-tool-*-mac.zip yubico-piv-tool-*-mac.zip
yubico-piv-tool-*-mac.zip.sig yubico-piv-tool-*-mac.zip.sig
lib/tests/api
lib/tests/api.log
lib/tests/api.o
lib/tests/api.trs
lib/tests/basic lib/tests/basic
lib/tests/basic.log lib/tests/basic.log
lib/tests/basic.o lib/tests/basic.o
@@ -46,7 +56,13 @@ lib/tests/parse_key.trs
lib/tests/test-suite.log lib/tests/test-suite.log
lib/error.lo lib/error.lo
lib/error.o lib/error.o
lib/internal.la
lib/internal.lo
lib/internal.o
lib/libykpiv.la lib/libykpiv.la
lib/util.la
lib/util.lo
lib/util.o
lib/version.lo lib/version.lo
lib/version.o lib/version.o
lib/ykpiv-version.h lib/ykpiv-version.h
@@ -67,17 +83,21 @@ tool/libpiv_cmd_la-cmdline.o
tool/libpiv_util.la tool/libpiv_util.la
tool/util.lo tool/util.lo
tool/util.o tool/util.o
tool/tests/cert_9a.pem
tool/tests/cert_9e.pem
tool/tests/basic.sh.log tool/tests/basic.sh.log
tool/tests/basic.sh.trs tool/tests/basic.sh.trs
tool/tests/test-suite.log tool/tests/key_9a.pub
tool/tests/parse_name tool/tests/parse_name
tool/tests/parse_name.log tool/tests/parse_name.log
tool/tests/parse_name.o tool/tests/parse_name.o
tool/tests/parse_name.trs tool/tests/parse_name.trs
tool/tests/req_9e.pem
tool/tests/test_inout tool/tests/test_inout
tool/tests/test_inout.log tool/tests/test_inout.log
tool/tests/test_inout.o tool/tests/test_inout.o
tool/tests/test_inout.trs tool/tests/test_inout.trs
tool/tests/test-suite.log
coverage/ coverage/
lib/error.gcno lib/error.gcno
lib/version.gcno lib/version.gcno
@@ -91,9 +111,11 @@ ykcs11/*.lo
ykcs11/ykcs11.pc ykcs11/ykcs11.pc
ykcs11/libykcs11.la ykcs11/libykcs11.la
ykcs11/ykcs11-version.h ykcs11/ykcs11-version.h
ykcs11/tests/reset.sh.log
ykcs11/tests/reset.sh.trs
ykcs11/tests/test-suite.log
ykcs11/tests/ykcs11_tests ykcs11/tests/ykcs11_tests
ykcs11/tests/ykcs11_tests.log ykcs11/tests/ykcs11_tests.log
ykcs11/tests/ykcs11_tests.o ykcs11/tests/ykcs11_tests.o
ykcs11/tests/ykcs11_tests.trs ykcs11/tests/ykcs11_tests.trs
ykcs11/tests/test-suite.log
yubico-piv-tool.1.txt yubico-piv-tool.1.txt
+8 -1
View File
@@ -33,7 +33,6 @@ EXTRA_DIST = windows.mk mac.mk tool/tests/basic.sh tools/fasc.pl
EXTRA_DIST += doc/Attestation.adoc doc/YKCS11_release_notes.adoc doc/YubiKey_PIV_introduction.adoc EXTRA_DIST += doc/Attestation.adoc doc/YKCS11_release_notes.adoc doc/YubiKey_PIV_introduction.adoc
if ENABLE_COV if ENABLE_COV
cov-reset: cov-reset:
rm -fr coverage rm -fr coverage
@@ -63,6 +62,14 @@ endif
# Maintainer rules. # Maintainer rules.
if DX_COND_html
doxygen:
doxygen lib/Doxyfile
endif
hwcheck:
@$(srcdir)/tools/confirm.sh && YKPIV_ENV_HWTESTS_CONFIRMED="1" $(MAKE) check
check-doc-dist: check-doc-dist:
perl -pe "s,^EXTRA_DIST \+= .*,EXTRA_DIST += `cd $(srcdir) && ls doc/*.adoc | xargs echo`," < $(srcdir)/Makefile.am > check-doc-dist.tmp perl -pe "s,^EXTRA_DIST \+= .*,EXTRA_DIST += `cd $(srcdir) && ls doc/*.adoc | xargs echo`," < $(srcdir)/Makefile.am > check-doc-dist.tmp
diff -ur $(srcdir)/Makefile.am check-doc-dist.tmp || \ diff -ur $(srcdir)/Makefile.am check-doc-dist.tmp || \
+15 -1
View File
@@ -1,6 +1,20 @@
yubico-piv-tool NEWS -- History of user-visible changes. -*- outline -*- yubico-piv-tool NEWS -- History of user-visible changes. -*- outline -*-
* Version 1.4.5 (unreleased) * Version 1.5.0 (unreleased)
** API additions: Higher-level "util" API added to libykpiv.
** Added ykpiv_attest(), ykpiv_get_pin_retries(), ykpiv_set_pin_retries()
** Added functions for using existing PCSC card handle.
** Support using custom memory allocator.
** Documentation updates. 'make doxygen' for HTML format.
** Expanded automated tests for hardware devices, moved to 'make hwcheck'.
** Moderate internal refactoring. Many small bugs fixed.
* Version 1.4.4 (released 2017-10-17) * Version 1.4.4 (released 2017-10-17)
+2 -2
View File
@@ -67,8 +67,8 @@ situations, running ./configure should automatically find the proper
backend to use. backend to use.
=== Building from Git === Building from Git
Recent versions of autoconf, automake, pkg-config and libtool must Recent versions of autoconf, automake, check, pkg-config, and libtool
be installed. Help2man is used to generate the manpages. Gengetopt must be installed. Help2man is used to generate the manpages. Gengetopt
version 2.22.6 or later is needed for command line parameter handling. version 2.22.6 or later is needed for command line parameter handling.
The The
link:https://github.com/Yubico/yubico-piv-tool/tree/master/vagrant/development[Vagrant link:https://github.com/Yubico/yubico-piv-tool/tree/master/vagrant/development[Vagrant
+2 -1
View File
@@ -32,7 +32,7 @@ if [ "x$TRAVIS_OS_NAME" != "xosx" ]; then
sudo apt-get update -qq sudo apt-get update -qq
sudo apt-get remove -qq -y $REMOVE sudo apt-get remove -qq -y $REMOVE
sudo apt-get autoremove -qq sudo apt-get autoremove -qq
sudo apt-get install -qq -y gengetopt help2man $EXTRA sudo apt-get install -qq -y gengetopt help2man check $EXTRA
TAR=tar TAR=tar
else else
ARCH=osx ARCH=osx
@@ -40,6 +40,7 @@ else
brew uninstall libtool brew uninstall libtool
brew install libtool brew install libtool
brew install help2man brew install help2man
brew install check
brew install pkg-config brew install pkg-config
brew install gengetopt brew install gengetopt
brew install gnu-tar brew install gnu-tar
+24 -4
View File
@@ -25,7 +25,7 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
AC_INIT([yubico-piv-tool], [1.4.5]) AC_INIT([yubico-piv-tool], [1.5.0])
AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_MACRO_DIR([m4])
@@ -33,9 +33,9 @@ AC_CONFIG_MACRO_DIR([m4])
# Interfaces changed/added/removed: CURRENT++ REVISION=0 # Interfaces changed/added/removed: CURRENT++ REVISION=0
# Interfaces added: AGE++ # Interfaces added: AGE++
# Interfaces removed: AGE=0 # Interfaces removed: AGE=0
AC_SUBST([LT_CURRENT], 4) AC_SUBST([LT_CURRENT], 5)
AC_SUBST([LT_REVISION], 7) AC_SUBST([LT_REVISION], 0)
AC_SUBST([LT_AGE], 3) AC_SUBST([LT_AGE], 4)
AM_INIT_AUTOMAKE([-Wall -Werror foreign]) AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AM_SILENT_RULES([yes]) AM_SILENT_RULES([yes])
@@ -50,10 +50,30 @@ AM_MISSING_PROG(GENGETOPT, gengetopt, $missing_dir)
PKG_PROG_PKG_CONFIG PKG_PROG_PKG_CONFIG
PKG_CHECK_MODULES(OPENSSL, libcrypto) PKG_CHECK_MODULES(OPENSSL, libcrypto)
PKG_CHECK_MODULES([CHECK], [check >= 0.9.6])
DX_HTML_FEATURE(ON)
DX_INIT_DOXYGEN(libykpiv,lib/Doxyfile)
gl_LD_VERSION_SCRIPT gl_LD_VERSION_SCRIPT
gl_VALGRIND_TESTS gl_VALGRIND_TESTS
# Check for clang
AC_CACHE_CHECK([for clang],
_cv_clang,[
AC_TRY_COMPILE([], [
#ifdef __clang__
#else
#error "NOT CLANG"
#endif
return 0;
],
[_cv_clang=yes],
[_cv_clang=no],
[])
])
AM_CONDITIONAL([COMPILER_CLANG], [test "$_cv_clang" = yes])
AC_ARG_WITH([backend], AC_ARG_WITH([backend],
[AS_HELP_STRING([--with-backend=ARG], [AS_HELP_STRING([--with-backend=ARG],
[use specific backend/linkage; 'pcsc', 'macscard' or 'winscard'])], [use specific backend/linkage; 'pcsc', 'macscard' or 'winscard'])],
+2473
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -32,7 +32,7 @@ AM_CPPFLAGS = $(OPENSSL_CFLAGS) $(PCSC_CFLAGS)
lib_LTLIBRARIES = libykpiv.la lib_LTLIBRARIES = libykpiv.la
libykpiv_la_SOURCES = ykpiv.c version.c ykpiv.pc.in ykpiv.map internal.h libykpiv_la_SOURCES = ykpiv.c util.c internal.c internal.h version.c ykpiv.pc.in ykpiv.map
libykpiv_la_SOURCES += error.c libykpiv_la_SOURCES += error.c
libykpiv_la_includedir = $(includedir)/ykpiv libykpiv_la_includedir = $(includedir)/ykpiv
libykpiv_la_include_HEADERS = ykpiv.h ykpiv-version.h libykpiv_la_include_HEADERS = ykpiv.h ykpiv-version.h
+445
View File
@@ -0,0 +1,445 @@
#ifdef _WINDOWS
#include <windows.h>
#include <wincrypt.h>
#include <bcrypt.h>
#else
#include <openssl/des.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <string.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include "internal.h"
/*
** Definitions
*/
#ifdef _WINDOWS
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
struct des_key {
HCRYPTPROV hProv;
HCRYPTKEY hKey;
ALG_ID alg;
};
static const BYTE PRIVATEKEY_EXPOF1_BLOB[] =
{
0x07, 0x02, 0x00, 0x00, 0x00, 0xA4, 0x00, 0x00,
0x52, 0x53, 0x41, 0x32, 0x00, 0x02, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0xAB, 0xEF, 0xFA, 0xC6,
0x7D, 0xE8, 0xDE, 0xFB, 0x68, 0x38, 0x09, 0x92,
0xD9, 0x42, 0x7E, 0x6B, 0x89, 0x9E, 0x21, 0xD7,
0x52, 0x1C, 0x99, 0x3C, 0x17, 0x48, 0x4E, 0x3A,
0x44, 0x02, 0xF2, 0xFA, 0x74, 0x57, 0xDA, 0xE4,
0xD3, 0xC0, 0x35, 0x67, 0xFA, 0x6E, 0xDF, 0x78,
0x4C, 0x75, 0x35, 0x1C, 0xA0, 0x74, 0x49, 0xE3,
0x20, 0x13, 0x71, 0x35, 0x65, 0xDF, 0x12, 0x20,
0xF5, 0xF5, 0xF5, 0xC1, 0xED, 0x5C, 0x91, 0x36,
0x75, 0xB0, 0xA9, 0x9C, 0x04, 0xDB, 0x0C, 0x8C,
0xBF, 0x99, 0x75, 0x13, 0x7E, 0x87, 0x80, 0x4B,
0x71, 0x94, 0xB8, 0x00, 0xA0, 0x7D, 0xB7, 0x53,
0xDD, 0x20, 0x63, 0xEE, 0xF7, 0x83, 0x41, 0xFE,
0x16, 0xA7, 0x6E, 0xDF, 0x21, 0x7D, 0x76, 0xC0,
0x85, 0xD5, 0x65, 0x7F, 0x00, 0x23, 0x57, 0x45,
0x52, 0x02, 0x9D, 0xEA, 0x69, 0xAC, 0x1F, 0xFD,
0x3F, 0x8C, 0x4A, 0xD0,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x64, 0xD5, 0xAA, 0xB1,
0xA6, 0x03, 0x18, 0x92, 0x03, 0xAA, 0x31, 0x2E,
0x48, 0x4B, 0x65, 0x20, 0x99, 0xCD, 0xC6, 0x0C,
0x15, 0x0C, 0xBF, 0x3E, 0xFF, 0x78, 0x95, 0x67,
0xB1, 0x74, 0x5B, 0x60,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const DWORD PRIVATEKEY_EXPOF1_BITLEN = 512;
const ALG_ID PRIVATEKEY_EXPOF1_ALG = CALG_RSA_KEYX;
#else
struct des_key {
DES_key_schedule ks1;
DES_key_schedule ks2;
DES_key_schedule ks3;
};
#endif
/*
** Methods
*/
des_rc des_import_key(const int type, const unsigned char* keyraw, const size_t keyrawlen, des_key** key) {
des_rc rc = DES_OK;
size_t cb_expectedkey = DES_LEN_3DES;
#ifdef _WINDOWS
HCRYPTKEY hNullKey = 0;
ALG_ID alg = 0;
unsigned char* pbSessionBlob = NULL;
DWORD cbSessionBlob = 0;
DWORD cbRandom = 0;
unsigned char* pbTmp = NULL;
size_t n = 0;
switch (type) {
case DES_TYPE_3DES:
alg = CALG_3DES;
cb_expectedkey = DES_LEN_3DES;
break;
default:
rc = DES_INVALID_PARAMETER;
goto ERROR_EXIT;
}
if (!keyraw) { rc = DES_INVALID_PARAMETER; goto ERROR_EXIT; }
if (keyrawlen != cb_expectedkey) { rc = DES_INVALID_PARAMETER; goto ERROR_EXIT; }
if (!key) { rc = DES_INVALID_PARAMETER; goto ERROR_EXIT; }
if (!(*key = (des_key*)malloc(sizeof(des_key)))) { rc = DES_MEMORY_ERROR; goto ERROR_EXIT; }
memset(*key, 0, sizeof(des_key));
(*key)->alg = alg;
if (!CryptAcquireContext(&((*key)->hProv), NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { rc = DES_GENERAL_ERROR; goto ERROR_EXIT; }
// Import the exponent-of-one private key.
if (!CryptImportKey((*key)->hProv, PRIVATEKEY_EXPOF1_BLOB, sizeof(PRIVATEKEY_EXPOF1_BLOB), 0, 0, &hNullKey)) { rc = DES_GENERAL_ERROR; goto ERROR_EXIT; }
// calculate Simple blob's length
cbSessionBlob = (PRIVATEKEY_EXPOF1_BITLEN / 8) + sizeof(ALG_ID) + sizeof(BLOBHEADER);
// allocate simple blob buffer
if (!(pbSessionBlob = malloc(cbSessionBlob))) { rc = DES_MEMORY_ERROR; goto ERROR_EXIT; }
memset(pbSessionBlob, 0, cbSessionBlob);
pbTmp = pbSessionBlob;
// SIMPLEBLOB Format is documented in SDK
// Copy header to buffer
((BLOBHEADER *)pbTmp)->bType = SIMPLEBLOB;
((BLOBHEADER *)pbTmp)->bVersion = 2;
((BLOBHEADER *)pbTmp)->reserved = 0;
((BLOBHEADER *)pbTmp)->aiKeyAlg = alg;
pbTmp += sizeof(BLOBHEADER);
// Copy private key algorithm to buffer
*((DWORD *)pbTmp) = PRIVATEKEY_EXPOF1_ALG;
pbTmp += sizeof(ALG_ID);
// Place the key material in reverse order
for (n = 0; n < keyrawlen; n++) {
pbTmp[n] = keyraw[keyrawlen - n - 1];
}
// 3 is for the first reserved byte after the key material + the 2 reserved bytes at the end.
cbRandom = cbSessionBlob - (sizeof(ALG_ID) + sizeof(BLOBHEADER) + (DWORD)keyrawlen + 3);
pbTmp += (keyrawlen + 1);
// Generate random data for the rest of the buffer
// (except that last two bytes)
if (!CryptGenRandom((*key)->hProv, cbRandom, pbTmp)) { rc = DES_GENERAL_ERROR; goto ERROR_EXIT; }
for (n = 0; n < cbRandom; n++) {
if (pbTmp[n] == 0) pbTmp[n] = 1;
}
pbSessionBlob[cbSessionBlob - 2] = 2;
if (!CryptImportKey((*key)->hProv, pbSessionBlob, cbSessionBlob, hNullKey, CRYPT_EXPORTABLE, &((*key)->hKey))) { rc = DES_GENERAL_ERROR; goto ERROR_EXIT; }
#else
const_DES_cblock key_tmp;
size_t cb_keysize = 8;
switch (type) {
case DES_TYPE_3DES:
cb_expectedkey = DES_LEN_3DES;
cb_keysize = 8;
break;
default:
rc = DES_INVALID_PARAMETER;
goto ERROR_EXIT;
}
if (cb_keysize > sizeof(key_tmp)) { rc = DES_MEMORY_ERROR; goto ERROR_EXIT; }
if (!keyraw) { rc = DES_INVALID_PARAMETER; goto ERROR_EXIT; }
if (keyrawlen != cb_expectedkey) { rc = DES_INVALID_PARAMETER; goto ERROR_EXIT; }
if (!key) { rc = DES_INVALID_PARAMETER; goto ERROR_EXIT; }
if (!(*key = (des_key*)malloc(sizeof(des_key)))) { rc = DES_MEMORY_ERROR; goto ERROR_EXIT; }
memset(*key, 0, sizeof(des_key));
memcpy(key_tmp, keyraw, cb_keysize);
DES_set_key_unchecked(&key_tmp, &((*key)->ks1));
memcpy(key_tmp, keyraw + cb_keysize, cb_keysize);
DES_set_key_unchecked(&key_tmp, &((*key)->ks2));
memcpy(key_tmp, keyraw + (2 * cb_keysize), cb_keysize);
DES_set_key_unchecked(&key_tmp, &((*key)->ks3));
#endif
EXIT:
#ifdef _WINDOWS
if (pbSessionBlob) {
free(pbSessionBlob);
pbSessionBlob = NULL;
}
if (hNullKey) {
CryptDestroyKey(hNullKey);
hNullKey = 0;
}
#endif
return rc;
ERROR_EXIT:
if (key) {
des_destroy_key(*key);
*key = NULL;
}
goto EXIT;
}
des_rc des_destroy_key(des_key* key) {
if (key) {
#ifdef _WINDOWS
if (key->hKey) {
CryptDestroyKey(key->hKey);
key->hKey = 0;
}
if (key->hProv) {
CryptReleaseContext(key->hProv, 0);
key->hProv = 0;
}
#endif
free(key);
}
return DES_OK;
}
des_rc des_encrypt(des_key* key, const unsigned char* in, const size_t inlen, unsigned char* out, size_t* outlen) {
des_rc rc = DES_OK;
#ifdef _WINDOWS
unsigned char buf[8] = { 0 };
size_t buflen = sizeof(buf);
#endif
if (!key || !outlen || (*outlen < inlen) || !in || !out) { rc = DES_INVALID_PARAMETER; goto EXIT; }
#ifdef _WINDOWS
if (!key->hKey) { rc = DES_INVALID_PARAMETER; goto EXIT; }
memcpy(out, in, inlen);
*outlen = inlen;
if (!CryptEncrypt(key->hKey, 0, FALSE, 0, out, (DWORD*)&inlen, (DWORD)*outlen)) { fwprintf(stderr, L"GetLastError = %x\n", GetLastError()); rc = DES_GENERAL_ERROR; goto EXIT; }
// reset key usage by encrypting a fake padded block
CryptEncrypt(key->hKey, 0, TRUE, 0, buf, (DWORD*)&buflen, (DWORD)buflen);
//if (CALG_3DES == key->alg) {
// // truncate the final pad block
// *outlen = inlen - 8;
//}
//else {
// *outlen = inlen;
//}
#else
/* openssl returns void */
DES_ecb3_encrypt((const_DES_cblock *)in, (DES_cblock*)out, &(key->ks1), &(key->ks2), &(key->ks3), 1);
#endif
EXIT:
return rc;
}
des_rc des_decrypt(des_key* key, const unsigned char* in, const size_t inlen, unsigned char* out, size_t* outlen) {
des_rc rc = DES_OK;
#ifdef _WINDOWS
unsigned char buf[8] = { 0 };
size_t buflen = sizeof(buf);
#endif
if (!key || !outlen || (*outlen < inlen) || !in || !out) { rc = DES_INVALID_PARAMETER; goto EXIT; }
#ifdef _WINDOWS
if (!key->hKey) { rc = DES_INVALID_PARAMETER; goto EXIT; }
memcpy(out, in, inlen);
*outlen = inlen;
if (!CryptDecrypt(key->hKey, 0, FALSE, 0, out, (DWORD*)outlen)) { fwprintf(stderr, L"GetLastError = %x\n", GetLastError()); rc = DES_GENERAL_ERROR; goto EXIT; }
// reset key usage by decrypting a fake padded block
CryptDecrypt(key->hKey, 0, TRUE, 0, buf, (DWORD*)&buflen);
#else
/* openssl returns void */
DES_ecb3_encrypt((const_DES_cblock*)in, (DES_cblock*)out, &(key->ks1), &(key->ks2), &(key->ks3), 0);
#endif
EXIT:
return rc;
}
bool yk_des_is_weak_key(const unsigned char *key, const size_t cb_key) {
#ifdef _WINDOWS
/* defined weak keys, borrowed from openssl to be consistent across platforms */
static const unsigned char weak_keys[][DES_LEN_DES] = {
/* weak keys */
{0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01},
{0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE},
{0x1F,0x1F,0x1F,0x1F,0x0E,0x0E,0x0E,0x0E},
{0xE0,0xE0,0xE0,0xE0,0xF1,0xF1,0xF1,0xF1},
/* semi-weak keys */
{0x01,0xFE,0x01,0xFE,0x01,0xFE,0x01,0xFE},
{0xFE,0x01,0xFE,0x01,0xFE,0x01,0xFE,0x01},
{0x1F,0xE0,0x1F,0xE0,0x0E,0xF1,0x0E,0xF1},
{0xE0,0x1F,0xE0,0x1F,0xF1,0x0E,0xF1,0x0E},
{0x01,0xE0,0x01,0xE0,0x01,0xF1,0x01,0xF1},
{0xE0,0x01,0xE0,0x01,0xF1,0x01,0xF1,0x01},
{0x1F,0xFE,0x1F,0xFE,0x0E,0xFE,0x0E,0xFE},
{0xFE,0x1F,0xFE,0x1F,0xFE,0x0E,0xFE,0x0E},
{0x01,0x1F,0x01,0x1F,0x01,0x0E,0x01,0x0E},
{0x1F,0x01,0x1F,0x01,0x0E,0x01,0x0E,0x01},
{0xE0,0xFE,0xE0,0xFE,0xF1,0xFE,0xF1,0xFE},
{0xFE,0xE0,0xFE,0xE0,0xFE,0xF1,0xFE,0xF1} };
unsigned char tmp[DES_LEN_3DES] = { 0 };
int i = 0;
unsigned char c = 0x00;
if (sizeof(tmp) != cb_key) return true;
/* set odd parity of key */
for (i = 0; i < sizeof(tmp); i++) {
/* count number of set bits in byte, excluding the low-order bit - SWAR method */
c = key[i] & 0xFE;
c = (c & 0x55) + ((c >> 1) & 0x55);
c = (c & 0x33) + ((c >> 2) & 0x33);
c = (c & 0x0F) + ((c >> 4) & 0x0F);
/* if count is even, set low key bit to 1, otherwise 0 */
tmp[i] = (key[i] & 0xFE) | ((c & 0x01) ? 0x00 : 0x01);
}
/* check odd parity key against table by DES key block*/
for (i = 0; i < sizeof(weak_keys) / sizeof(weak_keys[0]); i++) {
if ((0 == memcmp(weak_keys[i], tmp, DES_LEN_DES)) ||
(0 == memcmp(weak_keys[i], tmp + DES_LEN_DES, DES_LEN_DES)) ||
(0 == memcmp(weak_keys[i], tmp + 2*DES_LEN_DES, DES_LEN_DES))) {
return true;
}
}
return false;
#else
return DES_is_weak_key((const_DES_cblock *)key);
#endif
}
prng_rc _ykpiv_prng_generate(unsigned char *buffer, const size_t cb_req) {
prng_rc rc = PRNG_OK;
#ifdef _WINDOWS
HCRYPTPROV hProv = 0;
if (CryptAcquireContext(&hProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
if (!CryptGenRandom(hProv, (DWORD)cb_req, buffer)) {
rc = PRNG_GENERAL_ERROR;
}
CryptReleaseContext(hProv, 0);
}
else {
rc = PRNG_GENERAL_ERROR;
}
#else
if (-1 == RAND_pseudo_bytes(buffer, cb_req)) {
rc = PRNG_GENERAL_ERROR;
}
#endif
return rc;
}
pkcs5_rc pkcs5_pbkdf2_sha1(const unsigned char* password, const size_t cb_password, const unsigned char* salt, const size_t cb_salt, unsigned long long iterations, unsigned char* key, const size_t cb_key) {
pkcs5_rc rc = PKCS5_OK;
#ifdef _WINDOWS
BCRYPT_ALG_HANDLE hAlg = 0;
/* mingw64 defines the BCryptDeriveKeyPBKDF2 function, but its dll link library doesn't include the export.
**
** In case this is needed, we'll need to dynamically load the function:
**
** typedef NTSTATUS WINAPI (*PFN_BCryptDeriveKeyPBKDF2) (BCRYPT_ALG_HANDLE hPrf, PUCHAR pbPassword, ULONG cbPassword, PUCHAR pbSalt, ULONG cbSalt, ULONGLONG cIterations, PUCHAR pbDerivedKey, ULONG cbDerivedKey, ULONG dwFlags);
** HMODULE hBCrypt = LoadLibrary("bcrypt.dll");
** PFN_BCryptDeriveKeyPBKDF2 pbkdf2 = GetProcAddress(hBCrypt, "BCryptDeriveKeyPBKDF2");
*/
if (STATUS_SUCCESS == BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_SHA1_ALGORITHM, NULL, BCRYPT_ALG_HANDLE_HMAC_FLAG)) {
if (STATUS_SUCCESS != BCryptDeriveKeyPBKDF2(hAlg, (PUCHAR)password, (ULONG)cb_password, (PUCHAR)salt, (ULONG)cb_salt, iterations, key, (ULONG)cb_key, 0)) {
rc = PKCS5_GENERAL_ERROR;
}
BCryptCloseAlgorithmProvider(hAlg, 0);
}
else {
rc = PKCS5_GENERAL_ERROR;
}
#else
/* for some reason openssl always returns 1 for PBKDF2 */
PKCS5_PBKDF2_HMAC_SHA1((const char*)password, cb_password, salt, cb_salt, iterations, cb_key, key);
#endif
return rc;
}
+129 -3
View File
@@ -31,6 +31,8 @@
#ifndef YKPIV_INTERNAL_H #ifndef YKPIV_INTERNAL_H
#define YKPIV_INTERNAL_H #define YKPIV_INTERNAL_H
#include "ykpiv.h"
#include <stdbool.h> #include <stdbool.h>
#if BACKEND_PCSC #if BACKEND_PCSC
@@ -42,14 +44,115 @@
#endif #endif
#endif #endif
// Typedef DWORD (defined by pcsc lib) to pcsc_word to make it clear that this
// is not the Windows meaning of DWORD, but the PCSC library's meaning. This
// differs: Windows defines a DWORD as 32-bits, but pcsclite defines it as
// 'unsigned long' on x86_64 Linux, which is often 64-bits.
typedef DWORD pcsc_word;
#ifdef __cplusplus
extern "C"
{
#endif
#define DES_TYPE_3DES 1
#define DES_LEN_DES 8
#define DES_LEN_3DES DES_LEN_DES*3
#define READER_LEN 32 #define READER_LEN 32
#define MAX_READERS 16 #define MAX_READERS 16
#define CB_MGM_KEY DES_LEN_3DES
// the object size is restricted to the firmware's message buffer size, which
// always contains 0x5C + 1 byte len + 3 byte id + 0x53 + 3 byte len = 9 bytes,
// so while the message buffer == CB_BUF_MAX, the maximum object we can store
// is CB_BUF_MAX - 9
#define CB_OBJ_MAX_NEO (CB_BUF_MAX_NEO - 9)
#define CB_OBJ_MAX_YK4 (CB_BUF_MAX_YK4 - 9)
#define CB_OBJ_MAX CB_OBJ_MAX_YK4
#define CB_BUF_MAX_NEO 2048
#define CB_BUF_MAX_YK4 3072
#define CB_BUF_MAX CB_BUF_MAX_YK4
#define CB_ATR_MAX 33
#define CHREF_ACT_CHANGE_PIN 0
#define CHREF_ACT_UNBLOCK_PIN 1
#define CHREF_ACT_CHANGE_PUK 2
#define TAG_CERT 0x70
#define TAG_CERT_COMPRESS 0x71
#define TAG_CERT_LRC 0xFE
#define TAG_ADMIN 0x80
#define TAG_ADMIN_FLAGS_1 0x81
#define TAG_ADMIN_SALT 0x82
#define TAG_ADMIN_TIMESTAMP 0x83
#define TAG_PROTECTED 0x88
#define TAG_PROTECTED_FLAGS_1 0x81
#define TAG_PROTECTED_MGM 0x89
#define TAG_MSCMAP 0x81
#define TAG_MSROOTS_END 0x82
#define TAG_MSROOTS_MID 0x83
#define TAG_RSA_MODULUS 0x81
#define TAG_RSA_EXP 0x82
#define TAG_ECC_POINT 0x86
#define CB_ECC_POINTP256 65
#define CB_ECC_POINTP384 97
#define YKPIV_OBJ_ADMIN_DATA 0x5fff00
#define YKPIV_OBJ_ATTESTATION 0x5fff01
#define YKPIV_OBJ_MSCMAP 0x5fff10
#define YKPIV_OBJ_MSROOTS1 0x5fff11
#define YKPIV_OBJ_MSROOTS2 0x5fff12
#define YKPIV_OBJ_MSROOTS3 0x5fff13
#define YKPIV_OBJ_MSROOTS4 0x5fff14
#define YKPIV_OBJ_MSROOTS5 0x5fff15
#define ADMIN_FLAGS_1_PUK_BLOCKED 0x01
#define ADMIN_FLAGS_1_PROTECTED_MGM 0x02
#define CB_ADMIN_SALT 16
#define CB_ADMIN_TIMESTAMP 4
#define ITER_MGM_PBKDF2 10000
#define PROTECTED_FLAGS_1_PUK_NOBLOCK 0x01
#define CB_OBJ_TAG_MIN 2 // 1 byte tag + 1 byte len
#define CB_OBJ_TAG_MAX (CB_OBJ_TAG_MIN + 2) // 1 byte tag + 3 bytes len
#define member_size(type, member) sizeof(((type*)0)->member)
typedef enum {
DES_OK = 0,
DES_INVALID_PARAMETER = -1,
DES_BUFFER_TOO_SMALL = -2,
DES_MEMORY_ERROR = -3,
DES_GENERAL_ERROR = -4
} des_rc;
typedef enum {
PKCS5_OK = 0,
PKCS5_GENERAL_ERROR = -1
} pkcs5_rc;
typedef enum {
PRNG_OK = 0,
PRNG_GENERAL_ERROR = -1
} prng_rc;
struct ykpiv_state { struct ykpiv_state {
SCARDCONTEXT context; SCARDCONTEXT context;
SCARDHANDLE card; SCARDHANDLE card;
int verbose; int verbose;
char *pin; char *pin;
ykpiv_allocator allocator;
bool isNEO;
}; };
union u_APDU { union u_APDU {
@@ -65,9 +168,32 @@ union u_APDU {
}; };
typedef union u_APDU APDU; typedef union u_APDU APDU;
typedef struct des_key des_key;
unsigned const char aid[] = { extern unsigned const char aid[];
0xa0, 0x00, 0x00, 0x03, 0x08
}; des_rc des_import_key(const int type, const unsigned char* keyraw, const size_t keyrawlen, des_key** key);
des_rc des_destroy_key(des_key* key);
des_rc des_encrypt(des_key* key, const unsigned char* in, const size_t inlen, unsigned char* out, size_t* outlen);
des_rc des_decrypt(des_key* key, const unsigned char* in, const size_t inlen, unsigned char* out, size_t* outlen);
pkcs5_rc pkcs5_pbkdf2_sha1(const unsigned char* password, const size_t cb_password, const unsigned char* salt, const size_t cb_salt, unsigned long long iterations, unsigned char* key, const size_t cb_key);
bool yk_des_is_weak_key(const unsigned char *key, const size_t cb_key);
prng_rc _ykpiv_prng_generate(unsigned char *buffer, const size_t cb_req);
ykpiv_rc _ykpiv_begin_transaction(ykpiv_state *state);
ykpiv_rc _ykpiv_end_transaction(ykpiv_state *state);
ykpiv_rc _ykpiv_ensure_application_selected(ykpiv_state *state);
int _ykpiv_set_length(unsigned char *buffer, size_t length);
int _ykpiv_get_length(const unsigned char *buffer, size_t *len);
void* _ykpiv_alloc(ykpiv_state *state, size_t size);
void* _ykpiv_realloc(ykpiv_state *state, void *address, size_t size);
void _ykpiv_free(ykpiv_state *state, void *data);
ykpiv_rc _ykpiv_save_object(ykpiv_state *state, int object_id, unsigned char *indata, size_t len);
ykpiv_rc _ykpiv_fetch_object(ykpiv_state *state, int object_id, unsigned char *data, unsigned long *len);
#ifdef __cplusplus
}
#endif
#endif #endif
+12 -5
View File
@@ -25,13 +25,20 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
AM_CFLAGS = $(WARN_CFLAGS) AM_CFLAGS = $(WARN_CFLAGS) @CHECK_CFLAGS@ $(OPENSSL_CFLAGS)
AM_CPPFLAGS = -I$(top_srcdir)/lib -I$(top_builddir)/lib AM_CPPFLAGS = -I$(top_srcdir)/lib -I$(top_builddir)/lib $(OPENSSL_CFLAGS) $(PCSC_CFLAGS)
AM_LDFLAGS = -no-install AM_LDFLAGS = @CHECK_LIBS@
LDADD = ../libykpiv.la
check_PROGRAMS = basic parse_key if COMPILER_CLANG
AM_LDFLAGS += -no-fast-install
else
AM_LDFLAGS += -no-install
endif
LDADD = ../libykpiv.la $(OPENSSL_LIBS)
check_PROGRAMS = basic parse_key api
TESTS = $(check_PROGRAMS) TESTS = $(check_PROGRAMS)
LOG_COMPILER = $(VALGRIND) LOG_COMPILER = $(VALGRIND)
+974
View File
@@ -0,0 +1,974 @@
/*
* Copyright (c) 2014-2016 Yubico AB
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "ykpiv.h"
#include "internal.h"
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <check.h>
#ifdef __MINGW32__
#define dprintf(fd, ...) fprintf(stdout, __VA_ARGS__)
#endif
int destruction_confirmed(void);
// only defined in libcheck 0.11+ (linux distros still shipping 0.10)
#ifndef ck_assert_ptr_nonnull
#define ck_assert_ptr_nonnull(a) ck_assert((a) != NULL)
#endif
#ifndef ck_assert_mem_eq
#define ck_assert_mem_eq(a,b,n) ck_assert(memcmp((a), (b), (n)) == 0)
#endif
ykpiv_state *g_state;
const uint8_t g_cert[] = {
"0123456789ABCDEFGHIK0123456789ABCDEFGHIK0123456789ABCDEFGHIK0123456789ABCDEFGHIK"
"0123456789ABCDEFGHIK0123456789ABCDEFGHIK0123456789ABCDEFGHIK0123456789ABCDEFGHIK"
"0123456789ABCDEFGHIK0123456789ABCDEFGHIK0123456789ABCDEFGHIK0123456789ABCDEFGHIK"
"0123456789ABCDEFGHIK0123456789ABCDEFGHIK0123456789ABCDEFGHIK0123456789ABCDEFGHIK"
"0123456789ABCDEFGHIK0123456789ABCDEFGHIK0123456789ABCDEFGHIK0123456789ABCDEFGHIK"
};
void setup(void) {
ykpiv_rc res;
// Require user confirmation to continue, since this test suite will clear
// any data stored on connected keys.
if (!destruction_confirmed())
exit(77); // exit code 77 == skipped tests
res = ykpiv_init(&g_state, true);
ck_assert_int_eq(res, YKPIV_OK);
ck_assert_ptr_nonnull(g_state);
res = ykpiv_connect(g_state, NULL);
ck_assert_int_eq(res, YKPIV_OK);
}
void teardown(void) {
ykpiv_rc res;
// This is the expected case, if the allocator test ran, since it de-inits.
if (NULL == g_state)
return;
res = ykpiv_disconnect(g_state);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_done(g_state);
ck_assert_int_eq(res, YKPIV_OK);
}
START_TEST(test_devicemodel) {
ykpiv_rc res;
ykpiv_devmodel model;
char version[256];
char reader_buf[2048];
size_t num_readers = sizeof(reader_buf);
res = ykpiv_get_version(g_state, version, sizeof(version));
ck_assert_int_eq(res, YKPIV_OK);
fprintf(stderr, "Version: %s\n", version);
model = ykpiv_util_devicemodel(g_state);
fprintf(stdout, "Model: %u\n", model);
ck_assert(model == DEVTYPE_YK4 || model == DEVTYPE_NEOr3);
res = ykpiv_list_readers(g_state, reader_buf, &num_readers);
ck_assert_int_eq(res, YKPIV_OK);
ck_assert_int_gt(num_readers, 0);
if (model == DEVTYPE_YK4) {
ck_assert_ptr_nonnull(strstr(reader_buf, "Yubikey 4"));
ck_assert(version[0] == '4'); // Verify app version 4.x
ck_assert(version[1] == '.');
}
else {
ck_assert_ptr_nonnull(strstr(reader_buf, "Yubikey NEO"));
ck_assert(version[0] == '1'); // Verify app version 1.x
ck_assert(version[1] == '.');
}
}
END_TEST
START_TEST(test_get_set_cardid) {
ykpiv_rc res;
ykpiv_cardid set_id;
ykpiv_cardid get_id;
memset(&set_id.data, 'i', sizeof(set_id.data));
memset(&get_id.data, 0, sizeof(get_id.data));
res = ykpiv_util_set_cardid(g_state, &set_id);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_util_get_cardid(g_state, &get_id);
ck_assert_int_eq(res, YKPIV_OK);
ck_assert_mem_eq(&set_id.data, &get_id.data, sizeof(set_id.data));
}
END_TEST
START_TEST(test_list_readers) {
ykpiv_rc res;
char reader_buf[2048];
size_t num_readers = sizeof(reader_buf);
char *reader_ptr;
res = ykpiv_list_readers(g_state, reader_buf, &num_readers);
ck_assert_int_eq(res, YKPIV_OK);
ck_assert_int_gt(num_readers, 0);
for(reader_ptr = reader_buf; *reader_ptr != '\0'; reader_ptr += strlen(reader_ptr) + 1) {
fprintf(stdout, "Found device: %s\n", reader_ptr);
}
}
END_TEST
START_TEST(test_read_write_list_delete_cert) {
ykpiv_rc res;
uint8_t *read_cert = NULL;
size_t read_cert_len = 0;
{
res = ykpiv_util_write_cert(g_state, YKPIV_KEY_AUTHENTICATION, (uint8_t*)g_cert, sizeof(g_cert), YKPIV_CERTINFO_UNCOMPRESSED);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_util_read_cert(g_state, YKPIV_KEY_AUTHENTICATION, &read_cert, &read_cert_len);
ck_assert_int_eq(res, YKPIV_OK);
ck_assert_ptr_nonnull(read_cert);
ck_assert_int_eq(read_cert_len, sizeof(g_cert));
ck_assert_mem_eq(g_cert, read_cert, sizeof(g_cert));
res = ykpiv_util_free(g_state, read_cert);
ck_assert_int_eq(res, YKPIV_OK);
}
{
ykpiv_key *keys = NULL;
size_t data_len;
uint8_t key_count;
res = ykpiv_util_list_keys(g_state, &key_count, &keys, &data_len);
ck_assert_int_eq(res, YKPIV_OK);
ck_assert_ptr_nonnull(keys);
ck_assert_int_gt(key_count, 0);
res = ykpiv_util_free(g_state, keys);
ck_assert_int_eq(res, YKPIV_OK);
}
{
res = ykpiv_util_delete_cert(g_state, YKPIV_KEY_AUTHENTICATION);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_util_read_cert(g_state, YKPIV_KEY_AUTHENTICATION, &read_cert, &read_cert_len);
ck_assert_int_eq(res, YKPIV_GENERIC_ERROR);
res = ykpiv_util_free(g_state, read_cert);
ck_assert_int_eq(res, YKPIV_OK);
}
}
END_TEST
#include <openssl/des.h>
#include <openssl/pem.h>
#include <openssl/pkcs12.h>
#include <openssl/rand.h>
// RSA2048 private key, generated with: `openssl genrsa 2048 -out private.pem`
static const char *private_key_pem =
"-----BEGIN RSA PRIVATE KEY-----\n"
"MIIEpAIBAAKCAQEAwVUwmVbc+ffOy2+RivxBpgleTVN6bUa0q7jNYB+AseFQYaYq\n"
"EGfa+VGdxSGo+8DV1KT9+fNEd5243gXn/tcjtMItKeB+oAQc64s9lIFlYuR8bpq1\n"
"ibr33iW2elnnv9mpecqohdCVwM2McWveoPyb7MwlwVuhqexOzJO29bqJcazLbtkf\n"
"ZETK0oBx53/ylA4Y6nE9Pa46jW2qhj+KShf1iBg+gAyt3eI+wI2Wmub1WxLLH8D2\n"
"w+kow8QhQOa8dHCkRRw771JxVO5+d+Y/Y+x9B1HgF4q0q9xUlhWLK2TR4ChBFzXe\n"
"47sAHsSqi/pl5JbwYrHPOE/VEBLukmjL8NFCSQIDAQABAoIBADmEyOK2DyRnb6Ti\n"
"2qBJEJb/boj+7wuX36S/ZIrWlIlXiXyj3RvoaiOG/rNpokbURknvlIhKsfIMgLW9\n"
"eBo/k6Xxp1IwMjwVPS1uzbFjFfDoHYUijiQd9iSnf7TDDsnrThqoCp9VQViNTt1n\n"
"xGKNBS7cRddTFbPiVEdVIzfUeZPR2oRrc4maBCRCrQgg8WNknawmc8zhkf2NiPj3\n"
"tWLQHMy1/MgW2W1LM9sgzllEtS5CZUnyGy2HbbhS2tbZ6j9kPzOp0pPxxTTzJmmV\n"
"fi1vkJcVW4+MdXjWmhALcPA4dO7Y2Ljiu6VxIxQORRO1DyiCjAs1AVMQxgPAAY41\n"
"YR4Q2EkCgYEA4zE0oytg97aVaBY9CKi7/PqR+NI/uEvfoQCnT+ddaJgp/qsspuXo\n"
"tJt94p13ANd8O7suqQTVNvbZq1rX10xQjJZ9nvlqQa6iHkN6Epq31XBK3Z+acjIV\n"
"A2rAgKBByjz9/CpKHqnOsrTWU1Y7x416IG4BZt42hHdrxRH98/wiDH8CgYEA2djj\n"
"AjwgK+MwDnshwT1NNgCSP/2ZHatBAykZ5BCs9BJ6MNYqqXVGYoqs5Z5kSkow+Db3\n"
"pipkEieo5w2Rd5zkolTThaVCvRkSe5wRiBpZhaeY+b0UFwavGCb6zU/MmJIMDPiI\n"
"2iRGeCXgQDvIS/icIqzbTtp6dZaoMgG7LdSR7TcCgYBtxGhaLas8A8tL7vKuLFgn\n"
"cij0vyBqOr5hW596y54l2t7vXGTGfm5gVIAN7WaB0ZsEgPuaTet2Eu44DDwcmZKR\n"
"WmR3Wqor8eQCGzfvpTEMvqRtT5+fbPMaI4m+m68ttyo/m28UQZbMYPLscM2RLJnE\n"
"8WFcAiD0/33iST8ZksggoQKBgQDE/7Yhsj+hkHxHzB+1QPtOp2uaBHnvc4uCESwB\n"
"qvbMbN0kxrejsJLqz98UcozdBYSNIiAHmvQN2uGJuCJhGXdEORNjGxRkLoUhVPwh\n"
"qTplfC8BQHQncnrqi21oNw6ctg3BuQsAwaccRZwqWiWCVhrT3J8iCr6NEaWeOySK\n"
"iF1CNwKBgQCRpkkZArlccwS0kMvkK+tQ1rG2xWm7c05G34gP/g6dHFRy0gPNMyvi\n"
"SkiLTJmQIEZSAEiq0FFgcVwM6o556ftvQZuwDp5rHUbwqnHCpMJKpD9aJpStvfPi\n"
"4p9JbYdaGqnq4eoNKemmGnbUof0dR9Zr0lGmcMTwwzBib+4E1d7soA==\n"
"-----END RSA PRIVATE KEY-----\n";
// Certificate signed with key above:
// `openssl req -x509 -key private.pem -out cert.pem -subj "/CN=bar/OU=test/O=example.com/" -new`
static const char *certificate_pem =
"-----BEGIN CERTIFICATE-----\n"
"MIIC5zCCAc+gAwIBAgIJAOq8A/cmpxF5MA0GCSqGSIb3DQEBCwUAMDMxDDAKBgNV\n"
"BAMMA2JhcjENMAsGA1UECwwEdGVzdDEUMBIGA1UECgwLZXhhbXBsZS5jb20wHhcN\n"
"MTcwODAzMTE1MDI2WhcNMTgwODAzMTE1MDI2WjAzMQwwCgYDVQQDDANiYXIxDTAL\n"
"BgNVBAsMBHRlc3QxFDASBgNVBAoMC2V4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0B\n"
"AQEFAAOCAQ8AMIIBCgKCAQEAwVUwmVbc+ffOy2+RivxBpgleTVN6bUa0q7jNYB+A\n"
"seFQYaYqEGfa+VGdxSGo+8DV1KT9+fNEd5243gXn/tcjtMItKeB+oAQc64s9lIFl\n"
"YuR8bpq1ibr33iW2elnnv9mpecqohdCVwM2McWveoPyb7MwlwVuhqexOzJO29bqJ\n"
"cazLbtkfZETK0oBx53/ylA4Y6nE9Pa46jW2qhj+KShf1iBg+gAyt3eI+wI2Wmub1\n"
"WxLLH8D2w+kow8QhQOa8dHCkRRw771JxVO5+d+Y/Y+x9B1HgF4q0q9xUlhWLK2TR\n"
"4ChBFzXe47sAHsSqi/pl5JbwYrHPOE/VEBLukmjL8NFCSQIDAQABMA0GCSqGSIb3\n"
"DQEBCwUAA4IBAQCamrwdEhNmY2GCQWq6U90Q3XQT6w0HHW/JmtuGeF+BTpVr12gN\n"
"/UvEXTo9geWbGcCTjaMMURTa7mUjVUIttIWEVHZMKqBuvsUM1RcuOEX/vitaJJ8K\n"
"Sw4upjCNa3ZxUXmSA1FBixZgDzFqjEeSiaJjMU0yX5W2p1T4iNYtF3YqzMF5AWSI\n"
"qCO7gP5ezPyg5kDnrO3V7DBgnDiqawq7Pyn9DynKNULX/hc1yls/R+ebb2u8Z+h5\n"
"W4YXbzGZb8qdT27qIZaHD638tL6liLkI6UE4KCXH8X8e3fqdbmqvwrq403nOGmsP\n"
"cbJb2PEXibNEQG234riKxm7x7vNDLL79Jwtc\n"
"-----END CERTIFICATE-----\n";
static bool set_component(unsigned char *in_ptr, const BIGNUM *bn, int element_len) {
int real_len = BN_num_bytes(bn);
if(real_len > element_len) {
return false;
}
memset(in_ptr, 0, (size_t)(element_len - real_len));
in_ptr += element_len - real_len;
BN_bn2bin(bn, in_ptr);
return true;
}
static bool prepare_rsa_signature(const unsigned char *in, unsigned int in_len, unsigned char *out, unsigned int *out_len, int nid) {
X509_SIG digestInfo;
X509_ALGOR algor;
ASN1_TYPE parameter;
ASN1_OCTET_STRING digest;
unsigned char data[1024];
memcpy(data, in, in_len);
digestInfo.algor = &algor;
digestInfo.algor->algorithm = OBJ_nid2obj(nid);
digestInfo.algor->parameter = &parameter;
digestInfo.algor->parameter->type = V_ASN1_NULL;
digestInfo.algor->parameter->value.ptr = NULL;
digestInfo.digest = &digest;
digestInfo.digest->data = data;
digestInfo.digest->length = (int)in_len;
*out_len = (unsigned int)i2d_X509_SIG(&digestInfo, &out);
return true;
}
static void import_key(unsigned char slot, unsigned char pin_policy) {
ykpiv_rc res;
{
unsigned char pp = pin_policy;
unsigned char tp = YKPIV_TOUCHPOLICY_DEFAULT;
EVP_PKEY *private_key = NULL;
BIO *bio = NULL;
RSA *rsa_private_key = NULL;
unsigned char e[4];
unsigned char p[128];
unsigned char q[128];
unsigned char dmp1[128];
unsigned char dmq1[128];
unsigned char iqmp[128];
int element_len = 128;
bio = BIO_new_mem_buf(private_key_pem, strlen(private_key_pem));
private_key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
ck_assert_ptr_nonnull(private_key);
BIO_free(bio);
rsa_private_key = EVP_PKEY_get1_RSA(private_key);
ck_assert_ptr_nonnull(rsa_private_key);
ck_assert(set_component(e, rsa_private_key->e, 3));
ck_assert(set_component(p, rsa_private_key->p, element_len));
ck_assert(set_component(q, rsa_private_key->q, element_len));
ck_assert(set_component(dmp1, rsa_private_key->dmp1, element_len));
ck_assert(set_component(dmq1, rsa_private_key->dmq1, element_len));
ck_assert(set_component(iqmp, rsa_private_key->iqmp, element_len));
// Try wrong algorithm, fail.
res = ykpiv_import_private_key(g_state,
slot,
YKPIV_ALGO_RSA1024,
p, element_len,
q, element_len,
dmp1, element_len,
dmq1, element_len,
iqmp, element_len,
NULL, 0,
pp, tp);
ck_assert_int_eq(res, YKPIV_ALGORITHM_ERROR);
// Try right algorithm
res = ykpiv_import_private_key(g_state,
slot,
YKPIV_ALGO_RSA2048,
p, element_len,
q, element_len,
dmp1, element_len,
dmq1, element_len,
iqmp, element_len,
NULL, 0,
pp, tp);
ck_assert_int_eq(res, YKPIV_OK);
EVP_PKEY_free(private_key);
}
// Use imported key to decrypt a thing. See that it works.
{
BIO *bio = NULL;
X509 *cert = NULL;
EVP_PKEY *pub_key = NULL;
unsigned char secret[32];
unsigned char secret2[32];
unsigned char data[256];
int len;
size_t len2 = sizeof(data);
RSA *rsa = NULL;
bio = BIO_new_mem_buf(certificate_pem, strlen(certificate_pem));
cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
ck_assert_ptr_nonnull(cert);
BIO_free(bio);
pub_key = X509_get_pubkey(cert);
ck_assert_ptr_nonnull(pub_key);
rsa = EVP_PKEY_get1_RSA(pub_key);
ck_assert_ptr_nonnull(rsa);
ck_assert_int_gt(RAND_pseudo_bytes(secret, sizeof(secret)), 0);
len = RSA_public_encrypt(sizeof(secret), secret, data, rsa, RSA_PKCS1_PADDING);
ck_assert_int_ge(len, 0);
res = ykpiv_verify(g_state, "123456", NULL);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_decipher_data(g_state, data, (size_t)len, data, &len2, YKPIV_ALGO_RSA2048, slot);
ck_assert_int_eq(res, YKPIV_OK);
len = RSA_padding_check_PKCS1_type_2(secret2, sizeof(secret2), data + 1, len2 - 1, RSA_size(rsa));
ck_assert_int_eq(len, sizeof(secret));
ck_assert_int_eq(memcmp(secret, secret2, sizeof(secret)), 0);
X509_free(cert);
}
}
START_TEST(test_import_key) {
ykpiv_rc res;
import_key(0x9a, YKPIV_PINPOLICY_DEFAULT);
// Verify certificate
{
BIO *bio = NULL;
X509 *cert = NULL;
RSA *rsa = NULL;
EVP_PKEY *pub_key = NULL;
const EVP_MD *md = EVP_sha256();
EVP_MD_CTX *mdctx;
unsigned char signature[1024];
unsigned char encoded[1024];
unsigned char data[1024];
unsigned char signinput[1024];
unsigned char rand[128];
size_t sig_len = sizeof(signature);
size_t padlen = 256;
unsigned int enc_len;
unsigned int data_len;
bio = BIO_new_mem_buf(certificate_pem, strlen(certificate_pem));
cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
ck_assert_ptr_nonnull(cert);
BIO_free(bio);
pub_key = X509_get_pubkey(cert);
ck_assert_ptr_nonnull(pub_key);
rsa = EVP_PKEY_get1_RSA(pub_key);
ck_assert_ptr_nonnull(rsa);
ck_assert_int_gt(RAND_pseudo_bytes(rand, 128), 0);
mdctx = EVP_MD_CTX_create();
EVP_DigestInit_ex(mdctx, md, NULL);
EVP_DigestUpdate(mdctx, rand, 128);
EVP_DigestFinal_ex(mdctx, data, &data_len);
prepare_rsa_signature(data, data_len, encoded, &enc_len, EVP_MD_type(md));
ck_assert_int_ne(RSA_padding_add_PKCS1_type_1(signinput, padlen, encoded, enc_len), 0);
res = ykpiv_sign_data(g_state, signinput, padlen, signature, &sig_len, YKPIV_ALGO_RSA2048, 0x9a);
ck_assert_int_eq(res, YKPIV_OK);
ck_assert_int_eq(RSA_verify(EVP_MD_type(md), data, data_len, signature, sig_len, rsa), 1);
X509_free(cert);
}
// Verify that imported key can not be attested
{
unsigned char attest[2048];
size_t attest_len = sizeof(attest);
ykpiv_devmodel model;
model = ykpiv_util_devicemodel(g_state);
res = ykpiv_attest(g_state, 0x9a, attest, &attest_len);
if (model == DEVTYPE_YK4) {
ck_assert_int_eq(res, YKPIV_GENERIC_ERROR);
}
else {
ck_assert_int_eq(res, YKPIV_NOT_SUPPORTED);
}
}
}
END_TEST
START_TEST(test_pin_policy_always) {
ykpiv_rc res;
{
ykpiv_devmodel model;
model = ykpiv_util_devicemodel(g_state);
// Only works with YK4. NEO should skip.
if (model != DEVTYPE_YK4) {
fprintf(stderr, "WARNING: Not supported with Yubikey NEO. Test skipped.\n");
return;
}
}
import_key(0x9e, YKPIV_PINPOLICY_ALWAYS);
// Verify certificate
{
BIO *bio = NULL;
X509 *cert = NULL;
RSA *rsa = NULL;
EVP_PKEY *pub_key = NULL;
const EVP_MD *md = EVP_sha256();
EVP_MD_CTX *mdctx;
unsigned char signature[1024];
unsigned char encoded[1024];
unsigned char data[1024];
unsigned char signinput[1024];
unsigned char rand[128];
size_t sig_len = sizeof(signature);
size_t padlen = 256;
unsigned int enc_len;
unsigned int data_len;
bio = BIO_new_mem_buf(certificate_pem, strlen(certificate_pem));
cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
ck_assert_ptr_nonnull(cert);
BIO_free(bio);
pub_key = X509_get_pubkey(cert);
ck_assert_ptr_nonnull(pub_key);
rsa = EVP_PKEY_get1_RSA(pub_key);
ck_assert_ptr_nonnull(rsa);
ck_assert_int_gt(RAND_pseudo_bytes(rand, 128), 0);
mdctx = EVP_MD_CTX_create();
EVP_DigestInit_ex(mdctx, md, NULL);
EVP_DigestUpdate(mdctx, rand, 128);
EVP_DigestFinal_ex(mdctx, data, &data_len);
prepare_rsa_signature(data, data_len, encoded, &enc_len, EVP_MD_type(md));
ck_assert_int_ne(RSA_padding_add_PKCS1_type_1(signinput, padlen, encoded, enc_len), 0);
// Sign without verify: fail
res = ykpiv_sign_data(g_state, signinput, padlen, signature, &sig_len, YKPIV_ALGO_RSA2048, 0x9e);
ck_assert_int_eq(res, YKPIV_AUTHENTICATION_ERROR);
// Sign with verify: pass
res = ykpiv_verify(g_state, "123456", NULL);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_sign_data(g_state, signinput, padlen, signature, &sig_len, YKPIV_ALGO_RSA2048, 0x9e);
ck_assert_int_eq(res, YKPIV_OK);
// Sign again without verify: fail
res = ykpiv_sign_data(g_state, signinput, padlen, signature, &sig_len, YKPIV_ALGO_RSA2048, 0x9e);
ck_assert_int_eq(res, YKPIV_AUTHENTICATION_ERROR);
// Sign again with verify: pass
res = ykpiv_verify(g_state, "123456", NULL);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_sign_data(g_state, signinput, padlen, signature, &sig_len, YKPIV_ALGO_RSA2048, 0x9e);
ck_assert_int_eq(res, YKPIV_OK);
ck_assert_int_eq(RSA_verify(EVP_MD_type(md), data, data_len, signature, sig_len, rsa), 1);
X509_free(cert);
}
}
END_TEST
START_TEST(test_generate_key) {
ykpiv_rc res;
uint8_t *mod, *exp;
size_t mod_len, exp_len;
res = ykpiv_util_write_cert(g_state, YKPIV_KEY_AUTHENTICATION, (uint8_t*)g_cert, sizeof(g_cert), YKPIV_CERTINFO_UNCOMPRESSED);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_util_generate_key(g_state,
YKPIV_KEY_AUTHENTICATION,
YKPIV_ALGO_RSA2048,
YKPIV_PINPOLICY_DEFAULT,
YKPIV_TOUCHPOLICY_DEFAULT,
&mod,
&mod_len,
&exp,
&exp_len,
NULL,
NULL);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_util_free(g_state, mod);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_util_free(g_state, exp);
ck_assert_int_eq(res, YKPIV_OK);
// Verify that imported key can be attested
{
ykpiv_devmodel model;
unsigned char attest[2048];
size_t attest_len = sizeof(attest);
model = ykpiv_util_devicemodel(g_state);
res = ykpiv_attest(g_state, YKPIV_KEY_AUTHENTICATION, attest, &attest_len);
// Only works with YK4. NEO should error.
if (model == DEVTYPE_YK4) {
ck_assert_int_eq(res, YKPIV_OK);
ck_assert_int_gt(attest_len, 0);
}
else {
ck_assert_int_eq(res, YKPIV_NOT_SUPPORTED);
}
}
}
END_TEST
START_TEST(test_authenticate) {
ykpiv_rc res;
const char *default_mgm_key = "010203040506070801020304050607080102030405060708";
const char *mgm_key = "112233445566778811223344556677881122334455667788";
const char *weak_mgm_key = "FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFE";
unsigned char key[24];
size_t key_len = sizeof(key);
// Try new key, fail.
res = ykpiv_hex_decode(mgm_key, strlen(mgm_key), key, &key_len);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_authenticate(g_state, key);
ck_assert_int_eq(res, YKPIV_AUTHENTICATION_ERROR);
// Try default key, succeed
res = ykpiv_hex_decode(default_mgm_key, strlen(default_mgm_key), key, &key_len);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_authenticate(g_state, key);
ck_assert_int_eq(res, YKPIV_OK);
// Verify same key works twice
res = ykpiv_hex_decode(default_mgm_key, strlen(default_mgm_key), key, &key_len);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_authenticate(g_state, key);
ck_assert_int_eq(res, YKPIV_OK);
// Change to new key
res = ykpiv_hex_decode(mgm_key, strlen(mgm_key), key, &key_len);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_set_mgmkey(g_state, key);
ck_assert_int_eq(res, YKPIV_OK);
// Try new key, succeed.
res = ykpiv_hex_decode(mgm_key, strlen(mgm_key), key, &key_len);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_authenticate(g_state, key);
ck_assert_int_eq(res, YKPIV_OK);
// Change back to default key
res = ykpiv_hex_decode(default_mgm_key, strlen(default_mgm_key), key, &key_len);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_set_mgmkey(g_state, key);
ck_assert_int_eq(res, YKPIV_OK);
// Try default key, succeed
res = ykpiv_hex_decode(default_mgm_key, strlen(default_mgm_key), key, &key_len);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_authenticate(g_state, key);
ck_assert_int_eq(res, YKPIV_OK);
// Try to set a weak key, fail
res = ykpiv_hex_decode(weak_mgm_key, strlen(weak_mgm_key), key, &key_len);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_set_mgmkey(g_state, key);
ck_assert_int_eq(res, YKPIV_KEY_ERROR);
// Try default key, succeed
res = ykpiv_hex_decode(default_mgm_key, strlen(default_mgm_key), key, &key_len);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_authenticate(g_state, key);
ck_assert_int_eq(res, YKPIV_OK);
}
END_TEST
START_TEST(test_change_pin) {
ykpiv_rc res;
res = ykpiv_verify(g_state, "123456", NULL);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_change_pin(g_state, "123456", 6, "ABCDEF", 6, NULL);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_verify(g_state, "123456", NULL);
ck_assert_int_eq(res, YKPIV_WRONG_PIN);
res = ykpiv_verify(g_state, "ABCDEF", NULL);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_change_pin(g_state, "ABCDEF", 6, "123456", 6, NULL);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_verify(g_state, "ABCDEF", NULL);
ck_assert_int_eq(res, YKPIV_WRONG_PIN);
res = ykpiv_verify(g_state, "123456", NULL);
ck_assert_int_eq(res, YKPIV_OK);
}
END_TEST
START_TEST(test_change_puk) {
ykpiv_rc res;
res = ykpiv_unblock_pin(g_state, "12345678", 8, "123456", 6, NULL);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_change_puk(g_state, "12345678", 8, "ABCDEFGH", 8, NULL);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_unblock_pin(g_state, "12345678", 8, "123456", 6, NULL);
ck_assert_int_eq(res, YKPIV_WRONG_PIN);
res = ykpiv_unblock_pin(g_state, "ABCDEFGH", 8, "123456", 6, NULL);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_change_puk(g_state, "ABCDEFGH", 8, "12345678", 8, NULL);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_unblock_pin(g_state, "ABCDEFGH", 8, "123456", 6, NULL);
ck_assert_int_eq(res, YKPIV_WRONG_PIN);
res = ykpiv_unblock_pin(g_state, "12345678", 8, "123456", 6, NULL);
ck_assert_int_eq(res, YKPIV_OK);
}
END_TEST
static int block_and_reset() {
ykpiv_rc res;
int tries = 100;
int tries_until_blocked;
tries_until_blocked = 0;
while (tries) {
res = ykpiv_verify(g_state, "AAAAAA", &tries);
if (res == YKPIV_PIN_LOCKED)
break;
ck_assert_int_eq(res, YKPIV_WRONG_PIN);
tries_until_blocked++;
}
// Verify no PIN retries remaining
tries = 100;
res = ykpiv_get_pin_retries(g_state, &tries);
ck_assert_int_eq(res, YKPIV_OK);
ck_assert_int_eq(tries, 0);
tries = 100;
while (tries) {
res = ykpiv_change_puk(g_state, "AAAAAAAA", 8, "AAAAAAAA", 8, &tries);
if (res == YKPIV_PIN_LOCKED)
break;
ck_assert_int_eq(res, YKPIV_WRONG_PIN);
}
res = ykpiv_util_reset(g_state);
ck_assert_int_eq(res, YKPIV_OK);
return tries_until_blocked;
}
START_TEST(test_reset) {
ykpiv_rc res;
int tries = 100;
int tries_until_blocked;
// Block and reset, with default PIN retries
tries_until_blocked = block_and_reset();
ck_assert_int_eq(tries_until_blocked, 3);
// Authenticate and increase PIN retries
test_authenticate(0);
res = ykpiv_verify(g_state, "123456", NULL);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_set_pin_retries(g_state, 8, 3);
ck_assert_int_eq(res, YKPIV_OK);
// Block and reset again, verifying increased PIN retries
tries_until_blocked = block_and_reset();
ck_assert_int_eq(tries_until_blocked, 8);
// Note: defaults back to 3 retries after reset
// Verify default (3) PIN retries remaining
tries = 0;
res = ykpiv_get_pin_retries(g_state, &tries);
ck_assert_int_eq(res, YKPIV_OK);
ck_assert_int_eq(tries, 3);
// Verify still (3) PIN retries remaining
tries = 0;
res = ykpiv_get_pin_retries(g_state, &tries);
ck_assert_int_eq(res, YKPIV_OK);
ck_assert_int_eq(tries, 3);
// Try wrong PIN
res = ykpiv_verify(g_state, "AAAAAA", &tries);
ck_assert_int_eq(res, YKPIV_WRONG_PIN);
// Verify 2 PIN retries remaining
tries = 0;
res = ykpiv_get_pin_retries(g_state, &tries);
ck_assert_int_eq(res, YKPIV_OK);
ck_assert_int_eq(tries, 2);
// Verify correct PIN
tries = 100;
res = ykpiv_verify(g_state, "123456", &tries);
ck_assert_int_eq(res, YKPIV_OK);
// Verify back to 3 PIN retries remaining
tries = 0;
res = ykpiv_get_pin_retries(g_state, &tries);
ck_assert_int_eq(res, YKPIV_OK);
ck_assert_int_eq(tries, 3);
}
END_TEST
struct t_alloc_data{
uint32_t count;
} g_alloc_data;
static void* _test_alloc(void *data, size_t cb) {
ck_assert_ptr_eq(data, &g_alloc_data);
((struct t_alloc_data*)data)->count++;
return calloc(cb, 1);
}
static void * _test_realloc(void *data, void *p, size_t cb) {
ck_assert_ptr_eq(data, &g_alloc_data);
return realloc(p, cb);
}
static void _test_free(void *data, void *p) {
fflush(stderr);
ck_assert_ptr_eq(data, &g_alloc_data);
((struct t_alloc_data*)data)->count--;
free(p);
}
ykpiv_allocator test_allocator_cbs = {
.pfn_alloc = _test_alloc,
.pfn_realloc = _test_realloc,
.pfn_free = _test_free,
.alloc_data = &g_alloc_data
};
uint8_t *alloc_auth_cert() {
ykpiv_rc res;
uint8_t *read_cert = NULL;
size_t read_cert_len = 0;
res = ykpiv_util_write_cert(g_state, YKPIV_KEY_AUTHENTICATION, (uint8_t*)g_cert, sizeof(g_cert), YKPIV_CERTINFO_UNCOMPRESSED);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_util_read_cert(g_state, YKPIV_KEY_AUTHENTICATION, &read_cert, &read_cert_len);
ck_assert_int_eq(res, YKPIV_OK);
ck_assert_ptr_nonnull(read_cert);
ck_assert_int_eq(read_cert_len, sizeof(g_cert));
ck_assert_mem_eq(g_cert, read_cert, sizeof(g_cert));
return read_cert;
}
START_TEST(test_allocator) {
ykpiv_rc res;
const ykpiv_allocator allocator;
uint8_t *cert1, *cert2;
res = ykpiv_done(g_state);
ck_assert_int_eq(res, YKPIV_OK);
g_state = NULL;
res = ykpiv_init_with_allocator(&g_state, false, &test_allocator_cbs);
ck_assert_int_eq(res, YKPIV_OK);
ck_assert_ptr_nonnull(g_state);
// Verify we can communicate with device and make some allocations
res = ykpiv_connect(g_state, NULL);
ck_assert_int_eq(res, YKPIV_OK);
test_authenticate(0);
cert1 = alloc_auth_cert();
cert2 = alloc_auth_cert();
// Verify allocations went through custom allocator, and still live
ck_assert_int_gt(g_alloc_data.count, 1);
// Free and shutdown everything
ykpiv_util_free(g_state, cert2);
ykpiv_util_free(g_state, cert1);
res = ykpiv_disconnect(g_state);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_done(g_state);
ck_assert_int_eq(res, YKPIV_OK);
// Verify equal number of frees as allocations
ck_assert_int_eq(g_alloc_data.count, 0);
// Clear g_state so teardown() is skipped
g_state = NULL;
}
END_TEST
START_TEST(test_pin_cache) {
ykpiv_rc res;
ykpiv_state *local_state;
unsigned char data[256];
int len = sizeof(data);
size_t len2 = sizeof(data);
import_key(0x9a, YKPIV_PINPOLICY_DEFAULT);
// Disconnect and reconnect to device to guarantee it is not authed
res = ykpiv_disconnect(g_state);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_done(g_state);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_init(&g_state, true);
ck_assert_int_eq(res, YKPIV_OK);
ck_assert_ptr_nonnull(g_state);
res = ykpiv_connect(g_state, NULL);
ck_assert_int_eq(res, YKPIV_OK);
// Verify decryption does not work without auth
res = ykpiv_decipher_data(g_state, data, (size_t)len, data, &len2, YKPIV_ALGO_RSA2048, 0x9a);
ck_assert_int_eq(res, YKPIV_AUTHENTICATION_ERROR);
// Verify decryption does work when authed
res = ykpiv_verify_select(g_state, "123456", 6, NULL, true);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_decipher_data(g_state, data, (size_t)len, data, &len2, YKPIV_ALGO_RSA2048, 0x9a);
ck_assert_int_eq(res, YKPIV_OK);
// Verify PIN policy allows continuing to decrypt without re-verifying
res = ykpiv_decipher_data(g_state, data, (size_t)len, data, &len2, YKPIV_ALGO_RSA2048, 0x9a);
ck_assert_int_eq(res, YKPIV_OK);
// Create a new ykpiv state, connect, and close it.
// This forces a card reset from another context, so the original global
// context will require a reconnect for its next transaction.
res = ykpiv_init(&local_state, true);
ck_assert_int_eq(res, YKPIV_OK);
ck_assert_ptr_nonnull(local_state);
res = ykpiv_connect(local_state, NULL);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_disconnect(local_state);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_done(local_state);
ck_assert_int_eq(res, YKPIV_OK);
// Verify we are still authenticated on the global context. This will
// require an automatic reconnect and re-verify with the cached PIN.
//
// Note that you can verify that this fails by rebuilding with
// DISABLE_PIN_CACHE set to 1.
res = ykpiv_decipher_data(g_state, data, (size_t)len, data, &len2, YKPIV_ALGO_RSA2048, 0x9a);
ck_assert_int_eq(res, YKPIV_OK);
}
END_TEST
int destruction_confirmed(void) {
char *confirmed = getenv("YKPIV_ENV_HWTESTS_CONFIRMED");
if (confirmed && confirmed[0] == '1')
return 1;
// Use dprintf() to write directly to stdout, since automake eats the standard stdout/stderr pointers.
dprintf(0, "\n***\n*** Hardware tests skipped. Run \"make hwcheck\".\n***\n\n");
return 0;
}
Suite *test_suite(void) {
Suite *s;
TCase *tc;
s = suite_create("libykpiv api");
tc = tcase_create("api");
#ifdef HW_TESTS
tcase_add_unchecked_fixture(tc, setup, teardown);
// Must be first: Reset device. Tests run serially, and depend on a clean slate.
tcase_add_test(tc, test_reset);
// Authenticate after reset.
tcase_add_test(tc, test_authenticate);
// Test API functionality
tcase_add_test(tc, test_change_pin);
tcase_add_test(tc, test_change_puk);
tcase_add_test(tc, test_devicemodel);
tcase_add_test(tc, test_get_set_cardid);
tcase_add_test(tc, test_list_readers);
tcase_add_test(tc, test_read_write_list_delete_cert);
tcase_add_test(tc, test_import_key);
tcase_add_test(tc, test_pin_policy_always);
tcase_add_test(tc, test_generate_key);
tcase_add_test(tc, test_pin_cache);
// Must be last: tear down and re-test with custom memory allocator
tcase_add_test(tc, test_allocator);
#endif
suite_add_tcase(s, tc);
return s;
}
int main(void)
{
int number_failed;
Suite *s;
SRunner *sr;
s = test_suite();
sr = srunner_create(s);
srunner_set_fork_status(sr, CK_NOFORK);
srunner_run_all(sr, CK_VERBOSE);
number_failed = srunner_ntests_failed(sr);
srunner_free(sr);
return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}
+57 -34
View File
@@ -35,41 +35,64 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <check.h>
START_TEST(test_version_string) {
if (strcmp(YKPIV_VERSION_STRING, ykpiv_check_version(NULL)) != 0) {
ck_abort_msg("version mismatch %s != %s\n", YKPIV_VERSION_STRING,
ykpiv_check_version(NULL));
}
if (ykpiv_check_version(YKPIV_VERSION_STRING) == NULL) {
ck_abort_msg("version NULL?\n");
}
if (ykpiv_check_version("99.99.99") != NULL) {
ck_abort_msg("version not NULL?\n");
}
fprintf(stderr, "ykpiv version: header %s library %s\n",
YKPIV_VERSION_STRING, ykpiv_check_version (NULL));
}
END_TEST
START_TEST(test_strerror) {
const char *s;
if (ykpiv_strerror(YKPIV_OK) == NULL) {
ck_abort_msg("ykpiv_strerror NULL\n");
}
s = ykpiv_strerror_name(YKPIV_OK);
if (s == NULL || strcmp(s, "YKPIV_OK") != 0) {
ck_abort_msg("ykpiv_strerror_name %s\n", s);
}
}
END_TEST
Suite *basic_suite(void) {
Suite *s;
TCase *tc;
s = suite_create("libykpiv basic");
tc = tcase_create("basic");
tcase_add_test(tc, test_version_string);
tcase_add_test(tc, test_strerror);
suite_add_tcase(s, tc);
return s;
}
int main(void) int main(void)
{ {
if(strcmp(YKPIV_VERSION_STRING, ykpiv_check_version (NULL)) != 0) { int number_failed;
printf("version mismatch %s != %s\n", YKPIV_VERSION_STRING, Suite *s;
ykpiv_check_version(NULL)); SRunner *sr;
return EXIT_FAILURE;
}
if(ykpiv_check_version(YKPIV_VERSION_STRING) == NULL) { s = basic_suite();
printf("version NULL?\n"); sr = srunner_create(s);
return EXIT_FAILURE; srunner_run_all(sr, CK_NORMAL);
} number_failed = srunner_ntests_failed(sr);
srunner_free(sr);
if(ykpiv_check_version("99.99.99") != NULL) { return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
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;
} }
+38 -23
View File
@@ -32,6 +32,8 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <check.h>
#include "ykpiv.h" #include "ykpiv.h"
struct key { struct key {
@@ -57,30 +59,43 @@ static int parse_key(const char *text, const unsigned char *expected, int valid)
unsigned char key[24]; unsigned char key[24];
size_t len = sizeof(key); size_t len = sizeof(key);
ykpiv_rc res = ykpiv_hex_decode(text, strlen(text), key, &len); ykpiv_rc res = ykpiv_hex_decode(text, strlen(text), key, &len);
if(res != YKPIV_OK && valid == 1) { if (valid) {
printf("key check failed for %s!\n", text); ck_assert(res == YKPIV_OK);
return EXIT_FAILURE; ck_assert(memcmp(expected, key, 24) == 0);
} else if(res != YKPIV_OK && valid == 0) { } else {
return EXIT_SUCCESS; ck_assert(res != YKPIV_OK);
} }
if(memcmp(expected, key, 24) != 0) {
printf("keys not matching for %s!\n", text);
return EXIT_FAILURE;
}
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
int main(void) { START_TEST(test_parse_key) {
size_t i; int res = parse_key(keys[_i].text, keys[_i].formatted, keys[_i].valid);
ck_assert(res == EXIT_SUCCESS);
for(i = 0; i < sizeof(keys) / sizeof(struct key); i++) { }
int res = parse_key(keys[i].text, keys[i].formatted, keys[i].valid); END_TEST
if(res != EXIT_SUCCESS) {
return res; Suite *parsekey_suite(void) {
} Suite *s;
} TCase *tc;
return EXIT_SUCCESS; s = suite_create("libykpiv parsekey");
tc = tcase_create("parsekey");
tcase_add_loop_test(tc, test_parse_key, 0, sizeof(keys) / sizeof(struct key));
suite_add_tcase(s, tc);
return s;
}
int main(void)
{
int number_failed;
Suite *s;
SRunner *sr;
s = parsekey_suite();
sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
number_failed = srunner_ntests_failed(sr);
srunner_free(sr);
return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
} }
+1622
View File
File diff suppressed because it is too large Load Diff
+707 -184
View File
File diff suppressed because it is too large Load Diff
+463 -5
View File
@@ -28,11 +28,20 @@
* *
*/ */
/**
* @mainpage
*
* See ykpiv.h
*
* @file ykpiv.h
* libykpiv API
*/
#ifndef YKPIV_H #ifndef YKPIV_H
#define YKPIV_H #define YKPIV_H
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <stdbool.h>
#include <ykpiv-version.h> #include <ykpiv-version.h>
@@ -58,12 +67,27 @@ extern "C"
YKPIV_INVALID_OBJECT = -11, YKPIV_INVALID_OBJECT = -11,
YKPIV_ALGORITHM_ERROR = -12, YKPIV_ALGORITHM_ERROR = -12,
YKPIV_PIN_LOCKED = -13, YKPIV_PIN_LOCKED = -13,
YKPIV_ARGUMENT_ERROR = -14, //i.e. invalid input argument
YKPIV_RANGE_ERROR = -15, //i.e. value range error
YKPIV_NOT_SUPPORTED = -16
} ykpiv_rc; } ykpiv_rc;
typedef void* (*ykpiv_pfn_alloc)(void* alloc_data, size_t size);
typedef void* (*ykpiv_pfn_realloc)(void* alloc_data, void* address, size_t size);
typedef void (*ykpiv_pfn_free)(void* alloc_data, void* address);
typedef struct ykpiv_allocator {
ykpiv_pfn_alloc pfn_alloc;
ykpiv_pfn_realloc pfn_realloc;
ykpiv_pfn_free pfn_free;
void * alloc_data;
} ykpiv_allocator;
const char *ykpiv_strerror(ykpiv_rc err); const char *ykpiv_strerror(ykpiv_rc err);
const char *ykpiv_strerror_name(ykpiv_rc err); const char *ykpiv_strerror_name(ykpiv_rc err);
ykpiv_rc ykpiv_init(ykpiv_state **state, int verbose); ykpiv_rc ykpiv_init(ykpiv_state **state, int verbose);
ykpiv_rc ykpiv_init_with_allocator(ykpiv_state **state, int verbose, const ykpiv_allocator *allocator);
ykpiv_rc ykpiv_done(ykpiv_state *state); ykpiv_rc ykpiv_done(ykpiv_state *state);
ykpiv_rc ykpiv_connect(ykpiv_state *state, const char *wanted); ykpiv_rc ykpiv_connect(ykpiv_state *state, const char *wanted);
ykpiv_rc ykpiv_list_readers(ykpiv_state *state, char *readers, size_t *len); ykpiv_rc ykpiv_list_readers(ykpiv_state *state, char *readers, size_t *len);
@@ -78,9 +102,6 @@ extern "C"
ykpiv_rc ykpiv_sign_data(ykpiv_state *state, const unsigned char *sign_in, 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, size_t in_len, unsigned char *sign_out, size_t *out_len,
unsigned char algorithm, unsigned char key); unsigned char algorithm, unsigned char key);
ykpiv_rc ykpiv_sign_data2(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, int padding); // Allow not to add padding
ykpiv_rc ykpiv_decipher_data(ykpiv_state *state, const unsigned char *enc_in, ykpiv_rc ykpiv_decipher_data(ykpiv_state *state, const unsigned char *enc_in,
size_t in_len, unsigned char *enc_out, size_t *out_len, size_t in_len, unsigned char *enc_out, size_t *out_len,
unsigned char algorithm, unsigned char key); unsigned char algorithm, unsigned char key);
@@ -98,7 +119,7 @@ extern "C"
ykpiv_rc ykpiv_fetch_object(ykpiv_state *state, int object_id, ykpiv_rc ykpiv_fetch_object(ykpiv_state *state, int object_id,
unsigned char *data, unsigned long *len); unsigned char *data, unsigned long *len);
ykpiv_rc ykpiv_set_mgmkey2(ykpiv_state *state, const unsigned char *new_key, ykpiv_rc ykpiv_set_mgmkey2(ykpiv_state *state, const unsigned char *new_key,
const unsigned char touch); const unsigned char touch);
ykpiv_rc ykpiv_save_object(ykpiv_state *state, int object_id, ykpiv_rc ykpiv_save_object(ykpiv_state *state, int object_id,
unsigned char *indata, size_t len); unsigned char *indata, size_t len);
ykpiv_rc ykpiv_import_private_key(ykpiv_state *state, const unsigned char key, unsigned char algorithm, ykpiv_rc ykpiv_import_private_key(ykpiv_state *state, const unsigned char key, unsigned char algorithm,
@@ -109,6 +130,423 @@ extern "C"
const unsigned char *qinv, size_t qinv_len, const unsigned char *qinv, size_t qinv_len,
const unsigned char *ec_data, unsigned char ec_data_len, const unsigned char *ec_data, unsigned char ec_data_len,
const unsigned char pin_policy, const unsigned char touch_policy); const unsigned char pin_policy, const unsigned char touch_policy);
ykpiv_rc ykpiv_attest(ykpiv_state *state, const unsigned char key, unsigned char *data, size_t *data_len);
/**
* Return the number of PIN attempts remaining before PIN is locked.
*
* **NOTE:** If PIN is already verified, calling ykpiv_get_pin_retries() will unverify the PIN.
*
* @param state State handle from ykpiv_init()
* @param tries [out] Number of attempts remaining
*
* @return Error code
*/
ykpiv_rc ykpiv_get_pin_retries(ykpiv_state *state, int *tries);
/**
* Set number of attempts before locking for PIN and PUK codes.
*
* **NOTE:** If either \p pin_tries or \p puk_tries is 0, ykpiv_set_pin_retries() immediately returns YKPIV_OK.
*
* @param state State handle from ykpiv_init()
* @param pin_tries Number of attempts to permit for PIN code
* @param puk_tries Number of attempts to permit for PUK code
*
* @return Error code
*/
ykpiv_rc ykpiv_set_pin_retries(ykpiv_state *state, int pin_tries, int puk_tries);
/**
* Variant of ykpiv_connect() that accepts a card context obtained externally.
*
* Not for generic use. Use ykpiv_connect() instead.
*
* @param state State handle
* @param context Card context returned from SCardConnect() or equivalent.
* @param card Card ID returned from SCardConnect() or equivalent.
*
* @return Error code
*/
ykpiv_rc ykpiv_connect_with_external_card(ykpiv_state *state, uintptr_t context, uintptr_t card);
/**
* Variant of ykpiv_done() for external cards connected with ykpiv_connect_with_external_card()
*
* Card is not disconnected, unlike with normal calls to ykpiv_done().
*
* @param state State handle
*
* @return Error code
*/
ykpiv_rc ykpiv_done_with_external_card(ykpiv_state *state);
/**
* Variant of ykpiv_verify() that optionally selects the PIV applet first.
*
* @param state State handle
* @param pin PIN code to verify with
* @param pin_len Length of \p pin
* @param tries [out] Number of attempts remaining (if non-NULL)
* @param force_select Whether to select the PIV applet before verifying.
*
* @return Error code
*/
ykpiv_rc ykpiv_verify_select(ykpiv_state *state, const char *pin, const size_t pin_len, int *tries, bool force_select);
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////
////
//// High-level Util API
////
////
//// Util api always allocates data on your behalf, if data = 0, *data != 0,
//// or data_len = 0 an invalid parameter will be returned; to free data, call
//// ykpiv_util_free().
////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
typedef uint32_t ykpiv_devmodel;
/**
* Card identifier
*/
#define YKPIV_CARDID_SIZE 16
typedef struct {
uint8_t data[YKPIV_CARDID_SIZE];
} ykpiv_cardid;
/**
* Card Capability
*/
#define YKPIV_CCCID_SIZE 14
typedef struct {
uint8_t data[YKPIV_CCCID_SIZE];
} ykpiv_cccid;
#pragma pack(push, 1)
typedef struct _ykpiv_key {
uint8_t slot;
uint16_t cert_len;
uint8_t cert[1];
} ykpiv_key;
typedef struct _ykpiv_container {
wchar_t name[40];
uint8_t slot;
uint8_t key_spec;
uint16_t key_size_bits;
uint8_t flags;
uint8_t pin_id;
uint8_t associated_echd_container;
uint8_t cert_fingerprint[20];
} ykpiv_container;
#pragma pack(pop)
typedef enum {
YKPIV_CONFIG_MGM_MANUAL = 0,
YKPIV_CONFIG_MGM_DERIVED = 1,
YKPIV_CONFIG_MGM_PROTECTED = 2
} ykpiv_config_mgm_type;
#pragma pack(push, 1)
typedef struct _ykpiv_config {
uint8_t protected_data_available;
uint8_t puk_blocked;
uint8_t puk_noblock_on_upgrade;
uint32_t pin_last_changed;
ykpiv_config_mgm_type mgm_type;
} ykpiv_config;
typedef struct _ykpiv_mgm {
uint8_t data[24];
} ykpiv_mgm;
#pragma pack(pop)
/**
* Free allocated data
*
* Frees a buffer previously allocated by one of the other \p ykpiv_util functions.
*
* @param state State handle
* @param data Buffer previously allocated by a \p ykpiv_util function
*
* @return ypiv_rc error code
*/
ykpiv_rc ykpiv_util_free(ykpiv_state *state, void *data);
/**
* Returns a list of all saved certificates.
*
* \p data should be freed with \p ykpiv_util_free() after use.
*
* @param state State handle
* @param key_count [out] Number of certificates returned
* @param data [out] Set to a dynamically allocated list of certificates.
* @param data_len [out] Set to size of \p data in bytes
*
* @return Error code
*/
ykpiv_rc ykpiv_util_list_keys(ykpiv_state *state, uint8_t *key_count, ykpiv_key **data, size_t *data_len);
/**
* Read a certificate stored in the given slot
*
* \p data should be freed with \p ykpiv_util_free() after use.
*
* @param state State handle
* @param slot Slot to read from
* @param data Pointer to buffer to store the read data
* @param data_len Pointer to size of input buffer, in bytes. Update to length of read data after call.
*
* @return Error code
*/
ykpiv_rc ykpiv_util_read_cert(ykpiv_state *state, uint8_t slot, uint8_t **data, size_t *data_len);
/**
* Write a certificate to a given slot
*
* \p certinfo should be \p YKPIV_CERTINFO_UNCOMPRESSED for uncompressed certificates, which is the most
* common case, or \p YKPIV_CERTINFO_GZIP if the certificate in \p data is already compressed with gzip.
*
* @param state State handle
* @param slot Slot to write to
* @param data Buffer of data to write
* @param data_len Number of bytes to write
* @param certinfo Hint about type of certificate. Use the \p YKPIV_CERTINFO* defines.
*
* @return Error code
*/
ykpiv_rc ykpiv_util_write_cert(ykpiv_state *state, uint8_t slot, uint8_t *data, size_t data_len, uint8_t certinfo);
/**
* Delete the certificate stored in the given slot
*
* @param state State handle
* @param slot Slot to delete certificate from
*
* @return Error code
*/
ykpiv_rc ykpiv_util_delete_cert(ykpiv_state *state, uint8_t slot);
/**
* Generate key in given slot with specified parameters
*
* \p modulus, \p exp, and \p point should be freed with \p ykpiv_util_free() after use.
*
* If algorithm is RSA1024 or RSA2048, the modulus, modulus_len, exp, and exp_len output parameters must be supplied. They are filled with with public modulus (big-endian), its size, the public exponent (big-endian), and its size respectively.
*
* If algorithm is ECCP256 or ECCP384, the point and point_len output parameters must be supplied. They are filled with the public point (uncompressed octet-string encoded per SEC1 section 2.3.4)
*
* If algorithm is ECCP256, the curve is always ANSI X9.62 Prime 256v1
*
* If algorithm is ECCP384, the curve is always secp384r1
*
* @param state State handle
* @param slot Slot to generate key in
* @param algorithm Key algorithm, specified as one of the \p YKPIV_ALGO_* options
* @param pin_policy Per-slot PIN policy, specified as one of the \p YKPIV_PINPOLICY_* options
* @param touch_policy Per-slot touch policy, specified as one of the \p YKPIV_TOUCHPOLICY_* options.
* @param modulus [out] RSA public modulus (RSA-only)
* @param modulus_len [out] Size of \p modulus (RSA-only)
* @param exp [out] RSA public exponent (RSA-only)
* @param exp_len [out] Size of \p exp (RSA-only)
* @param point [out] Public curve point (ECC-only)
* @param point_len [out] Size of \p point (ECC-only)
*
* @return ykpiv_rc error code
*/
ykpiv_rc ykpiv_util_generate_key(ykpiv_state *state, uint8_t slot, uint8_t algorithm, uint8_t pin_policy, uint8_t touch_policy, uint8_t **modulus, size_t *modulus_len, uint8_t **exp, size_t *exp_len, uint8_t **point, size_t *point_len);
/**
* Get current PIV applet administration configuration state
*
* @param state State handle
* @param config [out] ykpiv_config struct filled with current applet data
*
* @return ykpiv_rc error code
*/
ykpiv_rc ykpiv_util_get_config(ykpiv_state *state, ykpiv_config *config);
/**
* Set last pin changed time to current time
*
* The applet must be authenticated to call this function
*
* @param state State handle
*
* @return ykpiv_rc error code
*/
ykpiv_rc ykpiv_util_set_pin_last_changed(ykpiv_state *state);
/**
* Get Derived MGM key
*
* @param state State handle
* @param pin PIN used to derive mgm key
* @param pin_len Length of pin in bytes
* @param mgm [out] Protected MGM key
*
* @return ykpiv_rc error code
*/
ykpiv_rc ykpiv_util_get_derived_mgm(ykpiv_state *state, const uint8_t *pin, const size_t pin_len, ykpiv_mgm *mgm);
/**
* Get Protected MGM key
*
* The user pin must be verified to call this function
*
* @param state State handle
* @param mgm [out] Protected MGM key
*
* @return ykpiv_rc error code
*/
ykpiv_rc ykpiv_util_get_protected_mgm(ykpiv_state *state, ykpiv_mgm *mgm);
/**
* Set Protected MGM key
*
* The applet must be authenticated and the user pin verified to call this function
*
* If \p mgm is NULL or \p mgm.data is all zeroes, generate MGM, otherwise set specified key.
*
* @param state State handle
* @param mgm [in, out] Input: NULL or new MGM key. Output: Generated MGM key
*
* @return ykpiv_rc error code
*/
ykpiv_rc ykpiv_util_set_protected_mgm(ykpiv_state *state, ykpiv_mgm *mgm);
/**
* Reset PIV applet
*
* The user PIN and PUK must be blocked to call this function.
*
* @param state State handle
*
* @return ykpiv_rc error code
*/
ykpiv_rc ykpiv_util_reset(ykpiv_state *state);
/**
* Get card identifier
*
* Gets the card identifier from the Cardholder Unique Identifier (CHUID).
*
* ID can be set with \p ykpiv_util_set_cardid().
*
* @param state State handle
* @param cardid [out] Unique Card ID stored in the CHUID
*
* @return ykpiv_rc error code
*/
ykpiv_rc ykpiv_util_get_cardid(ykpiv_state *state, ykpiv_cardid *cardid);
/**
* Set card identifier
*
* Set the card identifier in the Cardholder Unique Identifier (CHUID).
*
* The card must be authenticated to call this function.
*
* See also: \p ykpiv_util_set_cccid()
*
* @param state State handle
* @param cardid Unique Card ID to set. If NULL, randomly generate.
*
* @return ypiv_rc error code
*
*/
ykpiv_rc ykpiv_util_set_cardid(ykpiv_state *state, const ykpiv_cardid *cardid);
/**
* Get card capabilities identifier
*
* Gets the card identifier from the Card Capability Container (CCC).
*
* ID can be set with \p ykpiv_util_set_cccid().
*
* @param state State handle
* @param ccc [out] Unique Card ID stored in the CCC
*
* @return ykpiv_rc error code
*/
ykpiv_rc ykpiv_util_get_cccid(ykpiv_state *state, ykpiv_cccid *ccc);
/**
* Set card capabilities identifier
*
* Sets the card identifier in the Card Capability Container (CCC).
*
* The card must be authenticated to call this function.
*
* See also: \p ykpiv_util_set_cardid()
*
* @param state state
* @param ccc Unique Card ID to set. If NULL, randomly generate.
*
* @return ypiv_rc error code
*
*/
ykpiv_rc ykpiv_util_set_cccid(ykpiv_state *state, const ykpiv_cccid *ccc);
/**
* Get device model
*
* The card must be connected to call this function.
*
* @param state State handle
*
* @return Device model
*
*/
ykpiv_devmodel ykpiv_util_devicemodel(ykpiv_state *state);
/**
* Block PUK
*
* Utility function to block the PUK.
*
* To set the PUK blocked flag in the admin data, the applet must be authenticated.
*
* @param state State handle
*
* @return Error code
*
*/
ykpiv_rc ykpiv_util_block_puk(ykpiv_state *state);
/**
* Object ID of given slot.
*
* @param slot Key slot
*/
uint32_t ykpiv_util_slot_object(uint8_t slot);
ykpiv_rc ykpiv_util_read_mscmap(ykpiv_state *state, ykpiv_container **containers, size_t *n_containers);
ykpiv_rc ykpiv_util_write_mscmap(ykpiv_state *state, ykpiv_container *containers, size_t n_containers);
ykpiv_rc ykpiv_util_read_msroots(ykpiv_state *state, uint8_t **data, size_t *data_len);
ykpiv_rc ykpiv_util_write_msroots(ykpiv_state *state, uint8_t *data, size_t data_len);
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////
////
//// Defines
////
////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
#define YKPIV_ALGO_TAG 0x80 #define YKPIV_ALGO_TAG 0x80
#define YKPIV_ALGO_3DES 0x03 #define YKPIV_ALGO_3DES 0x03
@@ -181,6 +619,8 @@ extern "C"
#define YKPIV_OBJ_ATTESTATION 0x5fff01 #define YKPIV_OBJ_ATTESTATION 0x5fff01
#define YKPIV_OBJ_MAX_SIZE 3072
#define YKPIV_INS_VERIFY 0x20 #define YKPIV_INS_VERIFY 0x20
#define YKPIV_INS_CHANGE_REFERENCE 0x24 #define YKPIV_INS_CHANGE_REFERENCE 0x24
#define YKPIV_INS_RESET_RETRY 0x2c #define YKPIV_INS_RESET_RETRY 0x2c
@@ -188,6 +628,8 @@ extern "C"
#define YKPIV_INS_AUTHENTICATE 0x87 #define YKPIV_INS_AUTHENTICATE 0x87
#define YKPIV_INS_GET_DATA 0xcb #define YKPIV_INS_GET_DATA 0xcb
#define YKPIV_INS_PUT_DATA 0xdb #define YKPIV_INS_PUT_DATA 0xdb
#define YKPIV_INS_SELECT_APPLICATION 0xa4
#define YKPIV_INS_GET_RESPONSE_APDU 0xc0
/* sw is status words, see NIST special publication 800-73-4, section 5.6 */ /* sw is status words, see NIST special publication 800-73-4, section 5.6 */
#define SW_SUCCESS 0x9000 #define SW_SUCCESS 0x9000
@@ -196,8 +638,9 @@ extern "C"
#define SW_ERR_INCORRECT_PARAM 0x6a80 #define SW_ERR_INCORRECT_PARAM 0x6a80
/* this is a custom sw for yubikey */ /* this is a custom sw for yubikey */
#define SW_ERR_INCORRECT_SLOT 0x6b00 #define SW_ERR_INCORRECT_SLOT 0x6b00
#define SW_ERR_NOT_SUPPORTED 0x6d00
/* Yubico vendor specific instructions */ /* Yubico vendor specific instructions */
#define YKPIV_INS_SET_MGMKEY 0xff #define YKPIV_INS_SET_MGMKEY 0xff
#define YKPIV_INS_IMPORT_KEY 0xfe #define YKPIV_INS_IMPORT_KEY 0xfe
#define YKPIV_INS_GET_VERSION 0xfd #define YKPIV_INS_GET_VERSION 0xfd
@@ -220,6 +663,21 @@ extern "C"
#define YKPIV_IS_EC(a) ((a == YKPIV_ALGO_ECCP256 || a == YKPIV_ALGO_ECCP384)) #define YKPIV_IS_EC(a) ((a == YKPIV_ALGO_ECCP256 || a == YKPIV_ALGO_ECCP384))
#define YKPIV_IS_RSA(a) ((a == YKPIV_ALGO_RSA1024 || a == YKPIV_ALGO_RSA2048)) #define YKPIV_IS_RSA(a) ((a == YKPIV_ALGO_RSA1024 || a == YKPIV_ALGO_RSA2048))
#define YKPIV_RETRIES_DEFAULT 3
#define YKPIV_RETRIES_MAX 0xff
#define YKPIV_CERTINFO_UNCOMPRESSED 0
#define YKPIV_CERTINFO_GZIP 1
#define YKPIV_ATR_NEO_R3 "\x3b\xfc\x13\x00\x00\x81\x31\xfe\x15\x59\x75\x62\x69\x6b\x65\x79\x4e\x45\x4f\x72\x33\xe1"
#define YKPIV_ATR_YK4 "\x3b\xf8\x13\x00\x00\x81\x31\xfe\x15\x59\x75\x62\x69\x6b\x65\x79\x34\xd4"
#define DEVTYPE_UNKNOWN 0x00000000
#define DEVTYPE_NEO 0x4E450000 //"NE"
#define DEVTYPE_YK 0x594B0000 //"YK"
#define DEVTYPE_NEOr3 (DEVTYPE_NEO | 0x00007233) //"r3"
#define DEVTYPE_YK4 (DEVTYPE_YK | 0x00000034) // "4"
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
+38
View File
@@ -75,3 +75,41 @@ global:
ykpiv_change_puk; ykpiv_change_puk;
ykpiv_unblock_pin; ykpiv_unblock_pin;
} YKPIV_1.2.0; } YKPIV_1.2.0;
YKPIV_1.5.0
{
global:
ykpiv_attest;
ykpiv_auth_getchallenge;
ykpiv_auth_verifyresponse;
ykpiv_connect_with_external_card;
ykpiv_done_with_external_card;
ykpiv_get_pin_retries;
ykpiv_init_with_allocator;
ykpiv_set_pin_retries;
ykpiv_util_block_puk;
ykpiv_util_delete_cert;
ykpiv_util_devicemodel;
ykpiv_util_free;
ykpiv_util_generate_key;
ykpiv_util_get_cardid;
ykpiv_util_get_cccid;
ykpiv_util_get_config;
ykpiv_util_get_derived_mgm;
ykpiv_util_get_protected_mgm;
ykpiv_util_list_keys;
ykpiv_util_list_keys;
ykpiv_util_read_cert;
ykpiv_util_read_mscmap;
ykpiv_util_read_msroots;
ykpiv_util_reset;
ykpiv_util_set_cardid;
ykpiv_util_set_cccid;
ykpiv_util_set_pin_last_changed;
ykpiv_util_set_protected_mgm;
ykpiv_util_slot_object;
ykpiv_util_write_cert;
ykpiv_util_write_mscmap;
ykpiv_util_write_msroots;
ykpiv_verify_select;
} YKPIV_1.3.0;
+312
View File
@@ -0,0 +1,312 @@
# This file is part of Autoconf. -*- Autoconf -*-
# Copyright (C) 2004 Oren Ben-Kiki
# This file is distributed under the same terms as the Autoconf macro files.
# Generate automatic documentation using Doxygen. Works in concert with the
# aminclude.m4 file and a compatible doxygen configuration file. Defines the
# following public macros:
#
# DX_???_FEATURE(ON|OFF) - control the default setting fo a Doxygen feature.
# Supported features are 'DOXYGEN' itself, 'DOT' for generating graphics,
# 'HTML' for plain HTML, 'CHM' for compressed HTML help (for MS users), 'CHI'
# for generating a seperate .chi file by the .chm file, and 'MAN', 'RTF',
# 'XML', 'PDF' and 'PS' for the appropriate output formats. The environment
# variable DOXYGEN_PAPER_SIZE may be specified to override the default 'a4wide'
# paper size.
#
# By default, HTML, PDF and PS documentation is generated as this seems to be
# the most popular and portable combination. MAN pages created by Doxygen are
# usually problematic, though by picking an appropriate subset and doing some
# massaging they might be better than nothing. CHM and RTF are specific for MS
# (note that you can't generate both HTML and CHM at the same time). The XML is
# rather useless unless you apply specialized post-processing to it.
#
# The macro mainly controls the default state of the feature. The use can
# override the default by specifying --enable or --disable. The macros ensure
# that contradictory flags are not given (e.g., --enable-doxygen-html and
# --enable-doxygen-chm, --enable-doxygen-anything with --disable-doxygen, etc.)
# Finally, each feature will be automatically disabled (with a warning) if the
# required programs are missing.
#
# Once all the feature defaults have been specified, call DX_INIT_DOXYGEN with
# the following parameters: a one-word name for the project for use as a
# filename base etc., an optional configuration file name (the default is
# 'Doxyfile', the same as Doxygen's default), and an optional output directory
# name (the default is 'doxygen-doc').
## ----------##
## Defaults. ##
## ----------##
DX_ENV=""
AC_DEFUN([DX_FEATURE_doc], ON)
AC_DEFUN([DX_FEATURE_dot], ON)
AC_DEFUN([DX_FEATURE_man], OFF)
AC_DEFUN([DX_FEATURE_html], ON)
AC_DEFUN([DX_FEATURE_chm], OFF)
AC_DEFUN([DX_FEATURE_chi], OFF)
AC_DEFUN([DX_FEATURE_rtf], OFF)
AC_DEFUN([DX_FEATURE_xml], OFF)
AC_DEFUN([DX_FEATURE_pdf], ON)
AC_DEFUN([DX_FEATURE_ps], ON)
## --------------- ##
## Private macros. ##
## --------------- ##
# DX_ENV_APPEND(VARIABLE, VALUE)
# ------------------------------
# Append VARIABLE="VALUE" to DX_ENV for invoking doxygen.
AC_DEFUN([DX_ENV_APPEND], [AC_SUBST([DX_ENV], ["$DX_ENV $1='$2'"])])
# DX_DIRNAME_EXPR
# ---------------
# Expand into a shell expression prints the directory part of a path.
AC_DEFUN([DX_DIRNAME_EXPR],
[[expr ".$1" : '\(\.\)[^/]*$' \| "x$1" : 'x\(.*\)/[^/]*$']])
# DX_IF_FEATURE(FEATURE, IF-ON, IF-OFF)
# -------------------------------------
# Expands according to the M4 (static) status of the feature.
AC_DEFUN([DX_IF_FEATURE], [ifelse(DX_FEATURE_$1, ON, [$2], [$3])])
# DX_REQUIRE_PROG(VARIABLE, PROGRAM)
# ----------------------------------
# Require the specified program to be found for the DX_CURRENT_FEATURE to work.
AC_DEFUN([DX_REQUIRE_PROG], [
AC_PATH_TOOL([$1], [$2])
if test "$DX_FLAG_[]DX_CURRENT_FEATURE$$1" = 1; then
AC_MSG_WARN([$2 not found - will not DX_CURRENT_DESCRIPTION])
AC_SUBST([DX_FLAG_]DX_CURRENT_FEATURE, 0)
fi
])
# DX_TEST_FEATURE(FEATURE)
# ------------------------
# Expand to a shell expression testing whether the feature is active.
AC_DEFUN([DX_TEST_FEATURE], [test "$DX_FLAG_$1" = 1])
# DX_CHECK_DEPEND(REQUIRED_FEATURE, REQUIRED_STATE)
# -------------------------------------------------
# Verify that a required features has the right state before trying to turn on
# the DX_CURRENT_FEATURE.
AC_DEFUN([DX_CHECK_DEPEND], [
test "$DX_FLAG_$1" = "$2" \
|| AC_MSG_ERROR([doxygen-DX_CURRENT_FEATURE ifelse([$2], 1,
requires, contradicts) doxygen-DX_CURRENT_FEATURE])
])
# DX_CLEAR_DEPEND(FEATURE, REQUIRED_FEATURE, REQUIRED_STATE)
# ----------------------------------------------------------
# Turn off the DX_CURRENT_FEATURE if the required feature is off.
AC_DEFUN([DX_CLEAR_DEPEND], [
test "$DX_FLAG_$1" = "$2" || AC_SUBST([DX_FLAG_]DX_CURRENT_FEATURE, 0)
])
# DX_FEATURE_ARG(FEATURE, DESCRIPTION,
# CHECK_DEPEND, CLEAR_DEPEND,
# REQUIRE, DO-IF-ON, DO-IF-OFF)
# --------------------------------------------
# Parse the command-line option controlling a feature. CHECK_DEPEND is called
# if the user explicitly turns the feature on (and invokes DX_CHECK_DEPEND),
# otherwise CLEAR_DEPEND is called to turn off the default state if a required
# feature is disabled (using DX_CLEAR_DEPEND). REQUIRE performs additional
# requirement tests (DX_REQUIRE_PROG). Finally, an automake flag is set and
# DO-IF-ON or DO-IF-OFF are called according to the final state of the feature.
AC_DEFUN([DX_ARG_ABLE], [
AC_DEFUN([DX_CURRENT_FEATURE], [$1])
AC_DEFUN([DX_CURRENT_DESCRIPTION], [$2])
AC_ARG_ENABLE(doxygen-$1,
[AS_HELP_STRING(DX_IF_FEATURE([$1], [--disable-doxygen-$1],
[--enable-doxygen-$1]),
DX_IF_FEATURE([$1], [don't $2], [$2]))],
[
case "$enableval" in
#(
y|Y|yes|Yes|YES)
AC_SUBST([DX_FLAG_$1], 1)
$3
;; #(
n|N|no|No|NO)
AC_SUBST([DX_FLAG_$1], 0)
;; #(
*)
AC_MSG_ERROR([invalid value '$enableval' given to doxygen-$1])
;;
esac
], [
AC_SUBST([DX_FLAG_$1], [DX_IF_FEATURE([$1], 1, 0)])
$4
])
if DX_TEST_FEATURE([$1]); then
$5
:
fi
if DX_TEST_FEATURE([$1]); then
AM_CONDITIONAL(DX_COND_$1, :)
$6
:
else
AM_CONDITIONAL(DX_COND_$1, false)
$7
:
fi
])
## -------------- ##
## Public macros. ##
## -------------- ##
# DX_XXX_FEATURE(DEFAULT_STATE)
# -----------------------------
AC_DEFUN([DX_DOXYGEN_FEATURE], [AC_DEFUN([DX_FEATURE_doc], [$1])])
AC_DEFUN([DX_MAN_FEATURE], [AC_DEFUN([DX_FEATURE_man], [$1])])
AC_DEFUN([DX_HTML_FEATURE], [AC_DEFUN([DX_FEATURE_html], [$1])])
AC_DEFUN([DX_CHM_FEATURE], [AC_DEFUN([DX_FEATURE_chm], [$1])])
AC_DEFUN([DX_CHI_FEATURE], [AC_DEFUN([DX_FEATURE_chi], [$1])])
AC_DEFUN([DX_RTF_FEATURE], [AC_DEFUN([DX_FEATURE_rtf], [$1])])
AC_DEFUN([DX_XML_FEATURE], [AC_DEFUN([DX_FEATURE_xml], [$1])])
AC_DEFUN([DX_XML_FEATURE], [AC_DEFUN([DX_FEATURE_xml], [$1])])
AC_DEFUN([DX_PDF_FEATURE], [AC_DEFUN([DX_FEATURE_pdf], [$1])])
AC_DEFUN([DX_PS_FEATURE], [AC_DEFUN([DX_FEATURE_ps], [$1])])
# DX_INIT_DOXYGEN(PROJECT, [CONFIG-FILE], [OUTPUT-DOC-DIR])
# ---------------------------------------------------------
# PROJECT also serves as the base name for the documentation files.
# The default CONFIG-FILE is "Doxyfile" and OUTPUT-DOC-DIR is "doxygen-doc".
AC_DEFUN([DX_INIT_DOXYGEN], [
# Files:
AC_SUBST([DX_PROJECT], [$1])
AC_SUBST([DX_CONFIG], [ifelse([$2], [], Doxyfile, [$2])])
AC_SUBST([DX_DOCDIR], [ifelse([$3], [], doxygen-doc, [$3])])
# Environment variables used inside doxygen.cfg:
DX_ENV_APPEND(SRCDIR, $srcdir)
DX_ENV_APPEND(PROJECT, $DX_PROJECT)
DX_ENV_APPEND(DOCDIR, $DX_DOCDIR)
DX_ENV_APPEND(VERSION, $PACKAGE_VERSION)
# Doxygen itself:
DX_ARG_ABLE(doc, [generate any doxygen documentation],
[],
[],
[DX_REQUIRE_PROG([DX_DOXYGEN], doxygen)
DX_REQUIRE_PROG([DX_PERL], perl)],
[DX_ENV_APPEND(PERL_PATH, $DX_PERL)])
# Dot for graphics:
DX_ARG_ABLE(dot, [generate graphics for doxygen documentation],
[DX_CHECK_DEPEND(doc, 1)],
[DX_CLEAR_DEPEND(doc, 1)],
[DX_REQUIRE_PROG([DX_DOT], dot)],
[DX_ENV_APPEND(HAVE_DOT, YES)
DX_ENV_APPEND(DOT_PATH, [`DX_DIRNAME_EXPR($DX_DOT)`])],
[DX_ENV_APPEND(HAVE_DOT, NO)])
# Man pages generation:
DX_ARG_ABLE(man, [generate doxygen manual pages],
[DX_CHECK_DEPEND(doc, 1)],
[DX_CLEAR_DEPEND(doc, 1)],
[],
[DX_ENV_APPEND(GENERATE_MAN, YES)],
[DX_ENV_APPEND(GENERATE_MAN, NO)])
# RTF file generation:
DX_ARG_ABLE(rtf, [generate doxygen RTF documentation],
[DX_CHECK_DEPEND(doc, 1)],
[DX_CLEAR_DEPEND(doc, 1)],
[],
[DX_ENV_APPEND(GENERATE_RTF, YES)],
[DX_ENV_APPEND(GENERATE_RTF, NO)])
# XML file generation:
DX_ARG_ABLE(xml, [generate doxygen XML documentation],
[DX_CHECK_DEPEND(doc, 1)],
[DX_CLEAR_DEPEND(doc, 1)],
[],
[DX_ENV_APPEND(GENERATE_XML, YES)],
[DX_ENV_APPEND(GENERATE_XML, NO)])
# (Compressed) HTML help generation:
DX_ARG_ABLE(chm, [generate doxygen compressed HTML help documentation],
[DX_CHECK_DEPEND(doc, 1)],
[DX_CLEAR_DEPEND(doc, 1)],
[DX_REQUIRE_PROG([DX_HHC], hhc)],
[DX_ENV_APPEND(HHC_PATH, $DX_HHC)
DX_ENV_APPEND(GENERATE_HTML, YES)
DX_ENV_APPEND(GENERATE_HTMLHELP, YES)],
[DX_ENV_APPEND(GENERATE_HTMLHELP, NO)])
# Seperate CHI file generation.
DX_ARG_ABLE(chi, [generate doxygen seperate compressed HTML help index file],
[DX_CHECK_DEPEND(chm, 1)],
[DX_CLEAR_DEPEND(chm, 1)],
[],
[DX_ENV_APPEND(GENERATE_CHI, YES)],
[DX_ENV_APPEND(GENERATE_CHI, NO)])
# Plain HTML pages generation:
DX_ARG_ABLE(html, [generate doxygen plain HTML documentation],
[DX_CHECK_DEPEND(doc, 1) DX_CHECK_DEPEND(chm, 0)],
[DX_CLEAR_DEPEND(doc, 1) DX_CLEAR_DEPEND(chm, 0)],
[],
[DX_ENV_APPEND(GENERATE_HTML, YES)],
[DX_TEST_FEATURE(chm) || DX_ENV_APPEND(GENERATE_HTML, NO)])
# PostScript file generation:
DX_ARG_ABLE(ps, [generate doxygen PostScript documentation],
[DX_CHECK_DEPEND(doc, 1)],
[DX_CLEAR_DEPEND(doc, 1)],
[DX_REQUIRE_PROG([DX_LATEX], latex)
DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex)
DX_REQUIRE_PROG([DX_DVIPS], dvips)
DX_REQUIRE_PROG([DX_EGREP], egrep)])
# PDF file generation:
DX_ARG_ABLE(pdf, [generate doxygen PDF documentation],
[DX_CHECK_DEPEND(doc, 1)],
[DX_CLEAR_DEPEND(doc, 1)],
[DX_REQUIRE_PROG([DX_PDFLATEX], pdflatex)
DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex)
DX_REQUIRE_PROG([DX_EGREP], egrep)])
# LaTeX generation for PS and/or PDF:
if DX_TEST_FEATURE(ps) || DX_TEST_FEATURE(pdf); then
AM_CONDITIONAL(DX_COND_latex, :)
DX_ENV_APPEND(GENERATE_LATEX, YES)
else
AM_CONDITIONAL(DX_COND_latex, false)
DX_ENV_APPEND(GENERATE_LATEX, NO)
fi
# Paper size for PS and/or PDF:
AC_ARG_VAR(DOXYGEN_PAPER_SIZE,
[a4wide (default), a4, letter, legal or executive])
case "$DOXYGEN_PAPER_SIZE" in
#(
"")
AC_SUBST(DOXYGEN_PAPER_SIZE, "")
;; #(
a4wide|a4|letter|legal|executive)
DX_ENV_APPEND(PAPER_SIZE, $DOXYGEN_PAPER_SIZE)
;; #(
*)
AC_MSG_ERROR([unknown DOXYGEN_PAPER_SIZE='$DOXYGEN_PAPER_SIZE'])
;;
esac
#For debugging:
#echo DX_FLAG_doc=$DX_FLAG_doc
#echo DX_FLAG_dot=$DX_FLAG_dot
#echo DX_FLAG_man=$DX_FLAG_man
#echo DX_FLAG_html=$DX_FLAG_html
#echo DX_FLAG_chm=$DX_FLAG_chm
#echo DX_FLAG_chi=$DX_FLAG_chi
#echo DX_FLAG_rtf=$DX_FLAG_rtf
#echo DX_FLAG_xml=$DX_FLAG_xml
#echo DX_FLAG_pdf=$DX_FLAG_pdf
#echo DX_FLAG_ps=$DX_FLAG_ps
#echo DX_ENV=$DX_ENV
])
+8 -2
View File
@@ -29,12 +29,18 @@ TESTS_ENVIRONMENT = export VERSION=$(PACKAGE_VERSION); export EXEEXT=$(EXEEXT);
LOG_COMPILER = $(VALGRIND) LOG_COMPILER = $(VALGRIND)
AM_CFLAGS = $(WARN_CFLAGS) AM_CFLAGS = $(WARN_CFLAGS) @CHECK_CFLAGS@
AM_CPPFLAGS = -I$(top_srcdir)/lib -I$(top_builddir)/lib AM_CPPFLAGS = -I$(top_srcdir)/lib -I$(top_builddir)/lib
AM_CPPFLAGS += -I$(top_srcdir)/tool -I$(top_builddir)/tool AM_CPPFLAGS += -I$(top_srcdir)/tool -I$(top_builddir)/tool
AM_CPPFLAGS += $(OPENSSL_CFLAGS) AM_CPPFLAGS += $(OPENSSL_CFLAGS)
AM_LDFLAGS = -no-install AM_LDFLAGS = @CHECK_LIBS@
if COMPILER_CLANG
AM_LDFLAGS += -no-fast-install
else
AM_LDFLAGS += -no-install
endif
parse_name_LDADD = ../libpiv_util.la $(OPENSSL_LIBS) parse_name_LDADD = ../libpiv_util.la $(OPENSSL_LIBS)
test_inout_LDADD = ../libpiv_util.la test_inout_LDADD = ../libpiv_util.la
+95 -1
View File
@@ -1,4 +1,4 @@
#!/bin/sh #!/bin/bash
# Copyright (c) 2014-2016 Yubico AB # Copyright (c) 2014-2016 Yubico AB
# All rights reserved. # All rights reserved.
@@ -33,6 +33,7 @@
set -e set -e
BIN="../yubico-piv-tool${EXEEXT}" BIN="../yubico-piv-tool${EXEEXT}"
ROOT_MAKEFILE="../../Makefile"
HELP_OUTPUT=$($BIN --help) HELP_OUTPUT=$($BIN --help)
@@ -42,3 +43,96 @@ if [ "x$VERSION_OUTPUT" != "x$expected" ]; then
echo "Version ($VERSION_OUTPUT) not matching expected output $expected." echo "Version ($VERSION_OUTPUT) not matching expected output $expected."
exit 1 exit 1
fi fi
################################################################################
################################################################################
# HARDWARE TESTS
################################################################################
################################################################################
#
# Tests below here require a Yubikey to be connected.
# These tests are destructive.
#
################################################################################
################################################################################
# Verify that --enable-hardware-tests was a build flag.
! $(set -e && cat "$ROOT_MAKEFILE" |grep "^DEFS =" | grep -- "-DHW_TESTS" >/dev/null)
HW_TESTS=$?
if [[ $HW_TESTS -eq 0 ]]; then
exit 0
fi
# Verify that user has confirmed destructive hw-tests
if [ "x$YKPIV_ENV_HWTESTS_CONFIRMED" != "x1" ]; then
printf "\n***\n*** Hardware tests skipped. Run \"make hwcheck\".\n***\n\n" >&0
exit 77 # exit code 77 == skipped tests
fi
#
# Run basic import/validation tests on included keys/certs. Test keys generated
# with the following commands:
#
# $ openssl genrsa -out private.pem 2048
# $ openssl rsa -in private.pem -outform PEM -pubout -out public.pem
# $ openssl req -x509 -key private.pem -out cert.pem -subj "/CN=YubicoTest/OU=YubicoTestUnit/O=yubico.com/" -new
#
# Reset
$BIN -averify-pin -P000000 || true
$BIN -averify-pin -P000000 || true
$BIN -averify-pin -P000000 || true
$BIN -averify-pin -P000000 || true
$BIN -averify-pin -P000000 || true
$BIN -achange-puk -P000000 -N00000000 || true
$BIN -achange-puk -P000000 -N00000000 || true
$BIN -achange-puk -P000000 -N00000000 || true
$BIN -achange-puk -P000000 -N00000000 || true
$BIN -achange-puk -P000000 -N00000000 || true
$BIN -areset
# Generate key on-board, issue certificate, and verify it
$BIN -agenerate -s9a -AECCP256 -o key_9a.pub
$BIN -averify -P123456 -s9a -S'/CN=YubicoTest/OU=YubicoGenerated/O=yubico.com/' -aselfsign -i key_9a.pub -o cert_9a.pem
$BIN -averify -P123456 -s9a -atest-signature -i cert_9a.pem
$BIN -aimport-certificate -P123456 -s9a -i cert_9a.pem
# Import key, generate self-signed certificate, and verify it
$BIN -aimport-key -P123456 -s9e -iprivate.pem
$BIN -arequest-certificate -s9e -S"/CN=bar/OU=test/O=example.com/" -i public.pem -o req_9e.pem
$BIN -averify -P123456 -s9e -S'/CN=bar/OU=test/O=example.com/' -aselfsign -i public.pem -o cert_9e.pem
$BIN -atest-decipher -s9e -i cert_9e.pem
$BIN -aimport-certificate -P123456 -s9e -i cert.pem
# Read status and validate fields
STATUS=$($BIN -astatus)
echo "$STATUS"
ALGO_9A=$(echo "$STATUS" |grep "Slot 9a" -A 6 |grep "Algorithm" |tr -d "[:blank:]")
if [[ "x$ALGO_9A" != "xAlgorithm:ECCP256" ]]; then
echo "$ALGO_9A"
echo "Generated algorithm incorrect." >/dev/stderr
exit 1
fi
ALGO_9E=$(echo "$STATUS" |grep "Slot 9e" -A 6 |grep "Algorithm" |tr -d "[:blank:]")
if [[ "x$ALGO_9E" != "xAlgorithm:RSA2048" ]]; then
echo "$ALGO_9E"
echo "Generated algorithm incorrect." >/dev/stderr
exit 1
fi
SUBJECT_9A=$(echo "$STATUS" |grep "Slot 9a" -A 6 |grep "Subject DN" |tr -d "[:blank:]")
if [[ "x$SUBJECT_9A" != "xSubjectDN:CN=YubicoTest,OU=YubicoGenerated,O=yubico.com" ]]; then
echo "$SUBJECT_9A"
echo "Certificate subject incorrect." >/dev/stderr
exit 1
fi
SUBJECT_9E=$(echo "$STATUS" |grep "Slot 9e" -A 6 |grep "Subject DN" |tr -d "[:blank:]")
if [[ "x$SUBJECT_9E" != "xSubjectDN:CN=YubicoTest,OU=YubicoTestUnit,O=yubico.com" ]]; then
echo "$SUBJECT_9E"
echo "Certificate subject incorrect." >/dev/stderr
exit 1
fi
+22
View File
@@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIJAM1blQIJfeuCMA0GCSqGSIb3DQEBBQUAMEMxEzARBgNV
BAMTCll1Ymljb1Rlc3QxFzAVBgNVBAsTDll1Ymljb1Rlc3RVbml0MRMwEQYDVQQK
Ewp5dWJpY28uY29tMB4XDTE3MDkyNzEzMzYxNFoXDTE3MTAyNzEzMzYxNFowQzET
MBEGA1UEAxMKWXViaWNvVGVzdDEXMBUGA1UECxMOWXViaWNvVGVzdFVuaXQxEzAR
BgNVBAoTCnl1Ymljby5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
AQDKRTcyy2rVuV6cex5GdRT3VWntbqVPC4p6BcATEAeSXDgORaWhQOnv7WF9a8Rc
bsWH0j807f+CvqirtFZHbSBsMYlxyko4Mer/gxb2uqy08B4fWeOxeTpNNQ63gfHZ
g4tqnDmFulAiZilVnuiimAdC0iLliXaowkFEcPFGPEmjV/lWTgalgFOe/utySbVs
vUp2EjeBDcOdUqc1oHH1GVoMRKMm7PTT9/8SVawVUneCqOFCNNxyW7PjRpTr5qPq
1ucMOmz2UIPwJVHIQEbL5IL3NtkozmbZ0G9cqnRrYSXGmlBrdcq5fH7qrMcifIMW
QXVinhzbIcrRpW0Vm653efxDAgMBAAGjgaUwgaIwHQYDVR0OBBYEFKEF2ASXCBPt
+4Wh0o36Ee6+HvRRMHMGA1UdIwRsMGqAFKEF2ASXCBPt+4Wh0o36Ee6+HvRRoUek
RTBDMRMwEQYDVQQDEwpZdWJpY29UZXN0MRcwFQYDVQQLEw5ZdWJpY29UZXN0VW5p
dDETMBEGA1UEChMKeXViaWNvLmNvbYIJAM1blQIJfeuCMAwGA1UdEwQFMAMBAf8w
DQYJKoZIhvcNAQEFBQADggEBADaeHhj7vjZ8OGIAOd86UAqJrqyQ6Lhu133pBRoV
4qQprZFRXxsxVyAqKDAWMF/GTidMRlVRAQNnR9kHYuG7zpy+NjlK2khAEflAa6Z5
nGMntv0+y7NLkKGAAk9qxqpNwj90VzFcvopDFA70FVnWtgkuJuFf5n+fHTUMOzTk
p6+BMRUjJqu7weK+QUI8b9zl7pSzWbcHqxyrJSNRW87xvEQhyJzFqbqQprYGheZk
py5wUX22HBhAuw7cvakeUMIX133UJI7Yxwy5DKoiqsESKGr/oXIU3+M0kqzDwQCA
HI1y9cIY/3Zi3Y7HgQnHX2Oos3k2SY0VpYtO47Ja/oIkolc=
-----END CERTIFICATE-----
+49 -17
View File
@@ -28,6 +28,8 @@
* *
*/ */
#include <check.h>
#include <string.h> #include <string.h>
#include <stdbool.h> #include <stdbool.h>
@@ -39,18 +41,26 @@
#include "util.h" #include "util.h"
static void test_name(const char *name, const char *expected, bool fail) { struct name {
const char *name;
const char *parsed_name;
bool valid;
} names[] = {
{"/CN=test foo/", "CN = test foo", true},
{"/CN=test/OU=bar/O=EXAMPLE/", "CN = test, OU = bar, O = EXAMPLE", true},
{"/CN=test/OU=bar/O=EXAMPLE/", "CN = test, OU = wrong, O = EXAMPLE", false},
{"/foo/", "", false},
{"/CN=test/foobar/", "", false},
{"/CN=test/foo=bar/", "", false},
};
static bool test_name(const char *name, const char *expected) {
char buf[1024]; char buf[1024];
BIO *bio; BIO *bio;
const char none[] = {0}; const char none[] = {0};
X509_NAME *parsed = parse_name(name); X509_NAME *parsed = parse_name(name);
if(parsed == NULL) { if(parsed == NULL) {
if(fail) { return false;
return;
} else {
printf("Failed parsing of '%s'!\n", name);
exit(EXIT_FAILURE);
}
} }
bio = BIO_new(BIO_s_mem()); bio = BIO_new(BIO_s_mem());
@@ -61,17 +71,39 @@ static void test_name(const char *name, const char *expected, bool fail) {
BIO_free(bio); BIO_free(bio);
X509_NAME_free(parsed); X509_NAME_free(parsed);
if(strcmp(buf, expected) != 0) { if(strcmp(buf, expected) != 0) {
printf("Names not matching: '%s' != '%s'\n", expected, buf); fprintf(stderr, "Names not matching: '%s' != '%s'\n", expected, buf);
exit(EXIT_FAILURE); return false;
} }
return true;
} }
int main(void) { START_TEST(test_parse_name) {
test_name("/CN=test foo/", "CN = test foo", false); ck_assert(test_name(names[_i].name, names[_i].parsed_name) == names[_i].valid);
test_name("/CN=test/OU=bar/O=EXAMPLE/", "CN = test, OU = bar, O = EXAMPLE", false); }
test_name("/foo/", "", true); END_TEST
test_name("/CN=test/foobar/", "", true);
test_name("/CN=test/foo=bar/", "", true); Suite *test_suite(void) {
Suite *s;
return EXIT_SUCCESS; TCase *tc;
s = suite_create("yubico-piv-tool parse_name");
tc = tcase_create("parse_name");
tcase_add_loop_test(tc, test_parse_name, 0, sizeof(names) / sizeof(struct name));
suite_add_tcase(s, tc);
return s;
}
int main(void)
{
int number_failed;
Suite *s;
SRunner *sr;
s = test_suite();
sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
number_failed = srunner_ntests_failed(sr);
srunner_free(sr);
return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
} }
+27
View File
@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAykU3Mstq1blenHseRnUU91Vp7W6lTwuKegXAExAHklw4DkWl
oUDp7+1hfWvEXG7Fh9I/NO3/gr6oq7RWR20gbDGJccpKODHq/4MW9rqstPAeH1nj
sXk6TTUOt4Hx2YOLapw5hbpQImYpVZ7oopgHQtIi5Yl2qMJBRHDxRjxJo1f5Vk4G
pYBTnv7rckm1bL1KdhI3gQ3DnVKnNaBx9RlaDESjJuz00/f/ElWsFVJ3gqjhQjTc
cluz40aU6+aj6tbnDDps9lCD8CVRyEBGy+SC9zbZKM5m2dBvXKp0a2ElxppQa3XK
uXx+6qzHInyDFkF1Yp4c2yHK0aVtFZuud3n8QwIDAQABAoIBAE1Q9c+Bt/2oFMUl
vqXZ/UCpsorif2felnkcF5ZxyyMkAv1Zm/0ujf17NIe3mOBoKzNGp4h47PEyJdE0
ZsJ4sSsKKGqJk6M1WYl/t1hqdLfZDPqY5pMhLqryfASjNCobwT/oJYi7dgQgHu6u
hmgYSrY9Er/Ass3BKyeZMHDTfKZlvM8GZ/oF8bhkD1P/fi6xU1bhs1XCTQpkkING
eESbD5ZGMZU4HYusdmOmf2Y4LXqVZkag86Fw7XAg6b80FDR8Af6S9fzoPA7Aapmg
uvH19BHeSH/DiLTQ6d31GijSsx+rW/F5mrs5wldGO/htTJwRx0YoccUMPF2mMx+d
ShOlzckCgYEA9p9rT7kkj1ZglNB1gwo+IdbEZydlK3NvHmXMFa3IkmBg1nK1dpnM
0DK0Ycyb7LIuBN3sc2QV5+D3Yv8LspeTMBVajddts/dIJKQb51hRsC3PvQVUnaMD
3YYqDmZUlIv9bKfvAbOuNUOg4FXkaXFkkNNsLxv92bARKHPLo6eE7UcCgYEA0fYU
ImTKv9W8rpPcc7lf8Ffhw7mRrMTA/qFNSdJvjED9UXzH7Dp0abQ9nK8XxTWEl0oe
l0h+5H5YiV6Li8BXcWgnferbpi0Jwkdvh2Qc7LmJ/o278KLPKIqDItRd0gEgC9hR
H1M91Y1pNcv1Mj95hKx3L0ROXhwBAy4ohddjRyUCgYEAp8VFdEOHynbBVxsEhfNm
1xBKJb5YBZoOgohPsIO7SVCFL/1y0s7H1O5ZZZqSjA+eXLM30jvI5yhUQqUsKP8S
IwizxIBD4cSb8Ekvrk6Xq5lOk9DXgjFORNmrLIaSjUc6TDtlzSuVnCh4fYQQ0WZR
OnCJTPbm1rr+wR0c8CTauasCgYEAqie7eYQlrAITv4elCUQaNDWEiZJCNLnfjnw6
nrEkJY4lvXxaqV9WKLQhmnFr2i7dHZ672+6sp5CdP/aXMNLYCthV6P4EtE+bsQ8j
m53OsypKYzmKLiJDsJ9QV1G0FxVCW1cbpz9WxVKtCSQZuncmjBcZH/1DZZFcYK9v
t8gudOECgYEAh3hlXVeMv2/oEW21G7D0s3Cv2vOSvBogdwVRP8EGOkn43rh5GXZm
7NVJjJNwURwLcowb0F4B/RILxArtjW2srUo1nbq5UoCiFNJ/00JoRSzAz1ad7S2o
0nRuVLQ50WYG/HBTP2M4yQbpP/E+5PCNMGx2kgyuTUyeCl+LEE8bg5k=
-----END RSA PRIVATE KEY-----
+9
View File
@@ -0,0 +1,9 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAykU3Mstq1blenHseRnUU
91Vp7W6lTwuKegXAExAHklw4DkWloUDp7+1hfWvEXG7Fh9I/NO3/gr6oq7RWR20g
bDGJccpKODHq/4MW9rqstPAeH1njsXk6TTUOt4Hx2YOLapw5hbpQImYpVZ7oopgH
QtIi5Yl2qMJBRHDxRjxJo1f5Vk4GpYBTnv7rckm1bL1KdhI3gQ3DnVKnNaBx9Rla
DESjJuz00/f/ElWsFVJ3gqjhQjTccluz40aU6+aj6tbnDDps9lCD8CVRyEBGy+SC
9zbZKM5m2dBvXKp0a2ElxppQa3XKuXx+6qzHInyDFkF1Yp4c2yHK0aVtFZuud3n8
QwIDAQAB
-----END PUBLIC KEY-----
+43 -8
View File
@@ -28,6 +28,8 @@
* *
*/ */
#include <check.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@@ -40,25 +42,58 @@
#define pipe(fds) _pipe(fds,4096, 0) #define pipe(fds) _pipe(fds,4096, 0)
#endif #endif
static void test_inout(enum enum_format format) { enum enum_format formats[] = {
format_arg_base64,
format_arg_hex,
format_arg_binary,
};
static bool inout(enum enum_format format) {
const unsigned char buf[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; const unsigned char buf[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
unsigned char buf2[sizeof(buf)]; unsigned char buf2[sizeof(buf)];
int pipefd[2]; int pipefd[2];
FILE *tmp1, *tmp2; FILE *tmp1, *tmp2;
assert(pipe(pipefd) == 0); if (pipe(pipefd) != 0)
return false;
tmp1 = fdopen(pipefd[1], "w"); tmp1 = fdopen(pipefd[1], "w");
dump_data(buf, sizeof(buf), tmp1, false, format); dump_data(buf, sizeof(buf), tmp1, false, format);
fclose(tmp1); fclose(tmp1);
tmp2 = fdopen(pipefd[0], "r"); tmp2 = fdopen(pipefd[0], "r");
read_data(buf2, sizeof(buf2), tmp2, format); read_data(buf2, sizeof(buf2), tmp2, format);
assert(memcmp(buf, buf2, sizeof(buf)) == 0); if (memcmp(buf, buf2, sizeof(buf)) != 0)
return false;
fclose(tmp2); fclose(tmp2);
return true;
} }
int main(void) { START_TEST(test_inout) {
test_inout(format_arg_base64); ck_assert(inout(formats[_i]));
test_inout(format_arg_hex); }
test_inout(format_arg_binary); END_TEST
exit(0);
Suite *test_suite(void) {
Suite *s;
TCase *tc;
s = suite_create("yubico-piv-tool inout");
tc = tcase_create("inout");
tcase_add_loop_test(tc, test_inout, 0, sizeof(formats) / sizeof(*formats));
suite_add_tcase(s, tc);
return s;
}
int main(void)
{
int number_failed;
Suite *s;
SRunner *sr;
s = test_suite();
sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
number_failed = srunner_ntests_failed(sr);
srunner_free(sr);
return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
} }
+34 -136
View File
@@ -46,12 +46,31 @@
#include "cmdline.h" #include "cmdline.h"
#include "util.h" #include "util.h"
FILE *open_file(const char *file_name, int mode) { FILE *open_file(const char *file_name, enum file_mode mode) {
FILE *file; FILE *file;
const char *mod;
if(!strcmp(file_name, "-")) { if(!strcmp(file_name, "-")) {
file = mode == INPUT ? stdin : stdout; file = (mode == INPUT_TEXT || mode == INPUT_BIN) ? stdin : stdout;
} else { } else {
file = fopen(file_name, mode == INPUT ? "r" : "w"); switch (mode) {
case INPUT_TEXT:
mod = "r";
break;
case INPUT_BIN:
mod = "rb";
break;
case OUTPUT_TEXT:
mod = "w";
break;
case OUTPUT_BIN:
mod = "wb";
break;
default:
fprintf(stderr, "Invalid file mode.\n");
return NULL;
break;
}
file = fopen(file_name, mod);
if(!file) { if(!file) {
fprintf(stderr, "Failed opening '%s'!\n", file_name); fprintf(stderr, "Failed opening '%s'!\n", file_name);
return NULL; return NULL;
@@ -255,172 +274,49 @@ int set_length(unsigned char *buffer, int length) {
} }
} }
int get_object_id(enum enum_slot slot) { int get_slot_hex(enum enum_slot slot_enum) {
int object; int slot = -1;
switch(slot) { switch (slot_enum) {
case slot_arg_9a: case slot_arg_9a:
object = YKPIV_OBJ_AUTHENTICATION; slot = 0x9a;
break; break;
case slot_arg_9c: case slot_arg_9c:
object = YKPIV_OBJ_SIGNATURE;
break;
case slot_arg_9d: case slot_arg_9d:
object = YKPIV_OBJ_KEY_MANAGEMENT;
break;
case slot_arg_9e: case slot_arg_9e:
object = YKPIV_OBJ_CARD_AUTH; slot = 0x9c + ((int)slot_enum - (int)slot_arg_9c);
break; break;
case slot_arg_82: case slot_arg_82:
object = YKPIV_OBJ_RETIRED1;
break;
case slot_arg_83: case slot_arg_83:
object = YKPIV_OBJ_RETIRED2;
break;
case slot_arg_84: case slot_arg_84:
object = YKPIV_OBJ_RETIRED3;
break;
case slot_arg_85: case slot_arg_85:
object = YKPIV_OBJ_RETIRED4;
break;
case slot_arg_86: case slot_arg_86:
object = YKPIV_OBJ_RETIRED5;
break;
case slot_arg_87: case slot_arg_87:
object = YKPIV_OBJ_RETIRED6;
break;
case slot_arg_88: case slot_arg_88:
object = YKPIV_OBJ_RETIRED7;
break;
case slot_arg_89: case slot_arg_89:
object = YKPIV_OBJ_RETIRED8;
break;
case slot_arg_8a: case slot_arg_8a:
object = YKPIV_OBJ_RETIRED9;
break;
case slot_arg_8b: case slot_arg_8b:
object = YKPIV_OBJ_RETIRED10;
break;
case slot_arg_8c: case slot_arg_8c:
object = YKPIV_OBJ_RETIRED11;
break;
case slot_arg_8d: case slot_arg_8d:
object = YKPIV_OBJ_RETIRED12;
break;
case slot_arg_8e: case slot_arg_8e:
object = YKPIV_OBJ_RETIRED13;
break;
case slot_arg_8f: case slot_arg_8f:
object = YKPIV_OBJ_RETIRED14;
break;
case slot_arg_90: case slot_arg_90:
object = YKPIV_OBJ_RETIRED15;
break;
case slot_arg_91: case slot_arg_91:
object = YKPIV_OBJ_RETIRED16;
break;
case slot_arg_92: case slot_arg_92:
object = YKPIV_OBJ_RETIRED17;
break;
case slot_arg_93: case slot_arg_93:
object = YKPIV_OBJ_RETIRED18;
break;
case slot_arg_94: case slot_arg_94:
object = YKPIV_OBJ_RETIRED19;
break;
case slot_arg_95: case slot_arg_95:
object = YKPIV_OBJ_RETIRED20; slot = 0x82 + ((int)slot_enum - (int)slot_arg_82);
break; break;
case slot_arg_f9: case slot_arg_f9:
object = YKPIV_OBJ_ATTESTATION; slot = 0xf9;
break; break;
case slot__NULL: case slot__NULL:
default: default:
object = 0; slot = -1;
} }
return object;
}
int key_to_object_id(int key) { return slot;
int object;
switch(key) {
case YKPIV_KEY_AUTHENTICATION:
object = YKPIV_OBJ_AUTHENTICATION;
break;
case YKPIV_KEY_CARDMGM:
object = YKPIV_OBJ_SIGNATURE;
break;
case YKPIV_KEY_KEYMGM:
object = YKPIV_OBJ_KEY_MANAGEMENT;
break;
case YKPIV_KEY_CARDAUTH:
object = YKPIV_OBJ_CARD_AUTH;
break;
case YKPIV_KEY_RETIRED1:
object = YKPIV_OBJ_RETIRED1;
break;
case YKPIV_KEY_RETIRED2:
object = YKPIV_OBJ_RETIRED2;
break;
case YKPIV_KEY_RETIRED3:
object = YKPIV_OBJ_RETIRED3;
break;
case YKPIV_KEY_RETIRED4:
object = YKPIV_OBJ_RETIRED4;
break;
case YKPIV_KEY_RETIRED5:
object = YKPIV_OBJ_RETIRED5;
break;
case YKPIV_KEY_RETIRED6:
object = YKPIV_OBJ_RETIRED6;
break;
case YKPIV_KEY_RETIRED7:
object = YKPIV_OBJ_RETIRED7;
break;
case YKPIV_KEY_RETIRED8:
object = YKPIV_OBJ_RETIRED8;
break;
case YKPIV_KEY_RETIRED9:
object = YKPIV_OBJ_RETIRED9;
break;
case YKPIV_KEY_RETIRED10:
object = YKPIV_OBJ_RETIRED10;
break;
case YKPIV_KEY_RETIRED11:
object = YKPIV_OBJ_RETIRED11;
break;
case YKPIV_KEY_RETIRED12:
object = YKPIV_OBJ_RETIRED12;
break;
case YKPIV_KEY_RETIRED13:
object = YKPIV_OBJ_RETIRED13;
break;
case YKPIV_KEY_RETIRED14:
object = YKPIV_OBJ_RETIRED14;
break;
case YKPIV_KEY_RETIRED15:
object = YKPIV_OBJ_RETIRED15;
break;
case YKPIV_KEY_RETIRED16:
object = YKPIV_OBJ_RETIRED16;
break;
case YKPIV_KEY_RETIRED17:
object = YKPIV_OBJ_RETIRED17;
break;
case YKPIV_KEY_RETIRED18:
object = YKPIV_OBJ_RETIRED18;
break;
case YKPIV_KEY_RETIRED19:
object = YKPIV_OBJ_RETIRED19;
break;
case YKPIV_KEY_RETIRED20:
object = YKPIV_OBJ_RETIRED20;
break;
default:
object = 0;
}
return object;
} }
bool set_component(unsigned char *in_ptr, const BIGNUM *bn, int element_len) { bool set_component(unsigned char *in_ptr, const BIGNUM *bn, int element_len) {
@@ -647,7 +543,9 @@ int SSH_write_X509(FILE *fp, X509 *x) {
rsa = EVP_PKEY_get1_RSA(pkey); rsa = EVP_PKEY_get1_RSA(pkey);
set_component(n, rsa->n, RSA_size(rsa)); if (!set_component(n, rsa->n, RSA_size(rsa))) {
break;
}
uint32_t bytes = BN_num_bytes(rsa->n); uint32_t bytes = BN_num_bytes(rsa->n);
char len_buf[5]; char len_buf[5];
+8 -5
View File
@@ -37,8 +37,12 @@
#include "cmdline.h" #include "cmdline.h"
#define INPUT 1 enum file_mode {
#define OUTPUT 2 INPUT_TEXT,
OUTPUT_TEXT,
INPUT_BIN,
OUTPUT_BIN,
};
size_t read_data(unsigned char*, size_t, FILE*, enum enum_format); size_t read_data(unsigned char*, size_t, FILE*, enum enum_format);
void dump_data(unsigned const char*, unsigned int, FILE*, bool, enum enum_format); void dump_data(unsigned const char*, unsigned int, FILE*, bool, enum enum_format);
@@ -46,9 +50,8 @@ int set_length(unsigned char*, int);
int get_length(const unsigned char*, int*); int get_length(const unsigned char*, int*);
X509_NAME *parse_name(const char*); X509_NAME *parse_name(const char*);
unsigned char get_algorithm(EVP_PKEY*); unsigned char get_algorithm(EVP_PKEY*);
FILE *open_file(const char*, int); FILE *open_file(const char *file_name, enum file_mode mode);
int get_object_id(enum enum_slot slot); int get_slot_hex(enum enum_slot slot_enum);
int key_to_object_id(int key);
bool set_component(unsigned char *in_ptr, const BIGNUM *bn, int element_len); bool set_component(unsigned char *in_ptr, const BIGNUM *bn, int element_len);
bool prepare_rsa_signature(const unsigned char*, unsigned int, unsigned char*, bool prepare_rsa_signature(const unsigned char*, unsigned int, unsigned char*,
unsigned int*, int); unsigned int*, int);
+199 -337
View File
@@ -50,26 +50,8 @@
#include "cmdline.h" #include "cmdline.h"
#include "util.h" #include "util.h"
/* FASC-N containing S9999F9999F999999F0F1F0000000000300001E encoded in #define MAX(a,b) (a) > (b) ? (a) : (b)
* 4-bit BCD with 1 bit parity. run through the tools/fasc.pl script to get #define MIN(a,b) (a) < (b) ? (a) : (b)
* bytes. */
/* this CHUID has an expiry of 2030-01-01, maybe that should be variable.. */
unsigned const char chuid_tmpl[] = {
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 29
unsigned const char ccc_tmpl[] = {
0xf0, 0x15, 0xa0, 0x00, 0x00, 0x01, 0x16, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf1, 0x01, 0x21,
0xf2, 0x01, 0x21, 0xf3, 0x00, 0xf4, 0x01, 0x00, 0xf5, 0x01, 0x10, 0xf6, 0x00,
0xf7, 0x00, 0xfa, 0x00, 0xfb, 0x00, 0xfc, 0x00, 0xfd, 0x00, 0xfe, 0x00
};
#define CCC_ID_OFFS 9
#define CHUID 0 #define CHUID 0
#define CCC 1 #define CCC 1
@@ -78,9 +60,35 @@ unsigned const char ccc_tmpl[] = {
#define KEY_LEN 24 #define KEY_LEN 24
static enum file_mode key_file_mode(enum enum_key_format fmt, bool output) {
if (fmt == key_format_arg_PEM) {
if (output) {
return OUTPUT_TEXT;
}
return INPUT_TEXT;
}
if (output) {
return OUTPUT_BIN;
}
return INPUT_BIN;
}
static enum file_mode data_file_mode(enum enum_format fmt, bool output) {
if (fmt == format_arg_binary) {
if (output) {
return OUTPUT_BIN;
}
return INPUT_BIN;
}
if (output) {
return OUTPUT_TEXT;
}
return INPUT_TEXT;
}
static void print_version(ykpiv_state *state, const char *output_file_name) { static void print_version(ykpiv_state *state, const char *output_file_name) {
char version[7]; char version[7];
FILE *output_file = open_file(output_file_name, OUTPUT); FILE *output_file = open_file(output_file_name, OUTPUT_TEXT);
if(!output_file) { if(!output_file) {
return; return;
} }
@@ -115,167 +123,89 @@ static bool sign_data(ykpiv_state *state, const unsigned char *in, size_t len, u
return false; return false;
} }
static bool generate_key(ykpiv_state *state, const char *slot, static bool generate_key(ykpiv_state *state, enum enum_slot slot,
enum enum_algorithm algorithm, const char *output_file_name, enum enum_algorithm algorithm, const char *output_file_name,
enum enum_key_format key_format, enum enum_pin_policy pin_policy, enum enum_key_format key_format, enum enum_pin_policy pin_policy,
enum enum_touch_policy touch_policy) { enum enum_touch_policy touch_policy) {
unsigned char in_data[11];
unsigned char *in_ptr = in_data;
unsigned char data[1024];
unsigned char templ[] = {0, YKPIV_INS_GENERATE_ASYMMETRIC, 0, 0};
unsigned long recv_len = sizeof(data);
int sw;
int key = 0; int key = 0;
FILE *output_file = NULL;
bool ret = false; bool ret = false;
ykpiv_rc res;
FILE *output_file = NULL;
EVP_PKEY *public_key = NULL; EVP_PKEY *public_key = NULL;
RSA *rsa = NULL; RSA *rsa = NULL;
BIGNUM *bignum_n = NULL;
BIGNUM *bignum_e = NULL;
EC_KEY *eckey = NULL; EC_KEY *eckey = NULL;
EC_POINT *point = NULL; EC_POINT *ecpoint = NULL;
char version[7]; uint8_t *mod = NULL;
uint8_t *exp = NULL;
uint8_t *point = NULL;
size_t mod_len = 0;
size_t exp_len = 0;
size_t point_len = 0;
if(algorithm == algorithm_arg_RSA1024 || algorithm == algorithm_arg_RSA2048) { key = get_slot_hex(slot);
if(ykpiv_get_version(state, version, sizeof(version)) == YKPIV_OK) {
int major, minor, build;
int match = sscanf(version, "%d.%d.%d", &major, &minor, &build);
if(match == 3 && major == 4 && (minor < 3 || (minor == 3 && build < 5))) {
fprintf(stderr, "On-chip RSA key generation on this YubiKey has been blocked.\n");
fprintf(stderr, "Please see https://yubi.co/ysa201701/ for details.\n");
return false;
}
} else {
fprintf(stderr, "Failed to communicate.\n");
return false;
}
}
sscanf(slot, "%2x", &key); output_file = open_file(output_file_name, key_file_mode(key_format, true));
templ[3] = key;
output_file = open_file(output_file_name, OUTPUT);
if(!output_file) { if(!output_file) {
return false; return false;
} }
*in_ptr++ = 0xac; res = ykpiv_util_generate_key(state,
*in_ptr++ = 3; (uint8_t)(key & 0xFF),
*in_ptr++ = YKPIV_ALGO_TAG; get_piv_algorithm(algorithm),
*in_ptr++ = 1; get_pin_policy(pin_policy),
*in_ptr++ = get_piv_algorithm(algorithm); get_touch_policy(touch_policy),
if(in_data[4] == 0) { &mod,
fprintf(stderr, "Unexpected algorithm.\n"); &mod_len,
goto generate_out; &exp,
} &exp_len,
if(pin_policy != pin_policy__NULL) { &point,
in_data[1] += 3; &point_len);
*in_ptr++ = YKPIV_PINPOLICY_TAG; if (res != YKPIV_OK) {
*in_ptr++ = 1; fprintf(stderr, "Key generation failed.\n");
*in_ptr++ = get_pin_policy(pin_policy);
}
if(touch_policy != touch_policy__NULL) {
in_data[1] += 3;
*in_ptr++ = YKPIV_TOUCHPOLICY_TAG;
*in_ptr++ = 1;
*in_ptr++ = get_touch_policy(touch_policy);
}
if(ykpiv_transfer_data(state, templ, in_data, in_ptr - in_data, data,
&recv_len, &sw) != YKPIV_OK) {
fprintf(stderr, "Failed to communicate.\n");
goto generate_out;
} else if(sw != SW_SUCCESS) {
fprintf(stderr, "Failed to generate new key (");
if(sw == SW_ERR_INCORRECT_SLOT) {
fprintf(stderr, "slot not supported?)\n");
} else if(sw == SW_ERR_INCORRECT_PARAM) {
if(pin_policy != pin_policy__NULL) {
fprintf(stderr, "pin policy not supported?)\n");
} else if(touch_policy != touch_policy__NULL) {
fprintf(stderr, "touch policy not supported?)\n");
} else {
fprintf(stderr, "algorithm not supported?)\n");
}
} else {
fprintf(stderr, "error %x)\n", sw);
}
goto generate_out; goto generate_out;
} }
if(key_format == key_format_arg_PEM) { if(key_format == key_format_arg_PEM) {
public_key = EVP_PKEY_new(); public_key = EVP_PKEY_new();
if(algorithm == algorithm_arg_RSA1024 || algorithm == algorithm_arg_RSA2048) { if(algorithm == algorithm_arg_RSA1024 || algorithm == algorithm_arg_RSA2048) {
unsigned char *data_ptr = data + 5;
int len = 0;
rsa = RSA_new(); rsa = RSA_new();
rsa->n = BN_bin2bn(mod, mod_len, NULL);
if(*data_ptr != 0x81) { if (rsa->n == NULL) {
fprintf(stderr, "Failed to parse public key structure.\n");
goto generate_out;
}
data_ptr++;
data_ptr += get_length(data_ptr, &len);
bignum_n = BN_bin2bn(data_ptr, len, NULL);
if(bignum_n == NULL) {
fprintf(stderr, "Failed to parse public key modulus.\n"); fprintf(stderr, "Failed to parse public key modulus.\n");
goto generate_out; goto generate_out;
} }
data_ptr += len; rsa->e = BN_bin2bn(exp, exp_len, NULL);
if(rsa->e == NULL) {
if(*data_ptr != 0x82) {
fprintf(stderr, "Failed to parse public key structure (2).\n");
goto generate_out;
}
data_ptr++;
data_ptr += get_length(data_ptr, &len);
bignum_e = BN_bin2bn(data_ptr, len, NULL);
if(bignum_e == NULL) {
fprintf(stderr, "Failed to parse public key exponent.\n"); fprintf(stderr, "Failed to parse public key exponent.\n");
goto generate_out; goto generate_out;
} }
rsa->n = bignum_n;
rsa->e = bignum_e;
EVP_PKEY_set1_RSA(public_key, rsa); EVP_PKEY_set1_RSA(public_key, rsa);
} else if(algorithm == algorithm_arg_ECCP256 || algorithm == algorithm_arg_ECCP384) { } else if(algorithm == algorithm_arg_ECCP256 || algorithm == algorithm_arg_ECCP384) {
EC_GROUP *group; EC_GROUP *group;
unsigned char *data_ptr = data + 3;
int nid; int nid;
size_t len;
if(algorithm == algorithm_arg_ECCP256) { if(algorithm == algorithm_arg_ECCP256) {
nid = NID_X9_62_prime256v1; nid = NID_X9_62_prime256v1;
len = 65;
} else { } else {
nid = NID_secp384r1; nid = NID_secp384r1;
len = 97;
} }
eckey = EC_KEY_new(); eckey = EC_KEY_new();
group = EC_GROUP_new_by_curve_name(nid); group = EC_GROUP_new_by_curve_name(nid);
EC_GROUP_set_asn1_flag(group, nid); EC_GROUP_set_asn1_flag(group, nid);
EC_KEY_set_group(eckey, group); EC_KEY_set_group(eckey, group);
point = EC_POINT_new(group); ecpoint = EC_POINT_new(group);
if(*data_ptr++ != 0x86) {
fprintf(stderr, "Failed to parse public key structure.\n"); if(!EC_POINT_oct2point(group, ecpoint, point, point_len, NULL)) {
goto generate_out;
}
if(*data_ptr++ != len) { /* the curve point should always be 65 bytes */
fprintf(stderr, "Unexpected length.\n");
goto generate_out;
}
if(!EC_POINT_oct2point(group, point, data_ptr, len, NULL)) {
fprintf(stderr, "Failed to load public point.\n"); fprintf(stderr, "Failed to load public point.\n");
goto generate_out; goto generate_out;
} }
if(!EC_KEY_set_public_key(eckey, point)) { if(!EC_KEY_set_public_key(eckey, ecpoint)) {
fprintf(stderr, "Failed to set the public key.\n"); fprintf(stderr, "Failed to set the public key.\n");
goto generate_out; goto generate_out;
} }
EVP_PKEY_set1_EC_KEY(public_key, eckey); EVP_PKEY_set1_EC_KEY(public_key, eckey);
} else { } else {
fprintf(stderr, "Wrong algorithm.\n"); fprintf(stderr, "Wrong algorithm.\n");
goto generate_out;
} }
PEM_write_PUBKEY(output_file, public_key); PEM_write_PUBKEY(output_file, public_key);
ret = true; ret = true;
@@ -285,65 +215,53 @@ static bool generate_key(ykpiv_state *state, const char *slot,
} }
generate_out: generate_out:
if(output_file != stdout) { if (output_file != stdout) {
fclose(output_file); fclose(output_file);
} }
if(point) { if (ecpoint) {
EC_POINT_free(point); EC_POINT_free(ecpoint);
} }
if(eckey) { if (eckey) {
EC_KEY_free(eckey); EC_KEY_free(eckey);
} }
if(rsa) { if (rsa) {
RSA_free(rsa); RSA_free(rsa);
} }
if(public_key) { if (public_key) {
EVP_PKEY_free(public_key); EVP_PKEY_free(public_key);
} }
if (point) {
ykpiv_util_free(state, point);
}
if (mod) {
ykpiv_util_free(state, mod);
}
if (exp) {
ykpiv_util_free(state, exp);
}
return ret; return ret;
} }
static bool reset(ykpiv_state *state) { static bool reset(ykpiv_state *state) {
unsigned char templ[] = {0, YKPIV_INS_RESET, 0, 0}; return ykpiv_util_reset(state) == YKPIV_OK;
unsigned char data[0xff];
unsigned long recv_len = sizeof(data);
int sw;
/* note: the reset function is only available when both pins are blocked. */
if(ykpiv_transfer_data(state, templ, NULL, 0, data, &recv_len, &sw) != YKPIV_OK) {
return false;
} else if(sw == SW_SUCCESS) {
return true;
}
return false;
} }
static bool set_pin_retries(ykpiv_state *state, int pin_retries, int puk_retries, int verbose) { 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}; ykpiv_rc res;
unsigned char data[0xff];
unsigned long recv_len = sizeof(data);
int sw;
if(pin_retries > 0xff || puk_retries > 0xff || pin_retries < 1 || puk_retries < 1) {
fprintf(stderr, "pin and puk retries must be between 1 and 255.\n");
return false;
}
if(verbose) { if(verbose) {
fprintf(stderr, "Setting pin retries to %d and puk retries to %d.\n", pin_retries, puk_retries); fprintf(stderr, "Setting pin retries to %d and puk retries to %d.\n", pin_retries, puk_retries);
} }
res = ykpiv_set_pin_retries(state, pin_retries, puk_retries);
if(ykpiv_transfer_data(state, templ, NULL, 0, data, &recv_len, &sw) != YKPIV_OK) { if (res == YKPIV_RANGE_ERROR) {
return false; fprintf(stderr, "pin and puk retries must be between 1 and 255.\n");
} else if(sw == SW_SUCCESS) {
return true;
} }
return false; return res == YKPIV_OK;
} }
static bool import_key(ykpiv_state *state, enum enum_key_format key_format, static bool import_key(ykpiv_state *state, enum enum_key_format key_format,
const char *input_file_name, const char *slot, char *password, const char *input_file_name, enum enum_slot slot, char *password,
enum enum_pin_policy pin_policy, enum enum_touch_policy touch_policy) { enum enum_pin_policy pin_policy, enum enum_touch_policy touch_policy) {
int key = 0; int key = 0;
FILE *input_file = NULL; FILE *input_file = NULL;
@@ -353,9 +271,9 @@ static bool import_key(ykpiv_state *state, enum enum_key_format key_format,
bool ret = false; bool ret = false;
ykpiv_rc rc = YKPIV_GENERIC_ERROR; ykpiv_rc rc = YKPIV_GENERIC_ERROR;
sscanf(slot, "%2x", &key); key = get_slot_hex(slot);
input_file = open_file(input_file_name, INPUT); input_file = open_file(input_file_name, key_file_mode(key_format, false));
if(!input_file) { if(!input_file) {
return false; return false;
} }
@@ -518,7 +436,7 @@ static bool import_cert(ykpiv_state *state, enum enum_key_format cert_format,
int compress = 0; int compress = 0;
int cert_len = -1; int cert_len = -1;
input_file = open_file(input_file_name, INPUT); input_file = open_file(input_file_name, key_file_mode(cert_format, false));
if(!input_file) { if(!input_file) {
return false; return false;
} }
@@ -568,35 +486,18 @@ static bool import_cert(ykpiv_state *state, enum enum_key_format cert_format,
} }
{ {
unsigned char certdata[3072]; unsigned char certdata[YKPIV_OBJ_MAX_SIZE];
unsigned char *certptr = certdata; unsigned char *certptr = certdata;
int object = get_object_id(slot);
ykpiv_rc res; ykpiv_rc res;
if(4 + cert_len + 5 > sizeof(certdata)) { /* 4 is prefix size, 5 is postfix size */
fprintf(stderr, "Certificate is too large to fit in buffer.\n");
goto import_cert_out;
}
*certptr++ = 0x70;
certptr += set_length(certptr, cert_len);
if (compress) { if (compress) {
if (fread(certptr, 1, (size_t)cert_len, input_file) != (size_t)cert_len) { if (fread(certdata, 1, (size_t)cert_len, input_file) != (size_t)cert_len) {
fprintf(stderr, "Failed to read compressed certificate\n"); fprintf(stderr, "Failed to read compressed certificate\n");
goto import_cert_out; goto import_cert_out;
} }
certptr += cert_len;
} else { } else {
/* i2d_X509 increments certptr here.. */
i2d_X509(cert, &certptr); i2d_X509(cert, &certptr);
} }
*certptr++ = 0x71; if ((res = ykpiv_util_write_cert(state, get_slot_hex(slot), certdata, cert_len, compress)) != YKPIV_OK) {
*certptr++ = 1;
*certptr++ = compress; /* certinfo (gzip etc) */
*certptr++ = 0xfe; /* LRC */
*certptr++ = 0;
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)); fprintf(stderr, "Failed commands with device: %s\n", ykpiv_strerror(res));
} else { } else {
ret = true; ret = true;
@@ -620,45 +521,32 @@ import_cert_out:
return ret; return ret;
} }
static bool set_dataobject(ykpiv_state *state, int verbose, int type) { static bool set_cardid(ykpiv_state *state, int verbose, int type) {
unsigned char obj[1024];
ykpiv_rc res; ykpiv_rc res;
size_t offs, rand_len, len; unsigned char id[MAX(sizeof(ykpiv_cardid), sizeof(ykpiv_cccid))];
const unsigned char *tmpl;
int id;
if(type == CHUID) { if(type == CHUID) {
offs = CHUID_GUID_OFFS; res = ykpiv_util_set_cardid(state, NULL);
len = sizeof(chuid_tmpl);
rand_len = 0x10;
tmpl = chuid_tmpl;
id = YKPIV_OBJ_CHUID;
} else { } else {
offs = CCC_ID_OFFS; res = ykpiv_util_set_cccid(state, NULL);
rand_len = 0xe;
len = sizeof(ccc_tmpl);
tmpl = ccc_tmpl;
id = YKPIV_OBJ_CAPABILITY;
}
memcpy(obj, tmpl, len);
if(RAND_pseudo_bytes(obj + offs, rand_len) == -1) {
fprintf(stderr, "error: no randomness.\n");
return false;
}
if(verbose) {
fprintf(stderr, "Setting the %s to: ", type == CHUID ? "CHUID" : "CCC");
dump_data(obj, len, stderr, true, format_arg_hex);
}
if((res = ykpiv_save_object(state, id, obj, len)) != YKPIV_OK) {
fprintf(stderr, "Failed communicating with device: %s\n", ykpiv_strerror(res));
return false;
} }
return true; if(res == YKPIV_OK && verbose) {
if (type == CHUID) {
res = ykpiv_util_get_cardid(state, (ykpiv_cardid*)id);
} else {
res = ykpiv_util_get_cccid(state, (ykpiv_cccid*)id);
}
if (res == YKPIV_OK) {
fprintf(stderr, "Set the %s ID to: ", type == CHUID ? "CHUID" : "CCC");
dump_data(id, type == CHUID ? YKPIV_CARDID_SIZE : YKPIV_CCCID_SIZE, stderr, true, format_arg_hex);
}
}
return res == YKPIV_OK;
} }
static bool request_certificate(ykpiv_state *state, 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, enum enum_hash hash, const char *input_file_name, enum enum_slot slot, char *subject, enum enum_hash hash,
const char *output_file_name) { const char *output_file_name) {
X509_REQ *req = NULL; X509_REQ *req = NULL;
X509_NAME *name = NULL; X509_NAME *name = NULL;
@@ -682,10 +570,10 @@ static bool request_certificate(ykpiv_state *state, enum enum_key_format key_for
null_parameter.type = V_ASN1_NULL; null_parameter.type = V_ASN1_NULL;
null_parameter.value.ptr = NULL; null_parameter.value.ptr = NULL;
sscanf(slot, "%2x", &key); key = get_slot_hex(slot);
input_file = open_file(input_file_name, INPUT); input_file = open_file(input_file_name, key_file_mode(key_format, false));
output_file = open_file(output_file_name, OUTPUT); output_file = open_file(output_file_name, key_file_mode(key_format, true));
if(!input_file || !output_file) { if(!input_file || !output_file) {
goto request_out; goto request_out;
} }
@@ -805,7 +693,7 @@ request_out:
} }
static bool selfsign_certificate(ykpiv_state *state, 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, enum enum_hash hash, const char *input_file_name, enum enum_slot slot, char *subject, enum enum_hash hash,
const int *serial, int validDays, const char *output_file_name) { const int *serial, int validDays, const char *output_file_name) {
FILE *input_file = NULL; FILE *input_file = NULL;
FILE *output_file = NULL; FILE *output_file = NULL;
@@ -831,10 +719,10 @@ static bool selfsign_certificate(ykpiv_state *state, enum enum_key_format key_fo
null_parameter.type = V_ASN1_NULL; null_parameter.type = V_ASN1_NULL;
null_parameter.value.ptr = NULL; null_parameter.value.ptr = NULL;
sscanf(slot, "%2x", &key); key = get_slot_hex(slot);
input_file = open_file(input_file_name, INPUT); input_file = open_file(input_file_name, key_file_mode(key_format, false));
output_file = open_file(output_file_name, OUTPUT); output_file = open_file(output_file_name, key_file_mode(key_format, true));
if(!input_file || !output_file) { if(!input_file || !output_file) {
goto selfsign_out; goto selfsign_out;
} }
@@ -1075,92 +963,81 @@ static bool change_pin(ykpiv_state *state, enum enum_action action, const char *
} }
static bool delete_certificate(ykpiv_state *state, enum enum_slot slot) { static bool delete_certificate(ykpiv_state *state, enum enum_slot slot) {
int object = get_object_id(slot); return ykpiv_util_delete_cert(state, get_slot_hex(slot)) == YKPIV_OK;
if(ykpiv_save_object(state, object, NULL, 0) != YKPIV_OK) {
fprintf(stderr, "Failed deleting object.\n");
return false;
} else {
fprintf(stderr, "Certificate deleted.\n");
return true;
}
} }
static bool read_certificate(ykpiv_state *state, enum enum_slot slot, static bool read_certificate(ykpiv_state *state, enum enum_slot slot,
enum enum_key_format key_format, const char *output_file_name) { enum enum_key_format key_format, const char *output_file_name) {
FILE *output_file; FILE *output_file;
int object = get_object_id(slot); uint8_t *data = NULL;
unsigned char data[3072]; const unsigned char *ptr = NULL;
const unsigned char *ptr = data;
unsigned long len = sizeof(data);
int cert_len;
bool ret = false;
X509 *x509 = NULL; X509 *x509 = NULL;
bool ret = false;
size_t cert_len = 0;
if(key_format != key_format_arg_PEM && if (key_format != key_format_arg_PEM &&
key_format != key_format_arg_DER && key_format != key_format_arg_DER &&
key_format != key_format_arg_SSH) { key_format != key_format_arg_SSH) {
fprintf(stderr, "Only PEM, DER and SSH format are supported for read-certificate.\n"); fprintf(stderr, "Only PEM, DER and SSH format are supported for read-certificate.\n");
return false; return false;
} }
output_file = open_file(output_file_name, OUTPUT); output_file = open_file(output_file_name, key_file_mode(key_format, true));
if(!output_file) { if (!output_file) {
return false; return false;
} }
if(ykpiv_fetch_object(state, object, data, &len) != YKPIV_OK) { if (ykpiv_util_read_cert(state, get_slot_hex(slot), &data, &cert_len) != YKPIV_OK) {
fprintf(stderr, "Failed fetching certificate.\n"); fprintf(stderr, "Failed fetching certificate.\n");
goto read_cert_out; goto read_cert_out;
} }
ptr = data;
if(*ptr++ == 0x70) { if (key_format == key_format_arg_PEM ||
ptr += get_length(ptr, &cert_len); key_format == key_format_arg_SSH) {
if(key_format == key_format_arg_PEM || x509 = X509_new();
key_format == key_format_arg_SSH) { if (!x509) {
x509 = X509_new(); fprintf(stderr, "Failed allocating x509 structure.\n");
if(!x509) { goto read_cert_out;
fprintf(stderr, "Failed allocating x509 structure.\n"); }
goto read_cert_out; x509 = d2i_X509(NULL, (const unsigned char**)&ptr, cert_len);
} if (!x509) {
x509 = d2i_X509(NULL, &ptr, cert_len); fprintf(stderr, "Failed parsing x509 information.\n");
if(!x509) { goto read_cert_out;
fprintf(stderr, "Failed parsing x509 information.\n"); }
goto read_cert_out;
}
if (key_format == key_format_arg_PEM) { if (key_format == key_format_arg_PEM) {
PEM_write_X509(output_file, x509); PEM_write_X509(output_file, x509);
ret = true;
}
else {
if (!SSH_write_X509(output_file, x509)) {
fprintf(stderr, "Unable to extract public key or not an RSA key.\n");
goto read_cert_out;
}
ret = true;
}
} else { /* key_format_arg_DER */
/* XXX: This will just dump the raw data in tag 0x70.. */
fwrite(ptr, (size_t)cert_len, 1, output_file);
ret = true; ret = true;
} }
} else { else {
fprintf(stderr, "Failed parsing data.\n"); if (!SSH_write_X509(output_file, x509)) {
fprintf(stderr, "Unable to extract public key or not an RSA key.\n");
goto read_cert_out;
}
ret = true;
}
} else { /* key_format_arg_DER */
/* XXX: This will just dump the raw data in tag 0x70.. */
fwrite(ptr, (size_t)cert_len, 1, output_file);
ret = true;
} }
read_cert_out: read_cert_out:
if(output_file != stdout) { if (output_file != stdout) {
fclose(output_file); fclose(output_file);
} }
if(x509) { if (x509) {
X509_free(x509); X509_free(x509);
} }
if (data) {
ykpiv_util_free(state, data);
}
return ret; return ret;
} }
static bool sign_file(ykpiv_state *state, const char *input, const char *output, static bool sign_file(ykpiv_state *state, const char *input, const char *output,
const char *slot, enum enum_algorithm algorithm, enum enum_hash hash, enum enum_slot slot, enum enum_algorithm algorithm, enum enum_hash hash,
int verbosity) { int verbosity) {
FILE *input_file = NULL; FILE *input_file = NULL;
FILE *output_file = NULL; FILE *output_file = NULL;
@@ -1171,9 +1048,9 @@ static bool sign_file(ykpiv_state *state, const char *input, const char *output,
int algo; int algo;
const EVP_MD *md; const EVP_MD *md;
sscanf(slot, "%2x", &key); key = get_slot_hex(slot);
input_file = open_file(input, INPUT); input_file = open_file(input, INPUT_BIN);
if(!input_file) { if(!input_file) {
return false; return false;
} }
@@ -1182,7 +1059,7 @@ static bool sign_file(ykpiv_state *state, const char *input, const char *output,
fprintf(stderr, "Please paste the input...\n"); fprintf(stderr, "Please paste the input...\n");
} }
output_file = open_file(output, OUTPUT); output_file = open_file(output, OUTPUT_BIN);
if(!output_file) { if(!output_file) {
if(input_file && input_file != stdin) { if(input_file && input_file != stdin) {
fclose(input_file); fclose(input_file);
@@ -1253,7 +1130,7 @@ out:
static void print_cert_info(ykpiv_state *state, enum enum_slot slot, const EVP_MD *md, static void print_cert_info(ykpiv_state *state, enum enum_slot slot, const EVP_MD *md,
FILE *output) { FILE *output) {
int object = get_object_id(slot); int object = ykpiv_util_slot_object(get_slot_hex(slot));
int slot_name; int slot_name;
unsigned char data[3072]; unsigned char data[3072];
const unsigned char *ptr = data; const unsigned char *ptr = data;
@@ -1267,12 +1144,7 @@ static void print_cert_info(ykpiv_state *state, enum enum_slot slot, const EVP_M
return; return;
} }
if (slot == slot_arg_9a) slot_name = get_slot_hex(slot);
slot_name = 0x9a;
else if (slot >= slot_arg_9c && slot <= slot_arg_9e)
slot_name = 0x9b + slot;
else
slot_name = 0x82 + (slot - slot_arg_82);
fprintf(output, "Slot %x:\t", slot_name); fprintf(output, "Slot %x:\t", slot_name);
@@ -1368,7 +1240,7 @@ static bool status(ykpiv_state *state, enum enum_hash hash,
unsigned char buf[3072]; unsigned char buf[3072];
long unsigned len = sizeof(buf); long unsigned len = sizeof(buf);
int i; int i;
FILE *output_file = open_file(output_file_name, OUTPUT); FILE *output_file = open_file(output_file_name, OUTPUT_TEXT);
if(!output_file) { if(!output_file) {
return false; return false;
} }
@@ -1421,7 +1293,7 @@ static bool test_signature(ykpiv_state *state, enum enum_slot slot,
unsigned int data_len; unsigned int data_len;
X509 *x509 = NULL; X509 *x509 = NULL;
EVP_PKEY *pubkey; EVP_PKEY *pubkey;
FILE *input_file = open_file(input_file_name, INPUT); FILE *input_file = open_file(input_file_name, key_file_mode(cert_format, false));
if(!input_file) { if(!input_file) {
fprintf(stderr, "Failed opening input file %s.\n", input_file_name); fprintf(stderr, "Failed opening input file %s.\n", input_file_name);
@@ -1486,7 +1358,7 @@ static bool test_signature(ykpiv_state *state, enum enum_slot slot,
if(algorithm == 0) { if(algorithm == 0) {
goto test_out; goto test_out;
} }
sscanf(cmdline_parser_slot_values[slot], "%2x", &key); key = get_slot_hex(slot);
if(YKPIV_IS_RSA(algorithm)) { if(YKPIV_IS_RSA(algorithm)) {
prepare_rsa_signature(data, data_len, encoded, &enc_len, EVP_MD_type(md)); prepare_rsa_signature(data, data_len, encoded, &enc_len, EVP_MD_type(md));
ptr = encoded; ptr = encoded;
@@ -1554,7 +1426,7 @@ static bool test_decipher(ykpiv_state *state, enum enum_slot slot,
X509 *x509 = NULL; X509 *x509 = NULL;
EVP_PKEY *pubkey; EVP_PKEY *pubkey;
EC_KEY *tmpkey = NULL; EC_KEY *tmpkey = NULL;
FILE *input_file = open_file(input_file_name, INPUT); FILE *input_file = open_file(input_file_name, key_file_mode(cert_format, false));
if(!input_file) { if(!input_file) {
fprintf(stderr, "Failed opening input file %s.\n", input_file_name); fprintf(stderr, "Failed opening input file %s.\n", input_file_name);
@@ -1591,7 +1463,7 @@ static bool test_decipher(ykpiv_state *state, enum enum_slot slot,
if(algorithm == 0) { if(algorithm == 0) {
goto decipher_out; goto decipher_out;
} }
sscanf(cmdline_parser_slot_values[slot], "%2x", &key); key = get_slot_hex(slot);
if(YKPIV_IS_RSA(algorithm)) { if(YKPIV_IS_RSA(algorithm)) {
unsigned char secret[32]; unsigned char secret[32];
unsigned char secret2[32]; unsigned char secret2[32];
@@ -1703,57 +1575,47 @@ static bool list_readers(ykpiv_state *state) {
return true; return true;
} }
static bool attest(ykpiv_state *state, const char *slot, static bool attest(ykpiv_state *state, enum enum_slot slot,
enum enum_key_format key_format, const char *output_file_name) { enum enum_key_format key_format, const char *output_file_name) {
unsigned char data[2048]; unsigned char data[2048];
unsigned long len = sizeof(data); unsigned long len = sizeof(data);
bool ret = false; bool ret = false;
X509 *x509 = NULL; X509 *x509 = NULL;
unsigned char templ[] = {0, YKPIV_INS_ATTEST, 0, 0};
int key; int key;
int sw; FILE *output_file = open_file(output_file_name, key_file_mode(key_format, true));
FILE *output_file = open_file(output_file_name, OUTPUT);
if(!output_file) { if(!output_file) {
return false; return false;
} }
sscanf(slot, "%2x", &key);
templ[2] = key;
if(key_format != key_format_arg_PEM && key_format != key_format_arg_DER) { if(key_format != key_format_arg_PEM && key_format != key_format_arg_DER) {
fprintf(stderr, "Only PEM and DER format are supported for attest..\n"); fprintf(stderr, "Only PEM and DER format are supported for attest..\n");
return false; return false;
} }
if(ykpiv_transfer_data(state, templ, NULL, 0, data, &len, &sw) != YKPIV_OK) { key = get_slot_hex(slot);
fprintf(stderr, "Failed to communicate.\n"); if (ykpiv_attest(state, key, data, &len) != YKPIV_OK) {
goto attest_out; fprintf(stderr, "Failed to attest data.\n");
} else if(sw != SW_SUCCESS) {
fprintf(stderr, "Failed to attest key.\n");
goto attest_out; goto attest_out;
} }
if(data[0] == 0x30) { if(key_format == key_format_arg_PEM) {
if(key_format == key_format_arg_PEM) { const unsigned char *ptr = data;
const unsigned char *ptr = data; int len2 = len;
int len2 = len; x509 = X509_new();
x509 = X509_new(); if(!x509) {
if(!x509) { fprintf(stderr, "Failed allocating x509 structure.\n");
fprintf(stderr, "Failed allocating x509 structure.\n"); goto attest_out;
goto attest_out;
}
x509 = d2i_X509(NULL, &ptr, len2);
if(!x509) {
fprintf(stderr, "Failed parsing x509 information.\n");
goto attest_out;
}
PEM_write_X509(output_file, x509);
ret = true;
} else {
fwrite(data, len, 1, output_file);
} }
ret = true; x509 = d2i_X509(NULL, &ptr, len2);
if(!x509) {
fprintf(stderr, "Failed parsing x509 information.\n");
goto attest_out;
}
PEM_write_X509(output_file, x509);
} else {
fwrite(data, len, 1, output_file);
} }
ret = true;
attest_out: attest_out:
if(output_file != stdout) { if(output_file != stdout) {
@@ -1773,7 +1635,7 @@ static bool write_object(ykpiv_state *state, int id,
size_t len = sizeof(data); size_t len = sizeof(data);
ykpiv_rc res; ykpiv_rc res;
input_file = open_file(input_file_name, INPUT); input_file = open_file(input_file_name, data_file_mode(format, false));
if(!input_file) { if(!input_file) {
return false; return false;
} }
@@ -1812,7 +1674,7 @@ static bool read_object(ykpiv_state *state, int id, const char *output_file_name
unsigned long len = sizeof(data); unsigned long len = sizeof(data);
bool ret = false; bool ret = false;
output_file = open_file(output_file_name, OUTPUT); output_file = open_file(output_file_name, data_file_mode(format, true));
if(!output_file) { if(!output_file) {
return false; return false;
} }
@@ -1875,7 +1737,7 @@ int main(int argc, char *argv[]) {
} }
break; break;
case action_arg_pinMINUS_retries: case action_arg_pinMINUS_retries:
if(!args_info.pin_retries_arg || !args_info.puk_retries_arg) { if(!args_info.pin_retries_given || !args_info.puk_retries_given) {
fprintf(stderr, "The '%s' action needs both --pin-retries and --puk-retries arguments.\n", fprintf(stderr, "The '%s' action needs both --pin-retries and --puk-retries arguments.\n",
cmdline_parser_action_values[action]); cmdline_parser_action_values[action]);
return EXIT_FAILURE; return EXIT_FAILURE;
@@ -2013,7 +1875,7 @@ int main(int argc, char *argv[]) {
print_version(state, args_info.output_arg); print_version(state, args_info.output_arg);
break; break;
case action_arg_generate: case action_arg_generate:
if(generate_key(state, args_info.slot_orig, args_info.algorithm_arg, args_info.output_arg, args_info.key_format_arg, if(generate_key(state, args_info.slot_arg, args_info.algorithm_arg, args_info.output_arg, args_info.key_format_arg,
args_info.pin_policy_arg, args_info.touch_policy_arg) == false) { args_info.pin_policy_arg, args_info.touch_policy_arg) == false) {
ret = EXIT_FAILURE; ret = EXIT_FAILURE;
} else { } else {
@@ -2052,7 +1914,7 @@ int main(int argc, char *argv[]) {
break; break;
case action_arg_reset: case action_arg_reset:
if(reset(state) == false) { if(reset(state) == false) {
fprintf(stderr, "Reset failed, are pincodes blocked?\n"); fprintf(stderr, "Reset failed, are pincodes blocked?\n");
ret = EXIT_FAILURE; ret = EXIT_FAILURE;
} else { } else {
fprintf(stderr, "Successfully reset the application.\n"); fprintf(stderr, "Successfully reset the application.\n");
@@ -2068,7 +1930,7 @@ int main(int argc, char *argv[]) {
} }
break; break;
case action_arg_importMINUS_key: case action_arg_importMINUS_key:
if(import_key(state, args_info.key_format_arg, args_info.input_arg, args_info.slot_orig, password, if(import_key(state, args_info.key_format_arg, args_info.input_arg, args_info.slot_arg, password,
args_info.pin_policy_arg, args_info.touch_policy_arg) == false) { args_info.pin_policy_arg, args_info.touch_policy_arg) == false) {
fprintf(stderr, "Unable to import private key\n"); fprintf(stderr, "Unable to import private key\n");
ret = EXIT_FAILURE; ret = EXIT_FAILURE;
@@ -2085,7 +1947,7 @@ int main(int argc, char *argv[]) {
break; break;
case action_arg_setMINUS_ccc: case action_arg_setMINUS_ccc:
case action_arg_setMINUS_chuid: case action_arg_setMINUS_chuid:
if(set_dataobject(state, verbosity, action == action_arg_setMINUS_chuid ? CHUID : CCC) == false) { if(set_cardid(state, verbosity, action == action_arg_setMINUS_chuid ? CHUID : CCC) == false) {
ret = EXIT_FAILURE; ret = EXIT_FAILURE;
} else { } else {
fprintf(stderr, "Successfully set new %s.\n", action == action_arg_setMINUS_chuid ? "CHUID" : "CCC"); fprintf(stderr, "Successfully set new %s.\n", action == action_arg_setMINUS_chuid ? "CHUID" : "CCC");
@@ -2093,7 +1955,7 @@ int main(int argc, char *argv[]) {
break; break;
case action_arg_requestMINUS_certificate: case action_arg_requestMINUS_certificate:
if(request_certificate(state, args_info.key_format_arg, args_info.input_arg, if(request_certificate(state, args_info.key_format_arg, args_info.input_arg,
args_info.slot_orig, args_info.subject_arg, args_info.hash_arg, args_info.slot_arg, args_info.subject_arg, args_info.hash_arg,
args_info.output_arg) == false) { args_info.output_arg) == false) {
ret = EXIT_FAILURE; ret = EXIT_FAILURE;
} else { } else {
@@ -2153,7 +2015,7 @@ int main(int argc, char *argv[]) {
} }
case action_arg_selfsignMINUS_certificate: case action_arg_selfsignMINUS_certificate:
if(selfsign_certificate(state, args_info.key_format_arg, args_info.input_arg, if(selfsign_certificate(state, args_info.key_format_arg, args_info.input_arg,
args_info.slot_orig, args_info.subject_arg, args_info.hash_arg, args_info.slot_arg, args_info.subject_arg, args_info.hash_arg,
args_info.serial_given ? &args_info.serial_arg : NULL, args_info.valid_days_arg, args_info.serial_given ? &args_info.serial_arg : NULL, args_info.valid_days_arg,
args_info.output_arg) == false) { args_info.output_arg) == false) {
ret = EXIT_FAILURE; ret = EXIT_FAILURE;
@@ -2207,7 +2069,7 @@ int main(int argc, char *argv[]) {
} }
break; break;
case action_arg_attest: case action_arg_attest:
if(attest(state, args_info.slot_orig, args_info.key_format_arg, if(attest(state, args_info.slot_arg, args_info.key_format_arg,
args_info.output_arg) == false) { args_info.output_arg) == false) {
ret = EXIT_FAILURE; ret = EXIT_FAILURE;
} }
@@ -2228,7 +2090,7 @@ int main(int argc, char *argv[]) {
ret = EXIT_FAILURE; ret = EXIT_FAILURE;
} }
else if(sign_file(state, args_info.input_arg, args_info.output_arg, else if(sign_file(state, args_info.input_arg, args_info.output_arg,
args_info.slot_orig, args_info.algorithm_arg, args_info.hash_arg, args_info.slot_arg, args_info.algorithm_arg, args_info.hash_arg,
verbosity)) { verbosity)) {
fprintf(stderr, "Signature successful!\n"); fprintf(stderr, "Signature successful!\n");
} else { } else {
+29
View File
@@ -0,0 +1,29 @@
#!/bin/bash
# Output redirected to fd 0 so it can be run from 'make check' scripts.
echo >&0
echo "Hardware tests enabled!" >&0
echo >&0
echo "******* ******* ******* ******* ******* ******* ******* ******* ******* ******* ******* ******* ******* ******* *******" >&0
echo "WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING" >&0
echo "WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING" >&0
echo >&0
echo "******* ******* ******* ******* ******* ******* ******* ******* ******* ******* ******* ******* ******* ******* *******" >&0
echo >&0
echo " ALL DATA WILL BE ERASED ON CONNECTED YUBIKEYS " >&0
echo >&0
echo "******* ******* ******* ******* ******* ******* ******* ******* ******* ******* ******* ******* ******* ******* *******" >&0
echo >&0
echo "WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING" >&0
echo "WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING" >&0
echo "******* ******* ******* ******* ******* ******* ******* ******* ******* ******* ******* ******* ******* ******* *******" >&0
echo >&0
echo -n "Are you SURE you wish to proceed? If so, type 'CONFIRM': " >&0
read CONFIRM
if [[ "x$CONFIRM" != "xCONFIRM" ]]; then
echo "1"
exit 1
fi
echo "0"
+9 -1
View File
@@ -27,6 +27,7 @@
PACKAGE=yubico-piv-tool PACKAGE=yubico-piv-tool
OPENSSLVERSION=1.0.2l OPENSSLVERSION=1.0.2l
CHECKVERSION=0.12.0
all: usage 32bit 64bit all: usage 32bit 64bit
@@ -55,11 +56,18 @@ doit:
rm $(PWD)/tmp$(ARCH)/root/bin/c_rehash && \ rm $(PWD)/tmp$(ARCH)/root/bin/c_rehash && \
rm -rf $(PWD)/tmp$(ARCH)/root/lib/engines/ && \ rm -rf $(PWD)/tmp$(ARCH)/root/lib/engines/ && \
cd .. && \ cd .. && \
cp ../check-$(CHECKVERSION).tar.gz . || \
curl -L -O "https://github.com/libcheck/check/releases/download/$(CHECKVERSION)/check-$(CHECKVERSION).tar.gz" && \
tar xfa check-$(CHECKVERSION).tar.gz && \
cd check-$(CHECKVERSION) && \
CC=$(HOST)-gcc PKG_CONFIG_PATH=$(PWD)/tmp$(ARCH)/root/lib/pkgconfig ./configure --host=$(HOST) --build=x86_64-unknown-linux-gnu --prefix=$(PWD)/tmp$(ARCH)/root --disable-subunit --enable-static --disable-shared && \
make all install && \
cd .. && \
cp ../$(PACKAGE)-$(VERSION).tar.gz . && \ cp ../$(PACKAGE)-$(VERSION).tar.gz . && \
tar xfa $(PACKAGE)-$(VERSION).tar.gz && \ tar xfa $(PACKAGE)-$(VERSION).tar.gz && \
cd $(PACKAGE)-$(VERSION)/ && \ cd $(PACKAGE)-$(VERSION)/ && \
CC=$(HOST)-gcc PKG_CONFIG_PATH=$(PWD)/tmp$(ARCH)/root/lib/pkgconfig lt_cv_deplibs_check_method=pass_all ./configure --host=$(HOST) --build=x86_64-unknown-linux-gnu --prefix=$(PWD)/tmp$(ARCH)/root LDFLAGS=-L$(PWD)/tmp$(ARCH)/root/lib CPPFLAGS=-I$(PWD)/tmp$(ARCH)/root/include && \ CC=$(HOST)-gcc PKG_CONFIG_PATH=$(PWD)/tmp$(ARCH)/root/lib/pkgconfig lt_cv_deplibs_check_method=pass_all ./configure --host=$(HOST) --build=x86_64-unknown-linux-gnu --prefix=$(PWD)/tmp$(ARCH)/root LDFLAGS=-L$(PWD)/tmp$(ARCH)/root/lib CPPFLAGS=-I$(PWD)/tmp$(ARCH)/root/include && \
make install $(CHECK) && \ WINEPATH="/usr/$(HOST)/lib/" make install $(CHECK) && \
rm $(PWD)/tmp$(ARCH)/root/lib/*.la && \ rm $(PWD)/tmp$(ARCH)/root/lib/*.la && \
rm -rf $(PWD)/tmp$(ARCH)/root/lib/pkgconfig/ && \ rm -rf $(PWD)/tmp$(ARCH)/root/lib/pkgconfig/ && \
cp COPYING $(PWD)/tmp$(ARCH)/root/licenses/$(PACKAGE).txt && \ cp COPYING $(PWD)/tmp$(ARCH)/root/licenses/$(PACKAGE).txt && \
+1 -1
View File
@@ -173,7 +173,7 @@ CK_RV do_create_empty_cert(CK_BYTE_PTR in, CK_ULONG in_len, CK_BBOOL is_rsa,
// Manually set a signature (same reason as before) // Manually set a signature (same reason as before)
ASN1_BIT_STRING_set_bit(cert->signature, 8, 1); ASN1_BIT_STRING_set_bit(cert->signature, 8, 1);
ASN1_BIT_STRING_set(cert->signature, "\x00", 1); ASN1_BIT_STRING_set(cert->signature, (unsigned char*)"\x00", 1);
len = i2d_X509(cert, NULL); len = i2d_X509(cert, NULL);
if (len < 0) if (len < 0)
+9 -3
View File
@@ -29,17 +29,23 @@
#LOG_COMPILER = $(VALGRIND) #LOG_COMPILER = $(VALGRIND)
AM_CFLAGS = $(WARN_CFLAGS) AM_CFLAGS = $(WARN_CFLAGS) @CHECK_CFLAGS@
AM_CPPFLAGS = -I$(top_srcdir)/lib -I$(top_builddir)/lib AM_CPPFLAGS = -I$(top_srcdir)/lib -I$(top_builddir)/lib
AM_CPPFLAGS += -I$(top_srcdir)/ykcs11 -I$(top_builddir)/ykcs11 AM_CPPFLAGS += -I$(top_srcdir)/ykcs11 -I$(top_builddir)/ykcs11
AM_CPPFLAGS += $(OPENSSL_CFLAGS) AM_CPPFLAGS += $(OPENSSL_CFLAGS)
AM_LDFLAGS = -no-install AM_LDFLAGS = @CHECK_LIBS@
if COMPILER_CLANG
AM_LDFLAGS += -no-fast-install
else
AM_LDFLAGS += -no-install
endif
ykcs11_tests_LDADD = ../libykcs11.la $(OPENSSL_LIBS) ykcs11_tests_LDADD = ../libykcs11.la $(OPENSSL_LIBS)
check_PROGRAMS = ykcs11_tests check_PROGRAMS = ykcs11_tests
TESTS = $(check_PROGRAMS) TESTS = reset.sh $(check_PROGRAMS)
if ENABLE_COV if ENABLE_COV
AM_LDFLAGS += --coverage AM_LDFLAGS += --coverage
+20
View File
@@ -0,0 +1,20 @@
BIN="../../tool/yubico-piv-tool${EXEEXT}"
# Verify that user has confirmed destructive hw-tests
if [ "x$YKPIV_ENV_HWTESTS_CONFIRMED" != "x1" ]; then
printf "\n***\n*** Hardware tests skipped. Run \"make hwcheck\".\n***\n\n" >&0
exit 77 # exit code 77 == skipped tests
fi
# Reset
$BIN -averify-pin -P000000 || true
$BIN -averify-pin -P000000 || true
$BIN -averify-pin -P000000 || true
$BIN -averify-pin -P000000 || true
$BIN -averify-pin -P000000 || true
$BIN -achange-puk -P000000 -N00000000 || true
$BIN -achange-puk -P000000 -N00000000 || true
$BIN -achange-puk -P000000 -N00000000 || true
$BIN -achange-puk -P000000 -N00000000 || true
$BIN -achange-puk -P000000 -N00000000 || true
$BIN -areset
+36 -4
View File
@@ -39,6 +39,13 @@
#include <openssl/x509.h> #include <openssl/x509.h>
#include <openssl/rand.h> #include <openssl/rand.h>
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wpointer-sign"
#ifdef __MINGW32__
#define dprintf(fd, ...) fprintf(stdout, __VA_ARGS__)
#endif
void dump_hex(const unsigned char *buf, unsigned int len, FILE *output, int space) { void dump_hex(const unsigned char *buf, unsigned int len, FILE *output, int space) {
unsigned int i; unsigned int i;
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
@@ -104,10 +111,11 @@ static void test_initalize() {
} }
static void test_token_info() { static int test_token_info() {
const CK_CHAR_PTR TOKEN_LABEL = "YubiKey PIV"; const CK_CHAR_PTR TOKEN_LABEL = "YubiKey PIV";
const CK_CHAR_PTR TOKEN_MODEL = "YubiKey "; // Skip last 3 characters (version dependent) const CK_CHAR_PTR TOKEN_MODEL = "YubiKey "; // Skip last 3 characters (version dependent)
const CK_CHAR_PTR TOKEN_MODEL_YK4 = "YubiKey YK4";
const CK_CHAR_PTR TOKEN_SERIAL = "1234"; const CK_CHAR_PTR TOKEN_SERIAL = "1234";
const CK_FLAGS TOKEN_FLAGS = CKF_RNG | CKF_LOGIN_REQUIRED | CKF_USER_PIN_INITIALIZED | CKF_TOKEN_INITIALIZED; const CK_FLAGS TOKEN_FLAGS = CKF_RNG | CKF_LOGIN_REQUIRED | CKF_USER_PIN_INITIALIZED | CKF_TOKEN_INITIALIZED;
const CK_VERSION HW = {0, 0}; const CK_VERSION HW = {0, 0};
@@ -132,16 +140,22 @@ static void test_token_info() {
asrt(info.ulFreePublicMemory, CK_UNAVAILABLE_INFORMATION, "FREE_PUB_MEM"); asrt(info.ulFreePublicMemory, CK_UNAVAILABLE_INFORMATION, "FREE_PUB_MEM");
asrt(info.ulTotalPrivateMemory, CK_UNAVAILABLE_INFORMATION, "TOTAL_PVT_MEM"); asrt(info.ulTotalPrivateMemory, CK_UNAVAILABLE_INFORMATION, "TOTAL_PVT_MEM");
asrt(info.ulFreePrivateMemory, CK_UNAVAILABLE_INFORMATION, "FREE_PVT_MEM"); asrt(info.ulFreePrivateMemory, CK_UNAVAILABLE_INFORMATION, "FREE_PVT_MEM");
if (strncmp(info.model, TOKEN_MODEL_YK4, strlen(TOKEN_MODEL_YK4)) != 0) {
dprintf(0, "\n\n** WARNING: Only YK4 supported. Skipping remaining tests.\n\n");
return -1;
}
asrt(info.hardwareVersion.major, HW.major, "HW_MAJ"); asrt(info.hardwareVersion.major, HW.major, "HW_MAJ");
asrt(info.hardwareVersion.minor, HW.minor, "HW_MIN"); asrt(info.hardwareVersion.minor, HW.minor, "HW_MIN");
if (info.firmwareVersion.major != 4 && info.firmwareVersion.major != 0) if (info.firmwareVersion.major != 4 && info.firmwareVersion.major != 0)
asrt(info.firmwareVersion.major, 4, "FW_MAJ"); asrt(info.firmwareVersion.major, 4, "FW_MAJ");
asrt(strcmp(info.utcTime, TOKEN_TIME), 0, "TOKEN_TIME"); asrt(strncmp(info.utcTime, TOKEN_TIME, sizeof(info.utcTime)), 0, "TOKEN_TIME");
asrt(funcs->C_Finalize(NULL), CKR_OK, "FINALIZE"); asrt(funcs->C_Finalize(NULL), CKR_OK, "FINALIZE");
return 0;
} }
static void test_mechanism_list_and_info() { static void test_mechanism_list_and_info() {
@@ -627,6 +641,15 @@ static void test_import_and_sign_all_10_RSA() {
} }
#endif #endif
int destruction_confirmed(void) {
char *confirmed = getenv("YKPIV_ENV_HWTESTS_CONFIRMED");
if (confirmed && confirmed[0] == '1')
return 1;
// Use dprintf() to write directly to stdout, since automake eats the standard stdout/stderr pointers.
dprintf(0, "\n***\n*** Hardware tests skipped. Run \"make hwcheck\".\n***\n\n");
return 0;
}
int main(void) { int main(void) {
get_functions(&funcs); get_functions(&funcs);
@@ -634,8 +657,15 @@ int main(void) {
test_lib_info(); test_lib_info();
#ifdef HW_TESTS #ifdef HW_TESTS
// Require user confirmation to continue, since this test suite will clear
// any data stored on connected keys.
if (!destruction_confirmed())
exit(77); // exit code 77 == skipped tests
test_initalize(); test_initalize();
test_token_info(); // Require YK4 to continue. Skip if different model found.
if (test_token_info() != 0)
exit(77);
test_mechanism_list_and_info(); test_mechanism_list_and_info();
test_session(); test_session();
test_login(); test_login();
@@ -648,3 +678,5 @@ int main(void) {
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
#pragma clang diagnostic pop
+1 -1
View File
@@ -196,7 +196,7 @@ static CK_RV COMMON_token_generate_key(ykpiv_state *state, CK_BBOOL rsa,
*certptr++ = 0; *certptr++ = 0;
// Store the certificate into the token // Store the certificate into the token
if (ykpiv_save_object(state, key_to_object_id(key), data, (size_t)(certptr - data)) != YKPIV_OK) if (ykpiv_save_object(state, ykpiv_util_slot_object(key), data, (size_t)(certptr - data)) != YKPIV_OK)
return CKR_DEVICE_ERROR; return CKR_DEVICE_ERROR;
return CKR_OK; return CKR_OK;
+1 -2
View File
@@ -182,6 +182,7 @@ CK_RV create_token(ykpiv_state *state, CK_BYTE_PTR p, ykcs11_slot_t *slot) {
t_info->ulFreePrivateMemory = CK_UNAVAILABLE_INFORMATION; t_info->ulFreePrivateMemory = CK_UNAVAILABLE_INFORMATION;
memset(&t_info->hardwareVersion, 0, sizeof(t_info->hardwareVersion));
// Ignore hardware version, report firmware version // Ignore hardware version, report firmware version
if (token.get_token_version(state, &t_info->firmwareVersion) != CKR_OK) { if (token.get_token_version(state, &t_info->firmwareVersion) != CKR_OK) {
ykpiv_disconnect(state); ykpiv_disconnect(state);
@@ -257,8 +258,6 @@ void strip_DER_encoding_from_ECSIG(CK_BYTE_PTR data, CK_ULONG_PTR len) {
data_ptr++; data_ptr++;
memcpy(buf_ptr, data_ptr, elem_len); memcpy(buf_ptr, data_ptr, elem_len);
data_ptr += elem_len;
buf_ptr += elem_len;
*len = sig_halflen * 2; *len = sig_halflen * 2;
memcpy(data, buf, *len); memcpy(data, buf, *len);
+6
View File
@@ -215,6 +215,11 @@ CK_DEFINE_FUNCTION(CK_RV, C_GetSlotList)(
return CKR_OK; return CKR_OK;
} }
if (!pulCount) {
DOUT;
return CKR_ARGUMENTS_BAD;
}
if ((tokenPresent && *pulCount < n_slots_with_token) || (!tokenPresent && *pulCount < n_slots)) { if ((tokenPresent && *pulCount < n_slots_with_token) || (!tokenPresent && *pulCount < n_slots)) {
DBG("Buffer too small: needed %lu, provided %lu", n_slots, *pulCount); DBG("Buffer too small: needed %lu, provided %lu", n_slots, *pulCount);
return CKR_BUFFER_TOO_SMALL; return CKR_BUFFER_TOO_SMALL;
@@ -1214,6 +1219,7 @@ CK_DEFINE_FUNCTION(CK_RV, C_DestroyObject)(
rv = delete_cert(cert_id); rv = delete_cert(cert_id);
if (rv != CKR_OK) { if (rv != CKR_OK) {
free(obj_ptr);
DBG("Unable to delete certificate data"); DBG("Unable to delete certificate data");
return CKR_FUNCTION_FAILED; return CKR_FUNCTION_FAILED;
} }
+3 -3
View File
@@ -32,16 +32,16 @@
#include "pkcs11.h" #include "pkcs11.h"
#include <string.h> #include <string.h>
static const CK_UTF8CHAR_PTR slot_manufacturer = "Yubico"; static const CK_UTF8CHAR_PTR slot_manufacturer = (const CK_UTF8CHAR_PTR)"Yubico";
static const CK_FLAGS slot_flags = CKF_TOKEN_PRESENT | CKF_HW_SLOT; static const CK_FLAGS slot_flags = CKF_TOKEN_PRESENT | CKF_HW_SLOT;
static const CK_VERSION slot_version = {1, 0}; static const CK_VERSION slot_version = {1, 0};
CK_RV YUBICO_get_slot_manufacturer(CK_UTF8CHAR_PTR str, CK_ULONG len) { CK_RV YUBICO_get_slot_manufacturer(CK_UTF8CHAR_PTR str, CK_ULONG len) {
if (strlen(slot_manufacturer) > len) if (strlen((const char*)slot_manufacturer) > len)
return CKR_BUFFER_TOO_SMALL; return CKR_BUFFER_TOO_SMALL;
memcpy(str, slot_manufacturer, strlen(slot_manufacturer)); memcpy(str, slot_manufacturer, strlen((const char*)slot_manufacturer));
return CKR_OK; return CKR_OK;
} }
+1 -1
View File
@@ -372,7 +372,7 @@ CK_RV YUBICO_token_change_pin(ykpiv_state *state, CK_USER_TYPE user_type, CK_UTF
DBG("TODO implement other users pin change"); DBG("TODO implement other users pin change");
return CKR_FUNCTION_FAILED; return CKR_FUNCTION_FAILED;
} }
res = ykpiv_change_pin(state, pOldPin, ulOldLen, pNewPin, ulNewLen, &tries); res = ykpiv_change_pin(state, (const char*)pOldPin, ulOldLen, (const char*)pNewPin, ulNewLen, &tries);
switch (res) { switch (res) {
case YKPIV_OK: case YKPIV_OK:
return CKR_OK; return CKR_OK;