diff --git a/.gitignore b/.gitignore index 5f30be0..62b80fc 100644 --- a/.gitignore +++ b/.gitignore @@ -80,3 +80,9 @@ lib/ykpiv.gcno tool/util.gcno tool/yubico-piv-tool.gcda tool/yubico-piv-tool.gcno +ykcs11/.libs/ +ykcs11/*.o +ykcs11/*.lo +ykcs11/ykcs11.pc +ykcs11/libykcs11.la +ykcs11/ykcs11-version.h diff --git a/Makefile.am b/Makefile.am index 9a6517a..88d5fc4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -25,13 +25,13 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -SUBDIRS = lib tool +SUBDIRS = lib tool ykcs11 ACLOCAL_AMFLAGS = -I m4 EXTRA_DIST = windows.mk mac.mk tool/tests/basic.sh tools/fasc.pl -EXTRA_DIST += doc/Certificate_Authority_with_NEO.adoc doc/OS_X_code_signing.adoc doc/SSH_with_PIV_and_PKCS11.adoc doc/Windows_certificate.adoc doc/YubiKey_NEO_PIV_introduction.adoc +EXTRA_DIST += doc/Certificate_Authority.adoc doc/OS_X_code_signing.adoc doc/SSH_with_PIV_and_PKCS11.adoc doc/Windows_certificate.adoc doc/YubiKey_PIV_introduction.adoc if ENABLE_COV cov-reset: @@ -57,7 +57,7 @@ endif if ENABLE_CPPCHECK cppcheck: - $(CPPCHECK) -q -v -f --enable=all -i tool/cmdline.c lib tool + $(CPPCHECK) -q -v -f --enable=all -i tool/cmdline.c lib tool ykcs11 endif # Maintainer rules. diff --git a/NEWS b/NEWS index 69d5743..40dc892 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,8 @@ yubico-piv-tool NEWS -- History of user-visible changes. -*- outline -*- -* Version 1.0.4 (unreleased) +* Version 1.1.0 (unreleased) + +** Support for YubiKey 4 stuff * Version 1.0.3 (released 2015-10-01) diff --git a/README b/README index 72766b2..0215259 100644 --- a/README +++ b/README @@ -5,7 +5,7 @@ Introduction ------------ The Yubico PIV tool is used for interacting with the Privilege and -Identification Card (PIV) applet on a https://www.yubico.com[YubiKey NEO]. +Identification Card (PIV) application on a https://www.yubico.com[YubiKey]. With it you may generate keys on the device, importing keys and certificates, and create certificate requests, and other operations. diff --git a/configure.ac b/configure.ac index 3e94886..f51533e 100644 --- a/configure.ac +++ b/configure.ac @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -AC_INIT([yubico-piv-tool], [1.0.4]) +AC_INIT([yubico-piv-tool], [1.1.0]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) @@ -34,9 +34,9 @@ AC_CONFIG_MACRO_DIR([m4]) # Interfaces changed/added/removed: CURRENT++ REVISION=0 # Interfaces added: AGE++ # Interfaces removed: AGE=0 -AC_SUBST([LT_CURRENT], 1) -AC_SUBST([LT_REVISION], 9) -AC_SUBST([LT_AGE], 0) +AC_SUBST([LT_CURRENT], 2) +AC_SUBST([LT_REVISION], 0) +AC_SUBST([LT_AGE], 1) AM_INIT_AUTOMAKE([-Wall -Werror foreign]) AM_SILENT_RULES([yes]) @@ -52,6 +52,8 @@ PKG_PROG_PKG_CONFIG PKG_CHECK_MODULES(OPENSSL, libcrypto) +#PKG_CHECK_MODULES([LIBNSPR], [nspr], [], []) + gl_LD_VERSION_SCRIPT gl_VALGRIND_TESTS @@ -185,6 +187,11 @@ AC_SUBST(YKPIV_VERSION_MINOR, `echo $PACKAGE_VERSION | sed 's/.*\.\(.*\)\..*/\1/ AC_SUBST(YKPIV_VERSION_PATCH, `echo $PACKAGE_VERSION | sed 's/.*\..*\.\(.*\)/\1/g'`) AC_SUBST(YKPIV_VERSION_NUMBER, `printf "0x%02x%02x%02x" $YKPIV_VERSION_MAJOR $YKPIV_VERSION_MINOR $YKPIV_VERSION_PATCH`) +AC_SUBST(YKCS11_VERSION_MAJOR, `echo $PACKAGE_VERSION | sed 's/\(.*\)\..*\..*/\1/g'`) +AC_SUBST(YKCS11_VERSION_MINOR, `echo $PACKAGE_VERSION | sed 's/.*\.\(.*\)\..*/\1/g'`) +AC_SUBST(YKCS11_VERSION_PATCH, `echo $PACKAGE_VERSION | sed 's/.*\..*\.\(.*\)/\1/g'`) +AC_SUBST(YKCS11_VERSION_NUMBER, `printf "0x%02x%02x%02x" $YKCS11_VERSION_MAJOR $YKCS11_VERSION_MINOR $YKCS11_VERSION_PATCH`) + AC_CONFIG_FILES([ Makefile lib/Makefile @@ -193,6 +200,9 @@ AC_CONFIG_FILES([ tool/tests/Makefile lib/ykpiv-version.h lib/ykpiv.pc + ykcs11/Makefile + ykcs11/ykcs11-version.h + ykcs11/ykcs11.pc ]) AC_OUTPUT diff --git a/doc/Certificate_Authority_with_NEO.adoc b/doc/Certificate_Authority.adoc similarity index 90% rename from doc/Certificate_Authority_with_NEO.adoc rename to doc/Certificate_Authority.adoc index b607b74..7847cb4 100644 --- a/doc/Certificate_Authority_with_NEO.adoc +++ b/doc/Certificate_Authority.adoc @@ -1,8 +1,8 @@ -Certificate Authority with NEO +Certificate Authority with ------------------------------ This document explains how to set up a Certificate Authority (CA) with -Sub-CA private keys stored on YubiKey NEOs. Typical use for this is +Sub-CA private keys stored on YubiKeys. Typical use for this is to generate HTTPS certificates for internal servers. Considerations @@ -10,12 +10,12 @@ Considerations For our example, we have chosen to use one root CA with a private key stored in an offline machine, that signs sub-CAs with private keys -stored on YubiKey NEOs, which signs end-entity (EE) certs. We'll +stored on YubiKeys, which signs end-entity (EE) certs. We'll generate the Sub-CA private keys on an offline host and save a copy of those keys. We have chosen to use a RSA 3744 bit root CA key, and RSA 2048 bit -keys for the NEO Sub-CAs and EE certificates. The NEO is limited to +keys for the Sub-CAs and EE certificates. The is limited to RSA 1k and 2k keys (it supports ECDSA too but we chose to not use that here). @@ -39,7 +39,7 @@ offline machine, booted from a LiveCD. Some additional packages may be required (pcscd, etc, see below) and will have to be transferred on a USB stick. -You need a YubiKey NEO with the PIV applet on, which you can purchase +You need a YubiKey with the PIV application on, which you can purchase from Yubico. You need to install the PKCS#11 Engine: @@ -89,15 +89,15 @@ You may inspect the newly generated root CA with: openssl x509 -text < yubico-internal-https-ca-crt.pem -Preparing a Sub-CA NEO +Preparing a Sub-CA ---------------------- We need to change the management key, PIN and PUK code following the -YubiKey-NEO-PIV-Introduction.txt document. We also want to save a +YubiKey-PIV-Introduction.txt document. We also want to save a copy of these values. Here are the steps that are needed to be done -for each new Sub-CA NEO. +for each new Sub-CA. -This step is parametrized with the name of the YubiKey NEO user. +This step is parametrized with the name of the YubiKey user. Generate new management code, PIN and PUK as follows: user=Simon @@ -108,7 +108,7 @@ Generate new management code, PIN and PUK as follows: puk=`dd if=/dev/random bs=1 count=6 2>/dev/null | hexdump -v -e '/1 "%u"'|cut -c1-8` echo $puk > yubico-internal-https-$user-puk.txt -Configure a fresh NEO with these parameters as follows: +Configure a fresh with these parameters as follows: yubico-piv-tool -a set-mgm-key -n $key yubico-piv-tool -k $key -a change-pin -P 123456 -N $pin @@ -117,7 +117,7 @@ Configure a fresh NEO with these parameters as follows: Creating a Sub-CA ----------------- -This step is parametrized with the name of the YubiKey NEO user. This +This step is parametrized with the name of the YubiKey user. This means we will have one Sub-CA for every person authorized to sign certificates in our CA. @@ -157,11 +157,11 @@ You may inspect the newly generated EE cert with this command: openssl x509 -text < yubico-internal-https-subca-$user-crt.pem -Import Sub-CA key to NEO: +Import Sub-CA key to: yubico-piv-tool -k $key -a import-key -s 9c < yubico-internal-https-subca-$user-key.pem -Import Sub-CA cert to NEO: +Import Sub-CA cert to: yubico-piv-tool -k $key -a import-certificate -s 9c < yubico-internal-https-subca-$user-crt.pem @@ -190,7 +190,7 @@ Then generate a new private key and certificate request: EOF openssl req -sha256 -new -config yubico-internal-https-ee-$host-csr.conf -key yubico-internal-https-ee-$host-key.pem -nodes -out yubico-internal-https-ee-$host-csr.pem -Then sign the certificate using the NEO: +Then sign the certificate using the: cat>yubico-internal-https-ee-$host-crt.conf</dev/null | hexdump -v -e '/1 "%02X"'` @@ -63,7 +63,7 @@ To reset PIN/PUK retry counter AND codes (default pin 123456 puk yubico-piv-tool -k $key -a verify -P $pin -a pin-retries --pin-retries 3 --puk-retries 3 -To reset the applet (PIN/PUK need to be blocked hence trying a couple +To reset the application (PIN/PUK need to be blocked hence trying a couple of times -- you need to modify this if you have changed the default number of PIN/PUK retries). @@ -94,7 +94,7 @@ middleware. Card Holder Unique Identifier ----------------------------- -For the applet to be usable in windows the object CHUID (Card Holder +For the application to be usable in windows the object CHUID (Card Holder Unique Identifier) has to be set and unique. The card contents are also aggressively cached so the CHUID has to be changed if the card contents change. diff --git a/lib/error.c b/lib/error.c index ce2ac80..a812ed4 100644 --- a/lib/error.c +++ b/lib/error.c @@ -46,7 +46,7 @@ static const err_t errors[] = { ERR (YKPIV_MEMORY_ERROR, "Error allocating memory"), ERR (YKPIV_PCSC_ERROR, "Error in PCSC call"), ERR (YKPIV_SIZE_ERROR, "Wrong buffer size"), - ERR (YKPIV_APPLET_ERROR, "No PIV applet found"), + 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."), diff --git a/lib/internal.h b/lib/internal.h index 250ec9f..4ceafc3 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -42,10 +42,16 @@ #endif #endif +#define READER_LEN 32 +#define MAX_READERS 16 + struct ykpiv_state { SCARDCONTEXT context; SCARDHANDLE card; - int verbose; + unsigned long n_readers; + char readers[MAX_READERS][READER_LEN]; + unsigned long tot_readers_len; + int verbose; }; union u_APDU { diff --git a/lib/ykpiv.c b/lib/ykpiv.c index 96ccd55..0a8e830 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -103,6 +103,7 @@ ykpiv_rc ykpiv_init(ykpiv_state **state, int verbose) { } memset(s, 0, sizeof(ykpiv_state)); s->verbose = verbose; + s->context = -1; *state = s; return YKPIV_OK; } @@ -128,41 +129,15 @@ ykpiv_rc ykpiv_disconnect(ykpiv_state *state) { } ykpiv_rc ykpiv_connect(ykpiv_state *state, const char *wanted) { - unsigned long num_readers = 0; unsigned long active_protocol; - char reader_buf[1024]; + char reader_buf[2048]; + size_t num_readers = sizeof(reader_buf); long rc; char *reader_ptr; - rc = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &state->context); - if (rc != SCARD_S_SUCCESS) { - if(state->verbose) { - fprintf (stderr, "error: SCardEstablishContext failed, rc=%08lx\n", rc); - } - return YKPIV_PCSC_ERROR; - } - - rc = SCardListReaders(state->context, NULL, NULL, &num_readers); - if (rc != SCARD_S_SUCCESS) { - if(state->verbose) { - fprintf (stderr, "error: SCardListReaders failed, rc=%08lx\n", rc); - } - SCardReleaseContext(state->context); - return YKPIV_PCSC_ERROR; - } - - if (num_readers > sizeof(reader_buf)) { - num_readers = sizeof(reader_buf); - } - - rc = SCardListReaders(state->context, NULL, reader_buf, &num_readers); - if (rc != SCARD_S_SUCCESS) - { - if(state->verbose) { - fprintf (stderr, "error: SCardListReaders failed, rc=%08lx\n", rc); - } - SCardReleaseContext(state->context); - return YKPIV_PCSC_ERROR; + 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) { @@ -209,7 +184,7 @@ ykpiv_rc ykpiv_connect(ykpiv_state *state, const char *wanted) { return YKPIV_OK; } else { if(state->verbose) { - fprintf(stderr, "Failed selecting applet: %04x\n", sw); + fprintf(stderr, "Failed selecting application: %04x\n", sw); } } } @@ -226,6 +201,46 @@ ykpiv_rc ykpiv_connect(ykpiv_state *state, const char *wanted) { return YKPIV_GENERIC_ERROR; } +ykpiv_rc ykpiv_list_readers(ykpiv_state *state, char *readers, size_t *len) { + unsigned long num_readers = 0; + long rc; + + rc = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &state->context); + if (rc != SCARD_S_SUCCESS) { + if(state->verbose) { + fprintf (stderr, "error: SCardEstablishContext failed, rc=%08lx\n", rc); + } + return YKPIV_PCSC_ERROR; + } + + rc = SCardListReaders(state->context, NULL, NULL, &num_readers); + if (rc != SCARD_S_SUCCESS) { + if(state->verbose) { + fprintf (stderr, "error: SCardListReaders failed, rc=%08lx\n", rc); + } + SCardReleaseContext(state->context); + return YKPIV_PCSC_ERROR; + } + + if (num_readers > *len) { + num_readers = *len; + } + + 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); + return YKPIV_PCSC_ERROR; + } + + *len = num_readers; + + 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) { @@ -268,7 +283,7 @@ ykpiv_rc ykpiv_transfer_data(ykpiv_state *state, const unsigned char *templ, } 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); + fprintf(stderr, "Output buffer to small, wanted to write %lu, max was %lu.\n", *out_len + recv_len - 2, max_out); } return YKPIV_SIZE_ERROR; } @@ -406,7 +421,7 @@ ykpiv_rc ykpiv_authenticate(ykpiv_state *state, unsigned const char *key) { *dataptr++ = 8; if(RAND_pseudo_bytes(dataptr, 8) == -1) { if(state->verbose) { - fprintf(stderr, "Failed getting randomness for authentication.\n"); + fprintf(stderr, "Failed getting randomness for authentication.\n"); } return YKPIV_RANDOMNESS_ERROR; } @@ -433,6 +448,10 @@ ykpiv_rc ykpiv_authenticate(ykpiv_state *state, unsigned const char *key) { } 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]; unsigned long recv_len = sizeof(data); @@ -446,11 +465,11 @@ ykpiv_rc ykpiv_set_mgmkey(ykpiv_state *state, const unsigned char *new_key) { DES_set_odd_parity(&key_tmp); if(DES_is_weak_key(&key_tmp) != 0) { if(state->verbose) { - fprintf(stderr, "Won't set new key '"); - dump_hex(new_key + i * 8, 8); - fprintf(stderr, "' since it's weak (with parity the key is: "); - dump_hex(key_tmp, 8); - fprintf(stderr, ").\n"); + fprintf(stderr, "Won't set new key '"); + dump_hex(new_key + i * 8, 8); + fprintf(stderr, "' since it's weak (with parity the key is: "); + dump_hex(key_tmp, 8); + fprintf(stderr, ").\n"); } return YKPIV_GENERIC_ERROR; } @@ -459,7 +478,13 @@ ykpiv_rc ykpiv_set_mgmkey(ykpiv_state *state, const unsigned char *new_key) { memset(apdu.raw, 0, sizeof(apdu)); apdu.st.ins = YKPIV_INS_SET_MGMKEY; apdu.st.p1 = 0xff; - apdu.st.p2 = 0xff; + if(touch == 0) { + apdu.st.p2 = 0xff; + } else if(touch == 1) { + apdu.st.p2 = 0xfe; + } else { + return YKPIV_GENERIC_ERROR; + } apdu.st.lc = DES_KEY_SZ * 3 + 3; apdu.st.data[0] = YKPIV_ALGO_3DES; apdu.st.data[1] = YKPIV_KEY_CARDMGM; @@ -505,7 +530,7 @@ ykpiv_rc ykpiv_hex_decode(const char *hex_in, size_t in_len, } static ykpiv_rc _general_authenticate(ykpiv_state *state, - const unsigned char *raw_in, size_t in_len, + 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]; @@ -513,8 +538,7 @@ static ykpiv_rc _general_authenticate(ykpiv_state *state, unsigned char data[1024]; unsigned char templ[] = {0, YKPIV_INS_AUTHENTICATE, algorithm, key}; unsigned long recv_len = sizeof(data); - unsigned char sign_in[256]; - size_t pad_len = 0; + size_t key_len = 0; int sw; size_t bytes; size_t len = 0; @@ -522,31 +546,26 @@ static ykpiv_rc _general_authenticate(ykpiv_state *state, switch(algorithm) { case YKPIV_ALGO_RSA1024: - pad_len = 128; + key_len = 128; case YKPIV_ALGO_RSA2048: - if(pad_len == 0) { - pad_len = 256; + if(key_len == 0) { + key_len = 256; } - if(!decipher) { - if(in_len + RSA_PKCS1_PADDING_SIZE > pad_len) { - return YKPIV_SIZE_ERROR; - } - RSA_padding_add_PKCS1_type_1(sign_in, pad_len, raw_in, in_len); - in_len = pad_len; - } else { - if(in_len != pad_len) { - return YKPIV_SIZE_ERROR; - } - memcpy(sign_in, raw_in, in_len); + if(in_len != key_len) { + return YKPIV_SIZE_ERROR; } break; case YKPIV_ALGO_ECCP256: - if(!decipher && in_len > 32) { + key_len = 32; + 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 != 65) { + } else if(decipher && in_len != (key_len * 2) + 1) { return YKPIV_SIZE_ERROR; } - memcpy(sign_in, raw_in, in_len); break; default: return YKPIV_ALGORITHM_ERROR; @@ -564,7 +583,7 @@ static ykpiv_rc _general_authenticate(ykpiv_state *state, dataptr += set_length(dataptr, in_len + bytes + 3); *dataptr++ = 0x82; *dataptr++ = 0x00; - *dataptr++ = algorithm == YKPIV_ALGO_ECCP256 && decipher ? 0x85 : 0x81; + *dataptr++ = YKPIV_IS_EC(algorithm) && decipher ? 0x85 : 0x81; dataptr += set_length(dataptr, in_len); memcpy(dataptr, sign_in, (size_t)in_len); dataptr += in_len; @@ -579,7 +598,10 @@ static ykpiv_rc _general_authenticate(ykpiv_state *state, if(state->verbose) { fprintf(stderr, "Failed sign command with code %x.\n", sw); } - return YKPIV_GENERIC_ERROR; + if (sw == 0x6982) + return YKPIV_AUTHENTICATION_ERROR; + else + return YKPIV_GENERIC_ERROR; } /* skip the first 7c tag */ if(data[0] != 0x7c) { @@ -616,15 +638,14 @@ ykpiv_rc ykpiv_sign_data(ykpiv_state *state, unsigned char algorithm, unsigned char key) { return _general_authenticate(state, raw_in, in_len, sign_out, out_len, - algorithm, key, false); + algorithm, key, false); } - 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) { return _general_authenticate(state, in, in_len, out, out_len, - algorithm, key, true); + algorithm, key, true); } ykpiv_rc ykpiv_get_version(ykpiv_state *state, char *version, size_t len) { @@ -745,7 +766,7 @@ ykpiv_rc ykpiv_save_object(ykpiv_state *state, int object_id, dataptr += len; if((res = ykpiv_transfer_data(state, templ, data, dataptr - data, NULL, &outlen, - &sw)) != YKPIV_OK) { + &sw)) != YKPIV_OK) { return res; } diff --git a/lib/ykpiv.h b/lib/ykpiv.h index 4413f81..ffbee83 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -41,55 +41,62 @@ extern "C" { #endif - typedef struct ykpiv_state ykpiv_state; + typedef struct ykpiv_state ykpiv_state; - typedef enum { - YKPIV_OK = 0, - YKPIV_MEMORY_ERROR = -1, - YKPIV_PCSC_ERROR = -2, - YKPIV_SIZE_ERROR = -3, - YKPIV_APPLET_ERROR = -4, - YKPIV_AUTHENTICATION_ERROR = -5, - YKPIV_RANDOMNESS_ERROR = -6, - YKPIV_GENERIC_ERROR = -7, - YKPIV_KEY_ERROR = -8, - YKPIV_PARSE_ERROR = -9, - YKPIV_WRONG_PIN = -10, - YKPIV_INVALID_OBJECT = -11, - YKPIV_ALGORITHM_ERROR = -12, - } ykpiv_rc; + typedef enum { + YKPIV_OK = 0, + YKPIV_MEMORY_ERROR = -1, + YKPIV_PCSC_ERROR = -2, + YKPIV_SIZE_ERROR = -3, + YKPIV_APPLET_ERROR = -4, + YKPIV_AUTHENTICATION_ERROR = -5, + YKPIV_RANDOMNESS_ERROR = -6, + YKPIV_GENERIC_ERROR = -7, + YKPIV_KEY_ERROR = -8, + YKPIV_PARSE_ERROR = -9, + YKPIV_WRONG_PIN = -10, + YKPIV_INVALID_OBJECT = -11, + YKPIV_ALGORITHM_ERROR = -12, + } ykpiv_rc; - const char *ykpiv_strerror(ykpiv_rc err); - const char *ykpiv_strerror_name(ykpiv_rc err); + const char *ykpiv_strerror(ykpiv_rc err); + const char *ykpiv_strerror_name(ykpiv_rc err); - ykpiv_rc ykpiv_init(ykpiv_state **state, int verbose); - ykpiv_rc ykpiv_done(ykpiv_state *state); - ykpiv_rc ykpiv_connect(ykpiv_state *state, const char *wanted); - ykpiv_rc ykpiv_disconnect(ykpiv_state *state); - ykpiv_rc ykpiv_transfer_data(ykpiv_state *state, const unsigned char *templ, - const unsigned char *in_data, long in_len, - unsigned char *out_data, unsigned long *out_len, int *sw); - ykpiv_rc ykpiv_authenticate(ykpiv_state *state, const unsigned char *key); - ykpiv_rc ykpiv_set_mgmkey(ykpiv_state *state, const unsigned char *new_key); - ykpiv_rc ykpiv_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_fetch_object(ykpiv_state *state, int object_id, - unsigned char *data, unsigned long *len); + ykpiv_rc ykpiv_init(ykpiv_state **state, int verbose); + ykpiv_rc ykpiv_done(ykpiv_state *state); + ykpiv_rc ykpiv_connect(ykpiv_state *state, const char *wanted); + ykpiv_rc ykpiv_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_sign_data2(ykpiv_state *state, const unsigned char *sign_in, + size_t in_len, unsigned char *sign_out, size_t *out_len, + unsigned char algorithm, unsigned char key, int padding); // Allow not to add padding + ykpiv_rc ykpiv_decipher_data(ykpiv_state *state, const unsigned char *enc_in, + size_t in_len, unsigned char *enc_out, size_t *out_len, + unsigned char algorithm, unsigned char key); + ykpiv_rc ykpiv_get_version(ykpiv_state *state, char *version, size_t len); + ykpiv_rc ykpiv_verify(ykpiv_state *state, const char *pin, int *tries); + ykpiv_rc ykpiv_fetch_object(ykpiv_state *state, int object_id, + unsigned char *data, unsigned long *len); + ykpiv_rc ykpiv_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); + unsigned char *indata, size_t len); #define YKPIV_ALGO_3DES 0x03 #define YKPIV_ALGO_RSA1024 0x06 #define YKPIV_ALGO_RSA2048 0x07 #define YKPIV_ALGO_ECCP256 0x11 +#define YKPIV_ALGO_ECCP384 0x14 #define YKPIV_KEY_AUTHENTICATION 0x9a #define YKPIV_KEY_CARDMGM 0x9b @@ -111,6 +118,27 @@ extern "C" #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_INS_VERIFY 0x20 #define YKPIV_INS_CHANGE_REFERENCE 0x24 #define YKPIV_INS_RESET_RETRY 0x2c @@ -119,13 +147,25 @@ extern "C" #define YKPIV_INS_GET_DATA 0xcb #define YKPIV_INS_PUT_DATA 0xdb - /* Yubico vendor specific instructions */ + /* Yubico vendor specific instructions */ #define YKPIV_INS_SET_MGMKEY 0xff #define YKPIV_INS_IMPORT_KEY 0xfe #define YKPIV_INS_GET_VERSION 0xfd #define YKPIV_INS_RESET 0xfb #define YKPIV_INS_SET_PIN_RETRIES 0xfa +#define YKPIV_PINPOLICY_TAG 0xaa +#define YKPIV_PINPOLICY_NEVER 1 +#define YKPIV_PINPOLICY_ONCE 2 +#define YKPIV_PINPOLICY_ALWAYS 3 + +#define YKPIV_TOUCHPOLICY_TAG 0xab +#define YKPIV_TOUCHPOLICY_NEVER 1 +#define YKPIV_TOUCHPOLICY_ALWAYS 2 + +#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)) + #ifdef __cplusplus } #endif diff --git a/lib/ykpiv.map b/lib/ykpiv.map index b02d422..c647a5a 100644 --- a/lib/ykpiv.map +++ b/lib/ykpiv.map @@ -54,3 +54,10 @@ YKPIV_0.2.0 global: ykpiv_decipher_data; } YKPIV_0.1.0; + +YKPIV_1.1.0 +{ +global: + ykpiv_set_mgmkey2; + ykpiv_list_readers; +} YKPIV_0.1.0; diff --git a/m4/manywarnings.m4 b/m4/manywarnings.m4 index 864fc85..840550b 100644 --- a/m4/manywarnings.m4 +++ b/m4/manywarnings.m4 @@ -132,7 +132,6 @@ AC_DEFUN([gl_MANYWARN_ALL_GCC], -Wstack-protector \ -Woverlength-strings \ -Wbuiltin-macro-redefined \ - -Wmudflap \ -Wpacked-bitfield-compat \ -Wsync-nand \ ; do diff --git a/mac.mk b/mac.mk index 30e7d35..94549e7 100644 --- a/mac.mk +++ b/mac.mk @@ -65,8 +65,11 @@ doit: chmod u+w $(PWD)/tmp/root/lib/libcrypto.1.0.0.dylib && \ install_name_tool -id @executable_path/../lib/libcrypto.1.0.0.dylib $(PWD)/tmp/root/lib/libcrypto.1.0.0.dylib && \ install_name_tool -id @executable_path/../lib/libykpiv.1.dylib $(PWD)/tmp/root/lib/libykpiv.1.dylib && \ + install_name_tool -id @executable_path/../lib/libykcs11.1.dylib $(PWD)/tmp/root/lib/libykcs11.1.dylib && \ install_name_tool -change $(PWD)/tmp/root/lib/libcrypto.1.0.0.dylib @executable_path/../lib/libcrypto.1.0.0.dylib $(PWD)/tmp/root/lib/libykpiv.1.dylib && \ + install_name_tool -change $(PWD)/tmp/root/lib/libcrypto.1.0.0.dylib @executable_path/../lib/libcrypto.1.0.0.dylib $(PWD)/tmp/root/lib/libykcs11.1.dylib && \ install_name_tool -change $(PWD)/tmp/root/lib/libcrypto.1.0.0.dylib @executable_path/../lib/libcrypto.1.0.0.dylib $(PWD)/tmp/root/bin/yubico-piv-tool && \ + install_name_tool -change $(PWD)/tmp/root/lib/libykpiv.1.dylib @executable_path/../lib/libykpiv.1.dylib $(PWD)/tmp/root/lib/libykcs11.1.dylib && \ install_name_tool -change $(PWD)/tmp/root/lib/libykpiv.1.dylib @executable_path/../lib/libykpiv.1.dylib $(PWD)/tmp/root/bin/yubico-piv-tool ; \ if otool -L $(PWD)/tmp/root/lib/*.dylib $(PWD)/tmp/root/bin/* | grep '$(PWD)/tmp/root' | grep -q compatibility; then \ echo "something is incorrectly linked!"; \ diff --git a/tool/cmdline.ggo b/tool/cmdline.ggo index eefa59d..4d9ab43 100644 --- a/tool/cmdline.ggo +++ b/tool/cmdline.ggo @@ -32,18 +32,19 @@ option "action" a "Action to take" values="version","generate","set-mgm-key", "reset","pin-retries","import-key","import-certificate","set-chuid", "request-certificate","verify-pin","change-pin","change-puk","unblock-pin", "selfsign-certificate","delete-certificate","read-certificate","status", - "test-signature","test-decipher" enum multiple + "test-signature","test-decipher","list-readers" enum multiple text " Multiple actions may be given at once and will be executed in order for example --action=verify-pin --action=request-certificate\n" -option "slot" s "What key slot to operate on" values="9a","9c","9d","9e" enum optional +option "slot" s "What key slot to operate on" values="9a","9c","9d","9e","82","83","84","85","86","87","88","89","8a","8b","8c","8d","8e","8f","90","91","92","93","94","95" enum optional text " 9a is for PIV Authentication 9c is for Digital Signature (PIN always checked) 9d is for Key Management - 9e is for Card Authentication (PIN never checked)\n" -option "algorithm" A "What algorithm to use" values="RSA1024","RSA2048","ECCP256" enum optional default="RSA2048" -option "hash" H "Hash to use for signatures" values="SHA1","SHA256","SHA512" enum optional default="SHA256" + 9e is for Card Authentication (PIN never checked) + 82-95 is for Retired Key Management\n" +option "algorithm" A "What algorithm to use" values="RSA1024","RSA2048","ECCP256","ECCP384" enum optional default="RSA2048" +option "hash" H "Hash to use for signatures" values="SHA1","SHA256","SHA384","SHA512" enum optional default="SHA256" option "new-key" n "New authentication key to use" string optional option "pin-retries" - "Number of retries before the pin code is blocked" int optional dependon="puk-retries" option "puk-retries" - "Number of retries before the puk code is blocked" int optional dependon="pin-retries" @@ -57,4 +58,6 @@ text " /CN=host.example.com/OU=test/O=example.com/\n" option "pin" P "Pin/puk code for verification" string optional option "new-pin" N "New pin/puk code for changing" string optional dependon="pin" +option "pin-policy" - "Set pin policy for action generate or import-key" values="never","once","always" enum optional +option "touch-policy" - "Set touch policy for action generate, import-key or set-mgm-key" values="never","always" enum optional option "sign" - "Sign data" flag off hidden diff --git a/tool/util.c b/tool/util.c index 44686b1..17c4b12 100644 --- a/tool/util.c +++ b/tool/util.c @@ -82,6 +82,8 @@ unsigned char get_algorithm(EVP_PKEY *key) { int curve = EC_GROUP_get_curve_name(group); if(curve == NID_X9_62_prime256v1) { return YKPIV_ALGO_ECCP256; + } else if(curve == NID_secp384r1) { + return YKPIV_ALGO_ECCP384; } else { fprintf(stderr, "Unknown EC curve %d\n", curve); return 0; @@ -200,6 +202,66 @@ int get_object_id(enum enum_slot slot) { case slot_arg_9e: object = YKPIV_OBJ_CARD_AUTH; break; + case slot_arg_82: + object = YKPIV_OBJ_RETIRED1; + break; + case slot_arg_83: + object = YKPIV_OBJ_RETIRED2; + break; + case slot_arg_84: + object = YKPIV_OBJ_RETIRED3; + break; + case slot_arg_85: + object = YKPIV_OBJ_RETIRED4; + break; + case slot_arg_86: + object = YKPIV_OBJ_RETIRED5; + break; + case slot_arg_87: + object = YKPIV_OBJ_RETIRED6; + break; + case slot_arg_88: + object = YKPIV_OBJ_RETIRED7; + break; + case slot_arg_89: + object = YKPIV_OBJ_RETIRED8; + break; + case slot_arg_8a: + object = YKPIV_OBJ_RETIRED9; + break; + case slot_arg_8b: + object = YKPIV_OBJ_RETIRED10; + break; + case slot_arg_8c: + object = YKPIV_OBJ_RETIRED11; + break; + case slot_arg_8d: + object = YKPIV_OBJ_RETIRED12; + break; + case slot_arg_8e: + object = YKPIV_OBJ_RETIRED13; + break; + case slot_arg_8f: + object = YKPIV_OBJ_RETIRED14; + break; + case slot_arg_90: + object = YKPIV_OBJ_RETIRED15; + break; + case slot_arg_91: + object = YKPIV_OBJ_RETIRED16; + break; + case slot_arg_92: + object = YKPIV_OBJ_RETIRED17; + break; + case slot_arg_93: + object = YKPIV_OBJ_RETIRED18; + break; + case slot_arg_94: + object = YKPIV_OBJ_RETIRED19; + break; + case slot_arg_95: + object = YKPIV_OBJ_RETIRED20; + break; case slot__NULL: default: object = 0; @@ -207,6 +269,28 @@ int get_object_id(enum enum_slot slot) { return object; } +int key_to_object_id(int key) { + int object; + + switch(key) { + case 0x9a: + object = YKPIV_OBJ_AUTHENTICATION; + break; + case 0x9c: + object = YKPIV_OBJ_SIGNATURE; + break; + case 0x9d: + object = YKPIV_OBJ_KEY_MANAGEMENT; + break; + case 0x9e: + object = YKPIV_OBJ_CARD_AUTH; + break; + default: + object = 0; + } + return object; +} + bool set_component_with_len(unsigned char **in_ptr, const BIGNUM *bn, int element_len) { int real_len = BN_num_bytes(bn); *in_ptr += set_length(*in_ptr, element_len); @@ -262,3 +346,134 @@ bool read_pw(const char *name, char *pwbuf, size_t pwbuflen, int verify) { } return true; } + +static unsigned const char sha1oid[] = { + 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00, + 0x04, 0x14 +}; + +static unsigned const char sha256oid[] = { + 0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, + 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 +}; + +static unsigned const char sha384oid[] = { + 0x30, 0x41, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, + 0x02, 0x02, 0x05, 0x00, 0x04, 0x30 +}; + +static unsigned const char sha512oid[] = { + 0x30, 0x51, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, + 0x02, 0x03, 0x05, 0x00, 0x04, 0x40 +}; + +const EVP_MD *get_hash(enum enum_hash hash, const unsigned char **oid, size_t *oid_len) { + switch(hash) { + case hash_arg_SHA1: + if(oid) { + *oid = sha1oid; + *oid_len = sizeof(sha1oid); + } + return EVP_sha1(); + case hash_arg_SHA256: + if(oid) { + *oid = sha256oid; + *oid_len = sizeof(sha256oid); + } + return EVP_sha256(); + case hash_arg_SHA384: + if(oid) { + *oid = sha384oid; + *oid_len = sizeof(sha384oid); + } + return EVP_sha384(); + case hash_arg_SHA512: + if(oid) { + *oid = sha512oid; + *oid_len = sizeof(sha512oid); + } + return EVP_sha512(); + case hash__NULL: + default: + return NULL; + } +} + +int get_hashnid(enum enum_hash hash, unsigned char algorithm) { + switch(algorithm) { + case YKPIV_ALGO_RSA1024: + case YKPIV_ALGO_RSA2048: + switch(hash) { + case hash_arg_SHA1: + return NID_sha1WithRSAEncryption; + case hash_arg_SHA256: + return NID_sha256WithRSAEncryption; + case hash_arg_SHA384: + return NID_sha384WithRSAEncryption; + case hash_arg_SHA512: + return NID_sha512WithRSAEncryption; + case hash__NULL: + default: + return 0; + } + case YKPIV_ALGO_ECCP256: + case YKPIV_ALGO_ECCP384: + switch(hash) { + case hash_arg_SHA1: + return NID_ecdsa_with_SHA1; + case hash_arg_SHA256: + return NID_ecdsa_with_SHA256; + case hash_arg_SHA384: + return NID_ecdsa_with_SHA384; + case hash_arg_SHA512: + return NID_ecdsa_with_SHA512; + case hash__NULL: + default: + return 0; + } + default: + return 0; + } +} + +unsigned char get_piv_algorithm(enum enum_algorithm algorithm) { + switch(algorithm) { + case algorithm_arg_RSA2048: + return YKPIV_ALGO_RSA2048; + case algorithm_arg_RSA1024: + return YKPIV_ALGO_RSA1024; + case algorithm_arg_ECCP256: + return YKPIV_ALGO_ECCP256; + case algorithm_arg_ECCP384: + return YKPIV_ALGO_ECCP384; + case algorithm__NULL: + default: + return 0; + } +} + +unsigned char get_pin_policy(enum enum_pin_policy policy) { + switch(policy) { + case pin_policy_arg_never: + return YKPIV_PINPOLICY_NEVER; + case pin_policy_arg_once: + return YKPIV_PINPOLICY_ONCE; + case pin_policy_arg_always: + return YKPIV_PINPOLICY_ALWAYS; + case pin_policy__NULL: + default: + return 0; + } +} + +unsigned char get_touch_policy(enum enum_touch_policy policy) { + switch(policy) { + case touch_policy_arg_never: + return YKPIV_TOUCHPOLICY_NEVER; + case touch_policy_arg_always: + return YKPIV_TOUCHPOLICY_ALWAYS; + case touch_policy__NULL: + default: + return 0; + } +} diff --git a/tool/util.h b/tool/util.h index a3ecf8a..f6d984d 100644 --- a/tool/util.h +++ b/tool/util.h @@ -45,9 +45,15 @@ X509_NAME *parse_name(const char*); unsigned char get_algorithm(EVP_PKEY*); FILE *open_file(const char*, int); int get_object_id(enum enum_slot slot); +int key_to_object_id(int key); bool set_component_with_len(unsigned char**, const BIGNUM*, int); bool prepare_rsa_signature(const unsigned char*, unsigned int, unsigned char*, unsigned int*, int); bool read_pw(const char*, char*, size_t, int); +const EVP_MD *get_hash(enum enum_hash, const unsigned char**, size_t*); +int get_hashnid(enum enum_hash, unsigned char); +unsigned char get_piv_algorithm(enum enum_algorithm); +unsigned char get_pin_policy(enum enum_pin_policy); +unsigned char get_touch_policy(enum enum_touch_policy); #endif diff --git a/tool/yubico-piv-tool.adoc b/tool/yubico-piv-tool.adoc index e918907..1ed8ad9 100644 --- a/tool/yubico-piv-tool.adoc +++ b/tool/yubico-piv-tool.adoc @@ -30,7 +30,7 @@ For more information about what's happening --verbose can be added to any command. For much more information --verbose=2 may be used. -Display what version of the applet is running on the YubiKey Neo: +Display what version of the application is running on the YubiKey: yubico-piv-tool -a version diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index a4a6a6c..01a6690 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -63,20 +63,7 @@ unsigned const char chuid_tmpl[] = { }; #define CHUID_GUID_OFFS 29 -unsigned const char sha1oid[] = { - 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00, - 0x04, 0x14 -}; - -unsigned const char sha256oid[] = { - 0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, - 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 -}; - -unsigned const char sha512oid[] = { - 0x30, 0x51, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, - 0x02, 0x03, 0x05, 0x00, 0x04, 0x40 -}; +#define MAX_OID_LEN 19 #define KEY_LEN 24 @@ -88,9 +75,9 @@ static void print_version(ykpiv_state *state, const char *output_file_name) { } if(ykpiv_get_version(state, version, sizeof(version)) == YKPIV_OK) { - fprintf(output_file, "Applet version %s found.\n", version); + fprintf(output_file, "Application version %s found.\n", version); } else { - fprintf(stderr, "Failed to retrieve applet version.\n"); + fprintf(stderr, "Failed to retrieve application version.\n"); } if(output_file != stdout) { @@ -98,10 +85,31 @@ static void print_version(ykpiv_state *state, const char *output_file_name) { } } +static bool sign_data(ykpiv_state *state, const unsigned char *in, size_t len, unsigned char *out, + size_t *out_len, unsigned char algorithm, int key) { + + unsigned char signinput[1024]; + if(YKPIV_IS_RSA(algorithm)) { + size_t padlen = algorithm == YKPIV_ALGO_RSA1024 ? 128 : 256; + if(RSA_padding_add_PKCS1_type_1(signinput, padlen, in, len) == 0) { + fprintf(stderr, "Failed adding padding.\n"); + return false; + } + in = signinput; + len = padlen; + } + if(ykpiv_sign_data(state, in, len, out, out_len, algorithm, key) == YKPIV_OK) { + return true; + } + return false; +} + static bool generate_key(ykpiv_state *state, const char *slot, enum enum_algorithm algorithm, const char *output_file_name, - enum enum_key_format key_format) { - unsigned char in_data[5]; + enum enum_key_format key_format, enum enum_pin_policy pin_policy, + enum enum_touch_policy touch_policy) { + unsigned char in_data[11]; + unsigned char *in_ptr = in_data; unsigned char data[1024]; unsigned char templ[] = {0, YKPIV_INS_GENERATE_ASYMMERTRIC, 0, 0}; unsigned long recv_len = sizeof(data); @@ -124,31 +132,46 @@ static bool generate_key(ykpiv_state *state, const char *slot, return false; } - in_data[0] = 0xac; - in_data[1] = 3; - in_data[2] = 0x80; - in_data[3] = 1; - switch(algorithm) { - case algorithm_arg_RSA2048: - in_data[4] = YKPIV_ALGO_RSA2048; - break; - case algorithm_arg_RSA1024: - in_data[4] = YKPIV_ALGO_RSA1024; - break; - case algorithm_arg_ECCP256: - in_data[4] = YKPIV_ALGO_ECCP256; - break; - case algorithm__NULL: - default: - fprintf(stderr, "Unexepcted algorithm.\n"); - goto generate_out; + *in_ptr++ = 0xac; + *in_ptr++ = 3; + *in_ptr++ = 0x80; + *in_ptr++ = 1; + *in_ptr++ = get_piv_algorithm(algorithm); + if(in_data[4] == 0) { + fprintf(stderr, "Unexepcted algorithm.\n"); + goto generate_out; } - if(ykpiv_transfer_data(state, templ, in_data, sizeof(in_data), data, + if(pin_policy != pin_policy__NULL) { + in_data[1] += 3; + *in_ptr++ = YKPIV_PINPOLICY_TAG; + *in_ptr++ = 1; + *in_ptr++ = get_pin_policy(pin_policy); + } + if(touch_policy != touch_policy__NULL) { + in_data[1] += 3; + *in_ptr++ = YKPIV_TOUCHPOLICY_TAG; + *in_ptr++ = 1; + *in_ptr++ = get_touch_policy(touch_policy); + } + if(ykpiv_transfer_data(state, templ, in_data, in_ptr - in_data, data, &recv_len, &sw) != YKPIV_OK) { fprintf(stderr, "Failed to communicate.\n"); goto generate_out; } else if(sw != 0x9000) { - fprintf(stderr, "Failed to generate new key.\n"); + fprintf(stderr, "Failed to generate new key ("); + if(sw == 0x6b00) { + fprintf(stderr, "slot not supported?)\n"); + } else if(sw == 0x6a80) { + if(pin_policy != pin_policy__NULL) { + fprintf(stderr, "pin policy not supported?)\n"); + } else if(touch_policy != touch_policy__NULL) { + fprintf(stderr, "touch policy not supported?)\n"); + } else { + fprintf(stderr, "algorithm not supported?)\n"); + } + } else { + fprintf(stderr, "error %x)\n", sw); + } goto generate_out; } @@ -187,24 +210,34 @@ static bool generate_key(ykpiv_state *state, const char *slot, rsa->n = bignum_n; rsa->e = bignum_e; EVP_PKEY_set1_RSA(public_key, rsa); - } else if(algorithm == algorithm_arg_ECCP256) { + } else if(algorithm == algorithm_arg_ECCP256 || algorithm == algorithm_arg_ECCP384) { EC_GROUP *group; unsigned char *data_ptr = data + 3; + int nid; + size_t len; + + if(algorithm == algorithm_arg_ECCP256) { + nid = NID_X9_62_prime256v1; + len = 65; + } else { + nid = NID_secp384r1; + len = 97; + } eckey = EC_KEY_new(); - group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); - EC_GROUP_set_asn1_flag(group, NID_X9_62_prime256v1); + group = EC_GROUP_new_by_curve_name(nid); + EC_GROUP_set_asn1_flag(group, nid); EC_KEY_set_group(eckey, group); point = EC_POINT_new(group); if(*data_ptr++ != 0x86) { fprintf(stderr, "Failed to parse public key structure.\n"); goto generate_out; } - if(*data_ptr++ != 65) { /* the curve point should always be 65 bytes */ + if(*data_ptr++ != len) { /* the curve point should always be 65 bytes */ fprintf(stderr, "Unexpected length.\n"); goto generate_out; } - if(!EC_POINT_oct2point(group, point, data_ptr, 65, NULL)) { + if(!EC_POINT_oct2point(group, point, data_ptr, len, NULL)) { fprintf(stderr, "Failed to load public point.\n"); goto generate_out; } @@ -283,7 +316,8 @@ static bool set_pin_retries(ykpiv_state *state, int pin_retries, int puk_retries } static bool import_key(ykpiv_state *state, enum enum_key_format key_format, - const char *input_file_name, const char *slot, char *password) { + const char *input_file_name, const char *slot, char *password, + enum enum_pin_policy pin_policy, enum enum_touch_policy touch_policy) { int key = 0; FILE *input_file = NULL; EVP_PKEY *private_key = NULL; @@ -336,7 +370,7 @@ static bool import_key(ykpiv_state *state, enum enum_key_format key_format, unsigned char *in_ptr = in_data; unsigned char templ[] = {0, YKPIV_INS_IMPORT_KEY, algorithm, key}; int sw; - if(algorithm == YKPIV_ALGO_RSA1024 || algorithm == YKPIV_ALGO_RSA2048) { + if(YKPIV_IS_RSA(algorithm)) { RSA *rsa_private_key = EVP_PKEY_get1_RSA(private_key); unsigned char e[4]; unsigned char *e_ptr = e; @@ -380,20 +414,45 @@ static bool import_key(ykpiv_state *state, enum enum_key_format key_format, fprintf(stderr, "Failed setting iqmp component.\n"); goto import_out; } - } else if(algorithm == YKPIV_ALGO_ECCP256) { + } else if(YKPIV_IS_EC(algorithm)) { EC_KEY *ec = EVP_PKEY_get1_EC_KEY(private_key); const BIGNUM *s = EC_KEY_get0_private_key(ec); + int element_len = 32; + + if(algorithm == YKPIV_ALGO_ECCP384) { + element_len = 48; + } *in_ptr++ = 0x06; - if(set_component_with_len(&in_ptr, s, 32) == false) { + if(set_component_with_len(&in_ptr, s, element_len) == false) { fprintf(stderr, "Failed setting ec private key.\n"); goto import_out; } } + if(pin_policy != pin_policy__NULL) { + *in_ptr++ = YKPIV_PINPOLICY_TAG; + *in_ptr++ = 1; + *in_ptr++ = get_pin_policy(pin_policy); + } + if(touch_policy != touch_policy__NULL) { + *in_ptr++ = YKPIV_TOUCHPOLICY_TAG; + *in_ptr++ = 1; + *in_ptr++ = get_touch_policy(touch_policy); + } + if(ykpiv_transfer_data(state, templ, in_data, in_ptr - in_data, data, &recv_len, &sw) != YKPIV_OK) { return false; + } else if(sw == 0x6a80) { + fprintf(stderr, "Failed import."); + if(pin_policy != pin_policy__NULL) { + fprintf(stderr, "Maybe pin-policy is not supported on this key?\n"); + } else if(touch_policy != touch_policy__NULL) { + fprintf(stderr, "Maybe touch-policy is not supported on this key?\n"); + } else { + fprintf(stderr, "Maybe algorithm is not supported on this key?\n"); + } } else if(sw != 0x9000) { fprintf(stderr, "Failed import command with code %x.\n", sw); } else { @@ -559,7 +618,7 @@ static bool request_certificate(ykpiv_state *state, enum enum_key_format key_for EVP_PKEY *public_key = NULL; const EVP_MD *md; bool ret = false; - unsigned char digest[EVP_MAX_MD_SIZE + sizeof(sha512oid)]; // maximum.. + unsigned char digest[EVP_MAX_MD_SIZE + MAX_OID_LEN]; unsigned int digest_len; unsigned int md_len; unsigned char algorithm; @@ -597,25 +656,9 @@ static bool request_certificate(ykpiv_state *state, enum enum_key_format key_for goto request_out; } - switch(hash) { - case hash_arg_SHA1: - md = EVP_sha1(); - oid = sha1oid; - oid_len = sizeof(sha1oid); - break; - case hash_arg_SHA256: - md = EVP_sha256(); - oid = sha256oid; - oid_len = sizeof(sha256oid); - break; - case hash_arg_SHA512: - md = EVP_sha512(); - oid = sha512oid; - oid_len = sizeof(sha512oid); - break; - case hash__NULL: - default: - goto request_out; + md = get_hash(hash, &oid, &oid_len); + if(md == NULL) { + goto request_out; } md_len = (unsigned int)EVP_MD_size(md); @@ -651,54 +694,24 @@ static bool request_certificate(ykpiv_state *state, enum enum_key_format key_for goto request_out; } - switch(algorithm) { - case YKPIV_ALGO_RSA1024: - case YKPIV_ALGO_RSA2048: - signinput = digest; - len = oid_len + digest_len; - switch(hash) { - case hash_arg_SHA1: - nid = NID_sha1WithRSAEncryption; - break; - case hash_arg_SHA256: - nid = NID_sha256WithRSAEncryption; - break; - case hash_arg_SHA512: - nid = NID_sha512WithRSAEncryption; - break; - case hash__NULL: - default: - goto request_out; - } - break; - case YKPIV_ALGO_ECCP256: - signinput = digest + oid_len; - len = digest_len; - switch(hash) { - case hash_arg_SHA1: - nid = NID_ecdsa_with_SHA1; - break; - case hash_arg_SHA256: - nid = NID_ecdsa_with_SHA256; - break; - case hash_arg_SHA512: - nid = NID_ecdsa_with_SHA512; - break; - case hash__NULL: - default: - goto request_out; - } - break; - default: - fprintf(stderr, "Unsupported algorithm %x.\n", algorithm); - goto request_out; + nid = get_hashnid(hash, algorithm); + if(nid == 0) { + fprintf(stderr, "Unsupported algorithm %x or hash %x\n", algorithm, hash); + goto request_out; } + if(YKPIV_IS_RSA(algorithm)) { + signinput = digest; + len = oid_len + digest_len; + } else { + signinput = digest + oid_len; + len = digest_len; + } + req->sig_alg->algorithm = OBJ_nid2obj(nid); { unsigned char signature[1024]; size_t sig_len = sizeof(signature); - if(ykpiv_sign_data(state, signinput, len, signature, &sig_len, algorithm, key) - != YKPIV_OK) { + if(!sign_data(state, signinput, len, signature, &sig_len, algorithm, key)) { fprintf(stderr, "Failed signing request.\n"); goto request_out; } @@ -743,7 +756,7 @@ static bool selfsign_certificate(ykpiv_state *state, enum enum_key_format key_fo X509 *x509 = NULL; X509_NAME *name = NULL; const EVP_MD *md; - unsigned char digest[EVP_MAX_MD_SIZE + sizeof(sha512oid)]; + unsigned char digest[EVP_MAX_MD_SIZE + MAX_OID_LEN]; unsigned int digest_len; unsigned char algorithm; int key = 0; @@ -781,27 +794,10 @@ static bool selfsign_certificate(ykpiv_state *state, enum enum_key_format key_fo goto selfsign_out; } - switch(hash) { - case hash_arg_SHA1: - md = EVP_sha1(); - oid = sha1oid; - oid_len = sizeof(sha1oid); - break; - case hash_arg_SHA256: - md = EVP_sha256(); - oid = sha256oid; - oid_len = sizeof(sha256oid); - break; - case hash_arg_SHA512: - md = EVP_sha512(); - oid = sha512oid; - oid_len = sizeof(sha512oid); - break; - case hash__NULL: - default: - goto selfsign_out; + md = get_hash(hash, &oid, &oid_len); + if(md == NULL) { + goto selfsign_out; } - md_len = (unsigned int)EVP_MD_size(md); digest_len = sizeof(digest) - md_len; @@ -843,48 +839,18 @@ static bool selfsign_certificate(ykpiv_state *state, enum enum_key_format key_fo fprintf(stderr, "Failed setting certificate issuer.\n"); goto selfsign_out; } - switch(algorithm) { - case YKPIV_ALGO_RSA1024: - case YKPIV_ALGO_RSA2048: - signinput = digest; - len = oid_len + md_len; - switch(hash) { - case hash_arg_SHA1: - nid = NID_sha1WithRSAEncryption; - break; - case hash_arg_SHA256: - nid = NID_sha256WithRSAEncryption; - break; - case hash_arg_SHA512: - nid = NID_sha512WithRSAEncryption; - break; - case hash__NULL: - default: - goto selfsign_out; - } - break; - case YKPIV_ALGO_ECCP256: - signinput = digest + oid_len; - len = md_len; - switch(hash) { - case hash_arg_SHA1: - nid = NID_ecdsa_with_SHA1; - break; - case hash_arg_SHA256: - nid = NID_ecdsa_with_SHA256; - break; - case hash_arg_SHA512: - nid = NID_ecdsa_with_SHA512; - break; - case hash__NULL: - default: - goto selfsign_out; - } - break; - default: - fprintf(stderr, "Unsupported algorithm %x.\n", algorithm); - goto selfsign_out; + nid = get_hashnid(hash, algorithm); + if(nid == 0) { + goto selfsign_out; } + if(YKPIV_IS_RSA(algorithm)) { + signinput = digest; + len = oid_len + md_len; + } else { + signinput = digest + oid_len; + len = md_len; + } + x509->sig_alg->algorithm = OBJ_nid2obj(nid); x509->cert_info->signature->algorithm = x509->sig_alg->algorithm; memcpy(digest, oid, oid_len); @@ -897,8 +863,7 @@ static bool selfsign_certificate(ykpiv_state *state, enum enum_key_format key_fo { unsigned char signature[1024]; size_t sig_len = sizeof(signature); - if(ykpiv_sign_data(state, signinput, len, signature, &sig_len, algorithm, key) - != YKPIV_OK) { + if(!sign_data(state, signinput, len, signature, &sig_len, algorithm, key)) { fprintf(stderr, "Failed signing certificate.\n"); goto selfsign_out; } @@ -1028,7 +993,7 @@ static bool change_pin(ykpiv_state *state, enum enum_action action, const char * if(action == action_arg_changeMINUS_pin) { fprintf(stderr, "The pin code is blocked, use the unblock-pin action to unblock it.\n"); } else { - fprintf(stderr, "The puk code is blocked, you will have to reinitialize the applet.\n"); + fprintf(stderr, "The puk code is blocked, you will have to reinitialize the application.\n"); } } else { fprintf(stderr, "Failed changing/unblocking code, error: %x\n", sw); @@ -1138,37 +1103,17 @@ static bool sign_file(ykpiv_state *state, const char *input, const char *output, return false; } - switch(algorithm) { - case algorithm_arg_RSA2048: - algo = YKPIV_ALGO_RSA2048; - break; - case algorithm_arg_RSA1024: - algo = YKPIV_ALGO_RSA1024; - break; - case algorithm_arg_ECCP256: - algo = YKPIV_ALGO_ECCP256; - break; - case algorithm__NULL: - default: - goto out; + algo = get_piv_algorithm(algorithm); + if(algo == 0) { + goto out; } { EVP_MD_CTX *mdctx; - switch(hash) { - case hash_arg_SHA1: - md = EVP_sha1(); - break; - case hash_arg_SHA256: - md = EVP_sha256(); - break; - case hash_arg_SHA512: - md = EVP_sha512(); - break; - case hash__NULL: - default: - goto out; + md = get_hash(hash, NULL, NULL); + if(md == NULL) { + goto out; } mdctx = EVP_MD_CTX_create(); @@ -1187,16 +1132,15 @@ static bool sign_file(ykpiv_state *state, const char *input, const char *output, EVP_MD_CTX_destroy(mdctx); } - if(algo == YKPIV_ALGO_RSA1024 || algo == YKPIV_ALGO_RSA2048) { + if(YKPIV_IS_RSA(algo)) { prepare_rsa_signature(hashed, hash_len, hashed, &hash_len, EVP_MD_type(md)); } { unsigned char buf[1024]; size_t len = sizeof(buf); - ykpiv_rc rc = ykpiv_sign_data(state, hashed, hash_len, buf, &len, algo, key); - if(rc != YKPIV_OK) { - fprintf(stderr, "failed signing file: %s\n", ykpiv_strerror(rc)); + if(!sign_data(state, hashed, hash_len, buf, &len, algo, key)) { + fprintf(stderr, "failed signing file\n"); goto out; } @@ -1268,6 +1212,9 @@ static void print_cert_info(ykpiv_state *state, enum enum_slot slot, const EVP_M case YKPIV_ALGO_ECCP256: fprintf(output, "ECCP256\n"); break; + case YKPIV_ALGO_ECCP384: + fprintf(output, "ECCP384\n"); + break; default: fprintf(output, "Unknown\n"); } @@ -1328,19 +1275,9 @@ static bool status(ykpiv_state *state, enum enum_hash hash, return false; } - switch(hash) { - case hash_arg_SHA1: - md = EVP_sha1(); - break; - case hash_arg_SHA256: - md = EVP_sha256(); - break; - case hash_arg_SHA512: - md = EVP_sha512(); - break; - case hash__NULL: - default: - return false; + md = get_hash(hash, NULL, NULL); + if(md == NULL) { + return false; } fprintf(output_file, "CHUID:\t"); @@ -1404,19 +1341,9 @@ static bool test_signature(ykpiv_state *state, enum enum_slot slot, goto test_out; } - switch(hash) { - case hash_arg_SHA1: - md = EVP_sha1(); - break; - case hash_arg_SHA256: - md = EVP_sha256(); - break; - case hash_arg_SHA512: - md = EVP_sha512(); - break; - case hash__NULL: - default: - return false; + md = get_hash(hash, NULL, NULL); + if(md == NULL) { + return false; } { @@ -1456,14 +1383,13 @@ static bool test_signature(ykpiv_state *state, enum enum_slot slot, goto test_out; } sscanf(cmdline_parser_slot_values[slot], "%2x", &key); - if(algorithm == YKPIV_ALGO_RSA1024 || algorithm == YKPIV_ALGO_RSA2048) { + if(YKPIV_IS_RSA(algorithm)) { prepare_rsa_signature(data, data_len, encoded, &enc_len, EVP_MD_type(md)); ptr = encoded; } else { enc_len = data_len; } - if(ykpiv_sign_data(state, ptr, enc_len, signature, &sig_len, algorithm, key) - != YKPIV_OK) { + if(!sign_data(state, ptr, enc_len, signature, &sig_len, algorithm, key)) { fprintf(stderr, "Failed signing test data.\n"); goto test_out; } @@ -1490,6 +1416,7 @@ static bool test_signature(ykpiv_state *state, enum enum_slot slot, break; case YKPIV_ALGO_ECCP256: + case YKPIV_ALGO_ECCP384: { EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pubkey); if(ECDSA_verify(0, data, (int)data_len, signature, (int)sig_len, ec) == 1) { @@ -1561,7 +1488,7 @@ static bool test_decipher(ykpiv_state *state, enum enum_slot slot, goto decipher_out; } sscanf(cmdline_parser_slot_values[slot], "%2x", &key); - if(algorithm == YKPIV_ALGO_RSA1024 || algorithm == YKPIV_ALGO_RSA2048) { + if(YKPIV_IS_RSA(algorithm)) { unsigned char secret[32]; unsigned char secret2[32]; unsigned char data[256]; @@ -1602,20 +1529,30 @@ static bool test_decipher(ykpiv_state *state, enum enum_slot slot, } else { fprintf(stderr, "Failed unwrapping PKCS1 envelope.\n"); } - } else { - unsigned char secret[32]; - unsigned char secret2[32]; - unsigned char public_key[65]; + } else if(YKPIV_IS_EC(algorithm)) { + unsigned char secret[48]; + unsigned char secret2[48]; + unsigned char public_key[97]; unsigned char *ptr = public_key; size_t len = sizeof(secret); EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pubkey); + int nid; + size_t key_len; - tmpkey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + if(algorithm == YKPIV_ALGO_ECCP256) { + nid = NID_X9_62_prime256v1; + key_len = 32; + } else { + nid = NID_secp384r1; + key_len = 48; + } + + tmpkey = EC_KEY_new_by_curve_name(nid); EC_KEY_generate_key(tmpkey); ECDH_compute_key(secret, len, EC_KEY_get0_public_key(ec), tmpkey, NULL); i2o_ECPublicKey(tmpkey, &ptr); - if(ykpiv_decipher_data(state, public_key, sizeof(public_key), secret2, &len, algorithm, key) != YKPIV_OK) { + if(ykpiv_decipher_data(state, public_key, (key_len * 2) + 1, secret2, &len, algorithm, key) != YKPIV_OK) { fprintf(stderr, "Failed ECDH exchange!\n"); goto decipher_out; } @@ -1625,7 +1562,7 @@ static bool test_decipher(ykpiv_state *state, enum enum_slot slot, fprintf(stderr, "ECDH card generated: "); dump_hex(secret2, len, stderr, true); } - if(memcmp(secret, secret2, 32) == 0) { + if(memcmp(secret, secret2, key_len) == 0) { fprintf(stderr, "Successfully performed ECDH exchange with card.\n"); ret = true; } else { @@ -1647,6 +1584,21 @@ decipher_out: return ret; } +static bool list_readers(ykpiv_state *state) { + char readers[2048]; + char *reader_ptr; + size_t len = sizeof(readers); + ykpiv_rc rc = ykpiv_list_readers(state, readers, &len); + if(rc != YKPIV_OK) { + fprintf(stderr, "Failed listing readers.\n"); + return false; + } + for(reader_ptr = readers; *reader_ptr != '\0'; reader_ptr += strlen(reader_ptr) + 1) { + printf("%s\n", reader_ptr); + } + return true; +} + int main(int argc, char *argv[]) { struct gengetopt_args_info args_info; ykpiv_state *state; @@ -1700,6 +1652,7 @@ int main(int argc, char *argv[]) { case action_arg_version: case action_arg_reset: case action_arg_status: + case action_arg_listMINUS_readers: case action__NULL: default: continue; @@ -1744,6 +1697,7 @@ int main(int argc, char *argv[]) { case action_arg_status: case action_arg_testMINUS_signature: case action_arg_testMINUS_decipher: + case action_arg_listMINUS_readers: case action__NULL: default: if(verbosity) { @@ -1769,11 +1723,11 @@ int main(int argc, char *argv[]) { } if(ykpiv_authenticate(state, key) != YKPIV_OK) { - fprintf(stderr, "Failed authentication with the applet.\n"); + fprintf(stderr, "Failed authentication with the application.\n"); return EXIT_FAILURE; } if(verbosity) { - fprintf(stderr, "Successful applet authentication.\n"); + fprintf(stderr, "Successful application authentication.\n"); } break; } @@ -1795,7 +1749,8 @@ int main(int argc, char *argv[]) { print_version(state, args_info.output_arg); break; case action_arg_generate: - if(generate_key(state, args_info.slot_orig, args_info.algorithm_arg, args_info.output_arg, args_info.key_format_arg) == false) { + if(generate_key(state, args_info.slot_orig, args_info.algorithm_arg, args_info.output_arg, args_info.key_format_arg, + args_info.pin_policy_arg, args_info.touch_policy_arg) == false) { ret = EXIT_FAILURE; } else { fprintf(stderr, "Successfully generated a new private key.\n"); @@ -1816,8 +1771,12 @@ int main(int argc, char *argv[]) { if(ykpiv_hex_decode(new_mgm_key, strlen(new_mgm_key), new_key, &new_key_len) != YKPIV_OK) { fprintf(stderr, "Failed decoding new key!\n"); ret = EXIT_FAILURE; - } else if(ykpiv_set_mgmkey(state, new_key) != YKPIV_OK) { - fprintf(stderr, "Failed setting the new key!\n"); + } else if(ykpiv_set_mgmkey2(state, new_key, args_info.touch_policy_arg == touch_policy_arg_always ? 1 : 0) != YKPIV_OK) { + fprintf(stderr, "Failed setting the new key!"); + if(args_info.touch_policy_arg != touch_policy__NULL) { + fprintf(stderr, " Maybe touch policy is not supported on this key?"); + } + fprintf(stderr, "\n"); ret = EXIT_FAILURE; } else { fprintf(stderr, "Successfully set new management key.\n"); @@ -1832,7 +1791,7 @@ int main(int argc, char *argv[]) { fprintf(stderr, "Reset failed, are pincodes blocked?\n"); ret = EXIT_FAILURE; } else { - fprintf(stderr, "Successfully reset the applet.\n"); + fprintf(stderr, "Successfully reset the application.\n"); } break; case action_arg_pinMINUS_retries: @@ -1845,7 +1804,8 @@ int main(int argc, char *argv[]) { } break; case action_arg_importMINUS_key: - if(import_key(state, args_info.key_format_arg, args_info.input_arg, args_info.slot_orig, args_info.password_arg) == false) { + if(import_key(state, args_info.key_format_arg, args_info.input_arg, args_info.slot_orig, args_info.password_arg, + args_info.pin_policy_arg, args_info.touch_policy_arg) == false) { ret = EXIT_FAILURE; } else { fprintf(stderr, "Successfully imported a new private key.\n"); @@ -1932,6 +1892,11 @@ int main(int argc, char *argv[]) { ret = EXIT_FAILURE; } break; + case action_arg_listMINUS_readers: + if(list_readers(state) == false) { + ret = EXIT_FAILURE; + } + break; case action__NULL: default: fprintf(stderr, "Wrong action. %d.\n", action); diff --git a/tool/yubico-piv-tool.h2m b/tool/yubico-piv-tool.h2m index eda9546..8987cf1 100644 --- a/tool/yubico-piv-tool.h2m +++ b/tool/yubico-piv-tool.h2m @@ -30,7 +30,7 @@ For more information about what's happening \-\-verbose can be added to any command. For much more information \-\-verbose=2 may be used. -Display what version of the applet is running on the YubiKey Neo: +Display what version of the application is running on the YubiKey: yubico\-piv\-tool \-a version diff --git a/ykcs11/Makefile.am b/ykcs11/Makefile.am new file mode 100644 index 0000000..137df27 --- /dev/null +++ b/ykcs11/Makefile.am @@ -0,0 +1,77 @@ +# Copyright (c) 2015 Yubico AB +# All rights reserved. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Additional permission under GNU GPL version 3 section 7 +# +# If you modify this program, or any covered work, by linking or +# combining it with the OpenSSL project's OpenSSL library (or a +# modified version of that library), containing parts covered by the +# terms of the OpenSSL or SSLeay licenses, We grant you additional +# permission to convey the resulting work. Corresponding Source for a +# non-source form of such a combination shall include the source code +# for the parts of OpenSSL used as well as that of the covered work. + +SUBDIRS = . +#tests + +AM_CFLAGS = $(WERROR_CFLAGS) $(WARN_CFLAGS) +AM_CPPFLAGS = $(OPENSSL_CFLAGS) $(PCSC_CFLAGS) +AM_CPPFLAGS += -I$(top_srcdir)/lib -I$(top_builddir)/lib + +lib_LTLIBRARIES = libykcs11.la + +libykcs11_la_SOURCES = ykcs11.c version.c ykcs11.pc.in ykcs11.map +libykcs11_la_SOURCES += ykcs11.h debug.h +libykcs11_la_SOURCES += vendors.c vendors.h vendor_ids.h +libykcs11_la_SOURCES += slot_vendors.c slot_vendors.h +libykcs11_la_SOURCES += token_vendors.c token_vendors.h +libykcs11_la_SOURCES += mechanisms.c mechanisms.h +libykcs11_la_SOURCES += yubico_slot.c yubico_slot.h yubico_token.c yubico_token.h +libykcs11_la_SOURCES += utils.c utils.h +libykcs11_la_SOURCES += openssl_utils.c openssl_utils.h openssl_types.h +libykcs11_la_SOURCES += objects.c objects.h obj_types.h +libykcs11_la_SOURCES += pkcs11.h pkcs11f.h pkcs11t.h + +#internal.h +#libykcs11_la_SOURCES += error.c +#libykcs11_la_LDADD = ../lib/libykpiv.la +#$(OPENSSL_LIBS) +libykcs11_la_includedir = $(includedir)/ykcs11 +libykcs11_la_include_HEADERS = ykcs11-version.h +#ykcs11.h +EXTRA_libykcs11_la_DEPENDENCIES = ykcs11.map + +#libykcs11_la_LIBADD = $(OPENSSL_LIBS) $(PCSC_LIBS) +#libykcs11_la_LIBADD += $(PCSC_WIN_LIBS) $(PCSC_MACOSX_LIBS) +libykcs11_la_LIBADD = ../lib/libykpiv.la +libykcs11_la_LIBADD += ../tool/libpiv_util.la + +libykcs11_la_LDFLAGS = -no-undefined +libykcs11_la_LDFLAGS += -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) + +if HAVE_LD_VERSION_SCRIPT +libykcs11_la_LDFLAGS += -Wl,--version-script=$(srcdir)/ykcs11.map +else +libykcs11_la_LDFLAGS += -export-symbols-regex '^C_.*' +endif + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = ykcs11.pc + +if ENABLE_COV +AM_CFLAGS += --coverage +AM_LDFLAGS = --coverage +endif diff --git a/ykcs11/debug.h b/ykcs11/debug.h new file mode 100644 index 0000000..84ab9ce --- /dev/null +++ b/ykcs11/debug.h @@ -0,0 +1,28 @@ +#ifndef DEBUG_H +#define DEBUG_H + +#define YKCS11_DBG 1 // General debug, must be either 1 or 0 +#define YKCS11_DINOUT 1 // Function in/out debug, must be either 1 or 0 + +#define D(x...) do { \ + fprintf (stderr, "debug: %s:%d (%s): ", __FILE__, __LINE__, __FUNCTION__); \ + fprintf (stderr, x); \ + fprintf (stderr, "\n"); \ + } while (0) + +#if YKCS11_DBG +#include +#define DBG(x...) D(x); +#else +#define DBG(x...) +#endif + +#if YKCS11_DINOUT +#define DIN D(("In")); +#define DOUT D(("Out")); +#else +#define DIN +#define DOUT +#endif + +#endif diff --git a/ykcs11/mechanisms.c b/ykcs11/mechanisms.c new file mode 100644 index 0000000..98cdf02 --- /dev/null +++ b/ykcs11/mechanisms.c @@ -0,0 +1,527 @@ +#include "mechanisms.h" +#include "openssl_utils.h" +#include "utils.h" +#include "debug.h" +#include + +#define F4 "\x01\x00\x01" +#define PRIME256V1 "\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07" + +// Supported mechanisms for signature +static const CK_MECHANISM_TYPE sign_mechanisms[] = { + CKM_RSA_PKCS, + CKM_RSA_PKCS_PSS, + CKM_RSA_X_509, + CKM_SHA1_RSA_PKCS, + CKM_SHA256_RSA_PKCS, + CKM_SHA384_RSA_PKCS, + CKM_SHA512_RSA_PKCS, + CKM_SHA1_RSA_PKCS_PSS, + CKM_SHA256_RSA_PKCS_PSS, + CKM_SHA384_RSA_PKCS_PSS, + CKM_SHA512_RSA_PKCS_PSS, + CKM_ECDSA, + CKM_ECDSA_SHA1, + CKM_ECDSA_SHA256 +}; + +// Supported mechanisms for key pair generation +static const CK_MECHANISM_TYPE generation_mechanisms[] = { + CKM_RSA_PKCS_KEY_PAIR_GEN, + //CKM_ECDSA_KEY_PAIR_GEN, Deperecated + CKM_EC_KEY_PAIR_GEN +}; + +// Supported mechanisms for hashing +static const CK_MECHANISM_TYPE hash_mechanisms[] = { + CKM_SHA_1, + CKM_SHA256, + CKM_SHA384, + CKM_SHA512 +}; + +CK_RV check_sign_mechanism(const ykcs11_session_t *s, const CK_MECHANISM_PTR m) { + + CK_ULONG i; + CK_BBOOL supported = CK_FALSE; + token_vendor_t token; + CK_MECHANISM_INFO info; + + // Check if the mechanism is supported by the module + for (i = 0; i < sizeof(sign_mechanisms) / sizeof(CK_MECHANISM_TYPE); i++) { + if (m->mechanism == sign_mechanisms[i]) { + supported = CK_TRUE; + break; + } + } + if (supported == CK_FALSE) + return CKR_MECHANISM_INVALID; + + // Check if the mechanism is supported by the token + token = get_token_vendor(s->slot->token->vid); + + if (token.get_token_mechanism_info(m->mechanism, &info) != CKR_OK) + return CKR_MECHANISM_INVALID; + + // TODO: also check that parametes make sense if any? And key size is in [min max] + + return CKR_OK; + +} + +CK_BBOOL is_RSA_mechanism(CK_MECHANISM_TYPE m) { + + switch (m) { + case CKM_RSA_PKCS_KEY_PAIR_GEN: + case CKM_RSA_PKCS: + case CKM_RSA_9796: + case CKM_RSA_X_509: + case CKM_SHA1_RSA_PKCS: +// case CKM_SHA224_RSA_PKCS: + case CKM_SHA256_RSA_PKCS: + case CKM_SHA384_RSA_PKCS: + case CKM_SHA512_RSA_PKCS: +// case CKM_RIPEMD128_RSA_PKCS: +// case CKM_RIPEMD160_RSA_PKCS: +// case CKM_RSA_PKCS_OAEP: +// case CKM_RSA_X9_31_KEY_PAIR_GEN: +// case CKM_RSA_X9_31: +// case CKM_SHA1_RSA_X9_31: + case CKM_RSA_PKCS_PSS: + case CKM_SHA1_RSA_PKCS_PSS: +// case CKM_SHA224_RSA_PKCS_PSS: + case CKM_SHA256_RSA_PKCS_PSS: + case CKM_SHA512_RSA_PKCS_PSS: + case CKM_SHA384_RSA_PKCS_PSS: +// case CKM_RSA_PKCS_TPM_1_1: +// case CKM_RSA_PKCS_OAEP_TPM_1_1: +// case CKM_RSA_AES_KEY_WRAP: + return CK_TRUE; + + default: + return CK_FALSE; + } + + // Not reached + return CK_FALSE; +} + +CK_BBOOL is_PSS_mechanism(CK_MECHANISM_TYPE m) { + + switch (m) { + case CKM_RSA_PKCS_PSS: + case CKM_SHA1_RSA_PKCS_PSS: +// case CKM_SHA224_RSA_PKCS_PSS: + case CKM_SHA256_RSA_PKCS_PSS: + case CKM_SHA512_RSA_PKCS_PSS: + case CKM_SHA384_RSA_PKCS_PSS: + return CK_TRUE; + + default: + return CK_FALSE; + } + + // Not reached + return CK_FALSE; +} + +CK_BBOOL is_hashed_mechanism(CK_MECHANISM_TYPE m) { + + switch (m) { + case CKM_SHA1_RSA_PKCS: + case CKM_SHA256_RSA_PKCS: + case CKM_SHA384_RSA_PKCS: + case CKM_SHA512_RSA_PKCS: + case CKM_SHA1_RSA_PKCS_PSS: + case CKM_SHA256_RSA_PKCS_PSS: + case CKM_SHA384_RSA_PKCS_PSS: + case CKM_SHA512_RSA_PKCS_PSS: + case CKM_ECDSA_SHA1: + case CKM_ECDSA_SHA256: + case CKM_SHA_1: + case CKM_SHA256: + case CKM_SHA384: + case CKM_SHA512: + return CK_TRUE; + + default: + return CK_FALSE; + } + + // Not reached + return CK_FALSE; +} + +CK_RV apply_sign_mechanism_init(op_info_t *op_info) { + + if (op_info->type != YKCS11_SIGN) + return CKR_FUNCTION_FAILED; + + switch (op_info->mechanism.mechanism) { + case CKM_RSA_PKCS: + case CKM_RSA_PKCS_PSS: + case CKM_RSA_X_509: + case CKM_ECDSA: + // No hash required for this mechanism + op_info->op.sign.md_ctx = NULL; + return CKR_OK; + + case CKM_SHA1_RSA_PKCS: + case CKM_SHA1_RSA_PKCS_PSS: + case CKM_ECDSA_SHA1: + return do_md_init(YKCS11_SHA1, &op_info->op.sign.md_ctx); + + case CKM_SHA256_RSA_PKCS: + case CKM_SHA256_RSA_PKCS_PSS: + case CKM_ECDSA_SHA256: + return do_md_init(YKCS11_SHA256, &op_info->op.sign.md_ctx); + + case CKM_SHA384_RSA_PKCS: + case CKM_SHA384_RSA_PKCS_PSS: + return do_md_init(YKCS11_SHA384, &op_info->op.sign.md_ctx); + + case CKM_SHA512_RSA_PKCS: + case CKM_SHA512_RSA_PKCS_PSS: + return do_md_init(YKCS11_SHA512, &op_info->op.sign.md_ctx); + + default: + return CKR_FUNCTION_FAILED; + } + + // Never reached + return CKR_FUNCTION_FAILED; +} + +CK_RV apply_sign_mechanism_update(op_info_t *op_info, CK_BYTE_PTR in, CK_ULONG in_len) { + CK_RV rv; + + if (op_info->type != YKCS11_SIGN) + return CKR_FUNCTION_FAILED; + + switch (op_info->mechanism.mechanism) { + case CKM_RSA_PKCS: + case CKM_RSA_PKCS_PSS: + case CKM_ECDSA: + case CKM_RSA_X_509: + // Mechanism not suitable for multipart signatures + return CKR_FUNCTION_FAILED; + + case CKM_SHA1_RSA_PKCS: + case CKM_SHA256_RSA_PKCS: + case CKM_SHA384_RSA_PKCS: + case CKM_SHA512_RSA_PKCS: + case CKM_SHA1_RSA_PKCS_PSS: + case CKM_SHA256_RSA_PKCS_PSS: + case CKM_SHA384_RSA_PKCS_PSS: + case CKM_SHA512_RSA_PKCS_PSS: + case CKM_ECDSA_SHA1: + case CKM_ECDSA_SHA256: + rv = do_md_update(op_info->op.sign.md_ctx, in, in_len); + if (rv != CKR_OK) + return CKR_FUNCTION_FAILED; + + return CKR_OK; + + default: + return CKR_FUNCTION_FAILED; + } + +} + +CK_RV apply_sign_mechanism_finalize(op_info_t *op_info) { + + CK_RV rv; + int nid = NID_undef; + RSA *rsa; + CK_ULONG len; + + if (op_info->type != YKCS11_SIGN) + return CKR_FUNCTION_FAILED; + + switch (op_info->mechanism.mechanism) { + case CKM_SHA1_RSA_PKCS_PSS: + case CKM_SHA256_RSA_PKCS_PSS: + case CKM_SHA384_RSA_PKCS_PSS: + case CKM_SHA512_RSA_PKCS_PSS: + // Finalize the hash + rv = do_md_finalize(op_info->op.sign.md_ctx, op_info->buf, &op_info->buf_len, &nid); + op_info->op.sign.md_ctx = NULL; + if (rv != CKR_OK) + return CKR_FUNCTION_FAILED; + + case CKM_RSA_PKCS_PSS: + // Compute padding for all PSS variants + // TODO: digestinfo/paraminfo ? + + rv = do_encode_rsa_public_key(op_info->op.sign.key, op_info->op.sign.key_len, &rsa); + if (rv != CKR_OK) + return CKR_FUNCTION_FAILED; + + rv = do_pkcs_pss(rsa, op_info->buf, op_info->buf_len, nid, op_info->buf, &op_info->buf_len); + + // TODO: does rsa have to be free'd ? + + return rv; + + case CKM_RSA_X_509: + // Padding in this case consists of prepending zeroes + len = (op_info->op.sign.key_len / 8) - op_info->buf_len; + memmove(op_info->buf + len, op_info->buf, op_info->buf_len); + memset(op_info->buf, 0, len); + op_info->buf_len = op_info->op.sign.key_len / 8; + return CKR_OK; + + case CKM_SHA1_RSA_PKCS: + case CKM_SHA256_RSA_PKCS: + case CKM_SHA384_RSA_PKCS: + case CKM_SHA512_RSA_PKCS: + // Finalize the hash add digest info + rv = do_md_finalize(op_info->op.sign.md_ctx, op_info->buf, &op_info->buf_len, &nid); + op_info->op.sign.md_ctx = NULL; + if (rv != CKR_OK) + return CKR_FUNCTION_FAILED; + + case CKM_RSA_PKCS: + // Add digest info if needed + if (nid != NID_undef) { + rv = do_pkcs_1_digest_info(op_info->buf, op_info->buf_len, nid, op_info->buf, &op_info->buf_len); + if (rv != CKR_OK) + return CKR_FUNCTION_FAILED; + } + + // Compute padding for all PKCS1 variants + len = op_info->buf_len; + op_info->buf_len = sizeof(op_info->buf); + return do_pkcs_1_t1(op_info->buf, len, op_info->buf, &op_info->buf_len, op_info->op.sign.key_len); + + case CKM_ECDSA_SHA1: + case CKM_ECDSA_SHA256: + // Finalize the hash + rv = do_md_finalize(op_info->op.sign.md_ctx, op_info->buf, &op_info->buf_len, &nid); + op_info->op.sign.md_ctx = NULL; + if (rv != CKR_OK) + return CKR_FUNCTION_FAILED; + + case CKM_ECDSA: + return CKR_OK; + + default: + return CKR_FUNCTION_FAILED; + } +} + +CK_RV sign_mechanism_cleanup(op_info_t *op_info) { + + if (op_info->op.sign.md_ctx != NULL) { + do_md_cleanup(op_info->op.sign.md_ctx); + op_info->op.sign.md_ctx = NULL; + } + + return CKR_OK; +} + +CK_RV check_generation_mechanism(const ykcs11_session_t *s, CK_MECHANISM_PTR m) { + + CK_ULONG i; + CK_BBOOL supported = CK_FALSE; + token_vendor_t token; + CK_MECHANISM_INFO info; + + // Check if the mechanism is supported by the module + for (i = 0; i < sizeof(generation_mechanisms) / sizeof(CK_MECHANISM_TYPE); i++) { + if (m->mechanism == generation_mechanisms[i]) { + supported = CK_TRUE; + break; + } + } + if (supported == CK_FALSE) + return CKR_MECHANISM_INVALID; + + // Check if the mechanism is supported by the token + token = get_token_vendor(s->slot->token->vid); + + if (token.get_token_mechanism_info(m->mechanism, &info) != CKR_OK) + return CKR_MECHANISM_INVALID; + + // TODO: also check that parametes make sense if any? And key size is in [min max] + + return CKR_OK; + +} + +CK_RV check_pubkey_template(op_info_t *op_info, CK_ATTRIBUTE_PTR templ, CK_ULONG n) { + + CK_ULONG i; + + op_info->op.gen.rsa = is_RSA_mechanism(op_info->mechanism.mechanism); + + for (i = 0; i < n; i++) { + switch (templ[i].type) { + case CKA_CLASS: + if (*((CK_ULONG_PTR) templ[i].pValue) != CKO_PUBLIC_KEY) + return CKR_TEMPLATE_INCONSISTENT; + + break; + + case CKA_KEY_TYPE: + if ((op_info->op.gen.rsa == CK_TRUE && (*((CK_KEY_TYPE *)templ[i].pValue)) != CKK_RSA) || + (op_info->op.gen.rsa == CK_FALSE && (*((CK_KEY_TYPE *)templ[i].pValue)) != CKK_ECDSA)) + return CKR_TEMPLATE_INCONSISTENT; + + break; + + case CKA_PUBLIC_EXPONENT: + if (op_info->op.gen.rsa == CK_FALSE) + return CKR_ATTRIBUTE_VALUE_INVALID; + + // Only support F4 + if (templ[i].ulValueLen != 3 || memcmp((CK_BYTE_PTR)templ[i].pValue, F4, 3) != 0) { + DBG("Unsupported public exponent"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + + break; + + case CKA_MODULUS_BITS: + if (op_info->op.gen.rsa == CK_FALSE) + return CKR_ATTRIBUTE_VALUE_INVALID; + + if (*((CK_ULONG_PTR) templ[i].pValue) != 1024 && + *((CK_ULONG_PTR) templ[i].pValue) != 2048) { // TODO: make define? + DBG("Unsupported MODULUS_BITS (key length)"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + + op_info->op.gen.key_len = *((CK_ULONG_PTR) templ[i].pValue); + break; + + case CKA_EC_PARAMS: + // Only support PRIME256V1 + if (templ[i].ulValueLen != 10 || memcmp((CK_BYTE_PTR)templ[i].pValue, PRIME256V1, 10) != 0) + return CKR_CURVE_NOT_SUPPORTED; + + op_info->op.gen.key_len = 256; + break; + + case CKA_ID: + if (is_valid_key_id(*((CK_BYTE_PTR)templ[i].pValue)) == CK_FALSE) + return CKR_ATTRIBUTE_VALUE_INVALID; + + op_info->op.gen.key_id = PIV_PVTK_OBJ_PIV_AUTH + *((CK_BYTE_PTR)templ[i].pValue); + break; + + case CKA_TOKEN: + case CKA_ENCRYPT: + case CKA_VERIFY: + case CKA_WRAP: + case CKA_DERIVE: + // Ignore these attributes for now + break; + + default: + DBG("Invalid attribute %lx in public key template", templ[i].type); + return CKR_ATTRIBUTE_TYPE_INVALID; + } + } + + return CKR_OK; + +} + +CK_RV check_pvtkey_template(op_info_t *op_info, CK_ATTRIBUTE_PTR templ, CK_ULONG n) { + + CK_ULONG i; + + op_info->op.gen.rsa = is_RSA_mechanism(op_info->mechanism.mechanism); + op_info->op.gen.vendor_defined = 0; + + for (i = 0; i < n; i++) { + switch (templ[i].type) { + case CKA_CLASS: + if (*((CK_ULONG_PTR)templ[i].pValue) != CKO_PRIVATE_KEY) + return CKR_TEMPLATE_INCONSISTENT; + + break; + + case CKA_KEY_TYPE: + if ((op_info->op.gen.rsa == CK_TRUE && (*((CK_KEY_TYPE *)templ[i].pValue)) != CKK_RSA) || + (op_info->op.gen.rsa == CK_FALSE && (*((CK_KEY_TYPE *)templ[i].pValue)) != CKK_ECDSA)) + return CKR_TEMPLATE_INCONSISTENT; + + break; + +/* case CKA_MODULUS_BITS: + if (op_info->op.gen.rsa == CK_FALSE) + return CKR_MECHANISM_PARAM_INVALID; + + if (*((CK_ULONG_PTR)templ[i].pValue) != 1024 && + *((CK_ULONG_PTR) templ[i].pValue) != 2048) // TODO: make define? + return CKR_MECHANISM_PARAM_INVALID; + + op_info->op.gen.key_len = *((CK_ULONG_PTR) templ[i].pValue); // TODO: check length? + break;*/ + + case CKA_ID: + if (is_valid_key_id(*((CK_BYTE_PTR)templ[i].pValue)) == CK_FALSE) + return CKR_ATTRIBUTE_VALUE_INVALID; + + // Check if ID was already specified in the public key template + // In that case it has to match + if (op_info->op.gen.key_id != 0 && + op_info->op.gen.key_id != (*((CK_BYTE_PTR)templ[i].pValue) + PIV_PVTK_OBJ_PIV_AUTH)) + return CKR_TEMPLATE_INCONSISTENT; + + op_info->op.gen.key_id = PIV_PVTK_OBJ_PIV_AUTH + *((CK_BYTE_PTR)templ[i].pValue); + break; + + case CKA_VENDOR_DEFINED: + op_info->op.gen.vendor_defined = (*((CK_ULONG_PTR)templ[i].pValue)); + + case CKA_SENSITIVE: + case CKA_DECRYPT: + case CKA_UNWRAP: + case CKA_SIGN: + case CKA_PRIVATE: + case CKA_TOKEN: + case CKA_DERIVE: + // Ignore these attributes for now + break; + + default: + DBG("Invalid attribute %lx in private key template", templ[i].type); + return CKR_ATTRIBUTE_TYPE_INVALID; + } + } + + return CKR_OK; + +} + +CK_RV check_hash_mechanism(const ykcs11_session_t *s, CK_MECHANISM_PTR m) { + + CK_ULONG i; + CK_BBOOL supported = CK_FALSE; + token_vendor_t token; + CK_MECHANISM_INFO info; + + // Check if the mechanism is supported by the module + for (i = 0; i < sizeof(hash_mechanisms) / sizeof(CK_MECHANISM_TYPE); i++) { + if (m->mechanism == hash_mechanisms[i]) { + supported = CK_TRUE; + break; + } + } + if (supported == CK_FALSE) + return CKR_MECHANISM_INVALID; + + // Check if the mechanism is supported by the token + token = get_token_vendor(s->slot->token->vid); + + if (token.get_token_mechanism_info(m->mechanism, &info) != CKR_OK) + return CKR_MECHANISM_INVALID; + + // TODO: also check that parametes make sense if any? And key size is in [min max] + + return CKR_OK; + +} diff --git a/ykcs11/mechanisms.h b/ykcs11/mechanisms.h new file mode 100644 index 0000000..c919dde --- /dev/null +++ b/ykcs11/mechanisms.h @@ -0,0 +1,22 @@ +#ifndef MECHANISMS_H +#define MECHANISMS_H + +#include "ykcs11.h" + +CK_RV check_sign_mechanism(const ykcs11_session_t *s, CK_MECHANISM_PTR m); +CK_BBOOL is_RSA_mechanism(CK_MECHANISM_TYPE m); +CK_BBOOL is_PSS_mechanism(CK_MECHANISM_TYPE m); +CK_BBOOL is_hashed_mechanism(CK_MECHANISM_TYPE m); + +CK_RV apply_sign_mechanism_init(op_info_t *op_info); +CK_RV apply_sign_mechanism_update(op_info_t *op_info, CK_BYTE_PTR in, CK_ULONG in_len); +CK_RV apply_sign_mechanism_finalize(op_info_t *op_info); +CK_RV sign_mechanism_cleanup(op_info_t *op_info); + +CK_RV check_generation_mechanism(const ykcs11_session_t *s, CK_MECHANISM_PTR m); +CK_RV check_pubkey_template(op_info_t *op_info, CK_ATTRIBUTE_PTR templ, CK_ULONG n); // TODO: Move to objects.c +CK_RV check_pvtkey_template(op_info_t *op_info, CK_ATTRIBUTE_PTR templ, CK_ULONG n); // TODO: Move to objects.c + +CK_RV check_hash_mechanism(const ykcs11_session_t *s, CK_MECHANISM_PTR m); + +#endif diff --git a/ykcs11/obj_types.h b/ykcs11/obj_types.h new file mode 100644 index 0000000..ca4fc09 --- /dev/null +++ b/ykcs11/obj_types.h @@ -0,0 +1,136 @@ +#ifndef OBJ_TYPES_H +#define OBJ_TYPES_H + +#include "pkcs11t.h" + +#include + +// TODO: this is mostly from OpenSC, how to give credit? +typedef enum { + PIV_DATA_OBJ_X509_PIV_AUTH = 0, // PIV authentication + PIV_DATA_OBJ_X509_CARD_AUTH, // Certificate for card authentication + PIV_DATA_OBJ_X509_DS, // Certificate for digital signature + PIV_DATA_OBJ_X509_KM, // Certificate for key management + PIV_DATA_OBJ_CCC, // Card capability container + PIV_DATA_OBJ_CHUI, // Cardholder unique id + PIV_DATA_OBJ_CHF, // Cardholder fingerprints + PIV_DATA_OBJ_SEC_OBJ, // Security object + PIV_DATA_OBJ_CHFI, // Cardholder facial images + PIV_DATA_OBJ_PI, // Cardholder printed information + PIV_DATA_OBJ_DISCOVERY, // Discovery object + PIV_DATA_OBJ_HISTORY, // History object + PIV_DATA_OBJ_RETIRED_X509_1, // Retired certificate for KM 1 + PIV_DATA_OBJ_RETIRED_X509_2, // Retired certificate for KM 2 + PIV_DATA_OBJ_RETIRED_X509_3, // Retired certificate for KM 3 + PIV_DATA_OBJ_RETIRED_X509_4, // Retired certificate for KM 4 + PIV_DATA_OBJ_RETIRED_X509_5, // Retired certificate for KM 5 + PIV_DATA_OBJ_RETIRED_X509_6, // Retired certificate for KM 6 + PIV_DATA_OBJ_RETIRED_X509_7, // Retired certificate for KM 7 + PIV_DATA_OBJ_RETIRED_X509_8, // Retired certificate for KM 8 + PIV_DATA_OBJ_RETIRED_X509_9, // Retired certificate for KM 9 + PIV_DATA_OBJ_RETIRED_X509_10, // Retired certificate for KM 10 + PIV_DATA_OBJ_RETIRED_X509_11, // Retired certificate for KM 11 + PIV_DATA_OBJ_RETIRED_X509_12, // Retired certificate for KM 12 + PIV_DATA_OBJ_RETIRED_X509_13, // Retired certificate for KM 13 + PIV_DATA_OBJ_RETIRED_X509_14, // Retired certificate for KM 14 + PIV_DATA_OBJ_RETIRED_X509_15, // Retired certificate for KM 15 + PIV_DATA_OBJ_RETIRED_X509_16, // Retired certificate for KM 16 + PIV_DATA_OBJ_RETIRED_X509_17, // Retired certificate for KM 17 + PIV_DATA_OBJ_RETIRED_X509_18, // Retired certificate for KM 18 + PIV_DATA_OBJ_RETIRED_X509_19, // Retired certificate for KM 19 + PIV_DATA_OBJ_RETIRED_X509_20, // Retired certificate for KM 20 + PIV_DATA_OBJ_IRIS_IMAGE, // Cardholder iris images + PIV_DATA_OBJ_BITGT, // Biometric information templates group template + PIV_DATA_OBJ_SM_SIGNER, // Secure messaging signer + PIV_DATA_OBJ_PC_REF_DATA, // Pairing code reference data +/* PIV_DATA_OBJ_9B03, // NON-STANDARD TODO: remove? + PIV_DATA_OBJ_9A06, // NON-STANDARD + PIV_DATA_OBJ_9C06, // NON-STANDARD + PIV_DATA_OBJ_9D06, // NON-STANDARD + PIV_DATA_OBJ_9E06, // NON-STANDARD + PIV_DATA_OBJ_8206, // NON-STANDARD + PIV_DATA_OBJ_8306, // NON-STANDARD + PIV_DATA_OBJ_8406, // NON-STANDARD + PIV_DATA_OBJ_8506, // NON-STANDARD + PIV_DATA_OBJ_8606, // NON-STANDARD + PIV_DATA_OBJ_8706, // NON-STANDARD + PIV_DATA_OBJ_8806, // NON-STANDARD + PIV_DATA_OBJ_8906, // NON-STANDARD + PIV_DATA_OBJ_8A06, // NON-STANDARD + PIV_DATA_OBJ_8B06, // NON-STANDARD + PIV_DATA_OBJ_8C06, // NON-STANDARD + PIV_DATA_OBJ_8D06, // NON-STANDARD + PIV_DATA_OBJ_8E06, // NON-STANDARD + PIV_DATA_OBJ_8F06, // NON-STANDARD + PIV_DATA_OBJ_9006, // NON-STANDARD + PIV_DATA_OBJ_9106, // NON-STANDARD + PIV_DATA_OBJ_9206, // NON-STANDARD + PIV_DATA_OBJ_9306, // NON-STANDARD + PIV_DATA_OBJ_9406, // NON-STANDARD + PIV_DATA_OBJ_9506, // NON-STANDARD*/ + PIV_DATA_OBJ_LAST, + + PIV_CERT_OBJ_X509_PIV_AUTH, // PIV authentication + PIV_CERT_OBJ_X509_CARD_AUTH, // Certificate for card authentication + PIV_CERT_OBJ_X509_DS, // Certificate for digital signature + PIV_CERT_OBJ_X509_KM, // Certificate for key management + PIV_CERT_OBJ_LAST, + + PIV_PVTK_OBJ_PIV_AUTH, // Private key for PIV authentication + PIV_PVTK_OBJ_CARD_AUTH, // Private Key for card authentication + PIV_PVTK_OBJ_DS, // Private Key for digital signature + PIV_PVTK_OBJ_KM, // Private Key for key management + PIV_PVTK_OBJ_LAST, + + PIV_PUBK_OBJ_PIV_AUTH, // Public key for PIV authentication + PIV_PUBK_OBJ_CARD_AUTH, // Public Key for card authentication + PIV_PUBK_OBJ_DS, // Public Key for digital signature + PIV_PUBK_OBJ_KM, // Public Key for key management + PIV_PUBK_OBJ_LAST + +} piv_obj_id_t; + +#define OBJECT_INVALID (PIV_PUBK_OBJ_LAST + 1) + +typedef CK_RV (*get_attr_f)(CK_OBJECT_HANDLE, CK_ATTRIBUTE_PTR); + +typedef struct { + const char *oid; + CK_BYTE tag_len; + CK_BYTE tag_value[3]; // TODO: needed? + CK_BYTE containerid[2]; /* will use as relative paths for simulation */ // TODO: needed? +} piv_data_obj_t; + +typedef struct { + X509 *data; +} piv_cert_obj_t; + +typedef struct { // TODO: enough to use the public key for the parameters? + CK_BBOOL decrypt; + CK_BBOOL sign; + CK_BBOOL unwrap; + CK_BBOOL derive; + CK_BBOOL always_auth; +} piv_pvtk_obj_t; + +typedef struct { + EVP_PKEY *data; // TODO: make custom type for this and X509 + CK_BBOOL encrypt; + CK_BBOOL verify; + CK_BBOOL wrap; + CK_BBOOL derive; +} piv_pubk_obj_t; + +typedef struct { + piv_obj_id_t piv_id; // TODO: technically redundant + CK_BBOOL token; // TODO: not used yet + CK_BBOOL private; + CK_BBOOL modifiable; + const char *label; + CK_BBOOL copyable; // TODO: Optional, not used so far (default TRUE) + CK_BBOOL destroyable; // TODO: Optional, not used so far (default TRUE) + get_attr_f get_attribute; + CK_BYTE sub_id; // Sub-object id +} piv_obj_t; + +#endif diff --git a/ykcs11/objects.c b/ykcs11/objects.c new file mode 100644 index 0000000..b18fee4 --- /dev/null +++ b/ykcs11/objects.c @@ -0,0 +1,1292 @@ +#include "obj_types.h" +#include "objects.h" +#include +#include +#include +#include "openssl_utils.h" +#include "utils.h" +#include "debug.h" + +#define IS_CERT(x) (((x) >= PIV_CERT_OBJ_X509_PIV_AUTH && (x) < PIV_CERT_OBJ_LAST) ? CK_TRUE : CK_FALSE) + +#define F4 "\x01\x00\x01" // TODO: already define in mechanisms.c. Move +#define PRIME256V1 "\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07" // TODO: already define in mechanisms.c. Move + +CK_RV get_doa(CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_PTR template); // TODO: static? +CK_RV get_coa(CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_PTR template); +CK_RV get_proa(CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_PTR template); +CK_RV get_puoa(CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_PTR template); + +//TODO: this is mostly a snippet from OpenSC how to give credit? Less and less so now +/* Must be in order, and one per enumerated PIV_OBJ */ +static piv_obj_t piv_objects[] = { + {PIV_DATA_OBJ_X509_PIV_AUTH, 1, 0, 0, "X.509 Certificate for PIV Authentication", 0, 0, get_doa, 0}, + {PIV_DATA_OBJ_X509_CARD_AUTH, 1, 0, 0, "X.509 Certificate for Card Authentication", 0, 0, get_doa, 1}, + {PIV_DATA_OBJ_X509_DS, 1, 0, 0, "X.509 Certificate for Digital Signature", 0, 0, get_doa, 2}, + {PIV_DATA_OBJ_X509_KM, 1, 0, 0, "X.509 Certificate for Key Management", 0, 0, get_doa, 3}, + {PIV_DATA_OBJ_CCC, 1, 0, 0, "Card Capability Container", 0, 0, get_doa, 4}, + {PIV_DATA_OBJ_CHUI, 1, 0, 0, "Card Holder Unique Identifier", 0, 0, get_doa, 5}, + {PIV_DATA_OBJ_CHF, 1, 1, 0, "Card Holder Fingerprints", 0, 0, get_doa, 6}, + {PIV_DATA_OBJ_SEC_OBJ, 1, 0, 0, "Security Object", 0, 0, get_doa, 7}, + {PIV_DATA_OBJ_CHFI, 1, 1, 0, "Cardholder Facial Images", 0, 0, get_doa, 8}, + {PIV_DATA_OBJ_PI, 1, 1, 0, "Printed Information", 0, 0, get_doa, 9}, + {PIV_DATA_OBJ_DISCOVERY, 1, 0, 0, "Discovery Object", 0, 0, get_doa, 10}, + {PIV_DATA_OBJ_HISTORY, 1, 0, 0, "Key History Object", 0, 0, get_doa, 11}, + {PIV_DATA_OBJ_RETIRED_X509_1, 1, 0, 0, "Retired X.509 Certificate for Key Management 1", 0, 0, get_doa, 12}, + {PIV_DATA_OBJ_RETIRED_X509_2, 1, 0, 0, "Retired X.509 Certificate for Key Management 2", 0, 0, get_doa, 13}, + {PIV_DATA_OBJ_RETIRED_X509_3, 1, 0, 0, "Retired X.509 Certificate for Key Management 3", 0, 0, get_doa, 14}, + {PIV_DATA_OBJ_RETIRED_X509_4, 1, 0, 0, "Retired X.509 Certificate for Key Management 4", 0, 0, get_doa, 15}, + {PIV_DATA_OBJ_RETIRED_X509_5, 1, 0, 0, "Retired X.509 Certificate for Key Management 5", 0, 0, get_doa, 16}, + {PIV_DATA_OBJ_RETIRED_X509_6, 1, 0, 0, "Retired X.509 Certificate for Key Management 6", 0, 0, get_doa, 17}, + {PIV_DATA_OBJ_RETIRED_X509_7, 1, 0, 0, "Retired X.509 Certificate for Key Management 7", 0, 0, get_doa, 18}, + {PIV_DATA_OBJ_RETIRED_X509_8, 1, 0, 0, "Retired X.509 Certificate for Key Management 8", 0, 0, get_doa, 19}, + {PIV_DATA_OBJ_RETIRED_X509_9, 1, 0, 0, "Retired X.509 Certificate for Key Management 9", 0, 0, get_doa, 20}, + {PIV_DATA_OBJ_RETIRED_X509_10, 1, 0, 0, "Retired X.509 Certificate for Key Management 10", 0, 0, get_doa, 21}, + {PIV_DATA_OBJ_RETIRED_X509_11, 1, 0, 0, "Retired X.509 Certificate for Key Management 11", 0, 0, get_doa, 22}, + {PIV_DATA_OBJ_RETIRED_X509_12, 1, 0, 0, "Retired X.509 Certificate for Key Management 12", 0, 0, get_doa, 23}, + {PIV_DATA_OBJ_RETIRED_X509_13, 1, 0, 0, "Retired X.509 Certificate for Key Management 13", 0, 0, get_doa, 24}, + {PIV_DATA_OBJ_RETIRED_X509_14, 1, 0, 0, "Retired X.509 Certificate for Key Management 14", 0, 0, get_doa, 25}, + {PIV_DATA_OBJ_RETIRED_X509_15, 1, 0, 0, "Retired X.509 Certificate for Key Management 15", 0, 0, get_doa, 26}, + {PIV_DATA_OBJ_RETIRED_X509_16, 1, 0, 0, "Retired X.509 Certificate for Key Management 16", 0, 0, get_doa, 27}, + {PIV_DATA_OBJ_RETIRED_X509_17, 1, 0, 0, "Retired X.509 Certificate for Key Management 17", 0, 0, get_doa, 28}, + {PIV_DATA_OBJ_RETIRED_X509_18, 1, 0, 0, "Retired X.509 Certificate for Key Management 18", 0, 0, get_doa, 29}, + {PIV_DATA_OBJ_RETIRED_X509_19, 1, 0, 0, "Retired X.509 Certificate for Key Management 19", 0, 0, get_doa, 30}, + {PIV_DATA_OBJ_RETIRED_X509_20, 1, 0, 0, "Retired X.509 Certificate for Key Management 20", 0, 0, get_doa, 31}, + {PIV_DATA_OBJ_IRIS_IMAGE, 1, 1, 0, "Cardholder Iris Images", 0, 0, get_doa, 32}, + {PIV_DATA_OBJ_BITGT, 1, 0, 0, "Biometric Information Templates Group Template", 0, 0, get_doa, 33}, + {PIV_DATA_OBJ_SM_SIGNER, 1, 0, 0, "Secure Messaging Certificate Signer", 0, 0, get_doa, 34}, + {PIV_DATA_OBJ_PC_REF_DATA, 1, 1, 0, "Pairing Code Reference Data Container", 0, 0, get_doa, 35}, + {PIV_DATA_OBJ_LAST, 1, 0, 0, "", 0, 0, NULL, 36}, + + {PIV_CERT_OBJ_X509_PIV_AUTH, 1, 0, 0, "X.509 Certificate for PIV Authentication", 0, 0, get_coa, 0}, + {PIV_CERT_OBJ_X509_CARD_AUTH, 1, 0, 0, "X.509 Certificate for Card Authentication", 0, 0, get_coa, 1}, + {PIV_CERT_OBJ_X509_DS, 1, 0, 0, "X.509 Certificate for Digital Signature", 0, 0, get_coa, 2}, + {PIV_CERT_OBJ_X509_KM, 1, 0, 0, "X.509 Certificate for Key Management", 0, 0, get_coa, 3}, + {PIV_CERT_OBJ_LAST, 1, 0, 0, "", 0, 0, NULL, 4}, + + {PIV_PVTK_OBJ_PIV_AUTH, 1, 1, 0, "Private key for PIV Authentication", 0, 0, get_proa, 0}, // 9a + {PIV_PVTK_OBJ_CARD_AUTH, 1, 0, 0, "Private key for Card Authentication", 0, 0, get_proa, 1}, // 9e + {PIV_PVTK_OBJ_DS, 1, 1, 0, "Private key for Digital Signature", 0, 0, get_proa, 2}, // 9c + {PIV_PVTK_OBJ_KM, 1, 1, 0, "Private key for Key Management", 0, 0, get_proa, 3}, // 9d + {PIV_PVTK_OBJ_LAST, 1, 0, 0, "", 0, 0, NULL, 4}, + + {PIV_PUBK_OBJ_PIV_AUTH, 1, 0, 0, "Public key for PIV Authentication", 0, 0, get_puoa, 0}, + {PIV_PUBK_OBJ_CARD_AUTH, 1, 0, 0, "Public key for Card Authentication", 0, 0, get_puoa, 1}, + {PIV_PUBK_OBJ_DS, 1, 0, 0, "Public key for Digital Signature", 0, 0, get_puoa, 2}, + {PIV_PUBK_OBJ_KM, 1, 0, 0, "Public key for Key Management", 0, 0, get_puoa, 3}, + {PIV_PUBK_OBJ_LAST, 1, 0, 0, "", 0, 0, NULL, 4} +}; + +static piv_data_obj_t data_objects[] = { + {"2.16.840.1.101.3.7.2.1.1", 3, "\x5F\xC1\x05", "\x01\x01"}, + {"2.16.840.1.101.3.7.2.5.0", 3, "\x5F\xC1\x01", "\x05\x00"}, + {"2.16.840.1.101.3.7.2.1.0", 3, "\x5F\xC1\x0A", "\x01\x00"}, + {"2.16.840.1.101.3.7.2.1.2", 3, "\x5F\xC1\x0B", "\x01\x02"}, + {"2.16.840.1.101.3.7.1.219.0", 3, "\x5F\xC1\x07", "\xDB\x00"}, + {"2.16.840.1.101.3.7.2.48.0", 3, "\x5F\xC1\x02", "\x30\x00"}, + {"2.16.840.1.101.3.7.2.96.16", 3, "\x5F\xC1\x03", "\x60\x10"}, + {"2.16.840.1.101.3.7.2.144.0", 3, "\x5F\xC1\x06", "\x90\x00"}, + {"2.16.840.1.101.3.7.2.96.48", 3, "\x5F\xC1\x08", "\x60\x30"}, + {"2.16.840.1.101.3.7.2.48.1", 3, "\x5F\xC1\x09", "\x30\x01"}, + {"2.16.840.1.101.3.7.2.96.80", 1, "\x7E", "\x60\x50"}, + {"2.16.840.1.101.3.7.2.96.96", 3, "\x5F\xC1\x0C", "\x60\x60"}, + +/* 800-73-3, 21 new objects, 20 history certificates */ + {"2.16.840.1.101.3.7.2.16.1", 3, "\x5F\xC1\x0D", "\x10\x01"}, + {"2.16.840.1.101.3.7.2.16.2", 3, "\x5F\xC1\x0E", "\x10\x02"}, + {"2.16.840.1.101.3.7.2.16.3", 3, "\x5F\xC1\x0F", "\x10\x03"}, + {"2.16.840.1.101.3.7.2.16.4", 3, "\x5F\xC1\x10", "\x10\x04"}, + {"2.16.840.1.101.3.7.2.16.5", 3, "\x5F\xC1\x11", "\x10\x05"}, + {"2.16.840.1.101.3.7.2.16.7", 3, "\x5F\xC1\x13", "\x10\x07"}, + {"2.16.840.1.101.3.7.2.16.8", 3, "\x5F\xC1\x14", "\x10\x08"}, + {"2.16.840.1.101.3.7.2.16.9", 3, "\x5F\xC1\x15", "\x10\x09"}, + {"2.16.840.1.101.3.7.2.16.10", 3, "\x5F\xC1\x16", "\x10\x0A"}, + {"2.16.840.1.101.3.7.2.16.11", 3, "\x5F\xC1\x17", "\x10\x0B"}, + {"2.16.840.1.101.3.7.2.16.12", 3, "\x5F\xC1\x18", "\x10\x0C"}, + {"2.16.840.1.101.3.7.2.16.13", 3, "\x5F\xC1\x19", "\x10\x0D"}, + {"2.16.840.1.101.3.7.2.16.14", 3, "\x5F\xC1\x1A", "\x10\x0E"}, + {"2.16.840.1.101.3.7.2.16.15", 3, "\x5F\xC1\x1B", "\x10\x0F"}, + {"2.16.840.1.101.3.7.2.16.16", 3, "\x5F\xC1\x1C", "\x10\x10"}, + {"2.16.840.1.101.3.7.2.16.17", 3, "\x5F\xC1\x1D", "\x10\x11"}, + {"2.16.840.1.101.3.7.2.16.18", 3, "\x5F\xC1\x1E", "\x10\x12"}, + {"2.16.840.1.101.3.7.2.16.19", 3, "\x5F\xC1\x1F", "\x10\x13"}, + {"2.16.840.1.101.3.7.2.16.20", 3, "\x5F\xC1\x20", "\x10\x14"}, + {"2.16.840.1.101.3.7.2.16.21", 3, "\x5F\xC1\x21", "\x10\x15"}, + {"2.16.840.1.101.3.7.2.16.22", 2, "\x7F\x61", "\x10\x16"}, + {"2.16.840.1.101.3.7.2.16.23", 3, "\x5F\xC1\x22", "\x10\x17"}, + {"2.16.840.1.101.3.7.2.16.24", 3, "\x5F\xC1\x23", "\x10\x18"}, + {"", 0, "", ""} +}; + +static piv_cert_obj_t cert_objects[] = { + {NULL}, + {NULL}, + {NULL}, + {NULL}, + {NULL} +}; + +static piv_pvtk_obj_t pvtkey_objects[] = { + {1, 1, 0, 0, 0}, + {1, 1, 0, 0, 0}, + {1, 1, 0, 0, 0}, + {1, 1, 0, 0, 1}, + {1, 1, 0, 0, 0} +}; + +static piv_pubk_obj_t pubkey_objects[] = { + {NULL, 1, 1, 0, 0}, + {NULL, 1, 1, 0, 0}, + {NULL, 1, 1, 0, 0}, + {NULL, 1, 1, 0, 0}, + {NULL, 1, 1, 0, 0} +}; + +// Next two functions based off the code at +// https://github.com/m9aertner/oidConverter/blob/master/oid.c +// TODO: how to give credit? OR JUST STORE THE OID ALREADY ENCODED? +static void make_base128(unsigned long l, int first, CK_BYTE_PTR buf, CK_ULONG_PTR n) { + if (l > 127) + make_base128(l / 128, 0, buf, n); + + l %= 128; + + if (first) + buf[(*n)++] = (CK_BYTE)l; + else + buf[(*n)++] = 0x80 | (CK_BYTE)l; +} + +static void asn1_encode_oid(CK_CHAR_PTR oid, CK_BYTE_PTR asn1_oid, CK_ULONG_PTR len) { + CK_CHAR_PTR tmp = (CK_BYTE_PTR) strdup((char *)oid); + CK_CHAR_PTR p = tmp; + CK_BYTE_PTR q = NULL; + CK_ULONG n = 0; + CK_BYTE b = 0; + CK_ULONG l = 0; + CK_ULONG nodes; + + q = p; + *len = 0; + nodes = 1; + while (*p != 0) { + if (*p == '.') + nodes++; + p++; + } + + n = 0; + b = 0; + p = q; + while (n < nodes) { + q = p; + while (*p != 0) { + if (*p == '.') + break; + p++; + } + + if (*p == '.') { + *p = 0; + l = (CK_ULONG) atoi((char *)q); + q = p + 1; + p = q; + } + else { + l = (CK_ULONG) atoi((char *)q); + // q = p; + } + + /* Digit is in l. */ + if (n == 0) + b = 40 * ((CK_BYTE)l); + else if (n == 1) { + b += (CK_BYTE) l; + asn1_oid[(*len)++] = b; + } + else { + make_base128(l, 1, asn1_oid, len); + } + n++; + } + + free(tmp); +} + +static CK_KEY_TYPE get_key_type(EVP_PKEY *key) { + return do_get_key_type(key); +} + +static CK_ULONG get_modulus_bits(EVP_PKEY *key) { + return do_get_rsa_modulus_length(key); +} + +static CK_ULONG get_public_exponent(EVP_PKEY *key) { + return do_get_public_exponent(key); +} + +static CK_RV get_public_key(EVP_PKEY *key, CK_BYTE_PTR data, CK_ULONG_PTR len) { + return do_get_public_key(key, data, len); +} + +static CK_RV get_curve_parameters(EVP_PKEY *key, CK_BYTE_PTR data, CK_ULONG_PTR len) { + return do_get_curve_parameters(key, data, len); +} + +static CK_RV get_raw_cert(X509 *cert, CK_BYTE_PTR data, CK_ULONG_PTR len) { + return do_get_raw_cert(cert, data, len); +} + +/* Get data object attribute */ +CK_RV get_doa(CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_PTR template) { + CK_BYTE_PTR data; + CK_BYTE tmp[64]; + CK_ULONG len = 0; + DBG("For data object %lu, get ", obj); + + switch (template->type) { + case CKA_CLASS: + DBG("CLASS"); + len = 1; + tmp[0] = CKO_DATA; + data = tmp; + break; + + case CKA_TOKEN: + // Technically all these objects are token objects + DBG("TOKEN"); + len = 1; + tmp[0] = piv_objects[obj].token; + data = tmp; + break; + + case CKA_PRIVATE: + DBG("PRIVATE"); + len = 1; + tmp[0] = piv_objects[obj].private; + data = tmp; + break; + + case CKA_LABEL: + DBG("LABEL"); + len = strlen(piv_objects[obj].label) + 1; + data = (CK_BYTE_PTR) piv_objects[obj].label; + break; + + case CKA_APPLICATION: + DBG("APPLICATION"); + len = strlen(piv_objects[obj].label) + 1; + data = (CK_BYTE_PTR) piv_objects[obj].label; + break; + + case CKA_VALUE: // TODO: this can be done with -r and -d|-a + DBG("VALUE TODO!!!"); + return CKR_FUNCTION_FAILED; + + case CKA_OBJECT_ID: // TODO: how about just storing the OID in DER ? + DBG("OID"); + strcpy((char *)tmp, data_objects[piv_objects[obj].sub_id].oid); + asn1_encode_oid(tmp, tmp, &len); + data = tmp; + break; + + case CKA_MODIFIABLE: + DBG("MODIFIABLE"); + len = 1; + tmp[0] = piv_objects[obj].modifiable; + data = tmp; + break; + + default: + DBG("UNKNOWN ATTRIBUTE %lx", template[0].type); + template->ulValueLen = CK_UNAVAILABLE_INFORMATION; + return CKR_ATTRIBUTE_TYPE_INVALID; + } + + /* Just get the length */ + if (template->pValue == NULL_PTR) { + template->ulValueLen = len; + return CKR_OK; + } + + /* Actually get the attribute */ + if (template->ulValueLen < len) + return CKR_BUFFER_TOO_SMALL; + + template->ulValueLen = len; + memcpy(template->pValue, data, len); + + return CKR_OK; + +} + +/* Get certificate object attribute */ +CK_RV get_coa(CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_PTR template) { + CK_BYTE_PTR data; + CK_BYTE b_tmp[1024]; + CK_ULONG ul_tmp; + CK_ULONG len = 0; + DBG("For certificate object %lu, get ", obj); + + switch (template->type) { + case CKA_CLASS: + DBG("CLASS"); + len = sizeof(CK_ULONG); + ul_tmp = CKO_CERTIFICATE; + data = (CK_BYTE_PTR) &ul_tmp; + break; + + case CKA_TOKEN: + // Technically all these objects are token objects + DBG("TOKEN"); + len = sizeof(CK_BBOOL); + b_tmp[0] = piv_objects[obj].token; + data = b_tmp; + break; + + case CKA_PRIVATE: + DBG("PRIVATE"); + len = sizeof(CK_BBOOL); + b_tmp[0] = piv_objects[obj].private; + data = b_tmp; + break; + + case CKA_LABEL: + DBG("LABEL"); + len = strlen(piv_objects[obj].label) + 1; + data = (CK_BYTE_PTR) piv_objects[obj].label; + break; + + case CKA_VALUE: + DBG("VALUE"); + len = sizeof(b_tmp); + if (get_raw_cert(cert_objects[piv_objects[obj].sub_id].data, b_tmp, &len) != CKR_OK) + return CKR_FUNCTION_FAILED; + data = b_tmp; + break; + + case CKA_CERTIFICATE_TYPE: + DBG("CERTIFICATE TYPE"); + len = sizeof(CK_ULONG); + ul_tmp = CKC_X_509; // Support only X.509 certs + data = (CK_BYTE_PTR) &ul_tmp; + break; + + case CKA_ISSUER: + DBG("ISSUER TODO"); // Default empty + return CKR_FUNCTION_FAILED; + + case CKA_SERIAL_NUMBER: + DBG("SERIAL NUMBER TODO"); // Default empty + return CKR_FUNCTION_FAILED; + + case CKA_SUBJECT: + DBG("SUBJECT TODO"); // Required + return CKR_FUNCTION_FAILED; + + case CKA_ID: + DBG("ID"); + len = sizeof(CK_BYTE); + b_tmp[0] = piv_objects[obj].sub_id; + data = b_tmp; + break; + + case CKA_START_DATE: + DBG("START DATE TODO"); // Default empty + return CKR_FUNCTION_FAILED; + + case CKA_END_DATE: + DBG("END DATE TODO"); // Default empty + return CKR_FUNCTION_FAILED; + + case CKA_MODIFIABLE: + DBG("MODIFIABLE"); + len = sizeof(CK_BBOOL); + b_tmp[0] = piv_objects[obj].modifiable; + data = b_tmp; + break; + + default: // TODO: there are other attributes for a (x509) certificate + DBG("UNKNOWN ATTRIBUTE %lx", template[0].type); + template->ulValueLen = CK_UNAVAILABLE_INFORMATION; + return CKR_ATTRIBUTE_TYPE_INVALID; + } + + /* Just get the length */ + if (template->pValue == NULL_PTR) { + template->ulValueLen = len; + return CKR_OK; + } + + /* Actually get the attribute */ + if (template->ulValueLen < len) + return CKR_BUFFER_TOO_SMALL; + + template->ulValueLen = len; + memcpy(template->pValue, data, len); + + return CKR_OK; + +} + +/* Get private key object attribute */ +CK_RV get_proa(CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_PTR template) { + CK_BYTE_PTR data; + CK_BYTE b_tmp[1024]; + CK_ULONG ul_tmp; // TODO: fix elsewhere too + CK_ULONG len = 0; + DBG("For private key object %lu, get ", obj); + + switch (template->type) { + case CKA_CLASS: + DBG("CLASS"); + len = sizeof(CK_ULONG); + ul_tmp = CKO_PRIVATE_KEY; + data = (CK_BYTE_PTR) &ul_tmp; + break; + + case CKA_TOKEN: + // Technically all these objects are token objects + DBG("TOKEN"); + len = sizeof(CK_BBOOL); + b_tmp[0] = piv_objects[obj].token; + data = b_tmp; + break; + + case CKA_PRIVATE: + DBG("PRIVATE"); + len = sizeof(CK_BBOOL); + b_tmp[0] = piv_objects[obj].private; + data = b_tmp; + break; + + case CKA_LABEL: + DBG("LABEL"); + len = strlen(piv_objects[obj].label) + 1; + data = (CK_BYTE_PTR) piv_objects[obj].label; + break; + + case CKA_KEY_TYPE: + DBG("KEY TYPE"); + len = sizeof(CK_ULONG); + ul_tmp = get_key_type(pubkey_objects[piv_objects[obj].sub_id].data); // Getting the info from the pubk + if (ul_tmp == CKK_VENDOR_DEFINED) + return CKR_FUNCTION_FAILED; + data = (CK_BYTE_PTR) &ul_tmp; + break; + + case CKA_SUBJECT: + DBG("SUBJECT TODO"); // Default empty + return CKR_FUNCTION_FAILED; + + case CKA_ID: + DBG("ID"); + len = sizeof(CK_BYTE); + ul_tmp = piv_objects[obj].sub_id; + data = (CK_BYTE_PTR) &ul_tmp; + break; + + case CKA_SENSITIVE: + DBG("SENSITIVE TODO"); // Default empty + return CKR_FUNCTION_FAILED; + + case CKA_DECRYPT: + DBG("DECRYPT"); // Default empy + len = sizeof(CK_BBOOL); + b_tmp[0] = pvtkey_objects[piv_objects[obj].sub_id].decrypt; + data = b_tmp; + break; + + case CKA_UNWRAP: + DBG("UNWRAP"); // Default empty + len = sizeof(CK_BBOOL); + b_tmp[0] = pvtkey_objects[piv_objects[obj].sub_id].unwrap; + data = b_tmp; + break; + + case CKA_SIGN: + DBG("SIGN"); // Default empty + len = sizeof(CK_BBOOL); + b_tmp[0] = pvtkey_objects[piv_objects[obj].sub_id].sign; + data = b_tmp; + break; + + case CKA_SIGN_RECOVER: + DBG("SIGN RECOVER TODO"); // Default empty + return CKR_FUNCTION_FAILED; + + case CKA_DERIVE: + DBG("DERIVE"); // Default false + len = sizeof(CK_BBOOL); + b_tmp[0] = pvtkey_objects[piv_objects[obj].sub_id].derive; + data = b_tmp; + break; + + case CKA_START_DATE: + DBG("START DATE TODO"); // Default empty + return CKR_FUNCTION_FAILED; + + case CKA_END_DATE: + DBG("END DATE TODO"); // Default empty + return CKR_FUNCTION_FAILED; + + case CKA_MODULUS: + DBG("MODULUS"); + len = sizeof(b_tmp); + + // Make sure that this is an RSA key + ul_tmp = get_key_type(pubkey_objects[piv_objects[obj].sub_id].data); // Getting the info from the pubk + if (ul_tmp == CKK_VENDOR_DEFINED) + return CKR_FUNCTION_FAILED; + if (ul_tmp != CKK_RSA) + return CKR_ATTRIBUTE_VALUE_INVALID; + + if (get_public_key(pubkey_objects[piv_objects[obj].sub_id].data, b_tmp, &len) != CKR_OK) + return CKR_FUNCTION_FAILED; + data = b_tmp; + break; + + case CKA_EC_POINT: + DBG("EC_POINT"); + len = sizeof(b_tmp); + + // Make sure that this is an EC key + ul_tmp = get_key_type(pubkey_objects[piv_objects[obj].sub_id].data); // Getting the info from the pubk + if (ul_tmp == CKK_VENDOR_DEFINED) + return CKR_FUNCTION_FAILED; + if (ul_tmp != CKK_ECDSA) + return CKR_ATTRIBUTE_VALUE_INVALID; + + if (get_public_key(pubkey_objects[piv_objects[obj].sub_id].data, b_tmp, &len) != CKR_OK) + return CKR_FUNCTION_FAILED; + data = b_tmp; + break; + + case CKA_EC_PARAMS: + // Here we want the curve parameters (DER encoded OID) + DBG("EC_PARAMS"); + len = sizeof(b_tmp); + + // Make sure that this is an EC key + ul_tmp = get_key_type(pubkey_objects[piv_objects[obj].sub_id].data); // Getting the info from the pubk + if (ul_tmp == CKK_VENDOR_DEFINED) + return CKR_FUNCTION_FAILED; + if (ul_tmp != CKK_ECDSA) + return CKR_ATTRIBUTE_VALUE_INVALID; + + if (get_curve_parameters(pubkey_objects[piv_objects[obj].sub_id].data, b_tmp, &len) != CKR_OK) + return CKR_FUNCTION_FAILED; + + data = b_tmp; + break; + + case CKA_MODULUS_BITS: + DBG("MODULUS BITS"); + len = sizeof(CK_ULONG); + + // Make sure that this is an RSA key + ul_tmp = get_key_type(pubkey_objects[piv_objects[obj].sub_id].data); // Getting the info from the pubk + if (ul_tmp == CKK_VENDOR_DEFINED) + return CKR_FUNCTION_FAILED; + if (ul_tmp != CKK_RSA) + return CKR_ATTRIBUTE_VALUE_INVALID; + + ul_tmp = get_modulus_bits(pubkey_objects[piv_objects[obj].sub_id].data); // Getting the info from the pubk + if (ul_tmp == 0) + return CKR_FUNCTION_FAILED; + data = (CK_BYTE_PTR) &ul_tmp; + break; + + case CKA_PUBLIC_EXPONENT: + DBG("PUBLIC EXPONENT"); + len = sizeof(CK_ULONG); + + // Make sure that this is an RSA key + ul_tmp = get_key_type(pubkey_objects[piv_objects[obj].sub_id].data); // Getting the info from the pubk + if (ul_tmp == CKK_VENDOR_DEFINED) + return CKR_FUNCTION_FAILED; + if (ul_tmp != CKK_RSA) + return CKR_ATTRIBUTE_VALUE_INVALID; + + ul_tmp = get_public_exponent(pubkey_objects[piv_objects[obj].sub_id].data); // Getting the info from the pubk + if (ul_tmp == 0) + return CKR_FUNCTION_FAILED; + data = (CK_BYTE_PTR) &ul_tmp; + break; + + /* case CKA_PRIVATE_EXPONENT: */ + /* case CKA_PRIME_1: */ + /* case CKA_PRIME_2: */ + /* case CKA_EXPONENT_1: */ + /* case CKA_EXPONENT_2: */ + /* case CKA_COEFFICIENT: */ + /* case CKA_PRIME: */ + /* case CKA_SUBPRIME: */ + /* case CKA_BASE: */ + /* case CKA_VALUE_BITS: */ + /* case CKA_VALUE_LEN: */ + /* case CKA_EXTRACTABLE: */ + case CKA_LOCAL: + DBG("LOCAL TODO"); // Required + return CKR_FUNCTION_FAILED; + + /* case CKA_NEVER_EXTRACTABLE: */ + /*case CKA_ALWAYS_SENSITIVE:*/ + + case CKA_ALWAYS_AUTHENTICATE: + DBG("ALWAYS AUTHENTICATE"); + len = sizeof(CK_BBOOL); + b_tmp[0] = pvtkey_objects[piv_objects[obj].sub_id].always_auth; + data = b_tmp; + break; + + case CKA_MODIFIABLE: + DBG("MODIFIABLE"); + len = sizeof(CK_BBOOL); + b_tmp[0] = piv_objects[obj].modifiable; + data = b_tmp; + break; + + /*case CKA_VENDOR_DEFINED:*/ + default: + DBG("UNKNOWN ATTRIBUTE %lx", template[0].type); // TODO: there are other parameters for public keys, plus there is more if the key is RSA + template->ulValueLen = CK_UNAVAILABLE_INFORMATION; + return CKR_ATTRIBUTE_TYPE_INVALID; + } + + /* Just get the length */ + if (template->pValue == NULL_PTR) { + template->ulValueLen = len; + return CKR_OK; + } + + /* Actually get the attribute */ + if (template->ulValueLen < len) + return CKR_BUFFER_TOO_SMALL; + + template->ulValueLen = len; + memcpy(template->pValue, data, len); + + return CKR_OK; + +} + +/* Get public key object attribute */ +CK_RV get_puoa(CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_PTR template) { + CK_BYTE_PTR data; + CK_BYTE b_tmp[1024]; + CK_ULONG ul_tmp; + CK_ULONG len = 0; + DBG("For public key object %lu, get ", obj); + + switch (template->type) { + case CKA_CLASS: + DBG("CLASS"); + len = sizeof(CK_ULONG); + ul_tmp = CKO_PUBLIC_KEY; + data = (CK_BYTE_PTR) &ul_tmp; + break; + + case CKA_TOKEN: + // Technically all these objects are token objects + DBG("TOKEN"); + len = sizeof(CK_BBOOL); + b_tmp[0] = piv_objects[obj].token; + data = b_tmp; + break; + + case CKA_PRIVATE: + DBG("PRIVATE"); + len = sizeof(CK_BBOOL); + b_tmp[0] = piv_objects[obj].private; + data = b_tmp; + break; + + case CKA_LABEL: + DBG("LABEL"); + len = strlen(piv_objects[obj].label) + 1; + data = (CK_BYTE_PTR) piv_objects[obj].label; + break; + + case CKA_KEY_TYPE: + DBG("KEY TYPE"); + len = sizeof(CK_ULONG); + ul_tmp = get_key_type(pubkey_objects[piv_objects[obj].sub_id].data); + if (ul_tmp == CKK_VENDOR_DEFINED) // This value is used as an error here + return CKR_FUNCTION_FAILED; + data = (CK_BYTE_PTR) &ul_tmp; + break; + + case CKA_SUBJECT: + DBG("SUBJECT TODO"); // Default empty + return CKR_FUNCTION_FAILED; + + case CKA_ID: + DBG("ID"); + len = sizeof(CK_BYTE); + b_tmp[0] = piv_objects[obj].sub_id; + data = b_tmp; + break; + + case CKA_ENCRYPT: + DBG("ENCRYPT"); + len = sizeof(CK_BBOOL); + b_tmp[0] = pubkey_objects[piv_objects[obj].sub_id].encrypt; + data = b_tmp; + break; + + case CKA_VERIFY: // TODO: what about verify recover ? + DBG("VERIFY"); + len = sizeof(CK_BBOOL); + b_tmp[0] = pubkey_objects[piv_objects[obj].sub_id].verify; + data = b_tmp; + break; + + case CKA_WRAP: + DBG("WRAP"); + len = sizeof(CK_BBOOL); + b_tmp[0] = pubkey_objects[piv_objects[obj].sub_id].wrap; + data = b_tmp; + break; + + case CKA_DERIVE: + DBG("DERIVE"); + len = sizeof(CK_BBOOL); + b_tmp[0] = pubkey_objects[piv_objects[obj].sub_id].derive; + data = b_tmp; + break; + + case CKA_START_DATE: + DBG("START DATE TODO"); // Default empty + return CKR_FUNCTION_FAILED; + + case CKA_END_DATE: + DBG("END DATE TODO"); // Default empty + return CKR_FUNCTION_FAILED; + + case CKA_EC_POINT: + DBG("EC_POINT"); + len = sizeof(b_tmp); + + // Make sure that this is an EC key + ul_tmp = get_key_type(pubkey_objects[piv_objects[obj].sub_id].data); // Getting the info from the pubk + if (ul_tmp == CKK_VENDOR_DEFINED) + return CKR_FUNCTION_FAILED; + if (ul_tmp != CKK_ECDSA) + return CKR_ATTRIBUTE_VALUE_INVALID; + + if (get_public_key(pubkey_objects[piv_objects[obj].sub_id].data, b_tmp, &len) != CKR_OK) + return CKR_FUNCTION_FAILED; + data = b_tmp; + break; + + case CKA_EC_PARAMS: + // Here we want the curve parameters (DER encoded OID) + DBG("EC_PARAMS"); + len = sizeof(b_tmp); + + // Make sure that this is an EC key + ul_tmp = get_key_type(pubkey_objects[piv_objects[obj].sub_id].data); // Getting the info from the pubk + if (ul_tmp == CKK_VENDOR_DEFINED) + return CKR_FUNCTION_FAILED; + if (ul_tmp != CKK_ECDSA) + return CKR_ATTRIBUTE_VALUE_INVALID; + + if (get_curve_parameters(pubkey_objects[piv_objects[obj].sub_id].data, b_tmp, &len) != CKR_OK) + return CKR_FUNCTION_FAILED; + + data = b_tmp; + break; + + case CKA_MODULUS: + DBG("MODULUS"); + len = sizeof(b_tmp); + + // Make sure that this is an RSA key + ul_tmp = get_key_type(pubkey_objects[piv_objects[obj].sub_id].data); // Getting the info from the pubk + if (ul_tmp == CKK_VENDOR_DEFINED) + return CKR_FUNCTION_FAILED; + if (ul_tmp != CKK_RSA) + return CKR_ATTRIBUTE_VALUE_INVALID; + + if (get_public_key(pubkey_objects[piv_objects[obj].sub_id].data, b_tmp, &len) != CKR_OK) + return CKR_FUNCTION_FAILED; + data = b_tmp; + break; + + case CKA_MODULUS_BITS: + DBG("MODULUS BITS"); + len = sizeof(CK_ULONG); + + // Make sure that this is an RSA key + ul_tmp = get_key_type(pubkey_objects[piv_objects[obj].sub_id].data); // Getting the info from the pubk + if (ul_tmp == CKK_VENDOR_DEFINED) + return CKR_FUNCTION_FAILED; + if (ul_tmp != CKK_RSA) + return CKR_ATTRIBUTE_VALUE_INVALID; + + ul_tmp = get_modulus_bits(pubkey_objects[piv_objects[obj].sub_id].data); // Getting the info from the pubk + if (ul_tmp == 0) + return CKR_FUNCTION_FAILED; + data = (CK_BYTE_PTR) &ul_tmp; + break; + + case CKA_PUBLIC_EXPONENT: + DBG("PUBLIC EXPONENT"); + len = sizeof(CK_ULONG); + + // Make sure that this is an RSA key + ul_tmp = get_key_type(pubkey_objects[piv_objects[obj].sub_id].data); // Getting the info from the pubk + if (ul_tmp == CKK_VENDOR_DEFINED) + return CKR_FUNCTION_FAILED; + if (ul_tmp != CKK_RSA) + return CKR_ATTRIBUTE_VALUE_INVALID; + + ul_tmp = get_public_exponent(pubkey_objects[piv_objects[obj].sub_id].data); // Getting the info from the pubk + if (ul_tmp == 0) + return CKR_FUNCTION_FAILED; + data = (CK_BYTE_PTR) &ul_tmp; + break; + + case CKA_LOCAL: + DBG("LOCAL TODO"); // Required + return CKR_FUNCTION_FAILED; + + case CKA_MODIFIABLE: + DBG("MODIFIABLE"); + len = sizeof(CK_BBOOL); + b_tmp[0] = piv_objects[obj].modifiable; + data = b_tmp; + break; + + default: + DBG("UNKNOWN ATTRIBUTE %lx", template[0].type); // TODO: there are other parameters for public keys + template->ulValueLen = CK_UNAVAILABLE_INFORMATION; + return CKR_ATTRIBUTE_TYPE_INVALID; + } + + /* Just get the length */ + if (template->pValue == NULL_PTR) { + template->ulValueLen = len; + return CKR_OK; + } + + /* Actually get the attribute */ + if (template->ulValueLen < len) + return CKR_BUFFER_TOO_SMALL; + + template->ulValueLen = len; + memcpy(template->pValue, data, len); + + return CKR_OK; + +} + +CK_ULONG piv_2_ykpiv(piv_obj_id_t id) { + // TODO: add retired keys + switch(id) { + case PIV_CERT_OBJ_X509_PIV_AUTH: + return YKPIV_OBJ_AUTHENTICATION; + + case PIV_CERT_OBJ_X509_CARD_AUTH: + return YKPIV_OBJ_CARD_AUTH; + + case PIV_CERT_OBJ_X509_DS: + return YKPIV_OBJ_SIGNATURE; + + case PIV_CERT_OBJ_X509_KM: + return YKPIV_OBJ_KEY_MANAGEMENT; + + case PIV_PVTK_OBJ_PIV_AUTH: + return YKPIV_KEY_AUTHENTICATION; + + case PIV_PVTK_OBJ_CARD_AUTH: + return YKPIV_KEY_CARDAUTH; + + case PIV_PVTK_OBJ_DS: + return YKPIV_KEY_SIGNATURE; + + case PIV_PVTK_OBJ_KM: + return YKPIV_KEY_KEYMGM; + + default: + return 0ul; + } +} + +CK_RV get_attribute(ykcs11_session_t *s, CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_PTR template) { + CK_ULONG i; + + for (i = 0; i < s->slot->token->n_objects; i++) + if (s->slot->token->objects[i] == obj) { + return piv_objects[obj].get_attribute(obj, template); + } + + return CKR_OBJECT_HANDLE_INVALID; +} + +CK_BBOOL attribute_match(ykcs11_session_t *s, CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_PTR attribute) { + + CK_ATTRIBUTE to_match; + CK_BYTE_PTR data; + + // Get the size first + to_match.type = attribute->type; + to_match.pValue = NULL; + to_match.ulValueLen = 0; + + if (get_attribute(s, obj, &to_match) != CKR_OK) + return CK_FALSE; + + if (to_match.ulValueLen != attribute->ulValueLen) + return CK_FALSE; + + // Allocate space for the attribute + data = malloc(to_match.ulValueLen); + if (data == NULL) + return CK_FALSE; + + // Retrieve the attribute + to_match.pValue = data; + if (get_attribute(s, obj, &to_match) != CKR_OK) { + free(data); + data = NULL; + return CK_FALSE; + } + + // Compare the attributes + if (memcmp(attribute->pValue, to_match.pValue, to_match.ulValueLen) != 0) { + free(data); + data = NULL; + return CK_FALSE; + } + + free(data); + data = NULL; + + return CK_TRUE; +} + +CK_BBOOL is_private_object(ykcs11_session_t *s, CK_OBJECT_HANDLE obj) { + + CK_ATTRIBUTE attr; + CK_BYTE private; + + attr.type = CKA_PRIVATE; + attr.pValue = &private; + attr.ulValueLen = sizeof(private); + + if (get_attribute(s, obj, &attr) != CKR_OK) + return CK_FALSE; + + return private == CK_FALSE ? CK_FALSE : CK_TRUE; +} + +CK_RV get_available_certificate_ids(ykcs11_session_t *s, piv_obj_id_t *cert_ids, CK_ULONG n_certs) { + CK_ULONG i, j; + + if (n_certs > s->slot->token->n_objects) + return CKR_BUFFER_TOO_SMALL; + + j = 0; + for (i = 0; i < s->slot->token->n_objects; i++) + if (IS_CERT(s->slot->token->objects[i]) == CK_TRUE) + cert_ids[j++] = s->slot->token->objects[i]; + + return CKR_OK; +} + +CK_RV store_cert(piv_obj_id_t cert_id, CK_BYTE_PTR data, CK_ULONG len) { + + CK_RV rv; + + // Store the certificate as an object + rv = do_store_cert(data, len, &cert_objects[piv_objects[cert_id].sub_id].data); + if (rv != CKR_OK) + return rv; + + // Extract and store the public key as an object + rv = do_store_pubk(cert_objects[piv_objects[cert_id].sub_id].data, &pubkey_objects[piv_objects[cert_id].sub_id].data); + if (rv != CKR_OK) + return rv; + + return CKR_OK; +} + +CK_RV delete_cert(piv_obj_id_t cert_id) { + CK_RV rv; + + // Clear the object containing the certificate + rv = do_delete_cert(&cert_objects[piv_objects[cert_id].sub_id].data); + if (rv != CKR_OK) + return rv; + + // Clear the object containing the public key + rv = do_delete_pubk(&pubkey_objects[piv_objects[cert_id].sub_id].data); + if (rv != CKR_OK) + return rv; + + return CKR_OK; +} + +CK_RV check_create_cert(CK_ATTRIBUTE_PTR templ, CK_ULONG n, + CK_BYTE_PTR id, CK_BYTE_PTR *value, CK_ULONG_PTR cert_len) { + + CK_ULONG i; + CK_BBOOL has_id = CK_FALSE; + CK_BBOOL has_value = CK_FALSE; + + for (i = 0; i < n; i++) { + switch (templ[i].type) { + case CKA_CLASS: + // Technically redundant check + if (*((CK_ULONG_PTR)templ[i].pValue) != CKO_CERTIFICATE) + return CKR_ATTRIBUTE_VALUE_INVALID; + + break; + + case CKA_ID: + has_id = CK_TRUE; + if (is_valid_key_id(*((CK_BYTE_PTR)templ[i].pValue)) == CK_FALSE) + return CKR_ATTRIBUTE_VALUE_INVALID; + + *id = *((CK_BYTE_PTR)templ[i].pValue); + break; + + case CKA_VALUE: + has_value = CK_TRUE; + *value = (CK_BYTE_PTR)templ[i].pValue; + + *cert_len = templ[i].ulValueLen; + break; + + case CKA_TOKEN: + case CKA_LABEL: + case CKA_SUBJECT: + // Ignore other attributes + break; + + default: + DBG("Invalid %lx", templ[i].type); + return CKR_ATTRIBUTE_TYPE_INVALID; + } + } + + if (has_id == CK_FALSE || + has_value == CK_FALSE) + return CKR_TEMPLATE_INCOMPLETE; + + return CKR_OK; +} + +CK_RV check_create_ec_key(CK_ATTRIBUTE_PTR templ, CK_ULONG n, CK_BYTE_PTR id, + CK_BYTE_PTR *value, CK_ULONG_PTR value_len, CK_ULONG_PTR vendor_defined) { + + CK_ULONG i; + CK_BBOOL has_id = CK_FALSE; + CK_BBOOL has_value = CK_FALSE; + CK_BBOOL has_params = CK_FALSE; + + CK_BYTE_PTR ec_params = NULL; + CK_ULONG ec_params_len = 0; + + *vendor_defined = 0; + + for (i = 0; i < n; i++) { + switch (templ[i].type) { + case CKA_CLASS: + if (*((CK_ULONG_PTR)templ[i].pValue) != CKO_PRIVATE_KEY) + return CKR_ATTRIBUTE_VALUE_INVALID; + + break; + + case CKA_KEY_TYPE: + if (*((CK_ULONG_PTR)templ[i].pValue) != CKK_ECDSA) + return CKR_ATTRIBUTE_VALUE_INVALID; + + break; + + case CKA_ID: + has_id = CK_TRUE; + if (is_valid_key_id(*((CK_BYTE_PTR)templ[i].pValue)) == CK_FALSE) + return CKR_ATTRIBUTE_VALUE_INVALID; + + *id = *((CK_BYTE_PTR)templ[i].pValue); + break; + + case CKA_VALUE: + has_value = CK_TRUE; + *value = (CK_BYTE_PTR)templ[i].pValue; + *value_len = templ[i].ulValueLen; + break; + + case CKA_EC_PARAMS: + has_params = CK_TRUE; + ec_params = (CK_BYTE_PTR)templ[i].pValue; + ec_params_len = templ[i].ulValueLen; + + break; + + case CKA_VENDOR_DEFINED: + *vendor_defined = *((CK_ULONG_PTR)templ[i].pValue); + break; + + case CKA_TOKEN: + case CKA_LABEL: + case CKA_SUBJECT: + case CKA_SENSITIVE: + case CKA_DERIVE: + // Ignore other attributes + break; + + default: + DBG("Invalid %lx", templ[i].type); + return CKR_ATTRIBUTE_TYPE_INVALID; + } + } + + if (has_id == CK_FALSE || + has_value == CK_FALSE || + has_params == CK_FALSE) + return CKR_TEMPLATE_INCOMPLETE; + + if (*value_len != 32) + return CKR_ATTRIBUTE_VALUE_INVALID; + + if (*value_len == 32 && (ec_params_len != 10 || memcmp(ec_params, PRIME256V1, ec_params_len)) != 0) + return CKR_TEMPLATE_INCONSISTENT; + + return CKR_OK; +} + +CK_RV check_create_rsa_key(CK_ATTRIBUTE_PTR templ, CK_ULONG n, CK_BYTE_PTR id, + CK_BYTE_PTR *p, CK_BYTE_PTR *q, CK_BYTE_PTR *dp, + CK_BYTE_PTR *dq, CK_BYTE_PTR *qinv, CK_ULONG_PTR value_len, CK_ULONG_PTR vendor_defined) { + + CK_ULONG i; + CK_BBOOL has_id = CK_FALSE; + CK_BBOOL has_e = CK_FALSE; + CK_BBOOL has_p = CK_FALSE; + CK_BBOOL has_q = CK_FALSE; + CK_BBOOL has_dp = CK_FALSE; + CK_BBOOL has_dq = CK_FALSE; + CK_BBOOL has_qinv = CK_FALSE; + CK_ULONG p_len = 0; + CK_ULONG q_len = 0; + CK_ULONG dp_len = 0; + CK_ULONG dq_len = 0; + CK_ULONG qinv_len = 0; + + *vendor_defined = 0; + + for (i = 0; i < n; i++) { + switch (templ[i].type) { + case CKA_CLASS: + if (*((CK_ULONG_PTR)templ[i].pValue) != CKO_PRIVATE_KEY) + return CKR_ATTRIBUTE_VALUE_INVALID; + + break; + + case CKA_ID: + has_id = CK_TRUE; + if (is_valid_key_id(*((CK_BYTE_PTR)templ[i].pValue)) == CK_FALSE) + return CKR_ATTRIBUTE_VALUE_INVALID; + + *id = *((CK_BYTE_PTR)templ[i].pValue); + break; + + case CKA_KEY_TYPE: + if (*((CK_ULONG_PTR)templ[i].pValue) != CKK_RSA) + return CKR_ATTRIBUTE_VALUE_INVALID; + + break; + + case CKA_PUBLIC_EXPONENT: + has_e = CK_TRUE; + if (templ[i].ulValueLen != 3 || memcmp((CK_BYTE_PTR)templ[i].pValue, F4, 3) != 0) + return CKR_ATTRIBUTE_VALUE_INVALID; + break; + + case CKA_PRIME_1: + has_p = CK_TRUE; + *p = (CK_BYTE_PTR)templ[i].pValue; + p_len = templ[i].ulValueLen; + + break; + + case CKA_PRIME_2: + has_q = CK_TRUE; + *q = (CK_BYTE_PTR)templ[i].pValue; + q_len = templ[i].ulValueLen; + + break; + + case CKA_EXPONENT_1: + has_dp = CK_TRUE; + *dp = (CK_BYTE_PTR)templ[i].pValue; + dp_len = templ[i].ulValueLen; + + break; + + case CKA_EXPONENT_2: + has_dq = CK_TRUE; + *dq = (CK_BYTE_PTR)templ[i].pValue; + dq_len = templ[i].ulValueLen; + + break; + + case CKA_COEFFICIENT: + has_qinv = CK_TRUE; + *qinv = (CK_BYTE_PTR)templ[i].pValue; + qinv_len = templ[i].ulValueLen; + + break; + + case CKA_VENDOR_DEFINED: + *vendor_defined = *((CK_ULONG_PTR)templ[i].pValue); + break; + + case CKA_TOKEN: + case CKA_LABEL: + case CKA_SUBJECT: + case CKA_SENSITIVE: + case CKA_DERIVE: + // Ignore other attributes + break; + + default: + DBG("Invalid %lx", templ[i].type); + return CKR_ATTRIBUTE_TYPE_INVALID; + } + } + + if (has_id == CK_FALSE || + has_e == CK_FALSE || + has_p == CK_FALSE || + has_q == CK_FALSE || + has_dp == CK_FALSE || + has_dq == CK_FALSE || + has_qinv == CK_FALSE) + return CKR_TEMPLATE_INCOMPLETE; + + if (p_len != 64 && p_len != 128) + return CKR_ATTRIBUTE_VALUE_INVALID; + + *value_len = p_len; + + if (q_len != p_len || dp_len != p_len || + dq_len != p_len || qinv_len != p_len) + return CKR_ATTRIBUTE_VALUE_INVALID; + + return CKR_OK; +} + +CK_RV check_delete_cert(CK_OBJECT_HANDLE hObject, CK_BYTE_PTR id) { + + if (hObject < PIV_CERT_OBJ_X509_PIV_AUTH || hObject >= PIV_CERT_OBJ_LAST) + return CKR_ACTION_PROHIBITED; + + *id = hObject - PIV_CERT_OBJ_X509_PIV_AUTH; + + return CKR_OK; +} diff --git a/ykcs11/objects.h b/ykcs11/objects.h new file mode 100644 index 0000000..be9db19 --- /dev/null +++ b/ykcs11/objects.h @@ -0,0 +1,25 @@ +#ifndef OBJECTS_H +#define OBJECTS_H + +#include "ykcs11.h" + +CK_ULONG piv_2_ykpiv(piv_obj_id_t id); + +CK_RV get_attribute(ykcs11_session_t *s, CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_PTR template); +CK_BBOOL attribute_match(ykcs11_session_t *s, CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_PTR attribute); +CK_BBOOL is_private_object(ykcs11_session_t *s, CK_OBJECT_HANDLE obj); + +CK_RV get_available_certificate_ids(ykcs11_session_t *s, piv_obj_id_t *cert_ids, CK_ULONG n_certs); +CK_RV store_cert(piv_obj_id_t cert_id, CK_BYTE_PTR data, CK_ULONG len); +CK_RV delete_cert(piv_obj_id_t cert_id); + +CK_RV check_create_cert(CK_ATTRIBUTE_PTR templ, CK_ULONG n, CK_BYTE_PTR id, + CK_BYTE_PTR *value, CK_ULONG_PTR cert_len); +CK_RV check_create_ec_key(CK_ATTRIBUTE_PTR templ, CK_ULONG n, CK_BYTE_PTR id, + CK_BYTE_PTR *value, CK_ULONG_PTR value_len, CK_ULONG_PTR vendor_defined); +CK_RV check_create_rsa_key(CK_ATTRIBUTE_PTR templ, CK_ULONG n, CK_BYTE_PTR id, + CK_BYTE_PTR *p, CK_BYTE_PTR *q, CK_BYTE_PTR *dp, + CK_BYTE_PTR *dq, CK_BYTE_PTR *qinv, CK_ULONG_PTR value_len, CK_ULONG_PTR vendor_defined); +CK_RV check_delete_cert(CK_OBJECT_HANDLE hObject, CK_BYTE_PTR id); + +#endif diff --git a/ykcs11/openssl_types.h b/ykcs11/openssl_types.h new file mode 100644 index 0000000..eef3396 --- /dev/null +++ b/ykcs11/openssl_types.h @@ -0,0 +1,23 @@ +#ifndef OPENSSL_TYPES_H +#define OPENSSL_TYPES_H + +#include +#include +#include +#include + +typedef enum { + YKCS11_NO_HASH, + YKCS11_SHA1, + //YKCS11_SHA224, + YKCS11_SHA256, + YKCS11_SHA384, + YKCS11_SHA512, + //YKCS11_RIPEMD128_RSA_PKCS, + //YKCS11_RIPEMD160 +} hash_t; + +typedef EVP_MD_CTX ykcs11_md_ctx_t; +//typedef EVP_PKEY ykcs11_evp_pkey_t; + +#endif diff --git a/ykcs11/openssl_utils.c b/ykcs11/openssl_utils.c new file mode 100644 index 0000000..353c29f --- /dev/null +++ b/ykcs11/openssl_utils.c @@ -0,0 +1,613 @@ +#include "openssl_utils.h" +#include +#include "../tool/util.h" // TODO: share this better? +#include "debug.h" +#include + +CK_RV do_store_cert(CK_BYTE_PTR data, CK_ULONG len, X509 **cert) { + + const unsigned char *p = data; // Mandatory temp variable required by OpenSSL + int cert_len; + + if (*p == 0x70) { + // The certificate is in "PIV" format 0x70 len 0x30 len ... + p++; + p += get_length(p, &cert_len); + } + else { + // Raw certificate 0x30 len ... + cert_len = 0; + cert_len += get_length(p + 1, &cert_len) + 1; + } + + if ((CK_ULONG)cert_len > len) + return CKR_ARGUMENTS_BAD; + + *cert = d2i_X509(NULL, &p, cert_len); + if (*cert == NULL) + return CKR_FUNCTION_FAILED; + + return CKR_OK; + +} + +CK_RV do_create_empty_cert(CK_BYTE_PTR in, CK_ULONG in_len, CK_BBOOL is_rsa, + CK_BYTE_PTR out, CK_ULONG_PTR out_len) { + + X509 *cert = NULL; + EVP_PKEY *key = NULL; + RSA *rsa = NULL; + BIGNUM *bignum_n = NULL; + BIGNUM *bignum_e = NULL; + EC_KEY *eck = NULL; + EC_GROUP *ecg = NULL; + EC_POINT *ecp = NULL; + ASN1_TIME *tm = NULL; + + unsigned char *data_ptr; + unsigned char *p; + int len; + + CK_RV rv = CKR_FUNCTION_FAILED; + + cert = X509_new(); + if (cert == NULL) + goto create_empty_cert_cleanup; + + key = EVP_PKEY_new(); + if (key == NULL) + goto create_empty_cert_cleanup; + + if (is_rsa) { + // RSA + rsa = RSA_new(); + if (rsa == NULL) + goto create_empty_cert_cleanup; + + data_ptr = in + 5; + if (*data_ptr != 0x81) + goto create_empty_cert_cleanup; + + data_ptr++; + data_ptr += get_length(data_ptr, &len); + bignum_n = BN_bin2bn(data_ptr, len, NULL); + if(bignum_n == NULL) + goto create_empty_cert_cleanup; + + data_ptr += len; + + if(*data_ptr != 0x82) + goto create_empty_cert_cleanup; + + data_ptr++; + data_ptr += get_length(data_ptr, &len); + bignum_e = BN_bin2bn(data_ptr, len, NULL); + if(bignum_e == NULL) + goto create_empty_cert_cleanup; + + rsa->n = bignum_n; + rsa->e = bignum_e; + + if (EVP_PKEY_set1_RSA(key, rsa) == 0) + goto create_empty_cert_cleanup; + } + else { + // ECCP256 + data_ptr = in + 3; + + eck = EC_KEY_new(); + if (eck == NULL) + goto create_empty_cert_cleanup; + + ecg = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); + if (ecg == NULL) + goto create_empty_cert_cleanup; + + EC_GROUP_set_asn1_flag(ecg, NID_X9_62_prime256v1); + EC_KEY_set_group(eck, ecg); + ecp = EC_POINT_new(ecg); + + if(*data_ptr++ != 0x86) + goto create_empty_cert_cleanup; + + // The curve point should always be 65 bytes + if (*data_ptr++ != 65) + goto create_empty_cert_cleanup; + + if (EC_POINT_oct2point(ecg, ecp, data_ptr, 65, NULL) == 0) + goto create_empty_cert_cleanup; + + if (EC_KEY_set_public_key(eck, ecp) == 0) + goto create_empty_cert_cleanup; + + if (EVP_PKEY_set1_EC_KEY(key, eck) == 0) + goto create_empty_cert_cleanup; + } + + if (X509_set_pubkey(cert, key) == 0) // TODO: there is also X509_PUBKEY_set(X509_PUBKEY **x, EVP_PKEY *pkey); + goto create_empty_cert_cleanup; + + tm = ASN1_TIME_new(); + if (tm == NULL) + goto create_empty_cert_cleanup; + + ASN1_TIME_set_string(tm, "000001010000Z"); + X509_set_notBefore(cert, tm); + X509_set_notAfter(cert, tm); + + // Manually set the signature algorithms. + // OpenSSL 1.0.1i complains about empty DER fields + // 8 => md5WithRsaEncryption + cert->sig_alg->algorithm = OBJ_nid2obj(8); + cert->cert_info->signature->algorithm = OBJ_nid2obj(8); + + // Manually set a signature (same reason as before) + ASN1_BIT_STRING_set_bit(cert->signature, 8, 1); + ASN1_BIT_STRING_set(cert->signature, "\x00", 1); + + len = i2d_X509(cert, NULL); + if (len < 0) + goto create_empty_cert_cleanup; + + if ((CK_ULONG)len > *out_len) { + rv = CKR_BUFFER_TOO_SMALL; + goto create_empty_cert_cleanup; + } + + p = out; + if ((*out_len = (CK_ULONG) i2d_X509(cert, &p)) == 0) + goto create_empty_cert_cleanup; + + /********************/ + /*BIO *STDout = BIO_new_fp(stderr, BIO_NOCLOSE); + + X509_print_ex(STDout, cert, 0, 0); + + BIO_free(STDout);*/ + /********************/ + + rv = CKR_OK; + +create_empty_cert_cleanup: + + if (tm != NULL) { + ASN1_STRING_free(tm); + tm = NULL; + } + + if (bignum_n != NULL) { + BN_free(bignum_n); + bignum_n = NULL; + } + + if (bignum_e != NULL) { + BN_free(bignum_e); + bignum_e = NULL; + } + +/* if (rsa != NULL) { // TODO: adding this generates an error. Automatically free'd by EVP_PKEY_free ? + RSA_free(rsa); + rsa = NULL; + }*/ + + if (ecp != NULL) { + EC_POINT_free(ecp); + ecp = NULL; + } + + if (ecg != NULL) { + EC_GROUP_free(ecg); + ecg = NULL; + } + + if (eck != NULL) { + EC_KEY_free(eck); + eck = NULL; + } + + if (key != NULL) { + EVP_PKEY_free(key); + key = NULL; + } + + if (cert != NULL) { + X509_free(cert); + cert = NULL; + } + + return rv; +} + +CK_RV do_check_cert(CK_BYTE_PTR in, CK_ULONG_PTR cert_len) { + + X509 *cert; + const unsigned char *p = in; // Mandatory temp variable required by OpenSSL + int len; + + len = 0; + len += get_length(p + 1, &len) + 1; + + *cert_len = (CK_ULONG) len; + + cert = d2i_X509(NULL, &p, (long) *cert_len); + if (cert == NULL) + return CKR_FUNCTION_FAILED; + + return CKR_OK; +} + +CK_RV do_get_raw_cert(X509 *cert, CK_BYTE_PTR out, CK_ULONG_PTR out_len) { + + CK_BYTE_PTR p; + int len; + + len = i2d_X509(cert, NULL); + + if (len < 0) + return CKR_FUNCTION_FAILED; + + if ((CK_ULONG)len > *out_len) + return CKR_BUFFER_TOO_SMALL; + + p = out; + if ((*out_len = (CK_ULONG) i2d_X509(cert, &p)) == 0) + return CKR_FUNCTION_FAILED; + + return CKR_OK; +} + +CK_RV do_delete_cert(X509 **cert) { + + X509_free(*cert); + cert = NULL; + + return CKR_OK; + +} + +/*CK_RV free_cert(X509 *cert) { + + X509_free((X509 *) cert); + + return CKR_OK; +}*/ + + +CK_RV do_store_pubk(X509 *cert, EVP_PKEY **key) { + + *key = X509_get_pubkey(cert); + + if (*key == NULL) + return CKR_FUNCTION_FAILED; + + return CKR_OK; + +} + +CK_KEY_TYPE do_get_key_type(EVP_PKEY *key) { + + switch (key->type) { + case EVP_PKEY_RSA: + case EVP_PKEY_RSA2: + return CKK_RSA; + + case EVP_PKEY_EC: + return CKK_ECDSA; + + default: + return CKK_VENDOR_DEFINED; // Actually an error + } +} + +CK_ULONG do_get_rsa_modulus_length(EVP_PKEY *key) { + + CK_ULONG key_len = 0; + RSA *rsa; + + rsa = EVP_PKEY_get1_RSA(key); + if (rsa == NULL) + return 0; + + key_len = (CK_ULONG) (RSA_size(rsa) * 8); // There is also RSA_bits but only in >= 1.1.0 + + RSA_free(rsa); + rsa = NULL; + + return key_len; + +} + +CK_ULONG do_get_public_exponent(EVP_PKEY *key) { + + CK_ULONG e = 0; + RSA *rsa; + + rsa = EVP_PKEY_get1_RSA(key); + if (rsa == NULL) + return 0; + + BN_bn2bin(rsa->e, (unsigned char *)&e); + + RSA_free(rsa); + rsa = NULL; + + return e; +} + +/* #include */ +/* #include */ +/* ERR_load_crypto_strings(); */ +/* //SSL_load_error_strings(); */ +/* fprintf(stderr, "ERROR %s\n", ERR_error_string(ERR_get_error(), NULL)); */ +CK_RV do_get_public_key(EVP_PKEY *key, CK_BYTE_PTR data, CK_ULONG_PTR len) { + + RSA *rsa; + unsigned char *p; + + EC_KEY *eck; + const EC_GROUP *ecg; // Alternative solution is to get i2d_PUBKEY and manually offset + const EC_POINT *ecp; + point_conversion_form_t pcf = POINT_CONVERSION_UNCOMPRESSED; + + switch(key->type) { + case EVP_PKEY_RSA: + case EVP_PKEY_RSA2: + + rsa = EVP_PKEY_get1_RSA(key); + + if ((CK_ULONG)RSA_size(rsa) > *len) { + RSA_free(rsa); + rsa = NULL; + return CKR_BUFFER_TOO_SMALL; + } + + p = data; + + if ((*len = (CK_ULONG) i2d_RSAPublicKey(rsa, &p)) == 0) { + RSA_free(rsa); + rsa = NULL; + return CKR_FUNCTION_FAILED; + } + + // TODO: this is the correct thing to do so that we strip out the exponent + // OTOH we also need a function to get the exponent out with CKA_PUBLIC_EXPONENT + /*BN_bn2bin(rsa->n, data); + *len = 256;*/ + + /* fprintf(stderr, "Public key is: \n"); */ + /* dump_hex(data, *len, stderr, CK_TRUE); */ + + break; + + case EVP_PKEY_EC: + eck = EVP_PKEY_get1_EC_KEY(key); + ecg = EC_KEY_get0_group(eck); + ecp = EC_KEY_get0_public_key(eck); + + // Add the DER structure with length after extracting the point + data[0] = 0x04; + + if ((*len = EC_POINT_point2oct(ecg, ecp, pcf, data + 2, *len - 2, NULL)) == 0) { + EC_KEY_free(eck); + eck = NULL; + return CKR_FUNCTION_FAILED; + } + + data[1] = *len; + + *len += 2; + + EC_KEY_free(eck); + eck = NULL; + + break; + + default: + return CKR_FUNCTION_FAILED; + } + + return CKR_OK; + +} + +CK_RV do_encode_rsa_public_key(CK_BYTE_PTR data, CK_ULONG len, RSA **key) { + + const unsigned char *p = data; + + if (data == NULL) + return CKR_ARGUMENTS_BAD; + + if ((*key = d2i_RSAPublicKey(NULL, &p, (long) len)) == NULL) + return CKR_FUNCTION_FAILED; + + return CKR_OK; + +} + +CK_RV do_get_curve_parameters(EVP_PKEY *key, CK_BYTE_PTR data, CK_ULONG_PTR len) { + + EC_KEY *eck; + const EC_GROUP *ecg; + unsigned char *p; + + eck = EVP_PKEY_get1_EC_KEY(key); + ecg = EC_KEY_get0_group(eck); + + p = data; + + if ((*len = (CK_ULONG) i2d_ECPKParameters(ecg, &p)) == 0) { + EC_KEY_free(eck); + eck = NULL; + return CKR_FUNCTION_FAILED; + } + + EC_KEY_free(eck); + eck = NULL; + + return CKR_OK; +} + +CK_RV do_delete_pubk(EVP_PKEY **key) { + + EVP_PKEY_free(*key); + key = NULL; + + return CKR_OK; + +} + +/*CK_RV free_key(EVP_PKEY *key) { + + EVP_PKEY_free(key); + + return CKR_OK; + + }*/ + +CK_RV do_pkcs_1_t1(CK_BYTE_PTR in, CK_ULONG in_len, CK_BYTE_PTR out, CK_ULONG_PTR out_len, CK_ULONG key_len) { + unsigned char buffer[512]; + + key_len /= 8; + DBG("Apply padding to %lu bytes and get %lu\n", in_len, key_len); + + // TODO: rand must be seeded first (should be automatic) + if (*out_len < key_len) + return CKR_BUFFER_TOO_SMALL; + + if (RSA_padding_add_PKCS1_type_1(buffer, key_len, in, in_len) == 0) + return CKR_FUNCTION_FAILED; + + memcpy(out, buffer, key_len); + *out_len = key_len; + + return CKR_OK; +} + +CK_RV do_pkcs_1_digest_info(CK_BYTE_PTR in, CK_ULONG in_len, int nid, CK_BYTE_PTR out, CK_ULONG_PTR out_len) { + + unsigned int len; + CK_RV rv; + + rv = prepare_rsa_signature(in, in_len, out, &len, nid); + if (!rv) + return CKR_FUNCTION_FAILED; + + *out_len = len; + + return CKR_OK; + +} + +CK_RV do_pkcs_pss(RSA *key, CK_BYTE_PTR in, CK_ULONG in_len, int nid, + CK_BYTE_PTR out, CK_ULONG_PTR out_len) { + unsigned char em[512]; // Max for this is ceil((|key_len_bits| - 1) / 8) + + OpenSSL_add_all_digests(); + + // TODO: rand must be seeded first (should be automatic) + if (*out_len < (CK_ULONG)RSA_size(key)) + return CKR_BUFFER_TOO_SMALL; + + DBG("Apply PSS padding to %lu bytes and get %d\n", in_len, RSA_size(key)); + + if (out != in) + memcpy(out, in, in_len); + + // In case of raw PSS (no hash) this function will fail because OpenSSL requires an MD + if (RSA_padding_add_PKCS1_PSS(key, em, out, EVP_get_digestbynid(nid), -2) == 0) { + EVP_cleanup(); + return CKR_FUNCTION_FAILED; + } + + *out_len = (CK_ULONG) RSA_size(key); + + EVP_cleanup(); + + return CKR_OK; +} + +CK_RV do_md_init(hash_t hash, ykcs11_md_ctx_t **ctx) { + + const EVP_MD *md; + + switch (hash) { + case YKCS11_NO_HASH: + return CKR_FUNCTION_FAILED; + + case YKCS11_SHA1: + md = EVP_sha1(); + break; + + //case YKCS11_SHA224: + + case YKCS11_SHA256: + md = EVP_sha256(); + break; + + case YKCS11_SHA384: + md = EVP_sha384(); + break; + + case YKCS11_SHA512: + md = EVP_sha512(); + break; + + //case YKCS11_RIPEMD128_RSA_PKCS_HASH: + //case YKCS11_RIPEMD160_HASH: + + default: + return CKR_FUNCTION_FAILED; + } + + *ctx = EVP_MD_CTX_create(); + + // The OpenSSL function above never fail + if (EVP_DigestInit_ex(*ctx, md, NULL) == 0) { + EVP_MD_CTX_destroy((EVP_MD_CTX *)*ctx); + return CKR_FUNCTION_FAILED; + } + + return CKR_OK; +} + +CK_RV do_md_update(ykcs11_md_ctx_t *ctx, CK_BYTE_PTR in, CK_ULONG in_len) { + + if (EVP_DigestUpdate(ctx, in, in_len) != 1) { + EVP_MD_CTX_destroy(ctx); + return CKR_FUNCTION_FAILED; + } + + return CKR_OK; + +} + +CK_RV do_md_finalize(ykcs11_md_ctx_t *ctx, CK_BYTE_PTR out, CK_ULONG_PTR out_len, int *nid) { + + int rv; + unsigned int len; + + // Keep track of the md type if requested + if (nid != NULL) + *nid = EVP_MD_CTX_type(ctx); + + // Finalize digest and store result + rv = EVP_DigestFinal_ex(ctx, out, &len); + + // Destroy the md context + EVP_MD_CTX_destroy(ctx); + + // Error if the previous call failed + if (rv != 1) + return CKR_FUNCTION_FAILED; + + *out_len = len; + + return CKR_OK; +} + +CK_RV do_md_cleanup(ykcs11_md_ctx_t *ctx) { + + EVP_MD_CTX_destroy((EVP_MD_CTX *) ctx); + + return CKR_OK; +} diff --git a/ykcs11/openssl_utils.h b/ykcs11/openssl_utils.h new file mode 100644 index 0000000..35461a5 --- /dev/null +++ b/ykcs11/openssl_utils.h @@ -0,0 +1,41 @@ +#ifndef OPENSSL_UTIL_H +#define OPENSSL_UTIL_H + +/* #include */ +/* #include */ +/* #include */ +/* #include */ + +#include "openssl_types.h" +#include "pkcs11t.h" + +CK_RV do_store_cert(CK_BYTE_PTR data, CK_ULONG len, X509 **cert); +CK_RV do_create_empty_cert(CK_BYTE_PTR in, CK_ULONG in_len, CK_BBOOL is_rsa, + CK_BYTE_PTR out, CK_ULONG_PTR out_len); +CK_RV do_check_cert(CK_BYTE_PTR in, CK_ULONG_PTR cert_len); +CK_RV do_get_raw_cert(X509 *cert, CK_BYTE_PTR out, CK_ULONG_PTR out_len); +CK_RV do_delete_cert(X509 **cert); +//CK_RV free_cert(X509 *cert); + +CK_RV do_store_pubk(X509 *cert, EVP_PKEY **key); +CK_KEY_TYPE do_get_key_type(EVP_PKEY *key); +CK_ULONG do_get_rsa_modulus_length(EVP_PKEY *key); +CK_ULONG do_get_public_exponent(EVP_PKEY *key); +CK_RV do_get_public_key(EVP_PKEY *key, CK_BYTE_PTR data, CK_ULONG_PTR len); +CK_RV do_encode_rsa_public_key(CK_BYTE_PTR data, CK_ULONG len, RSA **key); +CK_RV do_get_curve_parameters(EVP_PKEY *key, CK_BYTE_PTR data, CK_ULONG_PTR len); +CK_RV do_delete_pubk(EVP_PKEY **key); +//CK_RV free_key(EVP_PKEY *key); + +CK_RV do_pkcs_1_t1(CK_BYTE_PTR in, CK_ULONG in_len, CK_BYTE_PTR out, CK_ULONG_PTR out_len, CK_ULONG key_len); +CK_RV do_pkcs_1_digest_info(CK_BYTE_PTR in, CK_ULONG in_len, int nid, CK_BYTE_PTR out, CK_ULONG_PTR out_len); + +CK_RV do_pkcs_pss(RSA *key, CK_BYTE_PTR in, CK_ULONG in_len, int nid, + CK_BYTE_PTR out, CK_ULONG_PTR out_len); + +CK_RV do_md_init(hash_t hash, ykcs11_md_ctx_t **ctx); +CK_RV do_md_update(ykcs11_md_ctx_t *ctx, CK_BYTE_PTR in, CK_ULONG in_len); +CK_RV do_md_finalize(ykcs11_md_ctx_t *ctx, CK_BYTE_PTR out, CK_ULONG_PTR out_len, int *nid); +CK_RV do_md_cleanup(ykcs11_md_ctx_t *ctx); + +#endif diff --git a/ykcs11/pkcs11.h b/ykcs11/pkcs11.h new file mode 100644 index 0000000..3d7e166 --- /dev/null +++ b/ykcs11/pkcs11.h @@ -0,0 +1,324 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ +/* + * Copyright (C) 1994-1999 RSA Security Inc. Licence to copy this document + * is granted provided that it is identified as "RSA Security In.c Public-Key + * Cryptography Standards (PKCS)" in all material mentioning or referencing + * this document. + */ +#ifndef _PKCS11_H_ +#define _PKCS11_H_ 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* Before including this file (pkcs11.h) (or pkcs11t.h by + * itself), 6 platform-specific macros must be defined. These + * macros are described below, and typical definitions for them + * are also given. Be advised that these definitions can depend + * on both the platform and the compiler used (and possibly also + * on whether a PKCS #11 library is linked statically or + * dynamically). + * + * In addition to defining these 6 macros, the packing convention + * for PKCS #11 structures should be set. The PKCS #11 + * convention on packing is that structures should be 1-byte + * aligned. + * + * In a Win32 environment, this might be done by using the + * following preprocessor directive before including pkcs11.h + * or pkcs11t.h: + * + * #pragma pack(push, cryptoki, 1) + * + * and using the following preprocessor directive after including + * pkcs11.h or pkcs11t.h: + * + * #pragma pack(pop, cryptoki) + * + * In a Win16 environment, this might be done by using the + * following preprocessor directive before including pkcs11.h + * or pkcs11t.h: + * + * #pragma pack(1) + * + * In a UNIX environment, you're on your own here. You might + * not need to do anything. + * + * + * Now for the macros: + * + * + * 1. CK_PTR: The indirection string for making a pointer to an + * object. It can be used like this: + * + * typedef CK_BYTE CK_PTR CK_BYTE_PTR; + * + * In a Win32 environment, it might be defined by + * + * #define CK_PTR * + * + * In a Win16 environment, it might be defined by + * + * #define CK_PTR far * + * + * In a UNIX environment, it might be defined by + * + * #define CK_PTR * + * + * + * 2. CK_DEFINE_FUNCTION(returnType, name): A macro which makes + * an exportable PKCS #11 library function definition out of a + * return type and a function name. It should be used in the + * following fashion to define the exposed PKCS #11 functions in + * a PKCS #11 library: + * + * CK_DEFINE_FUNCTION(CK_RV, C_Initialize)( + * CK_VOID_PTR pReserved + * ) + * { + * ... + * } + * + * For defining a function in a Win32 PKCS #11 .dll, it might be + * defined by + * + * #define CK_DEFINE_FUNCTION(returnType, name) \ + * returnType __declspec(dllexport) name + * + * For defining a function in a Win16 PKCS #11 .dll, it might be + * defined by + * + * #define CK_DEFINE_FUNCTION(returnType, name) \ + * returnType __export _far _pascal name + * + * In a UNIX environment, it might be defined by + * + * #define CK_DEFINE_FUNCTION(returnType, name) \ + * returnType name + * + * + * 3. CK_DECLARE_FUNCTION(returnType, name): A macro which makes + * an importable PKCS #11 library function declaration out of a + * return type and a function name. It should be used in the + * following fashion: + * + * extern CK_DECLARE_FUNCTION(CK_RV, C_Initialize)( + * CK_VOID_PTR pReserved + * ); + * + * For declaring a function in a Win32 PKCS #11 .dll, it might + * be defined by + * + * #define CK_DECLARE_FUNCTION(returnType, name) \ + * returnType __declspec(dllimport) name + * + * For declaring a function in a Win16 PKCS #11 .dll, it might + * be defined by + * + * #define CK_DECLARE_FUNCTION(returnType, name) \ + * returnType __export _far _pascal name + * + * In a UNIX environment, it might be defined by + * + * #define CK_DECLARE_FUNCTION(returnType, name) \ + * returnType name + * + * + * 4. CK_DECLARE_FUNCTION_POINTER(returnType, name): A macro + * which makes a PKCS #11 API function pointer declaration or + * function pointer type declaration out of a return type and a + * function name. It should be used in the following fashion: + * + * // Define funcPtr to be a pointer to a PKCS #11 API function + * // taking arguments args and returning CK_RV. + * CK_DECLARE_FUNCTION_POINTER(CK_RV, funcPtr)(args); + * + * or + * + * // Define funcPtrType to be the type of a pointer to a + * // PKCS #11 API function taking arguments args and returning + * // CK_RV, and then define funcPtr to be a variable of type + * // funcPtrType. + * typedef CK_DECLARE_FUNCTION_POINTER(CK_RV, funcPtrType)(args); + * funcPtrType funcPtr; + * + * For accessing functions in a Win32 PKCS #11 .dll, in might be + * defined by + * + * #define CK_DECLARE_FUNCTION_POINTER(returnType, name) \ + * returnType __declspec(dllimport) (* name) + * + * For accessing functions in a Win16 PKCS #11 .dll, it might be + * defined by + * + * #define CK_DECLARE_FUNCTION_POINTER(returnType, name) \ + * returnType __export _far _pascal (* name) + * + * In a UNIX environment, it might be defined by + * + * #define CK_DECLARE_FUNCTION_POINTER(returnType, name) \ + * returnType (* name) + * + * + * 5. CK_CALLBACK_FUNCTION(returnType, name): A macro which makes + * a function pointer type for an application callback out of + * a return type for the callback and a name for the callback. + * It should be used in the following fashion: + * + * CK_CALLBACK_FUNCTION(CK_RV, myCallback)(args); + * + * to declare a function pointer, myCallback, to a callback + * which takes arguments args and returns a CK_RV. It can also + * be used like this: + * + * typedef CK_CALLBACK_FUNCTION(CK_RV, myCallbackType)(args); + * myCallbackType myCallback; + * + * In a Win32 environment, it might be defined by + * + * #define CK_CALLBACK_FUNCTION(returnType, name) \ + * returnType (* name) + * + * In a Win16 environment, it might be defined by + * + * #define CK_CALLBACK_FUNCTION(returnType, name) \ + * returnType _far _pascal (* name) + * + * In a UNIX environment, it might be defined by + * + * #define CK_CALLBACK_FUNCTION(returnType, name) \ + * returnType (* name) + * + * + * 6. NULL_PTR: This macro is the value of a NULL pointer. + * + * In any ANSI/ISO C environment (and in many others as well), + * this should be defined by + * + * #ifndef NULL_PTR + * #define NULL_PTR 0 + * #endif + */ + + +/* All the various PKCS #11 types and #define'd values are in the + * file pkcs11t.h. */ +#include "pkcs11t.h" + +#define __PASTE(x,y) x##y + + +/* packing defines */ +//#include "pkcs11p.h" // TODO: msc specific? +/* ============================================================== + * Define the "extern" form of all the entry points. + * ============================================================== + */ + +#define CK_NEED_ARG_LIST 1 +#define CK_PKCS11_FUNCTION_INFO(name) \ + CK_DECLARE_FUNCTION(CK_RV, name) + +/* pkcs11f.h has all the information about the PKCS #11 + * function prototypes. */ +#include "pkcs11f.h" + +#undef CK_NEED_ARG_LIST +#undef CK_PKCS11_FUNCTION_INFO + + +/* ============================================================== + * Define the typedef form of all the entry points. That is, for + * each PKCS #11 function C_XXX, define a type CK_C_XXX which is + * a pointer to that kind of function. + * ============================================================== + */ + +#define CK_NEED_ARG_LIST 1 +#define CK_PKCS11_FUNCTION_INFO(name) \ + typedef CK_DECLARE_FUNCTION_POINTER(CK_RV, __PASTE(CK_,name)) + +/* pkcs11f.h has all the information about the PKCS #11 + * function prototypes. */ +#include "pkcs11f.h" + +#undef CK_NEED_ARG_LIST +#undef CK_PKCS11_FUNCTION_INFO + + +/* ============================================================== + * Define structed vector of entry points. A CK_FUNCTION_LIST + * contains a CK_VERSION indicating a library's PKCS #11 version + * and then a whole slew of function pointers to the routines in + * the library. This type was declared, but not defined, in + * pkcs11t.h. + * ============================================================== + */ + +#define CK_PKCS11_FUNCTION_INFO(name) \ + __PASTE(CK_,name) name; + +struct CK_FUNCTION_LIST { + + CK_VERSION version; /* PKCS #11 version */ + +/* Pile all the function pointers into the CK_FUNCTION_LIST. */ +/* pkcs11f.h has all the information about the PKCS #11 + * function prototypes. */ +#include "pkcs11f.h" + +}; + +#undef CK_PKCS11_FUNCTION_INFO + + +#undef __PASTE + +/* unpack */ +//#include "pkcs11u.h" // TODO: msc specific? + +#ifdef __cplusplus +} +#endif + +/* +** Functions called directly by applications to configure the FIPS token. +*/ +extern void PK11_ConfigurePKCS11(char *man, char *libdes, char *tokdes, + char *ptokdes, char *slotdes, char *pslotdes, char *fslotdes, + char *fpslotdes, int minPwd, int pwdRequired); +extern void PK11_ConfigureFIPS(char *slotdes, char *pslotdes); + +#endif diff --git a/ykcs11/pkcs11f.h b/ykcs11/pkcs11f.h new file mode 100644 index 0000000..71ee267 --- /dev/null +++ b/ykcs11/pkcs11f.h @@ -0,0 +1,933 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ +/* + * Copyright (C) 1994-1999 RSA Security Inc. Licence to copy this document + * is granted provided that it is identified as "RSA Security In.c Public-Key + * Cryptography Standards (PKCS)" in all material mentioning or referencing + * this document. + */ +/* This function contains pretty much everything about all the */ +/* PKCS #11 function prototypes. Because this information is */ +/* used for more than just declaring function prototypes, the */ +/* order of the functions appearing herein is important, and */ +/* should not be altered. */ + + + +/* General-purpose */ + +/* C_Initialize initializes the PKCS #11 library. */ +CK_PKCS11_FUNCTION_INFO(C_Initialize) +#ifdef CK_NEED_ARG_LIST +( + CK_VOID_PTR pInitArgs /* if this is not NULL_PTR, it gets + * cast to CK_C_INITIALIZE_ARGS_PTR + * and dereferenced */ +); +#endif + + +/* C_Finalize indicates that an application is done with the + * PKCS #11 library. */ +CK_PKCS11_FUNCTION_INFO(C_Finalize) +#ifdef CK_NEED_ARG_LIST +( + CK_VOID_PTR pReserved /* reserved. Should be NULL_PTR */ +); +#endif + + +/* C_GetInfo returns general information about PKCS #11. */ +CK_PKCS11_FUNCTION_INFO(C_GetInfo) +#ifdef CK_NEED_ARG_LIST +( + CK_INFO_PTR pInfo /* location that receives information */ +); +#endif + + +/* C_GetFunctionList returns the function list. */ +CK_PKCS11_FUNCTION_INFO(C_GetFunctionList) +#ifdef CK_NEED_ARG_LIST +( + CK_FUNCTION_LIST_PTR_PTR ppFunctionList /* receives pointer to + * function list */ +); +#endif + + + +/* Slot and token management */ + +/* C_GetSlotList obtains a list of slots in the system. */ +CK_PKCS11_FUNCTION_INFO(C_GetSlotList) +#ifdef CK_NEED_ARG_LIST +( + CK_BBOOL tokenPresent, /* only slots with tokens? */ + CK_SLOT_ID_PTR pSlotList, /* receives array of slot IDs */ + CK_ULONG_PTR pulCount /* receives number of slots */ +); +#endif + + +/* C_GetSlotInfo obtains information about a particular slot in + * the system. */ +CK_PKCS11_FUNCTION_INFO(C_GetSlotInfo) +#ifdef CK_NEED_ARG_LIST +( + CK_SLOT_ID slotID, /* the ID of the slot */ + CK_SLOT_INFO_PTR pInfo /* receives the slot information */ +); +#endif + + +/* C_GetTokenInfo obtains information about a particular token + * in the system. */ +CK_PKCS11_FUNCTION_INFO(C_GetTokenInfo) +#ifdef CK_NEED_ARG_LIST +( + CK_SLOT_ID slotID, /* ID of the token's slot */ + CK_TOKEN_INFO_PTR pInfo /* receives the token information */ +); +#endif + + +/* C_GetMechanismList obtains a list of mechanism types + * supported by a token. */ +CK_PKCS11_FUNCTION_INFO(C_GetMechanismList) +#ifdef CK_NEED_ARG_LIST +( + CK_SLOT_ID slotID, /* ID of token's slot */ + CK_MECHANISM_TYPE_PTR pMechanismList, /* gets mech. array */ + CK_ULONG_PTR pulCount /* gets # of mechs. */ +); +#endif + + +/* C_GetMechanismInfo obtains information about a particular + * mechanism possibly supported by a token. */ +CK_PKCS11_FUNCTION_INFO(C_GetMechanismInfo) +#ifdef CK_NEED_ARG_LIST +( + CK_SLOT_ID slotID, /* ID of the token's slot */ + CK_MECHANISM_TYPE type, /* type of mechanism */ + CK_MECHANISM_INFO_PTR pInfo /* receives mechanism info */ +); +#endif + + +/* C_InitToken initializes a token. */ +CK_PKCS11_FUNCTION_INFO(C_InitToken) +#ifdef CK_NEED_ARG_LIST +( + CK_SLOT_ID slotID, /* ID of the token's slot */ + CK_CHAR_PTR pPin, /* the SO's initial PIN */ + CK_ULONG ulPinLen, /* length in bytes of the PIN */ + CK_CHAR_PTR pLabel /* 32-byte token label (blank padded) */ +); +#endif + + +/* C_InitPIN initializes the normal user's PIN. */ +CK_PKCS11_FUNCTION_INFO(C_InitPIN) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_CHAR_PTR pPin, /* the normal user's PIN */ + CK_ULONG ulPinLen /* length in bytes of the PIN */ +); +#endif + + +/* C_SetPIN modifies the PIN of the user who is logged in. */ +CK_PKCS11_FUNCTION_INFO(C_SetPIN) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_CHAR_PTR pOldPin, /* the old PIN */ + CK_ULONG ulOldLen, /* length of the old PIN */ + CK_CHAR_PTR pNewPin, /* the new PIN */ + CK_ULONG ulNewLen /* length of the new PIN */ +); +#endif + + + +/* Session management */ + +/* C_OpenSession opens a session between an application and a + * token. */ +CK_PKCS11_FUNCTION_INFO(C_OpenSession) +#ifdef CK_NEED_ARG_LIST +( + CK_SLOT_ID slotID, /* the slot's ID */ + CK_FLAGS flags, /* from CK_SESSION_INFO */ + CK_VOID_PTR pApplication, /* passed to callback */ + CK_NOTIFY Notify, /* callback function */ + CK_SESSION_HANDLE_PTR phSession /* gets session handle */ +); +#endif + + +/* C_CloseSession closes a session between an application and a + * token. */ +CK_PKCS11_FUNCTION_INFO(C_CloseSession) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession /* the session's handle */ +); +#endif + + +/* C_CloseAllSessions closes all sessions with a token. */ +CK_PKCS11_FUNCTION_INFO(C_CloseAllSessions) +#ifdef CK_NEED_ARG_LIST +( + CK_SLOT_ID slotID /* the token's slot */ +); +#endif + + +/* C_GetSessionInfo obtains information about the session. */ +CK_PKCS11_FUNCTION_INFO(C_GetSessionInfo) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_SESSION_INFO_PTR pInfo /* receives session info */ +); +#endif + + +/* C_GetOperationState obtains the state of the cryptographic operation + * in a session. */ +CK_PKCS11_FUNCTION_INFO(C_GetOperationState) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* session's handle */ + CK_BYTE_PTR pOperationState, /* gets state */ + CK_ULONG_PTR pulOperationStateLen /* gets state length */ +); +#endif + + +/* C_SetOperationState restores the state of the cryptographic + * operation in a session. */ +CK_PKCS11_FUNCTION_INFO(C_SetOperationState) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* session's handle */ + CK_BYTE_PTR pOperationState, /* holds state */ + CK_ULONG ulOperationStateLen, /* holds state length */ + CK_OBJECT_HANDLE hEncryptionKey, /* en/decryption key */ + CK_OBJECT_HANDLE hAuthenticationKey /* sign/verify key */ +); +#endif + + +/* C_Login logs a user into a token. */ +CK_PKCS11_FUNCTION_INFO(C_Login) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_USER_TYPE userType, /* the user type */ + CK_CHAR_PTR pPin, /* the user's PIN */ + CK_ULONG ulPinLen /* the length of the PIN */ +); +#endif + + +/* C_Logout logs a user out from a token. */ +CK_PKCS11_FUNCTION_INFO(C_Logout) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession /* the session's handle */ +); +#endif + + + +/* Object management */ + +/* C_CreateObject creates a new object. */ +CK_PKCS11_FUNCTION_INFO(C_CreateObject) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_ATTRIBUTE_PTR pTemplate, /* the object's template */ + CK_ULONG ulCount, /* attributes in template */ + CK_OBJECT_HANDLE_PTR phObject /* gets new object's handle. */ +); +#endif + + +/* C_CopyObject copies an object, creating a new object for the + * copy. */ +CK_PKCS11_FUNCTION_INFO(C_CopyObject) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_OBJECT_HANDLE hObject, /* the object's handle */ + CK_ATTRIBUTE_PTR pTemplate, /* template for new object */ + CK_ULONG ulCount, /* attributes in template */ + CK_OBJECT_HANDLE_PTR phNewObject /* receives handle of copy */ +); +#endif + + +/* C_DestroyObject destroys an object. */ +CK_PKCS11_FUNCTION_INFO(C_DestroyObject) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_OBJECT_HANDLE hObject /* the object's handle */ +); +#endif + + +/* C_GetObjectSize gets the size of an object in bytes. */ +CK_PKCS11_FUNCTION_INFO(C_GetObjectSize) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_OBJECT_HANDLE hObject, /* the object's handle */ + CK_ULONG_PTR pulSize /* receives size of object */ +); +#endif + + +/* C_GetAttributeValue obtains the value of one or more object + * attributes. */ +CK_PKCS11_FUNCTION_INFO(C_GetAttributeValue) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_OBJECT_HANDLE hObject, /* the object's handle */ + CK_ATTRIBUTE_PTR pTemplate, /* specifies attrs; gets vals */ + CK_ULONG ulCount /* attributes in template */ +); +#endif + + +/* C_SetAttributeValue modifies the value of one or more object + * attributes */ +CK_PKCS11_FUNCTION_INFO(C_SetAttributeValue) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_OBJECT_HANDLE hObject, /* the object's handle */ + CK_ATTRIBUTE_PTR pTemplate, /* specifies attrs and values */ + CK_ULONG ulCount /* attributes in template */ +); +#endif + + +/* C_FindObjectsInit initializes a search for token and session + * objects that match a template. */ +CK_PKCS11_FUNCTION_INFO(C_FindObjectsInit) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_ATTRIBUTE_PTR pTemplate, /* attribute values to match */ + CK_ULONG ulCount /* attrs in search template */ +); +#endif + + +/* C_FindObjects continues a search for token and session + * objects that match a template, obtaining additional object + * handles. */ +CK_PKCS11_FUNCTION_INFO(C_FindObjects) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* session's handle */ + CK_OBJECT_HANDLE_PTR phObject, /* gets obj. handles */ + CK_ULONG ulMaxObjectCount, /* max handles to get */ + CK_ULONG_PTR pulObjectCount /* actual # returned */ +); +#endif + + +/* C_FindObjectsFinal finishes a search for token and session + * objects. */ +CK_PKCS11_FUNCTION_INFO(C_FindObjectsFinal) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession /* the session's handle */ +); +#endif + + + +/* Encryption and decryption */ + +/* C_EncryptInit initializes an encryption operation. */ +CK_PKCS11_FUNCTION_INFO(C_EncryptInit) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_MECHANISM_PTR pMechanism, /* the encryption mechanism */ + CK_OBJECT_HANDLE hKey /* handle of encryption key */ +); +#endif + + +/* C_Encrypt encrypts single-part data. */ +CK_PKCS11_FUNCTION_INFO(C_Encrypt) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* session's handle */ + CK_BYTE_PTR pData, /* the plaintext data */ + CK_ULONG ulDataLen, /* bytes of plaintext */ + CK_BYTE_PTR pEncryptedData, /* gets ciphertext */ + CK_ULONG_PTR pulEncryptedDataLen /* gets c-text size */ +); +#endif + + +/* C_EncryptUpdate continues a multiple-part encryption + * operation. */ +CK_PKCS11_FUNCTION_INFO(C_EncryptUpdate) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* session's handle */ + CK_BYTE_PTR pPart, /* the plaintext data */ + CK_ULONG ulPartLen, /* plaintext data len */ + CK_BYTE_PTR pEncryptedPart, /* gets ciphertext */ + CK_ULONG_PTR pulEncryptedPartLen /* gets c-text size */ +); +#endif + + +/* C_EncryptFinal finishes a multiple-part encryption + * operation. */ +CK_PKCS11_FUNCTION_INFO(C_EncryptFinal) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* session handle */ + CK_BYTE_PTR pLastEncryptedPart, /* last c-text */ + CK_ULONG_PTR pulLastEncryptedPartLen /* gets last size */ +); +#endif + + +/* C_DecryptInit initializes a decryption operation. */ +CK_PKCS11_FUNCTION_INFO(C_DecryptInit) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_MECHANISM_PTR pMechanism, /* the decryption mechanism */ + CK_OBJECT_HANDLE hKey /* handle of decryption key */ +); +#endif + + +/* C_Decrypt decrypts encrypted data in a single part. */ +CK_PKCS11_FUNCTION_INFO(C_Decrypt) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* session's handle */ + CK_BYTE_PTR pEncryptedData, /* ciphertext */ + CK_ULONG ulEncryptedDataLen, /* ciphertext length */ + CK_BYTE_PTR pData, /* gets plaintext */ + CK_ULONG_PTR pulDataLen /* gets p-text size */ +); +#endif + + +/* C_DecryptUpdate continues a multiple-part decryption + * operation. */ +CK_PKCS11_FUNCTION_INFO(C_DecryptUpdate) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* session's handle */ + CK_BYTE_PTR pEncryptedPart, /* encrypted data */ + CK_ULONG ulEncryptedPartLen, /* input length */ + CK_BYTE_PTR pPart, /* gets plaintext */ + CK_ULONG_PTR pulPartLen /* p-text size */ +); +#endif + + +/* C_DecryptFinal finishes a multiple-part decryption + * operation. */ +CK_PKCS11_FUNCTION_INFO(C_DecryptFinal) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_BYTE_PTR pLastPart, /* gets plaintext */ + CK_ULONG_PTR pulLastPartLen /* p-text size */ +); +#endif + + + +/* Message digesting */ + +/* C_DigestInit initializes a message-digesting operation. */ +CK_PKCS11_FUNCTION_INFO(C_DigestInit) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_MECHANISM_PTR pMechanism /* the digesting mechanism */ +); +#endif + + +/* C_Digest digests data in a single part. */ +CK_PKCS11_FUNCTION_INFO(C_Digest) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_BYTE_PTR pData, /* data to be digested */ + CK_ULONG ulDataLen, /* bytes of data to digest */ + CK_BYTE_PTR pDigest, /* gets the message digest */ + CK_ULONG_PTR pulDigestLen /* gets digest length */ +); +#endif + + +/* C_DigestUpdate continues a multiple-part message-digesting + * operation. */ +CK_PKCS11_FUNCTION_INFO(C_DigestUpdate) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_BYTE_PTR pPart, /* data to be digested */ + CK_ULONG ulPartLen /* bytes of data to be digested */ +); +#endif + + +/* C_DigestKey continues a multi-part message-digesting + * operation, by digesting the value of a secret key as part of + * the data already digested. */ +CK_PKCS11_FUNCTION_INFO(C_DigestKey) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_OBJECT_HANDLE hKey /* secret key to digest */ +); +#endif + + +/* C_DigestFinal finishes a multiple-part message-digesting + * operation. */ +CK_PKCS11_FUNCTION_INFO(C_DigestFinal) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_BYTE_PTR pDigest, /* gets the message digest */ + CK_ULONG_PTR pulDigestLen /* gets byte count of digest */ +); +#endif + + + +/* Signing and MACing */ + +/* C_SignInit initializes a signature (private key encryption) + * operation, where the signature is (will be) an appendix to + * the data, and plaintext cannot be recovered from the + *signature. */ +CK_PKCS11_FUNCTION_INFO(C_SignInit) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_MECHANISM_PTR pMechanism, /* the signature mechanism */ + CK_OBJECT_HANDLE hKey /* handle of signature key */ +); +#endif + + +/* C_Sign signs (encrypts with private key) data in a single + * part, where the signature is (will be) an appendix to the + * data, and plaintext cannot be recovered from the signature. */ +CK_PKCS11_FUNCTION_INFO(C_Sign) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_BYTE_PTR pData, /* the data to sign */ + CK_ULONG ulDataLen, /* count of bytes to sign */ + CK_BYTE_PTR pSignature, /* gets the signature */ + CK_ULONG_PTR pulSignatureLen /* gets signature length */ +); +#endif + + +/* C_SignUpdate continues a multiple-part signature operation, + * where the signature is (will be) an appendix to the data, + * and plaintext cannot be recovered from the signature. */ +CK_PKCS11_FUNCTION_INFO(C_SignUpdate) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_BYTE_PTR pPart, /* the data to sign */ + CK_ULONG ulPartLen /* count of bytes to sign */ +); +#endif + + +/* C_SignFinal finishes a multiple-part signature operation, + * returning the signature. */ +CK_PKCS11_FUNCTION_INFO(C_SignFinal) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_BYTE_PTR pSignature, /* gets the signature */ + CK_ULONG_PTR pulSignatureLen /* gets signature length */ +); +#endif + + +/* C_SignRecoverInit initializes a signature operation, where + * the data can be recovered from the signature. */ +CK_PKCS11_FUNCTION_INFO(C_SignRecoverInit) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_MECHANISM_PTR pMechanism, /* the signature mechanism */ + CK_OBJECT_HANDLE hKey /* handle of the signature key */ +); +#endif + + +/* C_SignRecover signs data in a single operation, where the + * data can be recovered from the signature. */ +CK_PKCS11_FUNCTION_INFO(C_SignRecover) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_BYTE_PTR pData, /* the data to sign */ + CK_ULONG ulDataLen, /* count of bytes to sign */ + CK_BYTE_PTR pSignature, /* gets the signature */ + CK_ULONG_PTR pulSignatureLen /* gets signature length */ +); +#endif + + + +/* Verifying signatures and MACs */ + +/* C_VerifyInit initializes a verification operation, where the + * signature is an appendix to the data, and plaintext cannot + * cannot be recovered from the signature (e.g. DSA). */ +CK_PKCS11_FUNCTION_INFO(C_VerifyInit) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_MECHANISM_PTR pMechanism, /* the verification mechanism */ + CK_OBJECT_HANDLE hKey /* verification key */ +); +#endif + + +/* C_Verify verifies a signature in a single-part operation, + * where the signature is an appendix to the data, and plaintext + * cannot be recovered from the signature. */ +CK_PKCS11_FUNCTION_INFO(C_Verify) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_BYTE_PTR pData, /* signed data */ + CK_ULONG ulDataLen, /* length of signed data */ + CK_BYTE_PTR pSignature, /* signature */ + CK_ULONG ulSignatureLen /* signature length*/ +); +#endif + + +/* C_VerifyUpdate continues a multiple-part verification + * operation, where the signature is an appendix to the data, + * and plaintext cannot be recovered from the signature. */ +CK_PKCS11_FUNCTION_INFO(C_VerifyUpdate) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_BYTE_PTR pPart, /* signed data */ + CK_ULONG ulPartLen /* length of signed data */ +); +#endif + + +/* C_VerifyFinal finishes a multiple-part verification + * operation, checking the signature. */ +CK_PKCS11_FUNCTION_INFO(C_VerifyFinal) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_BYTE_PTR pSignature, /* signature to verify */ + CK_ULONG ulSignatureLen /* signature length */ +); +#endif + + +/* C_VerifyRecoverInit initializes a signature verification + * operation, where the data is recovered from the signature. */ +CK_PKCS11_FUNCTION_INFO(C_VerifyRecoverInit) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_MECHANISM_PTR pMechanism, /* the verification mechanism */ + CK_OBJECT_HANDLE hKey /* verification key */ +); +#endif + + +/* C_VerifyRecover verifies a signature in a single-part + * operation, where the data is recovered from the signature. */ +CK_PKCS11_FUNCTION_INFO(C_VerifyRecover) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_BYTE_PTR pSignature, /* signature to verify */ + CK_ULONG ulSignatureLen, /* signature length */ + CK_BYTE_PTR pData, /* gets signed data */ + CK_ULONG_PTR pulDataLen /* gets signed data len */ +); +#endif + + + +/* Dual-function cryptographic operations */ + +/* C_DigestEncryptUpdate continues a multiple-part digesting + * and encryption operation. */ +CK_PKCS11_FUNCTION_INFO(C_DigestEncryptUpdate) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* session's handle */ + CK_BYTE_PTR pPart, /* the plaintext data */ + CK_ULONG ulPartLen, /* plaintext length */ + CK_BYTE_PTR pEncryptedPart, /* gets ciphertext */ + CK_ULONG_PTR pulEncryptedPartLen /* gets c-text length */ +); +#endif + + +/* C_DecryptDigestUpdate continues a multiple-part decryption and + * digesting operation. */ +CK_PKCS11_FUNCTION_INFO(C_DecryptDigestUpdate) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* session's handle */ + CK_BYTE_PTR pEncryptedPart, /* ciphertext */ + CK_ULONG ulEncryptedPartLen, /* ciphertext length */ + CK_BYTE_PTR pPart, /* gets plaintext */ + CK_ULONG_PTR pulPartLen /* gets plaintext len */ +); +#endif + + +/* C_SignEncryptUpdate continues a multiple-part signing and + * encryption operation. */ +CK_PKCS11_FUNCTION_INFO(C_SignEncryptUpdate) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* session's handle */ + CK_BYTE_PTR pPart, /* the plaintext data */ + CK_ULONG ulPartLen, /* plaintext length */ + CK_BYTE_PTR pEncryptedPart, /* gets ciphertext */ + CK_ULONG_PTR pulEncryptedPartLen /* gets c-text length */ +); +#endif + + +/* C_DecryptVerifyUpdate continues a multiple-part decryption and + * verify operation. */ +CK_PKCS11_FUNCTION_INFO(C_DecryptVerifyUpdate) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* session's handle */ + CK_BYTE_PTR pEncryptedPart, /* ciphertext */ + CK_ULONG ulEncryptedPartLen, /* ciphertext length */ + CK_BYTE_PTR pPart, /* gets plaintext */ + CK_ULONG_PTR pulPartLen /* gets p-text length */ +); +#endif + + + +/* Key management */ + +/* C_GenerateKey generates a secret key, creating a new key + * object. */ +CK_PKCS11_FUNCTION_INFO(C_GenerateKey) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_MECHANISM_PTR pMechanism, /* key generation mech. */ + CK_ATTRIBUTE_PTR pTemplate, /* template for new key */ + CK_ULONG ulCount, /* # of attrs in template */ + CK_OBJECT_HANDLE_PTR phKey /* gets handle of new key */ +); +#endif + + +/* C_GenerateKeyPair generates a public-key/private-key pair, + * creating new key objects. */ +CK_PKCS11_FUNCTION_INFO(C_GenerateKeyPair) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* session + * handle */ + CK_MECHANISM_PTR pMechanism, /* key-gen + * mech. */ + CK_ATTRIBUTE_PTR pPublicKeyTemplate, /* template + * for pub. + * key */ + CK_ULONG ulPublicKeyAttributeCount, /* # pub. + * attrs. */ + CK_ATTRIBUTE_PTR pPrivateKeyTemplate, /* template + * for priv. + * key */ + CK_ULONG ulPrivateKeyAttributeCount, /* # priv. + * attrs. */ + CK_OBJECT_HANDLE_PTR phPublicKey, /* gets pub. + * key + * handle */ + CK_OBJECT_HANDLE_PTR phPrivateKey /* gets + * priv. key + * handle */ +); +#endif + + +/* C_WrapKey wraps (i.e., encrypts) a key. */ +CK_PKCS11_FUNCTION_INFO(C_WrapKey) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_MECHANISM_PTR pMechanism, /* the wrapping mechanism */ + CK_OBJECT_HANDLE hWrappingKey, /* wrapping key */ + CK_OBJECT_HANDLE hKey, /* key to be wrapped */ + CK_BYTE_PTR pWrappedKey, /* gets wrapped key */ + CK_ULONG_PTR pulWrappedKeyLen /* gets wrapped key size */ +); +#endif + + +/* C_UnwrapKey unwraps (decrypts) a wrapped key, creating a new + * key object. */ +CK_PKCS11_FUNCTION_INFO(C_UnwrapKey) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* session's handle */ + CK_MECHANISM_PTR pMechanism, /* unwrapping mech. */ + CK_OBJECT_HANDLE hUnwrappingKey, /* unwrapping key */ + CK_BYTE_PTR pWrappedKey, /* the wrapped key */ + CK_ULONG ulWrappedKeyLen, /* wrapped key len */ + CK_ATTRIBUTE_PTR pTemplate, /* new key template */ + CK_ULONG ulAttributeCount, /* template length */ + CK_OBJECT_HANDLE_PTR phKey /* gets new handle */ +); +#endif + + +/* C_DeriveKey derives a key from a base key, creating a new key + * object. */ +CK_PKCS11_FUNCTION_INFO(C_DeriveKey) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* session's handle */ + CK_MECHANISM_PTR pMechanism, /* key deriv. mech. */ + CK_OBJECT_HANDLE hBaseKey, /* base key */ + CK_ATTRIBUTE_PTR pTemplate, /* new key template */ + CK_ULONG ulAttributeCount, /* template length */ + CK_OBJECT_HANDLE_PTR phKey /* gets new handle */ +); +#endif + + + +/* Random number generation */ + +/* C_SeedRandom mixes additional seed material into the token's + * random number generator. */ +CK_PKCS11_FUNCTION_INFO(C_SeedRandom) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_BYTE_PTR pSeed, /* the seed material */ + CK_ULONG ulSeedLen /* length of seed material */ +); +#endif + + +/* C_GenerateRandom generates random data. */ +CK_PKCS11_FUNCTION_INFO(C_GenerateRandom) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_BYTE_PTR RandomData, /* receives the random data */ + CK_ULONG ulRandomLen /* # of bytes to generate */ +); +#endif + + + +/* Parallel function management */ + +/* C_GetFunctionStatus is a legacy function; it obtains an + * updated status of a function running in parallel with an + * application. */ +CK_PKCS11_FUNCTION_INFO(C_GetFunctionStatus) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession /* the session's handle */ +); +#endif + + +/* C_CancelFunction is a legacy function; it cancels a function + * running in parallel. */ +CK_PKCS11_FUNCTION_INFO(C_CancelFunction) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession /* the session's handle */ +); +#endif + + + +/* Functions added in for PKCS #11 Version 2.01 or later */ + +/* C_WaitForSlotEvent waits for a slot event (token insertion, + * removal, etc.) to occur. */ +CK_PKCS11_FUNCTION_INFO(C_WaitForSlotEvent) +#ifdef CK_NEED_ARG_LIST +( + CK_FLAGS flags, /* blocking/nonblocking flag */ + CK_SLOT_ID_PTR pSlot, /* location that receives the slot ID */ + CK_VOID_PTR pRserved /* reserved. Should be NULL_PTR */ +); +#endif diff --git a/ykcs11/pkcs11t.h b/ykcs11/pkcs11t.h new file mode 100644 index 0000000..d357092 --- /dev/null +++ b/ykcs11/pkcs11t.h @@ -0,0 +1,1195 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ +/* + * Copyright (C) 1994-1999 RSA Security Inc. Licence to copy this document + * is granted provided that it is identified as "RSA Security In.c Public-Key + * Cryptography Standards (PKCS)" in all material mentioning or referencing + * this document. + */ +/* See top of pkcs11.h for information about the macros that + * must be defined and the structure-packing conventions that + * must be set before including this file. + */ + +#ifndef _PKCS11T_H_ +#define _PKCS11T_H_ 1 + +#ifndef CK_FALSE +#define CK_FALSE 0 +#endif + +#ifndef CK_TRUE +#define CK_TRUE (!CK_FALSE) +#endif + +//#include "prtypes.h" + +#define CK_PTR * +#define CK_NULL_PTR 0 +#define NULL_PTR 0 +#define CK_CALLBACK_FUNCTION(rv,func) rv (/*PR_CALLBACK*/ * func) +#define CK_DECLARE_FUNCTION(rv,func) /*PR_EXTERN(*/rv/*)*/ func +#define CK_DECLARE_FUNCTION_POINTER(rv,func) rv (/*PR_CALLBACK*/ * func) +#define CK_DEFINE_FUNCTION(rv, func) rv func + +/* an unsigned 8-bit value */ +typedef unsigned char CK_BYTE; + +/* an unsigned 8-bit character */ +typedef CK_BYTE CK_CHAR; + +/* an 8-bit UTF-8 character */ +typedef CK_BYTE CK_UTF8CHAR; + +/* a BYTE-sized Boolean flag */ +typedef CK_BYTE CK_BBOOL; + +/* an unsigned value, at least 32 bits long */ +typedef unsigned long int CK_ULONG; + +/* a signed value, the same size as a CK_ULONG */ +/* CK_LONG is new for v2.0 */ +typedef long int CK_LONG; + +/* at least 32 bits; each bit is a Boolean flag */ +typedef CK_ULONG CK_FLAGS; + +/* Custom type defined for consistency */ +typedef CK_FLAGS CK_PTR CK_FLAGS_PTR; + +/* some special values for certain CK_ULONG variables */ +#define CK_UNAVAILABLE_INFORMATION (~0UL) +#define CK_EFFECTIVELY_INFINITE 0 + + +typedef CK_BYTE CK_PTR CK_BYTE_PTR; +typedef CK_CHAR CK_PTR CK_CHAR_PTR; +typedef CK_UTF8CHAR CK_PTR CK_UTF8CHAR_PTR; +typedef CK_ULONG CK_PTR CK_ULONG_PTR; +typedef void CK_PTR CK_VOID_PTR; + +/* Pointer to a CK_VOID_PTR-- i.e., pointer to pointer to void */ +typedef CK_VOID_PTR CK_PTR CK_VOID_PTR_PTR; + + +/* The following value is always invalid if used as a session */ +/* handle or object handle */ +#define CK_INVALID_HANDLE 0 + + +/* pack */ +//#include "pkcs11p.h" // TODO: msc specific? + +typedef struct CK_VERSION { + CK_BYTE major; /* integer portion of version number */ + CK_BYTE minor; /* 1/100ths portion of version number */ +} CK_VERSION; + +typedef CK_VERSION CK_PTR CK_VERSION_PTR; + + +typedef struct CK_INFO { + CK_VERSION cryptokiVersion; /* PKCS #11 interface ver */ + CK_CHAR manufacturerID[32]; /* blank padded */ + CK_FLAGS flags; /* must be zero */ + + /* libraryDescription and libraryVersion are new for v2.0 */ + CK_CHAR libraryDescription[32]; /* blank padded */ + CK_VERSION libraryVersion; /* version of library */ +} CK_INFO; + +typedef CK_INFO CK_PTR CK_INFO_PTR; + + +/* CK_NOTIFICATION enumerates the types of notifications that + * PKCS #11 provides to an application */ +/* CK_NOTIFICATION has been changed from an enum to a CK_ULONG + * for v2.0 */ +typedef CK_ULONG CK_NOTIFICATION; +#define CKN_SURRENDER 0 + + +typedef CK_ULONG CK_SLOT_ID; + +typedef CK_SLOT_ID CK_PTR CK_SLOT_ID_PTR; + + +/* CK_SLOT_INFO provides information about a slot */ +typedef struct CK_SLOT_INFO { + CK_CHAR slotDescription[64]; /* blank padded */ + CK_CHAR manufacturerID[32]; /* blank padded */ + CK_FLAGS flags; + + /* hardwareVersion and firmwareVersion are new for v2.0 */ + CK_VERSION hardwareVersion; /* version of hardware */ + CK_VERSION firmwareVersion; /* version of firmware */ +} CK_SLOT_INFO; + +/* flags: bit flags that provide capabilities of the slot + * Bit Flag Mask Meaning + */ +#define CKF_TOKEN_PRESENT 0x00000001UL /* a token is there */ +#define CKF_REMOVABLE_DEVICE 0x00000002UL /* removable devices*/ +#define CKF_HW_SLOT 0x00000004UL /* hardware slot */ + +typedef CK_SLOT_INFO CK_PTR CK_SLOT_INFO_PTR; + + +/* CK_TOKEN_INFO provides information about a token */ +typedef struct CK_TOKEN_INFO { + CK_CHAR label[32]; /* blank padded */ + CK_CHAR manufacturerID[32]; /* blank padded */ + CK_CHAR model[16]; /* blank padded */ + CK_CHAR serialNumber[16]; /* blank padded */ + CK_FLAGS flags; /* see below */ + + /* ulMaxSessionCount, ulSessionCount, ulMaxRwSessionCount, + * ulRwSessionCount, ulMaxPinLen, and ulMinPinLen have all been + * changed from CK_USHORT to CK_ULONG for v2.0 */ + CK_ULONG ulMaxSessionCount; /* max open sessions */ + CK_ULONG ulSessionCount; /* sess. now open */ + CK_ULONG ulMaxRwSessionCount; /* max R/W sessions */ + CK_ULONG ulRwSessionCount; /* R/W sess. now open */ + CK_ULONG ulMaxPinLen; /* in bytes */ + CK_ULONG ulMinPinLen; /* in bytes */ + CK_ULONG ulTotalPublicMemory; /* in bytes */ + CK_ULONG ulFreePublicMemory; /* in bytes */ + CK_ULONG ulTotalPrivateMemory; /* in bytes */ + CK_ULONG ulFreePrivateMemory; /* in bytes */ + + /* hardwareVersion, firmwareVersion, and time are new for + * v2.0 */ + CK_VERSION hardwareVersion; /* version of hardware */ + CK_VERSION firmwareVersion; /* version of firmware */ + CK_CHAR utcTime[16]; /* time */ +} CK_TOKEN_INFO; + +/* The flags parameter is defined as follows: + * Bit Flag Mask Meaning + */ +#define CKF_RNG 0x00000001UL /* has random # + * generator */ +#define CKF_WRITE_PROTECTED 0x00000002UL /* token is + * write- + * protected */ +#define CKF_LOGIN_REQUIRED 0x00000004UL /* user must + * login */ +#define CKF_USER_PIN_INITIALIZED 0x00000008UL /* normal user's + * PIN is set */ + +/* CKF_RESTORE_KEY_NOT_NEEDED is new for v2.0. If it is set, + * that means that *every* time the state of cryptographic + * operations of a session is successfully saved, all keys + * needed to continue those operations are stored in the state */ +#define CKF_RESTORE_KEY_NOT_NEEDED 0x00000020UL + +/* CKF_CLOCK_ON_TOKEN is new for v2.0. If it is set, that means + * that the token has some sort of clock. The time on that + * clock is returned in the token info structure */ +#define CKF_CLOCK_ON_TOKEN 0x00000040UL + +/* CKF_PROTECTED_AUTHENTICATION_PATH is new for v2.0. If it is + * set, that means that there is some way for the user to login + * without sending a PIN through the PKCS #11 library itself */ +#define CKF_PROTECTED_AUTHENTICATION_PATH 0x00000100UL + +/* CKF_DUAL_CRYPTO_OPERATIONS is new for v2.0. If it is true, + * that means that a single session with the token can perform + * dual simultaneous cryptographic operations (digest and + * encrypt; decrypt and digest; sign and encrypt; and decrypt + * and sign) */ +#define CKF_DUAL_CRYPTO_OPERATIONS 0x00000200UL + +#define CKF_TOKEN_INITIALIZED 0x00000400UL + +typedef CK_TOKEN_INFO CK_PTR CK_TOKEN_INFO_PTR; + + +/* CK_SESSION_HANDLE is a PKCS #11-assigned value that + * identifies a session */ +typedef CK_ULONG CK_SESSION_HANDLE; + +typedef CK_SESSION_HANDLE CK_PTR CK_SESSION_HANDLE_PTR; + + +/* CK_USER_TYPE enumerates the types of PKCS #11 users */ +/* CK_USER_TYPE has been changed from an enum to a CK_ULONG for + * v2.0 */ +typedef CK_ULONG CK_USER_TYPE; +/* Security Officer */ +#define CKU_SO 0 +/* Normal user */ +#define CKU_USER 1 +/* Context specific */ +#define CKU_CONTEXT_SPECIFIC 2 + + +/* CK_STATE enumerates the session states */ +/* CK_STATE has been changed from an enum to a CK_ULONG for + * v2.0 */ +typedef CK_ULONG CK_STATE; +#define CKS_RO_PUBLIC_SESSION 0 +#define CKS_RO_USER_FUNCTIONS 1 +#define CKS_RW_PUBLIC_SESSION 2 +#define CKS_RW_USER_FUNCTIONS 3 +#define CKS_RW_SO_FUNCTIONS 4 + + +/* CK_SESSION_INFO provides information about a session */ +typedef struct CK_SESSION_INFO { + CK_SLOT_ID slotID; + CK_STATE state; + CK_FLAGS flags; /* see below */ + + /* ulDeviceError was changed from CK_USHORT to CK_ULONG for + * v2.0 */ + CK_ULONG ulDeviceError; /* device-dependent error code */ +} CK_SESSION_INFO; + +/* The flags are defined in the following table: + * Bit Flag Mask Meaning + */ +#define CKF_RW_SESSION 0x00000002UL /* session is r/w */ +#define CKF_SERIAL_SESSION 0x00000004UL /* no parallel */ + +typedef CK_SESSION_INFO CK_PTR CK_SESSION_INFO_PTR; + + +/* CK_OBJECT_HANDLE is a token-specific identifier for an + * object */ +typedef CK_ULONG CK_OBJECT_HANDLE; + +typedef CK_OBJECT_HANDLE CK_PTR CK_OBJECT_HANDLE_PTR; + + +/* CK_OBJECT_CLASS is a value that identifies the classes (or + * types) of objects that PKCS #11 recognizes. It is defined + * as follows: */ +/* CK_OBJECT_CLASS was changed from CK_USHORT to CK_ULONG for + * v2.0 */ +typedef CK_ULONG CK_OBJECT_CLASS; + +/* The following classes of objects are defined: */ +#define CKO_DATA 0x00000000UL +#define CKO_CERTIFICATE 0x00000001UL +#define CKO_PUBLIC_KEY 0x00000002UL +#define CKO_PRIVATE_KEY 0x00000003UL +#define CKO_SECRET_KEY 0x00000004UL +#define CKO_VENDOR_DEFINED 0x80000000UL + +typedef CK_OBJECT_CLASS CK_PTR CK_OBJECT_CLASS_PTR; + + +/* CK_KEY_TYPE is a value that identifies a key type */ +/* CK_KEY_TYPE was changed from CK_USHORT to CK_ULONG for v2.0 */ +typedef CK_ULONG CK_KEY_TYPE; + +/* the following key types are defined: */ +#define CKK_RSA 0x00000000UL +#define CKK_DSA 0x00000001UL +#define CKK_DH 0x00000002UL + +/* CKK_ECDSA and CKK_KEA are new for v2.0 */ + +/* PKCS #11 V2.01 probably won't actually have ECDSA in it */ +#define CKK_ECDSA 0x00000003UL + +#define CKK_KEA 0x00000005UL + +#define CKK_GENERIC_SECRET 0x00000010UL +#define CKK_RC2 0x00000011UL +#define CKK_RC4 0x00000012UL +#define CKK_DES 0x00000013UL +#define CKK_DES2 0x00000014UL +#define CKK_DES3 0x00000015UL + +/* all these key types are new for v2.0 */ +#define CKK_CAST 0x00000016UL +#define CKK_CAST3 0x00000017UL +#define CKK_CAST5 0x00000018UL +#define CKK_CAST128 0x00000018 /* CAST128=CAST5 */ +#define CKK_RC5 0x00000019UL +#define CKK_IDEA 0x0000001AUL +#define CKK_SKIPJACK 0x0000001BUL +#define CKK_BATON 0x0000001CUL +#define CKK_JUNIPER 0x0000001DUL +#define CKK_CDMF 0x0000001EUL + +/* all these key types are new for v2.11 */ +#define CKK_AES 0x0000001FUL + +#define CKK_VENDOR_DEFINED 0x80000000UL + + +/* CK_CERTIFICATE_TYPE is a value that identifies a certificate + * type */ +/* CK_CERTIFICATE_TYPE was changed from CK_USHORT to CK_ULONG + * for v2.0 */ +typedef CK_ULONG CK_CERTIFICATE_TYPE; +typedef CK_CERTIFICATE_TYPE CK_PTR CK_CERTIFICATE_TYPE_PTR; + +/* The following certificate types are defined: */ +#define CKC_X_509 0x00000000UL +#define CKC_VENDOR_DEFINED 0x80000000UL + + +/* CK_ATTRIBUTE_TYPE is a value that identifies an attribute + * type */ +/* CK_ATTRIBUTE_TYPE was changed from CK_USHORT to CK_ULONG for + * v2.0 */ +typedef CK_ULONG CK_ATTRIBUTE_TYPE; + +/* The following attribute types are defined: */ +#define CKA_CLASS 0x00000000UL +#define CKA_TOKEN 0x00000001UL +#define CKA_PRIVATE 0x00000002UL +#define CKA_LABEL 0x00000003UL +#define CKA_APPLICATION 0x00000010UL +#define CKA_VALUE 0x00000011UL +#define CKA_OBJECT_ID 0x00000012UL +#define CKA_CERTIFICATE_TYPE 0x00000080UL +#define CKA_ISSUER 0x00000081UL +#define CKA_SERIAL_NUMBER 0x00000082UL +#define CKA_KEY_TYPE 0x00000100UL +#define CKA_SUBJECT 0x00000101UL +#define CKA_ID 0x00000102UL +#define CKA_SENSITIVE 0x00000103UL +#define CKA_ENCRYPT 0x00000104UL +#define CKA_DECRYPT 0x00000105UL +#define CKA_WRAP 0x00000106UL +#define CKA_UNWRAP 0x00000107UL +#define CKA_SIGN 0x00000108UL +#define CKA_SIGN_RECOVER 0x00000109UL +#define CKA_VERIFY 0x0000010AUL +#define CKA_VERIFY_RECOVER 0x0000010BUL +#define CKA_DERIVE 0x0000010CUL +#define CKA_START_DATE 0x00000110UL +#define CKA_END_DATE 0x00000111UL +#define CKA_MODULUS 0x00000120UL +#define CKA_MODULUS_BITS 0x00000121UL +#define CKA_PUBLIC_EXPONENT 0x00000122UL +#define CKA_PRIVATE_EXPONENT 0x00000123UL +#define CKA_PRIME_1 0x00000124UL +#define CKA_PRIME_2 0x00000125UL +#define CKA_EXPONENT_1 0x00000126UL +#define CKA_EXPONENT_2 0x00000127UL +#define CKA_COEFFICIENT 0x00000128UL +#define CKA_PRIME 0x00000130UL +#define CKA_SUBPRIME 0x00000131UL +#define CKA_BASE 0x00000132UL +#define CKA_VALUE_BITS 0x00000160UL +#define CKA_VALUE_LEN 0x00000161UL + +/* CKA_EXTRACTABLE, CKA_LOCAL, CKA_NEVER_EXTRACTABLE, + * CKA_ALWAYS_SENSITIVE, and CKA_MODIFIABLE are new for v2.0 */ +#define CKA_EXTRACTABLE 0x00000162UL +#define CKA_LOCAL 0x00000163UL +#define CKA_NEVER_EXTRACTABLE 0x00000164UL +#define CKA_ALWAYS_SENSITIVE 0x00000165UL +#define CKA_MODIFIABLE 0x00000170UL + +/* New in 2.2 */ +#define CKA_COPYABLE 0x00000171UL +#define CKA_DESTROYABLE 0x00000172UL +#define CKA_ECDSA_PARAMS 0x00000180UL +#define CKA_EC_PARAMS 0x00000180UL +#define CKA_EC_POINT 0x00000181UL +#define CKA_ALWAYS_AUTHENTICATE 0x00000202UL + +#define CKA_VENDOR_DEFINED 0x80000000UL + +/* CK_ATTRIBUTE is a structure that includes the type, length + * and value of an attribute */ +typedef struct CK_ATTRIBUTE { + CK_ATTRIBUTE_TYPE type; + CK_VOID_PTR pValue; + + /* ulValueLen went from CK_USHORT to CK_ULONG for v2.0 */ + CK_ULONG ulValueLen; /* in bytes */ +} CK_ATTRIBUTE; + +typedef CK_ATTRIBUTE CK_PTR CK_ATTRIBUTE_PTR; + + +/* CK_DATE is a structure that defines a date */ +typedef struct CK_DATE{ + CK_CHAR year[4]; /* the year ("1900" - "9999") */ + CK_CHAR month[2]; /* the month ("01" - "12") */ + CK_CHAR day[2]; /* the day ("01" - "31") */ +} CK_DATE; + + +/* CK_MECHANISM_TYPE is a value that identifies a mechanism + * type */ +/* CK_MECHANISM_TYPE was changed from CK_USHORT to CK_ULONG for + * v2.0 */ +typedef CK_ULONG CK_MECHANISM_TYPE; + +/* the following mechanism types are defined: */ +#define CKM_RSA_PKCS_KEY_PAIR_GEN 0x00000000UL +#define CKM_RSA_PKCS 0x00000001UL +#define CKM_RSA_9796 0x00000002UL +#define CKM_RSA_X_509 0x00000003UL + +/* CKM_MD2_RSA_PKCS, CKM_MD5_RSA_PKCS, and CKM_SHA1_RSA_PKCS + * are new for v2.0. They are mechanisms which hash and sign */ +#define CKM_MD2_RSA_PKCS 0x00000004UL +#define CKM_MD5_RSA_PKCS 0x00000005UL +#define CKM_SHA1_RSA_PKCS 0x00000006UL + +/* Added for 2.4 */ +#define CKM_RSA_PKCS_PSS 0x0000000DUL +#define CKM_SHA1_RSA_PKCS_PSS 0x0000000EUL +/* Added for 2.4 */ + +#define CKM_DSA_KEY_PAIR_GEN 0x00000010UL +#define CKM_DSA 0x00000011UL +#define CKM_DSA_SHA1 0x00000012UL +#define CKM_DH_PKCS_KEY_PAIR_GEN 0x00000020UL +#define CKM_DH_PKCS_DERIVE 0x00000021UL + +/* Added for 2.4 */ +#define CKM_SHA256_RSA_PKCS 0x00000040UL +#define CKM_SHA384_RSA_PKCS 0x00000041UL +#define CKM_SHA512_RSA_PKCS 0x00000042UL +#define CKM_SHA256_RSA_PKCS_PSS 0x00000043UL +#define CKM_SHA384_RSA_PKCS_PSS 0x00000044UL +#define CKM_SHA512_RSA_PKCS_PSS 0x00000045UL +/* Added for 2.4 */ + +#define CKM_RC2_KEY_GEN 0x00000100UL +#define CKM_RC2_ECB 0x00000101UL +#define CKM_RC2_CBC 0x00000102UL +#define CKM_RC2_MAC 0x00000103UL + +/* CKM_RC2_MAC_GENERAL and CKM_RC2_CBC_PAD are new for v2.0 */ +#define CKM_RC2_MAC_GENERAL 0x00000104UL +#define CKM_RC2_CBC_PAD 0x00000105UL + +#define CKM_RC4_KEY_GEN 0x00000110UL +#define CKM_RC4 0x00000111UL +#define CKM_DES_KEY_GEN 0x00000120UL +#define CKM_DES_ECB 0x00000121UL +#define CKM_DES_CBC 0x00000122UL +#define CKM_DES_MAC 0x00000123UL + +/* CKM_DES_MAC_GENERAL and CKM_DES_CBC_PAD are new for v2.0 */ +#define CKM_DES_MAC_GENERAL 0x00000124UL +#define CKM_DES_CBC_PAD 0x00000125UL + +#define CKM_DES2_KEY_GEN 0x00000130UL +#define CKM_DES3_KEY_GEN 0x00000131UL +#define CKM_DES3_ECB 0x00000132UL +#define CKM_DES3_CBC 0x00000133UL +#define CKM_DES3_MAC 0x00000134UL + +/* CKM_DES3_MAC_GENERAL, CKM_DES3_CBC_PAD, CKM_CDMF_KEY_GEN, + * CKM_CDMF_ECB, CKM_CDMF_CBC, CKM_CDMF_MAC, + * CKM_CDMF_MAC_GENERAL, and CKM_CDMF_CBC_PAD are new for v2.0 */ +#define CKM_DES3_MAC_GENERAL 0x00000135UL +#define CKM_DES3_CBC_PAD 0x00000136UL +#define CKM_CDMF_KEY_GEN 0x00000140UL +#define CKM_CDMF_ECB 0x00000141UL +#define CKM_CDMF_CBC 0x00000142UL +#define CKM_CDMF_MAC 0x00000143UL +#define CKM_CDMF_MAC_GENERAL 0x00000144UL +#define CKM_CDMF_CBC_PAD 0x00000145UL + +#define CKM_MD2 0x00000200UL + +/* CKM_MD2_HMAC and CKM_MD2_HMAC_GENERAL are new for v2.0 */ +#define CKM_MD2_HMAC 0x00000201UL +#define CKM_MD2_HMAC_GENERAL 0x00000202UL + +#define CKM_MD5 0x00000210UL + +/* CKM_MD5_HMAC and CKM_MD5_HMAC_GENERAL are new for v2.0 */ +#define CKM_MD5_HMAC 0x00000211UL +#define CKM_MD5_HMAC_GENERAL 0x00000212UL + +#define CKM_SHA_1 0x00000220UL + +/* CKM_SHA_1_HMAC and CKM_SHA_1_HMAC_GENERAL are new for v2.0 */ +#define CKM_SHA_1_HMAC 0x00000221UL +#define CKM_SHA_1_HMAC_GENERAL 0x00000222UL + +/* Added for 2.4 */ +#define CKM_SHA256 0x00000250UL +#define CKM_SHA384 0x00000260UL +#define CKM_SHA512 0x00000270UL +/* Added for 2.4 */ + +/* All of the following mechanisms are new for v2.0 */ +/* Note that CAST128 and CAST5 are the same algorithm */ +#define CKM_CAST_KEY_GEN 0x00000300UL +#define CKM_CAST_ECB 0x00000301UL +#define CKM_CAST_CBC 0x00000302UL +#define CKM_CAST_MAC 0x00000303UL +#define CKM_CAST_MAC_GENERAL 0x00000304UL +#define CKM_CAST_CBC_PAD 0x00000305UL +#define CKM_CAST3_KEY_GEN 0x00000310UL +#define CKM_CAST3_ECB 0x00000311UL +#define CKM_CAST3_CBC 0x00000312UL +#define CKM_CAST3_MAC 0x00000313UL +#define CKM_CAST3_MAC_GENERAL 0x00000314UL +#define CKM_CAST3_CBC_PAD 0x00000315UL +#define CKM_CAST5_KEY_GEN 0x00000320UL +#define CKM_CAST128_KEY_GEN 0x00000320UL +#define CKM_CAST5_ECB 0x00000321UL +#define CKM_CAST128_ECB 0x00000321UL +#define CKM_CAST5_CBC 0x00000322UL +#define CKM_CAST128_CBC 0x00000322UL +#define CKM_CAST5_MAC 0x00000323UL +#define CKM_CAST128_MAC 0x00000323UL +#define CKM_CAST5_MAC_GENERAL 0x00000324UL +#define CKM_CAST128_MAC_GENERAL 0x00000324UL +#define CKM_CAST5_CBC_PAD 0x00000325UL +#define CKM_CAST128_CBC_PAD 0x00000325UL +#define CKM_RC5_KEY_GEN 0x00000330UL +#define CKM_RC5_ECB 0x00000331UL +#define CKM_RC5_CBC 0x00000332UL +#define CKM_RC5_MAC 0x00000333UL +#define CKM_RC5_MAC_GENERAL 0x00000334UL +#define CKM_RC5_CBC_PAD 0x00000335UL +#define CKM_IDEA_KEY_GEN 0x00000340UL +#define CKM_IDEA_ECB 0x00000341UL +#define CKM_IDEA_CBC 0x00000342UL +#define CKM_IDEA_MAC 0x00000343UL +#define CKM_IDEA_MAC_GENERAL 0x00000344UL +#define CKM_IDEA_CBC_PAD 0x00000345UL +#define CKM_GENERIC_SECRET_KEY_GEN 0x00000350UL +#define CKM_CONCATENATE_BASE_AND_KEY 0x00000360UL +#define CKM_CONCATENATE_BASE_AND_DATA 0x00000362UL +#define CKM_CONCATENATE_DATA_AND_BASE 0x00000363UL +#define CKM_XOR_BASE_AND_DATA 0x00000364UL +#define CKM_EXTRACT_KEY_FROM_KEY 0x00000365UL +#define CKM_SSL3_PRE_MASTER_KEY_GEN 0x00000370UL +#define CKM_SSL3_MASTER_KEY_DERIVE 0x00000371UL +#define CKM_SSL3_KEY_AND_MAC_DERIVE 0x00000372UL +#define CKM_SSL3_MD5_MAC 0x00000380UL +#define CKM_SSL3_SHA1_MAC 0x00000381UL +#define CKM_MD5_KEY_DERIVATION 0x00000390UL +#define CKM_MD2_KEY_DERIVATION 0x00000391UL +#define CKM_SHA1_KEY_DERIVATION 0x00000392UL +#define CKM_PBE_MD2_DES_CBC 0x000003A0UL +#define CKM_PBE_MD5_DES_CBC 0x000003A1UL +#define CKM_PBE_MD5_CAST_CBC 0x000003A2UL +#define CKM_PBE_MD5_CAST3_CBC 0x000003A3UL +#define CKM_PBE_MD5_CAST5_CBC 0x000003A4UL +#define CKM_PBE_MD5_CAST128_CBC 0x000003A4UL +#define CKM_PBE_SHA1_CAST5_CBC 0x000003A5UL +#define CKM_PBE_SHA1_CAST128_CBC 0x000003A5UL +#define CKM_PBE_SHA1_RC4_128 0x000003A6UL +#define CKM_PBE_SHA1_RC4_40 0x000003A7UL +#define CKM_PBE_SHA1_DES3_EDE_CBC 0x000003A8UL +#define CKM_PBE_SHA1_DES2_EDE_CBC 0x000003A9UL +#define CKM_PBE_SHA1_RC2_128_CBC 0x000003AAUL +#define CKM_PBE_SHA1_RC2_40_CBC 0x000003ABUL +#define CKM_PBA_SHA1_WITH_SHA1_HMAC 0x000003C0UL +#define CKM_KEY_WRAP_LYNKS 0x00000400UL +#define CKM_KEY_WRAP_SET_OAEP 0x00000401UL + +/* Fortezza mechanisms */ +#define CKM_SKIPJACK_KEY_GEN 0x00001000UL +#define CKM_SKIPJACK_ECB64 0x00001001UL +#define CKM_SKIPJACK_CBC64 0x00001002UL +#define CKM_SKIPJACK_OFB64 0x00001003UL +#define CKM_SKIPJACK_CFB64 0x00001004UL +#define CKM_SKIPJACK_CFB32 0x00001005UL +#define CKM_SKIPJACK_CFB16 0x00001006UL +#define CKM_SKIPJACK_CFB8 0x00001007UL +#define CKM_SKIPJACK_WRAP 0x00001008UL +#define CKM_SKIPJACK_PRIVATE_WRAP 0x00001009UL +#define CKM_SKIPJACK_RELAYX 0x0000100aUL +#define CKM_KEA_KEY_PAIR_GEN 0x00001010UL +#define CKM_KEA_KEY_DERIVE 0x00001011UL +#define CKM_FORTEZZA_TIMESTAMP 0x00001020UL +#define CKM_BATON_KEY_GEN 0x00001030UL +#define CKM_BATON_ECB128 0x00001031UL +#define CKM_BATON_ECB96 0x00001032UL +#define CKM_BATON_CBC128 0x00001033UL +#define CKM_BATON_COUNTER 0x00001034UL +#define CKM_BATON_SHUFFLE 0x00001035UL +#define CKM_BATON_WRAP 0x00001036UL + +/* PKCS #11 V2.01 probably won't actually have ECDSA in it */ +#define CKM_EC_KEY_PAIR_GEN 0x00001040UL +//#define CKM_ECDSA_KEY_PAIR_GEN 0x00001040 // Deprecated in 2.11 +#define CKM_ECDSA 0x00001041UL +#define CKM_ECDSA_SHA1 0x00001042UL +/* NOT STANDARD */ +#define CKM_ECDSA_SHA224 0x00001043UL +#define CKM_ECDSA_SHA256 0x00001044UL +#define CKM_ECDSA_SHA384 0x00001045UL +#define CKM_ECDSA_SHA512 0x00001046UL +/* NOT STANDARD */ + +/* Added for 2.4 */ +#define CKM_ECDH1_DERIVE 0x00001050UL +#define CKM_ECDH1_COFACTOR_DERIVE 0x00001051UL +/* Added for 2.4 */ + +#define CKM_JUNIPER_KEY_GEN 0x00001060UL +#define CKM_JUNIPER_ECB128 0x00001061UL +#define CKM_JUNIPER_CBC128 0x00001062UL +#define CKM_JUNIPER_COUNTER 0x00001063UL +#define CKM_JUNIPER_SHUFFLE 0x00001064UL +#define CKM_JUNIPER_WRAP 0x00001065UL +#define CKM_FASTHASH 0x00001070UL + +#define CKM_AES_KEY_GEN 0x00001080UL +#define CKM_AES_ECB 0x00001081UL +#define CKM_AES_CBC 0x00001082UL +#define CKM_AES_MAC 0x00001083UL +#define CKM_AES_MAC_GENERAL 0x00001084UL +#define CKM_AES_CBC_PAD 0x00001085UL + +#define CKM_VENDOR_DEFINED 0x80000000UL + +typedef CK_MECHANISM_TYPE CK_PTR CK_MECHANISM_TYPE_PTR; + + +/* CK_MECHANISM is a structure that specifies a particular + * mechanism */ +typedef struct CK_MECHANISM { + CK_MECHANISM_TYPE mechanism; + CK_VOID_PTR pParameter; + + /* ulParameterLen was changed from CK_USHORT to CK_ULONG for + * v2.0 */ + CK_ULONG ulParameterLen; /* in bytes */ +} CK_MECHANISM; + +typedef CK_MECHANISM CK_PTR CK_MECHANISM_PTR; + + +/* CK_MECHANISM_INFO provides information about a particular + * mechanism */ +typedef struct CK_MECHANISM_INFO { + CK_ULONG ulMinKeySize; + CK_ULONG ulMaxKeySize; + CK_FLAGS flags; +} CK_MECHANISM_INFO; + +/* The flags are defined as follows: + * Bit Flag Mask Meaning */ +#define CKF_HW 0x00000001UL /* performed by HW */ + +/* The flags CKF_ENCRYPT, CKF_DECRYPT, CKF_DIGEST, CKF_SIGN, + * CKG_SIGN_RECOVER, CKF_VERIFY, CKF_VERIFY_RECOVER, + * CKF_GENERATE, CKF_GENERATE_KEY_PAIR, CKF_WRAP, CKF_UNWRAP, + * and CKF_DERIVE are new for v2.0. They specify whether or not + * a mechanism can be used for a particular task */ +#define CKF_ENCRYPT 0x00000100UL +#define CKF_DECRYPT 0x00000200UL +#define CKF_DIGEST 0x00000400UL +#define CKF_SIGN 0x00000800UL +#define CKF_SIGN_RECOVER 0x00001000UL +#define CKF_VERIFY 0x00002000UL +#define CKF_VERIFY_RECOVER 0x00004000UL +#define CKF_GENERATE 0x00008000UL +#define CKF_GENERATE_KEY_PAIR 0x00010000UL +#define CKF_WRAP 0x00020000UL +#define CKF_UNWRAP 0x00040000UL +#define CKF_DERIVE 0x00080000UL + +#define CKF_EXTENSION 0x80000000UL /* FALSE for 2.01 */ + +typedef CK_MECHANISM_INFO CK_PTR CK_MECHANISM_INFO_PTR; + + +/* CK_RV is a value that identifies the return value of a + * PKCS #11 function */ +/* CK_RV was changed from CK_USHORT to CK_ULONG for v2.0 */ +typedef CK_ULONG CK_RV; + +#define CKR_OK 0x00000000UL +#define CKR_CANCEL 0x00000001UL +#define CKR_HOST_MEMORY 0x00000002UL +#define CKR_SLOT_ID_INVALID 0x00000003UL + +/* CKR_FLAGS_INVALID was removed for v2.0 */ + +/* CKR_GENERAL_ERROR and CKR_FUNCTION_FAILED are new for v2.0 */ +#define CKR_GENERAL_ERROR 0x00000005UL +#define CKR_FUNCTION_FAILED 0x00000006UL + +/* CKR_ARGUMENTS_BAD, CKR_NO_EVENT, CKR_NEED_TO_CREATE_THREADS, + * and CKR_CANT_LOCK are new for v2.01 */ +#define CKR_ARGUMENTS_BAD 0x00000007UL +#define CKR_NO_EVENT 0x00000008UL +#define CKR_NEED_TO_CREATE_THREADS 0x00000009UL +#define CKR_CANT_LOCK 0x0000000AUL + +#define CKR_ATTRIBUTE_READ_ONLY 0x00000010UL +#define CKR_ATTRIBUTE_SENSITIVE 0x00000011UL +#define CKR_ATTRIBUTE_TYPE_INVALID 0x00000012UL +#define CKR_ATTRIBUTE_VALUE_INVALID 0x00000013UL +#define CKR_COPY_PROHIBITED 0x0000001AUL +#define CKR_ACTION_PROHIBITED 0x0000001BUL +#define CKR_DATA_INVALID 0x00000020UL +#define CKR_DATA_LEN_RANGE 0x00000021UL +#define CKR_DEVICE_ERROR 0x00000030UL +#define CKR_DEVICE_MEMORY 0x00000031UL +#define CKR_DEVICE_REMOVED 0x00000032UL +#define CKR_ENCRYPTED_DATA_INVALID 0x00000040UL +#define CKR_ENCRYPTED_DATA_LEN_RANGE 0x00000041UL +#define CKR_FUNCTION_CANCELED 0x00000050UL +#define CKR_FUNCTION_NOT_PARALLEL 0x00000051UL + +/* CKR_FUNCTION_NOT_SUPPORTED is new for v2.0 */ +#define CKR_FUNCTION_NOT_SUPPORTED 0x00000054UL + +#define CKR_KEY_HANDLE_INVALID 0x00000060UL + +/* CKR_KEY_SENSITIVE was removed for v2.0 */ + +#define CKR_KEY_SIZE_RANGE 0x00000062UL +#define CKR_KEY_TYPE_INCONSISTENT 0x00000063UL + +/* CKR_KEY_NOT_NEEDED, CKR_KEY_CHANGED, CKR_KEY_NEEDED, + * CKR_KEY_INDIGESTIBLE, CKR_KEY_FUNCTION_NOT_PERMITTED, + * CKR_KEY_NOT_WRAPPABLE, and CKR_KEY_UNEXTRACTABLE are new for + * v2.0 */ +#define CKR_KEY_NOT_NEEDED 0x00000064UL +#define CKR_KEY_CHANGED 0x00000065UL +#define CKR_KEY_NEEDED 0x00000066UL +#define CKR_KEY_INDIGESTIBLE 0x00000067UL +#define CKR_KEY_FUNCTION_NOT_PERMITTED 0x00000068UL +#define CKR_KEY_NOT_WRAPPABLE 0x00000069UL +#define CKR_KEY_UNEXTRACTABLE 0x0000006AUL + +#define CKR_MECHANISM_INVALID 0x00000070UL +#define CKR_MECHANISM_PARAM_INVALID 0x00000071UL + +/* CKR_OBJECT_CLASS_INCONSISTENT and CKR_OBJECT_CLASS_INVALID + * were removed for v2.0 */ +#define CKR_OBJECT_HANDLE_INVALID 0x00000082UL +#define CKR_OPERATION_ACTIVE 0x00000090UL +#define CKR_OPERATION_NOT_INITIALIZED 0x00000091UL +#define CKR_PIN_INCORRECT 0x000000A0UL +#define CKR_PIN_INVALID 0x000000A1UL +#define CKR_PIN_LEN_RANGE 0x000000A2UL + +/* CKR_PIN_EXPIRED and CKR_PIN_LOCKED are new for v2.0 */ +#define CKR_PIN_EXPIRED 0x000000A3UL +#define CKR_PIN_LOCKED 0x000000A4UL + +#define CKR_SESSION_CLOSED 0x000000B0UL +#define CKR_SESSION_COUNT 0x000000B1UL +#define CKR_SESSION_HANDLE_INVALID 0x000000B3UL +#define CKR_SESSION_PARALLEL_NOT_SUPPORTED 0x000000B4UL +#define CKR_SESSION_READ_ONLY 0x000000B5UL +#define CKR_SESSION_EXISTS 0x000000B6UL + +/* CKR_SESSION_READ_ONLY_EXISTS and + * CKR_SESSION_READ_WRITE_SO_EXISTS are new for v2.0 */ +#define CKR_SESSION_READ_ONLY_EXISTS 0x000000B7UL +#define CKR_SESSION_READ_WRITE_SO_EXISTS 0x000000B8UL + +#define CKR_SIGNATURE_INVALID 0x000000C0UL +#define CKR_SIGNATURE_LEN_RANGE 0x000000C1UL +#define CKR_TEMPLATE_INCOMPLETE 0x000000D0UL +#define CKR_TEMPLATE_INCONSISTENT 0x000000D1UL +#define CKR_TOKEN_NOT_PRESENT 0x000000E0UL +#define CKR_TOKEN_NOT_RECOGNIZED 0x000000E1UL +#define CKR_TOKEN_WRITE_PROTECTED 0x000000E2UL +#define CKR_UNWRAPPING_KEY_HANDLE_INVALID 0x000000F0UL +#define CKR_UNWRAPPING_KEY_SIZE_RANGE 0x000000F1UL +#define CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT 0x000000F2UL +#define CKR_USER_ALREADY_LOGGED_IN 0x00000100UL +#define CKR_USER_NOT_LOGGED_IN 0x00000101UL +#define CKR_USER_PIN_NOT_INITIALIZED 0x00000102UL +#define CKR_USER_TYPE_INVALID 0x00000103UL + +/* CKR_USER_ANOTHER_ALREADY_LOGGED_IN and CKR_USER_TOO_MANY_TYPES + * are new to v2.01 */ +#define CKR_USER_ANOTHER_ALREADY_LOGGED_IN 0x00000104UL +#define CKR_USER_TOO_MANY_TYPES 0x00000105UL + +#define CKR_WRAPPED_KEY_INVALID 0x00000110UL +#define CKR_WRAPPED_KEY_LEN_RANGE 0x00000112UL +#define CKR_WRAPPING_KEY_HANDLE_INVALID 0x00000113UL +#define CKR_WRAPPING_KEY_SIZE_RANGE 0x00000114UL +#define CKR_WRAPPING_KEY_TYPE_INCONSISTENT 0x00000115UL +#define CKR_RANDOM_SEED_NOT_SUPPORTED 0x00000120UL + +/* These are new to v2.0 */ +#define CKR_RANDOM_NO_RNG 0x00000121UL +/* Next two are new for v2.2 */ +#define CKR_DOMAIN_PARAMS_INVALID 0x00000130UL +#define CKR_CURVE_NOT_SUPPORTED 0x00000140UL +/* ************************* */ +#define CKR_BUFFER_TOO_SMALL 0x00000150UL +#define CKR_SAVED_STATE_INVALID 0x00000160UL +#define CKR_INFORMATION_SENSITIVE 0x00000170UL +#define CKR_STATE_UNSAVEABLE 0x00000180UL + +/* These are new to v2.01 */ +#define CKR_CRYPTOKI_NOT_INITIALIZED 0x00000190UL +#define CKR_CRYPTOKI_ALREADY_INITIALIZED 0x00000191UL +#define CKR_MUTEX_BAD 0x000001A0UL +#define CKR_MUTEX_NOT_LOCKED 0x000001A1UL + +#define CKR_VENDOR_DEFINED 0x80000000UL + + +/* CK_NOTIFY is an application callback that processes events */ +typedef CK_CALLBACK_FUNCTION(CK_RV, CK_NOTIFY)( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_NOTIFICATION event, + CK_VOID_PTR pApplication /* passed to C_OpenSession */ +); + + +/* CK_FUNCTION_LIST is a structure holding a PKCS #11 spec + * version and pointers of appropriate types to all the + * PKCS #11 functions */ +/* CK_FUNCTION_LIST is new for v2.0 */ +typedef struct CK_FUNCTION_LIST CK_FUNCTION_LIST; + +typedef CK_FUNCTION_LIST CK_PTR CK_FUNCTION_LIST_PTR; + +typedef CK_FUNCTION_LIST_PTR CK_PTR CK_FUNCTION_LIST_PTR_PTR; + + +/* CK_CREATEMUTEX is an application callback for creating a + * mutex object */ +typedef CK_CALLBACK_FUNCTION(CK_RV, CK_CREATEMUTEX)( + CK_VOID_PTR_PTR ppMutex /* location to receive ptr to mutex */ +); + + +/* CK_DESTROYMUTEX is an application callback for destroying a + * mutex object */ +typedef CK_CALLBACK_FUNCTION(CK_RV, CK_DESTROYMUTEX)( + CK_VOID_PTR pMutex /* pointer to mutex */ +); + + +/* CK_LOCKMUTEX is an application callback for locking a mutex */ +typedef CK_CALLBACK_FUNCTION(CK_RV, CK_LOCKMUTEX)( + CK_VOID_PTR pMutex /* pointer to mutex */ +); + + +/* CK_UNLOCKMUTEX is an application callback for unlocking a + * mutex */ +typedef CK_CALLBACK_FUNCTION(CK_RV, CK_UNLOCKMUTEX)( + CK_VOID_PTR pMutex /* pointer to mutex */ +); + + +/* CK_C_INITIALIZE_ARGS provides the optional arguments to + * C_Initialize */ +typedef struct CK_C_INITIALIZE_ARGS { + CK_CREATEMUTEX CreateMutex; + CK_DESTROYMUTEX DestroyMutex; + CK_LOCKMUTEX LockMutex; + CK_UNLOCKMUTEX UnlockMutex; + CK_FLAGS flags; + CK_VOID_PTR pReserved; +} CK_C_INITIALIZE_ARGS; + +/* flags: bit flags that provide capabilities of the slot + * Bit Flag Mask Meaning + */ +#define CKF_LIBRARY_CANT_CREATE_OS_THREADS 0x00000001UL +#define CKF_OS_LOCKING_OK 0x00000002UL + +typedef CK_C_INITIALIZE_ARGS CK_PTR CK_C_INITIALIZE_ARGS_PTR; + + +/* additional flags for parameters to functions */ + +/* CKF_DONT_BLOCK is for the function C_WaitForSlotEvent */ +#define CKF_DONT_BLOCK 1 + + +/* CK_KEA_DERIVE_PARAMS provides the parameters to the + * CKM_KEA_DERIVE mechanism */ +/* CK_KEA_DERIVE_PARAMS is new for v2.0 */ +typedef struct CK_KEA_DERIVE_PARAMS { + CK_BBOOL isSender; + CK_ULONG ulRandomLen; + CK_BYTE_PTR pRandomA; + CK_BYTE_PTR pRandomB; + CK_ULONG ulPublicDataLen; + CK_BYTE_PTR pPublicData; +} CK_KEA_DERIVE_PARAMS; + +typedef CK_KEA_DERIVE_PARAMS CK_PTR CK_KEA_DERIVE_PARAMS_PTR; + + +/* CK_RC2_PARAMS provides the parameters to the CKM_RC2_ECB and + * CKM_RC2_MAC mechanisms. An instance of CK_RC2_PARAMS just + * holds the effective keysize */ +typedef CK_ULONG CK_RC2_PARAMS; + +typedef CK_RC2_PARAMS CK_PTR CK_RC2_PARAMS_PTR; + + +/* CK_RC2_CBC_PARAMS provides the parameters to the CKM_RC2_CBC + * mechanism */ +typedef struct CK_RC2_CBC_PARAMS { + /* ulEffectiveBits was changed from CK_USHORT to CK_ULONG for + * v2.0 */ + CK_ULONG ulEffectiveBits; /* effective bits (1-1024) */ + + CK_BYTE iv[8]; /* IV for CBC mode */ +} CK_RC2_CBC_PARAMS; + +typedef CK_RC2_CBC_PARAMS CK_PTR CK_RC2_CBC_PARAMS_PTR; + + +/* CK_RC2_MAC_GENERAL_PARAMS provides the parameters for the + * CKM_RC2_MAC_GENERAL mechanism */ +/* CK_RC2_MAC_GENERAL_PARAMS is new for v2.0 */ +typedef struct CK_RC2_MAC_GENERAL_PARAMS { + CK_ULONG ulEffectiveBits; /* effective bits (1-1024) */ + CK_ULONG ulMacLength; /* Length of MAC in bytes */ +} CK_RC2_MAC_GENERAL_PARAMS; + +typedef CK_RC2_MAC_GENERAL_PARAMS CK_PTR \ + CK_RC2_MAC_GENERAL_PARAMS_PTR; + + +/* CK_RC5_PARAMS provides the parameters to the CKM_RC5_ECB and + * CKM_RC5_MAC mechanisms */ +/* CK_RC5_PARAMS is new for v2.0 */ +typedef struct CK_RC5_PARAMS { + CK_ULONG ulWordsize; /* wordsize in bits */ + CK_ULONG ulRounds; /* number of rounds */ +} CK_RC5_PARAMS; + +typedef CK_RC5_PARAMS CK_PTR CK_RC5_PARAMS_PTR; + + +/* CK_RC5_CBC_PARAMS provides the parameters to the CKM_RC5_CBC + * mechanism */ +/* CK_RC5_CBC_PARAMS is new for v2.0 */ +typedef struct CK_RC5_CBC_PARAMS { + CK_ULONG ulWordsize; /* wordsize in bits */ + CK_ULONG ulRounds; /* number of rounds */ + CK_BYTE_PTR pIv; /* pointer to IV */ + CK_ULONG ulIvLen; /* length of IV in bytes */ +} CK_RC5_CBC_PARAMS; + +typedef CK_RC5_CBC_PARAMS CK_PTR CK_RC5_CBC_PARAMS_PTR; + + +/* CK_RC5_MAC_GENERAL_PARAMS provides the parameters for the + * CKM_RC5_MAC_GENERAL mechanism */ +/* CK_RC5_MAC_GENERAL_PARAMS is new for v2.0 */ +typedef struct CK_RC5_MAC_GENERAL_PARAMS { + CK_ULONG ulWordsize; /* wordsize in bits */ + CK_ULONG ulRounds; /* number of rounds */ + CK_ULONG ulMacLength; /* Length of MAC in bytes */ +} CK_RC5_MAC_GENERAL_PARAMS; + +typedef CK_RC5_MAC_GENERAL_PARAMS CK_PTR \ + CK_RC5_MAC_GENERAL_PARAMS_PTR; + + +/* CK_MAC_GENERAL_PARAMS provides the parameters to most block + * ciphers' MAC_GENERAL mechanisms. Its value is the length of + * the MAC */ +/* CK_MAC_GENERAL_PARAMS is new for v2.0 */ +typedef CK_ULONG CK_MAC_GENERAL_PARAMS; + +typedef CK_MAC_GENERAL_PARAMS CK_PTR CK_MAC_GENERAL_PARAMS_PTR; + + +/* CK_SKIPJACK_PRIVATE_WRAP_PARAMS provides the parameters to the + * CKM_SKIPJACK_PRIVATE_WRAP mechanism */ +/* CK_SKIPJACK_PRIVATE_WRAP_PARAMS is new for v2.0 */ +typedef struct CK_SKIPJACK_PRIVATE_WRAP_PARAMS { + CK_ULONG ulPasswordLen; + CK_BYTE_PTR pPassword; + CK_ULONG ulPublicDataLen; + CK_BYTE_PTR pPublicData; + CK_ULONG ulPAndGLen; + CK_ULONG ulQLen; + CK_ULONG ulRandomLen; + CK_BYTE_PTR pRandomA; + CK_BYTE_PTR pPrimeP; + CK_BYTE_PTR pBaseG; + CK_BYTE_PTR pSubprimeQ; +} CK_SKIPJACK_PRIVATE_WRAP_PARAMS; + +typedef CK_SKIPJACK_PRIVATE_WRAP_PARAMS CK_PTR \ + CK_SKIPJACK_PRIVATE_WRAP_PTR; + + +/* CK_SKIPJACK_RELAYX_PARAMS provides the parameters to the + * CKM_SKIPJACK_RELAYX mechanism */ +/* CK_SKIPJACK_RELAYX_PARAMS is new for v2.0 */ +typedef struct CK_SKIPJACK_RELAYX_PARAMS { + CK_ULONG ulOldWrappedXLen; + CK_BYTE_PTR pOldWrappedX; + CK_ULONG ulOldPasswordLen; + CK_BYTE_PTR pOldPassword; + CK_ULONG ulOldPublicDataLen; + CK_BYTE_PTR pOldPublicData; + CK_ULONG ulOldRandomLen; + CK_BYTE_PTR pOldRandomA; + CK_ULONG ulNewPasswordLen; + CK_BYTE_PTR pNewPassword; + CK_ULONG ulNewPublicDataLen; + CK_BYTE_PTR pNewPublicData; + CK_ULONG ulNewRandomLen; + CK_BYTE_PTR pNewRandomA; +} CK_SKIPJACK_RELAYX_PARAMS; + +typedef CK_SKIPJACK_RELAYX_PARAMS CK_PTR \ + CK_SKIPJACK_RELAYX_PARAMS_PTR; + + +typedef struct CK_PBE_PARAMS { + CK_CHAR_PTR pInitVector; + CK_CHAR_PTR pPassword; + CK_ULONG ulPasswordLen; + CK_CHAR_PTR pSalt; + CK_ULONG ulSaltLen; + CK_ULONG ulIteration; +} CK_PBE_PARAMS; + +typedef CK_PBE_PARAMS CK_PTR CK_PBE_PARAMS_PTR; + + +/* CK_KEY_WRAP_SET_OAEP_PARAMS provides the parameters to the + * CKM_KEY_WRAP_SET_OAEP mechanism */ +/* CK_KEY_WRAP_SET_OAEP_PARAMS is new for v2.0 */ +typedef struct CK_KEY_WRAP_SET_OAEP_PARAMS { + CK_BYTE bBC; /* block contents byte */ + CK_BYTE_PTR pX; /* extra data */ + CK_ULONG ulXLen; /* length of extra data in bytes */ +} CK_KEY_WRAP_SET_OAEP_PARAMS; + +typedef CK_KEY_WRAP_SET_OAEP_PARAMS CK_PTR \ + CK_KEY_WRAP_SET_OAEP_PARAMS_PTR; + + +typedef struct CK_SSL3_RANDOM_DATA { + CK_BYTE_PTR pClientRandom; + CK_ULONG ulClientRandomLen; + CK_BYTE_PTR pServerRandom; + CK_ULONG ulServerRandomLen; +} CK_SSL3_RANDOM_DATA; + + +typedef struct CK_SSL3_MASTER_KEY_DERIVE_PARAMS { + CK_SSL3_RANDOM_DATA RandomInfo; + CK_VERSION_PTR pVersion; +} CK_SSL3_MASTER_KEY_DERIVE_PARAMS; + +typedef struct CK_SSL3_MASTER_KEY_DERIVE_PARAMS CK_PTR \ + CK_SSL3_MASTER_KEY_DERIVE_PARAMS_PTR; + + +typedef struct CK_SSL3_KEY_MAT_OUT { + CK_OBJECT_HANDLE hClientMacSecret; + CK_OBJECT_HANDLE hServerMacSecret; + CK_OBJECT_HANDLE hClientKey; + CK_OBJECT_HANDLE hServerKey; + CK_BYTE_PTR pIVClient; + CK_BYTE_PTR pIVServer; +} CK_SSL3_KEY_MAT_OUT; + +typedef CK_SSL3_KEY_MAT_OUT CK_PTR CK_SSL3_KEY_MAT_OUT_PTR; + + +typedef struct CK_SSL3_KEY_MAT_PARAMS { + CK_ULONG ulMacSizeInBits; + CK_ULONG ulKeySizeInBits; + CK_ULONG ulIVSizeInBits; + CK_BBOOL bIsExport; + CK_SSL3_RANDOM_DATA RandomInfo; + CK_SSL3_KEY_MAT_OUT_PTR pReturnedKeyMaterial; +} CK_SSL3_KEY_MAT_PARAMS; + +typedef CK_SSL3_KEY_MAT_PARAMS CK_PTR CK_SSL3_KEY_MAT_PARAMS_PTR; + + +typedef struct CK_KEY_DERIVATION_STRING_DATA { + CK_BYTE_PTR pData; + CK_ULONG ulLen; +} CK_KEY_DERIVATION_STRING_DATA; + +typedef CK_KEY_DERIVATION_STRING_DATA CK_PTR \ + CK_KEY_DERIVATION_STRING_DATA_PTR; + + +/* The CK_EXTRACT_PARAMS is used for the + * CKM_EXTRACT_KEY_FROM_KEY mechanism. It specifies which bit + * of the base key should be used as the first bit of the + * derived key */ +/* CK_EXTRACT_PARAMS is new for v2.0 */ +typedef CK_ULONG CK_EXTRACT_PARAMS; + +typedef CK_EXTRACT_PARAMS CK_PTR CK_EXTRACT_PARAMS_PTR; + +/* Do not attempt to use these. They are only used by NETSCAPE's internal + * PKCS #11 interface. Most of these are place holders for other mechanism + * and will change in the future. + */ +#define CKM_NETSCAPE_PBE_KEY_GEN 0x80000001UL +#define CKM_NETSCAPE_PBE_SHA1_DES_CBC 0x80000002UL +#define CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC 0x80000003UL +#define CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC 0x80000004UL +#define CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC 0x80000005UL +#define CKM_NETSCAPE_PBE_SHA1_40_BIT_RC4 0x80000006UL +#define CKM_NETSCAPE_PBE_SHA1_128_BIT_RC4 0x80000007UL +#define CKM_NETSCAPE_PBE_SHA1_FAULTY_3DES_CBC 0x80000008UL +#define CKM_NETSCAPE_PBE_SHA1_HMAC_KEY_GEN 0x80000009UL +#define CKM_NETSCAPE_PBE_MD5_HMAC_KEY_GEN 0x8000000aUL +#define CKM_NETSCAPE_PBE_MD2_HMAC_KEY_GEN 0x8000000bUL +#define CKM_TLS_MASTER_KEY_DERIVE 0x80000371UL +#define CKM_TLS_KEY_AND_MAC_DERIVE 0x80000372UL +#define CKM_TLS_PRF_GENERAL 0x80000373UL + +/* define used to pass in the database key for DSA private keys */ +#define CKA_NETSCAPE_DB 0xD5A0DB00UL +#define CKA_NETSCAPE_TRUST 0x80000001UL + +/* undo packing */ +//#include "pkcs11u.h" // TODO: msc specific? + +// YUBICO specific attributes +#define CKA_TOUCH_PIN_DEFAULT 0x00000000U +#define CKA_TOUCH_ALWAYS 0x00000001U +#define CKA_PIN_ONCE 0x00000002U +#define CKA_PIN_ALWAYS 0x00000004U + + +#endif diff --git a/ykcs11/slot_vendors.c b/ykcs11/slot_vendors.c new file mode 100644 index 0000000..36f05c7 --- /dev/null +++ b/ykcs11/slot_vendors.c @@ -0,0 +1,24 @@ +#include "slot_vendors.h" +#include "yubico_slot.h" +#include + +slot_vendor_t get_slot_vendor(vendor_id_t vid) { + slot_vendor_t v; + + switch (vid) { + case YUBICO: + v.get_slot_manufacturer = YUBICO_get_slot_manufacturer; + v.get_slot_flags = YUBICO_get_slot_flags; + v.get_slot_version = YUBICO_get_slot_version; + break; + + case UNKNOWN: + default: + v.get_slot_manufacturer = NULL; + v.get_slot_flags = NULL; + v.get_slot_version = NULL; + } + + return v; + +} diff --git a/ykcs11/slot_vendors.h b/ykcs11/slot_vendors.h new file mode 100644 index 0000000..5ab475d --- /dev/null +++ b/ykcs11/slot_vendors.h @@ -0,0 +1,20 @@ +#ifndef SLOT_VENDORS_H +#define SLOT_VENDORS_H + +#include "pkcs11.h" +#include "vendor_ids.h" + +typedef CK_RV (*get_s_manufacturer_f)(CK_UTF8CHAR_PTR, CK_ULONG); +typedef CK_RV (*get_s_flags_f)(CK_FLAGS_PTR); +typedef CK_RV (*get_s_version_f)(CK_VERSION_PTR); + + +typedef struct { + get_s_manufacturer_f get_slot_manufacturer; + get_s_flags_f get_slot_flags; + get_s_version_f get_slot_version; +} slot_vendor_t; + +slot_vendor_t get_slot_vendor(vendor_id_t vid); + +#endif diff --git a/ykcs11/token_vendors.c b/ykcs11/token_vendors.c new file mode 100644 index 0000000..25db6d7 --- /dev/null +++ b/ykcs11/token_vendors.c @@ -0,0 +1,315 @@ +#include "token_vendors.h" +#include "yubico_token.h" +#include "openssl_utils.h" +#include +#include "debug.h" + +#include +#include "../tool/util.h" + +static CK_RV COMMON_token_login(ykpiv_state *state, CK_USER_TYPE user, CK_UTF8CHAR_PTR pin, CK_ULONG pin_len) { + + int tries = 0; // TODO: this is effectively disregarded, should we add a better value in ykpiv_verify? + unsigned char key[24]; + size_t key_len = sizeof(key); + + if (user == CKU_USER) { + if (ykpiv_verify(state, (char *)pin, &tries) != YKPIV_OK) { + DBG("Failed to login"); + return CKR_PIN_INCORRECT; + } + } + else if (user == CKU_SO) { + if(ykpiv_hex_decode((char *)pin, pin_len, key, &key_len) != YKPIV_OK) { + DBG("Failed decoding key"); + return CKR_FUNCTION_FAILED; + } + + if(ykpiv_authenticate(state, key) != YKPIV_OK) { + DBG("Failed to authenticate"); + return CKR_PIN_INCORRECT; + } + } + + return CKR_OK; +} + +static CK_RV COMMON_token_generate_key(ykpiv_state *state, CK_BBOOL rsa, + CK_BYTE key, CK_ULONG key_len, CK_ULONG vendor_defined) { + // TODO: make a function in ykpiv for this + unsigned char in_data[11]; + unsigned char *in_ptr = in_data; + unsigned char data[1024]; + unsigned char templ[] = {0, YKPIV_INS_GENERATE_ASYMMERTRIC, 0, 0}; + unsigned char *certptr; + unsigned long recv_len = sizeof(data); + int len_bytes; + int sw; + + CK_RV rv; + + templ[3] = key; + + *in_ptr++ = 0xac; + *in_ptr++ = 3; + *in_ptr++ = 0x80; + *in_ptr++ = 1; + + switch(key_len) { + case 2048: + if (rsa == CK_TRUE) + in_data[4] = YKPIV_ALGO_RSA2048; + else + return CKR_FUNCTION_FAILED; + + break; + + case 1024: + if (rsa == CK_TRUE) + *in_ptr++ = YKPIV_ALGO_RSA1024; + else + return CKR_FUNCTION_FAILED; + + break; + + case 256: + if (rsa == CK_FALSE) + *in_ptr++ = YKPIV_ALGO_ECCP256; + else + return CKR_FUNCTION_FAILED; + + break; + + default: + return CKR_FUNCTION_FAILED; + } + // PIN policy and touch + if (vendor_defined != 0) { + if (vendor_defined & CKA_PIN_ONCE) { + in_data[1] += 3; + *in_ptr++ = YKPIV_PINPOLICY_TAG; + *in_ptr++ = 0x01; + *in_ptr++ = YKPIV_PINPOLICY_ONCE; + } + else if (vendor_defined & CKA_PIN_ALWAYS) { + in_data[1] += 3; + *in_ptr++ = YKPIV_PINPOLICY_TAG; + *in_ptr++ = 0x01; + *in_ptr++ = YKPIV_PINPOLICY_ALWAYS; + } + + if (vendor_defined & CKA_TOUCH_ALWAYS) { + in_data[1] += 3; + *in_ptr++ = YKPIV_TOUCHPOLICY_TAG; + *in_ptr++ = 0x01; + *in_ptr++ = YKPIV_TOUCHPOLICY_ALWAYS; + } + } + + if(ykpiv_transfer_data(state, templ, in_data, in_ptr - in_data, data, &recv_len, &sw) != YKPIV_OK || + sw != 0x9000) + return CKR_DEVICE_ERROR; + + // Create a new empty certificate for the key + recv_len = sizeof(data); + if ((rv = do_create_empty_cert(data, recv_len, rsa, data, &recv_len)) != CKR_OK) + return rv; + + if (recv_len < 0x80) + len_bytes = 1; + else if (recv_len < 0xff) + len_bytes = 2; + else + len_bytes = 3; + + certptr = data; + memmove(data + len_bytes + 1, data, recv_len); + + *certptr++ = 0x70; + certptr += set_length(certptr, recv_len); + certptr += recv_len; + *certptr++ = 0x71; + *certptr++ = 1; + *certptr++ = 0; /* certinfo (gzip etc) */ + *certptr++ = 0xfe; /* LRC */ + *certptr++ = 0; + + // Store the certificate into the token + if (ykpiv_save_object(state, key_to_object_id(key), data, (size_t)(certptr - data)) != YKPIV_OK) + return CKR_DEVICE_ERROR; + + return CKR_OK; +} + +static CK_RV COMMON_token_import_cert(ykpiv_state *state, CK_ULONG cert_id, CK_BYTE_PTR in) { + + unsigned char certdata[2100]; + unsigned char *certptr; + CK_ULONG cert_len; + + CK_RV rv; + + // Check whether or not we have a valid cert + if ((rv = do_check_cert(in, &cert_len)) != CKR_OK) + return rv; + + if (cert_len > 2100) + return CKR_FUNCTION_FAILED; + + certptr = certdata; + + *certptr++ = 0x70; + certptr += set_length(certptr, cert_len); + memcpy(certptr, in, cert_len); + certptr += cert_len; + + *certptr++ = 0x71; + *certptr++ = 1; + *certptr++ = 0; /* certinfo (gzip etc) */ + *certptr++ = 0xfe; /* LRC */ + *certptr++ = 0; + + // Store the certificate into the token + if (ykpiv_save_object(state, cert_id, certdata, (size_t)(certptr - certdata)) != YKPIV_OK) + return CKR_DEVICE_ERROR; + + return CKR_OK; +} + +CK_RV COMMON_token_import_private_key(ykpiv_state *state, CK_BYTE key_id, CK_BYTE_PTR p, CK_BYTE_PTR q, + CK_BYTE_PTR dp, CK_BYTE_PTR dq, CK_BYTE_PTR qinv, + CK_BYTE_PTR ec_data, CK_ULONG elem_len, CK_ULONG vendor_defined) { + + unsigned char key_data[1024]; + unsigned char *in_ptr = key_data; + unsigned char templ[] = {0, YKPIV_INS_IMPORT_KEY, 0, key_id}; + unsigned char data[0xff]; + unsigned long recv_len = sizeof(data); + int sw; + + if (elem_len == 128) // TODO: add a flag to check algo type ? + templ[2] = YKPIV_ALGO_RSA2048; + else if (elem_len == 64) + templ[2] = YKPIV_ALGO_RSA1024; + else if(elem_len == 32) + templ[2] = YKPIV_ALGO_ECCP256; + + if (templ[2] == YKPIV_ALGO_RSA1024 ||templ[2] == YKPIV_ALGO_RSA2048) { + *in_ptr++ = 0x01; + in_ptr += set_length(in_ptr, elem_len); + memcpy(in_ptr, p, (size_t)(elem_len)); + in_ptr += elem_len; + + *in_ptr++ = 0x02; + in_ptr += set_length(in_ptr, elem_len); + memcpy(in_ptr, q, (size_t)(elem_len)); + in_ptr += elem_len; + + *in_ptr++ = 0x03; + in_ptr += set_length(in_ptr, elem_len); + memcpy(in_ptr, dp, (size_t)(elem_len)); + in_ptr += elem_len; + + *in_ptr++ = 0x04; + in_ptr += set_length(in_ptr, elem_len); + memcpy(in_ptr, dq, (size_t)(elem_len)); + in_ptr += elem_len; + + *in_ptr++ = 0x05; + in_ptr += set_length(in_ptr, elem_len); + memcpy(in_ptr, qinv, (size_t)(elem_len)); + in_ptr += elem_len; + } + else if (templ[2] == YKPIV_ALGO_ECCP256) { + *in_ptr++ = 0x06; + in_ptr += set_length(in_ptr, elem_len); + memcpy(in_ptr, ec_data, (size_t)(elem_len)); + in_ptr += elem_len; + } + + // PIN policy and touch + if (vendor_defined != 0) { + if (vendor_defined & CKA_PIN_ONCE) { + *in_ptr++ = YKPIV_PINPOLICY_TAG; + *in_ptr++ = 0x01; + *in_ptr++ = YKPIV_PINPOLICY_ONCE; + } + else if (vendor_defined & CKA_PIN_ALWAYS) { + *in_ptr++ = YKPIV_PINPOLICY_TAG; + *in_ptr++ = 0x01; + *in_ptr++ = YKPIV_PINPOLICY_ALWAYS; + } + + if (vendor_defined & CKA_TOUCH_ALWAYS) { + *in_ptr++ = YKPIV_TOUCHPOLICY_TAG; + *in_ptr++ = 0x01; + *in_ptr++ = YKPIV_TOUCHPOLICY_ALWAYS; + } + } + + if (ykpiv_transfer_data(state, templ, key_data, in_ptr - key_data, data, &recv_len, &sw) != YKPIV_OK) + return CKR_FUNCTION_FAILED; + + if (sw != 0x9000) + return CKR_DEVICE_ERROR; + + return CKR_OK; + +} + +CK_RV COMMON_token_delete_cert(ykpiv_state *state, CK_ULONG cert_id) { + + if (ykpiv_save_object(state, cert_id, NULL, 0) != YKPIV_OK) + return CKR_DEVICE_ERROR; + + return CKR_OK; +} + +token_vendor_t get_token_vendor(vendor_id_t vid) { + token_vendor_t v; + + switch (vid) { + case YUBICO: + v.get_token_label = YUBICO_get_token_label; + v.get_token_manufacturer = YUBICO_get_token_manufacturer; + v.get_token_model = YUBICO_get_token_model; + v.get_token_flags = YUBICO_get_token_flags; + v.get_token_version = YUBICO_get_token_version; + v.get_token_serial = YUBICO_get_token_serial; + v.get_token_mechanisms_num = YUBICO_get_token_mechanisms_num; + v.get_token_mechanism_list = YUBICO_get_token_mechanism_list; + v.get_token_mechanism_info = YUBICO_get_token_mechanism_info; + v.get_token_objects_num = YUBICO_get_token_objects_num; + v.get_token_object_list = YUBICO_get_token_object_list; + v.get_token_raw_certificate = YUBICO_get_token_raw_certificate; + v.token_login = COMMON_token_login; + v.token_generate_key = COMMON_token_generate_key; + v.token_import_cert = COMMON_token_import_cert; + v.token_import_private_key = COMMON_token_import_private_key; + v.token_delete_cert = COMMON_token_delete_cert; + break; + + case UNKNOWN: + default: + v.get_token_label = NULL; + v.get_token_manufacturer = NULL; + v.get_token_model = NULL; + v.get_token_flags = NULL; + v.get_token_version = NULL; + v.get_token_serial = NULL; + v.get_token_mechanisms_num = NULL; + v.get_token_mechanism_list = NULL; + v.get_token_mechanism_info = NULL; + v.get_token_objects_num = NULL; + v.get_token_object_list = NULL; + v.get_token_raw_certificate = NULL; + v.token_login = NULL; + v.token_generate_key = NULL; + v.token_import_cert = NULL; + v.token_import_private_key = NULL; + v.token_delete_cert = NULL; + } + + return v; + +} diff --git a/ykcs11/token_vendors.h b/ykcs11/token_vendors.h new file mode 100644 index 0000000..3b57209 --- /dev/null +++ b/ykcs11/token_vendors.h @@ -0,0 +1,54 @@ +#ifndef TOKEN_VENDORS_H +#define TOKEN_VENDORS_H + +#include "pkcs11.h" +#include "vendor_ids.h" +#include "obj_types.h" +#include + +typedef CK_RV (*get_t_label_f)(CK_UTF8CHAR_PTR, CK_ULONG); +typedef CK_RV (*get_t_manufacturer_f)(CK_UTF8CHAR_PTR, CK_ULONG); +typedef CK_RV (*get_t_model_f)(ykpiv_state *, CK_UTF8CHAR_PTR, CK_ULONG); +typedef CK_RV (*get_t_flags_f)(CK_FLAGS_PTR); +typedef CK_RV (*get_t_version_f)(CK_UTF8CHAR_PTR, CK_ULONG, CK_VERSION_PTR); +typedef CK_RV (*get_t_serial_f)(CK_CHAR_PTR, CK_ULONG); +typedef CK_RV (*get_t_mechanisms_num_f)(CK_ULONG_PTR); +typedef CK_RV (*get_t_mechanism_list_f)(CK_MECHANISM_TYPE_PTR, CK_ULONG); +typedef CK_RV (*get_t_mechanism_info_f)(CK_MECHANISM_TYPE, CK_MECHANISM_INFO_PTR); +typedef CK_RV (*get_t_objects_num_f)(ykpiv_state *, CK_ULONG_PTR, CK_ULONG_PTR); +typedef CK_RV (*get_t_object_list_f)(ykpiv_state *, piv_obj_id_t *, CK_ULONG); +typedef CK_RV (*get_t_raw_certificate_f)(ykpiv_state *, piv_obj_id_t, CK_BYTE_PTR, CK_ULONG_PTR); + +// Common token functions below +typedef CK_RV (*t_login_f)(ykpiv_state *, CK_USER_TYPE, CK_UTF8CHAR_PTR, CK_ULONG); +typedef CK_RV (*t_generate_key_f)(ykpiv_state *, CK_BBOOL, CK_BYTE, CK_ULONG, CK_ULONG); +typedef CK_RV (*t_import_cert_f)(ykpiv_state *, CK_ULONG, CK_BYTE_PTR); +typedef CK_RV (*t_import_private_key_f)(ykpiv_state *, CK_BYTE, CK_BYTE_PTR, CK_BYTE_PTR, CK_BYTE_PTR, + CK_BYTE_PTR, CK_BYTE_PTR, CK_BYTE_PTR, CK_ULONG, CK_ULONG); +typedef CK_RV (*t_delete_cert_f)(ykpiv_state *, CK_ULONG); + +// TODO: replace all the common calls with functions defined in .c that use libykpiv + +typedef struct { + get_t_label_f get_token_label; + get_t_manufacturer_f get_token_manufacturer; + get_t_model_f get_token_model; + get_t_flags_f get_token_flags; + get_t_version_f get_token_version; + get_t_serial_f get_token_serial; + get_t_mechanisms_num_f get_token_mechanisms_num; + get_t_mechanism_list_f get_token_mechanism_list; + get_t_mechanism_info_f get_token_mechanism_info; + get_t_objects_num_f get_token_objects_num; + get_t_object_list_f get_token_object_list; + get_t_raw_certificate_f get_token_raw_certificate; + t_login_f token_login; + t_generate_key_f token_generate_key; + t_import_cert_f token_import_cert; + t_import_private_key_f token_import_private_key; + t_delete_cert_f token_delete_cert; +} token_vendor_t; + +token_vendor_t get_token_vendor(vendor_id_t vid); + +#endif diff --git a/ykcs11/utils.c b/ykcs11/utils.c new file mode 100644 index 0000000..8efc89d --- /dev/null +++ b/ykcs11/utils.c @@ -0,0 +1,199 @@ +#include "utils.h" +#include +#include + +CK_BBOOL has_token(const ykcs11_slot_t *slot) { + + return (slot->info.flags & CKF_TOKEN_PRESENT); + +} + +CK_RV parse_readers(ykpiv_state *state, const CK_BYTE_PTR readers, const CK_ULONG len, + ykcs11_slot_t *slots, CK_ULONG_PTR n_slots, CK_ULONG_PTR n_with_token) { + + CK_BYTE i; + CK_BYTE_PTR p; + CK_BYTE_PTR s; + CK_ULONG l; + slot_vendor_t slot; + + *n_slots = 0; + *n_with_token = 0; + p = readers; + + /* + * According to pcsc-lite, the format of a reader name is: + * name [interface] (serial) index slot + * http://ludovicrousseau.blogspot.se/2010/05/what-is-in-pcsc-reader-name.html + */ + + for (i = 0; i < len; i++) + if (readers[i] == '\0' && i != len - 1) { + slots[*n_slots].vid = get_vendor_id(p); + + if (slots[*n_slots].vid == UNKNOWN) { // TODO: distinguish between tokenless and unsupported? + // Unknown slot, just save what info we have + memset(&slots[*n_slots].info, 0, sizeof(CK_SLOT_INFO)); + memset(slots[*n_slots].info.slotDescription, ' ', sizeof(slots[*n_slots].info.slotDescription)); + if (strlen(p) <= sizeof(slots[*n_slots].info.slotDescription)) + strncpy(slots[*n_slots].info.slotDescription, p, strlen(p)); + else + strncpy(slots[*n_slots].info.slotDescription, p, sizeof(slots[*n_slots].info.slotDescription)); + } + else { + // Supported slot + slot = get_slot_vendor(slots[*n_slots].vid); + + // Values must NOT be null terminated and ' ' padded + + memset(slots[*n_slots].info.slotDescription, ' ', sizeof(slots[*n_slots].info.slotDescription)); + s = slots[*n_slots].info.slotDescription; + l = sizeof(slots[*n_slots].info.slotDescription); + strncpy((char *)s, (char*)p, l); + + memset(slots[*n_slots].info.manufacturerID, ' ', sizeof(slots[*n_slots].info.manufacturerID)); + s = slots[*n_slots].info.manufacturerID; + l = sizeof(slots[*n_slots].info.manufacturerID); + if(slot.get_slot_manufacturer(s, l) != CKR_OK) + goto failure; + + if (slot.get_slot_flags(&slots[*n_slots].info.flags) != CKR_OK) + goto failure; + + // Treating hw and fw version the same + if (slot.get_slot_version(&slots[*n_slots].info.hardwareVersion) != CKR_OK) + goto failure; + + if (slot.get_slot_version(&slots[*n_slots].info.firmwareVersion) != CKR_OK) + goto failure; + + if (has_token(slots + *n_slots)) { + // Save token information + (*n_with_token)++; + + if (create_token(state, p, slots + *n_slots) != CKR_OK) + goto failure; + } + } + (*n_slots)++; + p += i + 1; + } + + return CKR_OK; + +failure: + // TODO: destroy all token objects + for (i = 0; i < *n_slots; i++) + if (has_token(slots + i)) + destroy_token(slots + i); + + return CKR_FUNCTION_FAILED; +} + +CK_RV create_token(ykpiv_state *state, CK_BYTE_PTR p, ykcs11_slot_t *slot) { + + token_vendor_t token; + CK_TOKEN_INFO_PTR t_info; + + slot->token = malloc(sizeof(ykcs11_token_t)); // TODO: free + if (slot->token == NULL) + return CKR_HOST_MEMORY; + + slot->token->vid = YUBICO; // TODO: this must become "slot_vendor.get_token_vid()" + token = get_token_vendor(slot->token->vid); + + t_info = &slot->token->info; + + memset(t_info->label, ' ', sizeof(t_info->label)); + if (token.get_token_label(t_info->label, sizeof(t_info->label)) != CKR_OK) + return CKR_FUNCTION_FAILED; + + memset(t_info->manufacturerID, ' ', sizeof(t_info->manufacturerID)); + if(token.get_token_manufacturer(t_info->manufacturerID, sizeof(t_info->manufacturerID)) != CKR_OK) + return CKR_FUNCTION_FAILED; + + if (ykpiv_connect(state, (char *)p) != YKPIV_OK) + return CKR_FUNCTION_FAILED; + memset(t_info->model, ' ', sizeof(t_info->model)); + if(token.get_token_model(state, t_info->model, sizeof(t_info->model)) != CKR_OK) + return CKR_FUNCTION_FAILED; + ykpiv_disconnect(state); + + memset(t_info->serialNumber, ' ', sizeof(t_info->serialNumber)); + if(token.get_token_serial(t_info->serialNumber, sizeof(t_info->serialNumber)) != CKR_OK) + return CKR_FUNCTION_FAILED; + + if (token.get_token_flags(&t_info->flags) != CKR_OK) + return CKR_FUNCTION_FAILED; + + t_info->ulMaxSessionCount = CK_UNAVAILABLE_INFORMATION; + + t_info->ulSessionCount = CK_UNAVAILABLE_INFORMATION; + + t_info->ulMaxRwSessionCount = CK_UNAVAILABLE_INFORMATION; + + t_info->ulRwSessionCount = CK_UNAVAILABLE_INFORMATION; + + t_info->ulMaxPinLen = 8; + + t_info->ulMinPinLen = 6; + + t_info->ulTotalPublicMemory = CK_UNAVAILABLE_INFORMATION; + + t_info->ulFreePublicMemory = CK_UNAVAILABLE_INFORMATION; + + t_info->ulTotalPrivateMemory = CK_UNAVAILABLE_INFORMATION; + + t_info->ulFreePrivateMemory = CK_UNAVAILABLE_INFORMATION; + + //ykpiv_get_version(piv_state, buf, sizeof(buf)); + //if (token_vendor.get_token_version(buf, strlen(buf), &ver) != CKR_OK) // TODO: fix this + // return CKR_FUNCTION_FAILED; + + //t_info->hardwareVersion = ver; // version number of hardware // TODO: fix + + //t_info->firmwareVersion = ver; // version number of firmware // TODO: fix + + memset(t_info->utcTime, ' ', sizeof(t_info->utcTime)); // No clock present, clear + + // TODO: also get token objects here? (and destroy on failure) + slot->token->objects = NULL; + slot->token->n_objects = 0; + + return CKR_OK; +} + +void destroy_token(ykcs11_slot_t *slot) { + free(slot->token); + slot->token = NULL; +} + +CK_BBOOL is_valid_key_id(CK_BYTE id) { + + // Valid ids are 0, 1, 2, 3 + if (id > 3) + return CK_FALSE; + + return CK_TRUE; +} + +void strip_DER_encoding_from_ECSIG(CK_BYTE_PTR data, CK_ULONG_PTR len) { + + CK_BYTE_PTR ptr; + CK_ULONG n_len; + + // Maximum DER length for P256 is 2 + 2 + 33 + 2 + 33 = 72 + if (*len <= 72) + n_len = 32; + else + n_len = 48; + + ptr = data + 4; + if (*ptr == 0) + ptr++; + + memmove(data, ptr, n_len); + memmove(data+n_len, data + *len - n_len, n_len); + + *len = n_len * 2; +} diff --git a/ykcs11/utils.h b/ykcs11/utils.h new file mode 100644 index 0000000..509b2d5 --- /dev/null +++ b/ykcs11/utils.h @@ -0,0 +1,16 @@ +#ifndef UTILS_H +#define UTILS_H + +#include "ykcs11.h" + +CK_BBOOL has_token(const ykcs11_slot_t *slot); +CK_RV parse_readers(ykpiv_state *state,const CK_BYTE_PTR readers, const CK_ULONG len, + ykcs11_slot_t *slots, CK_ULONG_PTR n_slots, CK_ULONG_PTR n_with_token); +CK_RV create_token(ykpiv_state *state, CK_BYTE_PTR p, ykcs11_slot_t *slot); +void destroy_token(ykcs11_slot_t *slot); + +CK_BBOOL is_valid_key_id(CK_BYTE id); + +void strip_DER_encoding_from_ECSIG(CK_BYTE_PTR data, CK_ULONG_PTR len); + +#endif diff --git a/ykcs11/vendor_ids.h b/ykcs11/vendor_ids.h new file mode 100644 index 0000000..8505750 --- /dev/null +++ b/ykcs11/vendor_ids.h @@ -0,0 +1,10 @@ +#ifndef VENDOR_IDS_H +#define VENDOR_IDS_H + +typedef enum { + UNKNOWN = 0x00, + YUBICO = 0x01 +} vendor_id_t; + + +#endif diff --git a/ykcs11/vendors.c b/ykcs11/vendors.c new file mode 100644 index 0000000..982208d --- /dev/null +++ b/ykcs11/vendors.c @@ -0,0 +1,10 @@ +#include "vendors.h" +#include + +vendor_id_t get_vendor_id(char *vendor_name) { + + if (strstr(vendor_name, "Yubico") != NULL) + return YUBICO; + + return UNKNOWN; +} diff --git a/ykcs11/vendors.h b/ykcs11/vendors.h new file mode 100644 index 0000000..4125d22 --- /dev/null +++ b/ykcs11/vendors.h @@ -0,0 +1,10 @@ +#ifndef VENDORS_H +#define VENDORS_H + +#include "vendor_ids.h" +#include "slot_vendors.h" +#include "token_vendors.h" + +vendor_id_t get_vendor_id(char *vendor_name); + +#endif diff --git a/ykcs11/version.c b/ykcs11/version.c new file mode 100644 index 0000000..ffdcdec --- /dev/null +++ b/ykcs11/version.c @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2015 Yubico AB + * All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Additional permission under GNU GPL version 3 section 7 + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, We grant you additional + * permission to convey the resulting work. Corresponding Source for a + * non-source form of such a combination shall include the source code + * for the parts of OpenSSL used as well as that of the covered work. + * + */ + +#include "ykcs11-version.h" + +#include + +#include + +/* From http://article.gmane.org/gmane.os.freebsd.devel.hackers/23606 */ +static int my_strverscmp (const char *s1, const char *s2) +{ + static const char *digits = "0123456789"; + 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 + * http://refspecs.freestandards.org/LSB_2.0.1/LSB-generic/ + * LSB-generic/baselib-strverscmp.html + */ + while (*s1 == '0' && *s2 == '0') { + ++s1; + ++s2; + } + + p1 = strspn (s1, digits); + p2 = strspn (s2, digits); + + /* Catch empty strings */ + if (p1 == 0 && p2 > 0) + return 1; + else if (p2 == 0 && p1 > 0) + return -1; + + /* Prefixes are not same */ + if (*s1 != *s2 && *s1 != '0' && *s2 != '0') { + if (p1 < p2) + return 1; + else if (p1 > p2) + return -1; + } else { + if (p1 < p2) + ret = strncmp (s1, s2, p1); + else if (p1 > p2) + ret = strncmp (s1, s2, p2); + if (ret != 0) + return ret; + } + } + + p1 = strspn (s1, digits); + p2 = strspn (s2, digits); + + if (p1 < p2) + return -1; + else if (p1 > p2) + return 1; + else if ((ret = strncmp (s1, s2, p1)) != 0) + return ret; + + /* Numbers are equal or not present, try with next ones. */ + s1 += p1; + s2 += p2; + p1 = strcspn (s1, digits); + p2 = strcspn (s2, digits); + } + + return strcmp (s1, s2); +} + +/** + * ykcs11_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 %YKCS11_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 * ykcs11_check_version (const char *req_version) +{ + if (!req_version + || my_strverscmp (req_version, YKCS11_VERSION_STRING) <= 0) + return YKCS11_VERSION_STRING; + + return NULL; +} diff --git a/ykcs11/ykcs11-version.h.in b/ykcs11/ykcs11-version.h.in new file mode 100644 index 0000000..f973c1e --- /dev/null +++ b/ykcs11/ykcs11-version.h.in @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2015 Yubico AB + * All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Additional permission under GNU GPL version 3 section 7 + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, We grant you additional + * permission to convey the resulting work. Corresponding Source for a + * non-source form of such a combination shall include the source code + * for the parts of OpenSSL used as well as that of the covered work. + * + */ + +#ifndef YKCS11_VERSION_H +#define YKCS11_VERSION_H + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * YKCS11_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 YKCS11_VERSION_STRING "@VERSION@" + + /** + * YKCS11_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 YKCS11_VERSION_NUMBER @YKCS11_VERSION_NUMBER@ + + /** + * YKCS11_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 YKCS11_VERSION_MAJOR @YKCS11_VERSION_MAJOR@ + + /** + * YKCS11_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 YKCS11_VERSION_MINOR @YKCS11_VERSION_MINOR@ + + /** + * YKCS11_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 YKCS11_VERSION_PATCH @YKCS11_VERSION_PATCH@ + + const char *ykcs11_check_version (const char *req_version); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ykcs11/ykcs11.c b/ykcs11/ykcs11.c new file mode 100644 index 0000000..9b6bf51 --- /dev/null +++ b/ykcs11/ykcs11.c @@ -0,0 +1,2391 @@ +#include "ykcs11.h" +#include "ykcs11-version.h" +#include +#include +#include +#include "obj_types.h" +#include "objects.h" +#include "utils.h" +#include "mechanisms.h" +#include "openssl_types.h" +#include "debug.h" + +#include +#include "../tool/util.h" + +#define YKCS11_MANUFACTURER "Yubico (www.yubico.com)" +#define YKCS11_LIBDESC "PKCS#11 PIV Library (SP-800-73)" + +#define PIV_MIN_PIN_LEN 6 +#define PIV_MAX_PIN_LEN 8 +#define PIV_MGM_KEY_LEN 48 + +#define YKCS11_MAX_SLOTS 16 +//#define YKCS11_MAX_SIG_BUF_LEN 1024 + +#define YKCS11_SESSION_ID 5355104 + +static ykpiv_state *piv_state = NULL; + +static ykcs11_slot_t slots[YKCS11_MAX_SLOTS]; +static CK_ULONG n_slots = 0; +static CK_ULONG n_slots_with_token = 0; + +static ykcs11_session_t session; + +static struct { + CK_BBOOL active; + CK_ULONG num; + CK_ULONG idx; + piv_obj_id_t *objects; +} find_obj; + +op_info_t op_info; + +extern CK_FUNCTION_LIST function_list; + +/* General Purpose */ + +CK_DEFINE_FUNCTION(CK_RV, C_Initialize)( + CK_VOID_PTR pInitArgs +) +{ + CK_BYTE readers[2048]; + CK_ULONG len = sizeof(readers); + + DIN; + + if (piv_state != NULL) + return CKR_CRYPTOKI_ALREADY_INITIALIZED; + + if (ykpiv_init(&piv_state, YKCS11_DBG) != YKPIV_OK) { + DBG("Unable to initialize libykpiv"); + return CKR_FUNCTION_FAILED; + } + + if (ykpiv_list_readers(piv_state, (char*)readers, &len) != YKPIV_OK) { + DBG("Unable to list readers"); + return CKR_FUNCTION_FAILED; + } + + if (parse_readers(piv_state, readers, len, slots, &n_slots, &n_slots_with_token) != CKR_OK) + return CKR_FUNCTION_FAILED; + + DBG("Found %lu slot(s) of which %lu tokenless/unsupported", n_slots, n_slots - n_slots_with_token); + + find_obj.active = CK_FALSE; + // TODO: FILL OUT INIT ARGS; + + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_Finalize)( + CK_VOID_PTR pReserved +) +{ + CK_ULONG i; + + DIN; + + if (pReserved != NULL_PTR) { + DBG("Finalized called with pReserved != NULL"); + return CKR_ARGUMENTS_BAD; + } + + if (piv_state == NULL) { + DBG("libykpiv is not initialized or already finalized"); + return CKR_CRYPTOKI_NOT_INITIALIZED; + } + + for (i = 0; i < n_slots; i++) { + destroy_token(slots + i); + } + memset(slots, 0, sizeof(slots)); + + ykpiv_done(piv_state); + piv_state = NULL; + + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_GetInfo)( + CK_INFO_PTR pInfo +) +{ + CK_VERSION ver = {YKCS11_VERSION_MAJOR, (YKCS11_VERSION_MINOR * 100) + YKCS11_VERSION_PATCH}; + + DIN; + + pInfo->cryptokiVersion = function_list.version; + + memset(pInfo->manufacturerID, ' ', sizeof(pInfo->manufacturerID)); + strcpy((char *)pInfo->manufacturerID, YKCS11_MANUFACTURER); + + pInfo->flags = 0; + + memset(pInfo->libraryDescription, ' ', sizeof(pInfo->libraryDescription)); + strcpy((char *)pInfo->libraryDescription, YKCS11_LIBDESC); + + pInfo->libraryVersion = ver; + + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_GetFunctionList)( + CK_FUNCTION_LIST_PTR_PTR ppFunctionList +) +{ + DIN; + + if(ppFunctionList == NULL_PTR) { + DBG("GetFunctionList called with ppFunctionList = NULL"); + return CKR_ARGUMENTS_BAD; + } + *ppFunctionList = &function_list; + + DOUT; + return CKR_OK; +} + +/* Slot and token management */ + +CK_DEFINE_FUNCTION(CK_RV, C_GetSlotList)( + CK_BBOOL tokenPresent, + CK_SLOT_ID_PTR pSlotList, + CK_ULONG_PTR pulCount +) +{ + CK_ULONG i; + int j; + + DIN; + + if (piv_state == NULL) { + DBG("libykpiv is not initialized or already finalized"); + return CKR_CRYPTOKI_NOT_INITIALIZED; + } + + if (pSlotList == NULL_PTR) { + // Just return the number of slots + *pulCount = n_slots; + + if (tokenPresent) + *pulCount = n_slots_with_token; + else + *pulCount = n_slots; + + DOUT; + return CKR_OK; + } + + if ((tokenPresent && *pulCount < n_slots_with_token) || (!tokenPresent && *pulCount < n_slots)) { + DBG("Buffer too small: needed %lu, provided %lu", n_slots, *pulCount); + return CKR_BUFFER_TOO_SMALL; + } + + for (j = 0, i = 0; i < n_slots; i++) { + if (tokenPresent) { + if (has_token(slots + i)) + pSlotList[j++] = i; + } + else + pSlotList[i] = i; + } + + DBG("token present is %d", tokenPresent); + DBG("number of slot(s) is %lu", *pulCount); + + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_GetSlotInfo)( + CK_SLOT_ID slotID, + CK_SLOT_INFO_PTR pInfo +) +{ + DIN; + + if (piv_state == NULL) { + DBG("libykpiv is not initialized or already finalized"); + return CKR_CRYPTOKI_NOT_INITIALIZED; + } + + if (slotID >= n_slots) { + DBG("Invalid slot ID %lu", slotID); + return CKR_SLOT_ID_INVALID; + } + + memcpy(pInfo, &slots[slotID].info, sizeof(CK_SLOT_INFO)); + + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_GetTokenInfo)( + CK_SLOT_ID slotID, + CK_TOKEN_INFO_PTR pInfo +) +{ + DIN; + + if (piv_state == NULL) { + DBG("libykpiv is not initialized or already finalized"); + return CKR_CRYPTOKI_NOT_INITIALIZED; + } + + if (slotID >= n_slots) { + DBG("Invalid slot ID %lu", slotID); + return CKR_SLOT_ID_INVALID; + } + + if (slots[slotID].vid == UNKNOWN) { + DBG("No support for slot %lu", slotID); + return CKR_SLOT_ID_INVALID; + } + + if (!has_token(slots + slotID)) { + DBG("Slot %lu has no token inserted", slotID); + return CKR_TOKEN_NOT_PRESENT; + } + + if (slots[slotID].token->vid == UNKNOWN) { + DBG("No support for token in slot %lu", slotID); + return CKR_TOKEN_NOT_RECOGNIZED; + } + + memcpy(pInfo, &slots[slotID].token->info, sizeof(CK_TOKEN_INFO)); + + // Overwrite values that are application specific + pInfo->ulMaxSessionCount = CK_UNAVAILABLE_INFORMATION; // TODO: should this be 1? + pInfo->ulSessionCount = CK_UNAVAILABLE_INFORMATION; // number of sessions that this application currently has open with the token + pInfo->ulMaxRwSessionCount = CK_UNAVAILABLE_INFORMATION; // maximum number of read/write sessions that can be opened with the token at one time by a single TODO: should this be 1? + pInfo->ulRwSessionCount = CK_UNAVAILABLE_INFORMATION; // number of read/write sessions that this application currently has open with the token + pInfo->ulMaxPinLen = PIV_MAX_PIN_LEN; // maximum length in bytes of the PIN + pInfo->ulMinPinLen = PIV_MIN_PIN_LEN; // minimum length in bytes of the PIN + pInfo->ulTotalPublicMemory = CK_UNAVAILABLE_INFORMATION; + pInfo->ulFreePublicMemory = CK_UNAVAILABLE_INFORMATION; + pInfo->ulTotalPrivateMemory = CK_UNAVAILABLE_INFORMATION; + pInfo->ulFreePrivateMemory = CK_UNAVAILABLE_INFORMATION; + + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_WaitForSlotEvent)( + CK_FLAGS flags, + CK_SLOT_ID_PTR pSlot, + CK_VOID_PTR pReserved +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_GetMechanismList)( + CK_SLOT_ID slotID, + CK_MECHANISM_TYPE_PTR pMechanismList, + CK_ULONG_PTR pulCount +) +{ + token_vendor_t token; + CK_ULONG count; + + DIN; + + if (piv_state == NULL) { + DBG("libykpiv is not initialized or already finalized"); + return CKR_CRYPTOKI_NOT_INITIALIZED; + } + + if (slotID >= n_slots) { + DBG("Invalid slot ID %lu", slotID); + return CKR_SLOT_ID_INVALID; + } + + if (pulCount == NULL_PTR) { + DBG("Wrong/Missing parameter"); + return CKR_ARGUMENTS_BAD; + } + + if (slots[slotID].vid == UNKNOWN) { + DBG("Slot %lu is tokenless/unsupported", slotID); + return CKR_SLOT_ID_INVALID; + } + + // TODO: check more return values + + token = get_token_vendor(slots[slotID].vid); + + if (token.get_token_mechanisms_num(&count) != CKR_OK) + return CKR_FUNCTION_FAILED; + + if (pMechanismList == NULL_PTR) { + *pulCount = count; + DBG("Found %lu mechanisms", *pulCount); + DOUT; + return CKR_OK; + } + + if (*pulCount < count) { + DBG("Buffer too small: needed %lu, provided %lu", count, *pulCount); + return CKR_BUFFER_TOO_SMALL; + } + + if (token.get_token_mechanism_list(pMechanismList, *pulCount) != CKR_OK) { + DBG("Unable to retrieve mechanism list"); + return CKR_FUNCTION_FAILED; + } + + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_GetMechanismInfo)( + CK_SLOT_ID slotID, + CK_MECHANISM_TYPE type, + CK_MECHANISM_INFO_PTR pInfo +) +{ + token_vendor_t token; + + DIN; + + if (piv_state == NULL) { + DBG("libykpiv is not initialized or already finalized"); + return CKR_CRYPTOKI_NOT_INITIALIZED; + } + + if (slotID >= n_slots) { + DBG("Invalid slot ID %lu", slotID); + return CKR_SLOT_ID_INVALID; + } + + if (pInfo == NULL_PTR) { + DBG("Wrong/Missing parameter"); + return CKR_ARGUMENTS_BAD; + } + + if (slots[slotID].vid == UNKNOWN) { + DBG("Slot %lu is tokenless/unsupported", slotID); + return CKR_SLOT_ID_INVALID; + } + + token = get_token_vendor(slots[slotID].vid); + + if (token.get_token_mechanism_info(type, pInfo) != CKR_OK) { + DBG("Unable to retrieve mechanism information"); + return CKR_MECHANISM_INVALID; + } + + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_InitToken)( + CK_SLOT_ID slotID, + CK_UTF8CHAR_PTR pPin, + CK_ULONG ulPinLen, + CK_UTF8CHAR_PTR pLabel +) +{ + DIN; + DBG("Token initialization unsupported"); + DOUT; + return CKR_FUNCTION_FAILED; +} + +CK_DEFINE_FUNCTION(CK_RV, C_InitPIN)( + CK_SESSION_HANDLE hSession, + CK_UTF8CHAR_PTR pPin, + CK_ULONG ulPinLen +) +{ + DIN; + DBG("PIN initialization unsupported"); + DOUT; + return CKR_FUNCTION_FAILED; +} + +CK_DEFINE_FUNCTION(CK_RV, C_SetPIN)( + CK_SESSION_HANDLE hSession, + CK_UTF8CHAR_PTR pOldPin, + CK_ULONG ulOldLen, + CK_UTF8CHAR_PTR pNewPin, + CK_ULONG ulNewLen +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_OpenSession)( + CK_SLOT_ID slotID, + CK_FLAGS flags, + CK_VOID_PTR pApplication, + CK_NOTIFY Notify, + CK_SESSION_HANDLE_PTR phSession +) +{ + token_vendor_t token; + CK_RV rv; + piv_obj_id_t *cert_ids; + CK_ULONG i; + CK_BYTE cert_data[2100]; // Max cert value for ykpiv + CK_ULONG cert_len = sizeof(cert_data); + + DIN; // TODO: pApplication and Notify + + if (piv_state == NULL) { + DBG("libykpiv is not initialized or already finalized"); + return CKR_CRYPTOKI_NOT_INITIALIZED; + } + + if (slotID >= n_slots) { + DBG("Invalid slot ID %lu", slotID); + return CKR_SLOT_ID_INVALID; + } + + if (phSession == NULL_PTR) { + DBG("Wrong/Missing parameter"); + return CKR_ARGUMENTS_BAD; + } + + if (slots[slotID].vid == UNKNOWN) { + DBG("No support for slot %lu", slotID); + return CKR_TOKEN_NOT_RECOGNIZED; + } + + if (slots[slotID].token->vid == UNKNOWN) { + DBG("No support for token in slot %lu", slotID); + return CKR_TOKEN_NOT_RECOGNIZED; + } + + if (!has_token(slots + slotID)) { + DBG("Slot %lu has no token inserted", slotID); + return CKR_TOKEN_NOT_PRESENT; + } + + if (session.handle != CK_INVALID_HANDLE) { + DBG("A session with this or another token already exists"); + return CKR_SESSION_COUNT; + } + + if ((flags & CKF_SERIAL_SESSION) == 0) { + DBG("Open session called without CKF_SERIAL_SESSION set"); // Reuired by spes + return CKR_SESSION_PARALLEL_NOT_SUPPORTED; + } + + // Connect to the slot + if(ykpiv_connect(piv_state, (char *)slots[slotID].info.slotDescription) != YKPIV_OK) { + DBG("Unable to connect to reader"); + return CKR_FUNCTION_FAILED; + } + + token = get_token_vendor(slots[slotID].token->vid); + + // Store the slot + session.slot = slots + slotID; + //session.slot->info.slotID = slotID; // Redundant but required in CK_SESSION_INFO + + // Store session state + session.info.state = 0; + + if ((flags & CKF_RW_SESSION)) { + // R/W Session + session.info.state = CKS_RW_PUBLIC_SESSION; // Nobody has logged in, default RO session + } + else { + // R/O Session + session.info.state = CKS_RO_PUBLIC_SESSION; // Nobody has logged in, default RW session + } + + session.info.flags = flags; + session.info.ulDeviceError = 0; + + // Get the number of token objects + rv = token.get_token_objects_num(piv_state, &session.slot->token->n_objects, &session.slot->token->n_certs); + if (rv != CKR_OK) { + DBG("Unable to retrieve number of token objects"); + return rv; + } + + // Get memory for the objects + session.slot->token->objects = malloc(sizeof(piv_obj_id_t) * session.slot->token->n_objects); + if (session.slot->token->objects == NULL) { + DBG("Unable to allocate memory for token object ids"); + return CKR_HOST_MEMORY; + } + + // Get memory for the certificates + cert_ids = malloc(sizeof(piv_obj_id_t) * session.slot->token->n_certs); + if (cert_ids == NULL) { + DBG("Unable to allocate memory for token certificate ids"); + return CKR_HOST_MEMORY; + } + + // Save a list of all the available objects in the token + rv = token.get_token_object_list(piv_state, session.slot->token->objects, session.slot->token->n_objects); + if (rv != CKR_OK) { + DBG("Unable to retrieve token objects"); + goto failure; + } + + // Get a list of object ids for available certificates object from the session + rv = get_available_certificate_ids(&session, cert_ids, session.slot->token->n_certs); + if (rv != CKR_OK) { + DBG("Unable to retrieve certificate ids from the session"); + goto failure; + } + + // Get the actual certificate data from the token and store it as an X509 object + for (i = 0; i < session.slot->token->n_certs; i++) { + cert_len = sizeof(cert_data); + rv = token.get_token_raw_certificate(piv_state, cert_ids[i], cert_data, &cert_len); + if (rv != CKR_OK) { + DBG("Unable to get certificate data from token"); + goto failure; + } + + rv = store_cert(cert_ids[i], cert_data, cert_len); + if (rv != CKR_OK) { + DBG("Unable to store certificate data"); + goto failure; + } + } + + free(cert_ids); + cert_ids = NULL; + + session.handle = YKCS11_SESSION_ID; + + *phSession = session.handle; + + DOUT; + return CKR_OK; + +failure: + if (session.slot->token->objects != NULL) { + free(session.slot->token->objects); + session.slot->token->objects = NULL; + } + + if (cert_ids != NULL) { + free(cert_ids); + cert_ids = NULL; + } + + //free_certs(); // TODO: remove the one allocated so far + + return rv; +} + +CK_DEFINE_FUNCTION(CK_RV, C_CloseSession)( + CK_SESSION_HANDLE hSession +) +{ + DIN; + + if (piv_state == NULL) { + DBG("libykpiv is not initialized or already finalized"); + return CKR_CRYPTOKI_NOT_INITIALIZED; + } + + if (session.handle == CK_INVALID_HANDLE) { + DBG("Trying to close a session, but there is no existing one"); + return CKR_SESSION_CLOSED; + } + + if (hSession != YKCS11_SESSION_ID) { + DBG("Unknown session %lu", hSession); + return CKR_SESSION_HANDLE_INVALID; + } + + free(session.slot->token->objects); + session.slot->token->objects = NULL; + + memset(&session, 0, sizeof(ykcs11_session_t)); + session.handle = CK_INVALID_HANDLE; + + ykpiv_disconnect(piv_state); + + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_CloseAllSessions)( + CK_SLOT_ID slotID +) +{ + CK_RV rv; + + DIN; + + if (piv_state == NULL) { + DBG("libykpiv is not initialized or already finalized"); + return CKR_CRYPTOKI_NOT_INITIALIZED; + } + + if (session.slot != slots + slotID) { + DBG("Invalid slot ID %lu", slotID); + return CKR_SLOT_ID_INVALID; + } + + rv = C_CloseSession(session.handle); + + DOUT; + return rv; +} + +CK_DEFINE_FUNCTION(CK_RV, C_GetSessionInfo)( + CK_SESSION_HANDLE hSession, + CK_SESSION_INFO_PTR pInfo +) +{ + DIN; + + if (piv_state == NULL) { + DBG("libykpiv is not initialized or already finalized"); + return CKR_CRYPTOKI_NOT_INITIALIZED; + } + + if (pInfo == NULL) { + DBG("Wrong/Missing parameter"); + return CKR_ARGUMENTS_BAD; + } + + if (hSession != session.handle) { + DBG("Unknown session %lu", hSession); + return CKR_SESSION_HANDLE_INVALID; + } + + memcpy(pInfo, &session.info, sizeof(CK_SESSION_INFO)); + + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_GetOperationState)( + CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pOperationState, + CK_ULONG_PTR pulOperationStateLen +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_SetOperationState)( + CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pOperationState, + CK_ULONG ulOperationStateLen, + CK_OBJECT_HANDLE hEncryptionKey, + CK_OBJECT_HANDLE hAuthenticationKey +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_Login)( + CK_SESSION_HANDLE hSession, + CK_USER_TYPE userType, + CK_UTF8CHAR_PTR pPin, + CK_ULONG ulPinLen +) +{ + DIN; + CK_RV rv; + token_vendor_t token; + + if (piv_state == NULL) { + DBG("libykpiv is not initialized or already finalized"); + return CKR_CRYPTOKI_NOT_INITIALIZED; + } + + if (userType != CKU_SO && + userType != CKU_USER && + userType != CKU_CONTEXT_SPECIFIC) + return CKR_USER_TYPE_INVALID; + + DBG("user %lu, pin %s, pinlen %lu", userType, pPin, ulPinLen); + + if (session.handle != YKCS11_SESSION_ID) { + DBG("Session is not open"); + return CKR_SESSION_CLOSED; + } + + if (hSession != session.handle) { + DBG("Unknown session %lu", hSession); + return CKR_SESSION_HANDLE_INVALID; + } + + if ((session.info.flags & CKF_RW_SESSION) == 0) { // TODO: make macros for these? + DBG("Tried to log-in to a read-only session"); + return CKR_SESSION_READ_ONLY_EXISTS; + } + + token = get_token_vendor(session.slot->token->vid); + + switch (userType) { + case CKU_USER: + if (ulPinLen < PIV_MIN_PIN_LEN || ulPinLen > PIV_MAX_PIN_LEN) + return CKR_ARGUMENTS_BAD; + + /*if (session.info.state == CKS_RW_USER_FUNCTIONS) { + DBG("This user type is already logged in"); + return CKR_USER_ALREADY_LOGGED_IN; + }*/ //TODO: FIx to allow multiple login. Decide on context specific. + + if (session.info.state == CKS_RW_SO_FUNCTIONS) { + DBG("A different uyser type is already logged in"); + return CKR_USER_ANOTHER_ALREADY_LOGGED_IN; + } + + rv = token.token_login(piv_state, CKU_USER, pPin, ulPinLen); + if (rv != CKR_OK) { + DBG("Unable to login as regular user"); + return rv; + } + + if ((session.info.flags & CKF_RW_SESSION) == 0) + session.info.state = CKS_RO_USER_FUNCTIONS; + else + session.info.state = CKS_RW_USER_FUNCTIONS; + break; + + case CKU_SO: + if (ulPinLen != PIV_MGM_KEY_LEN) + return CKR_ARGUMENTS_BAD; + + if (session.info.state == CKS_RW_SO_FUNCTIONS) + return CKR_USER_ALREADY_LOGGED_IN; + + if (session.info.state == CKS_RO_USER_FUNCTIONS || + session.info.state == CKS_RW_USER_FUNCTIONS) + return CKR_USER_ANOTHER_ALREADY_LOGGED_IN; + + rv = token.token_login(piv_state, CKU_SO, pPin, ulPinLen); + if (rv != CKR_OK) { + DBG("Unable to login as SO"); + return rv; + } + + session.info.state = CKS_RW_SO_FUNCTIONS; + break; + + case CKU_CONTEXT_SPECIFIC: + if (op_info.type == YKCS11_NOOP) { + DBG("No operation in progress. Context specific user is forbidden."); + return CKR_USER_TYPE_INVALID; + } + if (op_info.type == YKCS11_SIGN) { + return C_Login(hSession, CKU_USER, pPin, ulPinLen); + } + else + return C_Login(hSession, CKU_SO, pPin, ulPinLen); + + default: + return CKR_USER_TYPE_INVALID; + } + + DBG("Successfully logged in"); + + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_Logout)( + CK_SESSION_HANDLE hSession +) +{ + DIN; + + if (piv_state == NULL) { + DBG("libykpiv is not initialized or already finalized"); + return CKR_CRYPTOKI_NOT_INITIALIZED; + } + + if (session.handle != YKCS11_SESSION_ID) { + DBG("Session is not open"); + return CKR_SESSION_CLOSED; + } + + if (hSession != session.handle) { + DBG("Unknown session %lu", hSession); + return CKR_SESSION_HANDLE_INVALID; + } + + if (session.info.state == CKS_RO_PUBLIC_SESSION || + session.info.state == CKS_RW_PUBLIC_SESSION) + return CKR_USER_NOT_LOGGED_IN; + + if ((session.info.flags & CKF_RW_SESSION) == 0) + session.info.state = CKS_RO_PUBLIC_SESSION; + else + session.info.state = CKS_RW_PUBLIC_SESSION; + + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_CreateObject)( + CK_SESSION_HANDLE hSession, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulCount, + CK_OBJECT_HANDLE_PTR phObject +) +{ + CK_ULONG i; + CK_RV rv; + CK_OBJECT_CLASS class; + CK_BYTE id; + CK_BYTE_PTR value; + CK_ULONG value_len; + CK_BYTE_PTR p; + CK_BYTE_PTR q; + CK_BYTE_PTR dp; + CK_BYTE_PTR dq; + CK_BYTE_PTR qinv; + CK_ULONG vendor_defined; + token_vendor_t token; + CK_BBOOL is_new; + CK_BBOOL is_rsa; + CK_OBJECT_HANDLE object; + CK_ULONG cert_id; + CK_ULONG pvtk_id; + CK_ULONG pubk_id; + piv_obj_id_t *obj_ptr; + + DIN; + + if (piv_state == NULL) { + DBG("libykpiv is not initialized or already finalized"); + return CKR_CRYPTOKI_NOT_INITIALIZED; + } + + if (session.handle != YKCS11_SESSION_ID) { + DBG("Session is not open"); + return CKR_SESSION_CLOSED; + } + + if (hSession != session.handle) { + DBG("Unknown session %lu", hSession); + return CKR_SESSION_HANDLE_INVALID; + } + + if (session.info.state != CKS_RW_SO_FUNCTIONS) { + DBG("Authentication required to import objects"); + return CKR_SESSION_READ_ONLY; + } + + if (pTemplate == NULL_PTR || + phObject == NULL_PTR) { + DBG("Wrong/Missing parameter"); + return CKR_ARGUMENTS_BAD; + } + + class = CKO_VENDOR_DEFINED; // Use this as a known value + for (i = 0; i < ulCount; i++) { + if (pTemplate[i].type == CKA_CLASS) { + class = *((CK_ULONG_PTR)pTemplate[i].pValue); + + // Can only import certificates and private keys + if (*((CK_ULONG_PTR)pTemplate[i].pValue) != CKO_CERTIFICATE && + *((CK_ULONG_PTR)pTemplate[i].pValue) != CKO_PRIVATE_KEY) { + DBG("Unsupported class %lu", class); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + } + } + + if (class == CKO_VENDOR_DEFINED) { + DBG("Object class must be specified"); + return CKR_TEMPLATE_INCOMPLETE; + } + + token = get_token_vendor(session.slot->token->vid); + + switch (class) { + case CKO_CERTIFICATE: + DBG("Importing certificate"); + + rv = check_create_cert(pTemplate, ulCount, &id, &value, &value_len); + if (rv != CKR_OK) { + DBG("Certificate template not valid"); + return rv; + } + DBG("Certificate id is %u", id); + + object = PIV_CERT_OBJ_X509_PIV_AUTH + id; + + rv = token.token_import_cert(piv_state, piv_2_ykpiv(object), value); // TODO: make function to get cert id + if (rv != CKR_OK) { + DBG("Unable to import certificate"); + return rv; + } + + is_new = CK_TRUE; + for (i = 0; i < session.slot->token->n_objects; i++) { + if (session.slot->token->objects[i] == object) + is_new = CK_FALSE; + } + + cert_id = PIV_CERT_OBJ_X509_PIV_AUTH + id; // TODO: make function for these + pvtk_id = PIV_PVTK_OBJ_PIV_AUTH + id; + pubk_id = PIV_PUBK_OBJ_PIV_AUTH + id; + + // Check whether we created a new object or updated an existing one + if (is_new == CK_TRUE) { + // New object created, add it to the object list + + // Each object counts as three, even if we just only added a certificate + session.slot->token->n_objects += 3; + session.slot->token->n_certs++; + + obj_ptr = realloc(session.slot->token->objects, session.slot->token->n_objects * sizeof(piv_obj_id_t)); + if (obj_ptr == NULL) { + DBG("Unable to store new item in the session"); + return CKR_HOST_MEMORY; + } + session.slot->token->objects = obj_ptr; + + obj_ptr = session.slot->token->objects + session.slot->token->n_objects - 3; + *obj_ptr++ = cert_id; + *obj_ptr++ = pvtk_id; + *obj_ptr++ = pubk_id; + } + + rv = store_cert(cert_id, value, value_len); + if (rv != CKR_OK) { + DBG("Unable to store certificate data"); + return CKR_FUNCTION_FAILED; + } + + break; + + case CKO_PRIVATE_KEY: + DBG("Importing private key"); + + // Try to parse the key as EC + is_rsa = CK_FALSE; + rv = check_create_ec_key(pTemplate, ulCount, &id, &value, &value_len, &vendor_defined); + if (rv != CKR_OK) { + // Try to parse the key as RSA + is_rsa = CK_TRUE; + rv = check_create_rsa_key(pTemplate, ulCount, &id, &p, &q, &dp, &dq, &qinv, &value_len, &vendor_defined); + if (rv != CKR_OK) { + DBG("Private key template not valid"); + return rv; + } + } + + DBG("Key id is %u", id); + + object = PIV_PVTK_OBJ_PIV_AUTH + id; + + if (is_rsa == CK_TRUE) { + DBG("Key is RSA"); + rv = token.token_import_private_key(piv_state, piv_2_ykpiv(object), p, q, dp, dq, qinv, + NULL, + value_len, vendor_defined); + if (rv != CKR_OK) { + DBG("Unable to import RSA private key"); + return rv; + } + } + else { + DBG("Key is ECDSA"); + rv = token.token_import_private_key(piv_state, piv_2_ykpiv(object), NULL, NULL, NULL, NULL, NULL, + value, + value_len, vendor_defined); + if (rv != CKR_OK) { + DBG("Unable to import ECDSA private key"); + return rv; + } + } + break; + + default: + DBG("Unknown object type"); + return CKR_ATTRIBUTE_TYPE_INVALID; + } + + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_CopyObject)( + CK_SESSION_HANDLE hSession, + CK_OBJECT_HANDLE hObject, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulCount, + CK_OBJECT_HANDLE_PTR phNewObject +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_DestroyObject)( + CK_SESSION_HANDLE hSession, + CK_OBJECT_HANDLE hObject +) +{ + CK_RV rv; + token_vendor_t token; + CK_ULONG i; + CK_ULONG j; + CK_BYTE id; + CK_ULONG cert_id; + CK_ULONG pvtk_id; + CK_ULONG pubk_id; + piv_obj_id_t *obj_ptr; + + DIN; + + DBG("Deleting object %lu", hObject); + + if (piv_state == NULL) { + DBG("libykpiv is not initialized or already finalized"); + return CKR_CRYPTOKI_NOT_INITIALIZED; + } + + if (session.handle != YKCS11_SESSION_ID) { + DBG("Session is not open"); + return CKR_SESSION_CLOSED; + } + + if (hSession != session.handle) { + DBG("Unknown session %lu", hSession); + return CKR_SESSION_HANDLE_INVALID; + } + + // Only certificates can be deleted + // SO must be logged in + if (session.info.state != CKS_RW_SO_FUNCTIONS) { + DBG("Unable to delete objects, SO must be logged in"); + return CKR_ACTION_PROHIBITED; + } + + rv = check_delete_cert(hObject, &id); + if (rv != CKR_OK) { + DBG("Object %lu can not be deleted", hObject); + return rv; + } + + token = get_token_vendor(session.slot->token->vid); + + rv = token.token_delete_cert(piv_state, piv_2_ykpiv(hObject)); + if (rv != CKR_OK) { + DBG("Unable to delete object %lu", hObject); + return rv; + } + + // Remove the object from the session + // Do it in a slightly inefficient way but preserve ordering + + cert_id = PIV_CERT_OBJ_X509_PIV_AUTH + id; // TODO: make function for these + pvtk_id = PIV_PVTK_OBJ_PIV_AUTH + id; + pubk_id = PIV_PUBK_OBJ_PIV_AUTH + id; + + obj_ptr = malloc((session.slot->token->n_objects - 3) * sizeof(piv_obj_id_t)); + if (obj_ptr == NULL) { + DBG("Unable to allocate memory"); + return CKR_HOST_MEMORY; + } + + i = 0; + j = 0; + while (i < session.slot->token->n_objects) { + if (session.slot->token->objects[i] == cert_id || + session.slot->token->objects[i] == pvtk_id || + session.slot->token->objects[i] == pubk_id) { + i++; + continue; + } + + obj_ptr[j++] = session.slot->token->objects[i++]; + } + + rv = delete_cert(cert_id); + if (rv != CKR_OK) { + DBG("Unable to delete certificate data"); + return CKR_FUNCTION_FAILED; + } + + free(session.slot->token->objects); + + session.slot->token->n_objects -= 3; + session.slot->token->n_certs--; + session.slot->token->objects = obj_ptr; + + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_GetObjectSize)( + CK_SESSION_HANDLE hSession, + CK_OBJECT_HANDLE hObject, + CK_ULONG_PTR pulSize +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_GetAttributeValue)( + CK_SESSION_HANDLE hSession, + CK_OBJECT_HANDLE hObject, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulCount +) +{ + CK_ULONG i; + CK_RV rv, rv_final; + + DIN; + + if (piv_state == NULL) { + DBG("libykpiv is not initialized or already finalized"); + return CKR_CRYPTOKI_NOT_INITIALIZED; + } + + if (session.handle != YKCS11_SESSION_ID) { + DBG("Session is not open"); + return CKR_SESSION_CLOSED; + } + + if (hSession != session.handle) { + DBG("Unknown session %lu", hSession); + return CKR_SESSION_HANDLE_INVALID; + } + + if (pTemplate == NULL_PTR || ulCount == 0) + return CKR_ARGUMENTS_BAD; + + rv_final = CKR_OK; + for (i = 0; i < ulCount; i++) { + + rv = get_attribute(&session, hObject, pTemplate + i); + + // TODO: this function has some complex cases for return vlaue. Make sure to check them. + if (rv != CKR_OK) { + DBG("Unable to get attribute 0x%lx of object %lu", (pTemplate + i)->type, hObject); + rv_final = rv; + } + } + + DOUT; + return rv_final; +} + +CK_DEFINE_FUNCTION(CK_RV, C_SetAttributeValue)( + CK_SESSION_HANDLE hSession, + CK_OBJECT_HANDLE hObject, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulCount +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_FindObjectsInit)( + CK_SESSION_HANDLE hSession, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulCount +) +{ + CK_ULONG i; + CK_ULONG j; + CK_ULONG total; + CK_BBOOL private; + + DIN; + + if (piv_state == NULL) { + DBG("libykpiv is not initialized or already finalized"); + return CKR_CRYPTOKI_NOT_INITIALIZED; + } + + if (session.handle != YKCS11_SESSION_ID) { + DBG("Session is not open"); + return CKR_SESSION_CLOSED; + } + + if (hSession != session.handle) { + DBG("Unknown session %lu", hSession); + return CKR_SESSION_HANDLE_INVALID; + } + + if (find_obj.active == CK_TRUE) + return CKR_OPERATION_ACTIVE; + + if (ulCount != 0 && pTemplate == NULL_PTR) + return CKR_ARGUMENTS_BAD; + + find_obj.idx = 0; + find_obj.num = session.slot->token->n_objects; + + // Check if we should remove private objects + if (session.info.state == CKS_RO_PUBLIC_SESSION || + session.info.state == CKS_RW_PUBLIC_SESSION) { + DBG("Removing private objects because state is %lu", session.info.state); + private = CK_FALSE; + } + else { + DBG("Keeping private objects"); + private = CK_TRUE; + } + + find_obj.objects = malloc(sizeof(piv_obj_id_t) * find_obj.num); + if (find_obj.objects == NULL) { + DBG("Unable to allocate memory for finding objects"); + return CKR_HOST_MEMORY; + } + memcpy(find_obj.objects, session.slot->token->objects, sizeof(piv_obj_id_t) * find_obj.num); + + DBG("Initialized search with %lu parameters", ulCount); + + // Match parameters + total = find_obj.num; + for (i = 0; i < find_obj.num; i++) { + + if (find_obj.objects[i] == OBJECT_INVALID) + continue; // Object already discarded, keep going + + // Strip away private objects if needed + if (private == CK_FALSE) + if (is_private_object(&session, find_obj.objects[i]) == CK_TRUE) { + DBG("Stripping away private object %u", find_obj.objects[i]); + find_obj.objects[i] = OBJECT_INVALID; + total--; + continue; + } + + for (j = 0; j < ulCount; j++) { + DBG("Parameter %lu\nType: %lu Value: %lu Len: %lu", j, pTemplate[j].type, *((CK_ULONG_PTR)pTemplate[j].pValue), pTemplate[j].ulValueLen); + + if (attribute_match(&session, find_obj.objects[i], pTemplate + j) == CK_FALSE) { + DBG("Removing object %u from the list", find_obj.objects[i]); + find_obj.objects[i] = OBJECT_INVALID; // Object not matching, mark it + total--; + break; + } + else + DBG("Keeping object %u in the list", find_obj.objects[i]); + } + } + + DBG("%lu object(s) left after attribute matching", total); + + find_obj.active = CK_TRUE; + + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_FindObjects)( + CK_SESSION_HANDLE hSession, + CK_OBJECT_HANDLE_PTR phObject, + CK_ULONG ulMaxObjectCount, + CK_ULONG_PTR pulObjectCount +) +{ + DIN; + + if (piv_state == NULL) { + DBG("libykpiv is not initialized or already finalized"); + return CKR_CRYPTOKI_NOT_INITIALIZED; + } + + if (session.handle != YKCS11_SESSION_ID) { + DBG("Session is not open"); + return CKR_SESSION_CLOSED; + } + + if (hSession != session.handle) { + DBG("Unknown session %lu", hSession); + return CKR_SESSION_HANDLE_INVALID; + } + + if (phObject == NULL_PTR || + ulMaxObjectCount == 0 || + pulObjectCount == NULL_PTR) + return CKR_ARGUMENTS_BAD; + + if (find_obj.active != CK_TRUE) + return CKR_OPERATION_NOT_INITIALIZED; + + DBG("Can return %lu object(s)", ulMaxObjectCount); + + // Return the next object, if any + while(find_obj.idx < find_obj.num && + find_obj.objects[find_obj.idx] == OBJECT_INVALID) + find_obj.idx++; + + if (find_obj.idx == find_obj.num) { + *pulObjectCount = 0; + DOUT; + return CKR_OK; + } + + *phObject = find_obj.objects[find_obj.idx++]; + *pulObjectCount = 1; + + DBG("Returning object %lu", *phObject); + + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_FindObjectsFinal)( + CK_SESSION_HANDLE hSession +) +{ + DIN; + + if (piv_state == NULL) { + DBG("libykpiv is not initialized or already finalized"); + return CKR_CRYPTOKI_NOT_INITIALIZED; + } + + if (session.handle != YKCS11_SESSION_ID) { + DBG("Session is not open"); + return CKR_SESSION_CLOSED; + } + + if (hSession != session.handle) { + DBG("Unknown session %lu", hSession); + return CKR_SESSION_HANDLE_INVALID; + } + + if (find_obj.active != CK_TRUE) + return CKR_OPERATION_NOT_INITIALIZED; + + free(find_obj.objects); + find_obj.objects = NULL; + + find_obj.active = CK_FALSE; + + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_EncryptInit)( + CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_Encrypt)( + CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pData, + CK_ULONG ulDataLen, + CK_BYTE_PTR pEncryptedData, + CK_ULONG_PTR pulEncryptedDataLen +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_EncryptUpdate)( + CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pPart, + CK_ULONG ulPartLen, + CK_BYTE_PTR pEncryptedPart, + CK_ULONG_PTR pulEncryptedPartLen +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_EncryptFinal)( + CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pLastEncryptedPart, + CK_ULONG_PTR pulLastEncryptedPartLen +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_DecryptInit)( + CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_Decrypt)( + CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pEncryptedData, + CK_ULONG ulEncryptedDataLen, + CK_BYTE_PTR pData, + CK_ULONG_PTR pulDataLen +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_DecryptUpdate)( + CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pEncryptedPart, + CK_ULONG ulEncryptedPartLen, + CK_BYTE_PTR pPart, + CK_ULONG_PTR pulPartLen +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_DecryptFinal)( + CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pLastPart, + CK_ULONG_PTR pulLastPartLen +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_DigestInit)( + CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism +) +{ + DIN; + + if (piv_state == NULL) { + DBG("libykpiv is not initialized or already finalized"); + return CKR_CRYPTOKI_NOT_INITIALIZED; + } + + if (session.handle != YKCS11_SESSION_ID) { + DBG("Session is not open"); + return CKR_SESSION_CLOSED; + } + + if (hSession != session.handle) { + DBG("Unknown session %lu", hSession); + return CKR_SESSION_HANDLE_INVALID; + } + + if (op_info.type != YKCS11_NOOP) { + DBG("Other operation in process"); + return CKR_OPERATION_ACTIVE; + } + + if (pMechanism == NULL_PTR) { + DBG("Wrong/Missing parameter"); + return CKR_ARGUMENTS_BAD; + } + + DBG("Trying to hash some data with mechanism %lu", pMechanism->mechanism); + + // Check if mechanism is supported + if (check_hash_mechanism(&session, pMechanism) != CKR_OK) { + DBG("Mechanism %lu is not supported either by the token or the module", pMechanism->mechanism); + return CKR_MECHANISM_INVALID; + } + memcpy(&op_info.mechanism, pMechanism, sizeof(CK_MECHANISM)); + + op_info.type = YKCS11_HASH; + + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_Digest)( + CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pData, + CK_ULONG ulDataLen, + CK_BYTE_PTR pDigest, + CK_ULONG_PTR pulDigestLen +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_DigestUpdate)( + CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pPart, + CK_ULONG ulPartLen +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_DigestKey)( + CK_SESSION_HANDLE hSession, + CK_OBJECT_HANDLE hKey +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_DigestFinal)( + CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pDigest, + CK_ULONG_PTR pulDigestLen +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_SignInit)( + CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey +) +{ + CK_KEY_TYPE type = 0; + CK_ULONG key_len = 0; + CK_BYTE buf[1024]; + CK_ATTRIBUTE template[] = { + {CKA_KEY_TYPE, &type, sizeof(type)}, + {CKA_MODULUS_BITS, &key_len, sizeof(key_len)}, + {CKA_MODULUS, NULL, 0}, + {CKA_EC_POINT, buf, sizeof(buf)}, + }; + + DIN; + + if (piv_state == NULL) { + DBG("libykpiv is not initialized or already finalized"); + return CKR_CRYPTOKI_NOT_INITIALIZED; + } + + if (session.handle != YKCS11_SESSION_ID) { + DBG("Session is not open"); + return CKR_SESSION_CLOSED; + } + + if (hSession != session.handle) { + DBG("Unknown session %lu", hSession); + return CKR_SESSION_HANDLE_INVALID; + } + + if (op_info.type != YKCS11_NOOP) { + DBG("Other operation in process"); + return CKR_OPERATION_ACTIVE; + } + + if (pMechanism == NULL_PTR || + hKey == NULL_PTR) + return CKR_ARGUMENTS_BAD; + + DBG("Trying to sign some data with mechanism %lu and key %lu", pMechanism->mechanism, hKey); + + // Check if mechanism is supported + if (check_sign_mechanism(&session, pMechanism) != CKR_OK) { + DBG("Mechanism %lu is not supported either by the token or the module", pMechanism->mechanism); + return CKR_MECHANISM_INVALID; // TODO: also the key has a list of allowed mechanisms, check that + } + memcpy(&op_info.mechanism, pMechanism, sizeof(CK_MECHANISM)); + + // Get key algorithm + if (get_attribute(&session, hKey, template) != CKR_OK) { + DBG("Unable to get key type"); + return CKR_KEY_HANDLE_INVALID; + } + + DBG("Key type is %lu\n", type); + + // Get key length and algorithm type + if (type == CKK_RSA) { + // RSA key + if (get_attribute(&session, hKey, template + 1) != CKR_OK) { + DBG("Unable to get key length"); + return CKR_KEY_HANDLE_INVALID; + } + + op_info.op.sign.key_len = key_len; + + if (key_len == 1024) + op_info.op.sign.algo = YKPIV_ALGO_RSA1024; + else + op_info.op.sign.algo = YKPIV_ALGO_RSA2048; + + // Also store the raw public key if the mechanism is PSS + if (is_PSS_mechanism(pMechanism->mechanism)) { + op_info.op.sign.key = malloc(key_len); + if (op_info.op.sign.key == NULL) + return CKR_HOST_MEMORY; + + template[2].pValue = op_info.op.sign.key; + template[2].ulValueLen = key_len; + + if (get_attribute(&session, hKey, template + 2) != CKR_OK) { + DBG("Unable to get public key"); + return CKR_KEY_HANDLE_INVALID; + } + + } + else { + op_info.op.sign.key = NULL; + } + + } + else { + // ECDSA key + if (get_attribute(&session, hKey, template + 3) != CKR_OK) { + DBG("Unable to get key length"); + return CKR_KEY_HANDLE_INVALID; + } + + // The buffer contains an uncompressed point of the form 04, len, 04, x, y + // Where len is |x| + |y| + 1 bytes + + op_info.op.sign.key_len = (CK_ULONG) (((buf[1] - 1) / 2) * 8); + + if (op_info.op.sign.key_len == 256) + op_info.op.sign.algo = YKPIV_ALGO_ECCP256; + /*else + op_info.op.sign.algo = YKPIV_ALGO_ECCP384;*/ // TODO: add support for P384 + } + + DBG("Key length is %lu bit", op_info.op.sign.key_len); + + op_info.op.sign.key_id = piv_2_ykpiv(hKey); + if (op_info.op.sign.key_id == 0) { + DBG("Incorrect key %lu", hKey); + return CKR_KEY_HANDLE_INVALID; + } + + DBG("Algorithm is %d", op_info.op.sign.algo); + // Make sure that both mechanism and key have the same algorithm + if ((is_RSA_mechanism(pMechanism->mechanism) && op_info.op.sign.algo == YKPIV_ALGO_ECCP256) || + (!is_RSA_mechanism(pMechanism->mechanism) && (op_info.op.sign.algo != YKPIV_ALGO_ECCP256))) { + DBG("Key and mechanism algorithm do not match"); + return CKR_ARGUMENTS_BAD; + } + + op_info.type = YKCS11_SIGN; + + // TODO: check mechanism parameters and key length and key supported parameters + + if (apply_sign_mechanism_init(&op_info) != CKR_OK) { + DBG("Unable to initialize signing operation"); + return CKR_FUNCTION_FAILED; + } + + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_Sign)( + CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pData, + CK_ULONG ulDataLen, + CK_BYTE_PTR pSignature, + CK_ULONG_PTR pulSignatureLen +) +{ + ykpiv_rc piv_rv; + CK_RV rv; + + DIN; + + if (op_info.type != YKCS11_SIGN) { + DBG("Signature operation not initialized"); + rv = CKR_OPERATION_NOT_INITIALIZED; + goto sign_out; + } + + if (session.handle != YKCS11_SESSION_ID) { + DBG("Session is not open"); + rv = CKR_SESSION_CLOSED; + goto sign_out; + } + + if (hSession != session.handle) { + DBG("Unknown session %lu", hSession); + rv = CKR_SESSION_HANDLE_INVALID; + goto sign_out; + } + + if (op_info.type != YKCS11_SIGN) { + DBG("Operation not initialized"); + rv = CKR_OPERATION_NOT_INITIALIZED; + goto sign_out; + } + + if (session.info.state == CKS_RO_PUBLIC_SESSION || + session.info.state == CKS_RW_PUBLIC_SESSION) { + DBG("User is not logged in"); + rv = CKR_USER_NOT_LOGGED_IN; + goto sign_out; + } + + if (pSignature == NULL_PTR) { + // Just return the size of the signature + *pulSignatureLen = op_info.op.sign.key_len / 8 * 2 + 32; // Approximate the size of the signature. Specs agree with this. + DBG("The size of the signature will be %lu", *pulSignatureLen); + + DOUT; + return CKR_OK; + } + + DBG("Sending %lu bytes to sign", ulDataLen); + dump_hex(pData, ulDataLen, stderr, CK_TRUE); + + if (is_hashed_mechanism(op_info.mechanism.mechanism) == CK_TRUE) { + if (apply_sign_mechanism_update(&op_info, pData, ulDataLen) != CKR_OK) { + DBG("Unable to perform signing operation step"); + rv = CKR_FUNCTION_FAILED; // TODO: every error in here must stop and clear the signing operation + goto sign_out; + } + } + else { + if (is_RSA_mechanism(op_info.mechanism.mechanism)) { + // RSA_X_509 + if (ulDataLen > (op_info.op.sign.key_len / 8)) { + DBG("Data must be shorter than key length (%lu bits)", op_info.op.sign.key_len); + rv = CKR_FUNCTION_FAILED; + goto sign_out; + } + } + else { + // ECDSA + if (ulDataLen > 128) { + // Specs say ECDSA only supports 1024 bit + DBG("Maximum data length for ECDSA is 128 bytes"); + rv = CKR_FUNCTION_FAILED; + goto sign_out; + } + } + + op_info.buf_len = ulDataLen; + memcpy(op_info.buf, pData, ulDataLen); + } + + if (apply_sign_mechanism_finalize(&op_info) != CKR_OK) { + DBG("Unable to finalize signing operation"); + rv = CKR_FUNCTION_FAILED; + goto sign_out; + } + + DBG("Using key %lx", op_info.op.sign.key_id); + DBG("After padding and transformation there are %lu bytes", op_info.buf_len); + dump_hex(op_info.buf, op_info.buf_len, stderr, CK_TRUE); + + *pulSignatureLen = sizeof(op_info.buf); + + piv_rv = ykpiv_sign_data(piv_state, op_info.buf, op_info.buf_len, pSignature, pulSignatureLen, op_info.op.sign.algo, op_info.op.sign.key_id); + if (piv_rv != YKPIV_OK) { + if (piv_rv == YKPIV_AUTHENTICATION_ERROR) { + DBG("Operation requires authentication or touch"); + rv = CKR_USER_NOT_LOGGED_IN; + goto sign_out; + } + else { + DBG("Sign error, %s", ykpiv_strerror(piv_rv)); + rv = CKR_FUNCTION_FAILED; + goto sign_out; + } + } + + DBG("Got %lu bytes back", *pulSignatureLen); + dump_hex(pSignature, *pulSignatureLen, stderr, CK_TRUE); + + if (!is_RSA_mechanism(op_info.mechanism.mechanism)) { + // ECDSA, we must remove the DER encoding and only return R,S + // as required by the specs + strip_DER_encoding_from_ECSIG(pSignature, pulSignatureLen); + + DBG("After removing DER encoding %lu", *pulSignatureLen); + dump_hex(pSignature, *pulSignatureLen, stderr, CK_TRUE); + } + + op_info.type = YKCS11_NOOP; + + rv = CKR_OK; + + sign_out: + op_info.type = YKCS11_NOOP; + sign_mechanism_cleanup(&op_info); + + DOUT; + return rv; +} + +CK_DEFINE_FUNCTION(CK_RV, C_SignUpdate)( + CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pPart, + CK_ULONG ulPartLen +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_SignFinal)( + CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pSignature, + CK_ULONG_PTR pulSignatureLen +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_SignRecoverInit)( + CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_SignRecover)( + CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pData, + CK_ULONG ulDataLen, + CK_BYTE_PTR pSignature, + CK_ULONG_PTR pulSignatureLen +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_VerifyInit)( + CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_Verify)( + CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pData, + CK_ULONG ulDataLen, + CK_BYTE_PTR pSignature, + CK_ULONG ulSignatureLen +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_VerifyUpdate)( + CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pPart, + CK_ULONG ulPartLen +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_VerifyFinal)( + CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pSignature, + CK_ULONG ulSignatureLen +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_VerifyRecoverInit)( + CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_VerifyRecover)( + CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pSignature, + CK_ULONG ulSignatureLen, + CK_BYTE_PTR pData, + CK_ULONG_PTR pulDataLen +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_DigestEncryptUpdate)( + CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pPart, + CK_ULONG ulPartLen, + CK_BYTE_PTR pEncryptedPart, + CK_ULONG_PTR pulEncryptedPartLen +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_DecryptDigestUpdate)( + CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pEncryptedPart, + CK_ULONG ulEncryptedPartLen, + CK_BYTE_PTR pPart, + CK_ULONG_PTR pulPartLen +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_SignEncryptUpdate)( + CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pPart, + CK_ULONG ulPartLen, + CK_BYTE_PTR pEncryptedPart, + CK_ULONG_PTR pulEncryptedPartLen +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_DecryptVerifyUpdate)( + CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pEncryptedPart, + CK_ULONG ulEncryptedPartLen, + CK_BYTE_PTR pPart, + CK_ULONG_PTR pulPartLen +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_GenerateKey)( + CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulCount, + CK_OBJECT_HANDLE_PTR phKey +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_GenerateKeyPair)( + CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_ATTRIBUTE_PTR pPublicKeyTemplate, + CK_ULONG ulPublicKeyAttributeCount, + CK_ATTRIBUTE_PTR pPrivateKeyTemplate, + CK_ULONG ulPrivateKeyAttributeCount, + CK_OBJECT_HANDLE_PTR phPublicKey, + CK_OBJECT_HANDLE_PTR phPrivateKey +) +{ + CK_RV rv; + token_vendor_t token; + CK_ULONG i; + CK_BBOOL is_new; + CK_ULONG dobj_id; + CK_ULONG cert_id; + CK_ULONG pvtk_id; + CK_ULONG pubk_id; + piv_obj_id_t *obj_ptr; + CK_BYTE cert_data[2100]; + CK_ULONG cert_len; + + DIN; + + if (piv_state == NULL) { + DBG("libykpiv is not initialized or already finalized"); + return CKR_CRYPTOKI_NOT_INITIALIZED; + } + + if (session.handle != YKCS11_SESSION_ID) { + DBG("Session is not open"); + return CKR_SESSION_CLOSED; + } + + if (hSession != session.handle) { + DBG("Unknown session %lu", hSession); + return CKR_SESSION_HANDLE_INVALID; + } + + if (session.info.state != CKS_RW_SO_FUNCTIONS) { + DBG("Authentication required to generate keys"); + return CKR_SESSION_READ_ONLY; + } + + if (op_info.type != YKCS11_NOOP) { + DBG("Other operation in process"); + return CKR_OPERATION_ACTIVE; + } + + if (pMechanism == NULL_PTR || + pPublicKeyTemplate == NULL_PTR || + pPrivateKeyTemplate == NULL_PTR || + phPublicKey == NULL_PTR || + phPrivateKey == NULL_PTR) { + DBG("Wrong/Missing parameter"); + return CKR_ARGUMENTS_BAD; + } + + DBG("Trying to generate a key pair with mechanism %lx", pMechanism->mechanism); + + DBG("Found %lu attributes for the public key and %lu attributes for the private key", ulPublicKeyAttributeCount, ulPrivateKeyAttributeCount); + + // Check if mechanism is supported + if ((rv = check_generation_mechanism(&session, pMechanism)) != CKR_OK) { + DBG("Mechanism %lu is not supported either by the token or the module", pMechanism->mechanism); + return rv; + } + memcpy(&op_info.mechanism, pMechanism, sizeof(CK_MECHANISM)); + + // Clear values + op_info.op.gen.key_len = 0; + op_info.op.gen.key_id = 0; + + // Check the template for the public key + if ((rv = check_pubkey_template(&op_info, pPublicKeyTemplate, ulPublicKeyAttributeCount)) != CKR_OK) { + DBG("Invalid public key template"); + return rv; + } + + // Check the template for the private key + if ((rv = check_pvtkey_template(&op_info, pPrivateKeyTemplate, ulPrivateKeyAttributeCount)) != CKR_OK) { + DBG("Invalid private key template"); + return rv; + } + + if (op_info.op.gen.key_len == 0) { + DBG("Key length not specified"); + return CKR_TEMPLATE_INCOMPLETE; + } + + if (op_info.op.gen.key_id == 0) { + DBG("Key id not specified"); + return CKR_TEMPLATE_INCOMPLETE; + } + + if (op_info.op.gen.rsa) { + DBG("Generating %lu bit RSA key in object %u", op_info.op.gen.key_len, op_info.op.gen.key_id); + } + else { + DBG("Generating %lu bit EC key in object %u", op_info.op.gen.key_len, op_info.op.gen.key_id); + } + + token = get_token_vendor(session.slot->token->vid); + + if ((rv = token.token_generate_key(piv_state, op_info.op.gen.rsa, piv_2_ykpiv(op_info.op.gen.key_id), op_info.op.gen.key_len, op_info.op.gen.vendor_defined)) != CKR_OK) { + DBG("Unable to generate key pair"); + return rv; + } + + is_new = CK_TRUE; + for (i = 0; i < session.slot->token->n_objects; i++) { + if (session.slot->token->objects[i] == op_info.op.gen.key_id) + is_new = CK_FALSE; + } + + dobj_id = op_info.op.gen.key_id - PIV_PVTK_OBJ_PIV_AUTH; // TODO: make function for these + cert_id = PIV_DATA_OBJ_LAST + 1 + dobj_id; + pvtk_id = op_info.op.gen.key_id; + pubk_id = PIV_PVTK_OBJ_LAST + 1 + dobj_id; + + // Check whether we created a new object or updated an existing one + if (is_new == CK_TRUE) { + // New object created, add it to the object list + + // Each object counts as three (data object is always there) + session.slot->token->n_objects += 3; + session.slot->token->n_certs++; + + obj_ptr = realloc(session.slot->token->objects, session.slot->token->n_objects * sizeof(piv_obj_id_t)); + if (obj_ptr == NULL) { + DBG("Unable to store new item in the session"); + return CKR_HOST_MEMORY; + } + session.slot->token->objects = obj_ptr; + + obj_ptr = session.slot->token->objects + session.slot->token->n_objects - 3; + *obj_ptr++ = cert_id; + *obj_ptr++ = pvtk_id; + *obj_ptr++ = pubk_id; + } + + // Write/Update the object + cert_len = sizeof(cert_data); + rv = token.get_token_raw_certificate(piv_state, cert_id, cert_data, &cert_len); + if (rv != CKR_OK) { + DBG("Unable to get certificate data from token"); + return CKR_FUNCTION_FAILED; + } + + rv = store_cert(cert_id, cert_data, cert_len); + if (rv != CKR_OK) { + DBG("Unable to store certificate data"); + return CKR_FUNCTION_FAILED; + } + + *phPrivateKey = op_info.op.gen.key_id; + *phPublicKey = op_info.op.gen.key_id - PIV_PVTK_OBJ_KM + PIV_PUBK_OBJ_KM; // TODO: make function for these? + + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_WrapKey)( + CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hWrappingKey, + CK_OBJECT_HANDLE hKey, + CK_BYTE_PTR pWrappedKey, + CK_ULONG_PTR pulWrappedKeyLen +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_UnwrapKey)( + CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hUnwrappingKey, + CK_BYTE_PTR pWrappedKey, + CK_ULONG ulWrappedKeyLen, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, + CK_OBJECT_HANDLE_PTR phKey +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_DeriveKey)( + CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hBaseKey, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, + CK_OBJECT_HANDLE_PTR phKey +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +/* Random number generation functions */ + +CK_DEFINE_FUNCTION(CK_RV, C_SeedRandom)( + CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pSeed, + CK_ULONG ulSeedLen +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_GenerateRandom)( + CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pRandomData, + CK_ULONG ulRandomLen +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_GetFunctionStatus)( + CK_SESSION_HANDLE hSession +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_DEFINE_FUNCTION(CK_RV, C_CancelFunction)( + CK_SESSION_HANDLE hSession +) +{ + DIN; + DBG("TODO!!!"); + DOUT; + return CKR_OK; +} + +CK_FUNCTION_LIST function_list = { + { 2, 40 }, + C_Initialize, + C_Finalize, + C_GetInfo, + C_GetFunctionList, + C_GetSlotList, + C_GetSlotInfo, + C_GetTokenInfo, + C_GetMechanismList, + C_GetMechanismInfo, + C_InitToken, + C_InitPIN, + C_SetPIN, + C_OpenSession, + C_CloseSession, + C_CloseAllSessions, + C_GetSessionInfo, + C_GetOperationState, + C_SetOperationState, + C_Login, + C_Logout, + C_CreateObject, + C_CopyObject, + C_DestroyObject, + C_GetObjectSize, + C_GetAttributeValue, + C_SetAttributeValue, + C_FindObjectsInit, + C_FindObjects, + C_FindObjectsFinal, + C_EncryptInit, + C_Encrypt, + C_EncryptUpdate, + C_EncryptFinal, + C_DecryptInit, + C_Decrypt, + C_DecryptUpdate, + C_DecryptFinal, + C_DigestInit, + C_Digest, + C_DigestUpdate, + C_DigestKey, + C_DigestFinal, + C_SignInit, + C_Sign, + C_SignUpdate, + C_SignFinal, + C_SignRecoverInit, + C_SignRecover, + C_VerifyInit, + C_Verify, + C_VerifyUpdate, + C_VerifyFinal, + C_VerifyRecoverInit, + C_VerifyRecover, + C_DigestEncryptUpdate, + C_DecryptDigestUpdate, + C_SignEncryptUpdate, + C_DecryptVerifyUpdate, + C_GenerateKey, + C_GenerateKeyPair, + C_WrapKey, + C_UnwrapKey, + C_DeriveKey, + C_SeedRandom, + C_GenerateRandom, + C_GetFunctionStatus, + C_CancelFunction, + C_WaitForSlotEvent, +}; diff --git a/ykcs11/ykcs11.h b/ykcs11/ykcs11.h new file mode 100644 index 0000000..e29b24b --- /dev/null +++ b/ykcs11/ykcs11.h @@ -0,0 +1,77 @@ +#ifndef YKCS11_H +#define YKCS11_H + +#include "pkcs11t.h" +#include "obj_types.h" +#include "openssl_types.h" +#include "vendors.h" + +#define YKCS11_OP_BUFSIZE 4096 + +typedef struct { + vendor_id_t vid; + CK_TOKEN_INFO info; + piv_obj_id_t *objects; // List of objects in the token + CK_ULONG n_objects; // TOTAL number of objects in the token + CK_ULONG n_certs; // Number of certificate objects in the token (portion of n_objects) +} ykcs11_token_t; + +typedef struct { + vendor_id_t vid; + CK_SLOT_INFO info; + ykcs11_token_t *token; +} ykcs11_slot_t; + +typedef struct { + CK_SESSION_HANDLE handle; + CK_SESSION_INFO info; /* slotid, state, flags, deviceerror */ + ykcs11_slot_t *slot; +} ykcs11_session_t; + +typedef enum { + YKCS11_NOOP, + YKCS11_GEN, + YKCS11_SIGN, + YKCS11_HASH, + YKCS11_DECRYPT +} ykcs11_op_type_t; + +typedef struct { + CK_BBOOL rsa; // RSA or EC key + CK_BYTE key_id; // Key id + CK_ULONG key_len; // Length in bits + CK_ULONG vendor_defined; // Additional parameters (touch and PIN policy) +} gen_info_t; + +typedef struct { + ykcs11_md_ctx_t *md_ctx; // Digest context + CK_BYTE_PTR key; // Raw public key (needed for PSS) + CK_BYTE algo; // Algo for ykpiv // TODO: infer this from the key length? + CK_ULONG key_id; // Key id for ykpiv // TODO: make this a BYTE and store the id {0, 1, 2, 3} + CK_ULONG key_len; // Length in bits +} sign_info_t; + +typedef struct { + CK_BYTE todo; +} hash_info_t; + +typedef struct { + CK_BYTE todo; +} decrypt_info_t; + +typedef union { + gen_info_t gen; + sign_info_t sign; + hash_info_t hash; + decrypt_info_t decrypt; +} op_t; + +typedef struct { + ykcs11_op_type_t type; + CK_MECHANISM mechanism; + op_t op; + CK_BYTE buf[YKCS11_OP_BUFSIZE]; + CK_ULONG buf_len; +} op_info_t; + +#endif diff --git a/ykcs11/ykcs11.map b/ykcs11/ykcs11.map new file mode 100644 index 0000000..3b83e98 --- /dev/null +++ b/ykcs11/ykcs11.map @@ -0,0 +1,35 @@ +# Copyright (c) 2014 Yubico AB +# All rights reserved. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Additional permission under GNU GPL version 3 section 7 +# +# If you modify this program, or any covered work, by linking or +# combining it with the OpenSSL project's OpenSSL library (or a +# modified version of that library), containing parts covered by the +# terms of the OpenSSL or SSLeay licenses, We grant you additional +# permission to convey the resulting work. Corresponding Source for a +# non-source form of such a combination shall include the source code +# for the parts of OpenSSL used as well as that of the covered work. + +YKCS11_0.1.0 +{ +global: + C_Initialize; + C_GetFunctionList; +# TODO: add more here +local: + *; +}; \ No newline at end of file diff --git a/ykcs11/ykcs11.pc.in b/ykcs11/ykcs11.pc.in new file mode 100644 index 0000000..53703c0 --- /dev/null +++ b/ykcs11/ykcs11.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: @PACKAGE@ +Description: Yubico PIV PKCS#11 Module +URL: https://www.yubico.com/ +Version: @VERSION@ +Libs: -L${libdir} -lykcs11 +Cflags: -I${includedir}/ykpiv + diff --git a/ykcs11/yubico_slot.c b/ykcs11/yubico_slot.c new file mode 100644 index 0000000..d947f50 --- /dev/null +++ b/ykcs11/yubico_slot.c @@ -0,0 +1,33 @@ +#include "yubico_slot.h" +#include "pkcs11.h" +#include + +static const CK_UTF8CHAR_PTR slot_manufacturer = "Yubico"; +static const CK_FLAGS slot_flags = CKF_TOKEN_PRESENT | CKF_HW_SLOT; +static const CK_VERSION slot_version = {1, 0}; + +CK_RV YUBICO_get_slot_manufacturer(CK_UTF8CHAR_PTR str, CK_ULONG len) { + + if (strlen(slot_manufacturer) > len) + return CKR_BUFFER_TOO_SMALL; + + memcpy(str, slot_manufacturer, strlen(slot_manufacturer)); + return CKR_OK; + +} + +CK_RV YUBICO_get_slot_flags(CK_FLAGS_PTR flags) { + + *flags = slot_flags; + return CKR_OK; + +} + +CK_RV YUBICO_get_slot_version(CK_VERSION_PTR version) { + + version->major = slot_version.major; + version->minor = slot_version.minor; + + return CKR_OK; + +} diff --git a/ykcs11/yubico_slot.h b/ykcs11/yubico_slot.h new file mode 100644 index 0000000..7f84106 --- /dev/null +++ b/ykcs11/yubico_slot.h @@ -0,0 +1,10 @@ +#ifndef YUBICO_SLOT_H +#define YUBICO_SLOT_H + +#include "pkcs11.h" + +CK_RV YUBICO_get_slot_manufacturer(CK_UTF8CHAR_PTR str, CK_ULONG len); +CK_RV YUBICO_get_slot_flags(CK_FLAGS_PTR flags); +CK_RV YUBICO_get_slot_version(CK_VERSION_PTR version); + +#endif diff --git a/ykcs11/yubico_token.c b/ykcs11/yubico_token.c new file mode 100644 index 0000000..25da01a --- /dev/null +++ b/ykcs11/yubico_token.c @@ -0,0 +1,319 @@ +#include "yubico_token.h" +#include "pkcs11.h" +#include +#include "debug.h" +#include "objects.h" + +#define MIN_RSA_KEY_SIZE 1024 +#define MAX_RSA_KEY_SIZE 2048 +#define MIN_ECC_KEY_SIZE 256 +#define MAX_ECC_KEY_SIZE 384 + +static const char *token_label = "YubiKey PIV"; +static const char *token_manufacturer = "Yubico"; +static const char *token_model = "YubiKey XXX"; +static const CK_FLAGS token_flags = CKF_RNG | CKF_LOGIN_REQUIRED | CKF_USER_PIN_INITIALIZED | CKF_TOKEN_INITIALIZED; +static const char *token_serial = "1234"; +static const CK_MECHANISM_TYPE token_mechanisms[] = { // KEEP ALIGNED WITH token_mechanism_infos + CKM_RSA_PKCS_KEY_PAIR_GEN, + CKM_RSA_PKCS, + CKM_RSA_PKCS_PSS, + CKM_RSA_X_509, + CKM_SHA1_RSA_PKCS, + CKM_SHA256_RSA_PKCS, + CKM_SHA384_RSA_PKCS, + CKM_SHA512_RSA_PKCS, + CKM_SHA1_RSA_PKCS_PSS, + CKM_SHA256_RSA_PKCS_PSS, + CKM_SHA384_RSA_PKCS_PSS, + CKM_SHA512_RSA_PKCS_PSS, + CKM_EC_KEY_PAIR_GEN, + //CKM_ECDSA_KEY_PAIR_GEN, Same as CKM_EC_KEY_PAIR_GEN, deprecated in 2.11 + CKM_ECDSA, + CKM_ECDSA_SHA1, + CKM_ECDSA_SHA256, + CKM_SHA_1, + CKM_SHA256, + CKM_SHA384, + CKM_SHA512 + // SUPPORT FOR OATH? +}; +static const CK_ULONG token_mechanisms_num = sizeof(token_mechanisms) / sizeof(CK_MECHANISM_TYPE); + +static const CK_MECHANISM_INFO token_mechanism_infos[] = { // KEEP ALIGNED WITH token_mechanisms + {MIN_RSA_KEY_SIZE, MAX_RSA_KEY_SIZE, CKF_HW | CKF_GENERATE_KEY_PAIR}, // CKM_RSA_PKCS_KEY_PAIR_GEN + {MIN_RSA_KEY_SIZE, MAX_RSA_KEY_SIZE, CKF_HW | CKF_DECRYPT | CKF_SIGN}, // CKM_RSA_PKCS + {MIN_RSA_KEY_SIZE, MAX_RSA_KEY_SIZE, CKF_HW | CKF_SIGN}, // CKM_RSA_PKCS_PSS + {MIN_RSA_KEY_SIZE, MAX_RSA_KEY_SIZE, CKF_HW | CKF_DECRYPT | CKF_SIGN}, // CKM_RSA_X_509 + {MIN_RSA_KEY_SIZE, MAX_RSA_KEY_SIZE, CKF_HW | CKF_SIGN}, // CKM_SHA1_RSA_PKCS + {MIN_RSA_KEY_SIZE, MAX_RSA_KEY_SIZE, CKF_HW | CKF_SIGN}, // CKM_SHA256_RSA_PKCS + {MIN_RSA_KEY_SIZE, MAX_RSA_KEY_SIZE, CKF_HW | CKF_SIGN}, // CKM_SHA384_RSA_PKCS + {MIN_RSA_KEY_SIZE, MAX_RSA_KEY_SIZE, CKF_HW | CKF_SIGN}, // CKM_SHA512_RSA_PKCS + {MIN_RSA_KEY_SIZE, MAX_RSA_KEY_SIZE, CKF_HW | CKF_SIGN}, // CKM_SHA1_RSA_PKCS_PSS + {MIN_RSA_KEY_SIZE, MAX_RSA_KEY_SIZE, CKF_HW | CKF_SIGN}, // CKM_SHA256_RSA_PKCS_PSS + {MIN_RSA_KEY_SIZE, MAX_RSA_KEY_SIZE, CKF_HW | CKF_SIGN}, // CKM_SHA384_RSA_PKCS_PSS + {MIN_RSA_KEY_SIZE, MAX_RSA_KEY_SIZE, CKF_HW | CKF_SIGN}, // CKM_SHA512_RSA_PKCS_PSS + {MIN_ECC_KEY_SIZE, MAX_ECC_KEY_SIZE, CKF_HW | CKF_GENERATE_KEY_PAIR}, // CKM_EC_KEY_PAIR_GEN + //{, , }, // CKM_ECDSA_KEY_PAIR_GEN Same as CKM_EC_KEY_PAIR_GEN deprecated in 2.11 + {MIN_ECC_KEY_SIZE, MAX_ECC_KEY_SIZE, CKF_HW | CKF_SIGN}, // CKM_ECDSA + {MIN_ECC_KEY_SIZE, MAX_ECC_KEY_SIZE, CKF_HW | CKF_SIGN}, // CKM_ECDSA_SHA1 + {MIN_ECC_KEY_SIZE, MAX_ECC_KEY_SIZE, CKF_HW | CKF_SIGN}, // CKM_ECDSA_SHA256 + {0, 0, CKF_DIGEST}, // CKM_SHA_1 + {0, 0, CKF_DIGEST}, // CKM_SHA256 + {0, 0, CKF_DIGEST}, // CKM_SHA384 + {0, 0, CKF_DIGEST} // CKM_SHA512 +}; + +static const piv_obj_id_t token_objects[] = { // TODO: is there a way to get this from the token? + PIV_DATA_OBJ_X509_PIV_AUTH, // PIV authentication + PIV_DATA_OBJ_X509_CARD_AUTH, // Certificate for card authentication + PIV_DATA_OBJ_X509_DS, // Certificate for digital signature + PIV_DATA_OBJ_X509_KM, // Certificate for key management + PIV_DATA_OBJ_CCC, // Card capability container + PIV_DATA_OBJ_CHUI, // Cardholder unique id + PIV_DATA_OBJ_CHF, // Cardholder fingerprints + PIV_DATA_OBJ_SEC_OBJ, // Security object + PIV_DATA_OBJ_CHFI, // Cardholder facial images + //PIV_DATA_OBJ_PI, // Cardholder printed information + //PIV_DATA_OBJ_DISCOVERY, // Discovery object + //PIV_DATA_OBJ_HISTORY, // History object +/* PIV_DATA_OBJ_RETIRED_X509_1, // Retired certificate for KM 1 + PIV_DATA_OBJ_RETIRED_X509_2, // Retired certificate for KM 2 + PIV_DATA_OBJ_RETIRED_X509_3, // Retired certificate for KM 3 + PIV_DATA_OBJ_RETIRED_X509_4, // Retired certificate for KM 4 + PIV_DATA_OBJ_RETIRED_X509_5, // Retired certificate for KM 5 + PIV_DATA_OBJ_RETIRED_X509_6, // Retired certificate for KM 6 + PIV_DATA_OBJ_RETIRED_X509_7, // Retired certificate for KM 7 + PIV_DATA_OBJ_RETIRED_X509_8, // Retired certificate for KM 8 + PIV_DATA_OBJ_RETIRED_X509_9, // Retired certificate for KM 9 + PIV_DATA_OBJ_RETIRED_X509_10, // Retired certificate for KM 10 + PIV_DATA_OBJ_RETIRED_X509_11, // Retired certificate for KM 11 + PIV_DATA_OBJ_RETIRED_X509_12, // Retired certificate for KM 12 + PIV_DATA_OBJ_RETIRED_X509_13, // Retired certificate for KM 13 + PIV_DATA_OBJ_RETIRED_X509_14, // Retired certificate for KM 14 + PIV_DATA_OBJ_RETIRED_X509_15, // Retired certificate for KM 15 + PIV_DATA_OBJ_RETIRED_X509_16, // Retired certificate for KM 16 + PIV_DATA_OBJ_RETIRED_X509_17, // Retired certificate for KM 17 + PIV_DATA_OBJ_RETIRED_X509_18, // Retired certificate for KM 18 + PIV_DATA_OBJ_RETIRED_X509_19, // Retired certificate for KM 19 + PIV_DATA_OBJ_RETIRED_X509_20, // Retired certificate for KM 20*/ + //PIV_DATA_OBJ_IRIS_IMAGE, // Cardholder iris images + //PIV_DATA_OBJ_BITGT, // Biometric information templates group template + //PIV_DATA_OBJ_SM_SIGNER, // Secure messaging signer + //PIV_DATA_OBJ_PC_REF_DATA, // Pairing code reference data +}; +static const CK_ULONG token_objects_num = sizeof(token_objects) / sizeof(piv_obj_id_t); + +CK_RV YUBICO_get_token_label(CK_UTF8CHAR_PTR str, CK_ULONG len) { + + if (strlen(token_label) > len) + return CKR_BUFFER_TOO_SMALL; + + memcpy(str, token_label, strlen(token_label)); + return CKR_OK; + +} + +CK_RV YUBICO_get_token_manufacturer(CK_UTF8CHAR_PTR str, CK_ULONG len) { + + if (strlen(token_manufacturer) > len) + return CKR_BUFFER_TOO_SMALL; + + memcpy(str, token_manufacturer, strlen(token_manufacturer)); + return CKR_OK; + +} +#include "debug.h" +CK_RV YUBICO_get_token_model(ykpiv_state *state, CK_UTF8CHAR_PTR str, CK_ULONG len) { + + char buf[16]; + + if (strlen(token_model) > len) + return CKR_BUFFER_TOO_SMALL; + + if (ykpiv_get_version(state, buf, sizeof(buf)) != YKPIV_OK) + return CKR_FUNCTION_FAILED; + + memcpy(str, token_model, strlen(token_model)); + + if (buf[0] >= '4') + memcpy(str + strlen(token_model) - 3, "YK4", 3); + else + memcpy(str + strlen(token_model) - 3, "NEO", 3); + + return CKR_OK; + +} + +CK_RV YUBICO_get_token_flags(CK_FLAGS_PTR flags) { + + *flags = token_flags; + return CKR_OK; + +} + +CK_RV YUBICO_get_token_version(CK_UTF8CHAR_PTR v_str, CK_ULONG len, CK_VERSION_PTR version) { + + CK_VERSION v = {0, 0}; + unsigned int i = 0; + + while (i < len && v_str[i] != '.') { + v.major *= 10; + v.major += v_str[i++] - '0'; + } + + i++; + + while (i < len && v_str[i] != '.') { + v.minor *= 10; + v.minor += v_str[i++] - '0'; + } + + i++; + + while (i < len && v_str[i] != '.') { + v.minor *= 10; + v.minor += v_str[i++] - '0'; + } + + version->major = v.major; + version->minor = v.minor; + + return CKR_OK; +} + +CK_RV YUBICO_get_token_serial(CK_CHAR_PTR str, CK_ULONG len) { + + if (strlen(token_serial) > len) + return CKR_BUFFER_TOO_SMALL; + + memcpy(str, token_serial, strlen(token_serial)); + return CKR_OK; + +} + +CK_RV YUBICO_get_token_mechanisms_num(CK_ULONG_PTR num) { + + *num = token_mechanisms_num; + return CKR_OK; + +} + +CK_RV YUBICO_get_token_mechanism_list(CK_MECHANISM_TYPE_PTR mec, CK_ULONG num) { + + if (token_mechanisms_num > num) + return CKR_BUFFER_TOO_SMALL; + + memcpy(mec, token_mechanisms, token_mechanisms_num * sizeof(CK_MECHANISM_TYPE)); + return CKR_OK; + +} + +CK_RV YUBICO_get_token_mechanism_info(CK_MECHANISM_TYPE mec, CK_MECHANISM_INFO_PTR info) { + + CK_ULONG i; + + for (i = 0; i < token_mechanisms_num; i++) + if (token_mechanisms[i] == mec) { + memcpy((CK_BYTE_PTR) info, (CK_BYTE_PTR) (token_mechanism_infos + i), sizeof(CK_MECHANISM_INFO)); + return CKR_OK; + } + + return CKR_MECHANISM_INVALID; + +} + +static CK_RV get_objects(ykpiv_state *state, CK_BBOOL num_only, + piv_obj_id_t *obj, CK_ULONG_PTR len, CK_ULONG_PTR num_certs) { + CK_BYTE buf[2048]; + CK_ULONG buf_len; + + piv_obj_id_t certs[4]; // TODO: this can be > 4 if there are retired keys + piv_obj_id_t pvtkeys[4]; + piv_obj_id_t pubkeys[4]; + CK_ULONG n_cert = 0; + + if (state == NULL || len == NULL_PTR) + return CKR_ARGUMENTS_BAD; + + if (num_only == CK_FALSE && obj == NULL) + return CKR_ARGUMENTS_BAD; + + buf_len = sizeof(buf); + if (ykpiv_fetch_object(state, YKPIV_OBJ_AUTHENTICATION, buf, &buf_len) == YKPIV_OK) { + certs[n_cert] = PIV_CERT_OBJ_X509_PIV_AUTH; + pvtkeys[n_cert] = PIV_PVTK_OBJ_PIV_AUTH; + pubkeys[n_cert] = PIV_PUBK_OBJ_PIV_AUTH; + n_cert++; + DBG("Found AUTH cert (9a)"); + } + + buf_len = sizeof(buf); + if (ykpiv_fetch_object(state, YKPIV_OBJ_CARD_AUTH, buf, &buf_len) == YKPIV_OK) { + certs[n_cert] = PIV_CERT_OBJ_X509_CARD_AUTH; + pvtkeys[n_cert] = PIV_PVTK_OBJ_CARD_AUTH; + pubkeys[n_cert] = PIV_PUBK_OBJ_CARD_AUTH; + n_cert++; + DBG("Found CARD AUTH cert (9e)"); + } + + buf_len = sizeof(buf); + if (ykpiv_fetch_object(state, YKPIV_OBJ_SIGNATURE, buf, &buf_len) == YKPIV_OK) { + certs[n_cert] = PIV_CERT_OBJ_X509_DS; + pvtkeys[n_cert] = PIV_PVTK_OBJ_DS; + pubkeys[n_cert] = PIV_PUBK_OBJ_DS; + n_cert++; + DBG("Found SIGNATURE cert (9c)"); + } + + buf_len = sizeof(buf); + if (ykpiv_fetch_object(state, YKPIV_OBJ_KEY_MANAGEMENT, buf, &buf_len) == YKPIV_OK) { + certs[n_cert] = PIV_CERT_OBJ_X509_KM; + pvtkeys[n_cert] = PIV_PVTK_OBJ_KM; + pubkeys[n_cert] = PIV_PUBK_OBJ_KM; + n_cert++; + DBG("Found KMK cert (9d)"); + } + + DBG("The total number of objects for this token is %lu", (n_cert * 3) + token_objects_num); + + if (num_only == CK_TRUE) { + // We just want the number of objects + // Each cert object counts for 3: cert, pub key, pvt key + *len = (n_cert * 3) + token_objects_num; + if (num_certs != NULL) + *num_certs = n_cert; + return CKR_OK; + } + + if (*len < (n_cert * 3) + token_objects_num) + return CKR_BUFFER_TOO_SMALL; + + // Copy mandatory data objects + memcpy(obj, token_objects, token_objects_num * sizeof(piv_obj_id_t)); + + // Copy certificates + if (n_cert > 0) { + memcpy(obj + token_objects_num, certs, n_cert * sizeof(piv_obj_id_t)); + memcpy(obj + token_objects_num + n_cert, pvtkeys, n_cert * sizeof(piv_obj_id_t)); + memcpy(obj + token_objects_num + (2 * n_cert), pubkeys, n_cert * sizeof(piv_obj_id_t)); + } + + return CKR_OK; +} + +CK_RV YUBICO_get_token_objects_num(ykpiv_state *state, CK_ULONG_PTR num, CK_ULONG_PTR num_certs) { + return get_objects(state, CK_TRUE, NULL, num, num_certs); +} + +CK_RV YUBICO_get_token_object_list(ykpiv_state *state, piv_obj_id_t *obj, CK_ULONG num) { + return get_objects(state, CK_FALSE, obj, &num, NULL); +} + +CK_RV YUBICO_get_token_raw_certificate(ykpiv_state *state, piv_obj_id_t obj, CK_BYTE_PTR data, CK_ULONG_PTR len) { + + if (ykpiv_fetch_object(state, piv_2_ykpiv(obj), data, len) != YKPIV_OK) + return CKR_FUNCTION_FAILED; + + return CKR_OK; +} diff --git a/ykcs11/yubico_token.h b/ykcs11/yubico_token.h new file mode 100644 index 0000000..e95c8e3 --- /dev/null +++ b/ykcs11/yubico_token.h @@ -0,0 +1,21 @@ +#ifndef YUBICO_TOKEN_H +#define YUBICO_TOKEN_H + +#include "pkcs11.h" +#include "obj_types.h" +#include + +CK_RV YUBICO_get_token_label(CK_UTF8CHAR_PTR str, CK_ULONG len); +CK_RV YUBICO_get_token_manufacturer(CK_UTF8CHAR_PTR str, CK_ULONG len); +CK_RV YUBICO_get_token_model(ykpiv_state *state, CK_UTF8CHAR_PTR str, CK_ULONG len); +CK_RV YUBICO_get_token_flags(CK_FLAGS_PTR flags); +CK_RV YUBICO_get_token_serial(CK_CHAR_PTR str, CK_ULONG len); +CK_RV YUBICO_get_token_version(CK_UTF8CHAR_PTR v_str, CK_ULONG v_str_len, CK_VERSION_PTR version); +CK_RV YUBICO_get_token_mechanisms_num(CK_ULONG_PTR num); +CK_RV YUBICO_get_token_mechanism_list(CK_MECHANISM_TYPE_PTR mec, CK_ULONG num); +CK_RV YUBICO_get_token_mechanism_info(CK_MECHANISM_TYPE mec, CK_MECHANISM_INFO_PTR info); +CK_RV YUBICO_get_token_objects_num(ykpiv_state *state, CK_ULONG_PTR num, CK_ULONG_PTR num_certs); +CK_RV YUBICO_get_token_object_list(ykpiv_state *state, piv_obj_id_t *obj, CK_ULONG num); +CK_RV YUBICO_get_token_raw_certificate(ykpiv_state *state, piv_obj_id_t obj, CK_BYTE_PTR data, CK_ULONG_PTR len); + +#endif