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.status
configure
doxygen-doc/
libtool
m4/libtool.m4
m4/ltoptions.m4
@@ -15,6 +16,8 @@ m4/ltversion.m4
m4/lt~obsolete.m4
*.c~
*.h~
*.plist
.libs
ChangeLog
build-aux/ar-lib
build-aux/compile
@@ -25,6 +28,9 @@ build-aux/install-sh
build-aux/ltmain.sh
build-aux/missing
build-aux/test-driver
GPATH
GRTAGS
GTAGS
tmp32/
tmp64/
yubico-piv-tool-*-win32.zip
@@ -35,6 +41,10 @@ yubico-piv-tool-*.tar.gz
yubico-piv-tool-*.tar.gz.sig
yubico-piv-tool-*-mac.zip
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.log
lib/tests/basic.o
@@ -46,7 +56,13 @@ lib/tests/parse_key.trs
lib/tests/test-suite.log
lib/error.lo
lib/error.o
lib/internal.la
lib/internal.lo
lib/internal.o
lib/libykpiv.la
lib/util.la
lib/util.lo
lib/util.o
lib/version.lo
lib/version.o
lib/ykpiv-version.h
@@ -67,17 +83,21 @@ tool/libpiv_cmd_la-cmdline.o
tool/libpiv_util.la
tool/util.lo
tool/util.o
tool/tests/cert_9a.pem
tool/tests/cert_9e.pem
tool/tests/basic.sh.log
tool/tests/basic.sh.trs
tool/tests/test-suite.log
tool/tests/key_9a.pub
tool/tests/parse_name
tool/tests/parse_name.log
tool/tests/parse_name.o
tool/tests/parse_name.trs
tool/tests/req_9e.pem
tool/tests/test_inout
tool/tests/test_inout.log
tool/tests/test_inout.o
tool/tests/test_inout.trs
tool/tests/test-suite.log
coverage/
lib/error.gcno
lib/version.gcno
@@ -91,9 +111,11 @@ ykcs11/*.lo
ykcs11/ykcs11.pc
ykcs11/libykcs11.la
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.log
ykcs11/tests/ykcs11_tests.o
ykcs11/tests/ykcs11_tests.trs
ykcs11/tests/test-suite.log
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
if ENABLE_COV
cov-reset:
rm -fr coverage
@@ -63,6 +62,14 @@ endif
# 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:
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 || \
+15 -1
View File
@@ -1,6 +1,20 @@
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)
+2 -2
View File
@@ -67,8 +67,8 @@ situations, running ./configure should automatically find the proper
backend to use.
=== Building from Git
Recent versions of autoconf, automake, pkg-config and libtool must
be installed. Help2man is used to generate the manpages. Gengetopt
Recent versions of autoconf, automake, check, pkg-config, and libtool
must be installed. Help2man is used to generate the manpages. Gengetopt
version 2.22.6 or later is needed for command line parameter handling.
The
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 remove -qq -y $REMOVE
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
else
ARCH=osx
@@ -40,6 +40,7 @@ else
brew uninstall libtool
brew install libtool
brew install help2man
brew install check
brew install pkg-config
brew install gengetopt
brew install gnu-tar
+24 -4
View File
@@ -25,7 +25,7 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# 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_MACRO_DIR([m4])
@@ -33,9 +33,9 @@ AC_CONFIG_MACRO_DIR([m4])
# Interfaces changed/added/removed: CURRENT++ REVISION=0
# Interfaces added: AGE++
# Interfaces removed: AGE=0
AC_SUBST([LT_CURRENT], 4)
AC_SUBST([LT_REVISION], 7)
AC_SUBST([LT_AGE], 3)
AC_SUBST([LT_CURRENT], 5)
AC_SUBST([LT_REVISION], 0)
AC_SUBST([LT_AGE], 4)
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AM_SILENT_RULES([yes])
@@ -50,10 +50,30 @@ AM_MISSING_PROG(GENGETOPT, gengetopt, $missing_dir)
PKG_PROG_PKG_CONFIG
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_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],
[AS_HELP_STRING([--with-backend=ARG],
[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
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_includedir = $(includedir)/ykpiv
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
#define YKPIV_INTERNAL_H
#include "ykpiv.h"
#include <stdbool.h>
#if BACKEND_PCSC
@@ -42,14 +44,115 @@
#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 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 {
SCARDCONTEXT context;
SCARDHANDLE card;
int verbose;
char *pin;
ykpiv_allocator allocator;
bool isNEO;
};
union u_APDU {
@@ -65,9 +168,32 @@ union u_APDU {
};
typedef union u_APDU APDU;
typedef struct des_key des_key;
unsigned const char aid[] = {
0xa0, 0x00, 0x00, 0x03, 0x08
};
extern unsigned const char aid[];
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
+12 -5
View File
@@ -25,13 +25,20 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
AM_CFLAGS = $(WARN_CFLAGS)
AM_CPPFLAGS = -I$(top_srcdir)/lib -I$(top_builddir)/lib
AM_CFLAGS = $(WARN_CFLAGS) @CHECK_CFLAGS@ $(OPENSSL_CFLAGS)
AM_CPPFLAGS = -I$(top_srcdir)/lib -I$(top_builddir)/lib $(OPENSSL_CFLAGS) $(PCSC_CFLAGS)
AM_LDFLAGS = -no-install
LDADD = ../libykpiv.la
AM_LDFLAGS = @CHECK_LIBS@
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)
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 <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)
{
if(strcmp(YKPIV_VERSION_STRING, ykpiv_check_version (NULL)) != 0) {
printf("version mismatch %s != %s\n", YKPIV_VERSION_STRING,
ykpiv_check_version(NULL));
return EXIT_FAILURE;
}
int number_failed;
Suite *s;
SRunner *sr;
if(ykpiv_check_version(YKPIV_VERSION_STRING) == NULL) {
printf("version NULL?\n");
return EXIT_FAILURE;
}
if(ykpiv_check_version("99.99.99") != NULL) {
printf ("version not NULL?\n");
return EXIT_FAILURE;
}
printf ("ykpiv version: header %s library %s\n",
YKPIV_VERSION_STRING, ykpiv_check_version (NULL));
if(ykpiv_strerror(YKPIV_OK) == NULL) {
printf ("ykpiv_strerror NULL\n");
return EXIT_FAILURE;
}
{
const char *s;
s = ykpiv_strerror_name(YKPIV_OK);
if(s == NULL || strcmp(s, "YKPIV_OK") != 0) {
printf("ykpiv_strerror_name %s\n", s);
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
s = basic_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;
}
+37 -22
View File
@@ -32,6 +32,8 @@
#include <stdlib.h>
#include <string.h>
#include <check.h>
#include "ykpiv.h"
struct key {
@@ -57,30 +59,43 @@ static int parse_key(const char *text, const unsigned char *expected, int valid)
unsigned char key[24];
size_t len = sizeof(key);
ykpiv_rc res = ykpiv_hex_decode(text, strlen(text), key, &len);
if(res != YKPIV_OK && valid == 1) {
printf("key check failed for %s!\n", text);
return EXIT_FAILURE;
} else if(res != YKPIV_OK && valid == 0) {
return EXIT_SUCCESS;
if (valid) {
ck_assert(res == YKPIV_OK);
ck_assert(memcmp(expected, key, 24) == 0);
} else {
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;
}
int main(void) {
size_t i;
for(i = 0; i < sizeof(keys) / sizeof(struct key); i++) {
int res = parse_key(keys[i].text, keys[i].formatted, keys[i].valid);
if(res != EXIT_SUCCESS) {
return res;
}
}
return EXIT_SUCCESS;
START_TEST(test_parse_key) {
int res = parse_key(keys[_i].text, keys[_i].formatted, keys[_i].valid);
ck_assert(res == EXIT_SUCCESS);
}
END_TEST
Suite *parsekey_suite(void) {
Suite *s;
TCase *tc;
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
+702 -179
View File
File diff suppressed because it is too large Load Diff
+462 -4
View File
@@ -28,11 +28,20 @@
*
*/
/**
* @mainpage
*
* See ykpiv.h
*
* @file ykpiv.h
* libykpiv API
*/
#ifndef YKPIV_H
#define YKPIV_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <ykpiv-version.h>
@@ -58,12 +67,27 @@ extern "C"
YKPIV_INVALID_OBJECT = -11,
YKPIV_ALGORITHM_ERROR = -12,
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;
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_name(ykpiv_rc err);
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_connect(ykpiv_state *state, const char *wanted);
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,
size_t in_len, unsigned char *sign_out, size_t *out_len,
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,
size_t in_len, unsigned char *enc_out, size_t *out_len,
unsigned char algorithm, unsigned char key);
@@ -109,6 +130,423 @@ extern "C"
const unsigned char *qinv, size_t qinv_len,
const unsigned char *ec_data, unsigned char ec_data_len,
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_3DES 0x03
@@ -181,6 +619,8 @@ extern "C"
#define YKPIV_OBJ_ATTESTATION 0x5fff01
#define YKPIV_OBJ_MAX_SIZE 3072
#define YKPIV_INS_VERIFY 0x20
#define YKPIV_INS_CHANGE_REFERENCE 0x24
#define YKPIV_INS_RESET_RETRY 0x2c
@@ -188,6 +628,8 @@ extern "C"
#define YKPIV_INS_AUTHENTICATE 0x87
#define YKPIV_INS_GET_DATA 0xcb
#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 */
#define SW_SUCCESS 0x9000
@@ -196,8 +638,9 @@ extern "C"
#define SW_ERR_INCORRECT_PARAM 0x6a80
/* this is a custom sw for yubikey */
#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_IMPORT_KEY 0xfe
#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_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
}
#endif
+38
View File
@@ -75,3 +75,41 @@ global:
ykpiv_change_puk;
ykpiv_unblock_pin;
} 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)
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)/tool -I$(top_builddir)/tool
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)
test_inout_LDADD = ../libpiv_util.la
+95 -1
View File
@@ -1,4 +1,4 @@
#!/bin/sh
#!/bin/bash
# Copyright (c) 2014-2016 Yubico AB
# All rights reserved.
@@ -33,6 +33,7 @@
set -e
BIN="../yubico-piv-tool${EXEEXT}"
ROOT_MAKEFILE="../../Makefile"
HELP_OUTPUT=$($BIN --help)
@@ -42,3 +43,96 @@ if [ "x$VERSION_OUTPUT" != "x$expected" ]; then
echo "Version ($VERSION_OUTPUT) not matching expected output $expected."
exit 1
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 <stdbool.h>
@@ -39,18 +41,26 @@
#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];
BIO *bio;
const char none[] = {0};
X509_NAME *parsed = parse_name(name);
if(parsed == NULL) {
if(fail) {
return;
} else {
printf("Failed parsing of '%s'!\n", name);
exit(EXIT_FAILURE);
}
return false;
}
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);
X509_NAME_free(parsed);
if(strcmp(buf, expected) != 0) {
printf("Names not matching: '%s' != '%s'\n", expected, buf);
exit(EXIT_FAILURE);
fprintf(stderr, "Names not matching: '%s' != '%s'\n", expected, buf);
return false;
}
return true;
}
int main(void) {
test_name("/CN=test foo/", "CN = test foo", false);
test_name("/CN=test/OU=bar/O=EXAMPLE/", "CN = test, OU = bar, O = EXAMPLE", false);
test_name("/foo/", "", true);
test_name("/CN=test/foobar/", "", true);
test_name("/CN=test/foo=bar/", "", true);
return EXIT_SUCCESS;
START_TEST(test_parse_name) {
ck_assert(test_name(names[_i].name, names[_i].parsed_name) == names[_i].valid);
}
END_TEST
Suite *test_suite(void) {
Suite *s;
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 <stdio.h>
#include <string.h>
@@ -40,25 +42,58 @@
#define pipe(fds) _pipe(fds,4096, 0)
#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};
unsigned char buf2[sizeof(buf)];
int pipefd[2];
FILE *tmp1, *tmp2;
assert(pipe(pipefd) == 0);
if (pipe(pipefd) != 0)
return false;
tmp1 = fdopen(pipefd[1], "w");
dump_data(buf, sizeof(buf), tmp1, false, format);
fclose(tmp1);
tmp2 = fdopen(pipefd[0], "r");
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);
return true;
}
int main(void) {
test_inout(format_arg_base64);
test_inout(format_arg_hex);
test_inout(format_arg_binary);
exit(0);
START_TEST(test_inout) {
ck_assert(inout(formats[_i]));
}
END_TEST
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 "util.h"
FILE *open_file(const char *file_name, int mode) {
FILE *open_file(const char *file_name, enum file_mode mode) {
FILE *file;
const char *mod;
if(!strcmp(file_name, "-")) {
file = mode == INPUT ? stdin : stdout;
file = (mode == INPUT_TEXT || mode == INPUT_BIN) ? stdin : stdout;
} 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) {
fprintf(stderr, "Failed opening '%s'!\n", file_name);
return NULL;
@@ -255,172 +274,49 @@ int set_length(unsigned char *buffer, int length) {
}
}
int get_object_id(enum enum_slot slot) {
int object;
int get_slot_hex(enum enum_slot slot_enum) {
int slot = -1;
switch(slot) {
switch (slot_enum) {
case slot_arg_9a:
object = YKPIV_OBJ_AUTHENTICATION;
slot = 0x9a;
break;
case slot_arg_9c:
object = YKPIV_OBJ_SIGNATURE;
break;
case slot_arg_9d:
object = YKPIV_OBJ_KEY_MANAGEMENT;
break;
case slot_arg_9e:
object = YKPIV_OBJ_CARD_AUTH;
slot = 0x9c + ((int)slot_enum - (int)slot_arg_9c);
break;
case slot_arg_82:
object = YKPIV_OBJ_RETIRED1;
break;
case slot_arg_83:
object = YKPIV_OBJ_RETIRED2;
break;
case slot_arg_84:
object = YKPIV_OBJ_RETIRED3;
break;
case slot_arg_85:
object = YKPIV_OBJ_RETIRED4;
break;
case slot_arg_86:
object = YKPIV_OBJ_RETIRED5;
break;
case slot_arg_87:
object = YKPIV_OBJ_RETIRED6;
break;
case slot_arg_88:
object = YKPIV_OBJ_RETIRED7;
break;
case slot_arg_89:
object = YKPIV_OBJ_RETIRED8;
break;
case slot_arg_8a:
object = YKPIV_OBJ_RETIRED9;
break;
case slot_arg_8b:
object = YKPIV_OBJ_RETIRED10;
break;
case slot_arg_8c:
object = YKPIV_OBJ_RETIRED11;
break;
case slot_arg_8d:
object = YKPIV_OBJ_RETIRED12;
break;
case slot_arg_8e:
object = YKPIV_OBJ_RETIRED13;
break;
case slot_arg_8f:
object = YKPIV_OBJ_RETIRED14;
break;
case slot_arg_90:
object = YKPIV_OBJ_RETIRED15;
break;
case slot_arg_91:
object = YKPIV_OBJ_RETIRED16;
break;
case slot_arg_92:
object = YKPIV_OBJ_RETIRED17;
break;
case slot_arg_93:
object = YKPIV_OBJ_RETIRED18;
break;
case slot_arg_94:
object = YKPIV_OBJ_RETIRED19;
break;
case slot_arg_95:
object = YKPIV_OBJ_RETIRED20;
slot = 0x82 + ((int)slot_enum - (int)slot_arg_82);
break;
case slot_arg_f9:
object = YKPIV_OBJ_ATTESTATION;
slot = 0xf9;
break;
case slot__NULL:
default:
object = 0;
slot = -1;
}
return object;
}
int key_to_object_id(int key) {
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;
return slot;
}
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);
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);
char len_buf[5];
+8 -5
View File
@@ -37,8 +37,12 @@
#include "cmdline.h"
#define INPUT 1
#define OUTPUT 2
enum file_mode {
INPUT_TEXT,
OUTPUT_TEXT,
INPUT_BIN,
OUTPUT_BIN,
};
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);
@@ -46,9 +50,8 @@ int set_length(unsigned char*, int);
int get_length(const unsigned char*, int*);
X509_NAME *parse_name(const char*);
unsigned char get_algorithm(EVP_PKEY*);
FILE *open_file(const char*, int);
int get_object_id(enum enum_slot slot);
int key_to_object_id(int key);
FILE *open_file(const char *file_name, enum file_mode mode);
int get_slot_hex(enum enum_slot slot_enum);
bool set_component(unsigned char *in_ptr, const BIGNUM *bn, int element_len);
bool prepare_rsa_signature(const unsigned char*, unsigned int, unsigned char*,
unsigned int*, int);
+159 -297
View File
@@ -50,26 +50,8 @@
#include "cmdline.h"
#include "util.h"
/* FASC-N containing S9999F9999F999999F0F1F0000000000300001E encoded in
* 4-bit BCD with 1 bit parity. run through the tools/fasc.pl script to get
* 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 MAX(a,b) (a) > (b) ? (a) : (b)
#define MIN(a,b) (a) < (b) ? (a) : (b)
#define CHUID 0
#define CCC 1
@@ -78,9 +60,35 @@ unsigned const char ccc_tmpl[] = {
#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) {
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) {
return;
}
@@ -115,167 +123,89 @@ static bool sign_data(ykpiv_state *state, const unsigned char *in, size_t len, u
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_key_format key_format, enum enum_pin_policy pin_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;
FILE *output_file = NULL;
bool ret = false;
ykpiv_rc res;
FILE *output_file = NULL;
EVP_PKEY *public_key = NULL;
RSA *rsa = NULL;
BIGNUM *bignum_n = NULL;
BIGNUM *bignum_e = NULL;
EC_KEY *eckey = NULL;
EC_POINT *point = NULL;
char version[7];
EC_POINT *ecpoint = NULL;
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) {
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;
}
}
key = get_slot_hex(slot);
sscanf(slot, "%2x", &key);
templ[3] = key;
output_file = open_file(output_file_name, OUTPUT);
output_file = open_file(output_file_name, key_file_mode(key_format, true));
if(!output_file) {
return false;
}
*in_ptr++ = 0xac;
*in_ptr++ = 3;
*in_ptr++ = YKPIV_ALGO_TAG;
*in_ptr++ = 1;
*in_ptr++ = get_piv_algorithm(algorithm);
if(in_data[4] == 0) {
fprintf(stderr, "Unexpected algorithm.\n");
goto generate_out;
}
if(pin_policy != pin_policy__NULL) {
in_data[1] += 3;
*in_ptr++ = YKPIV_PINPOLICY_TAG;
*in_ptr++ = 1;
*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);
}
res = ykpiv_util_generate_key(state,
(uint8_t)(key & 0xFF),
get_piv_algorithm(algorithm),
get_pin_policy(pin_policy),
get_touch_policy(touch_policy),
&mod,
&mod_len,
&exp,
&exp_len,
&point,
&point_len);
if (res != YKPIV_OK) {
fprintf(stderr, "Key generation failed.\n");
goto generate_out;
}
if(key_format == key_format_arg_PEM) {
public_key = EVP_PKEY_new();
if(algorithm == algorithm_arg_RSA1024 || algorithm == algorithm_arg_RSA2048) {
unsigned char *data_ptr = data + 5;
int len = 0;
rsa = RSA_new();
if(*data_ptr != 0x81) {
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) {
rsa->n = BN_bin2bn(mod, mod_len, NULL);
if (rsa->n == NULL) {
fprintf(stderr, "Failed to parse public key modulus.\n");
goto generate_out;
}
data_ptr += len;
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) {
rsa->e = BN_bin2bn(exp, exp_len, NULL);
if(rsa->e == NULL) {
fprintf(stderr, "Failed to parse public key exponent.\n");
goto generate_out;
}
rsa->n = bignum_n;
rsa->e = bignum_e;
EVP_PKEY_set1_RSA(public_key, rsa);
} else if(algorithm == algorithm_arg_ECCP256 || algorithm == algorithm_arg_ECCP384) {
EC_GROUP *group;
unsigned char *data_ptr = data + 3;
int nid;
size_t len;
if(algorithm == algorithm_arg_ECCP256) {
nid = NID_X9_62_prime256v1;
len = 65;
} else {
nid = NID_secp384r1;
len = 97;
}
eckey = EC_KEY_new();
group = EC_GROUP_new_by_curve_name(nid);
EC_GROUP_set_asn1_flag(group, nid);
EC_KEY_set_group(eckey, group);
point = EC_POINT_new(group);
if(*data_ptr++ != 0x86) {
fprintf(stderr, "Failed to parse public key structure.\n");
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)) {
ecpoint = EC_POINT_new(group);
if(!EC_POINT_oct2point(group, ecpoint, point, point_len, NULL)) {
fprintf(stderr, "Failed to load public point.\n");
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");
goto generate_out;
}
EVP_PKEY_set1_EC_KEY(public_key, eckey);
} else {
fprintf(stderr, "Wrong algorithm.\n");
goto generate_out;
}
PEM_write_PUBKEY(output_file, public_key);
ret = true;
@@ -285,65 +215,53 @@ static bool generate_key(ykpiv_state *state, const char *slot,
}
generate_out:
if(output_file != stdout) {
if (output_file != stdout) {
fclose(output_file);
}
if(point) {
EC_POINT_free(point);
if (ecpoint) {
EC_POINT_free(ecpoint);
}
if(eckey) {
if (eckey) {
EC_KEY_free(eckey);
}
if(rsa) {
if (rsa) {
RSA_free(rsa);
}
if(public_key) {
if (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;
}
static bool reset(ykpiv_state *state) {
unsigned char templ[] = {0, YKPIV_INS_RESET, 0, 0};
unsigned char data[0xff];
unsigned long recv_len = sizeof(data);
int sw;
/* 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;
return ykpiv_util_reset(state) == YKPIV_OK;
}
static bool set_pin_retries(ykpiv_state *state, int pin_retries, int puk_retries, int verbose) {
unsigned char templ[] = {0, YKPIV_INS_SET_PIN_RETRIES, pin_retries, puk_retries};
unsigned char data[0xff];
unsigned long recv_len = sizeof(data);
int sw;
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;
}
ykpiv_rc res;
if(verbose) {
fprintf(stderr, "Setting pin retries to %d and puk retries to %d.\n", pin_retries, puk_retries);
}
if(ykpiv_transfer_data(state, templ, NULL, 0, data, &recv_len, &sw) != YKPIV_OK) {
return false;
} else if(sw == SW_SUCCESS) {
return true;
res = ykpiv_set_pin_retries(state, pin_retries, puk_retries);
if (res == YKPIV_RANGE_ERROR) {
fprintf(stderr, "pin and puk retries must be between 1 and 255.\n");
}
return false;
return res == YKPIV_OK;
}
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) {
int key = 0;
FILE *input_file = NULL;
@@ -353,9 +271,9 @@ static bool import_key(ykpiv_state *state, enum enum_key_format key_format,
bool ret = false;
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) {
return false;
}
@@ -518,7 +436,7 @@ static bool import_cert(ykpiv_state *state, enum enum_key_format cert_format,
int compress = 0;
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) {
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;
int object = get_object_id(slot);
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 (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");
goto import_cert_out;
}
certptr += cert_len;
} else {
/* i2d_X509 increments certptr here.. */
i2d_X509(cert, &certptr);
}
*certptr++ = 0x71;
*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) {
if ((res = ykpiv_util_write_cert(state, get_slot_hex(slot), certdata, cert_len, compress)) != YKPIV_OK) {
fprintf(stderr, "Failed commands with device: %s\n", ykpiv_strerror(res));
} else {
ret = true;
@@ -620,45 +521,32 @@ import_cert_out:
return ret;
}
static bool set_dataobject(ykpiv_state *state, int verbose, int type) {
unsigned char obj[1024];
static bool set_cardid(ykpiv_state *state, int verbose, int type) {
ykpiv_rc res;
size_t offs, rand_len, len;
const unsigned char *tmpl;
int id;
unsigned char id[MAX(sizeof(ykpiv_cardid), sizeof(ykpiv_cccid))];
if(type == CHUID) {
offs = CHUID_GUID_OFFS;
len = sizeof(chuid_tmpl);
rand_len = 0x10;
tmpl = chuid_tmpl;
id = YKPIV_OBJ_CHUID;
res = ykpiv_util_set_cardid(state, NULL);
} else {
offs = CCC_ID_OFFS;
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;
res = ykpiv_util_set_cccid(state, NULL);
}
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,
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) {
X509_REQ *req = 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.value.ptr = NULL;
sscanf(slot, "%2x", &key);
key = get_slot_hex(slot);
input_file = open_file(input_file_name, INPUT);
output_file = open_file(output_file_name, OUTPUT);
input_file = open_file(input_file_name, key_file_mode(key_format, false));
output_file = open_file(output_file_name, key_file_mode(key_format, true));
if(!input_file || !output_file) {
goto request_out;
}
@@ -805,7 +693,7 @@ request_out:
}
static bool selfsign_certificate(ykpiv_state *state, enum enum_key_format key_format,
const char *input_file_name, const char *slot, char *subject, 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) {
FILE *input_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.value.ptr = NULL;
sscanf(slot, "%2x", &key);
key = get_slot_hex(slot);
input_file = open_file(input_file_name, INPUT);
output_file = open_file(output_file_name, OUTPUT);
input_file = open_file(input_file_name, key_file_mode(key_format, false));
output_file = open_file(output_file_name, key_file_mode(key_format, true));
if(!input_file || !output_file) {
goto selfsign_out;
}
@@ -1075,56 +963,45 @@ static bool change_pin(ykpiv_state *state, enum enum_action action, const char *
}
static bool delete_certificate(ykpiv_state *state, enum enum_slot slot) {
int object = get_object_id(slot);
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;
}
return ykpiv_util_delete_cert(state, get_slot_hex(slot)) == YKPIV_OK;
}
static bool read_certificate(ykpiv_state *state, enum enum_slot slot,
enum enum_key_format key_format, const char *output_file_name) {
FILE *output_file;
int object = get_object_id(slot);
unsigned char data[3072];
const unsigned char *ptr = data;
unsigned long len = sizeof(data);
int cert_len;
bool ret = false;
uint8_t *data = NULL;
const unsigned char *ptr = 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_SSH) {
fprintf(stderr, "Only PEM, DER and SSH format are supported for read-certificate.\n");
return false;
}
output_file = open_file(output_file_name, OUTPUT);
if(!output_file) {
output_file = open_file(output_file_name, key_file_mode(key_format, true));
if (!output_file) {
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");
goto read_cert_out;
}
ptr = data;
if(*ptr++ == 0x70) {
ptr += get_length(ptr, &cert_len);
if(key_format == key_format_arg_PEM ||
if (key_format == key_format_arg_PEM ||
key_format == key_format_arg_SSH) {
x509 = X509_new();
if(!x509) {
if (!x509) {
fprintf(stderr, "Failed allocating x509 structure.\n");
goto read_cert_out;
}
x509 = d2i_X509(NULL, &ptr, cert_len);
if(!x509) {
x509 = d2i_X509(NULL, (const unsigned char**)&ptr, cert_len);
if (!x509) {
fprintf(stderr, "Failed parsing x509 information.\n");
goto read_cert_out;
}
@@ -1145,22 +1022,22 @@ static bool read_certificate(ykpiv_state *state, enum enum_slot slot,
fwrite(ptr, (size_t)cert_len, 1, output_file);
ret = true;
}
} else {
fprintf(stderr, "Failed parsing data.\n");
}
read_cert_out:
if(output_file != stdout) {
if (output_file != stdout) {
fclose(output_file);
}
if(x509) {
if (x509) {
X509_free(x509);
}
if (data) {
ykpiv_util_free(state, data);
}
return ret;
}
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) {
FILE *input_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;
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) {
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");
}
output_file = open_file(output, OUTPUT);
output_file = open_file(output, OUTPUT_BIN);
if(!output_file) {
if(input_file && input_file != stdin) {
fclose(input_file);
@@ -1253,7 +1130,7 @@ out:
static void print_cert_info(ykpiv_state *state, enum enum_slot slot, const EVP_MD *md,
FILE *output) {
int object = get_object_id(slot);
int object = ykpiv_util_slot_object(get_slot_hex(slot));
int slot_name;
unsigned char data[3072];
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;
}
if (slot == slot_arg_9a)
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);
slot_name = get_slot_hex(slot);
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];
long unsigned len = sizeof(buf);
int i;
FILE *output_file = open_file(output_file_name, OUTPUT);
FILE *output_file = open_file(output_file_name, OUTPUT_TEXT);
if(!output_file) {
return false;
}
@@ -1421,7 +1293,7 @@ static bool test_signature(ykpiv_state *state, enum enum_slot slot,
unsigned int data_len;
X509 *x509 = NULL;
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) {
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) {
goto test_out;
}
sscanf(cmdline_parser_slot_values[slot], "%2x", &key);
key = get_slot_hex(slot);
if(YKPIV_IS_RSA(algorithm)) {
prepare_rsa_signature(data, data_len, encoded, &enc_len, EVP_MD_type(md));
ptr = encoded;
@@ -1554,7 +1426,7 @@ static bool test_decipher(ykpiv_state *state, enum enum_slot slot,
X509 *x509 = NULL;
EVP_PKEY *pubkey;
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) {
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) {
goto decipher_out;
}
sscanf(cmdline_parser_slot_values[slot], "%2x", &key);
key = get_slot_hex(slot);
if(YKPIV_IS_RSA(algorithm)) {
unsigned char secret[32];
unsigned char secret2[32];
@@ -1703,37 +1575,29 @@ static bool list_readers(ykpiv_state *state) {
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) {
unsigned char data[2048];
unsigned long len = sizeof(data);
bool ret = false;
X509 *x509 = NULL;
unsigned char templ[] = {0, YKPIV_INS_ATTEST, 0, 0};
int key;
int sw;
FILE *output_file = open_file(output_file_name, OUTPUT);
FILE *output_file = open_file(output_file_name, key_file_mode(key_format, true));
if(!output_file) {
return false;
}
sscanf(slot, "%2x", &key);
templ[2] = key;
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");
return false;
}
if(ykpiv_transfer_data(state, templ, NULL, 0, data, &len, &sw) != YKPIV_OK) {
fprintf(stderr, "Failed to communicate.\n");
goto attest_out;
} else if(sw != SW_SUCCESS) {
fprintf(stderr, "Failed to attest key.\n");
key = get_slot_hex(slot);
if (ykpiv_attest(state, key, data, &len) != YKPIV_OK) {
fprintf(stderr, "Failed to attest data.\n");
goto attest_out;
}
if(data[0] == 0x30) {
if(key_format == key_format_arg_PEM) {
const unsigned char *ptr = data;
int len2 = len;
@@ -1748,12 +1612,10 @@ static bool attest(ykpiv_state *state, const char *slot,
goto attest_out;
}
PEM_write_X509(output_file, x509);
ret = true;
} else {
fwrite(data, len, 1, output_file);
}
ret = true;
}
attest_out:
if(output_file != stdout) {
@@ -1773,7 +1635,7 @@ static bool write_object(ykpiv_state *state, int id,
size_t len = sizeof(data);
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) {
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);
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) {
return false;
}
@@ -1875,7 +1737,7 @@ int main(int argc, char *argv[]) {
}
break;
case action_arg_pinMINUS_retries:
if(!args_info.pin_retries_arg || !args_info.puk_retries_arg) {
if(!args_info.pin_retries_given || !args_info.puk_retries_given) {
fprintf(stderr, "The '%s' action needs both --pin-retries and --puk-retries arguments.\n",
cmdline_parser_action_values[action]);
return EXIT_FAILURE;
@@ -2013,7 +1875,7 @@ int main(int argc, char *argv[]) {
print_version(state, args_info.output_arg);
break;
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) {
ret = EXIT_FAILURE;
} else {
@@ -2068,7 +1930,7 @@ int main(int argc, char *argv[]) {
}
break;
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) {
fprintf(stderr, "Unable to import private key\n");
ret = EXIT_FAILURE;
@@ -2085,7 +1947,7 @@ int main(int argc, char *argv[]) {
break;
case action_arg_setMINUS_ccc:
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;
} else {
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;
case action_arg_requestMINUS_certificate:
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) {
ret = EXIT_FAILURE;
} else {
@@ -2153,7 +2015,7 @@ int main(int argc, char *argv[]) {
}
case action_arg_selfsignMINUS_certificate:
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.output_arg) == false) {
ret = EXIT_FAILURE;
@@ -2207,7 +2069,7 @@ int main(int argc, char *argv[]) {
}
break;
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) {
ret = EXIT_FAILURE;
}
@@ -2228,7 +2090,7 @@ int main(int argc, char *argv[]) {
ret = EXIT_FAILURE;
}
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)) {
fprintf(stderr, "Signature successful!\n");
} 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
OPENSSLVERSION=1.0.2l
CHECKVERSION=0.12.0
all: usage 32bit 64bit
@@ -55,11 +56,18 @@ doit:
rm $(PWD)/tmp$(ARCH)/root/bin/c_rehash && \
rm -rf $(PWD)/tmp$(ARCH)/root/lib/engines/ && \
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 . && \
tar xfa $(PACKAGE)-$(VERSION).tar.gz && \
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 && \
make install $(CHECK) && \
WINEPATH="/usr/$(HOST)/lib/" make install $(CHECK) && \
rm $(PWD)/tmp$(ARCH)/root/lib/*.la && \
rm -rf $(PWD)/tmp$(ARCH)/root/lib/pkgconfig/ && \
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)
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);
if (len < 0)
+9 -3
View File
@@ -29,17 +29,23 @@
#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)/ykcs11 -I$(top_builddir)/ykcs11
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)
check_PROGRAMS = ykcs11_tests
TESTS = $(check_PROGRAMS)
TESTS = reset.sh $(check_PROGRAMS)
if ENABLE_COV
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/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) {
unsigned int 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_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_FLAGS TOKEN_FLAGS = CKF_RNG | CKF_LOGIN_REQUIRED | CKF_USER_PIN_INITIALIZED | CKF_TOKEN_INITIALIZED;
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.ulTotalPrivateMemory, CK_UNAVAILABLE_INFORMATION, "TOTAL_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.minor, HW.minor, "HW_MIN");
if (info.firmwareVersion.major != 4 && info.firmwareVersion.major != 0)
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");
return 0;
}
static void test_mechanism_list_and_info() {
@@ -627,6 +641,15 @@ static void test_import_and_sign_all_10_RSA() {
}
#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) {
get_functions(&funcs);
@@ -634,8 +657,15 @@ int main(void) {
test_lib_info();
#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_token_info();
// Require YK4 to continue. Skip if different model found.
if (test_token_info() != 0)
exit(77);
test_mechanism_list_and_info();
test_session();
test_login();
@@ -648,3 +678,5 @@ int main(void) {
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;
// 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_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;
memset(&t_info->hardwareVersion, 0, sizeof(t_info->hardwareVersion));
// Ignore hardware version, report firmware version
if (token.get_token_version(state, &t_info->firmwareVersion) != CKR_OK) {
ykpiv_disconnect(state);
@@ -257,8 +258,6 @@ void strip_DER_encoding_from_ECSIG(CK_BYTE_PTR data, CK_ULONG_PTR len) {
data_ptr++;
memcpy(buf_ptr, data_ptr, elem_len);
data_ptr += elem_len;
buf_ptr += elem_len;
*len = sig_halflen * 2;
memcpy(data, buf, *len);
+6
View File
@@ -215,6 +215,11 @@ CK_DEFINE_FUNCTION(CK_RV, C_GetSlotList)(
return CKR_OK;
}
if (!pulCount) {
DOUT;
return CKR_ARGUMENTS_BAD;
}
if ((tokenPresent && *pulCount < n_slots_with_token) || (!tokenPresent && *pulCount < n_slots)) {
DBG("Buffer too small: needed %lu, provided %lu", n_slots, *pulCount);
return CKR_BUFFER_TOO_SMALL;
@@ -1214,6 +1219,7 @@ CK_DEFINE_FUNCTION(CK_RV, C_DestroyObject)(
rv = delete_cert(cert_id);
if (rv != CKR_OK) {
free(obj_ptr);
DBG("Unable to delete certificate data");
return CKR_FUNCTION_FAILED;
}
+3 -3
View File
@@ -32,16 +32,16 @@
#include "pkcs11.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_VERSION slot_version = {1, 0};
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;
memcpy(str, slot_manufacturer, strlen(slot_manufacturer));
memcpy(str, slot_manufacturer, strlen((const char*)slot_manufacturer));
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");
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) {
case YKPIV_OK:
return CKR_OK;