diff --git a/.travis.yml b/.travis.yml index 98b23d8..09a447e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,3 +20,6 @@ matrix: - compiler: gcc os: linux env: COVERAGE="--enable-coverage" EXTRA="libpcsclite-dev lcov" + - compiler: gcc + os: linux + env: BUILD_OPENSSL_VERSION="1.1" EXTRA="libpcsclite-dev" diff --git a/Makefile.am b/Makefile.am index 68ea000..853d538 100644 --- a/Makefile.am +++ b/Makefile.am @@ -29,7 +29,7 @@ SUBDIRS = lib tool ykcs11 ACLOCAL_AMFLAGS = -I m4 -EXTRA_DIST = windows.mk mac.mk tool/tests/basic.sh tools/fasc.pl +EXTRA_DIST = windows.mk mac.mk tool/tests/basic.sh tools/fasc.pl ykcs11/tests/reset.sh EXTRA_DIST += doc/Attestation.adoc doc/YKCS11_release_notes.adoc doc/YubiKey_PIV_introduction.adoc diff --git a/build-and-test.sh b/build-and-test.sh index 8f574d7..969e1b2 100755 --- a/build-and-test.sh +++ b/build-and-test.sh @@ -61,6 +61,12 @@ if [ "x$ARCH" != "x" ]; then else make -f mac.mk mac VERSION=$version fi +elif [ "x$BUILD_OPENSSL_VERSION" = "x1.1" ]; then + version=`cat NEWS | grep unreleased | cut -d' ' -f3` + set +e + $TAR --exclude .git --transform="s/^\./yubico-piv-tool-${version}/" -czf yubico-piv-tool-${version}.tar.gz . + set -e + make -f linux.mk VERSION=$version else ./configure $COVERAGE make check diff --git a/configure.ac b/configure.ac index 7797bd1..f67d51f 100644 --- a/configure.ac +++ b/configure.ac @@ -37,7 +37,7 @@ AC_SUBST([LT_CURRENT], 5) AC_SUBST([LT_REVISION], 0) AC_SUBST([LT_AGE], 4) -AM_INIT_AUTOMAKE([-Wall -Werror foreign]) +AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects]) AM_SILENT_RULES([yes]) AC_PROG_CC m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) @@ -49,7 +49,7 @@ AM_MISSING_PROG(HELP2ADOC, help2adoc, $missing_dir) AM_MISSING_PROG(GENGETOPT, gengetopt, $missing_dir) PKG_PROG_PKG_CONFIG -PKG_CHECK_MODULES(OPENSSL, libcrypto) +PKG_CHECK_MODULES([OPENSSL], [libcrypto], [OPENSSL_VERSION=`$PKG_CONFIG --modversion libcrypto`]) PKG_CHECK_MODULES([CHECK], [check >= 0.9.6]) DX_HTML_FEATURE(ON) @@ -261,6 +261,7 @@ AC_MSG_NOTICE([summary of build options: CPPFLAGS: ${CPPFLAGS} Warnings: ${WARN_CFLAGS} Backend: ${with_backend} + OpenSSL version: ${OPENSSL_VERSION} PCSC CFLAGS: ${PCSC_CFLAGS} LIBS: ${PCSC_LIBS} diff --git a/lib/internal.c b/lib/internal.c index e8942b3..2846045 100644 --- a/lib/internal.c +++ b/lib/internal.c @@ -21,7 +21,7 @@ #ifdef _WINDOWS -#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) +#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) struct des_key { HCRYPTPROV hProv; @@ -399,7 +399,7 @@ prng_rc _ykpiv_prng_generate(unsigned char *buffer, const size_t cb_req) { } #else - if (-1 == RAND_pseudo_bytes(buffer, cb_req)) { + if (-1 == RAND_bytes(buffer, cb_req)) { rc = PRNG_GENERAL_ERROR; } diff --git a/lib/tests/Makefile.am b/lib/tests/Makefile.am index 46264f4..d0900f0 100644 --- a/lib/tests/Makefile.am +++ b/lib/tests/Makefile.am @@ -38,6 +38,7 @@ endif LDADD = ../libykpiv.la $(OPENSSL_LIBS) +api_SOURCES = api.c ../../tool/openssl-compat.c ../../tool/openssl-compat.h check_PROGRAMS = basic parse_key api TESTS = $(check_PROGRAMS) diff --git a/lib/tests/api.c b/lib/tests/api.c index b36f0ff..7e2cf13 100644 --- a/lib/tests/api.c +++ b/lib/tests/api.c @@ -30,6 +30,7 @@ #include "ykpiv.h" #include "internal.h" +#include "../../tool/openssl-compat.h" #include #include @@ -268,23 +269,21 @@ static bool set_component(unsigned char *in_ptr, const BIGNUM *bn, int element_l } static bool prepare_rsa_signature(const unsigned char *in, unsigned int in_len, unsigned char *out, unsigned int *out_len, int nid) { - X509_SIG digestInfo; - X509_ALGOR algor; + X509_SIG *digestInfo; + X509_ALGOR *algor; ASN1_TYPE parameter; - ASN1_OCTET_STRING digest; + ASN1_OCTET_STRING *digest; unsigned char data[1024]; memcpy(data, in, in_len); - digestInfo.algor = &algor; - digestInfo.algor->algorithm = OBJ_nid2obj(nid); - digestInfo.algor->parameter = ¶meter; - digestInfo.algor->parameter->type = V_ASN1_NULL; - digestInfo.algor->parameter->value.ptr = NULL; - digestInfo.digest = &digest; - digestInfo.digest->data = data; - digestInfo.digest->length = (int)in_len; - *out_len = (unsigned int)i2d_X509_SIG(&digestInfo, &out); + digestInfo = X509_SIG_new(); + X509_SIG_getm(digestInfo, &algor, &digest); + algor->algorithm = OBJ_nid2obj(nid); + X509_ALGOR_set0(algor, OBJ_nid2obj(nid), V_ASN1_NULL, NULL); + ASN1_STRING_set(digest, data, in_len); + *out_len = (unsigned int)i2d_X509_SIG(digestInfo, &out); + X509_SIG_free(digestInfo); return true; } @@ -303,6 +302,7 @@ static void import_key(unsigned char slot, unsigned char pin_policy) { unsigned char dmq1[128]; unsigned char iqmp[128]; int element_len = 128; + const BIGNUM *bn_e, *bn_p, *bn_q, *bn_dmp1, *bn_dmq1, *bn_iqmp; bio = BIO_new_mem_buf(private_key_pem, strlen(private_key_pem)); private_key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); @@ -310,12 +310,15 @@ static void import_key(unsigned char slot, unsigned char pin_policy) { BIO_free(bio); rsa_private_key = EVP_PKEY_get1_RSA(private_key); ck_assert_ptr_nonnull(rsa_private_key); - ck_assert(set_component(e, rsa_private_key->e, 3)); - ck_assert(set_component(p, rsa_private_key->p, element_len)); - ck_assert(set_component(q, rsa_private_key->q, element_len)); - ck_assert(set_component(dmp1, rsa_private_key->dmp1, element_len)); - ck_assert(set_component(dmq1, rsa_private_key->dmq1, element_len)); - ck_assert(set_component(iqmp, rsa_private_key->iqmp, element_len)); + RSA_get0_key(rsa_private_key, NULL, &bn_e, NULL); + RSA_get0_factors(rsa_private_key, &bn_p, &bn_q); + RSA_get0_crt_params(rsa_private_key, &bn_dmp1, &bn_dmq1, &bn_iqmp); + ck_assert(set_component(e, bn_e, 3)); + ck_assert(set_component(p, bn_p, element_len)); + ck_assert(set_component(q, bn_q, element_len)); + ck_assert(set_component(dmp1, bn_dmp1, element_len)); + ck_assert(set_component(dmq1, bn_dmq1, element_len)); + ck_assert(set_component(iqmp, bn_iqmp, element_len)); // Try wrong algorithm, fail. res = ykpiv_import_private_key(g_state, @@ -342,6 +345,7 @@ static void import_key(unsigned char slot, unsigned char pin_policy) { NULL, 0, pp, tp); ck_assert_int_eq(res, YKPIV_OK); + RSA_free(rsa_private_key); EVP_PKEY_free(private_key); } @@ -364,7 +368,9 @@ static void import_key(unsigned char slot, unsigned char pin_policy) { ck_assert_ptr_nonnull(pub_key); rsa = EVP_PKEY_get1_RSA(pub_key); ck_assert_ptr_nonnull(rsa); - ck_assert_int_gt(RAND_pseudo_bytes(secret, sizeof(secret)), 0); + EVP_PKEY_free(pub_key); + + ck_assert_int_gt(RAND_bytes(secret, sizeof(secret)), 0); len = RSA_public_encrypt(sizeof(secret), secret, data, rsa, RSA_PKCS1_PADDING); ck_assert_int_ge(len, 0); res = ykpiv_verify(g_state, "123456", NULL); @@ -374,6 +380,7 @@ static void import_key(unsigned char slot, unsigned char pin_policy) { len = RSA_padding_check_PKCS1_type_2(secret2, sizeof(secret2), data + 1, len2 - 1, RSA_size(rsa)); ck_assert_int_eq(len, sizeof(secret)); ck_assert_int_eq(memcmp(secret, secret2, sizeof(secret)), 0); + RSA_free(rsa); X509_free(cert); } } @@ -411,8 +418,9 @@ START_TEST(test_import_key) { ck_assert_ptr_nonnull(pub_key); rsa = EVP_PKEY_get1_RSA(pub_key); ck_assert_ptr_nonnull(rsa); + EVP_PKEY_free(pub_key); - ck_assert_int_gt(RAND_pseudo_bytes(rand, 128), 0); + ck_assert_int_gt(RAND_bytes(rand, 128), 0); mdctx = EVP_MD_CTX_create(); EVP_DigestInit_ex(mdctx, md, NULL); EVP_DigestUpdate(mdctx, rand, 128); @@ -425,7 +433,9 @@ START_TEST(test_import_key) { ck_assert_int_eq(RSA_verify(EVP_MD_type(md), data, data_len, signature, sig_len, rsa), 1); + RSA_free(rsa); X509_free(cert); + EVP_MD_CTX_destroy(mdctx); } // Verify that imported key can not be attested @@ -488,8 +498,9 @@ START_TEST(test_pin_policy_always) { ck_assert_ptr_nonnull(pub_key); rsa = EVP_PKEY_get1_RSA(pub_key); ck_assert_ptr_nonnull(rsa); + EVP_PKEY_free(pub_key); - ck_assert_int_gt(RAND_pseudo_bytes(rand, 128), 0); + ck_assert_int_gt(RAND_bytes(rand, 128), 0); mdctx = EVP_MD_CTX_create(); EVP_DigestInit_ex(mdctx, md, NULL); EVP_DigestUpdate(mdctx, rand, 128); @@ -520,7 +531,9 @@ START_TEST(test_pin_policy_always) { ck_assert_int_eq(RSA_verify(EVP_MD_type(md), data, data_len, signature, sig_len, rsa), 1); + RSA_free(rsa); X509_free(cert); + EVP_MD_CTX_destroy(mdctx); } } END_TEST @@ -860,6 +873,7 @@ START_TEST(test_pin_cache) { ykpiv_rc res; ykpiv_state *local_state; unsigned char data[256]; + unsigned char data_in[256] = {0}; int len = sizeof(data); size_t len2 = sizeof(data); @@ -877,17 +891,17 @@ START_TEST(test_pin_cache) { ck_assert_int_eq(res, YKPIV_OK); // Verify decryption does not work without auth - res = ykpiv_decipher_data(g_state, data, (size_t)len, data, &len2, YKPIV_ALGO_RSA2048, 0x9a); + res = ykpiv_decipher_data(g_state, data_in, (size_t)len, data, &len2, YKPIV_ALGO_RSA2048, 0x9a); ck_assert_int_eq(res, YKPIV_AUTHENTICATION_ERROR); // Verify decryption does work when authed res = ykpiv_verify_select(g_state, "123456", 6, NULL, true); ck_assert_int_eq(res, YKPIV_OK); - res = ykpiv_decipher_data(g_state, data, (size_t)len, data, &len2, YKPIV_ALGO_RSA2048, 0x9a); + res = ykpiv_decipher_data(g_state, data_in, (size_t)len, data, &len2, YKPIV_ALGO_RSA2048, 0x9a); ck_assert_int_eq(res, YKPIV_OK); // Verify PIN policy allows continuing to decrypt without re-verifying - res = ykpiv_decipher_data(g_state, data, (size_t)len, data, &len2, YKPIV_ALGO_RSA2048, 0x9a); + res = ykpiv_decipher_data(g_state, data_in, (size_t)len, data, &len2, YKPIV_ALGO_RSA2048, 0x9a); ck_assert_int_eq(res, YKPIV_OK); // Create a new ykpiv state, connect, and close it. @@ -908,7 +922,7 @@ START_TEST(test_pin_cache) { // // Note that you can verify that this fails by rebuilding with // DISABLE_PIN_CACHE set to 1. - res = ykpiv_decipher_data(g_state, data, (size_t)len, data, &len2, YKPIV_ALGO_RSA2048, 0x9a); + res = ykpiv_decipher_data(g_state, data_in, (size_t)len, data, &len2, YKPIV_ALGO_RSA2048, 0x9a); ck_assert_int_eq(res, YKPIV_OK); } END_TEST diff --git a/lib/ykpiv.c b/lib/ykpiv.c index f556c87..e9d5a92 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -399,7 +399,7 @@ ykpiv_rc ykpiv_connect(ykpiv_state *state, const char *wanted) { } static ykpiv_rc reconnect(ykpiv_state *state) { - pcsc_word active_protocol; + pcsc_word active_protocol = 0; long rc; ykpiv_rc res; int tries; @@ -813,7 +813,7 @@ static ykpiv_rc _general_authenticate(ykpiv_state *state, unsigned char templ[] = {0, YKPIV_INS_AUTHENTICATE, algorithm, key}; unsigned long recv_len = sizeof(data); size_t key_len = 0; - int sw; + int sw = 0; size_t bytes; size_t len = 0; ykpiv_rc res; @@ -982,6 +982,9 @@ static ykpiv_rc _cache_pin(ykpiv_state *state, const char *pin, size_t len) { #else if (!state) return YKPIV_ARGUMENT_ERROR; + if (pin && state->pin == pin) { + return YKPIV_OK; + } if (state->pin) { _ykpiv_free(state, state->pin); state->pin = NULL; diff --git a/linux.mk b/linux.mk new file mode 100644 index 0000000..3662505 --- /dev/null +++ b/linux.mk @@ -0,0 +1,58 @@ +# Copyright (c) 2014-2017 Yubico AB +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# +# Note: this build script is for testing OpenSSL 1.1 builds. The official +# Linux release builds are handled by the standard Makefile. +# +PACKAGE=yubico-piv-tool +OPENSSLVERSION=1.1.0g + +all: linux + +doit: + rm -rf tmp && mkdir tmp && cd tmp && \ + mkdir -p root/licenses && \ + cp ../openssl-$(OPENSSLVERSION).tar.gz . || \ + curl -L -O "https://www.openssl.org/source/openssl-$(OPENSSLVERSION).tar.gz" && \ + tar xfz openssl-$(OPENSSLVERSION).tar.gz && \ + cd openssl-$(OPENSSLVERSION) && \ + ./Configure linux-x86_64 shared --prefix=$(PWD)/tmp/root $(CFLAGS) && \ + make all install VERSION="$(OPENSSLVERSION)" && \ + cp LICENSE $(PWD)/tmp$(ARCH)/root/licenses/openssl.txt && \ + cd .. && \ + cp ../$(PACKAGE)-$(VERSION).tar.gz . && \ + tar xfz $(PACKAGE)-$(VERSION).tar.gz && \ + cd $(PACKAGE)-$(VERSION)/ && \ + CFLAGS=$(CFLAGS) PKG_CONFIG_PATH=$(PWD)/tmp/root/lib/pkgconfig ./configure --prefix=$(PWD)/tmp/root && \ + make install $(CHECK) && \ + cd .. && \ + cd root && \ + zip -r ../../$(PACKAGE)-$(VERSION)-linux-openssl-$(OPENSSLVERSION).zip * + +linux: + $(MAKE) -f linux.mk doit CHECK=check diff --git a/mac.mk b/mac.mk index 7fff5da..ac342bd 100644 --- a/mac.mk +++ b/mac.mk @@ -49,7 +49,7 @@ doit: tar xfz openssl-$(OPENSSLVERSION).tar.gz && \ cd openssl-$(OPENSSLVERSION) && \ ./Configure darwin64-x86_64-cc shared no-ssl2 no-ssl3 no-engines --prefix=$(PWD)/tmp/root $(CFLAGS) && \ - make all install_sw && \ + make all install_sw VERSION="$(OPENSSLVERSION)" && \ cp LICENSE $(PWD)/tmp$(ARCH)/root/licenses/openssl.txt && \ rm -rf $(PWD)/tmp/root/ssl/ && \ rm -rf $(PWD)/tmp/root/bin/ && \ diff --git a/tool/Makefile.am b/tool/Makefile.am index 4a1657b..45206e3 100644 --- a/tool/Makefile.am +++ b/tool/Makefile.am @@ -40,7 +40,7 @@ noinst_LTLIBRARIES = libpiv_cmd.la libpiv_util.la libpiv_cmd_la_SOURCES = cmdline.ggo cmdline.c cmdline.h libpiv_cmd_la_CFLAGS = -libpiv_util_la_SOURCES = util.c util.h +libpiv_util_la_SOURCES = util.c util.h openssl-compat.c openssl-compat.h libpiv_util_la_LIBADD = $(top_builddir)/lib/libykpiv.la $(OPENSSL_LIBS) cmdline.c cmdline.h: cmdline.ggo Makefile.am $(top_srcdir)/configure.ac diff --git a/tool/openssl-compat.c b/tool/openssl-compat.c new file mode 100644 index 0000000..a51af90 --- /dev/null +++ b/tool/openssl-compat.c @@ -0,0 +1,83 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "openssl-compat.h" +#if OPENSSL_VERSION_NUMBER < 0x10100000L + +#include +#include + + +int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) +{ + /* If the fields n and e in r are NULL, the corresponding input + * parameters MUST be non-NULL for n and e. d may be + * left NULL (in case only the public key is used). + */ + if ((r->n == NULL && n == NULL) + || (r->e == NULL && e == NULL)) + return 0; + + if (n != NULL) { + BN_free(r->n); + r->n = n; + } + if (e != NULL) { + BN_free(r->e); + r->e = e; + } + if (d != NULL) { + BN_free(r->d); + r->d = d; + } + + return 1; +} + +void RSA_get0_key(const RSA *r, + const BIGNUM **n, const BIGNUM **e, const BIGNUM **d) +{ + if (n != NULL) + *n = r->n; + if (e != NULL) + *e = r->e; + if (d != NULL) + *d = r->d; +} + +void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q) +{ + if (p != NULL) + *p = r->p; + if (q != NULL) + *q = r->q; +} + +void RSA_get0_crt_params(const RSA *r, + const BIGNUM **dmp1, const BIGNUM **dmq1, + const BIGNUM **iqmp) +{ + if (dmp1 != NULL) + *dmp1 = r->dmp1; + if (dmq1 != NULL) + *dmq1 = r->dmq1; + if (iqmp != NULL) + *iqmp = r->iqmp; +} + +void X509_SIG_getm(X509_SIG *sig, X509_ALGOR **palg, + ASN1_OCTET_STRING **pdigest) +{ + if (palg) + *palg = sig->algor; + if (pdigest) + *pdigest = sig->digest; +} + +#endif /* OPENSSL_VERSION_NUMBER */ diff --git a/tool/openssl-compat.h b/tool/openssl-compat.h new file mode 100644 index 0000000..3700bea --- /dev/null +++ b/tool/openssl-compat.h @@ -0,0 +1,37 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#ifndef LIBCRYPTO_COMPAT_H +#define LIBCRYPTO_COMPAT_H + +#ifndef _WINDOWS + +#include +#if OPENSSL_VERSION_NUMBER < 0x10100000L + +#include +#include +#include +#include +#include +#include + +int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d); +void RSA_get0_key(const RSA *r, + const BIGNUM **n, const BIGNUM **e, const BIGNUM **d); +void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q); +void RSA_get0_crt_params(const RSA *r, + const BIGNUM **dmp1, const BIGNUM **dmq1, + const BIGNUM **iqmp); +void X509_SIG_getm(X509_SIG *sig, X509_ALGOR **palg, + ASN1_OCTET_STRING **pdigest); + +#endif /* _WINDOWS */ +#endif /* OPENSSL_VERSION_NUMBER */ +#endif /* LIBCRYPTO_COMPAT_H */ diff --git a/tool/util.c b/tool/util.c index d3f88cd..de6b071 100644 --- a/tool/util.c +++ b/tool/util.c @@ -37,6 +37,7 @@ #include #endif +#include "openssl-compat.h" #include #include #include @@ -80,7 +81,7 @@ FILE *open_file(const char *file_name, enum file_mode mode) { } unsigned char get_algorithm(EVP_PKEY *key) { - int type = EVP_PKEY_type(key->type); + int type = EVP_PKEY_type(EVP_PKEY_id(key)); switch(type) { case EVP_PKEY_RSA: { @@ -333,23 +334,21 @@ bool set_component(unsigned char *in_ptr, const BIGNUM *bn, int element_len) { } bool prepare_rsa_signature(const unsigned char *in, unsigned int in_len, unsigned char *out, unsigned int *out_len, int nid) { - X509_SIG digestInfo; - X509_ALGOR algor; + X509_SIG *digestInfo; + X509_ALGOR *algor; ASN1_TYPE parameter; - ASN1_OCTET_STRING digest; + ASN1_OCTET_STRING *digest; unsigned char data[1024]; memcpy(data, in, in_len); - digestInfo.algor = &algor; - digestInfo.algor->algorithm = OBJ_nid2obj(nid); - digestInfo.algor->parameter = ¶meter; - digestInfo.algor->parameter->type = V_ASN1_NULL; - digestInfo.algor->parameter->value.ptr = NULL; - digestInfo.digest = &digest; - digestInfo.digest->data = data; - digestInfo.digest->length = (int)in_len; - *out_len = (unsigned int)i2d_X509_SIG(&digestInfo, &out); + digestInfo = X509_SIG_new(); + X509_SIG_getm(digestInfo, &algor, &digest); + algor->algorithm = OBJ_nid2obj(nid); + X509_ALGOR_set0(algor, OBJ_nid2obj(nid), V_ASN1_NULL, NULL); + ASN1_STRING_set(digest, data, in_len); + *out_len = (unsigned int)i2d_X509_SIG(digestInfo, &out); + X509_SIG_free(digestInfo); return true; } @@ -532,22 +531,24 @@ int SSH_write_X509(FILE *fp, X509 *x) { return ret; } - switch (pkey->type) { + switch (EVP_PKEY_id(pkey)) { case EVP_PKEY_RSA: case EVP_PKEY_RSA2: { RSA *rsa; unsigned char n[256]; + const BIGNUM *bn_n; char rsa_id[] = "\x00\x00\x00\x07ssh-rsa"; char rsa_f4[] = "\x00\x00\x00\x03\x01\x00\x01"; rsa = EVP_PKEY_get1_RSA(pkey); + RSA_get0_key(rsa, &bn_n, NULL, NULL); - if (!set_component(n, rsa->n, RSA_size(rsa))) { + if (!set_component(n, bn_n, RSA_size(rsa))) { break; } - uint32_t bytes = BN_num_bytes(rsa->n); + uint32_t bytes = BN_num_bytes(bn_n); char len_buf[5]; int len = 4; diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 4567b16..89daa79 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -42,6 +42,7 @@ #include #endif +#include "openssl-compat.h" #include #include #include @@ -123,6 +124,58 @@ static bool sign_data(ykpiv_state *state, const unsigned char *in, size_t len, u return false; } +#if OPENSSL_VERSION_NUMBER >= 0x10100000L +static int ec_key_ex_data_idx = -1; + +struct internal_key { + ykpiv_state *state; + int algorithm; + int key; +}; + +int yk_rsa_meth_sign(int dtype, const unsigned char *m, unsigned int m_length, + unsigned char *sigret, unsigned int *siglen, const RSA *rsa) { + const RSA_METHOD *meth = RSA_get_method(rsa); + const struct internal_key *key = RSA_meth_get0_app_data(meth); + if (sign_data(key->state, m, m_length, sigret, (size_t *)siglen, key->algorithm, key->key)) + return 0; + + return 1; +} + +int yk_ec_meth_sign(int type, const unsigned char *dgst, int dlen, + unsigned char *sig, unsigned int *siglen, const BIGNUM *kinv, + const BIGNUM *r, EC_KEY *ec) { + const struct internal_key *key = EC_KEY_get_ex_data(ec, ec_key_ex_data_idx); + if (sign_data(key->state, dgst, dlen, sig, (size_t *)siglen, key->algorithm, key->key)) + return 0; + + return 1; +} + +static int wrap_public_key(ykpiv_state *state, int algorithm, EVP_PKEY *public_key, + int key) { + if(YKPIV_IS_RSA(algorithm)) { + RSA_METHOD *meth = RSA_meth_dup(RSA_get_default_method()); + RSA *rsa = EVP_PKEY_get0_RSA(public_key); + struct internal_key int_key = {state, algorithm, key}; + RSA_meth_set0_app_data(meth, &int_key); + RSA_meth_set_sign(meth, yk_rsa_meth_sign); + RSA_set_method(rsa, meth); + } else { + EC_KEY *ec = EVP_PKEY_get0_EC_KEY(public_key); + EC_KEY_METHOD *meth = EC_KEY_METHOD_new(EC_KEY_get_method(ec)); + struct internal_key int_key = {state, algorithm, key}; + if (ec_key_ex_data_idx == -1) + ec_key_ex_data_idx = EC_KEY_get_ex_new_index(0, NULL, NULL, NULL, 0); + EC_KEY_set_ex_data(ec, ec_key_ex_data_idx, &int_key); + EC_KEY_METHOD_set_sign(meth, yk_ec_meth_sign, NULL, NULL); /* XXX ?? */ + EC_KEY_set_method(ec, meth); + } + return 0; +} +#endif + static bool generate_key(ykpiv_state *state, enum enum_slot slot, enum enum_algorithm algorithm, const char *output_file_name, enum enum_key_format key_format, enum enum_pin_policy pin_policy, @@ -168,17 +221,21 @@ static bool generate_key(ykpiv_state *state, enum enum_slot slot, if(key_format == key_format_arg_PEM) { public_key = EVP_PKEY_new(); if(algorithm == algorithm_arg_RSA1024 || algorithm == algorithm_arg_RSA2048) { + BIGNUM *bignum_n = NULL; + BIGNUM *bignum_e = NULL; rsa = RSA_new(); - rsa->n = BN_bin2bn(mod, mod_len, NULL); - if (rsa->n == NULL) { + bignum_n = BN_bin2bn(mod, mod_len, NULL); + if (bignum_n == NULL) { fprintf(stderr, "Failed to parse public key modulus.\n"); goto generate_out; } - rsa->e = BN_bin2bn(exp, exp_len, NULL); - if(rsa->e == NULL) { + bignum_e = BN_bin2bn(exp, exp_len, NULL); + if(bignum_e == NULL) { fprintf(stderr, "Failed to parse public key exponent.\n"); goto generate_out; } + + RSA_set0_key(rsa, bignum_n, bignum_e, NULL); EVP_PKEY_set1_RSA(public_key, rsa); } else if(algorithm == algorithm_arg_ECCP256 || algorithm == algorithm_arg_ECCP384) { EC_GROUP *group; @@ -329,39 +386,43 @@ static bool import_key(ykpiv_state *state, enum enum_key_format key_format, unsigned char dmp1[128]; unsigned char dmq1[128]; unsigned char iqmp[128]; + const BIGNUM *bn_e, *bn_p, *bn_q, *bn_dmp1, *bn_dmq1, *bn_iqmp; int element_len = 128; if(algorithm == YKPIV_ALGO_RSA1024) { element_len = 64; } - if((set_component(e, rsa_private_key->e, 3) == false) || + RSA_get0_key(rsa_private_key, NULL, &bn_e, NULL); + RSA_get0_factors(rsa_private_key, &bn_p, &bn_q); + RSA_get0_crt_params(rsa_private_key, &bn_dmp1, &bn_dmq1, &bn_iqmp); + if((set_component(e, bn_e, 3) == false) || !(e[0] == 0x01 && e[1] == 0x00 && e[2] == 0x01)) { fprintf(stderr, "Invalid public exponent for import (only 0x10001 supported)\n"); goto import_out; } - if(set_component(p, rsa_private_key->p, element_len) == false) { + if(set_component(p, bn_p, element_len) == false) { fprintf(stderr, "Failed setting p component.\n"); goto import_out; } - if(set_component(q, rsa_private_key->q, element_len) == false) { + if(set_component(q, bn_q, element_len) == false) { fprintf(stderr, "Failed setting q component.\n"); goto import_out; } - if(set_component(dmp1, rsa_private_key->dmp1, element_len) == false) { + if(set_component(dmp1, bn_dmp1, element_len) == false) { fprintf(stderr, "Failed setting dmp1 component.\n"); goto import_out; } - if(set_component(dmq1, rsa_private_key->dmq1, element_len) == false) { + if(set_component(dmq1, bn_dmq1, element_len) == false) { fprintf(stderr, "Failed setting dmq1 component.\n"); goto import_out; } - if(set_component(iqmp, rsa_private_key->iqmp, element_len) == false) { + if(set_component(iqmp, bn_iqmp, element_len) == false) { fprintf(stderr, "Failed setting iqmp component.\n"); goto import_out; } @@ -627,6 +688,7 @@ static bool request_certificate(ykpiv_state *state, enum enum_key_format key_for goto request_out; } +#if OPENSSL_VERSION_NUMBER < 0x10100000L memcpy(digest, oid, oid_len); /* XXX: this should probably use X509_REQ_digest() but that's buggy */ if(!ASN1_item_digest(ASN1_ITEM_rptr(X509_REQ_INFO), md, req->req_info, @@ -640,6 +702,7 @@ static bool request_certificate(ykpiv_state *state, enum enum_key_format key_for 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; @@ -662,6 +725,13 @@ static bool request_certificate(ykpiv_state *state, enum enum_key_format key_for /* mark that all bits should be used. */ req->signature->flags = ASN1_STRING_FLAG_BITS_LEFT; } +#else + /* With opaque structures we can not touch whatever we want, but we need + * to embed the sign_data function in the RSA/EC key structures */ + wrap_public_key(state, algorithm, public_key, key); + + X509_REQ_sign(req, public_key, md); +#endif if(key_format == key_format_arg_PEM) { PEM_write_X509_REQ(output_file, req); @@ -681,9 +751,11 @@ request_out: EVP_PKEY_free(public_key); } if(req) { +#if OPENSSL_VERSION_NUMBER < 0x10100000L if(req->sig_alg->parameter) { req->sig_alg->parameter = NULL; } +#endif X509_REQ_free(req); } if(name) { @@ -812,6 +884,7 @@ static bool selfsign_certificate(ykpiv_state *state, enum enum_key_format key_fo if(nid == 0) { goto selfsign_out; } +#if OPENSSL_VERSION_NUMBER < 0x10100000L if(YKPIV_IS_RSA(algorithm)) { signinput = digest; len = oid_len + md_len; @@ -845,6 +918,13 @@ static bool selfsign_certificate(ykpiv_state *state, enum enum_key_format key_fo * certificate can be validated. */ x509->signature->flags = ASN1_STRING_FLAG_BITS_LEFT; } +#else + /* With opaque structures we can not touch whatever we want, but we need + * to embed the sign_data function in the RSA/EC key structures */ + wrap_public_key(state, algorithm, public_key, key); + + X509_sign(x509, public_key, md); +#endif if(key_format == key_format_arg_PEM) { PEM_write_X509(output_file, x509); @@ -861,10 +941,12 @@ selfsign_out: fclose(output_file); } if(x509) { +#if OPENSSL_VERSION_NUMBER < 0x10100000L if(x509->sig_alg->parameter) { x509->sig_alg->parameter = NULL; x509->cert_info->signature->parameter = NULL; } +#endif X509_free(x509); } if(public_key) { @@ -1325,7 +1407,7 @@ static bool test_signature(ykpiv_state *state, enum enum_slot slot, { unsigned char rand[128]; EVP_MD_CTX *mdctx; - if(RAND_pseudo_bytes(rand, 128) == -1) { + if(RAND_bytes(rand, 128) == -1) { fprintf(stderr, "error: no randomness.\n"); return false; } @@ -1472,7 +1554,7 @@ static bool test_decipher(ykpiv_state *state, enum enum_slot slot, size_t len2 = sizeof(data); RSA *rsa = EVP_PKEY_get1_RSA(pubkey); - if(RAND_pseudo_bytes(secret, sizeof(secret)) == -1) { + if(RAND_bytes(secret, sizeof(secret)) == -1) { fprintf(stderr, "error: no randomness.\n"); ret = false; goto decipher_out; diff --git a/windows.mk b/windows.mk index 5dfc9db..b5b9b06 100644 --- a/windows.mk +++ b/windows.mk @@ -49,7 +49,7 @@ doit: tar xfa openssl-$(OPENSSLVERSION).tar.gz && \ cd openssl-$(OPENSSLVERSION) && \ CROSS_COMPILE="$(HOST)-" ./Configure mingw$(64) no-ssl2 no-ssl3 no-engines shared --prefix=$(PWD)/tmp$(ARCH)/root -static-libgcc && \ - make depend all install_sw && \ + make depend all install_sw VERSION="$(OPENSSLVERSION)" && \ cp LICENSE $(PWD)/tmp$(ARCH)/root/licenses/openssl.txt && \ rm -rf $(PWD)/tmp$(ARCH)/root/ssl/ && \ rm $(PWD)/tmp$(ARCH)/root/bin/openssl.exe && \ diff --git a/ykcs11/openssl_utils.c b/ykcs11/openssl_utils.c index a53ff6f..68fb29a 100644 --- a/ykcs11/openssl_utils.c +++ b/ykcs11/openssl_utils.c @@ -31,6 +31,7 @@ #include "openssl_utils.h" #include #include "../tool/util.h" // TODO: share this better? +#include "../tool/openssl-compat.h" // TODO: share this better? #include "debug.h" #include @@ -115,8 +116,7 @@ CK_RV do_create_empty_cert(CK_BYTE_PTR in, CK_ULONG in_len, CK_BBOOL is_rsa, if(bignum_e == NULL) goto create_empty_cert_cleanup; - rsa->n = bignum_n; - rsa->e = bignum_e; + RSA_set0_key(rsa, bignum_n, bignum_e, NULL); if (EVP_PKEY_set1_RSA(key, rsa) == 0) goto create_empty_cert_cleanup; @@ -165,6 +165,7 @@ CK_RV do_create_empty_cert(CK_BYTE_PTR in, CK_ULONG in_len, CK_BBOOL is_rsa, X509_set_notBefore(cert, tm); X509_set_notAfter(cert, tm); +#if OPENSSL_VERSION_NUMBER < 10100000L // Manually set the signature algorithms. // OpenSSL 1.0.1i complains about empty DER fields // 8 => md5WithRsaEncryption @@ -174,6 +175,8 @@ CK_RV do_create_empty_cert(CK_BYTE_PTR in, CK_ULONG in_len, CK_BBOOL is_rsa, // Manually set a signature (same reason as before) ASN1_BIT_STRING_set_bit(cert->signature, 8, 1); ASN1_BIT_STRING_set(cert->signature, (unsigned char*)"\x00", 1); + ASN1_BIT_STRING_set(cert->signature, (unsigned char*)"\x00", 1); +#endif len = i2d_X509(cert, NULL); if (len < 0) @@ -316,7 +319,7 @@ CK_RV do_store_pubk(X509 *cert, EVP_PKEY **key) { CK_KEY_TYPE do_get_key_type(EVP_PKEY *key) { - switch (key->type) { + switch (EVP_PKEY_id(key)) { case EVP_PKEY_RSA: case EVP_PKEY_RSA2: return CKK_RSA; @@ -349,18 +352,20 @@ CK_ULONG do_get_rsa_modulus_length(EVP_PKEY *key) { CK_RV do_get_modulus(EVP_PKEY *key, CK_BYTE_PTR data, CK_ULONG_PTR len) { RSA *rsa; + const BIGNUM *n; rsa = EVP_PKEY_get1_RSA(key); if (rsa == NULL) return CKR_FUNCTION_FAILED; - if ((CK_ULONG)BN_num_bytes(rsa->n) > *len) { + RSA_get0_key(rsa, &n, NULL, NULL); + if ((CK_ULONG)BN_num_bytes(n) > *len) { RSA_free(rsa); rsa = NULL; return CKR_BUFFER_TOO_SMALL; } - *len = (CK_ULONG)BN_bn2bin(rsa->n, data); + *len = (CK_ULONG)BN_bn2bin(n, data); RSA_free(rsa); rsa = NULL; @@ -372,18 +377,20 @@ CK_RV do_get_public_exponent(EVP_PKEY *key, CK_BYTE_PTR data, CK_ULONG_PTR len) CK_ULONG e = 0; RSA *rsa; + const BIGNUM *bn_e; rsa = EVP_PKEY_get1_RSA(key); if (rsa == NULL) return CKR_FUNCTION_FAILED; - if ((CK_ULONG)BN_num_bytes(rsa->e) > *len) { + RSA_get0_key(rsa, NULL, &bn_e, NULL); + if ((CK_ULONG)BN_num_bytes(bn_e) > *len) { RSA_free(rsa); rsa = NULL; return CKR_BUFFER_TOO_SMALL; } - *len = (CK_ULONG)BN_bn2bin(rsa->e, data); + *len = (CK_ULONG)BN_bn2bin(bn_e, data); RSA_free(rsa); rsa = NULL; @@ -406,7 +413,7 @@ CK_RV do_get_public_key(EVP_PKEY *key, CK_BYTE_PTR data, CK_ULONG_PTR len) { const EC_POINT *ecp; point_conversion_form_t pcf = POINT_CONVERSION_UNCOMPRESSED; - switch(key->type) { + switch(EVP_PKEY_id(key)) { case EVP_PKEY_RSA: case EVP_PKEY_RSA2: @@ -470,16 +477,20 @@ CK_RV do_get_public_key(EVP_PKEY *key, CK_BYTE_PTR data, CK_ULONG_PTR len) { CK_RV do_encode_rsa_public_key(ykcs11_rsa_key_t **key, CK_BYTE_PTR modulus, CK_ULONG mlen, CK_BYTE_PTR exponent, CK_ULONG elen) { ykcs11_rsa_key_t *k; + BIGNUM *k_n = NULL, *k_e = NULL; if (modulus == NULL || exponent == NULL) return CKR_ARGUMENTS_BAD; if ((k = RSA_new()) == NULL) return CKR_HOST_MEMORY; - if ((k->n = BN_bin2bn(modulus, mlen, NULL)) == NULL) + if ((k_n = BN_bin2bn(modulus, mlen, NULL)) == NULL) return CKR_FUNCTION_FAILED; - if ((k->e = BN_bin2bn(exponent, elen, NULL)) == NULL) + if ((k_e = BN_bin2bn(exponent, elen, NULL)) == NULL) + return CKR_FUNCTION_FAILED; + + if (RSA_set0_key(k, k_n, k_e, NULL) == 0) return CKR_FUNCTION_FAILED; *key = k; diff --git a/ykcs11/tests/Makefile.am b/ykcs11/tests/Makefile.am index 800b610..456abbb 100644 --- a/ykcs11/tests/Makefile.am +++ b/ykcs11/tests/Makefile.am @@ -44,6 +44,7 @@ endif ykcs11_tests_LDADD = ../libykcs11.la $(OPENSSL_LIBS) +ykcs11_tests_SOURCES = ykcs11_tests.c ../../tool/openssl-compat.c ../../tool/openssl-compat.h check_PROGRAMS = ykcs11_tests TESTS = reset.sh $(check_PROGRAMS) diff --git a/ykcs11/tests/ykcs11_tests.c b/ykcs11/tests/ykcs11_tests.c index 5f19d81..9fb51da 100644 --- a/ykcs11/tests/ykcs11_tests.c +++ b/ykcs11/tests/ykcs11_tests.c @@ -28,6 +28,7 @@ * */ +#include "../../tool/openssl-compat.h" #include #include @@ -273,6 +274,32 @@ static void test_login() { } +#if OPENSSL_VERSION_NUMBER >= 0x10100000L +static int bogus_sign(int dtype, const unsigned char *m, unsigned int m_length, + unsigned char *sigret, unsigned int *siglen, const RSA *rsa) { + sigret = malloc(1); + sigret = ""; + *siglen = 1; + return 0; +} + +static void bogus_sign_cert(X509 *cert) { + EVP_PKEY *pkey = EVP_PKEY_new(); + RSA *rsa = RSA_new(); + RSA_METHOD *meth = RSA_meth_dup(RSA_get_default_method()); + BIGNUM *e = BN_new(); + + BN_set_word(e, 65537); + RSA_generate_key_ex(rsa, 1024, e, NULL); + RSA_meth_set_sign(meth, bogus_sign); + RSA_set_method(rsa, meth); + EVP_PKEY_set1_RSA(pkey, rsa); + X509_sign(cert, pkey, EVP_md5()); + EVP_PKEY_free(pkey); +} +#endif + + // Import a newly generated P256 pvt key and a certificate // to every slot and use the key to sign some data static void test_import_and_sign_all_10() { @@ -358,11 +385,15 @@ static void test_import_and_sign_all_10() { X509_set_notBefore(cert, tm); X509_set_notAfter(cert, tm); +#if OPENSSL_VERSION_NUMBER < 0x10100000L cert->sig_alg->algorithm = OBJ_nid2obj(8); cert->cert_info->signature->algorithm = OBJ_nid2obj(8); ASN1_BIT_STRING_set_bit(cert->signature, 8, 1); ASN1_BIT_STRING_set(cert->signature, "\x00", 1); +#else + bogus_sign_cert(cert); +#endif p = value_c; if ((cert_len = (CK_ULONG) i2d_X509(cert, &p)) == 0 || cert_len > sizeof(value_c)) @@ -385,7 +416,7 @@ static void test_import_and_sign_all_10() { for (i = 0; i < 24; i++) { for (j = 0; j < 10; j++) { - if(RAND_pseudo_bytes(some_data, sizeof(some_data)) == -1) + if(RAND_bytes(some_data, sizeof(some_data)) == -1) exit(EXIT_FAILURE); asrt(funcs->C_Login(session, CKU_USER, "123456", 6), CKR_OK, "Login USER"); @@ -480,6 +511,7 @@ static void test_import_and_sign_all_10_RSA() { CK_BYTE_PTR s_ptr; CK_ULONG r_len; CK_ULONG s_len; + const BIGNUM *bp, *bq, *biqmp, *bdmp1, *bdmq1; unsigned char *px; @@ -522,11 +554,13 @@ static void test_import_and_sign_all_10_RSA() { asrt(RSA_generate_key_ex(rsak, 1024, e_bn, NULL), 1, "GENERATE RSAK"); - asrt(BN_bn2bin(rsak->p, p), 64, "GET P"); - asrt(BN_bn2bin(rsak->q, q), 64, "GET Q"); - asrt(BN_bn2bin(rsak->dmp1, dp), 64, "GET DP"); - asrt(BN_bn2bin(rsak->dmq1, dp), 64, "GET DQ"); - asrt(BN_bn2bin(rsak->iqmp, qinv), 64, "GET QINV"); + RSA_get0_factors(rsak, &bp, &bq); + RSA_get0_crt_params(rsak, &bdmp1, &bdmq1, &biqmp); + asrt(BN_bn2bin(bp, p), 64, "GET P"); + asrt(BN_bn2bin(bq, q), 64, "GET Q"); + asrt(BN_bn2bin(bdmp1, dp), 64, "GET DP"); + asrt(BN_bn2bin(bdmq1, dp), 64, "GET DQ"); + asrt(BN_bn2bin(biqmp, qinv), 64, "GET QINV"); @@ -549,11 +583,16 @@ static void test_import_and_sign_all_10_RSA() { X509_set_notBefore(cert, tm); X509_set_notAfter(cert, tm); +#if OPENSSL_VERSION_NUMBER < 0x10100000L + /* putting bogus data to signature to make some checks happy */ cert->sig_alg->algorithm = OBJ_nid2obj(8); cert->cert_info->signature->algorithm = OBJ_nid2obj(8); ASN1_BIT_STRING_set_bit(cert->signature, 8, 1); ASN1_BIT_STRING_set(cert->signature, "\x00", 1); +#else + bogus_sign_cert(cert); +#endif px = value_c; if ((cert_len = (CK_ULONG) i2d_X509(cert, &px)) == 0 || cert_len > sizeof(value_c)) @@ -576,7 +615,7 @@ static void test_import_and_sign_all_10_RSA() { for (i = 0; i < 24; i++) { for (j = 0; j < 10; j++) { - if(RAND_pseudo_bytes(some_data, sizeof(some_data)) == -1) + if(RAND_bytes(some_data, sizeof(some_data)) == -1) exit(EXIT_FAILURE); asrt(funcs->C_Login(session, CKU_USER, "123456", 6), CKR_OK, "Login USER");