From bf5cda9af9ed75ae83a716e31109e68bc4663680 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Mon, 18 Nov 2019 15:47:04 -0800 Subject: [PATCH] Remove legacy C implementation This repository contains the entire history of the C source code since the project started. However, the code is largely translated at this point, so keeping the C code around at this point no longer makes sense. If we need to consult it or the history of changes, git has them. --- src/error.c | 106 --- src/internal.c | 612 ------------- src/internal.h | 258 ------ src/util.c | 1681 ---------------------------------- src/version.c | 143 --- src/ykpiv-version.h | 92 -- src/ykpiv-version.h.in | 92 -- src/ykpiv.c | 1978 ---------------------------------------- src/ykpiv.h | 702 -------------- 9 files changed, 5664 deletions(-) delete mode 100644 src/error.c delete mode 100644 src/internal.c delete mode 100644 src/internal.h delete mode 100644 src/util.c delete mode 100644 src/version.c delete mode 100644 src/ykpiv-version.h delete mode 100644 src/ykpiv-version.h.in delete mode 100644 src/ykpiv.c delete mode 100644 src/ykpiv.h diff --git a/src/error.c b/src/error.c deleted file mode 100644 index 2e19cd5..0000000 --- a/src/error.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * 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 - -#define ERR(name, desc) { name, #name, desc } - -typedef struct -{ - ykpiv_rc rc; - const char *name; - const char *description; -} err_t; - -static const err_t *errors = { - ERR (YKPIV_OK, "Successful return"), - ERR (YKPIV_MEMORY_ERROR, "Error allocating memory"), - ERR (YKPIV_PCSC_ERROR, "Error in PCSC call"), - ERR (YKPIV_SIZE_ERROR, "Wrong buffer size"), - ERR (YKPIV_APPLET_ERROR, "No PIV application found"), - ERR (YKPIV_AUTHENTICATION_ERROR, "Error during authentication"), - ERR (YKPIV_RANDOMNESS_ERROR, "Error getting randomness"), - ERR (YKPIV_GENERIC_ERROR, "Something went wrong."), - ERR (YKPIV_KEY_ERROR, "Error in key"), - ERR (YKPIV_PARSE_ERROR, "Parse error"), - ERR (YKPIV_WRONG_PIN, "Wrong PIN code"), - ERR (YKPIV_INVALID_OBJECT, "Object invalid"), - ERR (YKPIV_ALGORITHM_ERROR, "Algorithm error"), -}; - -/** - * ykpiv_strerror: - * @err: error code - * - * Convert return code to human readable string explanation of the - * reason for the particular error code. - * - * This string can be used to output a diagnostic message to the user. - * - * Return value: Returns a pointer to a statically allocated string - * containing an explanation of the error code @err. - **/ -const char *ykpiv_strerror(ykpiv_rc err) { - static const char *unknown = "Unknown ykpiv error"; - const char *p; - - if (-err < 0 || -err >= (int) (sizeof (errors) / sizeof (errors[0]))) - return unknown; - - p = errors[-err].description; - if (!p) - p = unknown; - - return p; -} - - -/** - * ykpiv_strerror_name: - * @err: error code - * - * Convert return code to human readable string representing the error - * code symbol itself. For example, ykpiv_strerror_name(%YKPIV_OK) - * returns the string "YKPIV_OK". - * - * This string can be used to output a diagnostic message to the user. - * - * Return value: Returns a pointer to a statically allocated string - * containing a string version of the error code @err, or NULL if - * the error code is not known. - **/ -const char *ykpiv_strerror_name(ykpiv_rc err) { - if (-err < 0 || -err >= (int) (sizeof (errors) / sizeof (errors[0]))) - return NULL; - - return errors[-err].name; -} diff --git a/src/internal.c b/src/internal.c deleted file mode 100644 index f85acf8..0000000 --- a/src/internal.c +++ /dev/null @@ -1,612 +0,0 @@ -#ifdef _WIN32 -#include -#ifdef _MSC_VER -#define strcasecmp _stricmp -#endif -#else -#include -#include -#endif - -/* the _WINDOWS define really means Windows native crypto-api/CNG */ -#ifdef _WINDOWS -#include -#include -#else -#include -#include -#include -#endif - -#include -#include -#include -#include - -#ifdef _WIN32 -#include /* must be included after openssl headers */ -#endif - -#include "internal.h" - -/* -** Definitions -*/ - -/* crypt defines */ - -#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 - -/* config defines */ - -#ifdef _WIN32 -#define _CONFIG_REGKEY "Software\\Yubico\\yubikeypiv" -#else -#define _CONFIG_FILE "/etc/yubico/yubikeypiv.conf" -#endif - -#define _ENV_PREFIX "YUBIKEY_PIV_" - -char *_strip_ws(char *sz); -setting_bool_t _get_bool_config(const char *sz_setting); -setting_bool_t _get_bool_env(const char *sz_setting); - -/* log */ - -const char *szLOG_SOURCE = "YubiKey PIV Library"; - -/* -** 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) { - yc_memzero(pbSessionBlob, cbSessionBlob); - 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); - -#else - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wcast-qual" - /* openssl returns void */ - DES_ecb3_encrypt((const_DES_cblock *)in, (DES_cblock*)out, &(key->ks1), &(key->ks2), &(key->ks3), 1); -#pragma GCC diagnostic pop - -#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 - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wcast-qual" - /* openssl returns void */ - DES_ecb3_encrypt((const_DES_cblock*)in, (DES_cblock*)out, &(key->ks1), &(key->ks2), &(key->ks3), 0); -#pragma GCC diagnostic pop - -#endif - -EXIT: - return rc; -} - -bool yk_des_is_weak_key(const unsigned char *key, const size_t cb_key) { -#ifdef _WINDOWS - bool rv = false; - /* 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))) { - rv = true; - break; - } - } - - yc_memzero(tmp, DES_LEN_3DES); - return rv; -#else - (void)cb_key; /* unused */ - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wcast-qual" - return DES_is_weak_key((const_DES_cblock *)key); -#pragma GCC diagnostic pop -#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_bytes(buffer, cb_req)) { - rc = PRNG_GENERAL_ERROR; - } - -#endif - - return rc; -} - -pkcs5_rc pkcs5_pbkdf2_sha1(const uint8_t* password, const size_t cb_password, const uint8_t* salt, const size_t cb_salt, uint64_t iterations, const uint8_t* 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)) { - /* suppress const qualifier warning b/c BCrypt doesn't take const input buffers */ -#pragma warning(suppress: 4090) - 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 - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wcast-qual" - /* for some reason openssl always returns 1 for PBKDF2 */ - PKCS5_PBKDF2_HMAC_SHA1((const char*)password, cb_password, salt, cb_salt, iterations, cb_key, (unsigned char*)key); -#pragma GCC diagnostic pop - -#endif - - return rc; -} - -/* settings */ - -char *_strip_ws(char *sz) { - char *psz_head = sz; - char *psz_tail = sz + strlen(sz) - 1; - - /* strip leading whitespace */ - while (isspace(*psz_head)) { - psz_head++; - } - - /* strip trailing whitespace */ - while ((psz_tail >= psz_head) && isspace(*psz_tail)) { - *psz_tail-- = '\0'; - } - - return psz_head; -} - -setting_bool_t _get_bool_config(const char *sz_setting) { - setting_bool_t setting = { false, SETTING_SOURCE_DEFAULT }; - -#ifdef _WIN32 - HKEY hKey = 0; - DWORD dwValue = 0; - DWORD dwType = 0; - DWORD cbValue = sizeof(dwValue); - - /* MINGW doesn't define RRF_SUBKEY_WOW6464KEY for RegGetValue, so read the traditional way */ - if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, _CONFIG_REGKEY, 0, KEY_READ | KEY_WOW64_64KEY, &hKey) == 0) { - if (RegQueryValueExA(hKey, sz_setting, NULL, &dwType, (LPBYTE)&dwValue, &cbValue) == 0) { - setting.value = ((dwType == REG_DWORD) && (dwValue == 1)); - setting.source = SETTING_SOURCE_ADMIN; - } - RegCloseKey(hKey); - hKey = 0; - } - -#else - /* read from config file*/ - char sz_line[256]; - char *psz_name = 0; - char *psz_value = 0; - char sz_name[256]; /* XXX REMEMBER TO ZERO */ - char sz_value[256]; /* XXX REMEMBER TO ZERO */ - FILE *pf = 0; - - if ((pf = fopen(_CONFIG_FILE, "r"))) { - while (!feof(pf)) { - if (fgets(sz_line, sizeof(sz_line), pf)) { - if (sz_line[0] == '#') continue; - if (sz_line[0] == '\r') continue; - if (sz_line[0] == '\n') continue; - - if (sscanf(sz_line, "%255[^=]=%255s", sz_name, sz_value) == 2) { - /* strip leading/trailing whitespace */ - psz_name = _strip_ws(sz_name); - - if (!strcasecmp(psz_name, sz_setting)) { - psz_value = _strip_ws(sz_value); - - setting.source = SETTING_SOURCE_ADMIN; - setting.value = (!strcmp(psz_value, "1") || !strcasecmp(psz_value, "true")); - break; - } - } - } - } - fclose(pf); - } - -#endif - - return setting; -} - -setting_bool_t _get_bool_env(const char *sz_setting) { - setting_bool_t setting = { false, SETTING_SOURCE_DEFAULT }; - char *psz_value = NULL; - char sz_name[256]; /* XXX REMEMBER TO ZERO */ - - snprintf(sz_name, sizeof(sz_name) - 1, "%s%s", _ENV_PREFIX, sz_setting); - - /* MINGW does not implement getenv_s, only _wgetenv_s */ -#ifdef _MSC_VER - size_t cb_value = 0; - char sz_value[100] = { 0 }; - - if ((getenv_s(&cb_value, sz_value, sizeof(sz_value) - 1, sz_name) == 0) && (cb_value > 0)) { - psz_value = sz_value; - } - -#else - psz_value = getenv(sz_name); - -#endif - - if (psz_value) { - setting.source = SETTING_SOURCE_USER; - setting.value = (!strcmp(psz_value, "1") || !strcasecmp(psz_value, "true")); - } - - return setting; -} - -setting_bool_t setting_get_bool(const char *sz_setting, bool def) { - setting_bool_t setting = { def, SETTING_SOURCE_DEFAULT }; - - setting = _get_bool_config(sz_setting); - - if (setting.source == SETTING_SOURCE_DEFAULT) { - setting = _get_bool_env(sz_setting); - } - - if (setting.source == SETTING_SOURCE_DEFAULT) { - setting.value = def; - } - - return setting; -} diff --git a/src/internal.h b/src/internal.h deleted file mode 100644 index 6a8493b..0000000 --- a/src/internal.h +++ /dev/null @@ -1,258 +0,0 @@ -/* - * 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. - * - */ - -#ifndef YKPIV_INTERNAL_H -#define YKPIV_INTERNAL_H - -#include "ykpiv.h" - -#include - -#if BACKEND_PCSC -#if defined HAVE_PCSC_WINSCARD_H -# include -# include -#else -# include -#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 3063 - -#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 CB_PIN_MAX 8 -#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; - -typedef struct _ykpiv_version_t { - uint8_t major; - uint8_t minor; - uint8_t patch; -} ykpiv_version_t; - -struct ykpiv_state { - SCARDCONTEXT context; - SCARDHANDLE card; - int verbose; - char *pin; - ykpiv_allocator allocator; - bool isNEO; - ykpiv_version_t ver; - uint32_t serial; -}; - -union u_APDU { - struct { - unsigned char cla; - unsigned char ins; - unsigned char p1; - unsigned char p2; - unsigned char lc; - unsigned char data[0xff]; - } st; - unsigned char raw[0xff + 5]; -}; - -typedef union u_APDU APDU; -typedef struct des_key des_key; - -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 uint8_t* password, const size_t cb_password, const uint8_t* salt, const size_t cb_salt, uint64_t iterations, const uint8_t* 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); -ykpiv_rc _ykpiv_select_application(ykpiv_state *state); -unsigned int _ykpiv_set_length(unsigned char *buffer, size_t length); -unsigned int _ykpiv_get_length(const unsigned char *buffer, size_t *len); -bool _ykpiv_has_valid_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); -ykpiv_rc _send_data(ykpiv_state *state, APDU *apdu, unsigned char *data, uint32_t *recv_len, int *sw); -ykpiv_rc _ykpiv_transfer_data( - ykpiv_state *state, - const unsigned char *templ, - const unsigned char *in_data, - long in_len, - unsigned char *out_data, - unsigned long *out_len, - int *sw); - -/* authentication functions not ready for public api */ -ykpiv_rc ykpiv_auth_getchallenge(ykpiv_state *state, uint8_t *challenge, const size_t challenge_len); -ykpiv_rc ykpiv_auth_verifyresponse(ykpiv_state *state, uint8_t *response, const size_t response_len); -ykpiv_rc ykpiv_auth_deauthenticate(ykpiv_state *state); - -typedef enum _setting_source_t { - SETTING_SOURCE_USER, - SETTING_SOURCE_ADMIN, - SETTING_SOURCE_DEFAULT -} setting_source_t; - -typedef struct _setting_bool_t { - bool value; - setting_source_t source; -} setting_bool_t; - -setting_bool_t setting_get_bool(const char *sz_setting, bool f_default); - -typedef enum _yc_log_level_t { - YC_LOG_LEVEL_ERROR, - YC_LOG_LEVEL_WARN, - YC_LOG_LEVEL_INFO, - YC_LOG_LEVEL_VERBOSE, - YC_LOG_LEVEL_DEBUG -} yc_log_level_t; - -#ifdef _WIN32 -#include -#define yc_memzero SecureZeroMemory -#elif defined(BSD) -#include -#define yc_memzero explicit_bzero -#elif defined(__linux__) -#include -#define yc_memzero OPENSSL_cleanse -#else -#define __STDC_WANT_LIB_EXT1__ 1 -#include -#define yc_memzero(_p, _n) (void)memset_s(_p, (rsize_t)_n, 0, (rsize_t)_n) -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/util.c b/src/util.c deleted file mode 100644 index 07aa15d..0000000 --- a/src/util.c +++ /dev/null @@ -1,1681 +0,0 @@ - /* - * 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 -#include -#include -#include -#include -#include - -#include "internal.h" -#include "ykpiv.h" - -#define MAX(a,b) (a) > (b) ? (a) : (b) -#define MIN(a,b) (a) < (b) ? (a) : (b) - - -#define SIZEOF_CHUID_TMPL 59 -#define SIZEOF_CCC_TMPL 51 - -/* - * Format defined in SP-800-73-4, Appendix A, Table 9 - * - * 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. - * - * Defined fields: - * - 0x30: FASC-N (hard-coded) - * - 0x34: Card UUID / GUID (settable) - * - 0x35: Exp. Date (hard-coded) - * - 0x3e: Signature (hard-coded, empty) - * - 0xfe: Error Detection Code (hard-coded) - */ -const uint8_t *CHUID_TMPL = { - 0x30, 0x19, 0xd4, 0xe7, 0x39, 0xda, 0x73, 0x9c, 0xed, 0x39, 0xce, 0x73, 0x9d, - 0x83, 0x68, 0x58, 0x21, 0x08, 0x42, 0x10, 0x84, 0x21, 0xc8, 0x42, 0x10, 0xc3, - 0xeb, 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 - -// f0: Card Identifier -// - 0xa000000116 == GSC-IS RID -// - 0xff == Manufacturer ID (dummy) -// - 0x02 == Card type (javaCard) -// - next 14 bytes: card ID -const uint8_t *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 - -static ykpiv_rc _read_certificate(ykpiv_state *state, uint8_t slot, uint8_t *buf, size_t *buf_len); -static ykpiv_rc _write_certificate(ykpiv_state *state, uint8_t slot, uint8_t *data, size_t data_len, uint8_t certinfo); - -static ykpiv_rc _read_metadata(ykpiv_state *state, uint8_t tag, uint8_t* data, size_t* pcb_data); -static ykpiv_rc _write_metadata(ykpiv_state *state, uint8_t tag, uint8_t *data, size_t cb_data); -static ykpiv_rc _get_metadata_item(uint8_t *data, size_t cb_data, uint8_t tag, uint8_t **pp_item, size_t *pcb_item); -static ykpiv_rc _set_metadata_item(uint8_t *data, size_t *pcb_data, size_t cb_data_max, uint8_t tag, uint8_t *p_item, size_t cb_item); - -static size_t _obj_size_max(ykpiv_state *state) { - return (state && state->is_neo) ? CB_OBJ_MAX_NEO : CB_OBJ_MAX; -} - -/* -** YKPIV Utility API - aggregate functions and slightly nicer interface -*/ - -ykpiv_rc ykpiv_util_get_cardid(ykpiv_state *state, ykpiv_cardid *cardid) { - ykpiv_rc res = YKPIV_OK; - uint8_t buf[CB_OBJ_MAX]; - size_t len = sizeof(buf); - - if (!cardid) return YKPIV_GENERIC_ERROR; - - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; - - res = _ykpiv_fetch_object(state, YKPIV_OBJ_CHUID, buf, (unsigned long *)&len); - if (YKPIV_OK == res) { - if (len != SIZEOF_CHUID_TMPL) { - res = YKPIV_GENERIC_ERROR; - } - else { - memcpy(cardid->data, buf + CHUID_GUID_OFFS, YKPIV_CARDID_SIZE); - } - } - -Cleanup: - - _ykpiv_end_transaction(state); - return res; -} - -ykpiv_rc ykpiv_util_set_cardid(ykpiv_state *state, const ykpiv_cardid *cardid) { - ykpiv_rc res = YKPIV_OK; - uint8_t id[YKPIV_CARDID_SIZE]; - uint8_t buf[SIZEOF_CHUID_TMPL]; - size_t len = 0; - - if (!state) return YKPIV_GENERIC_ERROR; - - if (!cardid) { - if (PRNG_OK != _ykpiv_prng_generate(id, sizeof(id))) { - return YKPIV_RANDOMNESS_ERROR; - } - } - else { - memcpy(id, cardid->data, sizeof(id)); - } - - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; - - memcpy(buf, CHUID_TMPL, SIZEOF_CHUID_TMPL); - memcpy(buf + CHUID_GUID_OFFS, id, sizeof(id)); - len = SIZEOF_CHUID_TMPL; - - res = _ykpiv_save_object(state, YKPIV_OBJ_CHUID, buf, len); - -Cleanup: - - _ykpiv_end_transaction(state); - return res; -} - -ykpiv_rc ykpiv_util_get_cccid(ykpiv_state *state, ykpiv_cccid *ccc) { - ykpiv_rc res = YKPIV_OK; - uint8_t buf[CB_OBJ_MAX]; - size_t len = sizeof(buf); - - if (!ccc) return YKPIV_GENERIC_ERROR; - - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; - - res = _ykpiv_fetch_object(state, YKPIV_OBJ_CAPABILITY, buf, (unsigned long *)&len); - if (YKPIV_OK == res) { - if (len != SIZEOF_CCC_TMPL) { - res = YKPIV_GENERIC_ERROR; - } - else { - memcpy(ccc->data, buf + CCC_ID_OFFS, YKPIV_CCCID_SIZE); - } - } - -Cleanup: - - _ykpiv_end_transaction(state); - return res; -} - -ykpiv_rc ykpiv_util_set_cccid(ykpiv_state *state, const ykpiv_cccid *ccc) { - ykpiv_rc res = YKPIV_OK; - uint8_t id[YKPIV_CCCID_SIZE]; - uint8_t buf[SIZEOF_CCC_TMPL]; - size_t len = 0; - - if (!state) return YKPIV_GENERIC_ERROR; - - if (!ccc) { - if (PRNG_OK != _ykpiv_prng_generate(id, sizeof(id))) { - return YKPIV_RANDOMNESS_ERROR; - } - } - else { - memcpy(id, ccc->data, sizeof(id)); - } - - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; - - len = SIZEOF_CCC_TMPL; - memcpy(buf, CCC_TMPL, len); - memcpy(buf + CCC_ID_OFFS, id, YKPIV_CCCID_SIZE); - res = _ykpiv_save_object(state, YKPIV_OBJ_CAPABILITY, buf, len); - -Cleanup: - _ykpiv_end_transaction(state); - return res; -} - -ykpiv_devmodel ykpiv_util_devicemodel(ykpiv_state *state) { - if (!state || !state->context || (state->context == (uintptr_t)-1)) { - return DEVTYPE_UNKNOWN; - } - return (state->is_neo ? DEVTYPE_NEOr3 : DEVTYPE_YK4); -} - -ykpiv_rc ykpiv_util_list_keys(ykpiv_state *state, uint8_t *key_count, ykpiv_key **data, size_t *data_len) { - ykpiv_rc res = YKPIV_OK; - ykpiv_key *pKey = NULL; - uint8_t *pData = NULL; - uint8_t *pTemp = NULL; - size_t cbData = 0; - size_t offset = 0; - uint8_t buf[CB_BUF_MAX]; - size_t cbBuf = 0; - size_t i = 0; - size_t cbRealloc = 0; - - const size_t CB_PAGE = 4096; - - const uint8_t *SLOTS = { - YKPIV_KEY_AUTHENTICATION, - YKPIV_KEY_SIGNATURE, - YKPIV_KEY_KEYMGM, - YKPIV_KEY_RETIRED1, - YKPIV_KEY_RETIRED2, - YKPIV_KEY_RETIRED3, - YKPIV_KEY_RETIRED4, - YKPIV_KEY_RETIRED5, - YKPIV_KEY_RETIRED6, - YKPIV_KEY_RETIRED7, - YKPIV_KEY_RETIRED8, - YKPIV_KEY_RETIRED9, - YKPIV_KEY_RETIRED10, - YKPIV_KEY_RETIRED11, - YKPIV_KEY_RETIRED12, - YKPIV_KEY_RETIRED13, - YKPIV_KEY_RETIRED14, - YKPIV_KEY_RETIRED15, - YKPIV_KEY_RETIRED16, - YKPIV_KEY_RETIRED17, - YKPIV_KEY_RETIRED18, - YKPIV_KEY_RETIRED19, - YKPIV_KEY_RETIRED20, - YKPIV_KEY_CARDAUTH - }; - - if ((NULL == data) || (NULL == data_len) || (NULL == key_count)) { return YKPIV_GENERIC_ERROR; } - - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; - - // init return parameters - *key_count = 0; - *data = NULL; - *data_len = 0; - - // allocate initial page of buffer - if (NULL == (pData = _ykpiv_alloc(state, CB_PAGE))) { - res = YKPIV_MEMORY_ERROR; - goto Cleanup; - } - - cbData = CB_PAGE; - - for (i = 0; i < sizeof(SLOTS); i++) { - cbBuf = sizeof(buf); - res = _read_certificate(state, SLOTS[i], buf, &cbBuf); - - if ((res == YKPIV_OK) && (cbBuf > 0)) { - // add current slot to result, grow result buffer if necessary - - cbRealloc = (sizeof(ykpiv_key) + cbBuf - 1) > (cbData - offset) ? MAX((sizeof(ykpiv_key) + cbBuf - 1) - (cbData - offset), CB_PAGE) : 0; - - if (0 != cbRealloc) { - if (!(pTemp = _ykpiv_realloc(state, pData, cbData + cbRealloc))) { - /* realloc failed, pData will be freed in cleanup */ - res = YKPIV_MEMORY_ERROR; - goto Cleanup; - } - pData = pTemp; - pTemp = NULL; - } - - cbData += cbRealloc; - - // If ykpiv_key is misaligned or results in padding, this causes problems - // in the array we return. If this becomes a problem, we'll probably want - // to go with a flat byte array. - - pKey = (ykpiv_key*)(pData + offset); - - pKey->slot = SLOTS[i]; - pKey->cert_len = (uint16_t)cbBuf; - memcpy(pKey->cert, buf, cbBuf); - - offset += sizeof(ykpiv_key) + cbBuf - 1; - (*key_count)++; - } - } - - *data = (ykpiv_key*)pData; - pData = NULL; - - if (data_len) { - *data_len = offset; - } - - res = YKPIV_OK; - -Cleanup: - - if (pData) { _ykpiv_free(state, pData); } - - _ykpiv_end_transaction(state); - return res; -} - -ykpiv_rc ykpiv_util_free(ykpiv_state *state, void *data) { - if (!data) return YKPIV_OK; - if (!state || (!(state->allocator.pfn_free))) return YKPIV_GENERIC_ERROR; - - _ykpiv_free(state, data); - - return YKPIV_OK; -} - -ykpiv_rc ykpiv_util_read_cert(ykpiv_state *state, uint8_t slot, uint8_t **data, size_t *data_len) { - ykpiv_rc res = YKPIV_OK; - uint8_t buf[CB_BUF_MAX]; - size_t cbBuf = sizeof(buf); - - if ((NULL == data )|| (NULL == data_len)) return YKPIV_GENERIC_ERROR; - - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; - - *data = 0; - *data_len = 0; - - if (YKPIV_OK == (res = _read_certificate(state, slot, buf, &cbBuf))) { - - /* handle those who write empty certificate blobs to PIV objects */ - if (cbBuf == 0) { - *data = NULL; - *data_len = 0; - goto Cleanup; - } - - if (!(*data = _ykpiv_alloc(state, cbBuf))) { - res = YKPIV_MEMORY_ERROR; - goto Cleanup; - } - - memcpy(*data, buf, cbBuf); - - *data_len = cbBuf; - } - -Cleanup: - - _ykpiv_end_transaction(state); - return res; -} - -ykpiv_rc ykpiv_util_write_cert(ykpiv_state *state, uint8_t slot, uint8_t *data, size_t data_len, uint8_t certinfo) { - ykpiv_rc res = YKPIV_OK; - - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; - - res = _write_certificate(state, slot, data, data_len, certinfo); - -Cleanup: - - _ykpiv_end_transaction(state); - return res; -} - -ykpiv_rc ykpiv_util_delete_cert(ykpiv_state *state, uint8_t slot) { - return ykpiv_util_write_cert(state, slot, NULL, 0, 0); -} - -ykpiv_rc ykpiv_util_block_puk(ykpiv_state *state) { - ykpiv_rc res = YKPIV_OK; - uint8_t *puk = { 0x30, 0x42, 0x41, 0x44, 0x46, 0x30, 0x30, 0x44 }; - int tries = -1; - uint8_t data[CB_BUF_MAX]; - size_t cb_data = sizeof(data); - uint8_t *p_item = NULL; - size_t cb_item = 0; - uint8_t flags = 0; - - if (NULL == state) return YKPIV_GENERIC_ERROR; - - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; - - while (tries != 0) { - if (YKPIV_OK == (res = ykpiv_change_puk(state, (const char*)puk, sizeof(puk), (const char*)puk, sizeof(puk), &tries))) { - /* did we accidentally choose the correct PUK?, change our puk and try again */ - puk[0]++; - } - else { - /* depending on the firmware, tries may not be set to zero when the PUK is blocked, */ - /* instead, the return code will be PIN_LOCKED and tries will be unset */ - if (YKPIV_PIN_LOCKED == res) { - tries = 0; - res = YKPIV_OK; - } - } - } - - /* attempt to set the puk blocked flag in admin data */ - - if (YKPIV_OK == _read_metadata(state, TAG_ADMIN, data, &cb_data)) { - if (YKPIV_OK == _get_metadata_item(data, cb_data, TAG_ADMIN_FLAGS_1, &p_item, &cb_item)) { - if (sizeof(flags) == cb_item) { - memcpy(&flags, p_item, cb_item); - } - else { - if (state->verbose) { fprintf(stderr, "admin flags exist, but are incorrect size = %lu", (unsigned long)cb_item); } - } - } - } - - flags |= ADMIN_FLAGS_1_PUK_BLOCKED; - - if (YKPIV_OK != _set_metadata_item(data, &cb_data, CB_OBJ_MAX, TAG_ADMIN_FLAGS_1, (uint8_t*)&flags, sizeof(flags))) { - if (state->verbose) { fprintf(stderr, "could not set admin flags"); } - } - else { - if (YKPIV_OK != _write_metadata(state, TAG_ADMIN, data, cb_data)) { - if (state->verbose) { fprintf(stderr, "could not write admin metadata"); } - } - } - -Cleanup: - - _ykpiv_end_transaction(state); - return res; -} - -ykpiv_rc ykpiv_util_read_mscmap(ykpiv_state *state, ykpiv_container **containers, size_t *n_containers) { - ykpiv_rc res = YKPIV_OK; - uint8_t buf[CB_BUF_MAX]; - size_t cbBuf = sizeof(buf); - size_t len = 0; - uint8_t *ptr = NULL; - - if ((NULL == containers) || (NULL == n_containers)) { res = YKPIV_GENERIC_ERROR; goto Cleanup; } - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; - - *containers = 0; - *n_containers = 0; - - if (YKPIV_OK == (res = _ykpiv_fetch_object(state, YKPIV_OBJ_MSCMAP, buf, (unsigned long*)&cbBuf))) { - ptr = buf; - - /* check that object contents are at least large enough to read the header */ - if (cbBuf < CB_OBJ_TAG_MIN) { - res = YKPIV_OK; - goto Cleanup; - } - - if (*ptr++ == TAG_MSCMAP) { - ptr += (unsigned long)_ykpiv_get_length(ptr, &len); - - /* check that decoded length represents object contents */ - if (len > (cbBuf - (size_t)(ptr - buf))) { - res = YKPIV_OK; - goto Cleanup; - } - - if (NULL == (*containers = _ykpiv_alloc(state, len))) { - res = YKPIV_MEMORY_ERROR; - goto Cleanup; - } - - /* should check if container map isn't corrupt */ - - memcpy(*containers, ptr, len); - *n_containers = len / sizeof(ykpiv_container); - } - } - -Cleanup: - - _ykpiv_end_transaction(state); - return res; -} - -ykpiv_rc ykpiv_util_write_mscmap(ykpiv_state *state, ykpiv_container *containers, size_t n_containers) { - ykpiv_rc res = YKPIV_OK; - uint8_t buf[CB_OBJ_MAX]; - size_t offset = 0; - size_t req_len = 0; - size_t data_len = n_containers * sizeof(ykpiv_container); - - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; - - // check if data and data_len are zero, this means that - // we intend to delete the object - if ((NULL == containers) || (0 == n_containers)) { - - // if either containers or n_containers are non-zero, return an error, - // that we only delete strictly when both are set properly - if ((NULL != containers) || (0 != n_containers)) { - res = YKPIV_GENERIC_ERROR; - } - else { - res = _ykpiv_save_object(state, YKPIV_OBJ_MSCMAP, NULL, 0); - } - - goto Cleanup; - } - - // encode object data for storage - - // calculate the required length of the encoded object - req_len = 1 /* data tag */ + (unsigned long)_ykpiv_set_length(buf, data_len) + data_len; - - if (req_len > _obj_size_max(state)) { - res = YKPIV_SIZE_ERROR; - goto Cleanup; - } - - buf[offset++] = TAG_MSCMAP; - offset += _ykpiv_set_length(buf + offset, data_len); - memcpy(buf + offset, (uint8_t*)containers, data_len); - offset += data_len; - - // write onto device - res = _ykpiv_save_object(state, YKPIV_OBJ_MSCMAP, buf, offset); - -Cleanup: - - _ykpiv_end_transaction(state); - return res; -} - -ykpiv_rc ykpiv_util_read_msroots(ykpiv_state *state, uint8_t **data, size_t *data_len) { - ykpiv_rc res = YKPIV_OK; - uint8_t buf[CB_BUF_MAX]; - size_t cbBuf = sizeof(buf); - size_t len = 0; - uint8_t *ptr = NULL; - int object_id = 0; - uint8_t tag = 0; - uint8_t *pData = NULL; - uint8_t *pTemp = NULL; - size_t cbData = 0; - size_t cbRealloc = 0; - size_t offset = 0; - - if (!data || !data_len) return YKPIV_GENERIC_ERROR; - - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; - - *data = 0; - *data_len = 0; - - // allocate first page - cbData = _obj_size_max(state); - if (NULL == (pData = _ykpiv_alloc(state, cbData))) { res = YKPIV_MEMORY_ERROR; goto Cleanup; } - - for (object_id = YKPIV_OBJ_MSROOTS1; object_id <= YKPIV_OBJ_MSROOTS5; object_id++) { - cbBuf = sizeof(buf); - - if (YKPIV_OK != (res = _ykpiv_fetch_object(state, object_id, buf, (unsigned long*)&cbBuf))) { - goto Cleanup; - } - - ptr = buf; - - if (cbBuf < CB_OBJ_TAG_MIN) { - res = YKPIV_OK; - goto Cleanup; - } - - tag = *ptr++; - - if (((TAG_MSROOTS_MID != tag) && (TAG_MSROOTS_END != tag)) || - ((YKPIV_OBJ_MSROOTS5 == object_id) && (TAG_MSROOTS_END != tag))) { - // the current object doesn't contain a valid part of a msroots file - res = YKPIV_OK; // treat condition as object isn't found - goto Cleanup; - } - - ptr += _ykpiv_get_length(ptr, &len); - - // check that decoded length represents object contents - if (len > (cbBuf - (size_t)(ptr - buf))) { - res = YKPIV_OK; - goto Cleanup; - } - - cbRealloc = len > (cbData - offset) ? len - (cbData - offset) : 0; - - if (0 != cbRealloc) { - if (!(pTemp = _ykpiv_realloc(state, pData, cbData + cbRealloc))) { - /* realloc failed, pData will be freed in cleanup */ - res = YKPIV_MEMORY_ERROR; - goto Cleanup; - } - pData = pTemp; - pTemp = NULL; - } - - cbData += cbRealloc; - - memcpy(pData + offset, ptr, len); - offset += len; - - if (TAG_MSROOTS_END == tag) { - break; - } - } - - // return data - *data = pData; - pData = NULL; - *data_len = offset; - - res = YKPIV_OK; - -Cleanup: - - if (pData) { _ykpiv_free(state, pData); } - - _ykpiv_end_transaction(state); - return res; -} - -ykpiv_rc ykpiv_util_write_msroots(ykpiv_state *state, uint8_t *data, size_t data_len) { - ykpiv_rc res = YKPIV_OK; - uint8_t buf[CB_OBJ_MAX]; - size_t offset = 0; - size_t data_offset = 0; - size_t data_chunk = 0; - size_t n_objs = 0; - unsigned int i = 0; - size_t cb_obj_max = _obj_size_max(state); - - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; - - // check if either data and data_len are zero, this means that - // we intend to delete the object - if ((NULL == data) || (0 == data_len)) { - - // if either data or data_len are non-zero, return an error, - // that we only delete strictly when both are set properly - if ((NULL != data) || (0 != data_len)) { - res = YKPIV_GENERIC_ERROR; - } - else { - // it should be sufficient to just delete the first object, though - // to be complete we should erase all of the MSROOTS objects - res = _ykpiv_save_object(state, YKPIV_OBJ_MSROOTS1, NULL, 0); - } - - goto Cleanup; - } - - // calculate number of objects required to store blob - n_objs = (data_len / (cb_obj_max - CB_OBJ_TAG_MAX)) + 1; - - // we're allowing 5 objects to be used to span the msroots file - if (n_objs > 5) { - res = YKPIV_SIZE_ERROR; - goto Cleanup; - } - - for (i = 0; i < n_objs; i++) { - offset = 0; - data_chunk = MIN(cb_obj_max - CB_OBJ_TAG_MAX, data_len - data_offset); - - /* encode object data for storage */ - buf[offset++] = (i == (n_objs - 1)) ? TAG_MSROOTS_END : TAG_MSROOTS_MID; - offset += _ykpiv_set_length(buf + offset, data_chunk); - memcpy(buf + offset, data + data_offset, data_chunk); - offset += data_chunk; - - /* write onto device */ - res = _ykpiv_save_object(state, (int)(YKPIV_OBJ_MSROOTS1 + i), buf, offset); - - if (YKPIV_OK != res) { - goto Cleanup; - } - - data_offset += data_chunk; - } - -Cleanup: - - _ykpiv_end_transaction(state); - return res; -} - -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) { - ykpiv_rc res = YKPIV_OK; - 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; - uint8_t *ptr_modulus = NULL; - size_t cb_modulus = 0; - uint8_t *ptr_exp = NULL; - size_t cb_exp = 0; - uint8_t *ptr_point = NULL; - size_t cb_point = 0; - - setting_bool_t setting_roca; /* XXX was 0 */ - const char *sz_setting_roca = "Enable_Unsafe_Keygen_ROCA"; - const char *sz_roca_format = "YubiKey serial number %u is affected by vulnerability " - "CVE-2017-15361 (ROCA) and should be replaced. On-chip key generation %s " - "See YSA-2017-01 " - "for additional information on device replacement and mitigation assistance.\n"; - const char *sz_roca_allow_user = "was permitted by an end-user configuration setting, but is not recommended."; - const char *sz_roca_allow_admin = "was permitted by an administrator configuration setting, but is not recommended."; - const char *sz_roca_block_user = "was blocked due to an end-user configuration setting."; - const char *sz_roca_block_admin = "was blocked due to an administrator configuration setting."; - const char *sz_roca_default = "was permitted by default, but is not recommended. " - "The default behavior will change in a future Yubico release."; - - if (!state) return YKPIV_ARGUMENT_ERROR; - - if (ykpiv_util_devicemodel(state) == DEVTYPE_YK4 && (algorithm == YKPIV_ALGO_RSA1024 || algorithm == YKPIV_ALGO_RSA2048)) { - if ((state->ver.major == 4) && (state->ver.minor < 3 || ((state->ver.minor == 3) && (state->ver.patch < 5)))) { - const char *psz_msg = NULL; - setting_roca = setting_get_bool(sz_setting_roca, true); - - switch (setting_roca.source) { - case SETTING_SOURCE_ADMIN: - psz_msg = setting_roca.value ? sz_roca_allow_admin : sz_roca_block_admin; - break; - - case SETTING_SOURCE_USER: - psz_msg = setting_roca.value ? sz_roca_allow_user : sz_roca_block_user; - break; - - default: - case SETTING_SOURCE_DEFAULT: - psz_msg = sz_roca_default; - break; - } - - fprintf(stderr, sz_roca_format, state->serial, psz_msg); - //yc_log_event(1, setting_roca.value ? YC_LOG_LEVEL_WARN : YC_LOG_LEVEL_ERROR, sz_roca_format, state->serial, psz_msg); - - if (!setting_roca.value) { - return YKPIV_NOT_SUPPORTED; - } - } - } - - switch (algorithm) { - case YKPIV_ALGO_RSA1024: - case YKPIV_ALGO_RSA2048: - if (!modulus || !modulus_len || !exp || !exp_len) { - if (state->verbose) { fprintf(stderr, "Invalid output parameter for RSA algorithm"); } - return YKPIV_GENERIC_ERROR; - } - *modulus = NULL; - *modulus_len = 0; - *exp = NULL; - *exp_len = 0; - break; - - case YKPIV_ALGO_ECCP256: - case YKPIV_ALGO_ECCP384: - if (!point || !point_len) { - if (state->verbose) { fprintf(stderr, "Invalid output parameter for ECC algorithm"); } - return YKPIV_GENERIC_ERROR; - } - *point = NULL; - *point_len = 0; - break; - - default: - if (state->verbose) { fprintf(stderr, "Invalid algorithm specified"); } - return YKPIV_GENERIC_ERROR; - } - - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; - - templ[3] = slot; - - *in_ptr++ = 0xac; - *in_ptr++ = 3; - *in_ptr++ = YKPIV_ALGO_TAG; - *in_ptr++ = 1; - *in_ptr++ = algorithm; - - if (in_data[4] == 0) { - res = YKPIV_ALGORITHM_ERROR; - if (state->verbose) { fprintf(stderr, "Unexpected algorithm.\n"); } - goto Cleanup; - } - - if (pin_policy != YKPIV_PINPOLICY_DEFAULT) { - in_data[1] += 3; - *in_ptr++ = YKPIV_PINPOLICY_TAG; - *in_ptr++ = 1; - *in_ptr++ = pin_policy; - } - - if (touch_policy != YKPIV_TOUCHPOLICY_DEFAULT) { - in_data[1] += 3; - *in_ptr++ = YKPIV_TOUCHPOLICY_TAG; - *in_ptr++ = 1; - *in_ptr++ = touch_policy; - } - - if (YKPIV_OK != (res = _ykpiv_transfer_data(state, templ, in_data, (long)(in_ptr - in_data), data, &recv_len, &sw))) { - if (state->verbose) { fprintf(stderr, "Failed to communicate.\n"); } - goto Cleanup; - } - else if (sw != SW_SUCCESS) { - if (state->verbose) { fprintf(stderr, "Failed to generate new key ("); } - - if (sw == SW_ERR_INCORRECT_SLOT) { - res = YKPIV_KEY_ERROR; - if (state->verbose) { fprintf(stderr, "incorrect slot)\n"); } - } - else if (sw == SW_ERR_INCORRECT_PARAM) { - res = YKPIV_ALGORITHM_ERROR; - - if (state->verbose) { - if (pin_policy != YKPIV_PINPOLICY_DEFAULT) { - fprintf(stderr, "pin policy not supported?)\n"); - } - else if (touch_policy != YKPIV_TOUCHPOLICY_DEFAULT) { - fprintf(stderr, "touch policy not supported?)\n"); - } - else { - fprintf(stderr, "algorithm not supported?)\n"); - } - } - } - else if (sw == SW_ERR_SECURITY_STATUS) { - res = YKPIV_AUTHENTICATION_ERROR; - if (state->verbose) { fprintf(stderr, "not authenticated)\n"); } - } - else { - res = YKPIV_GENERIC_ERROR; - if (state->verbose) { fprintf(stderr, "error %x)\n", sw); } - } - - goto Cleanup; - } - - if ((YKPIV_ALGO_RSA1024 == algorithm) || (YKPIV_ALGO_RSA2048 == algorithm)) { - unsigned char *data_ptr = data + 5; - size_t len = 0; - - if (*data_ptr != TAG_RSA_MODULUS) { - if (state->verbose) { fprintf(stderr, "Failed to parse public key structure (modulus).\n"); } - res = YKPIV_PARSE_ERROR; - goto Cleanup; - } - - data_ptr++; - data_ptr += _ykpiv_get_length(data_ptr, &len); - - cb_modulus = len; - if (NULL == (ptr_modulus = _ykpiv_alloc(state, cb_modulus))) { - if (state->verbose) { fprintf(stderr, "Failed to allocate memory for modulus.\n"); } - res = YKPIV_MEMORY_ERROR; - goto Cleanup; - } - - memcpy(ptr_modulus, data_ptr, cb_modulus); - - data_ptr += len; - - if (*data_ptr != TAG_RSA_EXP) { - if (state->verbose) { fprintf(stderr, "Failed to parse public key structure (public exponent).\n"); } - res = YKPIV_PARSE_ERROR; - goto Cleanup; - } - - data_ptr++; - data_ptr += _ykpiv_get_length(data_ptr, &len); - - cb_exp = len; - if (NULL == (ptr_exp = _ykpiv_alloc(state, cb_exp))) { - if (state->verbose) { fprintf(stderr, "Failed to allocate memory for public exponent.\n"); } - res = YKPIV_MEMORY_ERROR; - goto Cleanup; - } - - memcpy(ptr_exp, data_ptr, cb_exp); - - // set output parameters - - *modulus = ptr_modulus; - ptr_modulus = NULL; - *modulus_len = cb_modulus; - *exp = ptr_exp; - ptr_exp = NULL; - *exp_len = cb_exp; - } - else if ((YKPIV_ALGO_ECCP256 == algorithm) || (YKPIV_ALGO_ECCP384 == algorithm)) { - unsigned char *data_ptr = data + 3; - size_t len; - - if (YKPIV_ALGO_ECCP256 == algorithm) { - len = CB_ECC_POINTP256; - } - else { - len = CB_ECC_POINTP384; - } - - if (*data_ptr++ != TAG_ECC_POINT) { - if (state->verbose) { fprintf(stderr, "Failed to parse public key structure.\n"); } - res = YKPIV_PARSE_ERROR; - goto Cleanup; - } - - if (*data_ptr++ != len) { /* the curve point should always be determined by the curve */ - if (state->verbose) { fprintf(stderr, "Unexpected length.\n"); } - res = YKPIV_ALGORITHM_ERROR; - goto Cleanup; - } - - cb_point = len; - if (NULL == (ptr_point = _ykpiv_alloc(state, cb_point))) { - if (state->verbose) { fprintf(stderr, "Failed to allocate memory for public point.\n"); } - res = YKPIV_MEMORY_ERROR; - goto Cleanup; - } - - memcpy(ptr_point, data_ptr, cb_point); - - // set output parameters - - *point = ptr_point; - ptr_point = NULL; - *point_len = cb_point; - } - else { - if (state->verbose) { fprintf(stderr, "Wrong algorithm.\n"); } - res = YKPIV_ALGORITHM_ERROR; - goto Cleanup; - } - -Cleanup: - - if (ptr_modulus) { _ykpiv_free(state, modulus); } - if (ptr_exp) { _ykpiv_free(state, ptr_exp); } - if (ptr_point) { _ykpiv_free(state, ptr_exp); } - - _ykpiv_end_transaction(state); - return res; -} - -ykpiv_rc ykpiv_util_get_config(ykpiv_state *state, ykpiv_config *config) { - ykpiv_rc res = YKPIV_OK; - uint8_t data[CB_BUF_MAX]; /* XXX REMEMBER TO ZERO */ - size_t cb_data = sizeof(data); - uint8_t *p_item = NULL; - size_t cb_item = 0; - - if (NULL == state) return YKPIV_GENERIC_ERROR; - if (NULL == config) return YKPIV_GENERIC_ERROR; - - // initialize default values - - config->protected_data_available = false; - config->puk_blocked = false; - config->puk_noblock_on_upgrade = false; - config->pin_last_changed = 0; - config->mgm_type = YKPIV_CONFIG_MGM_MANUAL; - - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; - - /* recover admin data */ - if (YKPIV_OK == _read_metadata(state, TAG_ADMIN, data, &cb_data)) { - if (YKPIV_OK == _get_metadata_item(data, cb_data, TAG_ADMIN_FLAGS_1, &p_item, &cb_item)) { - if (*p_item & ADMIN_FLAGS_1_PUK_BLOCKED) config->puk_blocked = true; - if (*p_item & ADMIN_FLAGS_1_PROTECTED_MGM) config->mgm_type = YKPIV_CONFIG_MGM_PROTECTED; - } - - if (YKPIV_OK == _get_metadata_item(data, cb_data, TAG_ADMIN_SALT, &p_item, &cb_item)) { - if (config->mgm_type != YKPIV_CONFIG_MGM_MANUAL) { - if (state->verbose) { - fprintf(stderr, "conflicting types of mgm key administration configured\n"); - } - } - else { - config->mgm_type = YKPIV_CONFIG_MGM_DERIVED; - } - } - - if (YKPIV_OK == _get_metadata_item(data, cb_data, TAG_ADMIN_TIMESTAMP, &p_item, &cb_item)) { - if (CB_ADMIN_TIMESTAMP != cb_item) { - if (state->verbose) { - fprintf(stderr, "pin timestamp in admin metadata is an invalid size"); - } - } - else { - memcpy(&(config->pin_last_changed), p_item, cb_item); - } - } - } - - /* recover protected data */ - cb_data = sizeof(data); - - if (YKPIV_OK == _read_metadata(state, TAG_PROTECTED, data, &cb_data)) { - config->protected_data_available = true; - - if (YKPIV_OK == _get_metadata_item(data, cb_data, TAG_PROTECTED_FLAGS_1, &p_item, &cb_item)) { - if (*p_item & PROTECTED_FLAGS_1_PUK_NOBLOCK) config->puk_noblock_on_upgrade = true; - } - - if (YKPIV_OK == _get_metadata_item(data, cb_data, TAG_PROTECTED_MGM, &p_item, &cb_item)) { - if (config->mgm_type != YKPIV_CONFIG_MGM_PROTECTED) { - if (state->verbose) { - fprintf(stderr, "conflicting types of mgm key administration configured - protected mgm exists\n"); - } - } - config->mgm_type = YKPIV_CONFIG_MGM_PROTECTED; /* always favor protected mgm */ - } - } - -Cleanup: - - _ykpiv_end_transaction(state); - return res; -} - -ykpiv_rc ykpiv_util_set_pin_last_changed(ykpiv_state *state) { - ykpiv_rc res = YKPIV_OK; - ykpiv_rc ykrc = YKPIV_OK; - uint8_t data[CB_BUF_MAX]; /* XXX REMEMBER TO ZERO */ - size_t cb_data = sizeof(data); - time_t tnow = 0; - - if (NULL == state) return YKPIV_GENERIC_ERROR; - - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; - - /* recover admin data */ - if (YKPIV_OK != (ykrc = _read_metadata(state, TAG_ADMIN, data, &cb_data))) { - cb_data = 0; /* set current metadata blob size to zero, we'll add the timestamp to the blank blob */ - } - - tnow = time(NULL); - - if (YKPIV_OK != (res = _set_metadata_item(data, &cb_data, CB_OBJ_MAX, TAG_ADMIN_TIMESTAMP, (uint8_t*)&tnow, CB_ADMIN_TIMESTAMP))) { - if (state->verbose) fprintf(stderr, "could not set pin timestamp, err = %d\n", res); - } - else { - if (YKPIV_OK != (res = _write_metadata(state, TAG_ADMIN, data, cb_data))) { - /* Note: this can fail if authenticate() wasn't called previously - expected behavior */ - if (state->verbose) fprintf(stderr, "could not write admin data, err = %d\n", res); - } - } - -Cleanup: - - _ykpiv_end_transaction(state); - return res; -} - -ykpiv_rc ykpiv_util_get_derived_mgm(ykpiv_state *state, const uint8_t *pin, const size_t pin_len, ykpiv_mgm *mgm) { - ykpiv_rc res = YKPIV_OK; - pkcs5_rc p5rc = PKCS5_OK; - uint8_t data[CB_BUF_MAX]; /* XXX REMEMBER TO ZERO */ - size_t cb_data = sizeof(data); - uint8_t *p_item = NULL; - size_t cb_item = 0; - - if (NULL == state) return YKPIV_GENERIC_ERROR; - if ((NULL == pin) || (0 == pin_len) || (NULL == mgm)) return YKPIV_GENERIC_ERROR; - - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; - - /* recover management key */ - if (YKPIV_OK == (res = _read_metadata(state, TAG_ADMIN, data, &cb_data))) { - if (YKPIV_OK == (res = _get_metadata_item(data, cb_data, TAG_ADMIN_SALT, &p_item, &cb_item))) { - if (cb_item != CB_ADMIN_SALT) { - if (state->verbose) fprintf(stderr, "derived mgm salt exists, but is incorrect size = %lu\n", (unsigned long)cb_item); - res = YKPIV_GENERIC_ERROR; - goto Cleanup; - } - - if (PKCS5_OK != (p5rc = pkcs5_pbkdf2_sha1(pin, pin_len, p_item, cb_item, ITER_MGM_PBKDF2, mgm->data, member_size(ykpiv_mgm, data)))) { - if (state->verbose) fprintf(stderr, "pbkdf2 failure, err = %d\n", p5rc); - res = YKPIV_GENERIC_ERROR; - goto Cleanup; - } - } - } - -Cleanup: - - _ykpiv_end_transaction(state); - return res; -} - -ykpiv_rc ykpiv_util_get_protected_mgm(ykpiv_state *state, ykpiv_mgm *mgm) { - ykpiv_rc res = YKPIV_OK; - uint8_t data[CB_BUF_MAX]; /* XXX REMEMBER TO ZERO */ - size_t cb_data = sizeof(data); - uint8_t *p_item = NULL; - size_t cb_item = 0; - - if (NULL == state) return YKPIV_GENERIC_ERROR; - if (NULL == mgm) return YKPIV_GENERIC_ERROR; - - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; - - if (YKPIV_OK != (res = _read_metadata(state, TAG_PROTECTED, data, &cb_data))) { - if (state->verbose) fprintf(stderr, "could not read protected data, err = %d\n", res); - goto Cleanup; - } - - if (YKPIV_OK != (res = _get_metadata_item(data, cb_data, TAG_PROTECTED_MGM, &p_item, &cb_item))) { - if (state->verbose) fprintf(stderr, "could not read protected mgm from metadata, err = %d\n", res); - goto Cleanup; - } - - if (cb_item != member_size(ykpiv_mgm, data)) { - if (state->verbose) fprintf(stderr, "protected data contains mgm, but is the wrong size = %lu\n", (unsigned long)cb_item); - res = YKPIV_AUTHENTICATION_ERROR; - goto Cleanup; - } - - memcpy(mgm->data, p_item, cb_item); - -Cleanup: - - yc_memzero(data, sizeof(data)); - - _ykpiv_end_transaction(state); - return res; - -} - -/* to set a generated mgm, pass NULL for mgm, or set mgm.data to all zeroes */ -ykpiv_rc ykpiv_util_set_protected_mgm(ykpiv_state *state, ykpiv_mgm *mgm) { - ykpiv_rc res = YKPIV_OK; - ykpiv_rc ykrc = YKPIV_OK; - prng_rc prngrc = PRNG_OK; - bool fGenerate = false; - uint8_t mgm_key[24]; - size_t i = 0; - uint8_t data[CB_BUF_MAX]; /* XXX REMEMBER TO ZERO */ - size_t cb_data = sizeof(data); - uint8_t *p_item = NULL; - size_t cb_item = 0; - uint8_t flags_1 = 0; - - if (NULL == state) return YKPIV_GENERIC_ERROR; - - if (!mgm) { - fGenerate = true; - } - else { - fGenerate = true; - memcpy(mgm_key, mgm->data, sizeof(mgm_key)); - - for (i = 0; i < sizeof(mgm_key); i++) { - if (mgm_key[i] != 0) { - fGenerate = false; - break; - } - } - } - - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) { res = YKPIV_PCSC_ERROR; goto Cleanup; } - if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; - - /* try to set the mgm key as long as we don't encounter a fatal error */ - do { - if (fGenerate) { - /* generate a new mgm key */ - if (PRNG_OK != (prngrc = _ykpiv_prng_generate(mgm_key, sizeof(mgm_key)))) { - if (state->verbose) fprintf(stderr, "could not generate new mgm, err = %d\n", prngrc); - res = YKPIV_RANDOMNESS_ERROR; - goto Cleanup; - } - } - - if (YKPIV_OK != (ykrc = ykpiv_set_mgmkey(state, mgm_key))) { - /* - ** if _set_mgmkey fails with YKPIV_KEY_ERROR, it means the generated key is weak - ** otherwise, log a warning, since the device mgm key is corrupt or we're in - ** a state where we can't set the mgm key - */ - if (YKPIV_KEY_ERROR != ykrc) { - if (state->verbose) fprintf(stderr, "could not set new derived mgm key, err = %d\n", ykrc); - res = ykrc; - goto Cleanup; - } - } - else { - /* _set_mgmkey succeeded, stop generating */ - fGenerate = false; - } - } while (fGenerate); - - /* set output mgm */ - if (mgm) { - memcpy(mgm->data, mgm_key, sizeof(mgm_key)); - } - - /* after this point, we've set the mgm key, so the function should succeed, regardless of being able to set the metadata */ - - /* set the new mgm key in protected data */ - if (YKPIV_OK != (ykrc = _read_metadata(state, TAG_PROTECTED, data, &cb_data))) { - cb_data = 0; /* set current metadata blob size to zero, we'll add to the blank blob */ - } - - if (YKPIV_OK != (ykrc = _set_metadata_item(data, &cb_data, CB_OBJ_MAX, TAG_PROTECTED_MGM, mgm_key, sizeof(mgm_key)))) { - if (state->verbose) fprintf(stderr, "could not set protected mgm item, err = %d\n", ykrc); - } - else { - if (YKPIV_OK != (ykrc = _write_metadata(state, TAG_PROTECTED, data, cb_data))) { - if (state->verbose) fprintf(stderr, "could not write protected data, err = %d\n", ykrc); - goto Cleanup; - } - } - - /* set the protected mgm flag in admin data */ - cb_data = sizeof(data); - - if (YKPIV_OK != (ykrc = _read_metadata(state, TAG_ADMIN, data, &cb_data))) { - cb_data = 0; - } - else { - - if (YKPIV_OK != (ykrc = _get_metadata_item(data, cb_data, TAG_ADMIN_FLAGS_1, &p_item, &cb_item))) { - /* flags are not set */ - if (state->verbose) fprintf(stderr, "admin data exists, but flags are not present\n"); - } - - if (cb_item == sizeof(flags_1)) { - memcpy(&flags_1, p_item, cb_item); - } - else { - if (state->verbose) fprintf(stderr, "admin data flags are an incorrect size = %lu\n", (unsigned long)cb_item); - } - - /* remove any existing salt */ - if (YKPIV_OK != (ykrc = _set_metadata_item(data, &cb_data, CB_OBJ_MAX, TAG_ADMIN_SALT, NULL, 0))) { - if (state->verbose) fprintf(stderr, "could not unset derived mgm salt, err = %d\n", ykrc); - } - } - - flags_1 |= ADMIN_FLAGS_1_PROTECTED_MGM; - - if (YKPIV_OK != (ykrc = _set_metadata_item(data, &cb_data, CB_OBJ_MAX, TAG_ADMIN_FLAGS_1, &flags_1, sizeof(flags_1)))) { - if (state->verbose) fprintf(stderr, "could not set admin flags item, err = %d\n", ykrc); - } - else { - if (YKPIV_OK != (ykrc = _write_metadata(state, TAG_ADMIN, data, cb_data))) { - if (state->verbose) fprintf(stderr, "could not write admin data, err = %d\n", ykrc); - goto Cleanup; - } - } - - -Cleanup: - - yc_memzero(data, sizeof(data)); - yc_memzero(mgm_key, sizeof(mgm_key)); - - _ykpiv_end_transaction(state); - return res; -} - -ykpiv_rc ykpiv_util_reset(ykpiv_state *state) { - unsigned char *templ = {0, YKPIV_INS_RESET, 0, 0}; - unsigned char data[0xff]; - unsigned long recv_len = sizeof(data); - ykpiv_rc res; - int sw; - - /* note: the reset function is only available when both pins are blocked. */ - res = ykpiv_transfer_data(state, templ, NULL, 0, data, &recv_len, &sw); - if (YKPIV_OK == res && SW_SUCCESS == sw) { - return YKPIV_OK; - } - return YKPIV_GENERIC_ERROR; -} - -uint32_t ykpiv_util_slot_object(uint8_t slot) { - int object_id = -1; - - switch (slot) { - case YKPIV_KEY_AUTHENTICATION: - object_id = YKPIV_OBJ_AUTHENTICATION; - break; - - case YKPIV_KEY_SIGNATURE: - object_id = YKPIV_OBJ_SIGNATURE; - break; - - case YKPIV_KEY_KEYMGM: - object_id = YKPIV_OBJ_KEY_MANAGEMENT; - break; - - case YKPIV_KEY_CARDAUTH: - object_id = YKPIV_OBJ_CARD_AUTH; - break; - - case YKPIV_KEY_ATTESTATION: - object_id = YKPIV_OBJ_ATTESTATION; - break; - - default: - if ((slot >= YKPIV_KEY_RETIRED1) && (slot <= YKPIV_KEY_RETIRED20)) { - object_id = YKPIV_OBJ_RETIRED1 + (slot - YKPIV_KEY_RETIRED1); - } - break; - } - - return (uint32_t)object_id; -} - -static ykpiv_rc _read_certificate(ykpiv_state *state, uint8_t slot, uint8_t *buf, size_t *buf_len) { - ykpiv_rc res = YKPIV_OK; - uint8_t *ptr = NULL; - int object_id = (int)ykpiv_util_slot_object(slot); - size_t len = 0; - - if (-1 == object_id) return YKPIV_INVALID_OBJECT; - - if (YKPIV_OK == (res = _ykpiv_fetch_object(state, object_id, buf, (unsigned long*)buf_len))) { - ptr = buf; - - // check that object contents are at least large enough to read the tag - if (*buf_len < CB_OBJ_TAG_MIN) { - *buf_len = 0; - return YKPIV_OK; - } - - // check that first byte indicates "certificate" type - - if (*ptr++ == TAG_CERT) { - ptr += _ykpiv_get_length(ptr, &len); - - // check that decoded length represents object contents - if (len > (*buf_len - (size_t)(ptr - buf))) { - *buf_len = 0; - return YKPIV_OK; - } - - memmove(buf, ptr, len); - *buf_len = len; - } - } - else { - *buf_len = 0; - } - - return res; -} - -static ykpiv_rc _write_certificate(ykpiv_state *state, uint8_t slot, uint8_t *data, size_t data_len, uint8_t certinfo) { - uint8_t buf[CB_OBJ_MAX]; - int object_id = (int)ykpiv_util_slot_object(slot); - size_t offset = 0; - size_t req_len = 0; - - if (-1 == object_id) return YKPIV_INVALID_OBJECT; - - // check if data or data_len are zero, this means that we intend to delete the object - if ((NULL == data) || (0 == data_len)) { - - // if either data or data_len are non-zero, return an error, - // that we only delete strictly when both are set properly - if ((NULL != data) || (0 != data_len)) { - return YKPIV_GENERIC_ERROR; - } - - return _ykpiv_save_object(state, object_id, NULL, 0); - } - - // encode certificate data for storage - - // calculate the required length of the encoded object - req_len = 1 /* cert tag */ + 3 /* compression tag + data*/ + 2 /* lrc */; - req_len += _ykpiv_set_length(buf, data_len); - req_len += data_len; - - if (req_len < data_len) return YKPIV_SIZE_ERROR; /* detect overflow of unsigned size_t */ - if (req_len > _obj_size_max(state)) return YKPIV_SIZE_ERROR; /* obj_size_max includes limits for TLV encoding */ - - buf[offset++] = TAG_CERT; - offset += _ykpiv_set_length(buf + offset, data_len); - memcpy(buf + offset, data, data_len); - offset += data_len; - - // write compression info and LRC trailer - buf[offset++] = TAG_CERT_COMPRESS; - buf[offset++] = 0x01; - buf[offset++] = certinfo == YKPIV_CERTINFO_GZIP ? 0x01 : 0x00; - buf[offset++] = TAG_CERT_LRC; - buf[offset++] = 00; - - // write onto device - return _ykpiv_save_object(state, object_id, buf, offset); -} - -/* -** PIV Manager data helper functions -** -** These functions allow the PIV Manager to extend the YKPIV_OBJ_ADMIN_DATA object without having to change -** this implementation. New items may be added without modifying these functions. Data items are picked -** from the pivman_data buffer by tag, and replaced either in place if length allows or the data object is -** expanded to fit a new/updated data item. -*/ - -/* -** _get_metadata_item -** -** Parses the metadata blob, specified by data, looking for the specified tag. If found, the item is -** returned in pp_item and its size in pcb_item. -** -** If the item is not found, this function returns YKPIV_GENERIC_ERROR. -*/ -static ykpiv_rc _get_metadata_item(uint8_t *data, size_t cb_data, uint8_t tag, uint8_t **pp_item, size_t *pcb_item) { - uint8_t *p_temp = data; - size_t cb_temp = 0; - uint8_t tag_temp = 0; - - if (!data || !pp_item || !pcb_item) return YKPIV_GENERIC_ERROR; - - *pp_item = NULL; - *pcb_item = 0; - - while (p_temp < (data + cb_data)) { - tag_temp = *p_temp++; - - if (!_ykpiv_has_valid_length(p_temp, (data + cb_data - p_temp))) { - return YKPIV_SIZE_ERROR; - } - - p_temp += _ykpiv_get_length(p_temp, &cb_temp); - - if (tag_temp == tag) { - // found tag - break; - } - - p_temp += cb_temp; - } - - if (p_temp < (data + cb_data)) { - *pp_item = p_temp; - *pcb_item = cb_temp; - return YKPIV_OK; - } - - return YKPIV_GENERIC_ERROR; -} - -static int _get_length_size(size_t length) { - if (length < 0x80) { - return 1; - } - else if (length < 0xff) { - return 2; - } - else { - return 3; - } -} - -/* -** _set_metadata_item -** -** Adds or replaces a data item encoded in a metadata blob, specified by tag to the existing -** metadata blob (data) until it reaches the a maximum buffer size (cb_data_max). -** -** If adding/replacing the item would exceed cb_data_max, this function returns YKPIV_GENERIC_ERROR. -** -** The new size of the blob is returned in pcb_data. -*/ -static ykpiv_rc _set_metadata_item(uint8_t *data, size_t *pcb_data, size_t cb_data_max, uint8_t tag, uint8_t *p_item, size_t cb_item) { - uint8_t *p_temp = data; - size_t cb_temp = 0; - uint8_t tag_temp = 0; - size_t cb_len = 0; - uint8_t *p_next = NULL; - long cb_moved = 0; /* must be signed to have negative offsets */ - - if (!data || !pcb_data) return YKPIV_GENERIC_ERROR; - - while (p_temp < (data + *pcb_data)) { - tag_temp = *p_temp++; - cb_len = _ykpiv_get_length(p_temp, &cb_temp); - p_temp += cb_len; - - if (tag_temp == tag) { - /* found tag */ - - /* check length, if it matches, overwrite */ - if (cb_temp == cb_item) { - memcpy(p_temp, p_item, cb_item); - return YKPIV_OK; - } - - /* length doesn't match, expand/shrink to fit */ - p_next = p_temp + cb_temp; - cb_moved = (long)cb_item - (long)cb_temp + - ((long)(cb_item != 0 ? _get_length_size(cb_item) : -1 /* for tag, if deleting */) - - (long)cb_len); /* accounts for different length encoding */ - - /* length would cause buffer overflow, return error */ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wsign-conversion" - if ((size_t)(*pcb_data + cb_moved) > cb_data_max) { - return YKPIV_GENERIC_ERROR; - } -#pragma GCC diagnostic pop - - /* move remaining data */ - memmove(p_next + cb_moved, p_next, *pcb_data - (size_t)(p_next - data)); -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wsign-conversion" - *pcb_data += cb_moved; -#pragma GCC diagnostic pop - - /* re-encode item and insert */ - if (cb_item != 0) { - p_temp -= cb_len; - p_temp += _ykpiv_set_length(p_temp, cb_item); - memcpy(p_temp, p_item, cb_item); - } - - return YKPIV_OK; - } /* if tag found */ - - p_temp += cb_temp; - } - - if (cb_item == 0) { - /* we've been asked to delete an existing item that isn't in the blob */ - return YKPIV_OK; - } - - // we did not find an existing tag, append - p_temp = data + *pcb_data; - cb_len = (size_t)_get_length_size(cb_item); - - // length would cause buffer overflow, return error - if (*pcb_data + cb_len + cb_item > cb_data_max) { - return YKPIV_GENERIC_ERROR; - } - - *p_temp++ = tag; - p_temp += _ykpiv_set_length(p_temp, cb_item); - memcpy(p_temp, p_item, cb_item); - *pcb_data += 1 + cb_len + cb_item; - - return YKPIV_OK; -} - -/* -** _read_metadata -** -** Reads admin or protected data (specified by tag) from its associated object. -** -** The data stored in the object is parsed to ensure it has the correct tag and valid length. -** -** data must point to a buffer of at least CB_BUF_MAX bytes, and pcb_data should point to -** the size of data. -** -** To read from protected data, the pin must be verified prior to calling this function. -*/ -static ykpiv_rc _read_metadata(ykpiv_state *state, uint8_t tag, uint8_t* data, size_t* pcb_data) { - ykpiv_rc res = YKPIV_OK; - uint8_t *p_temp = NULL; - size_t cb_temp = 0; - int obj_id = 0; - - if (!data || !pcb_data || (CB_BUF_MAX > *pcb_data)) return YKPIV_GENERIC_ERROR; - - switch (tag) { - case TAG_ADMIN: obj_id = YKPIV_OBJ_ADMIN_DATA; break; - case TAG_PROTECTED: obj_id = YKPIV_OBJ_PRINTED; break; - default: return YKPIV_INVALID_OBJECT; - } - - cb_temp = *pcb_data; - *pcb_data = 0; - - if (YKPIV_OK != (res = _ykpiv_fetch_object(state, obj_id, data, (unsigned long*)&cb_temp))) { - return res; - } - - if (cb_temp < CB_OBJ_TAG_MIN) return YKPIV_GENERIC_ERROR; - - p_temp = data; - - if (tag != *p_temp++) return YKPIV_GENERIC_ERROR; - - p_temp += _ykpiv_get_length(p_temp, pcb_data); - - if (*pcb_data > (cb_temp - (size_t)(p_temp - data))) { - *pcb_data = 0; - return YKPIV_GENERIC_ERROR; - } - - memmove(data, p_temp, *pcb_data); - - return YKPIV_OK; -} - -/* -** _write_metadata -** -** Writes admin/protected data, specified by tag to its associated object. -** -** To delete the metadata, set data to NULL and cb_data to 0. -** -** To write protected data, the pin must be verified prior to calling this function. -*/ -static ykpiv_rc _write_metadata(ykpiv_state *state, uint8_t tag, uint8_t *data, size_t cb_data) { - ykpiv_rc res = YKPIV_OK; - uint8_t buf[CB_OBJ_MAX]; /* XXX REMEMBER TO ZERO */ - uint8_t *pTemp = buf; - int obj_id = 0; - - if (cb_data > (_obj_size_max(state) - CB_OBJ_TAG_MAX)) { - return YKPIV_GENERIC_ERROR; - } - - switch (tag) { - case TAG_ADMIN: obj_id = YKPIV_OBJ_ADMIN_DATA; break; - case TAG_PROTECTED: obj_id = YKPIV_OBJ_PRINTED; break; - default: return YKPIV_INVALID_OBJECT; - } - - if (!data || (0 == cb_data)) { - // deleting metadata - res = _ykpiv_save_object(state, obj_id, NULL, 0); - } - else { - *pTemp++ = tag; - pTemp += _ykpiv_set_length(pTemp, cb_data); - - memcpy(pTemp, data, cb_data); - pTemp += cb_data; - - res = _ykpiv_save_object(state, obj_id, buf, (size_t)(pTemp - buf)); - } - - return res; -} diff --git a/src/version.c b/src/version.c deleted file mode 100644 index 89fe4ab..0000000 --- a/src/version.c +++ /dev/null @@ -1,143 +0,0 @@ -/* - * 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-version.h" - -#include - -#include - -/* From https://article.gmane.org/gmane.os.freebsd.devel.hackers/23606 */ -static int my_strverscmp (const char *s1, const char *s2) -{ - static const char *digits = "0123456789"; - size_t p1, p2; - - p1 = strcspn (s1, digits); - p2 = strcspn (s2, digits); - while (p1 == p2 && s1[p1] != '\0' && s2[p2] != '\0') { - int ret, lz1, lz2; - /* Different prefix */ - if ((ret = strncmp (s1, s2, p1)) != 0) - return ret; - - s1 += p1; - s2 += p2; - - lz1 = lz2 = 0; - if (*s1 == '0') - lz1 = 1; - if (*s2 == '0') - lz2 = 1; - - if (lz1 > lz2) - return -1; - else if (lz1 < lz2) - return 1; - else if (lz1 == 1) { - /* - * If the common prefix for s1 and s2 consists only of zeros, then the - * "longer" number has to compare less. Otherwise the comparison needs - * to be numerical (just fallthrough). See - * https://refspecs.linuxfoundation.org/LSB_2.0.1/LSB-generic/ - * https://refspecs.linuxfoundation.org/LSB_2.0.1/LSB-generic/LSB-generic/baselib-strverscmp.html - */ - while (*s1 == '0' && *s2 == '0') { - ++s1; - ++s2; - } - - p1 = strspn (s1, digits); - p2 = strspn (s2, digits); - - /* Catch empty strings */ - if (p1 == 0 && p2 > 0) - return 1; - else if (p2 == 0 && p1 > 0) - return -1; - - /* Prefixes are not same */ - if (*s1 != *s2 && *s1 != '0' && *s2 != '0') { - if (p1 < p2) - return 1; - else if (p1 > p2) - return -1; - } else { - if (p1 < p2) - ret = strncmp (s1, s2, p1); - else if (p1 > p2) - ret = strncmp (s1, s2, p2); - if (ret != 0) - return ret; - } - } - - p1 = strspn (s1, digits); - p2 = strspn (s2, digits); - - if (p1 < p2) - return -1; - else if (p1 > p2) - return 1; - else if ((ret = strncmp (s1, s2, p1)) != 0) - return ret; - - /* Numbers are equal or not present, try with next ones. */ - s1 += p1; - s2 += p2; - p1 = strcspn (s1, digits); - p2 = strcspn (s2, digits); - } - - return strcmp (s1, s2); -} - -/** - * ykpiv_check_version: - * @req_version: Required version number, or NULL. - * - * Check that the version of the library is at minimum the requested - * one and return the version string; return NULL if the condition is - * not satisfied. If a NULL is passed to this function, no check is - * done, but the version string is simply returned. - * - * See %YKPIV_VERSION_STRING for a suitable @req_version string. - * - * Return value: Version string of run-time library, or NULL if the - * run-time library does not meet the required version number. - */ -const char * ykpiv_check_version (const char *req_version) -{ - if (!req_version - || my_strverscmp (req_version, YKPIV_VERSION_STRING) <= 0) - return YKPIV_VERSION_STRING; - - return NULL; -} diff --git a/src/ykpiv-version.h b/src/ykpiv-version.h deleted file mode 100644 index 64976d8..0000000 --- a/src/ykpiv-version.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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. - * - */ - -#ifndef YKPIV_VERSION_H -#define YKPIV_VERSION_H - -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * YKPIV_VERSION_STRING - * - * Pre-processor symbol with a string that describe the header file - * version number. Used together with ykneomgr_check_version() to verify - * header file and run-time library consistency. - */ -#define YKPIV_VERSION_STRING "@VERSION@" - - /** - * YKPIV_VERSION_NUMBER - * - * Pre-processor symbol with a hexadecimal value describing the header - * file version number. For example, when the header version is 1.2.3 - * this symbol will have the value 0x01020300. The last two digits - * are only used between public releases, and will otherwise be 00. - */ -#define YKPIV_VERSION_NUMBER @YKPIV_VERSION_NUMBER@ - - /** - * YKPIV_VERSION_MAJOR - * - * Pre-processor symbol with a decimal value that describe the major - * level of the header file version number. For example, when the - * header version is 1.2.3 this symbol will be 1. - */ -#define YKPIV_VERSION_MAJOR @YKPIV_VERSION_MAJOR@ - - /** - * YKPIV_VERSION_MINOR - * - * Pre-processor symbol with a decimal value that describe the minor - * level of the header file version number. For example, when the - * header version is 1.2.3 this symbol will be 2. - */ -#define YKPIV_VERSION_MINOR @YKPIV_VERSION_MINOR@ - - /** - * YKPIV_VERSION_PATCH - * - * Pre-processor symbol with a decimal value that describe the patch - * level of the header file version number. For example, when the - * header version is 1.2.3 this symbol will be 3. - */ -#define YKPIV_VERSION_PATCH @YKPIV_VERSION_PATCH@ - - const char *ykpiv_check_version (const char *req_version); - - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/ykpiv-version.h.in b/src/ykpiv-version.h.in deleted file mode 100644 index 64976d8..0000000 --- a/src/ykpiv-version.h.in +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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. - * - */ - -#ifndef YKPIV_VERSION_H -#define YKPIV_VERSION_H - -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * YKPIV_VERSION_STRING - * - * Pre-processor symbol with a string that describe the header file - * version number. Used together with ykneomgr_check_version() to verify - * header file and run-time library consistency. - */ -#define YKPIV_VERSION_STRING "@VERSION@" - - /** - * YKPIV_VERSION_NUMBER - * - * Pre-processor symbol with a hexadecimal value describing the header - * file version number. For example, when the header version is 1.2.3 - * this symbol will have the value 0x01020300. The last two digits - * are only used between public releases, and will otherwise be 00. - */ -#define YKPIV_VERSION_NUMBER @YKPIV_VERSION_NUMBER@ - - /** - * YKPIV_VERSION_MAJOR - * - * Pre-processor symbol with a decimal value that describe the major - * level of the header file version number. For example, when the - * header version is 1.2.3 this symbol will be 1. - */ -#define YKPIV_VERSION_MAJOR @YKPIV_VERSION_MAJOR@ - - /** - * YKPIV_VERSION_MINOR - * - * Pre-processor symbol with a decimal value that describe the minor - * level of the header file version number. For example, when the - * header version is 1.2.3 this symbol will be 2. - */ -#define YKPIV_VERSION_MINOR @YKPIV_VERSION_MINOR@ - - /** - * YKPIV_VERSION_PATCH - * - * Pre-processor symbol with a decimal value that describe the patch - * level of the header file version number. For example, when the - * header version is 1.2.3 this symbol will be 3. - */ -#define YKPIV_VERSION_PATCH @YKPIV_VERSION_PATCH@ - - const char *ykpiv_check_version (const char *req_version); - - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/ykpiv.c b/src/ykpiv.c deleted file mode 100644 index a22aa42..0000000 --- a/src/ykpiv.c +++ /dev/null @@ -1,1978 +0,0 @@ - /* - * 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. - * - */ -/** @file */ - -#include -#include -#include -#include -#include - -#include "internal.h" -#include "ykpiv.h" - -/** - * DISABLE_PIN_CACHE - disable in-RAM cache of PIN - * - * By default, the PIN is cached in RAM when provided to \p ykpiv_verify() or - * changed with \p ykpiv_change_pin(). If the USB connection is lost between - * calls, the device will be re-authenticated on the next call using the cached - * PIN. The PIN is cleared with a call to \p ykpiv_done(). - * - * The PIN cache prevents problems with long-running applications losing their - * authentication in some cases, such as when a laptop sleeps. - * - * The cache can be disabled by setting this define to 1 if it is not desired - * to store the PIN in RAM. - * - */ -#ifndef DISABLE_PIN_CACHE -#define DISABLE_PIN_CACHE 0 -#endif - -/** - * ENABLE_APPLICATION_RESELECT - re-select application for all public API calls - * - * If this is enabled, every public call (prefixed with \r ykpiv_) will check - * that the PIV application is currently selected, or re-select it if it is - * not. - * - * Auto re-selection allows a long-running PIV application to cooperate on - * a system that may simultaneously use the non-PIV applications of connected - * devices. - * - * This is \b DANGEROUS - with this enabled, slots with the policy - * \p YKPIV_PINPOLICY_ALWAYS will not be accessible. - * - */ -#ifndef ENABLE_APPLICATION_RESELECTION -#define ENABLE_APPLICATION_RESELECTION 0 -#endif - - -/** - * ENABLE_IMPLICIT_TRANSACTIONS - call SCardBeginTransaction for all public API calls - * - * If this is enabled, every public call (prefixed with \r ykpiv_) will call - * SCardBeginTransaction on entry and SCardEndTransaction on exit. - * - * For applications that do not do their own transaction management, like the piv tool - * itself, retaining the default setting of enabled can allow other applications and - * threads to make calls to CCID that can interfere with multi-block data sent to the - * card via SCardTransmitData. - */ -#ifndef ENABLE_IMPLICIT_TRANSACTIONS -#define ENABLE_IMPLICIT_TRANSACTIONS 1 -#endif - -/** - * Platform specific definitions - */ -#ifdef _MSC_VER -#define strncasecmp _strnicmp -#endif - -#define YKPIV_MGM_DEFAULT "\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" - -static ykpiv_rc _cache_pin(ykpiv_state *state, const char *pin, size_t len); -static ykpiv_rc _ykpiv_get_serial(ykpiv_state *state, uint32_t *p_serial, bool force); -static ykpiv_rc _ykpiv_get_version(ykpiv_state *state, ykpiv_version_t *p_version); - -static unsigned const char *aid = { - 0xa0, 0x00, 0x00, 0x03, 0x08 -}; - -static void* _default_alloc(void *data, size_t cb) { - (void)data; - return calloc(cb, 1); -} - -static void * _default_realloc(void *data, void *p, size_t cb) { - (void)data; - return realloc(p, cb); -} - -static void _default_free(void *data, void *p) { - (void)data; - free(p); -} - -ykpiv_allocator _default_allocator = { - .pfn_alloc = _default_alloc, - .pfn_realloc = _default_realloc, - .pfn_free = _default_free, - .alloc_data = 0 -}; - -/* Memory helper functions */ - -void* _ykpiv_alloc(ykpiv_state *state, size_t size) { - if (!state || !(state->allocator.pfn_alloc)) return NULL; - return state->allocator.pfn_alloc(state->allocator.alloc_data, size); -} - -void* _ykpiv_realloc(ykpiv_state *state, void *address, size_t size) { - if (!state || !(state->allocator.pfn_realloc)) return NULL; - return state->allocator.pfn_realloc(state->allocator.alloc_data, address, size); -} - -void _ykpiv_free(ykpiv_state *state, void *data) { - if (!data || !state || (!(state->allocator.pfn_free))) return; - state->allocator.pfn_free(state->allocator.alloc_data, data); -} - -static void dump_hex(const unsigned char *buf, unsigned int len) { - unsigned int i; - for (i = 0; i < len; i++) { - fprintf(stderr, "%02x ", buf[i]); - } -} - -unsigned int _ykpiv_set_length(unsigned char *buffer, size_t length) { - if(length < 0x80) { - *buffer++ = (unsigned char)length; - return 1; - } else if(length < 0x100) { - *buffer++ = 0x81; - *buffer++ = (unsigned char)length; - return 2; - } else { - *buffer++ = 0x82; - *buffer++ = (length >> 8) & 0xff; - *buffer++ = (unsigned char)length & 0xff; - return 3; - } -} - -unsigned int _ykpiv_get_length(const unsigned char *buffer, size_t *len) { - if(buffer[0] < 0x81) { - *len = buffer[0]; - return 1; - } else if((*buffer & 0x7f) == 1) { - *len = buffer[1]; - return 2; - } else if((*buffer & 0x7f) == 2) { - size_t tmp = buffer[1]; - *len = (tmp << 8) + buffer[2]; - return 3; - } - return 0; -} - -bool _ykpiv_has_valid_length(const unsigned char* buffer, size_t len) { - if ((buffer[0] < 0x81) && (len > 0)) { - return true; - } - else if (((*buffer & 0x7f) == 1) && (len > 1)) { - return true; - } - else if (((*buffer & 0x7f) == 2) && (len > 2)) { - return true; - } - return false; -} - -static unsigned char *set_object(int object_id, unsigned char *buffer) { - *buffer++ = 0x5c; - if(object_id == YKPIV_OBJ_DISCOVERY) { - *buffer++ = 1; - *buffer++ = YKPIV_OBJ_DISCOVERY; - } else if(object_id > 0xffff && object_id <= 0xffffff) { - *buffer++ = 3; - *buffer++ = (object_id >> 16) & 0xff; - *buffer++ = (object_id >> 8) & 0xff; - *buffer++ = object_id & 0xff; - } - return buffer; -} - -ykpiv_rc ykpiv_init_with_allocator(ykpiv_state **state, int verbose, const ykpiv_allocator *allocator) { - ykpiv_state *s; - if (NULL == state) { - return YKPIV_GENERIC_ERROR; - } - if (NULL == allocator || !allocator->pfn_alloc || !allocator->pfn_realloc || !allocator->pfn_free) { - return YKPIV_MEMORY_ERROR; - } - - s = allocator->pfn_alloc(allocator->alloc_data, sizeof(ykpiv_state)); - if (NULL == s) { - return YKPIV_MEMORY_ERROR; - } - - memset(s, 0, sizeof(ykpiv_state)); - s->pin = NULL; - s->allocator = *allocator; - s->verbose = verbose; - s->context = (SCARDCONTEXT)-1; - *state = s; - return YKPIV_OK; -} - -ykpiv_rc ykpiv_init(ykpiv_state **state, int verbose) { - return ykpiv_init_with_allocator(state, verbose, &_default_allocator); -} - -static ykpiv_rc _ykpiv_done(ykpiv_state *state, bool disconnect) { - if (disconnect) - ykpiv_disconnect(state); - _cache_pin(state, NULL, 0); - _ykpiv_free(state, state); - return YKPIV_OK; -} - -ykpiv_rc ykpiv_done_with_external_card(ykpiv_state *state) { - return _ykpiv_done(state, false); -} - -ykpiv_rc ykpiv_done(ykpiv_state *state) { - return _ykpiv_done(state, true); -} - -ykpiv_rc ykpiv_disconnect(ykpiv_state *state) { - if(state->card) { - SCardDisconnect(state->card, SCARD_RESET_CARD); - state->card = 0; - } - - if(SCardIsValidContext(state->context) == SCARD_S_SUCCESS) { - SCardReleaseContext(state->context); - state->context = (SCARDCONTEXT)-1; - } - - return YKPIV_OK; -} - -ykpiv_rc _ykpiv_select_application(ykpiv_state *state) { - APDU apdu; - unsigned char data[0xff]; - uint32_t recv_len = sizeof(data); - int sw; - ykpiv_rc res = YKPIV_OK; - - /* XXX FIX THIS!!! */ -/* - memset(apdu.raw, 0, sizeof(apdu)); - apdu.st.ins = YKPIV_INS_SELECT_APPLICATION; - apdu.st.p1 = 0x04; - apdu.st.lc = sizeof(aid); - memcpy(apdu.st.data, aid, sizeof(aid)); -*/ - - if((res = _send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) { - if(state->verbose) { - fprintf(stderr, "Failed communicating with card: '%s'\n", ykpiv_strerror(res)); - } - return res; - } - else if(sw != SW_SUCCESS) { - if(state->verbose) { - fprintf(stderr, "Failed selecting application: %04x\n", sw); - } - return YKPIV_GENERIC_ERROR; - } - - /* now that the PIV application is selected, retrieve the version - * and serial number. Previously the NEO/YK4 required switching - * to the yk applet to retrieve the serial, YK5 implements this - * as a PIV applet command. Unfortunately, this change requires - * that we retrieve the version number first, so that get_serial - * can determine how to get the serial number, which for the NEO/Yk4 - * will result in another selection of the PIV applet. */ - - res = _ykpiv_get_version(state, NULL); - if (res != YKPIV_OK) { - if (state->verbose) { - fprintf(stderr, "Failed to retrieve version: '%s'\n", ykpiv_strerror(res)); - } - } - - res = _ykpiv_get_serial(state, NULL, false); - if (res != YKPIV_OK) { - if (state->verbose) { - fprintf(stderr, "Failed to retrieve serial number: '%s'\n", ykpiv_strerror(res)); - } - res = YKPIV_OK; - } - - return res; -} - -ykpiv_rc _ykpiv_ensure_application_selected(ykpiv_state *state) { - ykpiv_rc res = YKPIV_OK; - -#if ENABLE_APPLICATION_RESELECTION - if (NULL == state) { - return YKPIV_GENERIC_ERROR; - } - - res = ykpiv_verify(state, NULL, 0); - - if ((YKPIV_OK != res) && (YKPIV_WRONG_PIN != res)) { - res = _ykpiv_select_application(state); - } - else { - res = YKPIV_OK; - } - - return res; -#else - (void)state; - return res; -#endif -} - -static ykpiv_rc _ykpiv_connect(ykpiv_state *state, uintptr_t context, uintptr_t card) { - ykpiv_rc res = YKPIV_OK; - - if (NULL == state) { - return YKPIV_GENERIC_ERROR; - } - - // if the context has changed, and the new context is not valid, return an error - if ((context != state->context) && (SCARD_S_SUCCESS != SCardIsValidContext(context))) { - return YKPIV_PCSC_ERROR; - } - - // if card handle has changed, determine if handle is valid (less efficient, but complete) - if ((card != state->card)) { - char reader[CB_BUF_MAX]; - pcsc_word reader_len = sizeof(reader); - uint8_t atr[CB_ATR_MAX]; - pcsc_word atr_len = sizeof(atr); - - // Cannot set the reader len to NULL. Confirmed in OSX 10.10, so we have to retrieve it even though we don't need it. - if (SCARD_S_SUCCESS != SCardStatus(card, reader, &reader_len, NULL, NULL, atr, &atr_len)) { - return YKPIV_PCSC_ERROR; - } - - state->is_neo = (((sizeof(YKPIV_ATR_NEO_R3) - 1) == atr_len) && (0 == memcmp(YKPIV_ATR_NEO_R3, atr, atr_len))); - } - - state->context = context; - state->card = card; - - /* - ** Do not select the applet here, as we need to accommodate commands that are - ** sensitive to re-select (custom apdu/auth). All commands that can handle explicit - ** selection already check the applet state and select accordingly anyway. - ** ykpiv_verify_select is supplied for those who want to select explicitly. - ** - ** The applet _is_ selected by ykpiv_connect(), but is not selected when bypassing - ** it with ykpiv_connect_with_external_card(). - */ - return res; -} - -ykpiv_rc ykpiv_connect_with_external_card(ykpiv_state *state, uintptr_t context, uintptr_t card) { - return _ykpiv_connect(state, context, card); -} - -ykpiv_rc ykpiv_connect(ykpiv_state *state, const char *wanted) { - pcsc_word active_protocol; - char reader_buf[2048]; - size_t num_readers = sizeof(reader_buf); - long rc; - char *reader_ptr; - SCARDHANDLE card = (SCARDHANDLE)-1; - - ykpiv_rc ret = ykpiv_list_readers(state, reader_buf, &num_readers); - if(ret != YKPIV_OK) { - return ret; - } - - for(reader_ptr = reader_buf; *reader_ptr != '\0'; reader_ptr += strlen(reader_ptr) + 1) { - if(wanted) { - char *ptr = reader_ptr; - bool found = false; - do { - if(strlen(ptr) < strlen(wanted)) { - break; - } - if(strncasecmp(ptr, wanted, strlen(wanted)) == 0) { - found = true; - break; - } - } while(*ptr++); - - if(found == false) { - if(state->verbose) { - fprintf(stderr, "skipping reader '%s' since it doesn't match '%s'.\n", reader_ptr, wanted); - } - continue; - } - } - if(state->verbose) { - fprintf(stderr, "trying to connect to reader '%s'.\n", reader_ptr); - } - rc = SCardConnect(state->context, reader_ptr, SCARD_SHARE_SHARED, - SCARD_PROTOCOL_T1, &card, &active_protocol); - if(rc != SCARD_S_SUCCESS) - { - if(state->verbose) { - fprintf(stderr, "SCardConnect failed, rc=%08lx\n", rc); - } - continue; - } - - // at this point, card should not equal state->card, to allow _ykpiv_connect() to determine device type - if (YKPIV_OK == _ykpiv_connect(state, state->context, card)) { - /* - * Select applet. This is done here instead of in _ykpiv_connect() because - * you may not want to select the applet when connecting to a card handle that - * was supplied by an external library. - */ - if (YKPIV_OK != (ret = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; -#if ENABLE_APPLICATION_RESELECTION - ret = _ykpiv_ensure_application_selected(state); -#else - ret = _ykpiv_select_application(state); -#endif - _ykpiv_end_transaction(state); - return ret; - } - } - - if(*reader_ptr == '\0') { - if(state->verbose) { - fprintf(stderr, "error: no usable reader found.\n"); - } - SCardReleaseContext(state->context); - state->context = (SCARDCONTEXT)-1; - return YKPIV_PCSC_ERROR; - } - - return YKPIV_GENERIC_ERROR; -} - -static ykpiv_rc reconnect(ykpiv_state *state) { - pcsc_word active_protocol = 0; - long rc; - ykpiv_rc res; - int tries; - if(state->verbose) { - fprintf(stderr, "trying to reconnect to current reader.\n"); - } - rc = SCardReconnect(state->card, SCARD_SHARE_SHARED, - SCARD_PROTOCOL_T1, SCARD_RESET_CARD, &active_protocol); - if(rc != SCARD_S_SUCCESS) { - if(state->verbose) { - fprintf(stderr, "SCardReconnect failed, rc=%08lx\n", rc); - } - return YKPIV_PCSC_ERROR; - } - if ((res = _ykpiv_select_application(state)) != YKPIV_OK) { - return res; - } - if (state->pin) { - return ykpiv_verify(state, state->pin, &tries); - } - return YKPIV_OK; -} - -ykpiv_rc ykpiv_list_readers(ykpiv_state *state, char *readers, size_t *len) { - pcsc_word num_readers = 0; - long rc; - - if(SCardIsValidContext(state->context) != SCARD_S_SUCCESS) { - rc = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &state->context); - if (rc != SCARD_S_SUCCESS) { - if(state->verbose) { - fprintf (stderr, "error: SCardEstablishContext failed, rc=%08lx\n", rc); - } - return YKPIV_PCSC_ERROR; - } - } - - rc = SCardListReaders(state->context, NULL, NULL, &num_readers); - if (rc != SCARD_S_SUCCESS) { - if(state->verbose) { - fprintf (stderr, "error: SCardListReaders failed, rc=%08lx\n", rc); - } - SCardReleaseContext(state->context); - state->context = (SCARDCONTEXT)-1; - return YKPIV_PCSC_ERROR; - } - - if (num_readers > *len) { - num_readers = (pcsc_word)*len; - } else if (num_readers < *len) { - *len = (size_t)num_readers; - } - - rc = SCardListReaders(state->context, NULL, readers, &num_readers); - if (rc != SCARD_S_SUCCESS) - { - if(state->verbose) { - fprintf (stderr, "error: SCardListReaders failed, rc=%08lx\n", rc); - } - SCardReleaseContext(state->context); - state->context = (SCARDCONTEXT)-1; - return YKPIV_PCSC_ERROR; - } - - *len = num_readers; - - return YKPIV_OK; -} - -ykpiv_rc _ykpiv_begin_transaction(ykpiv_state *state) { -#if ENABLE_IMPLICIT_TRANSACTIONS - long rc; - - rc = SCardBeginTransaction(state->card); - if((long)((unsigned long)rc & 0xFFFFFFFF) == SCARD_W_RESET_CARD) { - ykpiv_rc res = YKPIV_OK; - if((res = reconnect(state)) != YKPIV_OK) { - return res; - } - rc = SCardBeginTransaction(state->card); - } - if(rc != SCARD_S_SUCCESS) { - if(state->verbose) { - fprintf(stderr, "error: Failed to begin pcsc transaction, rc=%08lx\n", rc); - } - return YKPIV_PCSC_ERROR; - } -#endif /* ENABLE_IMPLICIT_TRANSACTIONS */ - - return YKPIV_OK; -} - -ykpiv_rc _ykpiv_end_transaction(ykpiv_state *state) { -#if ENABLE_IMPLICIT_TRANSACTIONS - long rc = SCardEndTransaction(state->card, SCARD_LEAVE_CARD); - if(rc != SCARD_S_SUCCESS && state->verbose) { - fprintf(stderr, "error: Failed to end pcsc transaction, rc=%08lx\n", rc); - return YKPIV_PCSC_ERROR; - } -#endif /* ENABLE_IMPLICIT_TRANSACTIONS */ - return YKPIV_OK; -} - -ykpiv_rc _ykpiv_transfer_data(ykpiv_state *state, const unsigned char *templ, - const unsigned char *in_data, - long in_len, - unsigned char *out_data, - unsigned long *out_len, - int *sw) { - const unsigned char *in_ptr = in_data; - unsigned long max_out = *out_len; - ykpiv_rc res; - *out_len = 0; - - do { - size_t this_size = 0xff; - unsigned char data[261]; - uint32_t recv_len = sizeof(data); - APDU apdu; - - /* XXX FIX THIS!!! */ -/* - memset(apdu.raw, 0, sizeof(apdu.raw)); - memcpy(apdu.raw, templ, 4); - if(in_ptr + 0xff < in_data + in_len) { - apdu.st.cla = 0x10; - } else { - this_size = (size_t)((in_data + in_len) - in_ptr); - } - */ - if(state->verbose > 2) { - fprintf(stderr, "Going to send %lu bytes in this go.\n", (unsigned long)this_size); - } - /* XXX FIX THIS!!! */ - /* - apdu.st.lc = (unsigned char)this_size; - memcpy(apdu.st.data, in_ptr, this_size); - */ - res = _send_data(state, &apdu, data, &recv_len, sw); - if(res != YKPIV_OK) { - goto Cleanup; - } else if(*sw != SW_SUCCESS && *sw >> 8 != 0x61) { - goto Cleanup; - } - if(*out_len + recv_len - 2 > max_out) { - if(state->verbose) { - fprintf(stderr, "Output buffer to small, wanted to write %lu, max was %lu.\n", *out_len + recv_len - 2, max_out); - } - res = YKPIV_SIZE_ERROR; - goto Cleanup; - } - if(out_data) { - memcpy(out_data, data, recv_len - 2); - out_data += recv_len - 2; - *out_len += recv_len - 2; - } - in_ptr += this_size; - } while(in_ptr < in_data + in_len); - while(*sw >> 8 == 0x61) { - APDU apdu; - unsigned char data[261]; - uint32_t recv_len = sizeof(data); - - if(state->verbose > 2) { - fprintf(stderr, "The card indicates there is %d bytes more data for us.\n", *sw & 0xff); - } - - /* XXX FIX THIS!!! */ - /* - memset(apdu.raw, 0, sizeof(apdu.raw)); - apdu.st.ins = YKPIV_INS_GET_RESPONSE_APDU; - */ - res = _send_data(state, &apdu, data, &recv_len, sw); - if(res != YKPIV_OK) { - goto Cleanup; - } else if(*sw != SW_SUCCESS && *sw >> 8 != 0x61) { - goto Cleanup; - } - if(*out_len + recv_len - 2 > max_out) { - if(state->verbose) { - fprintf(stderr, "Output buffer to small, wanted to write %lu, max was %lu.", *out_len + recv_len - 2, max_out); - } - res = YKPIV_SIZE_ERROR; - goto Cleanup; - } - if(out_data) { - memcpy(out_data, data, recv_len - 2); - out_data += recv_len - 2; - *out_len += recv_len - 2; - } - } -Cleanup: - return res; -} - -ykpiv_rc ykpiv_transfer_data(ykpiv_state *state, const unsigned char *templ, - const unsigned char *in_data, long in_len, - unsigned char *out_data, unsigned long *out_len, int *sw) { - ykpiv_rc res; - - if ((res = _ykpiv_begin_transaction(state)) != YKPIV_OK) { - *out_len = 0; - return YKPIV_PCSC_ERROR; - } - res = _ykpiv_transfer_data(state, templ, in_data, in_len, out_data, out_len, sw); - _ykpiv_end_transaction(state); - return res; -} - -ykpiv_rc _send_data(ykpiv_state *state, APDU *apdu, - unsigned char *data, uint32_t *recv_len, int *sw) { - long rc; - unsigned int send_len = 0; /* XXX FIX THIS!!! (unsigned int)apdu->st.lc + 5; */ - pcsc_word tmp_len = *recv_len; - - /* XXX FIX THIS!!! */ - /* - if(state->verbose > 1) { - fprintf(stderr, "> "); - dump_hex(apdu->raw, send_len); - fprintf(stderr, "\n"); - } - - rc = SCardTransmit(state->card, SCARD_PCI_T1, apdu->raw, send_len, NULL, data, &tmp_len); - if(rc != SCARD_S_SUCCESS) { - if(state->verbose) { - fprintf (stderr, "error: SCardTransmit failed, rc=%08lx\n", rc); - } - return YKPIV_PCSC_ERROR; - } - */ - *recv_len = (uint32_t)tmp_len; - - if(state->verbose > 1) { - fprintf(stderr, "< "); - dump_hex(data, *recv_len); - fprintf(stderr, "\n"); - } - if(*recv_len >= 2) { - *sw = (data[*recv_len - 2] << 8) | data[*recv_len - 1]; - } else { - *sw = 0; - } - return YKPIV_OK; -} - -ykpiv_rc ykpiv_authenticate(ykpiv_state *state, unsigned const char *key) { - APDU apdu; - unsigned char data[261]; - unsigned char challenge[8]; - uint32_t recv_len = sizeof(data); - int sw; - ykpiv_rc res; - des_rc drc = DES_OK; - des_key* mgm_key = NULL; - size_t out_len = 0; - - if (NULL == state) return YKPIV_GENERIC_ERROR; - - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; - - if (NULL == key) { - /* use the derived mgm key to authenticate, if it hasn't been derived, use default */ - key = (unsigned const char*)YKPIV_MGM_DEFAULT; - } - - /* set up our key */ - if (DES_OK != des_import_key(DES_TYPE_3DES, key, CB_MGM_KEY, &mgm_key)) { - res = YKPIV_ALGORITHM_ERROR; - goto Cleanup; - } - - /* get a challenge from the card */ - /* XXX FIX THIS!!! */ - /* - { - memset(apdu.raw, 0, sizeof(apdu)); - apdu.st.ins = YKPIV_INS_AUTHENTICATE; - apdu.st.p1 = YKPIV_ALGO_3DES; // triple des - apdu.st.p2 = YKPIV_KEY_CARDMGM; // management key - apdu.st.lc = 0x04; - apdu.st.data[0] = 0x7c; - apdu.st.data[1] = 0x02; - apdu.st.data[2] = 0x80; - if ((res = _send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) { - goto Cleanup; - } - else if (sw != SW_SUCCESS) { - res = YKPIV_AUTHENTICATION_ERROR; - goto Cleanup; - } - memcpy(challenge, data + 4, 8); - }*/ - - /* send a response to the cards challenge and a challenge of our own. */ - /* XXX FIX THIS!!! */ - /* - { - unsigned char *dataptr = apdu.st.data; - unsigned char response[8]; - out_len = sizeof(response); - drc = des_decrypt(mgm_key, challenge, sizeof(challenge), response, &out_len); - - if (drc != DES_OK) { - res = YKPIV_AUTHENTICATION_ERROR; - goto Cleanup; - } - - recv_len = sizeof(data); - memset(apdu.raw, 0, sizeof(apdu)); - apdu.st.ins = YKPIV_INS_AUTHENTICATE; - apdu.st.p1 = YKPIV_ALGO_3DES; // triple des - apdu.st.p2 = YKPIV_KEY_CARDMGM; // management key - *dataptr++ = 0x7c; - *dataptr++ = 20; // 2 + 8 + 2 +8 - *dataptr++ = 0x80; - *dataptr++ = 8; - memcpy(dataptr, response, 8); - dataptr += 8; - *dataptr++ = 0x81; - *dataptr++ = 8; - if (PRNG_GENERAL_ERROR == _ykpiv_prng_generate(dataptr, 8)) { - if (state->verbose) { - fprintf(stderr, "Failed getting randomness for authentication.\n"); - } - res = YKPIV_RANDOMNESS_ERROR; - goto Cleanup; - } - memcpy(challenge, dataptr, 8); - dataptr += 8; - apdu.st.lc = (unsigned char)(dataptr - apdu.st.data); - if ((res = _send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) { - goto Cleanup; - } - else if (sw != SW_SUCCESS) { - res = YKPIV_AUTHENTICATION_ERROR; - goto Cleanup; - } - } - */ - - /* compare the response from the card with our challenge */ - { - unsigned char response[8]; - out_len = sizeof(response); - drc = des_encrypt(mgm_key, challenge, sizeof(challenge), response, &out_len); - - if (drc != DES_OK) { - res = YKPIV_AUTHENTICATION_ERROR; - goto Cleanup; - } - - if (memcmp(response, data + 4, 8) == 0) { - res = YKPIV_OK; - } - else { - res = YKPIV_AUTHENTICATION_ERROR; - } - } - -Cleanup: - - if (mgm_key) { - des_destroy_key(mgm_key); - } - - _ykpiv_end_transaction(state); - return res; -} - -ykpiv_rc ykpiv_set_mgmkey(ykpiv_state *state, const unsigned char *new_key) { - return ykpiv_set_mgmkey2(state, new_key, 0); -} - -ykpiv_rc ykpiv_set_mgmkey2(ykpiv_state *state, const unsigned char *new_key, const unsigned char touch) { - APDU apdu; - unsigned char data[261]; - uint32_t recv_len = sizeof(data); - int sw; - ykpiv_rc res = YKPIV_OK; - - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; - - if (yk_des_is_weak_key(new_key, DES_LEN_3DES)) { - if (state->verbose) { - fprintf(stderr, "Won't set new key '"); - dump_hex(new_key, DES_LEN_3DES); - fprintf(stderr, "' since it's weak (with odd parity).\n"); - } - res = YKPIV_KEY_ERROR; - goto Cleanup; - } - - /* XXX FIX THIS!!! */ - /* - memset(apdu.raw, 0, sizeof(apdu)); - apdu.st.ins = YKPIV_INS_SET_MGMKEY; - apdu.st.p1 = 0xff; - if (touch == 0) { - apdu.st.p2 = 0xff; - } - else if (touch == 1) { - apdu.st.p2 = 0xfe; - } - else { - res = YKPIV_GENERIC_ERROR; - goto Cleanup; - } - - apdu.st.lc = DES_LEN_3DES + 3; - apdu.st.data[0] = YKPIV_ALGO_3DES; - apdu.st.data[1] = YKPIV_KEY_CARDMGM; - apdu.st.data[2] = DES_LEN_3DES; - memcpy(apdu.st.data + 3, new_key, DES_LEN_3DES); - */ - - if ((res = _send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) { - goto Cleanup; - } - else if (sw == SW_SUCCESS) { - goto Cleanup; - } - res = YKPIV_GENERIC_ERROR; - -Cleanup: - yc_memzero(&apdu, sizeof(APDU)); - _ykpiv_end_transaction(state); - return res; -} - -static char *hex_translate = "0123456789abcdef"; - -ykpiv_rc ykpiv_hex_decode(const char *hex_in, size_t in_len, - unsigned char *hex_out, size_t *out_len) { - - size_t i; - bool first = true; - if(*out_len < in_len / 2) { - return YKPIV_SIZE_ERROR; - } else if(in_len % 2 != 0) { - return YKPIV_SIZE_ERROR; - } - *out_len = in_len / 2; - for(i = 0; i < in_len; i++) { - char *ind_ptr = strchr(hex_translate, tolower(*hex_in++)); - int index = 0; - if(ind_ptr) { - index = (int)(ind_ptr - hex_translate); - } else { - return YKPIV_PARSE_ERROR; - } - if(first) { - *hex_out = index << 4; - } else { - *hex_out++ |= index; - } - first = !first; - } - return YKPIV_OK; -} - -static ykpiv_rc _general_authenticate(ykpiv_state *state, - const unsigned char *sign_in, size_t in_len, - unsigned char *out, size_t *out_len, - unsigned char algorithm, unsigned char key, bool decipher) { - unsigned char indata[1024]; - unsigned char *dataptr = indata; - unsigned char data[1024]; - unsigned char *templ = {0, YKPIV_INS_AUTHENTICATE, algorithm, key}; - unsigned long recv_len = sizeof(data); - size_t key_len = 0; - int sw = 0; - size_t bytes; - size_t len = 0; - ykpiv_rc res; - - switch(algorithm) { - case YKPIV_ALGO_RSA1024: - key_len = 128; - // fall through - case YKPIV_ALGO_RSA2048: - if(key_len == 0) { - key_len = 256; - } - if(in_len != key_len) { - return YKPIV_SIZE_ERROR; - } - break; - case YKPIV_ALGO_ECCP256: - key_len = 32; - // fall through - case YKPIV_ALGO_ECCP384: - if(key_len == 0) { - key_len = 48; - } - if(!decipher && in_len > key_len) { - return YKPIV_SIZE_ERROR; - } else if(decipher && in_len != (key_len * 2) + 1) { - return YKPIV_SIZE_ERROR; - } - break; - default: - return YKPIV_ALGORITHM_ERROR; - } - - if(in_len < 0x80) { - bytes = 1; - } else if(in_len < 0xff) { - bytes = 2; - } else { - bytes = 3; - } - - *dataptr++ = 0x7c; - dataptr += _ykpiv_set_length(dataptr, in_len + bytes + 3); - *dataptr++ = 0x82; - *dataptr++ = 0x00; - *dataptr++ = YKPIV_IS_EC(algorithm) && decipher ? 0x85 : 0x81; - dataptr += _ykpiv_set_length(dataptr, in_len); - memcpy(dataptr, sign_in, (size_t)in_len); - dataptr += in_len; - - if((res = ykpiv_transfer_data(state, templ, indata, (long)(dataptr - indata), data, - &recv_len, &sw)) != YKPIV_OK) { - if(state->verbose) { - fprintf(stderr, "Sign command failed to communicate.\n"); - } - return res; - } else if(sw != SW_SUCCESS) { - if(state->verbose) { - fprintf(stderr, "Failed sign command with code %x.\n", sw); - } - if (sw == SW_ERR_SECURITY_STATUS) - return YKPIV_AUTHENTICATION_ERROR; - else - return YKPIV_GENERIC_ERROR; - } - /* skip the first 7c tag */ - if(data[0] != 0x7c) { - if(state->verbose) { - fprintf(stderr, "Failed parsing signature reply.\n"); - } - return YKPIV_PARSE_ERROR; - } - dataptr = data + 1; - dataptr += _ykpiv_get_length(dataptr, &len); - /* skip the 82 tag */ - if(*dataptr != 0x82) { - if(state->verbose) { - fprintf(stderr, "Failed parsing signature reply.\n"); - } - return YKPIV_PARSE_ERROR; - } - dataptr++; - dataptr += _ykpiv_get_length(dataptr, &len); - if(len > *out_len) { - if(state->verbose) { - fprintf(stderr, "Wrong size on output buffer.\n"); - } - return YKPIV_SIZE_ERROR; - } - *out_len = len; - memcpy(out, dataptr, len); - return YKPIV_OK; -} - -ykpiv_rc ykpiv_sign_data(ykpiv_state *state, - const unsigned char *raw_in, size_t in_len, - unsigned char *sign_out, size_t *out_len, - unsigned char algorithm, unsigned char key) { - ykpiv_rc res = YKPIV_OK; - - if (NULL == state) return YKPIV_GENERIC_ERROR; - - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - /* don't attempt to reselect in crypt operations to avoid problems with PIN_ALWAYS */ - /*if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup;*/ - - res = _general_authenticate(state, raw_in, in_len, sign_out, out_len, - algorithm, key, false); -/* Cleanup: */ - _ykpiv_end_transaction(state); - return res; -} - -ykpiv_rc ykpiv_decipher_data(ykpiv_state *state, const unsigned char *in, - size_t in_len, unsigned char *out, size_t *out_len, - unsigned char algorithm, unsigned char key) { - ykpiv_rc res = YKPIV_OK; - - if (NULL == state) return YKPIV_GENERIC_ERROR; - - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - /* don't attempt to reselect in crypt operations to avoid problems with PIN_ALWAYS */ - /*if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup;*/ - - res = _general_authenticate(state, in, in_len, out, out_len, - algorithm, key, true); - -/* Cleanup: */ - - _ykpiv_end_transaction(state); - return res; -} - -static ykpiv_rc _ykpiv_get_version(ykpiv_state *state, ykpiv_version_t *p_version) { - APDU apdu; - unsigned char data[261]; - uint32_t recv_len = sizeof(data); - int sw; - ykpiv_rc res; - - if (!state) { - return YKPIV_ARGUMENT_ERROR; - } - - /* get version from state if already from device */ - - if (state->ver.major || state->ver.minor || state->ver.patch) { - if (p_version) { - memcpy(p_version, &(state->ver), sizeof(ykpiv_version_t)); - } - return YKPIV_OK; - } - - /* get version from device */ - /* XXX FIX THIS!!! */ - /* - memset(apdu.raw, 0, sizeof(apdu)); - apdu.st.ins = YKPIV_INS_GET_VERSION; - */ - if((res = _send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) { - return res; - } else if(sw == SW_SUCCESS) { - - /* check that we received enough data for the verson number */ - if (recv_len < 3) { - return YKPIV_SIZE_ERROR; - } - - state->ver.major = data[0]; - state->ver.minor = data[1]; - state->ver.patch = data[2]; - if (p_version) { - memcpy(p_version, &(state->ver), sizeof(ykpiv_version_t)); - } - } else { - res = YKPIV_GENERIC_ERROR; - } - - return res; -} - -ykpiv_rc ykpiv_get_version(ykpiv_state *state, char *version, size_t len) { - ykpiv_rc res; - int result = 0; - ykpiv_version_t ver = {0}; - - if ((res = _ykpiv_begin_transaction(state)) < YKPIV_OK) return YKPIV_PCSC_ERROR; - if ((res = _ykpiv_ensure_application_selected(state)) < YKPIV_OK) goto Cleanup; - - if ((res = _ykpiv_get_version(state, &ver)) >= YKPIV_OK) { - result = snprintf(version, len, "%d.%d.%d", ver.major, ver.minor, ver.patch); - if(result < 0) { - res = YKPIV_SIZE_ERROR; - } - } - -Cleanup: - - _ykpiv_end_transaction(state); - return res; -} - -/* caller must make sure that this is wrapped in a transaction for synchronized operation */ -static ykpiv_rc _ykpiv_get_serial(ykpiv_state *state, uint32_t *p_serial, bool f_force) { - ykpiv_rc res = YKPIV_OK; - APDU apdu; - const uint8_t *yk_applet = { 0xa0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x01, 0x01 }; - unsigned char data[0xff]; - uint32_t recv_len = sizeof(data); - int sw; - uint8_t *p_temp = NULL; - - if (!state) { - return YKPIV_ARGUMENT_ERROR; - } - - if (!f_force && (state->serial != 0)) { - if (p_serial) *p_serial = state->serial; - return YKPIV_OK; - } - - if (state->ver.major < 5) { - /* get serial from neo/yk4 devices using the otp applet */ - uint8_t temp[0xff]; - - recv_len = sizeof(temp); - /* XXX FIX THIS!!! */ - /*memset(apdu.raw, 0, sizeof(apdu)); - apdu.st.ins = YKPIV_INS_SELECT_APPLICATION; - apdu.st.p1 = 0x04; - apdu.st.lc = sizeof(yk_applet); - memcpy(apdu.st.data, yk_applet, sizeof(yk_applet));*/ - - if ((res = _send_data(state, &apdu, temp, &recv_len, &sw)) < YKPIV_OK) { - if (state->verbose) { - fprintf(stderr, "Failed communicating with card: '%s'\n", ykpiv_strerror(res)); - } - goto Cleanup; - } - else if (sw != SW_SUCCESS) { - if (state->verbose) { - fprintf(stderr, "Failed selecting yk application: %04x\n", sw); - } - res = YKPIV_GENERIC_ERROR; - goto Cleanup; - } - - recv_len = sizeof(data); - /* XXX FIX THIS!!! */ - /* - memset(apdu.raw, 0, sizeof(apdu)); - apdu.st.ins = 0x01; - apdu.st.p1 = 0x10; - apdu.st.lc = 0x00; - */ - - if ((res = _send_data(state, &apdu, data, &recv_len, &sw)) < YKPIV_OK) { - if (state->verbose) { - fprintf(stderr, "Failed communicating with card: '%s'\n", ykpiv_strerror(res)); - } - goto Cleanup; - } - else if (sw != SW_SUCCESS) { - if (state->verbose) { - fprintf(stderr, "Failed retrieving serial number: %04x\n", sw); - } - res = YKPIV_GENERIC_ERROR; - goto Cleanup; - } - - recv_len = sizeof(temp); - /* XXX FIX THIS!!! */ - /* - memset(apdu.raw, 0, sizeof(apdu)); - apdu.st.ins = YKPIV_INS_SELECT_APPLICATION; - apdu.st.p1 = 0x04; - apdu.st.lc = (unsigned char)sizeof(aid); - memcpy(apdu.st.data, aid, sizeof(aid)); - */ - - if((res = _send_data(state, &apdu, temp, &recv_len, &sw)) < YKPIV_OK) { - if(state->verbose) { - fprintf(stderr, "Failed communicating with card: '%s'\n", ykpiv_strerror(res)); - } - return res; - } - else if(sw != SW_SUCCESS) { - if(state->verbose) { - fprintf(stderr, "Failed selecting application: %04x\n", sw); - } - return YKPIV_GENERIC_ERROR; - } - } - else { - /* get serial from yk5 and later devices using the f8 command */ - - /* XXX FIX THIS!!! */ - /*memset(apdu.raw, 0, sizeof(apdu)); - apdu.st.ins = YKPIV_INS_GET_SERIAL;*/ - - if ((res = _send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) { - if(state->verbose) { - fprintf(stderr, "Failed communicating with card: '%s'\n", ykpiv_strerror(res)); - } - return res; - } - else if(sw != SW_SUCCESS) { - if(state->verbose) { - fprintf(stderr, "Failed retrieving serial number: %04x\n", sw); - } - return YKPIV_GENERIC_ERROR; - } - } - - /* check that we received enough data for the serial number */ - if (recv_len < 4) { - return YKPIV_SIZE_ERROR; - } - - p_temp = (uint8_t*)(&state->serial); - - *p_temp++ = data[3]; - *p_temp++ = data[2]; - *p_temp++ = data[1]; - *p_temp++ = data[0]; - - if (p_serial) *p_serial = state->serial; - -Cleanup: - - return res; -} - -ykpiv_rc ykpiv_get_serial(ykpiv_state *state, uint32_t *p_serial) { - ykpiv_rc res = YKPIV_OK; - - if ((res = _ykpiv_begin_transaction(state)) != YKPIV_OK) return YKPIV_PCSC_ERROR; - if ((res = _ykpiv_ensure_application_selected(state)) != YKPIV_OK) goto Cleanup; - - res = _ykpiv_get_serial(state, p_serial, false); - -Cleanup: - - _ykpiv_end_transaction(state); - return res; -} - -static ykpiv_rc _cache_pin(ykpiv_state *state, const char *pin, size_t len) { -#if DISABLE_PIN_CACHE - // Some embedded applications of this library may not want to keep the PIN - // data in RAM for security reasons. - return YKPIV_OK; -#else - if (!state) - return YKPIV_ARGUMENT_ERROR; - if (pin && state->pin == pin) { - return YKPIV_OK; - } - if (state->pin) { - yc_memzero(state->pin, strnlen(state->pin, CB_PIN_MAX)); - _ykpiv_free(state, state->pin); - state->pin = NULL; - } - if (pin && len > 0) { - state->pin = _ykpiv_alloc(state, len * sizeof(char) + 1); - if (state->pin == NULL) { - return YKPIV_MEMORY_ERROR; - } - memcpy(state->pin, pin, len); - state->pin[len] = 0; - } - return YKPIV_OK; -#endif -} - -static ykpiv_rc _verify(ykpiv_state *state, const char *pin, const size_t pin_len, int *tries) { - APDU apdu; - unsigned char data[261]; - uint32_t recv_len = sizeof(data); - int sw; - ykpiv_rc res; - - if (pin_len > CB_PIN_MAX) { - return YKPIV_SIZE_ERROR; - } - - /* XXX FIX THIS!!! */ - /* - memset(apdu.raw, 0, sizeof(apdu.raw)); - apdu.st.ins = YKPIV_INS_VERIFY; - apdu.st.p1 = 0x00; - apdu.st.p2 = 0x80; - apdu.st.lc = pin ? 0x08 : 0; - if (pin) { - memcpy(apdu.st.data, pin, pin_len); - if (pin_len < CB_PIN_MAX) { - memset(apdu.st.data + pin_len, 0xff, CB_PIN_MAX - pin_len); - } - }*/ - - res = _send_data(state, &apdu, data, &recv_len, &sw); - yc_memzero(&apdu, sizeof(apdu)); - - if (res != YKPIV_OK) { - return res; - } - else if (sw == SW_SUCCESS) { - if (pin && pin_len) { - // Intentionally ignore errors. If the PIN fails to save, it will only - // be a problem if a reconnect is attempted. Failure deferred until then. - _cache_pin(state, pin, pin_len); - } - - if (tries) *tries = (sw & 0xf); - return YKPIV_OK; - } - else if ((sw >> 8) == 0x63) { - if (tries) *tries = (sw & 0xf); - return YKPIV_WRONG_PIN; - } - else if (sw == SW_ERR_AUTH_BLOCKED) { - if (tries) *tries = 0; - return YKPIV_WRONG_PIN; - } - else { - return YKPIV_GENERIC_ERROR; - } -} - -ykpiv_rc ykpiv_verify(ykpiv_state *state, const char *pin, int *tries) { - return ykpiv_verify_select(state, pin, pin ? strlen(pin) : 0, tries, false); -} - -ykpiv_rc ykpiv_verify_select(ykpiv_state *state, const char *pin, const size_t pin_len, int *tries, bool force_select) { - ykpiv_rc res = YKPIV_OK; - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - if (force_select) { - if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; - } - res = _verify(state, pin, pin_len, tries); -Cleanup: - - _ykpiv_end_transaction(state); - return res; -} - -ykpiv_rc ykpiv_get_pin_retries(ykpiv_state *state, int *tries) { - ykpiv_rc res; - ykpiv_rc ykrc; - if (NULL == state || NULL == tries) { - return YKPIV_ARGUMENT_ERROR; - } - // Force a re-select to unverify, because once verified the spec dictates that - // subsequent verify calls will return a "verification not needed" instead of - // the number of tries left... - res = _ykpiv_select_application(state); - if (res != YKPIV_OK) return res; - ykrc = ykpiv_verify(state, NULL, tries); - // WRONG_PIN is expected on successful query. - return ykrc == YKPIV_WRONG_PIN ? YKPIV_OK : ykrc; -} - - -ykpiv_rc ykpiv_set_pin_retries(ykpiv_state *state, int pin_tries, int puk_tries) { - ykpiv_rc res = YKPIV_OK; - unsigned char *templ = {0, YKPIV_INS_SET_PIN_RETRIES, 0, 0}; - unsigned char data[0xff]; - unsigned long recv_len = sizeof(data); - int sw = 0; - - // Special case: if either retry count is 0, it's a successful no-op - if (pin_tries == 0 || puk_tries == 0) { - return YKPIV_OK; - } - - if (pin_tries > 0xff || puk_tries > 0xff || pin_tries < 1 || puk_tries < 1) { - return YKPIV_RANGE_ERROR; - } - - templ[2] = (unsigned char)pin_tries; - templ[3] = (unsigned char)puk_tries; - - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; - - res = ykpiv_transfer_data(state, templ, NULL, 0, data, &recv_len, &sw); - if (YKPIV_OK == res) { - if (SW_SUCCESS == sw) { - // success, fall through - } else if (sw == SW_ERR_AUTH_BLOCKED) { - res = YKPIV_AUTHENTICATION_ERROR; - } else if (sw == SW_ERR_SECURITY_STATUS) { - res = YKPIV_AUTHENTICATION_ERROR; - } else { - res = YKPIV_GENERIC_ERROR; - } - } - -Cleanup: - _ykpiv_end_transaction(state); - return res; -} - -static ykpiv_rc _ykpiv_change_pin(ykpiv_state *state, int action, const char * current_pin, size_t current_pin_len, const char * new_pin, size_t new_pin_len, int *tries) { - int sw; - unsigned char *templ = {0, YKPIV_INS_CHANGE_REFERENCE, 0, 0x80}; - unsigned char indata[0x10]; - unsigned char data[0xff]; - unsigned long recv_len = sizeof(data); - ykpiv_rc res; - if (current_pin_len > CB_PIN_MAX) { - return YKPIV_SIZE_ERROR; - } - if (new_pin_len > CB_PIN_MAX) { - return YKPIV_SIZE_ERROR; - } - if(action == CHREF_ACT_UNBLOCK_PIN) { - templ[1] = YKPIV_INS_RESET_RETRY; - } - else if(action == CHREF_ACT_CHANGE_PUK) { - templ[3] = 0x81; - } - memcpy(indata, current_pin, current_pin_len); - if(current_pin_len < CB_PIN_MAX) { - memset(indata + current_pin_len, 0xff, CB_PIN_MAX - current_pin_len); - } - memcpy(indata + CB_PIN_MAX, new_pin, new_pin_len); - if(new_pin_len < CB_PIN_MAX) { - memset(indata + CB_PIN_MAX + new_pin_len, 0xff, CB_PIN_MAX - new_pin_len); - } - - res = ykpiv_transfer_data(state, templ, indata, sizeof(indata), data, &recv_len, &sw); - yc_memzero(indata, sizeof(indata)); - - if(res != YKPIV_OK) { - return res; - } else if(sw != SW_SUCCESS) { - if((sw >> 8) == 0x63) { - if (tries) *tries = sw & 0xf; - return YKPIV_WRONG_PIN; - } else if(sw == SW_ERR_AUTH_BLOCKED) { - return YKPIV_PIN_LOCKED; - } else { - if(state->verbose) { - fprintf(stderr, "Failed changing pin, token response code: %x.\n", sw); - } - return YKPIV_GENERIC_ERROR; - } - } - return YKPIV_OK; -} - -ykpiv_rc ykpiv_change_pin(ykpiv_state *state, const char * current_pin, size_t current_pin_len, const char * new_pin, size_t new_pin_len, int *tries) { - ykpiv_rc res = YKPIV_GENERIC_ERROR; - - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; - - res = _ykpiv_change_pin(state, CHREF_ACT_CHANGE_PIN, current_pin, current_pin_len, new_pin, new_pin_len, tries); - if (res == YKPIV_OK && new_pin != NULL) { - // Intentionally ignore errors. If the PIN fails to save, it will only - // be a problem if a reconnect is attempted. Failure deferred until then. - _cache_pin(state, new_pin, new_pin_len); - } - -Cleanup: - _ykpiv_end_transaction(state); - return res; -} - -ykpiv_rc ykpiv_change_puk(ykpiv_state *state, const char * current_puk, size_t current_puk_len, const char * new_puk, size_t new_puk_len, int *tries) { - ykpiv_rc res = YKPIV_GENERIC_ERROR; - - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; - - res = _ykpiv_change_pin(state, CHREF_ACT_CHANGE_PUK, current_puk, current_puk_len, new_puk, new_puk_len, tries); - -Cleanup: - _ykpiv_end_transaction(state); - return res; -} - -ykpiv_rc ykpiv_unblock_pin(ykpiv_state *state, const char * puk, size_t puk_len, const char * new_pin, size_t new_pin_len, int *tries) { - ykpiv_rc res = YKPIV_GENERIC_ERROR; - - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; - - res = _ykpiv_change_pin(state, CHREF_ACT_UNBLOCK_PIN, puk, puk_len, new_pin, new_pin_len, tries); - -Cleanup: - _ykpiv_end_transaction(state); - return res; -} - -ykpiv_rc ykpiv_fetch_object(ykpiv_state *state, int object_id, - unsigned char *data, unsigned long *len) { - ykpiv_rc res; - - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; - - res = _ykpiv_fetch_object(state, object_id, data, len); - -Cleanup: - _ykpiv_end_transaction(state); - return res; -} - -ykpiv_rc _ykpiv_fetch_object(ykpiv_state *state, int object_id, - unsigned char *data, unsigned long *len) { - int sw; - unsigned char indata[5]; - unsigned char *inptr = indata; - unsigned char *templ = {0, YKPIV_INS_GET_DATA, 0x3f, 0xff}; - ykpiv_rc res; - - inptr = set_object(object_id, inptr); - if(inptr == NULL) { - return YKPIV_INVALID_OBJECT; - } - - if((res = ykpiv_transfer_data(state, templ, indata, (long)(inptr - indata), data, len, &sw)) - != YKPIV_OK) { - return res; - } - - if(sw == SW_SUCCESS) { - size_t outlen = 0; - unsigned int offs = 0; - - if ((*len < 2) || !_ykpiv_has_valid_length(data + 1, *len - 1)) { - return YKPIV_SIZE_ERROR; - } - - offs = _ykpiv_get_length(data + 1, &outlen); - if(offs == 0) { - return YKPIV_SIZE_ERROR; - } - if(outlen + offs + 1 != *len) { - if(state->verbose) { - fprintf(stderr, "Invalid length indicated in object, total objlen is %lu, indicated length is %lu.", *len, (unsigned long)outlen); - } - return YKPIV_SIZE_ERROR; - } - memmove(data, data + 1 + offs, outlen); - *len = (unsigned long)outlen; - return YKPIV_OK; - } else { - return YKPIV_GENERIC_ERROR; - } -} - -ykpiv_rc ykpiv_save_object(ykpiv_state *state, int object_id, - unsigned char *indata, size_t len) { - ykpiv_rc res; - - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; - - res = _ykpiv_save_object(state, object_id, indata, len); - -Cleanup: - _ykpiv_end_transaction(state); - return res; -} - -ykpiv_rc _ykpiv_save_object( - ykpiv_state *state, - int object_id, - unsigned char *indata, - size_t len) { - unsigned char data[CB_BUF_MAX]; - unsigned char *dataptr = data; - unsigned char *templ = {0, YKPIV_INS_PUT_DATA, 0x3f, 0xff}; - int sw; - ykpiv_rc res; - unsigned long outlen = 0; - - if(len > sizeof(data) - 9) { - return YKPIV_SIZE_ERROR; - } - dataptr = set_object(object_id, dataptr); - if(dataptr == NULL) { - return YKPIV_INVALID_OBJECT; - } - *dataptr++ = 0x53; - dataptr += _ykpiv_set_length(dataptr, len); - memcpy(dataptr, indata, len); - dataptr += len; - - if((res = _ykpiv_transfer_data(state, templ, data, (long)(dataptr - data), NULL, &outlen, - &sw)) != YKPIV_OK) { - return res; - } - - if(SW_SUCCESS == sw) { - return YKPIV_OK; - } - else if (SW_ERR_SECURITY_STATUS == sw) { - return YKPIV_AUTHENTICATION_ERROR; - } - else { - return YKPIV_GENERIC_ERROR; - } -} - -ykpiv_rc ykpiv_import_private_key(ykpiv_state *state, const unsigned char key, unsigned char algorithm, - const unsigned char *p, size_t p_len, - const unsigned char *q, size_t q_len, - const unsigned char *dp, size_t dp_len, - const unsigned char *dq, size_t dq_len, - 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) { - - unsigned char key_data[1024]; - unsigned char *in_ptr = key_data; - unsigned char *templ = {0, YKPIV_INS_IMPORT_KEY, algorithm, key}; - unsigned char data[256]; - unsigned long recv_len = sizeof(data); - unsigned elem_len; - int sw; - const unsigned char *params[5]; - size_t lens[5]; - size_t padding; - unsigned char n_params; - int i; - int param_tag; - ykpiv_rc res; - - if (state == NULL) - return YKPIV_GENERIC_ERROR; - - if (key == YKPIV_KEY_CARDMGM || - key < YKPIV_KEY_RETIRED1 || - (key > YKPIV_KEY_RETIRED20 && key < YKPIV_KEY_AUTHENTICATION) || - (key > YKPIV_KEY_CARDAUTH && key != YKPIV_KEY_ATTESTATION)) { - return YKPIV_KEY_ERROR; - } - - if (pin_policy != YKPIV_PINPOLICY_DEFAULT && - pin_policy != YKPIV_PINPOLICY_NEVER && - pin_policy != YKPIV_PINPOLICY_ONCE && - pin_policy != YKPIV_PINPOLICY_ALWAYS) - return YKPIV_GENERIC_ERROR; - - if (touch_policy != YKPIV_TOUCHPOLICY_DEFAULT && - touch_policy != YKPIV_TOUCHPOLICY_NEVER && - touch_policy != YKPIV_TOUCHPOLICY_ALWAYS && - touch_policy != YKPIV_TOUCHPOLICY_CACHED) - return YKPIV_GENERIC_ERROR; - - if (algorithm == YKPIV_ALGO_RSA1024 || algorithm == YKPIV_ALGO_RSA2048) { - - if (p_len + q_len + dp_len + dq_len + qinv_len >= sizeof(key_data)) { - return YKPIV_SIZE_ERROR; - } - - if (algorithm == YKPIV_ALGO_RSA1024) - elem_len = 64; - if (algorithm == YKPIV_ALGO_RSA2048) - elem_len = 128; - - if (p == NULL || q == NULL || dp == NULL || - dq == NULL || qinv == NULL) - return YKPIV_GENERIC_ERROR; - - params[0] = p; - lens[0] = p_len; - params[1] = q; - lens[1] = q_len; - params[2] = dp; - lens[2] = dp_len; - params[3] = dq; - lens[3] = dq_len; - params[4] = qinv; - lens[4] = qinv_len; - param_tag = 0x01; - - n_params = 5; - } - else if (algorithm == YKPIV_ALGO_ECCP256 || algorithm == YKPIV_ALGO_ECCP384) { - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wtype-limits" - if ((size_t)ec_data_len >= sizeof(key_data)) { - /* This can never be true, but check to be explicit. */ - return YKPIV_SIZE_ERROR; - } -#pragma GCC diagnostic pop - - if (algorithm == YKPIV_ALGO_ECCP256) - elem_len = 32; - if (algorithm == YKPIV_ALGO_ECCP384) - elem_len = 48; - - if (ec_data == NULL) - return YKPIV_GENERIC_ERROR; - - params[0] = ec_data; - lens[0] = ec_data_len; - param_tag = 0x06; - n_params = 1; - } - else - return YKPIV_ALGORITHM_ERROR; - - for (i = 0; i < n_params; i++) { - size_t remaining; - *in_ptr++ = param_tag + i; - in_ptr += _ykpiv_set_length(in_ptr, elem_len); - padding = elem_len - lens[i]; - remaining = (uintptr_t)key_data + sizeof(key_data) - (uintptr_t)in_ptr; - if (padding > remaining) { - res = YKPIV_ALGORITHM_ERROR; - goto Cleanup; - } - memset(in_ptr, 0, padding); - in_ptr += padding; - memcpy(in_ptr, params[i], lens[i]); - in_ptr += lens[i]; - } - - if (pin_policy != YKPIV_PINPOLICY_DEFAULT) { - *in_ptr++ = YKPIV_PINPOLICY_TAG; - *in_ptr++ = 0x01; - *in_ptr++ = pin_policy; - } - - if (touch_policy != YKPIV_TOUCHPOLICY_DEFAULT) { - *in_ptr++ = YKPIV_TOUCHPOLICY_TAG; - *in_ptr++ = 0x01; - *in_ptr++ = touch_policy; - } - - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; - - if ((res = ykpiv_transfer_data(state, templ, key_data, (long)(in_ptr - key_data), data, &recv_len, &sw)) != YKPIV_OK) { - goto Cleanup; - } - if (SW_SUCCESS != sw) { - res = YKPIV_GENERIC_ERROR; - if (sw == SW_ERR_SECURITY_STATUS) { - res = YKPIV_AUTHENTICATION_ERROR; - } - goto Cleanup; - } - -Cleanup: - yc_memzero(key_data, sizeof(key_data)); - _ykpiv_end_transaction(state); - return res; -} - -ykpiv_rc ykpiv_attest(ykpiv_state *state, const unsigned char key, unsigned char *data, size_t *data_len) { - ykpiv_rc res; - unsigned char *templ = {0, YKPIV_INS_ATTEST, key, 0}; - int sw; - unsigned long ul_data_len; - - if (state == NULL || data == NULL || data_len == NULL) { - return YKPIV_ARGUMENT_ERROR; - } - - ul_data_len = (unsigned long)*data_len; - - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; - - if ((res = ykpiv_transfer_data(state, templ, NULL, 0, data, &ul_data_len, &sw)) != YKPIV_OK) { - goto Cleanup; - } - if (SW_SUCCESS != sw) { - res = YKPIV_GENERIC_ERROR; - if (SW_ERR_NOT_SUPPORTED == sw) { - res = YKPIV_NOT_SUPPORTED; - } - goto Cleanup; - } - if (data[0] != 0x30) { - res = YKPIV_GENERIC_ERROR; - goto Cleanup; - } - - *data_len = (size_t)ul_data_len; - -Cleanup: - _ykpiv_end_transaction(state); - return res; -} - - -ykpiv_rc ykpiv_auth_getchallenge(ykpiv_state *state, uint8_t *challenge, const size_t challenge_len) { - ykpiv_rc res = YKPIV_OK; - APDU apdu = { 0 }; - unsigned char data[261]; /* XXX ZERO THIS!!! */ - uint32_t recv_len = sizeof(data); - int sw = 0; - - if (NULL == state) return YKPIV_GENERIC_ERROR; - if (NULL == challenge) return YKPIV_GENERIC_ERROR; - if (8 != challenge_len) return YKPIV_SIZE_ERROR; - - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; - - /* get a challenge from the card */ - /* XXX FIX THIS!!! */ - /* - memset(apdu.raw, 0, sizeof(apdu)); - apdu.st.ins = YKPIV_INS_AUTHENTICATE; - apdu.st.p1 = YKPIV_ALGO_3DES; // triple des - apdu.st.p2 = YKPIV_KEY_CARDMGM; // management key - apdu.st.lc = 0x04; - apdu.st.data[0] = 0x7c; - apdu.st.data[1] = 0x02; - apdu.st.data[2] = 0x81; //0x80; - */ - if ((res = _send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) { - goto Cleanup; - } - else if (sw != SW_SUCCESS) { - res = YKPIV_AUTHENTICATION_ERROR; - goto Cleanup; - } - memcpy(challenge, data + 4, 8); - -Cleanup: - - _ykpiv_end_transaction(state); - return res; -} - -ykpiv_rc ykpiv_auth_verifyresponse(ykpiv_state *state, uint8_t *response, const size_t response_len) { - ykpiv_rc res = YKPIV_OK; - APDU apdu = { 0 }; - unsigned char data[261]; /* XXX ZERO THIS!!! */ - uint32_t recv_len = sizeof(data); - int sw = 0; - unsigned char *dataptr = 0; /* XXX FIX THIS!!! apdu.st.data; */ - - if (NULL == state) return YKPIV_GENERIC_ERROR; - if (NULL == response) return YKPIV_GENERIC_ERROR; - if (8 != response_len) return YKPIV_SIZE_ERROR; - - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - /* note: do not select the applet here, as it resets the challenge state */ - - /* send the response to the card and a challenge of our own. */ - recv_len = sizeof(data); - /* XXX FIX THIS!!! */ - /* - memset(apdu.raw, 0, sizeof(apdu)); - apdu.st.ins = YKPIV_INS_AUTHENTICATE; - apdu.st.p1 = YKPIV_ALGO_3DES; // triple des - apdu.st.p2 = YKPIV_KEY_CARDMGM; // management key - */ - *dataptr++ = 0x7c; - *dataptr++ = 0x0a; /* 2 + 8 */ - *dataptr++ = 0x82; - *dataptr++ = 8; - memcpy(dataptr, response, response_len); - dataptr += 8; - /* XXX FIX THIS!!! */ - //apdu.st.lc = (unsigned char)(dataptr - apdu.st.data); - if ((res = _send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) { - goto Cleanup; - } - else if (sw != SW_SUCCESS) { - res = YKPIV_AUTHENTICATION_ERROR; - goto Cleanup; - } - -Cleanup: - - yc_memzero(&apdu, sizeof(apdu)); - _ykpiv_end_transaction(state); - return res; -} - -static const uint8_t *MGMT_AID = { 0xa0, 0x00, 0x00, 0x05, 0x27, 0x47, 0x11, 0x17 }; - -/* deauthenticates the user pin and mgm key */ -ykpiv_rc ykpiv_auth_deauthenticate(ykpiv_state *state) { - ykpiv_rc res = YKPIV_OK; - APDU apdu; - unsigned char data[0xff]; - uint32_t recv_len = sizeof(data); - int sw; - - if (!state) { - return YKPIV_ARGUMENT_ERROR; - } - - /* this function does not use ykpiv_transfer_data because it selects a different app */ - if ((res = _ykpiv_begin_transaction(state)) < YKPIV_OK) return res; - - /* XXX FIX THIS!!! */ - /* - memset(apdu.raw, 0, sizeof(apdu)); - apdu.st.ins = YKPIV_INS_SELECT_APPLICATION; - apdu.st.p1 = 0x04; - apdu.st.lc = sizeof(MGMT_AID); - memcpy(apdu.st.data, MGMT_AID, sizeof(MGMT_AID)); - */ - - if ((res = _send_data(state, &apdu, data, &recv_len, &sw)) < YKPIV_OK) { - if (state->verbose) { - fprintf(stderr, "Failed communicating with card: '%s'\n", ykpiv_strerror(res)); - } - goto Cleanup; - } - else if (sw != SW_SUCCESS) { - if (state->verbose) { - fprintf(stderr, "Failed selecting mgmt application: %04x\n", sw); - } - res = YKPIV_GENERIC_ERROR; - goto Cleanup; - } - -Cleanup: - - _ykpiv_end_transaction(state); - return res; -} diff --git a/src/ykpiv.h b/src/ykpiv.h deleted file mode 100644 index 5b0694c..0000000 --- a/src/ykpiv.h +++ /dev/null @@ -1,702 +0,0 @@ -/* - * 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. - * - */ - -/** - * @mainpage - * - * See ykpiv.h - * - * @file ykpiv.h - * libykpiv API - */ -#ifndef YKPIV_H -#define YKPIV_H - -#include -#include -#include - -#include - -#ifdef __cplusplus -extern "C" -{ -#endif - - typedef struct ykpiv_state ykpiv_state; - - typedef enum { - YKPIV_OK = 0, - YKPIV_MEMORY_ERROR = -1, - YKPIV_PCSC_ERROR = -2, - YKPIV_SIZE_ERROR = -3, - YKPIV_APPLET_ERROR = -4, - YKPIV_AUTHENTICATION_ERROR = -5, - YKPIV_RANDOMNESS_ERROR = -6, - YKPIV_GENERIC_ERROR = -7, - YKPIV_KEY_ERROR = -8, - YKPIV_PARSE_ERROR = -9, - YKPIV_WRONG_PIN = -10, - YKPIV_INVALID_OBJECT = -11, - YKPIV_ALGORITHM_ERROR = -12, - YKPIV_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); - ykpiv_rc ykpiv_disconnect(ykpiv_state *state); - ykpiv_rc ykpiv_transfer_data(ykpiv_state *state, const unsigned char *templ, - const unsigned char *in_data, long in_len, - unsigned char *out_data, unsigned long *out_len, int *sw); - ykpiv_rc ykpiv_authenticate(ykpiv_state *state, const unsigned char *key); - ykpiv_rc ykpiv_set_mgmkey(ykpiv_state *state, const unsigned char *new_key); - ykpiv_rc ykpiv_hex_decode(const char *hex_in, size_t in_len, - unsigned char *hex_out, size_t *out_len); - 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_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); - ykpiv_rc ykpiv_get_version(ykpiv_state *state, char *version, size_t len); - ykpiv_rc ykpiv_verify(ykpiv_state *state, const char *pin, int *tries); - ykpiv_rc ykpiv_change_pin(ykpiv_state *state, const char * current_pin, size_t current_pin_len, - const char * new_pin, size_t new_pin_len, - int *tries); - ykpiv_rc ykpiv_change_puk(ykpiv_state *state, const char * current_puk, size_t current_puk_len, - const char * new_puk, size_t new_puk_len, - int *tries); - ykpiv_rc ykpiv_unblock_pin(ykpiv_state *state, const char * puk, size_t puk_len, - const char * new_pin, size_t new_pin_len, - int *tries); - ykpiv_rc ykpiv_fetch_object(ykpiv_state *state, int object_id, - unsigned char *data, unsigned long *len); - ykpiv_rc ykpiv_set_mgmkey2(ykpiv_state *state, const unsigned char *new_key, - const unsigned char touch); - ykpiv_rc ykpiv_save_object(ykpiv_state *state, int object_id, - unsigned char *indata, size_t len); - ykpiv_rc ykpiv_import_private_key(ykpiv_state *state, const unsigned char key, unsigned char algorithm, - const unsigned char *p, size_t p_len, - const unsigned char *q, size_t q_len, - const unsigned char *dp, size_t dp_len, - const unsigned char *dq, size_t dq_len, - 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); - - /** - * Get serial number - * - * The card must be connected to call this function. - * - * @param state [in] State handle - * @param p_serial [out] uint32 to store retrieved serial number - * - * @return ykpiv_rc error code - * - */ - ykpiv_rc ykpiv_get_serial(ykpiv_state *state, uint32_t* p_serial); - - -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//// -//// -//// 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 ykpiv_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 -#define YKPIV_ALGO_RSA1024 0x06 -#define YKPIV_ALGO_RSA2048 0x07 -#define YKPIV_ALGO_ECCP256 0x11 -#define YKPIV_ALGO_ECCP384 0x14 - -#define YKPIV_KEY_AUTHENTICATION 0x9a -#define YKPIV_KEY_CARDMGM 0x9b -#define YKPIV_KEY_SIGNATURE 0x9c -#define YKPIV_KEY_KEYMGM 0x9d -#define YKPIV_KEY_CARDAUTH 0x9e -#define YKPIV_KEY_RETIRED1 0x82 -#define YKPIV_KEY_RETIRED2 0x83 -#define YKPIV_KEY_RETIRED3 0x84 -#define YKPIV_KEY_RETIRED4 0x85 -#define YKPIV_KEY_RETIRED5 0x86 -#define YKPIV_KEY_RETIRED6 0x87 -#define YKPIV_KEY_RETIRED7 0x88 -#define YKPIV_KEY_RETIRED8 0x89 -#define YKPIV_KEY_RETIRED9 0x8a -#define YKPIV_KEY_RETIRED10 0x8b -#define YKPIV_KEY_RETIRED11 0x8c -#define YKPIV_KEY_RETIRED12 0x8d -#define YKPIV_KEY_RETIRED13 0x8e -#define YKPIV_KEY_RETIRED14 0x8f -#define YKPIV_KEY_RETIRED15 0x90 -#define YKPIV_KEY_RETIRED16 0x91 -#define YKPIV_KEY_RETIRED17 0x92 -#define YKPIV_KEY_RETIRED18 0x93 -#define YKPIV_KEY_RETIRED19 0x94 -#define YKPIV_KEY_RETIRED20 0x95 -#define YKPIV_KEY_ATTESTATION 0xf9 - -#define YKPIV_OBJ_CAPABILITY 0x5fc107 -#define YKPIV_OBJ_CHUID 0x5fc102 -#define YKPIV_OBJ_AUTHENTICATION 0x5fc105 /* cert for 9a key */ -#define YKPIV_OBJ_FINGERPRINTS 0x5fc103 -#define YKPIV_OBJ_SECURITY 0x5fc106 -#define YKPIV_OBJ_FACIAL 0x5fc108 -#define YKPIV_OBJ_PRINTED 0x5fc109 -#define YKPIV_OBJ_SIGNATURE 0x5fc10a /* cert for 9c key */ -#define YKPIV_OBJ_KEY_MANAGEMENT 0x5fc10b /* cert for 9d key */ -#define YKPIV_OBJ_CARD_AUTH 0x5fc101 /* cert for 9e key */ -#define YKPIV_OBJ_DISCOVERY 0x7e -#define YKPIV_OBJ_KEY_HISTORY 0x5fc10c -#define YKPIV_OBJ_IRIS 0x5fc121 - -#define YKPIV_OBJ_RETIRED1 0x5fc10d -#define YKPIV_OBJ_RETIRED2 0x5fc10e -#define YKPIV_OBJ_RETIRED3 0x5fc10f -#define YKPIV_OBJ_RETIRED4 0x5fc110 -#define YKPIV_OBJ_RETIRED5 0x5fc111 -#define YKPIV_OBJ_RETIRED6 0x5fc112 -#define YKPIV_OBJ_RETIRED7 0x5fc113 -#define YKPIV_OBJ_RETIRED8 0x5fc114 -#define YKPIV_OBJ_RETIRED9 0x5fc115 -#define YKPIV_OBJ_RETIRED10 0x5fc116 -#define YKPIV_OBJ_RETIRED11 0x5fc117 -#define YKPIV_OBJ_RETIRED12 0x5fc118 -#define YKPIV_OBJ_RETIRED13 0x5fc119 -#define YKPIV_OBJ_RETIRED14 0x5fc11a -#define YKPIV_OBJ_RETIRED15 0x5fc11b -#define YKPIV_OBJ_RETIRED16 0x5fc11c -#define YKPIV_OBJ_RETIRED17 0x5fc11d -#define YKPIV_OBJ_RETIRED18 0x5fc11e -#define YKPIV_OBJ_RETIRED19 0x5fc11f -#define YKPIV_OBJ_RETIRED20 0x5fc120 - -#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 -#define YKPIV_INS_GENERATE_ASYMMETRIC 0x47 -#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 -#define SW_ERR_SECURITY_STATUS 0x6982 -#define SW_ERR_AUTH_BLOCKED 0x6983 -#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 */ -#define YKPIV_INS_SET_MGMKEY 0xff -#define YKPIV_INS_IMPORT_KEY 0xfe -#define YKPIV_INS_GET_VERSION 0xfd -#define YKPIV_INS_RESET 0xfb -#define YKPIV_INS_SET_PIN_RETRIES 0xfa -#define YKPIV_INS_ATTEST 0xf9 -#define YKPIV_INS_GET_SERIAL 0xf8 - -#define YKPIV_PINPOLICY_TAG 0xaa -#define YKPIV_PINPOLICY_DEFAULT 0 -#define YKPIV_PINPOLICY_NEVER 1 -#define YKPIV_PINPOLICY_ONCE 2 -#define YKPIV_PINPOLICY_ALWAYS 3 - -#define YKPIV_TOUCHPOLICY_TAG 0xab -#define YKPIV_TOUCHPOLICY_DEFAULT 0 -#define YKPIV_TOUCHPOLICY_NEVER 1 -#define YKPIV_TOUCHPOLICY_ALWAYS 2 -#define YKPIV_TOUCHPOLICY_CACHED 3 - -#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 YKPIV_ATR_YK5_P1 "\x3b\xf8\x13\x00\x00\x81\x31\xfe\x15\x01\x59\x75\x62\x69\x4b\x65\x79\xc1" -#define YKPIV_ATR_YK5 "\x3b\xfd\x13\x00\x00\x81\x31\xfe\x15\x80\x73\xc0\x21\xc0\x57\x59\x75\x62\x69\x4b\x65\x79\x40" - -#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" -#define DEVYTPE_YK5 (DEVTYPE_YK | 0x00000035) // "5" - -#ifdef __cplusplus -} -#endif - -#endif