diff --git a/.gitignore b/.gitignore index 0653efec..e6784d7d 100644 --- a/.gitignore +++ b/.gitignore @@ -45,6 +45,8 @@ libekmfweb/detect-openssl-version.dep libekmfweb/libekmfweb.so libekmfweb/libekmfweb.so.1 libekmfweb/libekmfweb.so.1.0 +libseckey/check-dep-libseckey +libseckey/detect-openssl-version.dep libutil/*_example libvmcp/vmcp_example libzds/libzds.a diff --git a/Makefile b/Makefile index a62ece72..70c15909 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,8 @@ ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/ar # Include common definitions include common.mak -LIB_DIRS = libvtoc libutil libzds libdasd libvmdump libccw libvmcp libekmfweb +LIB_DIRS = libvtoc libutil libzds libdasd libvmdump libccw libvmcp libekmfweb \ + libseckey TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \ tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \ vmconvert vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \ diff --git a/common.mak b/common.mak index 8ddb571a..d8847376 100644 --- a/common.mak +++ b/common.mak @@ -374,6 +374,10 @@ $(rootdir)/libekmfweb/libekmfweb.so: $(rootdir)/libekmfweb $(MAKE) -C $(rootdir)/libekmfweb/ libekmfweb.so .PHONY: $(rootdir)/libekmfweb +$(rootdir)/libseckey/libseckey.a: $(rootdir)/libseckey + $(MAKE) -C $(rootdir)/libseckey/ libseckey.a +.PHONY: $(rootdir)/libseckey + $(rootdir)/zipl/boot/data.o: $(MAKE) -C $(rootdir)/zipl/boot/ data.o diff --git a/include/libseckey/sk_cca.h b/include/libseckey/sk_cca.h new file mode 100644 index 00000000..bbc144c9 --- /dev/null +++ b/include/libseckey/sk_cca.h @@ -0,0 +1,48 @@ +/* + * libseckey - Secure key library + * + * Copyright IBM Corp. 2021 + * + * s390-tools is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ +#ifndef SK_CCA_H +#define SK_CCA_H + +#include +#include + +#include + +#include "libseckey/sk_openssl.h" + +#define CCA_MAX_PKA_KEY_TOKEN_SIZE 3500 + +int SK_CCA_generate_ec_key_pair(const struct sk_ext_cca_lib *cca_lib, + int curve_nid, unsigned char *key_token, + size_t *key_token_length, bool debug); + +int SK_CCA_generate_rsa_key_pair(const struct sk_ext_cca_lib *cca_lib, + size_t modulus_bits, unsigned int pub_exp, + unsigned char *key_token, + size_t *key_token_length, bool debug); + +int SK_CCA_get_key_type(const unsigned char *key_token, size_t key_token_length, + int *pkey_type); + +int SK_CCA_get_secure_key_as_pkey(const struct sk_ext_cca_lib *cca_lib, + const unsigned char *key_token, + size_t key_token_length, + bool rsa_pss, EVP_PKEY **pkey, bool debug); + +int SK_CCA_get_public_from_secure_key(const unsigned char *key_token, + size_t key_token_length, + sk_pub_key_func_t pub_key_cb, + void *private, + bool debug); + +int SK_CCA_reencipher_key(const struct sk_ext_cca_lib *cca_lib, + unsigned char *key_token, size_t key_token_length, + bool to_new, bool debug); + +#endif diff --git a/include/libseckey/sk_ep11.h b/include/libseckey/sk_ep11.h new file mode 100644 index 00000000..c98c9e97 --- /dev/null +++ b/include/libseckey/sk_ep11.h @@ -0,0 +1,177 @@ +/* + * libseckey - Secure key library + * + * Copyright IBM Corp. 2021 + * + * s390-tools is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ +#ifndef SK_EP11_H +#define SK_EP11_H + +#include +#include + +#include + +#include "libseckey/sk_openssl.h" + +#define EP11_MAX_KEY_TOKEN_SIZE 8192 + +int SK_EP11_generate_ec_key_pair(const struct sk_ext_ep11_lib *ep11_lib, + int curve_nid, unsigned char *key_token, + size_t *key_token_length, bool debug); + +int SK_EP11_generate_rsa_key_pair(const struct sk_ext_ep11_lib *ep11_lib, + size_t modulus_bits, unsigned int pub_exp, + bool x9_31, unsigned char *key_token, + size_t *key_token_length, bool debug); + +int SK_EP11_get_key_type(const unsigned char *key_token, + size_t key_token_length, + int *pkey_type); + +const unsigned char *SK_EP11_get_key_blob(const unsigned char *key_token, + size_t key_token_length); + +size_t SK_EP11_get_key_blob_size(const unsigned char *key_token, + size_t key_token_length); + +int SK_EP11_get_secure_key_as_pkey(const struct sk_ext_ep11_lib *ep11_lib, + const unsigned char *key_token, + size_t key_token_length, + bool rsa_pss, EVP_PKEY **pkey, bool debug); + +int SK_EP11_get_public_from_secure_key(const unsigned char *key_token, + size_t key_token_length, + sk_pub_key_func_t pub_key_cb, + void *private, + bool debug); + +int SK_EP11_reencipher_key(const struct sk_ext_ep11_lib *ep11_lib, + unsigned char *key_token, size_t key_token_length, + bool debug); + +/* PKCS#11 definitions */ + +#define CK_PTR * + +typedef unsigned char CK_BYTE; +typedef CK_BYTE CK_CHAR; +typedef CK_BYTE CK_UTF8CHAR; +typedef CK_BYTE CK_BBOOL; +typedef unsigned long CK_ULONG; +typedef long CK_LONG; +typedef CK_ULONG CK_FLAGS; +typedef CK_ULONG CK_RV; +typedef CK_ULONG CK_SLOT_ID; +typedef CK_ULONG CK_MECHANISM_TYPE; +typedef CK_ULONG CK_ATTRIBUTE_TYPE; +typedef CK_ULONG CK_OBJECT_CLASS; +typedef CK_ULONG CK_KEY_TYPE; +typedef CK_ULONG CK_RSA_PKCS_OAEP_SOURCE_TYPE; +typedef CK_ULONG CK_RSA_PKCS_MGF_TYPE; + +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; +typedef CK_SLOT_ID CK_PTR CK_SLOT_ID_PTR; +typedef CK_MECHANISM_TYPE CK_PTR CK_MECHANISM_TYPE_PTR; +typedef CK_RSA_PKCS_MGF_TYPE CK_PTR CK_RSA_PKCS_MGF_TYPE_PTR; + +typedef struct CK_MECHANISM { + CK_MECHANISM_TYPE mechanism; + CK_VOID_PTR pParameter; + CK_ULONG ulParameterLen; +} CK_MECHANISM; + +typedef CK_MECHANISM CK_PTR CK_MECHANISM_PTR; + +typedef struct CK_ATTRIBUTE { + CK_ATTRIBUTE_TYPE type; + CK_VOID_PTR pValue; + CK_ULONG ulValueLen; +} CK_ATTRIBUTE; + +typedef CK_ATTRIBUTE CK_PTR CK_ATTRIBUTE_PTR; + +typedef struct CK_RSA_PKCS_PSS_PARAMS { + CK_MECHANISM_TYPE hashAlg; + CK_RSA_PKCS_MGF_TYPE mgf; + CK_ULONG sLen; +} CK_RSA_PKCS_PSS_PARAMS; + +typedef CK_RSA_PKCS_PSS_PARAMS CK_PTR CK_RSA_PKCS_PSS_PARAMS_PTR; + +typedef struct CK_RSA_PKCS_OAEP_PARAMS { + CK_MECHANISM_TYPE hashAlg; + CK_RSA_PKCS_MGF_TYPE mgf; + CK_RSA_PKCS_OAEP_SOURCE_TYPE source; + CK_VOID_PTR pSourceData; + CK_ULONG ulSourceDataLen; +} CK_RSA_PKCS_OAEP_PARAMS; + +typedef CK_RSA_PKCS_OAEP_PARAMS CK_PTR CK_RSA_PKCS_OAEP_PARAMS_PTR; + +#define CKZ_DATA_SPECIFIED 0x00000001 + +#define CKG_MGF1_SHA1 0x00000001 +#define CKG_MGF1_SHA224 0x00000005 +#define CKG_MGF1_SHA256 0x00000002 +#define CKG_MGF1_SHA384 0x00000003 +#define CKG_MGF1_SHA512 0x00000004 + +#define CKG_VENDOR_DEFINED 0x80000000UL +#define CKG_IBM_MGF1_SHA3_224 (CKG_VENDOR_DEFINED + 1) +#define CKG_IBM_MGF1_SHA3_256 (CKG_VENDOR_DEFINED + 2) +#define CKG_IBM_MGF1_SHA3_384 (CKG_VENDOR_DEFINED + 3) +#define CKG_IBM_MGF1_SHA3_512 (CKG_VENDOR_DEFINED + 4) + +#define CKR_OK 0x00000000 +#define CKR_VENDOR_DEFINED 0x80000000 + +#define CKO_PUBLIC_KEY 0x00000002 +#define CKO_PRIVATE_KEY 0x00000003 + +#define CKK_EC 0x00000003 + +#define CKM_RSA_PKCS_KEY_PAIR_GEN 0x00000000 +#define CKM_RSA_PKCS 0x00000001 +#define CKM_RSA_PKCS_OAEP 0x00000009 +#define CKM_RSA_X9_31_KEY_PAIR_GEN 0x0000000A +#define CKM_RSA_X9_31 0x0000000B +#define CKM_RSA_PKCS_PSS 0x0000000D +#define CKM_SHA_1 0x00000220 +#define CKM_SHA256 0x00000250 +#define CKM_SHA224 0x00000255 +#define CKM_SHA384 0x00000260 +#define CKM_SHA512 0x00000270 +#define CKM_SHA512_224 0x00000048 +#define CKM_SHA512_256 0x0000004C +#define CKM_EC_KEY_PAIR_GEN 0x00001040 +#define CKM_ECDSA 0x00001041 + +#define CKM_VENDOR_DEFINED 0x80000000 +#define CKM_IBM_SHA3_224 (CKM_VENDOR_DEFINED + 0x00010001) +#define CKM_IBM_SHA3_256 (CKM_VENDOR_DEFINED + 0x00010002) +#define CKM_IBM_SHA3_384 (CKM_VENDOR_DEFINED + 0x00010003) +#define CKM_IBM_SHA3_512 (CKM_VENDOR_DEFINED + 0x00010004) + +#define CKA_CLASS 0x00000000 +#define CKA_KEY_TYPE 0x00000100 +#define CKA_SENSITIVE 0x00000103 +#define CKA_ENCRYPT 0x00000104 +#define CKA_DECRYPT 0x00000105 +#define CKA_SIGN 0x00000108 +#define CKA_VERIFY 0x0000010A +#define CKA_DERIVE 0x0000010C +#define CKA_DECRYPT 0x00000105 +#define CKA_WRAP 0x00000106 +#define CKA_UNWRAP 0x00000107 +#define CKA_MODULUS_BITS 0x00000121 +#define CKA_PUBLIC_EXPONENT 0x00000122 +#define CKA_EC_PARAMS 0x00000180 + +#endif diff --git a/include/libseckey/sk_openssl.h b/include/libseckey/sk_openssl.h new file mode 100644 index 00000000..e3bb015e --- /dev/null +++ b/include/libseckey/sk_openssl.h @@ -0,0 +1,234 @@ +/* + * libseckey - Secure key library + * + * Copyright IBM Corp. 2021 + * + * s390-tools is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ +#ifndef SK_OPENSSL_H +#define SK_OPENSSL_H + +#include +#include + +#include +#include +#include +#include + +#ifndef OPENSSL_VERSION_PREREQ + #if defined(OPENSSL_VERSION_MAJOR) && defined(OPENSSL_VERSION_MINOR) + #define OPENSSL_VERSION_PREREQ(maj, min) \ + ((OPENSSL_VERSION_MAJOR << 16) + \ + OPENSSL_VERSION_MINOR >= ((maj) << 16) + (min)) + #else + #define OPENSSL_VERSION_PREREQ(maj, min) \ + (OPENSSL_VERSION_NUMBER >= (((maj) << 28) | \ + ((min) << 20))) + #endif +#endif + +/** + * External crypto library definitions + */ + +struct sk_ext_cca_lib { + void *cca_lib; /* Handle of CCA host library loaded via dlopen */ +}; + +typedef uint64_t target_t; + +struct sk_ext_ep11_lib { + void *ep11_lib; /* Handle of EP11 host library loaded via dlopen */ + target_t target; /* single or group target handle */ +}; + +enum sk_ext_lib_type { + SK_EXT_LIB_CCA = 1, + SK_EXT_LIB_EP11 = 2, +}; + +struct sk_ext_lib { + enum sk_ext_lib_type type; + union { + struct sk_ext_cca_lib *cca; /* Used if type = EXT_LIB_CCA */ + struct sk_ext_ep11_lib *ep11; /* Used if type = EXT_LIB_EP11 */ + }; +}; + +/* + * Secure key library initialization and termination functions + */ + +int SK_OPENSSL_init(bool debug); +void SK_OPENSSL_term(void); + +/* + * Secure key generation and reenciphering definitions and functions + */ + +enum sk_key_type { + SK_KEY_TYPE_EC = 1, + SK_KEY_TYPE_RSA = 2, +}; + +struct sk_key_gen_info { + enum sk_key_type type; + union { + struct { + int curve_nid; + } ec; + struct { + size_t modulus_bits; + unsigned int pub_exp; + bool x9_31; + } rsa; + }; +}; + +int SK_OPENSSL_generate_secure_key(unsigned char *secure_key, + size_t *secure_key_size, + const struct sk_key_gen_info *info, + const struct sk_ext_lib *ext_lib, + bool debug); + +int SK_OPENSSL_reencipher_secure_key(unsigned char *secure_key, + size_t secure_key_size, bool to_new, + const struct sk_ext_lib *ext_lib, + bool debug); + +/* + * Get an OpenSSL PKEY from a secure key to be used with OpenSSL. + */ +int SK_OPENSSL_get_secure_key_as_pkey(const unsigned char *secure_key, + size_t secure_key_size, bool rsa_pss, + EVP_PKEY **pkey, + const struct sk_ext_lib *ext_lib, + bool debug); + +/* + * Get the public key parts from a secure key. + */ +struct sk_pub_key_info { + enum sk_key_type type; + union { + struct { + int curve_nid; + size_t prime_len; + const unsigned char *x; + const unsigned char *y; + } ec; + struct { + size_t modulus_len; + const unsigned char *modulus; + size_t pub_exp_len; + const unsigned char *pub_exp; + } rsa; + }; +}; + +typedef int (*sk_pub_key_func_t)(const struct sk_pub_key_info *pub_key, + void *private); + +int SK_OPENSSL_get_public_from_secure_key(const unsigned char *secure_key, + size_t secure_key_size, + sk_pub_key_func_t pub_key_cb, + void *private, + const struct sk_ext_lib *ext_lib, + bool debug); + +/* + * Helper functions to setup a secure key sign context and to generate + * certificate signing requests or self signed certificates with the secure key + */ + +struct sk_rsa_pss_params { + /* + * salt length in bytes, or OpenSSL constants + * RSA_PSS_SALTLEN_DIGEST (-1), RSA_PSS_SALTLEN_AUTO (-2), or + * RSA_PSS_SALTLEN_MAX(-3) + */ + int salt_len; + /* + * OpenSSl digest nid, or NID_undef to use the same digest algorithm + * as the signature algorithm + */ + int mgf_digest_nid; +}; + +int SK_OPENSSL_setup_sign_context(EVP_PKEY *pkey, bool verify, int digest_nid, + struct sk_rsa_pss_params *rsa_pss_params, + EVP_MD_CTX **md_ctx, EVP_PKEY_CTX **pkey_ctx, + bool debug); + +int SK_OPENSSL_generate_csr(const unsigned char *secure_key, + size_t secure_key_size, + const char *subject_rdns[], size_t num_subject_rdns, + bool subject_utf8, const X509 *renew_cert, + const char *extensions[], size_t num_extensions, + int digest_nid, + struct sk_rsa_pss_params *rsa_pss_params, + X509_REQ **csr, + const struct sk_ext_lib *ext_lib, bool debug); + +int SK_OPENSSL_generate_ss_cert(const unsigned char *secure_key, + size_t secure_key_size, + const char *subject_rdns[], + size_t num_subject_rdns, bool subject_utf8, + const X509 *renew_cert, + const char *extensions[], size_t num_extensions, + int validity_days, int digest_nid, + struct sk_rsa_pss_params *rsa_pss_params, + X509 **ss_cert, + const struct sk_ext_lib *ext_lib, bool debug); + +/* + * Import secure keys as PKEY, or import clear public keys as PKEY + */ + +typedef int (*sk_rsa_sign_t)(const unsigned char *key_blob, + size_t key_blob_length, + unsigned char *sig, size_t *siglen, + const unsigned char *tbs, size_t tbslen, + int padding_type, int md_nid, + void *private, bool debug); +typedef int (*sk_rsa_pss_sign_t)(const unsigned char *key_blob, + size_t key_blob_length, unsigned char *sig, + size_t *siglen, const unsigned char *tbs, + size_t tbslen, int md_nid, int mfgmd_nid, + int saltlen, void *private, bool debug); +typedef int (*sk_ecdsa_sign_t)(const unsigned char *key_blob, + size_t key_blob_length, unsigned char *sig, + size_t *siglen, const unsigned char *tbs, + size_t tbslen, int md_nid, void *private, + bool debug); +typedef int (*sk_rsa_decrypt_t)(const unsigned char *key_blob, + size_t key_blob_length, + unsigned char *to, size_t *tolen, + const unsigned char *from, size_t fromlen, + int padding_type, void *private, bool debug); +typedef int (*sk_rsa_decrypt_oaep_t)(const unsigned char *key_blob, + size_t key_blob_length, + unsigned char *to, size_t *tolen, + const unsigned char *from, size_t fromlen, + int oaep_md_nid, int mgfmd_nid, + unsigned char *label, int label_len, + void *private, bool debug); + +struct sk_funcs { + sk_rsa_sign_t rsa_sign; + sk_rsa_pss_sign_t rsa_pss_sign; + sk_ecdsa_sign_t ecdsa_sign; + sk_rsa_decrypt_t rsa_decrypt; + sk_rsa_decrypt_oaep_t rsa_decrypt_oaep; +}; + +int SK_OPENSSL_get_pkey(const unsigned char *secure_key, size_t secure_key_size, + const struct sk_pub_key_info *pub_key, bool rsa_pss, + const struct sk_funcs *sk_funcs, const void *private, + EVP_PKEY **pkey, bool debug); + +int SK_OPENSSL_get_curve_from_ec_pkey(EVP_PKEY *pkey); + +#endif diff --git a/include/libseckey/sk_utilities.h b/include/libseckey/sk_utilities.h new file mode 100644 index 00000000..ecdeb595 --- /dev/null +++ b/include/libseckey/sk_utilities.h @@ -0,0 +1,89 @@ +/* + * libseckey - Secure key library + * + * Copyright IBM Corp. 2021 + * + * s390-tools is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ +#ifndef SK_UTILITIES_H +#define SK_UTILITIES_H + +#include +#include + +#include +#include +#include + +#include "libseckey/sk_openssl.h" + +void SK_UTIL_warnx(const char *func, const char *fmt, ...); + +#define sk_debug(debug, fmt...) \ + do { \ + if (debug) \ + SK_UTIL_warnx(__func__, fmt); \ + } while (0) + +/* EC curve information definitions and functions */ +struct sk_ec_curve_info { + int curve_nid; + enum { + SK_EC_TYPE_PRIME = 0, + SK_EC_TYPE_BRAINPOOL = 1, + } type; + size_t prime_bits; + size_t prime_len; + const unsigned char *der; /* DER encoded OID */ + size_t der_size; +}; + +const struct sk_ec_curve_info *SK_UTIL_ec_get_curve_info(int curve_nid); +int SK_UTIL_ec_get_prime_curve_by_prime_bits(size_t prime_bits); +int SK_UTIL_ec_get_brainpool_curve_by_prime_bits(size_t prime_bits); + +int SK_UTIL_ec_calculate_y_coordinate(int nid, size_t prime_len, + const unsigned char *x, int y_bit, + unsigned char *y); + +/* Digest information definitions and functions */ +struct sk_digest_info { + int digest_nid; + size_t digest_size; + const char *cca_keyword; + const unsigned char *der; /* DER encoded SEQ of OID and OCT-STRING */ + size_t der_size; + unsigned long pkcs11_mech; + unsigned long pkcs11_mgf; + unsigned char x9_31_md; /* X9.31 digest identifier */ +}; + +const struct sk_digest_info *SK_UTIL_get_digest_info(int digest_nid); + +/* Helper functions for certificate and CSR handling */ +int SK_UTIL_build_subject_name(X509_NAME **name, const char *rdns[], + size_t num_rdns, bool utf8); +int SK_UTIL_build_certificate_extensions(X509 *cert, X509_REQ *req, + const char *exts[], size_t num_exts, + const STACK_OF(X509_EXTENSION) + *addl_exts); +int SK_UTIL_generate_x509_serial_number(X509 *cert, size_t sn_bit_size); + +int SK_UTIL_build_ecdsa_signature(const unsigned char *raw_sig, + size_t raw_sig_len, + unsigned char *sig, size_t *sig_len); + +/* Functions to read and write keys, certificates, requests, etc. */ +int SK_UTIL_read_x509_certificate(const char *pem_filename, X509 **cert); +int SK_UTIL_write_x509_certificate(const char *pem_filename, X509 *cert); +int SK_UTIL_write_x509_request(const char *pem_filename, X509_REQ *req, + bool new_hdr); +int SK_UTIL_read_key_blob(const char *filename, unsigned char *key_blob, + size_t *key_blob_len); +int SK_UTIL_write_key_blob(const char *filename, unsigned char *key_blob, + size_t key_blob_len); +int SK_UTIL_read_public_key(const char *pem_filename, EVP_PKEY **pkey); +int SK_UTIL_write_public_key(const char *pem_filename, EVP_PKEY *pkey); + +#endif diff --git a/libseckey/Makefile b/libseckey/Makefile new file mode 100644 index 00000000..66b94446 --- /dev/null +++ b/libseckey/Makefile @@ -0,0 +1,68 @@ +include ../common.mak + +lib = libseckey.a + +ifneq (${HAVE_OPENSSL},0) + BUILD_TARGETS = $(lib) +else + BUILD_TARGETS = skip-libseckey +endif + +TMPFILE := $(shell mktemp) + +detect-openssl-version.dep: + echo "#include " > $(TMPFILE) + echo "#include " >> $(TMPFILE) + echo "#ifndef OPENSSL_VERSION_PREREQ" >> $(TMPFILE) + echo " #if defined(OPENSSL_VERSION_MAJOR) && defined(OPENSSL_VERSION_MINOR)" >> $(TMPFILE) + echo " #define OPENSSL_VERSION_PREREQ(maj, min) \\" >> $(TMPFILE) + echo " ((OPENSSL_VERSION_MAJOR << 16) + \\" >> $(TMPFILE) + echo " OPENSSL_VERSION_MINOR >= ((maj) << 16) + (min))" >> $(TMPFILE) + echo " #else" >> $(TMPFILE) + echo " #define OPENSSL_VERSION_PREREQ(maj, min) \\" >> $(TMPFILE) + echo " (OPENSSL_VERSION_NUMBER >= (((maj) << 28) | \\" >> $(TMPFILE) + echo " ((min) << 20)))" >> $(TMPFILE) + echo " #endif" >> $(TMPFILE) + echo "#endif" >> $(TMPFILE) + echo "#if !OPENSSL_VERSION_PREREQ(1, 1)" >> $(TMPFILE) + echo " #error openssl version 1.1 is required" >> $(TMPFILE) + echo "#endif" >> $(TMPFILE) + echo "static void __attribute__((unused)) test(void) {" >> $(TMPFILE) + echo " EVP_PKEY_meth_remove(NULL);" >> $(TMPFILE) + echo "}" >> $(TMPFILE) + mv $(TMPFILE) $@ + +check-dep-libseckey: detect-openssl-version.dep + $(call check_dep, \ + "libseckey", \ + "detect-openssl-version.dep", \ + "openssl-devel version >= 1.1.1", \ + "HAVE_OPENSSL=0", \ + -I. -lcrypto -DOPENSSL_SUPPRESS_DEPRECATED) + touch check-dep-libseckey + +objects = sk_openssl.o sk_pkeymeth.o sk_provider.o sk_utilities.o sk_cca.o sk_ep11.o +headers = $(rootdir)include/libseckey/sk_openssl.h $(rootdir)include/libseckey/sk_utilities.h \ + $(rootdir)include/libseckey/sk_cca.h $(rootdir)include/libseckey/sk_ep11.h + +ALL_CFLAGS += -fPIC + +$(lib): $(objects) + +sk_openssl.o: check-dep-libseckey sk_openssl.c $(headers) +sk_pkeymeth.o: check-dep-libseckey sk_pkeymeth.c $(headers) +sk_provider.o: check-dep-libseckey sk_provider.c $(headers) +sk_cca.o: check-dep-libseckey sk_cca.c $(headers) +sk_ep11.o: check-dep-libseckey sk_ep11.c $(headers) + +all: $(BUILD_TARGETS) + +skip-libseckey: + echo " SKIP libseckey due to HAVE_OPENSSL=0" + +install: all + +clean: + rm -f *.o $(lib) detect-openssl-version.dep check-dep-libseckey + +.PHONY: all install clean skip-libseckey diff --git a/libseckey/sk_cca.c b/libseckey/sk_cca.c new file mode 100644 index 00000000..24ae8bc5 --- /dev/null +++ b/libseckey/sk_cca.c @@ -0,0 +1,1580 @@ +/* + * libseckey - Secure key library + * + * Copyright IBM Corp. 2021 + * + * s390-tools is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "lib/zt_common.h" + +#include "libseckey/sk_cca.h" +#include "libseckey/sk_openssl.h" +#include "libseckey/sk_utilities.h" + +/* Internal CCA definitions */ + +/* CCA PKA Key Generate function */ +typedef void (*CSNDPKG_t)(long *return_code, + long *reason_code, + long *exit_data_length, + unsigned char *exit_data, + long *rule_array_count, + unsigned char *rule_array, + long *regeneration_data_length, + unsigned char *regeneration_data, + long *skeleton_key_token_length, + unsigned char *skeleton_key_token, + unsigned char *transport_key_identifier, + long *generated_key_identifier_length, + unsigned char *generated_key_identifier); + +/* CCA PKA Key Token Build function */ +typedef void (*CSNDPKB_t)(long *return_code, + long *reason_code, + long *exit_data_length, + unsigned char *exit_data, + long *rule_array_count, + unsigned char *rule_array, + long *key_values_structure_length, + unsigned char *key_values_structure, + long *key_name_ln, + unsigned char *key_name, + long *reserved_1_length, + unsigned char *reserved_1, + long *reserved_2_length, + unsigned char *reserved_2, + long *reserved_3_length, + unsigned char *reserved_3, + long *reserved_4_length, + unsigned char *reserved_4, + long *reserved_5_length, + unsigned char *reserved_5, + long *token_length, unsigned char *token); + +/* CCA PKA Key Token Change function */ +typedef void (*CSNDKTC_t)(long *return_code, + long *reason_code, + long *exit_data_length, + unsigned char *exit_data, + long *rule_array_count, + unsigned char *rule_array, + long *key_identifier_length, + unsigned char *key_identifier); + +/* CCA Digital Signature Generate function */ +typedef void (*CSNDDSG_t)(long *return_code, + long *reason_code, + long *exit_data_length, + unsigned char *exit_data, + long *rule_array_count, + unsigned char *rule_array, + long *PKA_private_key_identifier_length, + unsigned char *PKA_private_key_identifier, + long *hash_length, + unsigned char *hash, + long *signature_field_length, + long *signature_bit_length, + unsigned char *signature_field); + +/* PKA Decrypt */ +typedef void (*CSNDPKD_t)(long *return_code, + long *reason_code, + long *exit_data_length, + unsigned char *exit_data, + long *rule_array_count, + unsigned char *rule_array, + long *PKA_enciphered_keyvalue_length, + unsigned char *PKA_enciphered_keyvalue, + long *data_structure_length, + unsigned char *data_structure, + long *PKA_key_identifier_length, + unsigned char *PKA_key_identifier, + long *target_keyvalue_length, + unsigned char *target_keyvalue); + +struct cca_lib { + CSNDPKG_t dll_CSNDPKG; + CSNDPKB_t dll_CSNDPKB; + CSNDKTC_t dll_CSNDKTC; + CSNDDSG_t dll_CSNDDSG; + CSNDPKD_t dll_CSNDPKD; +}; + +#define CCA_KEYWORD_SIZE 8 +#define CCA_KEY_ID_SIZE 64 + +struct cca_ec_key_pair_value_struct { + uint8_t curve_type; + uint8_t reserved; + uint16_t curve_length; + uint16_t priv_key_length; + uint16_t public_key_len; +} __packed; + +struct cca_rsa_key_pair_value_struct { + uint16_t modulus_bit_length; + uint16_t modulus_length; + uint16_t public_exp_length; + uint16_t reserved; + uint16_t p_length; + uint16_t q_length; + uint16_t dp_length; + uint16_t dq_length; + uint16_t u_length; + unsigned char public_exponent[3]; +} __packed; + +struct cca_ec_pub_key_value_struct { + uint8_t curve_type; + uint8_t reserved; + uint16_t curve_length; + uint16_t public_key_len; +} __packed; + +#define CCA_PRIME_CURVE 0x00 +#define CCA_BRAINPOOL_CURVE 0x01 + +struct cca_token_header { + uint8_t token_identifier; + uint8_t token_version1; /* Used for PKA key tokens */ + uint16_t token_length; + uint8_t token_version2; /* Used for symmetric key tokens */ + uint8_t reserved[3]; +} __packed; + +/* Key token identifiers */ +#define CCA_TOKEN_ID_NULL 0x00 +#define CCA_TOKEN_ID_EXTERNAL_PKA 0x1e +#define CCA_TOKEN_ID_INTERNAL_PKA 0x1f + +/* Key token versions */ +#define CCA_TOKEN_VERS1_V0 0x00 + +struct cca_section_header { + uint8_t section_identifier; + uint8_t section_version; + uint16_t section_length; +} __packed; + +#define CCA_SECTION_ID_RSA_ME_1024_PRIV 0x02 +#define CCA_SECTION_ID_RSA_PUBL 0x04 +#define CCA_SECTION_ID_RSA_CRT_2048_PRIV 0x05 +#define CCA_SECTION_ID_RSA_ME_1024_OPK_PRIV 0x06 +#define CCA_SECTION_ID_RSA_CRT_4096_OPK_PRIV 0x08 +#define CCA_SECTION_ID_RSA_ME_4096_PRIV 0x09 +#define CCA_SECTION_ID_EC_PRIV 0x20 +#define CCA_SECTION_ID_EC_PUBL 0x21 +#define CCA_SECTION_ID_RSA_ME_1024_EOPK_PRIV 0x30 +#define CCA_SECTION_ID_RSA_CRT_4096_EOPK_PRIV 0x31 + +struct cca_ec_pub_key_section { + struct cca_section_header section_header; + uint8_t reserved1[4]; + uint8_t curve_type; + uint8_t reserved2; + uint16_t prime_bits_length; + uint16_t pub_key_length; /* Incl. compression indication byte */ + /* Public key of length pub_key_length */ +} __packed; + +struct cca_rsa_pub_key_section { + struct cca_section_header section_header; + uint16_t reserved1; + uint16_t pub_exp_length; + uint16_t modulus_bits_length; + uint16_t modulus_length; /* if 0 -> see priv key section */ + /* Public exponent of length pub_exp_length */ + /* Modulus of length modulus_length */ +} __packed; + +struct cca_rsa_crt_priv_key_section { + struct cca_section_header section_header; + uint16_t assoc_data_length; + uint16_t payload_length; + uint16_t reserved1; + uint8_t assoc_data_version; + uint8_t key_format; + uint8_t key_source; + uint8_t reserved2; + uint8_t hash_type; + uint8_t hash[32]; + uint8_t reserved3[3]; + uint8_t key_usage; + uint8_t format_restriction; + uint16_t p_length; + uint16_t q_length; + uint16_t dp_length; + uint16_t dq_length; + uint16_t u_length; + uint16_t modulus_length; + uint32_t reserved4; + uint8_t opk[48]; + uint8_t kvp[16]; + uint16_t reserved6; + /* Public modulus in length modulus_length */ + /* Encrypted payload (AESKW-wrapped key material) */ +} __packed; + +#define POINT_CONVERSION_ODD_EVEN 0x01 + +/** + * Gets the CCA library function entry points from the library handle + */ +static int sk_cca_get_library_functions(const struct sk_ext_cca_lib *cca_lib, + struct cca_lib *cca) +{ + if (cca_lib == NULL || cca == NULL) + return -EINVAL; + + cca->dll_CSNDPKG = (CSNDPKG_t)dlsym(cca_lib->cca_lib, "CSNDPKG"); + cca->dll_CSNDPKB = (CSNDPKB_t)dlsym(cca_lib->cca_lib, "CSNDPKB"); + cca->dll_CSNDKTC = (CSNDKTC_t)dlsym(cca_lib->cca_lib, "CSNDKTC"); + cca->dll_CSNDDSG = (CSNDDSG_t)dlsym(cca_lib->cca_lib, "CSNDDSG"); + cca->dll_CSNDPKD = (CSNDPKD_t)dlsym(cca_lib->cca_lib, "CSNDPKD"); + + if (cca->dll_CSNDPKG == NULL || cca->dll_CSNDPKB == NULL || + cca->dll_CSNDKTC == NULL || cca->dll_CSNDDSG == NULL || + cca->dll_CSNDPKD == NULL) + return -EIO; + + return 0; +} + +/** + * Generates an CCA EC key of the specified curve type and length using the + * CCA host library. + * + * @param cca_lib the CCA library structure + * @param curve_nid the nid specifying the curve. + * @param key_token a buffer to store the generated key token + * @param key_token_length On entry: the size of the buffer + * On return: the size of the key token + * @param debug if true, debug messages are printed + * + * @returns a negative errno in case of an error, 0 if success. + */ +int SK_CCA_generate_ec_key_pair(const struct sk_ext_cca_lib *cca_lib, + int curve_nid, unsigned char *key_token, + size_t *key_token_length, bool debug) +{ + long return_code, reason_code, rule_array_count, exit_data_len = 0; + unsigned char transport_key_identifier[CCA_KEY_ID_SIZE] = { 0 }; + unsigned char key_skeleton[CCA_MAX_PKA_KEY_TOKEN_SIZE] = { 0 }; + long key_value_structure_length, private_key_name_length = 0; + unsigned char regeneration_data[CCA_KEY_ID_SIZE] = { 0 }; + struct cca_ec_key_pair_value_struct key_value_structure; + unsigned char private_key_name[CCA_KEY_ID_SIZE] = { 0 }; + unsigned char rule_array[3 * CCA_KEYWORD_SIZE] = { 0 }; + long regeneration_data_length = 0, key_skeleton_length; + const struct sk_ec_curve_info *curve; + unsigned char *exit_data = NULL; + unsigned char *param2 = NULL; + struct cca_lib cca; + long token_length; + long param1 = 0; + int rc; + + if (cca_lib == NULL || key_token == NULL || key_token_length == NULL) + return -EINVAL; + + if (key_token == NULL) { + *key_token_length = CCA_MAX_PKA_KEY_TOKEN_SIZE; + return 0; + } + + sk_debug(debug, "curve_nid: %d", curve_nid); + + rc = sk_cca_get_library_functions(cca_lib, &cca); + if (rc != 0) { + sk_debug(debug, + "ERROR: Failed to get CCA functions from library"); + return rc; + } + + memset(key_token, 0, *key_token_length); + token_length = *key_token_length; + + memset(&key_value_structure, 0, sizeof(key_value_structure)); + curve = SK_UTIL_ec_get_curve_info(curve_nid); + if (curve == NULL) { + sk_debug(debug, "ERROR: Unsupported curve: %d", curve_nid); + return -EINVAL; + } + switch (curve->type) { + case SK_EC_TYPE_PRIME: + key_value_structure.curve_type = CCA_PRIME_CURVE; + break; + case SK_EC_TYPE_BRAINPOOL: + key_value_structure.curve_type = CCA_BRAINPOOL_CURVE; + break; + default: + sk_debug(debug, "ERROR: Unknown curve type: %d", curve->type); + return -EINVAL; + } + + key_value_structure.curve_length = curve->prime_bits; + key_value_structure_length = sizeof(key_value_structure); + + rule_array_count = 3; + memcpy(rule_array, "ECC-PAIR", CCA_KEYWORD_SIZE); + memcpy(rule_array + CCA_KEYWORD_SIZE, "KEY-MGMT", CCA_KEYWORD_SIZE); + memcpy(rule_array + 2 * CCA_KEYWORD_SIZE, "ECC-VER1", CCA_KEYWORD_SIZE); + + key_skeleton_length = sizeof(key_skeleton); + + cca.dll_CSNDPKB(&return_code, &reason_code, + &exit_data_len, exit_data, + &rule_array_count, rule_array, + &key_value_structure_length, + (unsigned char *)&key_value_structure, + &private_key_name_length, private_key_name, + ¶m1, param2, ¶m1, param2, + ¶m1, param2, ¶m1, param2, + ¶m1, param2, + &key_skeleton_length, key_skeleton); + if (return_code != 0) { + sk_debug(debug, "ERROR: CCA CSNDPKB (EC KEY TOKEN BUILD) " + "failed: return_code: %ld reason_code: %ld", + return_code, reason_code); + return -EIO; + } + + rule_array_count = 1; + memset(rule_array, 0, sizeof(rule_array)); + memcpy(rule_array, "MASTER ", (size_t)CCA_KEYWORD_SIZE); + + cca.dll_CSNDPKG(&return_code, &reason_code, + NULL, NULL, + &rule_array_count, rule_array, + ®eneration_data_length, regeneration_data, + &key_skeleton_length, key_skeleton, + transport_key_identifier, + &token_length, key_token); + if (return_code != 0) { + sk_debug(debug, "ERROR: CCA CSNDPKG (EC KEY GENERATE) failed: " + "return_code: %ld reason_code: %ld", return_code, + reason_code); + return -EIO; + } + + *key_token_length = token_length; + + return 0; +} + +/** + * Generates an CCA RSA key of the specified key size and optionally the + * specified public exponent using the CCA host library. + * + * @param cca_lib the CCA library structure + * @param modulus_bits the size of the key in bits (512, 1024, 2048, 4096) + * @param pub_exp the public exponent or zero. Possible values are: + * 3, 5, 17, 257, or 65537. Specify zero to choose the + * exponent by random (only possible for modulus_bits + * up to 2048). + * @param key_token a buffer to store the generated key token + * @param key_token_length On entry: the size of the buffer + * On return: the size of the key token + * @param debug if true, debug messages are printed + * + * @returns a negative errno in case of an error, 0 if success. + */ +int SK_CCA_generate_rsa_key_pair(const struct sk_ext_cca_lib *cca_lib, + size_t modulus_bits, unsigned int pub_exp, + unsigned char *key_token, + size_t *key_token_length, bool debug) +{ + long return_code, reason_code, rule_array_count, exit_data_len = 0; + unsigned char transport_key_identifier[CCA_KEY_ID_SIZE] = { 0 }; + unsigned char key_skeleton[CCA_MAX_PKA_KEY_TOKEN_SIZE] = { 0 }; + long key_value_structure_length, private_key_name_length = 0; + unsigned char regeneration_data[CCA_KEY_ID_SIZE] = { 0 }; + struct cca_rsa_key_pair_value_struct key_value_structure; + unsigned char private_key_name[CCA_KEY_ID_SIZE] = { 0 }; + unsigned char rule_array[2 * CCA_KEYWORD_SIZE] = { 0 }; + long regeneration_data_length = 0, key_skeleton_length; + unsigned char *exit_data = NULL; + unsigned char *param2 = NULL; + struct cca_lib cca; + long token_length; + long param1 = 0; + int rc; + + if (cca_lib == NULL || key_token == NULL || key_token_length == NULL) + return -EINVAL; + + if (key_token == NULL) { + *key_token_length = CCA_MAX_PKA_KEY_TOKEN_SIZE; + return 0; + } + + sk_debug(debug, "modulus_bits: %lu pub_exp: %u", modulus_bits, + pub_exp); + + rc = sk_cca_get_library_functions(cca_lib, &cca); + if (rc != 0) { + sk_debug(debug, + "ERROR: Failed to get CCA functions from library"); + return rc; + } + + memset(key_token, 0, *key_token_length); + token_length = *key_token_length; + + memset(&key_value_structure, 0, sizeof(key_value_structure)); + key_value_structure.modulus_bit_length = modulus_bits; + switch (pub_exp) { + case 0: + if (modulus_bits > 2048) { + sk_debug(debug, "ERROR: Cannot auto-generate public " + "exponent for keys > 2048"); + return -EINVAL; + } + key_value_structure.public_exp_length = 0; + break; + case 3: + key_value_structure.public_exp_length = 1; + key_value_structure.public_exponent[0] = 3; + break; + case 5: + key_value_structure.public_exp_length = 1; + key_value_structure.public_exponent[0] = 5; + break; + case 17: + key_value_structure.public_exp_length = 1; + key_value_structure.public_exponent[0] = 17; + break; + case 257: + key_value_structure.public_exp_length = 2; + key_value_structure.public_exponent[0] = 0x01; + key_value_structure.public_exponent[0] = 0x01; + break; + case 65537: + key_value_structure.public_exp_length = 3; + key_value_structure.public_exponent[0] = 0x01; + key_value_structure.public_exponent[1] = 0x00; + key_value_structure.public_exponent[2] = 0x01; + break; + default: + sk_debug(debug, "ERROR: Invalid public exponent: %d", pub_exp); + return -EINVAL; + } + + key_value_structure_length = sizeof(key_value_structure) + + key_value_structure.public_exp_length; + + rule_array_count = 2; + memcpy(rule_array, "RSA-AESC", CCA_KEYWORD_SIZE); + memcpy(rule_array + CCA_KEYWORD_SIZE, "KEY-MGMT", CCA_KEYWORD_SIZE); + + key_skeleton_length = sizeof(key_skeleton); + + cca.dll_CSNDPKB(&return_code, &reason_code, + &exit_data_len, exit_data, + &rule_array_count, rule_array, + &key_value_structure_length, + (unsigned char *)&key_value_structure, + &private_key_name_length, private_key_name, + ¶m1, param2, ¶m1, param2, + ¶m1, param2, ¶m1, param2, + ¶m1, param2, + &key_skeleton_length, key_skeleton); + if (return_code != 0) { + sk_debug(debug, "ERROR: CCA CSNDPKB (RSA KEY TOKEN BUILD) " + "failed: return_code: %ld reason_code: %ld", + return_code, reason_code); + return -EIO; + } + + rule_array_count = 1; + memset(rule_array, 0, sizeof(rule_array)); + memcpy(rule_array, "MASTER ", (size_t)CCA_KEYWORD_SIZE); + + cca.dll_CSNDPKG(&return_code, &reason_code, + NULL, NULL, + &rule_array_count, rule_array, + ®eneration_data_length, regeneration_data, + &key_skeleton_length, key_skeleton, + transport_key_identifier, + &token_length, key_token); + if (return_code != 0) { + sk_debug(debug, "ERROR: CCA CSNDPKG (RSA KEY GENERATE) failed: " + "return_code: %ld reason_code: %ld", return_code, + reason_code); + return -EIO; + } + + *key_token_length = token_length; + + return 0; +} + +/** + * Finds a specific section of a CCA internal PKA key token. + */ +static const void *sk_cca_get_pka_section(const unsigned char *key_token, + size_t key_token_length, + unsigned int section_id, bool debug) +{ + const struct cca_section_header *section_hdr; + const struct cca_token_header *token_hdr; + size_t ofs; + + if (key_token == NULL) + return NULL; + + sk_debug(debug, "section_id: %x", section_id); + + if (key_token_length < sizeof(struct cca_token_header)) { + sk_debug(debug, "ERROR: key token length too small"); + return NULL; + } + + token_hdr = (struct cca_token_header *)key_token; + if (token_hdr->token_length > key_token_length) { + sk_debug(debug, "ERROR: key token length too small"); + return NULL; + } + if (token_hdr->token_identifier != CCA_TOKEN_ID_INTERNAL_PKA) { + sk_debug(debug, "ERROR: not an internal PKA token"); + return NULL; + } + if (token_hdr->token_version1 != CCA_TOKEN_VERS1_V0) { + sk_debug(debug, "ERROR: invalid token version"); + return NULL; + } + + ofs = sizeof(struct cca_token_header); + section_hdr = (struct cca_section_header *)&key_token[ofs]; + + while (section_hdr->section_identifier != section_id) { + ofs += section_hdr->section_length; + if (ofs >= token_hdr->token_length) { + sk_debug(debug, "ERROR: section %u not found", + section_id); + return NULL; + } + section_hdr = (struct cca_section_header *)&key_token[ofs]; + } + + if (ofs + section_hdr->section_length > token_hdr->token_length) { + sk_debug(debug, "ERROR: section exceed the token length"); + return NULL; + } + + return section_hdr; +} + +/** + * Queries the PKEY type of the key token. + * + * @param key_token the key token containing an CCA EC key + * @param key_token_length the size of the key token + * @param pkey_type On return: the PKEY type of the key token + * + * @returns a negative errno in case of an error, 0 if success. + */ +int SK_CCA_get_key_type(const unsigned char *key_token, size_t key_token_length, + int *pkey_type) +{ + if (key_token == NULL || pkey_type == NULL) + return -EINVAL; + + if (sk_cca_get_pka_section(key_token, key_token_length, + CCA_SECTION_ID_EC_PUBL, false) != NULL) + *pkey_type = EVP_PKEY_EC; + else if (sk_cca_get_pka_section(key_token, key_token_length, + CCA_SECTION_ID_RSA_PUBL, false) != NULL) + *pkey_type = EVP_PKEY_RSA; + else + return -EINVAL; + + return 0; +} + +/** + * Sign data using RSA. + * + * @param key_token the RSA key token + * @param key_token_length the length of the key token + * @param sig a buffer to store the signature on return. + * @param siglen on input: the size if the signature buffer + * on return: the size of the signature + * @param tbs the data to be signed. + * @param tbslen the size of the data to be signed + * @param padding_type the OpenSSL padding type (RSA_X931_PADDING or + * RSA_PKCS1_PADDING) + * @param digest_nid the OpenSSL nid of the message digest used to + * produce the data to be signed + * @param private the CCA library structure + * @param debug if true, debug messages are printed + * + * @returns a negative errno in case of an error, 0 if success. + */ +static int sk_cca_rsa_sign(const unsigned char *key_token, + size_t key_token_length, + unsigned char *sig, size_t *siglen, + const unsigned char *tbs, size_t tbslen, + int padding_type, int md_nid, + void *private, bool debug) +{ + long return_code, reason_code, rule_array_count, exit_data_len = 0; + long token_length, hash_length, sign_bit_length, sign_length; + unsigned char rule_array[4 * CCA_KEYWORD_SIZE] = { 0 }; + const struct sk_ext_cca_lib *cca_lib = private; + unsigned char *hash = NULL, *buf = NULL; + const struct sk_digest_info *digest; + unsigned char *exit_data = NULL; + struct cca_lib cca; + int rc; + + if (cca_lib == NULL || key_token == NULL || sig == NULL || + siglen == NULL || tbs == NULL) + return -EINVAL; + + sk_debug(debug, "tbslen: %lu siglen: %lu padding_type: %d md_nid: %d", + tbslen, *siglen, padding_type, md_nid); + + rc = sk_cca_get_library_functions(cca_lib, &cca); + if (rc != 0) { + sk_debug(debug, + "ERROR: Failed to get CCA functions from library"); + return rc; + } + + digest = SK_UTIL_get_digest_info(md_nid); + if (digest == NULL) { + sk_debug(debug, "ERROR: Invalid digest nid: %d", md_nid); + return -EINVAL; + } + + if (tbslen != digest->digest_size) { + sk_debug(debug, "ERROR: Invalid data length: %lu", tbslen); + return -EINVAL; + } + + rule_array_count = 2; + memcpy(rule_array, "RSA ", CCA_KEYWORD_SIZE); + memcpy(rule_array + CCA_KEYWORD_SIZE, "HASH ", CCA_KEYWORD_SIZE); + + switch (padding_type) { + case RSA_X931_PADDING: + hash = (unsigned char *)tbs; + hash_length = tbslen; + + memcpy(rule_array + 2 * CCA_KEYWORD_SIZE, "X9.31 ", + CCA_KEYWORD_SIZE); + memcpy(rule_array + 3 * CCA_KEYWORD_SIZE, digest->cca_keyword, + CCA_KEYWORD_SIZE); + rule_array_count = 4; + break; + + case RSA_PKCS1_PADDING: + hash_length = digest->der_size + tbslen; + buf = (unsigned char *)malloc(hash_length); + if (buf == NULL) { + sk_debug(debug, "ERROR: malloc failed"); + return -ENOMEM; + } + + memcpy(buf, digest->der, digest->der_size); + memcpy(buf + digest->der_size, tbs, tbslen); + hash = buf; + + memcpy(rule_array + 2 * CCA_KEYWORD_SIZE, "PKCS-1.1", + CCA_KEYWORD_SIZE); + rule_array_count = 3; + break; + + default: + sk_debug(debug, "ERROR: Invalid padding type: %d", + padding_type); + return -EINVAL; + } + + token_length = key_token_length; + sign_length = *siglen; + + cca.dll_CSNDDSG(&return_code, &reason_code, + &exit_data_len, exit_data, + &rule_array_count, rule_array, + &token_length, (unsigned char *)key_token, + &hash_length, hash, + &sign_length, &sign_bit_length, sig); + + if (return_code != 0) { + sk_debug(debug, "ERROR: CCA CSNDDSG (DIG. SIGNATURE CREATE, " + "RSA) failed: return_code: %ld reason_code: %ld", + return_code, reason_code); + rc = -EIO; + goto out; + } + + *siglen = sign_length; + rc = 0; + + sk_debug(debug, "siglen: %lu", *siglen); + +out: + if (buf != NULL) + free(buf); + + return rc; +} + +/** + * Sign data using RSA-PSS. + * + * @param key_token the RSA key token + * @param key_token_length the length of the key token + * @param sig a buffer to store the signature on return. + * @param siglen on input: the size if the signature buffer + * on return: the size of the signature + * @param tbs the data to be signed. + * @param tbslen the size of the data to be signed + * @param digest_nid the OpenSSL nid of the message digest used to + * produce the data to be signed + * @param mgf_digest_nid the OpenSSL nid of the mask generation function for + * PSS padding + * @param saltlen the length of the salt for PSS + * @param private the CCA library structure + * @param debug if true, debug messages are printed + * + * @returns a negative errno in case of an error, 0 if success. + */ +static int sk_cca_rsa_pss_sign(const unsigned char *key_token, + size_t key_token_length, + unsigned char *sig, size_t *siglen, + const unsigned char *tbs, size_t tbslen, + int digest_nid, int mgf_digest_nid, int saltlen, + void *private, bool debug) +{ + long return_code, reason_code, rule_array_count, exit_data_len = 0; + long token_length, hash_length, sign_bit_length, sign_length; + unsigned char rule_array[4 * CCA_KEYWORD_SIZE] = { 0 }; + const struct sk_ext_cca_lib *cca_lib = private; + const struct sk_digest_info *digest; + unsigned char *exit_data = NULL; + unsigned char *buf = NULL; + struct cca_lib cca; + uint32_t salt_len; + int rc; + + if (cca_lib == NULL || key_token == NULL || sig == NULL || + siglen == NULL || tbs == NULL) + return -EINVAL; + + sk_debug(debug, "tbslen: %lu siglen: %lu digest_nid: %d " + "mgf_digest_nid: %d saltlen: %d", + tbslen, *siglen, digest_nid, mgf_digest_nid, saltlen); + + rc = sk_cca_get_library_functions(cca_lib, &cca); + if (rc != 0) { + sk_debug(debug, + "ERROR: Failed to get CCA functions from library"); + return rc; + } + + if (mgf_digest_nid != digest_nid) { + sk_debug(debug, "ERROR: Mgf nid must be the same as the " + "message digest nid"); + return -EINVAL; + } + + digest = SK_UTIL_get_digest_info(digest_nid); + if (digest == NULL || digest->cca_keyword == NULL) { + sk_debug(debug, "ERROR: Invalid digest nid: %d", digest_nid); + return -EINVAL; + } + + if (tbslen != digest->digest_size) { + sk_debug(debug, "ERROR: Invalid data length: %lu", tbslen); + return -EINVAL; + } + + rule_array_count = 4; + memcpy(rule_array, "RSA ", CCA_KEYWORD_SIZE); + memcpy(rule_array + CCA_KEYWORD_SIZE, "PKCS-PSS", CCA_KEYWORD_SIZE); + memcpy(rule_array + 2 * CCA_KEYWORD_SIZE, "HASH ", CCA_KEYWORD_SIZE); + memcpy(rule_array + 3 * CCA_KEYWORD_SIZE, digest->cca_keyword, + CCA_KEYWORD_SIZE); + + hash_length = sizeof(uint32_t) + tbslen; + buf = (unsigned char *)malloc(hash_length); + if (buf == NULL) { + sk_debug(debug, "ERROR: malloc failed"); + return -ENOMEM; + } + + salt_len = saltlen; + memcpy(buf, &salt_len, sizeof(uint32_t)); + memcpy(buf + sizeof(uint32_t), tbs, tbslen); + + token_length = key_token_length; + sign_length = *siglen; + + cca.dll_CSNDDSG(&return_code, &reason_code, + &exit_data_len, exit_data, + &rule_array_count, rule_array, + &token_length, (unsigned char *)key_token, + &hash_length, buf, + &sign_length, &sign_bit_length, sig); + + if (return_code != 0) { + sk_debug(debug, "ERROR: CCA CSNDDSG (DIG. SIGNATURE CREATE, " + "RSA-PSS) failed: return_code: %ld reason_code: %ld", + return_code, reason_code); + rc = -EIO; + goto out; + } + + *siglen = sign_length; + rc = 0; + + sk_debug(debug, "siglen: %lu", *siglen); + +out: + free(buf); + + return rc; +} + +/** + * Decrypt data using RSA. + * + * @param key_token the RSA key token + * @param key_token_length the length of the key token + * @param to a buffer to store the decrypted data on return. + * @param tolen on input: the size if the to buffer + * on return: the size of the decrypted data + * @param from the data to be decrypted. + * @param fromlen the size of the data to be decrypted + * @param padding_type the OpenSSL padding type + * @param private the CCA library structure + * @param debug if true, debug messages are printed + * + * @returns a negative errno in case of an error, 0 if success. + */ +static int sk_cca_rsa_decrypt(const unsigned char *key_token, + size_t key_token_length, + unsigned char *to, size_t *tolen, + const unsigned char *from, size_t fromlen, + int padding_type, void *private, bool debug) +{ + long return_code, reason_code, rule_array_count, exit_data_len = 0; + long token_length, from_length, to_length, data_struct_len = 0; + unsigned char rule_array[3 * CCA_KEYWORD_SIZE] = { 0 }; + const struct sk_ext_cca_lib *cca_lib = private; + unsigned char *data_struct = NULL; + unsigned char *exit_data = NULL; + struct cca_lib cca; + int rc; + + if (cca_lib == NULL || key_token == NULL || to == NULL || + tolen == NULL || from == NULL) + return -EINVAL; + + sk_debug(debug, "fromlen: %lu tolen: %lu padding_type: %d", + fromlen, *tolen, padding_type); + + rc = sk_cca_get_library_functions(cca_lib, &cca); + if (rc != 0) { + sk_debug(debug, + "ERROR: Failed to get CCA functions from library"); + return rc; + } + + rule_array_count = 1; + + switch (padding_type) { + case RSA_PKCS1_PADDING: + memcpy(rule_array, "PKCS-1.2", CCA_KEYWORD_SIZE); + break; + + default: + sk_debug(debug, "ERROR: Invalid padding type: %d", + padding_type); + return -EINVAL; + } + + token_length = key_token_length; + from_length = fromlen; + to_length = *tolen; + if (to_length > from_length) + to_length = from_length; + + cca.dll_CSNDPKD(&return_code, &reason_code, + &exit_data_len, exit_data, + &rule_array_count, rule_array, + &from_length, (unsigned char *)from, + &data_struct_len, data_struct, + &token_length, (unsigned char *)key_token, + &to_length, to); + + if (return_code != 0) { + sk_debug(debug, "ERROR: CCA CSNDPKD (PKA DECRYPT) " + "failed: return_code: %ld reason_code: %ld", + return_code, reason_code); + rc = -EIO; + goto out; + } + + *tolen = to_length; + rc = 0; + + sk_debug(debug, "tolen: %lu", *tolen); + +out: + return rc; +} + +/** + * Decrypt data using RSA OAEP. + * + * @param key_token the RSA key token + * @param key_token_length the length of the key token + * @param to a buffer to store the decrypted data on return. + * @param tolen on input: the size if the to buffer + * on return: the size of the decrypted data + * @param from the data to be decrypted. + * @param fromlen the size of the data to be decrypted + * @param oaep_md_nid the OpenSSL nid of the OAEP hashing algorithm + * @param mgfmd_nid the OpenSSL nid of the mask generation function + * @param label the label for OAEP + * @param label_len the length of the label for OAEP + * @param private the CCA library structure + * @param debug if true, debug messages are printed + * + * @returns a negative errno in case of an error, 0 if success. + */ +static int sk_cca_rsa_decrypt_oaep(const unsigned char *key_token, + size_t key_token_length, + unsigned char *to, size_t *tolen, + const unsigned char *from, size_t fromlen, + int oaep_md_nid, int mgfmd_nid, + unsigned char *UNUSED(label), + int label_len, void *private, bool debug) +{ + + long return_code, reason_code, rule_array_count, exit_data_len = 0; + long token_length, from_length, to_length, data_struct_len = 0; + unsigned char rule_array[3 * CCA_KEYWORD_SIZE] = { 0 }; + const struct sk_ext_cca_lib *cca_lib = private; + unsigned char *data_struct = NULL; + const struct sk_digest_info *digest; + unsigned char *exit_data = NULL; + struct cca_lib cca; + int rc; + + if (cca_lib == NULL || key_token == NULL || to == NULL || + tolen == NULL || from == NULL) + return -EINVAL; + + sk_debug(debug, "fromlen: %lu tolen: %lu oaep_md_nid: %d mgfmd_nid: %d", + fromlen, *tolen, oaep_md_nid, mgfmd_nid); + + rc = sk_cca_get_library_functions(cca_lib, &cca); + if (rc != 0) { + sk_debug(debug, + "ERROR: Failed to get CCA functions from library"); + return rc; + } + + if (label_len != 0) { + sk_debug(debug, "ERROR: CCA does not support non-empty OAEP " + "label"); + return -EINVAL; + } + + if (oaep_md_nid != mgfmd_nid) { + sk_debug(debug, "ERROR: Mgf nid must be the same as the oaep " + "nid"); + return -EINVAL; + } + + digest = SK_UTIL_get_digest_info(mgfmd_nid); + if (digest == NULL || digest->cca_keyword == NULL) { + sk_debug(debug, "ERROR: Invalid mgf nid: %d", mgfmd_nid); + return -EINVAL; + } + + rule_array_count = 2; + memcpy(rule_array, "PKCSOAEP", CCA_KEYWORD_SIZE); + memcpy(rule_array + CCA_KEYWORD_SIZE, digest->cca_keyword, + CCA_KEYWORD_SIZE); + + token_length = key_token_length; + from_length = fromlen; + to_length = *tolen; + if (to_length > from_length) + to_length = from_length; + + cca.dll_CSNDPKD(&return_code, &reason_code, + &exit_data_len, exit_data, + &rule_array_count, rule_array, + &from_length, (unsigned char *)from, + &data_struct_len, data_struct, + &token_length, (unsigned char *)key_token, + &to_length, to); + + if (return_code != 0) { + sk_debug(debug, "ERROR: CCA CSNDPKD (PKA DECRYPT) " + "failed: return_code: %ld reason_code: %ld", + return_code, reason_code); + rc = -EIO; + goto out; + } + + *tolen = to_length; + rc = 0; + + sk_debug(debug, "tolen: %lu", *tolen); + +out: + return rc; +} + +/** + * Sign data using ECDSA. + * + * @param key_token the RSA key token + * @param key_token_length the length of the key token + * @param sig a buffer to store the signature on return. + * @param siglen on input: the size if the signature buffer + * on return: the size of the signature + * @param tbs the data to be signed. + * @param tbslen the size of the data to be signed + * @param digest_nid the OpenSSL nid of the message digest used to + * produce the data to be signed + * @param private the CCA library structure + * @param debug if true, debug messages are printed + * + * @returns a negative errno in case of an error, 0 if success. + */ +static int sk_cca_ecdsa_sign(const unsigned char *key_token, + size_t key_token_length, + unsigned char *sig, size_t *siglen, + const unsigned char *tbs, size_t tbslen, + int digest_nid, void *private, + bool debug) +{ + long return_code, reason_code, rule_array_count, exit_data_len = 0; + long token_length, hash_length, sign_bit_length, sign_length; + unsigned char rule_array[2 * CCA_KEYWORD_SIZE] = { 0 }; + const struct sk_ext_cca_lib *cca_lib = private; + unsigned char *exit_data = NULL; + struct cca_lib cca; + int rc; + + if (cca_lib == NULL || key_token == NULL || sig == NULL || + siglen == NULL || tbs == NULL) + return -EINVAL; + + sk_debug(debug, "tbslen: %lu siglen: %lu digest_nid: %d", + tbslen, *siglen, digest_nid); + + rc = sk_cca_get_library_functions(cca_lib, &cca); + if (rc != 0) { + sk_debug(debug, + "ERROR: Failed to get CCA functions from library"); + return rc; + } + + rule_array_count = 2; + memcpy(rule_array, "ECDSA ", CCA_KEYWORD_SIZE); + memcpy(rule_array + CCA_KEYWORD_SIZE, "HASH ", CCA_KEYWORD_SIZE); + + hash_length = tbslen; + token_length = key_token_length; + sign_length = *siglen; + + cca.dll_CSNDDSG(&return_code, &reason_code, + &exit_data_len, exit_data, + &rule_array_count, rule_array, + &token_length, (unsigned char *)key_token, + &hash_length, (unsigned char *)tbs, + &sign_length, &sign_bit_length, sig); + + if (return_code != 0) { + sk_debug(debug, "ERROR: CCA CSNDDSG (DIG. SIGNATURE CREATE, " + "ECDSA) failed: return_code: %ld reason_code: %ld", + return_code, reason_code); + return -EIO; + } + + rc = SK_UTIL_build_ecdsa_signature(sig, sign_length, sig, siglen); + if (rc != 0) { + sk_debug(debug, "ERROR: build_ecdsa_signature failed"); + return -EIO; + } + + sk_debug(debug, "siglen: %lu", *siglen); + + return 0; +} + +static const struct sk_funcs sk_cca_funcs = { + .rsa_sign = sk_cca_rsa_sign, + .rsa_pss_sign = sk_cca_rsa_pss_sign, + .rsa_decrypt = sk_cca_rsa_decrypt, + .rsa_decrypt_oaep = sk_cca_rsa_decrypt_oaep, + .ecdsa_sign = sk_cca_ecdsa_sign, +}; + +struct pub_key_cb_data { + const struct sk_ext_cca_lib *cca_lib; + const unsigned char *key_token; + size_t key_token_length; + bool rsa_pss; + EVP_PKEY *pkey; + bool debug; +}; + +/* + * Callback for generating an PKEY from a secure key + */ +static int sk_cca_get_secure_key_as_pkey_cb( + const struct sk_pub_key_info *pub_key, void *private) +{ + struct pub_key_cb_data *data = private; + int rc; + + if (pub_key == NULL || data == NULL) + return -EINVAL; + + rc = SK_OPENSSL_get_pkey(data->key_token, data->key_token_length, + pub_key, data->rsa_pss, &sk_cca_funcs, + data->cca_lib, &data->pkey, data->debug); + if (rc != 0) { + sk_debug(data->debug, + "ERROR: SK_OPENSSL_get_pkey failed"); + return rc; + } + + sk_debug(data->debug, "pkey: %p", data->pkey); + + return 0; +} + +/** + * Extracts the public key from a CCA internal RSA or EC key token, and returns + * it as OpenSSL PKEY. + * + * @param cca_lib the CCA library structure + * @param key_token the key token containing an CCA secure key + * @param key_token_length the size of the key token + * @param rsa_pss For RSA public keys: create a RSA-PSS type PKEY + * @param pkey On return: a PKEY containing the public key + * @param debug if true, debug messages are printed + * + * @returns a negative errno in case of an error, 0 if success. + */ +int SK_CCA_get_secure_key_as_pkey(const struct sk_ext_cca_lib *cca_lib, + const unsigned char *key_token, + size_t key_token_length, + bool rsa_pss, EVP_PKEY **pkey, bool debug) +{ + struct pub_key_cb_data data; + int rc; + + sk_debug(debug, "rsa_pss: %d", rsa_pss); + + data.cca_lib = cca_lib; + data.key_token = key_token; + data.key_token_length = key_token_length; + data.rsa_pss = rsa_pss; + data.pkey = NULL; + data.debug = debug; + + rc = SK_CCA_get_public_from_secure_key(key_token, key_token_length, + sk_cca_get_secure_key_as_pkey_cb, + &data, debug); + if (rc != 0) { + sk_debug(debug, + "ERROR: SK_CCA_get_public_from_secure_key failed"); + return rc; + } + + sk_debug(debug, "pkey: %p", data.pkey); + + *pkey = data.pkey; + return 0; +} + +/** + * Extracts the public key from a CCA internal RSA or EC key token, and calls + * the specified callback function with the public key information. + * + * @param key_token the key token containing an CCA secure key + * @param key_token_length the size of the key token + * @param pub_key_cb the callback function to call with the public key + * @param private a private pointer passed as is to the callback + * @param debug if true, debug messages are printed + * + * @returns a negative errno in case of an error, 0 if success. + */ +static int sk_cca_get_public_from_ec_key(const unsigned char *key_token, + size_t key_token_length, + sk_pub_key_func_t pub_key_cb, + void *private, bool debug) +{ + struct cca_ec_pub_key_section *ec_pub_section; + struct sk_pub_key_info pub_key = { 0 }; + const struct sk_ec_curve_info *curve; + const unsigned char *ec_pub_key; + unsigned char *buf = NULL; + int y_bit = 0; + int rc = 0; + + if (key_token == NULL || pub_key_cb == NULL) + return -EINVAL; + + pub_key.type = SK_KEY_TYPE_EC; + + ec_pub_section = (struct cca_ec_pub_key_section *) + sk_cca_get_pka_section(key_token, key_token_length, + CCA_SECTION_ID_EC_PUBL, debug); + if (ec_pub_section == NULL) + return -EINVAL; + if (ec_pub_section->section_header.section_version != 0x00) { + sk_debug(debug, "ERROR: invalid EC public key section version"); + return -EINVAL; + } + if (ec_pub_section->section_header.section_length < + sizeof(struct cca_ec_pub_key_section)) { + sk_debug(debug, "ERROR: invalid EC public key section length"); + return -EINVAL; + } + + ec_pub_key = ((unsigned char *)ec_pub_section) + + sizeof(struct cca_ec_pub_key_section); + + sk_debug(debug, "CCA curve_type: %u", ec_pub_section->curve_type); + + if (ec_pub_section->curve_type == CCA_PRIME_CURVE) + pub_key.ec.curve_nid = + SK_UTIL_ec_get_prime_curve_by_prime_bits( + ec_pub_section->prime_bits_length); + else if (ec_pub_section->curve_type == CCA_BRAINPOOL_CURVE) + pub_key.ec.curve_nid = + SK_UTIL_ec_get_brainpool_curve_by_prime_bits( + ec_pub_section->prime_bits_length); + else + pub_key.ec.curve_nid = 0; + + sk_debug(debug, "curve_nid: %d", pub_key.ec.curve_nid); + curve = SK_UTIL_ec_get_curve_info(pub_key.ec.curve_nid); + if (pub_key.ec.curve_nid == 0 || curve == NULL) { + sk_debug(debug, "ERROR: unsupported curve: %d", + pub_key.ec.curve_nid); + rc = -EIO; + goto out; + } + + pub_key.ec.prime_len = curve->prime_len; + sk_debug(debug, "prime_len: %lu", pub_key.ec.prime_len); + + if (ec_pub_section->pub_key_length != 2 * pub_key.ec.prime_len + 1) { + sk_debug(debug, "ERROR: invalid public key length"); + return -EINVAL; + } + + pub_key.ec.x = ec_pub_key + 1; + + /* First byte of public key contains indication of key compression */ + switch (ec_pub_key[0]) { + case POINT_CONVERSION_COMPRESSED: + case POINT_CONVERSION_COMPRESSED + POINT_CONVERSION_ODD_EVEN: + /* Compressed form, only x is available */ + y_bit = (ec_pub_key[0] & POINT_CONVERSION_ODD_EVEN) ? 1 : 0; + + buf = malloc(pub_key.ec.prime_len); + if (buf == NULL) { + sk_debug(debug, "ERROR: malloc failed"); + rc = -ENOMEM; + goto out; + } + + rc = SK_UTIL_ec_calculate_y_coordinate(pub_key.ec.curve_nid, + pub_key.ec.prime_len, + pub_key.ec.x, y_bit, + buf); + if (rc != 0) { + sk_debug(debug, "ERROR: ec_calculate_y_coordinate " + "failed"); + goto out; + } + + pub_key.ec.y = buf; + break; + + case POINT_CONVERSION_UNCOMPRESSED: + case POINT_CONVERSION_HYBRID: + case POINT_CONVERSION_HYBRID + POINT_CONVERSION_ODD_EVEN: + /* Uncompressed or hybrid, x and y are available */ + pub_key.ec.y = pub_key.ec.x + pub_key.ec.prime_len; + break; + + default: + sk_debug(debug, "ERROR: invalid compression indication"); + rc = -EIO; + goto out; + } + + rc = pub_key_cb(&pub_key, private); + if (rc != 0) { + sk_debug(debug, "ERROR: pub_key_cb failed"); + goto out; + } + +out: + if (buf != NULL) + free(buf); + + return rc; +} + +/** + * Extracts the public key from a CCA internal RSA or EC key token, and calls + * the specified callback function with the public key information. + * + * @param key_token the key token containing an CCA secure key + * @param key_token_length the size of the key token + * @param pub_key_cb the callback function to call with the public key + * @param private a private pointer passed as is to the callback + * @param debug if true, debug messages are printed + * + * @returns a negative errno in case of an error, 0 if success. + */ +static int sk_cca_get_public_from_rsa_key(const unsigned char *key_token, + size_t key_token_length, + sk_pub_key_func_t pub_key_cb, + void *private, + bool debug) +{ + const struct cca_rsa_crt_priv_key_section *rsa_priv_section; + const struct cca_rsa_pub_key_section *rsa_pub_section; + struct sk_pub_key_info pub_key = { 0 }; + int rc = 0; + + if (key_token == NULL || pub_key_cb == NULL) + return -EINVAL; + + pub_key.type = SK_KEY_TYPE_RSA; + + rsa_pub_section = (struct cca_rsa_pub_key_section *) + sk_cca_get_pka_section(key_token, key_token_length, + CCA_SECTION_ID_RSA_PUBL, debug); + if (rsa_pub_section == NULL) + return -EINVAL; + if (rsa_pub_section->section_header.section_version != 0x00) { + sk_debug(debug, + "ERROR: invalid RSA public key section version"); + return -EINVAL; + } + if (rsa_pub_section->section_header.section_length < + sizeof(struct cca_ec_pub_key_section)) { + sk_debug(debug, "ERROR: invalid RSA public key section length"); + return -EINVAL; + } + + pub_key.rsa.pub_exp = ((unsigned char *)rsa_pub_section) + + sizeof(struct cca_rsa_pub_key_section); + pub_key.rsa.pub_exp_len = rsa_pub_section->pub_exp_length; + pub_key.rsa.modulus = pub_key.rsa.pub_exp + + rsa_pub_section->pub_exp_length; + pub_key.rsa.modulus_len = rsa_pub_section->modulus_length; + + /* + * The public key section may have a modulus_length of zero, need to + * get the modulus from the private key section instead. + */ + if (rsa_pub_section->modulus_length == 0) { + rsa_priv_section = (struct cca_rsa_crt_priv_key_section *) + sk_cca_get_pka_section(key_token, key_token_length, + CCA_SECTION_ID_RSA_CRT_4096_EOPK_PRIV, debug); + + if (rsa_priv_section == NULL) + return -EINVAL; + if (rsa_priv_section->section_header.section_version != 0x00) { + sk_debug(debug, "ERROR: invalid RSA private key " + "section version"); + return -EINVAL; + } + if (rsa_priv_section->section_header.section_length < + sizeof(struct cca_rsa_crt_priv_key_section)) { + sk_debug(debug, "ERROR: invalid RSA private key " + "section length"); + return -EINVAL; + } + + pub_key.rsa.modulus = ((unsigned char *)rsa_priv_section) + + sizeof(struct cca_rsa_crt_priv_key_section); + pub_key.rsa.modulus_len = rsa_priv_section->modulus_length; + } + + rc = pub_key_cb(&pub_key, private); + if (rc != 0) { + sk_debug(debug, "ERROR: pub_key_cb failed"); + goto out; + } + +out: + return rc; + +} + +/** + * Extracts the public key from a CCA internal RSA or EC key token, and calls + * the specified callback function with the public key information. + * + * @param key_token the key token containing an CCA secure key + * @param key_token_length the size of the key token + * @param pub_key_cb the callback function to call with the public key + * @param private a private pointer passed as is to the callback + * @param debug if true, debug messages are printed + * + * @returns a negative errno in case of an error, 0 if success. + */ +int SK_CCA_get_public_from_secure_key(const unsigned char *key_token, + size_t key_token_length, + sk_pub_key_func_t pub_key_cb, + void *private, bool debug) +{ + int rc, pkey_type; + + rc = SK_CCA_get_key_type(key_token, key_token_length, &pkey_type); + if (rc != 0) { + sk_debug(debug, "ERROR: Failed to get the CCA key type: %s", + strerror(-rc)); + return rc; + } + + sk_debug(debug, "pkey_type: %d", pkey_type); + + switch (pkey_type) { + case EVP_PKEY_EC: + rc = sk_cca_get_public_from_ec_key(key_token, key_token_length, + pub_key_cb, private, debug); + if (rc != 0) { + sk_debug(debug, + "ERROR: sk_cca_get_public_from_ec_key failed"); + return rc; + } + break; + case EVP_PKEY_RSA: + case EVP_PKEY_RSA_PSS: + rc = sk_cca_get_public_from_rsa_key(key_token, key_token_length, + pub_key_cb, private, debug); + if (rc != 0) { + sk_debug(debug, + "ERROR: sk_cca_get_public_from_rsa_key failed"); + return rc; + } + break; + default: + sk_debug(debug, "ERROR: Invalid key type: %d", pkey_type); + return -EIO; + } + + return 0; +} + +/** + * Reenciphers a CCA secure key with a new CCA master key + * + * @param cca_lib the CCA library structure + * @param key_token the key token containing an CCA secure key + * @param key_token_length the size of the key token + * @param to_new if true, reencipher with the MK in then NEW register + * @param debug if true, debug messages are printed + * + * @returns a negative errno in case of an error, 0 if success. + */ +int SK_CCA_reencipher_key(const struct sk_ext_cca_lib *cca_lib, + unsigned char *key_token, size_t key_token_length, + bool to_new, bool debug) +{ + long return_code, reason_code, rule_array_count, exit_data_len = 0; + unsigned char rule_array[2 * CCA_KEYWORD_SIZE] = { 0 }; + unsigned char *exit_data = NULL; + struct cca_lib cca; + long token_length; + int rc, type; + + if (cca_lib == NULL || key_token == NULL) + return -EINVAL; + + sk_debug(debug, "to_new: %d", to_new); + + rc = sk_cca_get_library_functions(cca_lib, &cca); + if (rc != 0) { + sk_debug(debug, + "ERROR: Failed to get CCA functions from library"); + return rc; + } + + rc = SK_CCA_get_key_type(key_token, key_token_length, &type); + if (rc != 0) { + sk_debug(debug, + "ERROR: Failed to determine the key token type"); + return rc; + } + + rule_array_count = 2; + switch (type) { + case EVP_PKEY_EC: + memcpy(rule_array, "ECC ", CCA_KEYWORD_SIZE); + break; + case EVP_PKEY_RSA: + case EVP_PKEY_RSA_PSS: + memcpy(rule_array, "RSA ", CCA_KEYWORD_SIZE); + break; + default: + sk_debug(debug, "ERROR: Invalid key token type: %d", type); + return -EINVAL; + } + + if (to_new) + memcpy(rule_array + CCA_KEYWORD_SIZE, "RTNMK ", + CCA_KEYWORD_SIZE); + else + memcpy(rule_array + CCA_KEYWORD_SIZE, "RTCMK ", + CCA_KEYWORD_SIZE); + + token_length = key_token_length; + + cca.dll_CSNDKTC(&return_code, &reason_code, + &exit_data_len, exit_data, + &rule_array_count, rule_array, + &token_length, key_token); + + if (return_code != 0) { + sk_debug(debug, "ERROR: CCA CSNDKTC (PKA KEY TOKEN CHANGE) " + "failed: return_code: %ld reason_code: %ld", + return_code, reason_code); + + if (return_code == 12 && reason_code == 764) { + sk_debug(debug, + "ERROR: The master keys are not loaded"); + return -ENODEV; + } + + return -EIO; + } + + return 0; +} + diff --git a/libseckey/sk_ep11.c b/libseckey/sk_ep11.c new file mode 100644 index 00000000..b8676267 --- /dev/null +++ b/libseckey/sk_ep11.c @@ -0,0 +1,1555 @@ +/* + * libseckey - Secure key library + * + * Copyright IBM Corp. 2021 + * + * s390-tools is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "lib/zt_common.h" + +#include "libseckey/sk_ep11.h" +#include "libseckey/sk_openssl.h" +#include "libseckey/sk_utilities.h" + +/* EP11 library definitions */ + +#define XCP_SERIALNR_CHARS 8 +#define XCP_ADMCTR_BYTES ((size_t) (128/8)) +#define XCP_KEYCSUM_BYTES (256/8) + +#define XCP_ADM_REENCRYPT 25 /* transform blobs to next WK */ + + +#define CKR_VENDOR_DEFINED 0x80000000 +#define CKR_IBM_WKID_MISMATCH (CKR_VENDOR_DEFINED + 0x10001) + +typedef struct XCPadmresp { + uint32_t fn; + uint32_t domain; + uint32_t domainInst; + + /* module ID || module instance */ + unsigned char module[XCP_SERIALNR_CHARS + XCP_SERIALNR_CHARS]; + unsigned char modNr[XCP_SERIALNR_CHARS]; + unsigned char modInst[XCP_SERIALNR_CHARS]; + + unsigned char tctr[XCP_ADMCTR_BYTES]; /* transaction counter */ + + CK_RV rv; + uint32_t reason; + + const unsigned char *payload; + size_t pllen; +} *XCPadmresp_t; + +typedef struct CK_IBM_DOMAIN_INFO { + CK_ULONG domain; + CK_BYTE wk[XCP_KEYCSUM_BYTES]; + CK_BYTE nextwk[XCP_KEYCSUM_BYTES]; + CK_ULONG flags; + CK_BYTE mode[8]; +} CK_IBM_DOMAIN_INFO; + +#define CK_IBM_DOM_COMMITTED_NWK 8 + +#define CK_IBM_XCPQ_DOMAIN 3 + +typedef CK_RV (*m_GenerateKeyPair_t)(CK_MECHANISM_PTR mech, + CK_ATTRIBUTE_PTR public, + CK_ULONG pubattrs, + CK_ATTRIBUTE_PTR private, + CK_ULONG prvattrs, + const unsigned char *pin, size_t pinlen, + unsigned char *key, size_t *klen, + unsigned char *pubkey, size_t *pklen, + target_t target); + +typedef CK_RV (*m_SignSingle_t)(const unsigned char *key, size_t klen, + CK_MECHANISM_PTR pmech, + CK_BYTE_PTR data, CK_ULONG dlen, + CK_BYTE_PTR sig, CK_ULONG_PTR slen, + target_t target); + +typedef CK_RV (*m_DecryptSingle_t)(const unsigned char *key, size_t klen, + CK_MECHANISM_PTR mech, + CK_BYTE_PTR cipher, CK_ULONG clen, + CK_BYTE_PTR plain, CK_ULONG_PTR plen, + target_t target); + +typedef CK_RV (*m_get_xcp_info_t)(CK_VOID_PTR pinfo, CK_ULONG_PTR infbytes, + unsigned int query, unsigned int subquery, + target_t target); +typedef unsigned long (*m_admin_t)(unsigned char *resp1, size_t *r1len, + unsigned char *resp2, size_t *r2len, + const unsigned char *cmd, size_t clen, + const unsigned char *sigs, size_t slen, + target_t target); +typedef long (*xcpa_cmdblock_t)(unsigned char *blk, size_t blen, + unsigned int fn, const struct XCPadmresp *minf, + const unsigned char *tctr, + const unsigned char *payload, size_t plen); +typedef long (*xcpa_internal_rv_t)(const unsigned char *rsp, size_t rlen, + struct XCPadmresp *rspblk, CK_RV *rv); + +struct ep11_lib { + m_GenerateKeyPair_t dll_m_GenerateKeyPair; + m_SignSingle_t dll_m_SignSingle; + m_DecryptSingle_t dll_m_DecryptSingle; + m_get_xcp_info_t dll_m_get_xcp_info; + m_admin_t dll_m_admin; + xcpa_cmdblock_t dll_xcpa_cmdblock; + xcpa_internal_rv_t dll_xcpa_internal_rv; +}; + +#define TOKTYPE_NON_CCA 0 + +#define PKEY_TYPE_EP11_ECC 6 +#define PKEY_TYPE_EP11_RSA 7 + +#define PKEY_TYPE_EP11_HVER 0 + +#define PKEY_TYPE_EP11_FLAG_X9_31 0x01 + +/* + * EP11 secure key blobs of type PKEY_TYPE_EP11_ECC and PKEY_TYPE_EP11_RSA + * are EP11 blobs prepended by this header (aligned with definition in kernel + * header arch/s390/include/uapi/asm/pkey.h): + */ +struct ep11kblob_header { + uint8_t type; /* always 0x00 (TOKTYPE_NON_CCA) */ + uint8_t hver; /* header version, currently needs to be 0x00 */ + uint16_t len; /* total length in bytes (including this header) */ + uint8_t version; /* PKEY_TYPE_EP11_ECC or PKEY_TYPE_EP11_RSA */ + uint8_t flags; /* Flags, see PKEY_TYPE_EP11_FLAG */ + uint16_t bitlen; /* clear key bit len, 0 for unknown */ + uint8_t res0[8]; +} __packed; +/* Followed by len - sizeof(struct ep11kblob_header) bytes EP11 key blob */ +/* Followed by secure key size - len bytes SPKI (public key) */ + +#define POINT_CONVERSION_ODD_EVEN 0x01 + +/** + * Gets the Ep11 library function entry points from the library handle + */ +static int sk_ep11_get_library_functions(const struct sk_ext_ep11_lib *ep11_lib, + struct ep11_lib *ep11) +{ + if (ep11_lib == NULL || ep11 == NULL) + return -EINVAL; + + ep11->dll_m_GenerateKeyPair = (m_GenerateKeyPair_t) + dlsym(ep11_lib->ep11_lib, "m_GenerateKeyPair"); + ep11->dll_m_SignSingle = (m_SignSingle_t) + dlsym(ep11_lib->ep11_lib, "m_SignSingle"); + ep11->dll_m_DecryptSingle = (m_DecryptSingle_t) + dlsym(ep11_lib->ep11_lib, "m_DecryptSingle"); + ep11->dll_m_get_xcp_info = (m_get_xcp_info_t) + dlsym(ep11_lib->ep11_lib, "m_get_xcp_info"); + ep11->dll_m_admin = (m_admin_t) + dlsym(ep11_lib->ep11_lib, "m_admin"); + ep11->dll_xcpa_cmdblock = (xcpa_cmdblock_t) + dlsym(ep11_lib->ep11_lib, "xcpa_cmdblock"); + ep11->dll_xcpa_internal_rv = (xcpa_internal_rv_t) + dlsym(ep11_lib->ep11_lib, "xcpa_internal_rv"); + + if (ep11->dll_m_GenerateKeyPair == NULL || + ep11->dll_m_SignSingle == NULL || + ep11->dll_m_DecryptSingle == NULL || + ep11->dll_m_get_xcp_info == NULL || + ep11->dll_m_admin == NULL || + ep11->dll_xcpa_cmdblock == NULL || + ep11->dll_xcpa_internal_rv == NULL) + return -EIO; + + return 0; +} + +/** + * Generates an EP11 asymmetric key using the specified key type, mechanism, and + * templates. + */ +static int sk_ep11_generate_key_pair(const struct sk_ext_ep11_lib *ep11_lib, + CK_MECHANISM *mech, CK_ATTRIBUTE *pub_tmpl, + CK_ULONG pub_tmpl_num, + CK_ATTRIBUTE *priv_tmpl, + CK_ULONG priv_tmpl_num, + unsigned char *key_token, + size_t *key_token_length, bool debug) +{ + unsigned char spki[EP11_MAX_KEY_TOKEN_SIZE] = { 0 }; + struct ep11kblob_header *hdr; + size_t blob_size, spki_size; + struct ep11_lib ep11; + unsigned char *blob; + size_t tok_len; + CK_RV rv; + int rc; + + if (ep11_lib == NULL || key_token == NULL || key_token_length == NULL) + return -EINVAL; + + if (key_token == NULL) { + *key_token_length = EP11_MAX_KEY_TOKEN_SIZE; + return 0; + } + if (*key_token_length <= sizeof(struct ep11kblob_header)) { + sk_debug(debug, "ERROR: key token too short"); + return -EINVAL; + } + + hdr = (struct ep11kblob_header *)key_token; + memset(hdr, 0, sizeof(struct ep11kblob_header)); + hdr->type = TOKTYPE_NON_CCA; + hdr->hver = PKEY_TYPE_EP11_HVER; + + blob_size = *key_token_length - sizeof(struct ep11kblob_header); + spki_size = sizeof(spki); + + blob = key_token + sizeof(struct ep11kblob_header); + + rc = sk_ep11_get_library_functions(ep11_lib, &ep11); + if (rc != 0) { + sk_debug(debug, + "ERROR: Failed to get EP11 functions from library"); + return rc; + } + + sk_debug(debug, "mech: 0x%x", mech->mechanism); + + rv = ep11.dll_m_GenerateKeyPair(mech, pub_tmpl, pub_tmpl_num, + priv_tmpl, priv_tmpl_num, + NULL, 0, blob, &blob_size, + spki, &spki_size, + ep11_lib->target); + if (rv != CKR_OK) { + sk_debug(debug, "ERROR: m_GenerateKeyPair failed: rc: 0x%x", + rv); + return -EIO; + } + + sk_debug(debug, "blob_size: %lu spki_len: %lu", blob_size, spki_size); + + hdr->len = sizeof(struct ep11kblob_header) + blob_size; + + tok_len = sizeof(struct ep11kblob_header) + blob_size + spki_size; + sk_debug(debug, "tok_len: %lu", tok_len); + + if (tok_len > *key_token_length) { + sk_debug(debug, "ERROR: key token too short"); + return -EINVAL; + } + + memcpy(blob + blob_size, spki, spki_size); + + *key_token_length = tok_len; + + return 0; +} + +/** + * Generates an EP11 EC key of the specified curve type and length using the + * Ep11 host library. + * + * @param ep11_lib the Ep11 library structure + * @param curve_nid the nid specifying the curve. + * @param key_token a buffer to store the generated key token + * @param key_token_length On entry: the size of the buffer + * On return: the size of the key token + * @param debug if true, debug messages are printed + * + * @returns a negative errno in case of an error, 0 if success. + */ +int SK_EP11_generate_ec_key_pair(const struct sk_ext_ep11_lib *ep11_lib, + int curve_nid, unsigned char *key_token, + size_t *key_token_length, bool debug) +{ + CK_MECHANISM mech = { .mechanism = CKM_EC_KEY_PAIR_GEN, + .pParameter = NULL, .ulParameterLen = 0 }; + struct ep11kblob_header *hdr; + CK_BBOOL _false = false; + CK_BBOOL _true = true; + CK_ATTRIBUTE pub_tmpl[] = { + { CKA_EC_PARAMS, NULL, 0 }, + { CKA_VERIFY, &_true, sizeof(_true) }, + }; + CK_ULONG pub_tmpl_num = sizeof(pub_tmpl) / sizeof(CK_ATTRIBUTE); + CK_ATTRIBUTE priv_tmpl[] = { + { CKA_SENSITIVE, &_true, sizeof(_true) }, + { CKA_SIGN, &_true, sizeof(_true) }, + { CKA_DERIVE, &_false, sizeof(_false) }, + }; + CK_ULONG priv_tmpl_num = sizeof(priv_tmpl) / sizeof(CK_ATTRIBUTE); + const struct sk_ec_curve_info *curve; + int rc; + + if (ep11_lib == NULL || key_token == NULL || key_token_length == NULL) + return -EINVAL; + + sk_debug(debug, "curve_nid: %d", curve_nid); + + curve = SK_UTIL_ec_get_curve_info(curve_nid); + if (curve == NULL) { + sk_debug(debug, "ERROR: Curve %d not supported", curve_nid); + return -EIO; + } + + pub_tmpl[0].pValue = (void *)curve->der; + pub_tmpl[0].ulValueLen = curve->der_size; + + rc = sk_ep11_generate_key_pair(ep11_lib, &mech, pub_tmpl, pub_tmpl_num, + priv_tmpl, priv_tmpl_num, key_token, + key_token_length, debug); + if (rc != 0) { + sk_debug(debug, "ERROR: sk_ep11_generate_key_pair failed"); + return -rc; + } + + hdr = (struct ep11kblob_header *)key_token; + hdr->version = PKEY_TYPE_EP11_ECC; + hdr->bitlen = curve->prime_bits; + + return 0; +} + +/** + * Generates an EP11 RSA key of the specified key size and optionally the + * specified public exponent using the EP11 host library. + * + * @param ep11_lib the EP11 library structure + * @param modulus_bits the size of the key in bits (512, 1024, 2048, 4096) + * @param pub_exp the public exponent or zero. Possible values are: + * 3, 5, 17, 257, or 65537. Specify zero to choose the + * exponent by random. + * @param x9_31 if true, generate a X9.31 RSA key + * @param key_token a buffer to store the generated key token + * @param key_token_length On entry: the size of the buffer + * On return: the size of the key token + * @param debug if true, debug messages are printed + * + * @returns a negative errno in case of an error, 0 if success. + */ +int SK_EP11_generate_rsa_key_pair(const struct sk_ext_ep11_lib *ep11_lib, + size_t modulus_bits, unsigned int pub_exp, + bool x9_31, unsigned char *key_token, + size_t *key_token_length, bool debug) +{ + CK_MECHANISM mech = { .mechanism = CKM_RSA_PKCS_KEY_PAIR_GEN, + .pParameter = NULL, .ulParameterLen = 0 }; + CK_ULONG mod_bits = modulus_bits; + struct ep11kblob_header *hdr; + CK_BBOOL _true = true; + CK_ATTRIBUTE pub_tmpl[] = { + { CKA_MODULUS_BITS, &mod_bits, sizeof(mod_bits) }, + { CKA_VERIFY, &_true, sizeof(_true) }, + { CKA_ENCRYPT, &_true, sizeof(_true) }, + { CKA_WRAP, &_true, sizeof(_true) }, + { CKA_PUBLIC_EXPONENT, &pub_exp, sizeof(pub_exp) }, + }; + CK_ULONG pub_tmpl_num = sizeof(pub_tmpl) / sizeof(CK_ATTRIBUTE); + CK_ATTRIBUTE priv_tmpl[] = { + { CKA_SENSITIVE, &_true, sizeof(_true) }, + { CKA_SIGN, &_true, sizeof(_true) }, + { CKA_DECRYPT, &_true, sizeof(_true) }, + { CKA_UNWRAP, &_true, sizeof(_true) }, + }; + CK_ULONG priv_tmpl_num = sizeof(priv_tmpl) / sizeof(CK_ATTRIBUTE); + int rc; + + if (ep11_lib == NULL || key_token == NULL || key_token_length == NULL) + return -EINVAL; + + sk_debug(debug, "modulus_bits: %lu pub_exp: %u x9_31: %d", + modulus_bits, pub_exp, x9_31); + + if (pub_exp == 0) + pub_tmpl_num--; + + if (x9_31) + mech.mechanism = CKM_RSA_X9_31_KEY_PAIR_GEN; + + rc = sk_ep11_generate_key_pair(ep11_lib, &mech, pub_tmpl, pub_tmpl_num, + priv_tmpl, priv_tmpl_num, key_token, + key_token_length, debug); + if (rc != 0) { + sk_debug(debug, "ERROR: sk_ep11_generate_key_pair failed"); + return -rc; + } + + hdr = (struct ep11kblob_header *)key_token; + hdr->version = PKEY_TYPE_EP11_RSA; + if (x9_31) + hdr->flags |= PKEY_TYPE_EP11_FLAG_X9_31; + hdr->bitlen = modulus_bits; + + return 0; +} + +/* + * Parses a DER encoded tag, returns the tag id, and sets the tag length and + * value length. + */ +static unsigned char sk_ep11_parse_der_tag(const unsigned char *data, + size_t data_len, size_t *tag_len, + const unsigned char **value, + size_t *value_len) +{ + size_t num, i; + + if (data == NULL || data_len < 2) + return 0; + + if (data[1] & 0x80) { + num = data[1] & 0x7f; + if (num > sizeof(size_t)) + return 0; + *value_len = data[2]; + for (i = 1; i < num; i++) { + *value_len <<= 8; + *value_len |= data[2 + i]; + } + *value = &data[2 + num]; + *tag_len = 2 + num + *value_len; + } else { + *value_len = data[1] & 0x7f; + *value = &data[2]; + *tag_len = 2 + *value_len; + } + + if (*tag_len > data_len) + return 0; + + return data[0]; +} + +/* + * Extract data from an SPKI + * SubjectPublicKeyInfo ::= SEQUENCE { + * algorithm AlgorithmIdentifier, + * subjectPublicKey BIT STRING + * } + * + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL + * } + */ +static int sk_ep11_parse_spki(const unsigned char *spki, size_t spki_len, + enum sk_key_type *keytype, + const unsigned char **params, + size_t *params_len, const unsigned char **pub_key, + size_t *pub_key_len) +{ + size_t tag_len, seq1_len, seq2_tag_len, seq2_len, oid_len; + const unsigned char *seq1, *seq2, *oid; + ASN1_OBJECT *obj = NULL; + unsigned char tag; + int algo_nid; + + /* Outer sequence */ + tag = sk_ep11_parse_der_tag(spki, spki_len, &tag_len, &seq1, &seq1_len); + if (tag != 0x30) /* SEQUENCE */ + return -EINVAL; + + /* Inner sequence */ + tag = sk_ep11_parse_der_tag(seq1, seq1_len, &seq2_tag_len, &seq2, + &seq2_len); + if (tag != 0x30) /* SEQUENCE */ + return -EINVAL; + + /* Algorithm OID */ + tag = sk_ep11_parse_der_tag(seq2, seq2_len, &tag_len, &oid, &oid_len); + if (tag != 0x06) /* OID */ + return -EINVAL; + + oid = seq2; + if (d2i_ASN1_OBJECT(&obj, &oid, tag_len) == NULL) + return -EIO; + algo_nid = OBJ_obj2nid(obj); + ASN1_OBJECT_free(obj); + + switch (algo_nid) { + case NID_rsaEncryption: + *keytype = SK_KEY_TYPE_RSA; + break; + case NID_X9_62_id_ecPublicKey: + *keytype = SK_KEY_TYPE_EC; + break; + default: + return -EINVAL; + } + + /* Parameters */ + *params = seq2 + tag_len; + *params_len = seq2_len - tag_len; + + /* Public key */ + tag = sk_ep11_parse_der_tag(seq1 + seq2_tag_len, + seq1_len - seq2_tag_len, &tag_len, + pub_key, pub_key_len); + if (tag != 0x03) /* BITSTRING */ + return -EINVAL; + + /* skip unsused-bits byte */ + (*pub_key)++; + (*pub_key_len)--; + + return 0; +} + +static bool sk_ep11_valid_ep11_blob(const unsigned char *key_token, + size_t key_token_length) +{ + const struct ep11kblob_header *hdr = + (struct ep11kblob_header *)key_token; + + if (key_token == NULL) + return false; + + if (key_token_length <= sizeof(struct ep11kblob_header)) + return false; + + if (hdr->len > key_token_length || + hdr->type != TOKTYPE_NON_CCA || + hdr->hver != PKEY_TYPE_EP11_HVER || + (hdr->version != PKEY_TYPE_EP11_ECC && + hdr->version != PKEY_TYPE_EP11_RSA)) + return false; + + return true; +} + +/** + * Queries the PKEY type of the key token. + * + * @param key_token the key token containing an Ep11 EC key + * @param key_token_length the size of the key token + * @param pkey_type On return: the PKEY type of the key token + * + * @returns a negative errno in case of an error, 0 if success. + */ +int SK_EP11_get_key_type(const unsigned char *key_token, + size_t key_token_length, + int *pkey_type) +{ + const struct ep11kblob_header *hdr = + (struct ep11kblob_header *)key_token; + size_t params_len, pub_key_len, spki_size; + const unsigned char *params, *pub_key; + enum sk_key_type type; + int rc; + + if (key_token == NULL || pkey_type == NULL) + return -EINVAL; + + if (!sk_ep11_valid_ep11_blob(key_token, key_token_length)) + return -EINVAL; + + spki_size = key_token_length - hdr->len; + if (spki_size <= 0) + return -EINVAL; + + rc = sk_ep11_parse_spki(key_token + hdr->len, spki_size, + &type, ¶ms, ¶ms_len, &pub_key, + &pub_key_len); + if (rc != 0) + return rc; + + switch (type) { + case SK_KEY_TYPE_RSA: + *pkey_type = EVP_PKEY_RSA; + break; + case SK_KEY_TYPE_EC: + *pkey_type = EVP_PKEY_EC; + break; + default: + *pkey_type = -1; + return -EINVAL; + } + + return 0; +} + +/** + * Returns the EP11 private key blob of the key token. + * + * @param key_token the key token containing an Ep11 EC key + * @param key_token_length the size of the key token + * + * @returns the address of the EP11 key blob, or NULL in case of an error + */ +const unsigned char *SK_EP11_get_key_blob(const unsigned char *key_token, + size_t key_token_length) +{ + if (!sk_ep11_valid_ep11_blob(key_token, key_token_length)) + return NULL; + + return key_token + sizeof(struct ep11kblob_header); +} + +/** + * Returns the EP11 private key blob size of the key token. + * + * @param key_token the key token containing an Ep11 EC key + * @param key_token_length the size of the key token + * + * @returns the size of the EP11 key blob, or 0 in case of an error + */ +size_t SK_EP11_get_key_blob_size(const unsigned char *key_token, + size_t key_token_length) +{ + const struct ep11kblob_header *hdr = + (struct ep11kblob_header *)key_token; + + if (!sk_ep11_valid_ep11_blob(key_token, key_token_length)) + return 0; + + return hdr->len - sizeof(struct ep11kblob_header); +} + +/** + * Sign data using RSA. + * + * @param key_token the RSA key token + * @param key_token_length the length of the key token + * @param sig a buffer to store the signature on return. + * @param siglen on input: the size if the signature buffer + * on return: the size of the signature + * @param tbs the data to be signed. + * @param tbslen the size of the data to be signed + * @param padding_type the OpenSSL padding type (RSA_X931_PADDING or + * RSA_PKCS1_PADDING) + * @param digest_nid the OpenSSL nid of the message digest used to + * produce the data to be signed + * @param private the CCA library structure + * @param debug if true, debug messages are printed + * + * @returns a negative errno in case of an error, 0 if success. + */ +static int sk_ep11_rsa_sign(const unsigned char *key_token, + size_t key_token_length, + unsigned char *sig, size_t *siglen, + const unsigned char *tbs, size_t tbslen, + int padding_type, int md_nid, + void *private, bool debug) +{ + const struct ep11kblob_header *hdr = + (struct ep11kblob_header *)key_token; + const unsigned char *blob = key_token + + sizeof(struct ep11kblob_header); + CK_MECHANISM mech = { .mechanism = CKM_RSA_PKCS, + .pParameter = NULL, .ulParameterLen = 0 }; + const struct sk_ext_ep11_lib *ep11_lib = private; + const struct sk_digest_info *digest; + unsigned char *msg = NULL; + struct ep11_lib ep11; + size_t msg_len; + CK_RV rv; + int rc; + + if (ep11_lib == NULL || key_token == NULL || sig == NULL || + siglen == NULL || tbs == NULL) + return -EINVAL; + + if (!sk_ep11_valid_ep11_blob(key_token, key_token_length)) + return -EINVAL; + + sk_debug(debug, "tbslen: %lu siglen: %lu padding_type: %d md_nid: %d", + tbslen, *siglen, padding_type, md_nid); + + rc = sk_ep11_get_library_functions(ep11_lib, &ep11); + if (rc != 0) { + sk_debug(debug, + "ERROR: Failed to get EP11 functions from library"); + return rc; + } + + digest = SK_UTIL_get_digest_info(md_nid); + if (digest == NULL) { + sk_debug(debug, "ERROR: Invalid digest nid: %d", md_nid); + return -EINVAL; + } + + if (tbslen != digest->digest_size) { + sk_debug(debug, "ERROR: Invalid data length: %lu", tbslen); + return -EINVAL; + } + + switch (padding_type) { + case RSA_PKCS1_PADDING: + msg_len = digest->der_size + tbslen; + msg = (unsigned char *)malloc(msg_len); + if (msg == NULL) { + sk_debug(debug, "ERROR: malloc failed"); + return -ENOMEM; + } + + memcpy(msg, digest->der, digest->der_size); + memcpy(msg + digest->der_size, tbs, tbslen); + tbs = msg; + tbslen = msg_len; + break; + + case RSA_X931_PADDING: + mech.mechanism = CKM_RSA_X9_31; + + if ((hdr->flags && PKEY_TYPE_EP11_FLAG_X9_31) == 0) { + sk_debug(debug, "ERROR: no RSA X9.31 key"); + return -EINVAL; + } + + msg_len = tbslen + 2; + msg = (unsigned char *)malloc(msg_len); + if (msg == NULL) { + sk_debug(debug, "ERROR: malloc failed"); + return -ENOMEM; + } + + memcpy(msg, tbs, tbslen); + msg[tbslen] = digest->x9_31_md; + msg[tbslen + 1] = 0xcc; + + tbs = msg; + tbslen = msg_len; + break; + + default: + sk_debug(debug, "ERROR: Invalid padding type: %d", + padding_type); + return -EINVAL; + } + + rv = ep11.dll_m_SignSingle(blob, hdr->len - sizeof(*hdr), &mech, + (CK_BYTE_PTR)tbs, tbslen, + sig, siglen, ep11_lib->target); + if (rv != CKR_OK) { + sk_debug(debug, "ERROR: m_SignSingle failed: rc: 0x%x", rv); + rc = -EIO; + goto out; + } + + rc = 0; + + sk_debug(debug, "siglen: %lu", *siglen); + +out: + if (msg != NULL) + free(msg); + + return rc; +} + +/** + * Sign data using RSA-PSS. + * + * @param key_token the RSA key token + * @param key_token_length the length of the key token + * @param sig a buffer to store the signature on return. + * @param siglen on input: the size if the signature buffer + * on return: the size of the signature + * @param tbs the data to be signed. + * @param tbslen the size of the data to be signed + * @param digest_nid the OpenSSL nid of the message digest used to + * produce the data to be signed + * @param mgf_digest_nid the OpenSSL nid of the mask generation function for + * PSS padding + * @param saltlen the length of the salt for PSS + * @param private the CCA library structure + * @param debug if true, debug messages are printed + * + * @returns a negative errno in case of an error, 0 if success. + */ +static int sk_ep11_rsa_pss_sign(const unsigned char *key_token, + size_t key_token_length, + unsigned char *sig, size_t *siglen, + const unsigned char *tbs, size_t tbslen, + int digest_nid, int mgf_digest_nid, int saltlen, + void *private, bool debug) +{ + const struct ep11kblob_header *hdr = + (struct ep11kblob_header *)key_token; + const unsigned char *blob = key_token + + sizeof(struct ep11kblob_header); + + CK_RSA_PKCS_PSS_PARAMS pss_params; + CK_MECHANISM mech = { .mechanism = CKM_RSA_PKCS_PSS, + .pParameter = &pss_params, + .ulParameterLen = sizeof(pss_params) }; + const struct sk_ext_ep11_lib *ep11_lib = private; + const struct sk_digest_info *digest; + struct ep11_lib ep11; + CK_RV rv; + int rc; + + if (ep11_lib == NULL || key_token == NULL || sig == NULL || + siglen == NULL || tbs == NULL) + return -EINVAL; + + if (!sk_ep11_valid_ep11_blob(key_token, key_token_length)) + return -EINVAL; + + sk_debug(debug, "tbslen: %lu siglen: %lu digest_nid: %d " + "mgf_digest_nid: %d saltlen: %d", + tbslen, *siglen, digest_nid, mgf_digest_nid, saltlen); + + rc = sk_ep11_get_library_functions(ep11_lib, &ep11); + if (rc != 0) { + sk_debug(debug, + "ERROR: Failed to get EP11 functions from library"); + return rc; + } + + if (mgf_digest_nid != digest_nid) { + sk_debug(debug, "ERROR: Mgf nid must be the same as the " + "message digest nid"); + return -EINVAL; + } + + digest = SK_UTIL_get_digest_info(digest_nid); + if (digest == NULL || digest->pkcs11_mech == 0 || + digest->pkcs11_mgf == 0) { + sk_debug(debug, "ERROR: Invalid digest nid: %d", digest_nid); + return -EINVAL; + } + + if (tbslen != digest->digest_size) { + sk_debug(debug, "ERROR: Invalid data length: %lu", tbslen); + return -EINVAL; + } + + if (saltlen != (int)digest->digest_size) { + sk_debug(debug, "ERROR: saltlen must be size of digest"); + return -EINVAL; + } + + pss_params.hashAlg = digest->pkcs11_mech; + pss_params.mgf = digest->pkcs11_mgf; + pss_params.sLen = saltlen; + + sk_debug(debug, "pss_params.hashAlg: 0x%x", pss_params.hashAlg); + sk_debug(debug, "pss_params.mgf: 0x%x", pss_params.mgf); + + rv = ep11.dll_m_SignSingle(blob, hdr->len - sizeof(*hdr), &mech, + (CK_BYTE_PTR)tbs, tbslen, + sig, siglen, ep11_lib->target); + if (rv != CKR_OK) { + sk_debug(debug, "ERROR: m_SignSingle failed: rc: 0x%x", rv); + return -EIO; + } + + sk_debug(debug, "siglen: %lu", *siglen); + + return 0; +} + +/** + * Decrypt data using RSA. + * + * @param key_token the RSA key token + * @param key_token_length the length of the key token + * @param to a buffer to store the decrypted data on return. + * @param tolen on input: the size if the to buffer + * on return: the size of the decrypted data + * @param from the data to be decrypted. + * @param fromlen the size of the data to be decrypted + * @param padding_type the OpenSSL padding type + * @param private the CCA library structure + * @param debug if true, debug messages are printed + * + * @returns a negative errno in case of an error, 0 if success. + */ +static int sk_ep11_rsa_decrypt(const unsigned char *key_token, + size_t key_token_length, + unsigned char *to, size_t *tolen, + const unsigned char *from, size_t fromlen, + int padding_type, void *private, bool debug) +{ + const struct ep11kblob_header *hdr = + (struct ep11kblob_header *)key_token; + const unsigned char *blob = key_token + + sizeof(struct ep11kblob_header); + CK_MECHANISM mech = { .mechanism = CKM_RSA_PKCS, + .pParameter = NULL, .ulParameterLen = 0 }; + const struct sk_ext_ep11_lib *ep11_lib = private; + struct ep11_lib ep11; + CK_RV rv; + int rc; + + if (ep11_lib == NULL || key_token == NULL || to == NULL || + tolen == NULL || from == NULL) + return -EINVAL; + + if (!sk_ep11_valid_ep11_blob(key_token, key_token_length)) + return -EINVAL; + + sk_debug(debug, "fromlen: %lu tolen: %lu padding_type: %d", + fromlen, *tolen, padding_type); + + rc = sk_ep11_get_library_functions(ep11_lib, &ep11); + if (rc != 0) { + sk_debug(debug, + "ERROR: Failed to get EP11 functions from library"); + return rc; + } + + switch (padding_type) { + case RSA_PKCS1_PADDING: + break; + + default: + sk_debug(debug, "ERROR: Invalid padding type: %d", + padding_type); + return -EINVAL; + } + + rv = ep11.dll_m_DecryptSingle(blob, hdr->len - sizeof(*hdr), &mech, + (CK_BYTE_PTR)from, fromlen, + to, tolen, ep11_lib->target); + if (rv != CKR_OK) { + sk_debug(debug, "ERROR: m_DecryptSingle failed: rc: 0x%x", rv); + return -EIO; + } + + sk_debug(debug, "tolen: %lu", *tolen); + + return 0; +} + +/** + * Decrypt data using RSA OAEP. + * + * @param key_token the RSA key token + * @param key_token_length the length of the key token + * @param to a buffer to store the decrypted data on return. + * @param tolen on input: the size if the to buffer + * on return: the size of the decrypted data + * @param from the data to be decrypted. + * @param fromlen the size of the data to be decrypted + * @param oaep_md_nid the OpenSSL nid of the OAEP hashing algorithm + * @param mgfmd_nid the OpenSSL nid of the mask generation function + * @param label the label for OAEP + * @param label_len the length of the label for OAEP + * @param private the CCA library structure + * @param debug if true, debug messages are printed + * + * @returns a negative errno in case of an error, 0 if success. + */ +static int sk_ep11_rsa_decrypt_oaep(const unsigned char *key_token, + size_t key_token_length, + unsigned char *to, size_t *tolen, + const unsigned char *from, size_t fromlen, + int oaep_md_nid, int mgfmd_nid, + unsigned char *label, + int label_len, + void *private, bool debug) +{ + const struct ep11kblob_header *hdr = + (struct ep11kblob_header *)key_token; + const unsigned char *blob = key_token + + sizeof(struct ep11kblob_header); + CK_RSA_PKCS_OAEP_PARAMS oaep_params; + CK_MECHANISM mech = { .mechanism = CKM_RSA_PKCS_OAEP, + .pParameter = &oaep_params, + .ulParameterLen = sizeof(oaep_params) }; + const struct sk_ext_ep11_lib *ep11_lib = private; + const struct sk_digest_info *digest; + struct ep11_lib ep11; + CK_RV rv; + int rc; + + if (ep11_lib == NULL || key_token == NULL || to == NULL || + tolen == NULL || from == NULL) + return -EINVAL; + + if (!sk_ep11_valid_ep11_blob(key_token, key_token_length)) + return -EINVAL; + + sk_debug(debug, "fromlen: %lu tolen: %lu oaep_md_nid: %d mgfmd_nid: %d", + fromlen, *tolen, oaep_md_nid, mgfmd_nid); + + rc = sk_ep11_get_library_functions(ep11_lib, &ep11); + if (rc != 0) { + sk_debug(debug, + "ERROR: Failed to get EP11 functions from library"); + return rc; + } + + if (mgfmd_nid != oaep_md_nid) { + sk_debug(debug, "ERROR: Mgf nid must be the same as the " + "OAEP digest nid"); + return -EINVAL; + } + + digest = SK_UTIL_get_digest_info(oaep_md_nid); + if (digest == NULL || digest->pkcs11_mech == 0 || + digest->pkcs11_mgf == 0) { + sk_debug(debug, "ERROR: Invalid digest nid: %d", oaep_md_nid); + return -EINVAL; + } + + oaep_params.hashAlg = digest->pkcs11_mech; + oaep_params.mgf = digest->pkcs11_mgf; + oaep_params.source = label_len > 0 ? CKZ_DATA_SPECIFIED : 0; + oaep_params.pSourceData = label_len > 0 ? label : NULL; + oaep_params.ulSourceDataLen = label_len; + + sk_debug(debug, "oaep_params.hashAlg: 0x%x", oaep_params.hashAlg); + sk_debug(debug, "oaep_params.mgf: 0x%x", oaep_params.mgf); + sk_debug(debug, "oaep_params.source: 0x%x", oaep_params.source); + sk_debug(debug, "oaep_params.ulSourceDataLen: %lu", + oaep_params.ulSourceDataLen); + + rv = ep11.dll_m_DecryptSingle(blob, hdr->len - sizeof(*hdr), &mech, + (CK_BYTE_PTR)from, fromlen, + to, tolen, ep11_lib->target); + if (rv != CKR_OK) { + sk_debug(debug, "ERROR: m_DecryptSingle failed: rc: 0x%x", rv); + return -EIO; + } + + sk_debug(debug, "tolen: %lu", *tolen); + + return 0; +} + +/** + * Sign data using ECDSA. + * + * @param key_token the RSA key token + * @param key_token_length the length of the key token + * @param sig a buffer to store the signature on return. + * @param siglen on input: the size if the signature buffer + * on return: the size of the signature + * @param tbs the data to be signed. + * @param tbslen the size of the data to be signed + * @param digest_nid the OpenSSL nid of the message digest used to + * produce the data to be signed + * @param private the CCA library structure + * @param debug if true, debug messages are printed + * + * @returns a negative errno in case of an error, 0 if success. + */ +static int sk_ep11_ecdsa_sign(const unsigned char *key_token, + size_t key_token_length, + unsigned char *sig, size_t *siglen, + const unsigned char *tbs, size_t tbslen, + int digest_nid, void *private, + bool debug) +{ + const struct ep11kblob_header *hdr = + (struct ep11kblob_header *)key_token; + const unsigned char *blob = key_token + + sizeof(struct ep11kblob_header); + CK_MECHANISM mech = { .mechanism = CKM_ECDSA, + .pParameter = NULL, .ulParameterLen = 0 }; + const struct sk_ext_ep11_lib *ep11_lib = private; + struct ep11_lib ep11; + CK_ULONG sig_len; + CK_RV rv; + int rc; + + if (ep11_lib == NULL || key_token == NULL || sig == NULL || + siglen == NULL || tbs == NULL) + return -EINVAL; + + if (!sk_ep11_valid_ep11_blob(key_token, key_token_length)) + return -EINVAL; + + sk_debug(debug, "tbslen: %lu siglen: %lu digest_nid: %d", + tbslen, *siglen, digest_nid); + + rc = sk_ep11_get_library_functions(ep11_lib, &ep11); + if (rc != 0) { + sk_debug(debug, + "ERROR: Failed to get EP11 functions from library"); + return rc; + } + + sig_len = *siglen; + rv = ep11.dll_m_SignSingle(blob, hdr->len - sizeof(*hdr), &mech, + (CK_BYTE_PTR)tbs, tbslen, + sig, &sig_len, ep11_lib->target); + if (rv != CKR_OK) { + sk_debug(debug, "ERROR: m_SignSingle failed: rc: 0x%x", rv); + return -EIO; + } + + rc = SK_UTIL_build_ecdsa_signature(sig, sig_len, sig, siglen); + if (rc != 0) { + sk_debug(debug, "ERROR: build_ecdsa_signature failed"); + return -EIO; + } + + sk_debug(debug, "siglen: %lu", *siglen); + + return 0; +} + +static const struct sk_funcs sk_ep11_funcs = { + .rsa_sign = sk_ep11_rsa_sign, + .rsa_pss_sign = sk_ep11_rsa_pss_sign, + .rsa_decrypt = sk_ep11_rsa_decrypt, + .rsa_decrypt_oaep = sk_ep11_rsa_decrypt_oaep, + .ecdsa_sign = sk_ep11_ecdsa_sign, +}; + +struct pub_key_cb_data { + const struct sk_ext_ep11_lib *ep11_lib; + const unsigned char *key_token; + size_t key_token_length; + bool rsa_pss; + EVP_PKEY *pkey; + bool debug; +}; + +/* + * Callback for generating an PKEY from a secure key + */ +static int sk_ep11_get_secure_key_as_pkey_cb( + const struct sk_pub_key_info *pub_key, void *private) +{ + struct pub_key_cb_data *data = private; + int rc; + + if (pub_key == NULL || data == NULL) + return -EINVAL; + + rc = SK_OPENSSL_get_pkey(data->key_token, data->key_token_length, + pub_key, data->rsa_pss, &sk_ep11_funcs, + data->ep11_lib, &data->pkey, data->debug); + if (rc != 0) { + sk_debug(data->debug, + "ERROR: SK_OPENSSL_get_pkey failed"); + return rc; + } + + sk_debug(data->debug, "pkey: %p", data->pkey); + + return 0; +} + +/** + * Extracts the public key from a Ep11 RSA or EC key token, and returns + * it as OpenSSL PKEY. + * + * @param ep11_lib the EP11 library structure + * @param key_token the key token containing an EP11 secure key + * @param key_token_length the size of the key token + * @param rsa_pss For RSA public keys: create a RSA-PSS type PKEY + * @param pkey On return: a PKEY containing the public key + * @param debug if true, debug messages are printed + * + * @returns a negative errno in case of an error, 0 if success. + */ +int SK_EP11_get_secure_key_as_pkey(const struct sk_ext_ep11_lib *ep11_lib, + const unsigned char *key_token, + size_t key_token_length, + bool rsa_pss, EVP_PKEY **pkey, bool debug) +{ + struct pub_key_cb_data data; + int rc; + + sk_debug(debug, "rsa_pss: %d", rsa_pss); + + data.ep11_lib = ep11_lib; + data.key_token = key_token; + data.key_token_length = key_token_length; + data.rsa_pss = rsa_pss; + data.pkey = NULL; + data.debug = debug; + + rc = SK_EP11_get_public_from_secure_key(key_token, key_token_length, + sk_ep11_get_secure_key_as_pkey_cb, + &data, debug); + if (rc != 0) { + sk_debug(debug, + "ERROR: SK_EP11_get_public_from_secure_key failed"); + return rc; + } + + sk_debug(debug, "pkey: %p", data.pkey); + + *pkey = data.pkey; + return 0; +} + +static int sk_ep11_get_public_from_ec_key(const unsigned char *pub_key, + size_t pub_key_len, + const unsigned char *params, + size_t params_len, + sk_pub_key_func_t pub_key_cb, + void *private, + bool debug) +{ + struct sk_pub_key_info pub_key_info = { 0 }; + const struct sk_ec_curve_info *curve; + unsigned char *buf = NULL; + ASN1_OBJECT *obj = NULL; + int y_bit = 0; + int rc = 0; + + /* + * ECParameters ::= CHOICE { + * namedCurve OBJECT IDENTIFIER + * -- implicitCurve NULL + * -- specifiedCurve SpecifiedECDomain + * } + * + * EC PublicKey ECPoint + */ + if (d2i_ASN1_OBJECT(&obj, ¶ms, params_len) == NULL) { + sk_debug(debug, "ERROR: d2i_ASN1_OBJECT failed"); + return -EIO; + } + pub_key_info.ec.curve_nid = OBJ_obj2nid(obj); + ASN1_OBJECT_free(obj); + + sk_debug(debug, "curve_nid: %d", pub_key_info.ec.curve_nid); + curve = SK_UTIL_ec_get_curve_info(pub_key_info.ec.curve_nid); + if (curve == NULL) { + sk_debug(debug, "ERROR: unsupported curve"); + return -EIO; + } + pub_key_info.ec.prime_len = curve->prime_len; + sk_debug(debug, "prime_len: %lu", pub_key_info.ec.prime_len); + + if (pub_key_len != 2 * pub_key_info.ec.prime_len + 1) { + sk_debug(debug, "ERROR: invalid public key length"); + return -EINVAL; + } + + pub_key_info.ec.x = pub_key + 1; + + /* First byte of public key contains indication of key compression */ + switch (pub_key[0]) { + case POINT_CONVERSION_COMPRESSED: + case POINT_CONVERSION_COMPRESSED + POINT_CONVERSION_ODD_EVEN: + /* Compressed form, only x is available */ + y_bit = (pub_key[0] & POINT_CONVERSION_ODD_EVEN) ? 1 : 0; + + buf = malloc(pub_key_info.ec.prime_len); + if (buf == NULL) { + sk_debug(debug, "ERROR: malloc failed"); + rc = -ENOMEM; + goto out; + } + + rc = SK_UTIL_ec_calculate_y_coordinate( + pub_key_info.ec.curve_nid, + pub_key_info.ec.prime_len, + pub_key_info.ec.x, y_bit, + buf); + if (rc != 0) { + sk_debug(debug, "ERROR: ec_calculate_y_coordinate " + "failed"); + goto out; + } + + pub_key_info.ec.y = buf; + break; + + case POINT_CONVERSION_UNCOMPRESSED: + case POINT_CONVERSION_HYBRID: + case POINT_CONVERSION_HYBRID + POINT_CONVERSION_ODD_EVEN: + /* Uncompressed or hybrid, x and y are available */ + pub_key_info.ec.y = pub_key_info.ec.x + + pub_key_info.ec.prime_len; + break; + + default: + sk_debug(debug, "ERROR: invalid compression indication"); + rc = -EIO; + goto out; + } + + pub_key_info.type = SK_KEY_TYPE_EC; + + rc = pub_key_cb(&pub_key_info, private); + if (rc != 0) { + sk_debug(debug, "ERROR: pub_key_cb failed"); + goto out; + } + +out: + if (buf != NULL) + free(buf); + + return rc; +} + +/* + * Extracts the public key from a EP11 RSA key blob, and calls + * the specified callback function with the public key information. + */ +static int sk_ep11_get_public_from_rsa_key(const unsigned char *pub_key, + size_t pub_key_len, + sk_pub_key_func_t pub_key_cb, + void *private, + bool debug) +{ + struct sk_pub_key_info pub_key_info = { 0 }; + const unsigned char *seq; + size_t tag_len, seq_len; + unsigned char tag; + int rc; + + /* + * RSAPublicKey ::= SEQUENCE { + * modulus INTEGER, -- n + * publicExponent INTEGER -- e + * } + */ + tag = sk_ep11_parse_der_tag(pub_key, pub_key_len, &tag_len, &seq, + &seq_len); + if (tag != 0x30) { /* SEQUENCE */ + sk_debug(debug, "ERROR: failed to parse SEQUENCE"); + return -EINVAL; + } + + tag = sk_ep11_parse_der_tag(seq, seq_len, &tag_len, + &pub_key_info.rsa.modulus, + &pub_key_info.rsa.modulus_len); + if (tag != 0x02) { /* INTEGER */ + sk_debug(debug, "ERROR: failed to parse INTEGER (modulus)"); + return -EINVAL; + } + + tag = sk_ep11_parse_der_tag(seq + tag_len, seq_len - tag_len, &tag_len, + &pub_key_info.rsa.pub_exp, + &pub_key_info.rsa.pub_exp_len); + if (tag != 0x02) { /* INTEGER */ + sk_debug(debug, "ERROR: failed to parse INTEGER (pub-exp)"); + return -EINVAL; + } + + pub_key_info.type = SK_KEY_TYPE_RSA; + + rc = pub_key_cb(&pub_key_info, private); + if (rc != 0) { + sk_debug(debug, "ERROR: pub_key_cb failed"); + return rc; + } + + return 0; +} + + +/** + * Extracts the public key from a EP11 RSA or EC key blob, and calls + * the specified callback function with the public key information. + * + * @param key_token the key token containing an EP11 secure key + * @param key_token_length the size of the key token + * @param pub_key_cb the callback function to call with the public key + * @param private a private pointer passed as is to the callback + * @param debug if true, debug messages are printed + * + * @returns a negative errno in case of an error, 0 if success. + */ +int SK_EP11_get_public_from_secure_key(const unsigned char *key_token, + size_t key_token_length, + sk_pub_key_func_t pub_key_cb, + void *private, + bool debug) +{ + const struct ep11kblob_header *hdr = + (struct ep11kblob_header *)key_token; + size_t params_len, pub_key_len, spki_size; + const unsigned char *params, *pub_key; + enum sk_key_type type; + int rc; + + if (key_token == NULL || pub_key_cb == NULL) + return -EINVAL; + + if (!sk_ep11_valid_ep11_blob(key_token, key_token_length)) + return -EINVAL; + + spki_size = key_token_length - hdr->len; + if (spki_size <= 0) + return -EINVAL; + + rc = sk_ep11_parse_spki(key_token + hdr->len, spki_size, + &type, ¶ms, ¶ms_len, &pub_key, + &pub_key_len); + if (rc != 0) { + sk_debug(debug, "ERROR: sk_ep11_parse_spki failed"); + return rc; + } + + sk_debug(debug, "type: %d", type); + + switch (type) { + case SK_KEY_TYPE_EC: + rc = sk_ep11_get_public_from_ec_key(pub_key, pub_key_len, + params, params_len, + pub_key_cb, + private, debug); + if (rc != 0) { + sk_debug(debug, + "ERROR: sk_ep11_get_public_from_ec_key failed"); + return rc; + } + break; + case SK_KEY_TYPE_RSA: + rc = sk_ep11_get_public_from_rsa_key(pub_key, pub_key_len, + pub_key_cb, + private, debug); + if (rc != 0) { + sk_debug(debug, + "ERROR: sk_ep11_get_public_from_rsa_key failed"); + return rc; + } + break; + default: + sk_debug(debug, "ERROR: Invalid key type: %d", type); + return -EIO; + } + + return 0; +} + +/* + * Checks that the specified target is a single APQN target, and extracts the + * card and domain from it. + */ +static int sk_ep11_target_get_apqn(target_t target, unsigned short *card, + unsigned short *domain) +{ + if ((target & 0x8000000000000000L) != 0) + return -ENODEV; + + *card = (target & 0x0000FFFF00000000) >> 32; + *domain = target & 0x00000000000FFFF; + return 0; +} + +/** + * Reenciphers an EP11 secure key with a new EP11 master key. + * The target passed in via ep11_lib must be a single APQN target, and the + * domain and card numbers must be specified. + * + * @param ep11_lib the EP11 library structure + * @param key_token the key token containing an EP11 secure key + * @param key_token_length the size of the key token + * @param debug if true, debug messages are printed + * + * @returns a negative errno in case of an error, 0 if success. + */ +int SK_EP11_reencipher_key(const struct sk_ext_ep11_lib *ep11_lib, + unsigned char *key_token, size_t key_token_length, + bool debug) +{ + const struct ep11kblob_header *hdr = + (struct ep11kblob_header *)key_token; + unsigned char *blob = key_token + sizeof(struct ep11kblob_header); + CK_BYTE resp[EP11_MAX_KEY_TOKEN_SIZE]; + CK_BYTE req[EP11_MAX_KEY_TOKEN_SIZE]; + unsigned short card, domain; + CK_IBM_DOMAIN_INFO dinf; + struct XCPadmresp lrb; + struct XCPadmresp rb; + struct ep11_lib ep11; + CK_ULONG dinf_len; + size_t resp_len; + long req_len; + CK_RV rv; + int rc; + + if (ep11_lib == NULL || key_token == NULL) + return -EINVAL; + + if (!sk_ep11_valid_ep11_blob(key_token, key_token_length)) + return -EINVAL; + + rc = sk_ep11_target_get_apqn(ep11_lib->target, &card, &domain); + if (rc != 0) { + sk_debug(debug, + "ERROR: Need a single-APQN target for reencipher"); + return rc; + } + + rc = sk_ep11_get_library_functions(ep11_lib, &ep11); + if (rc != 0) { + sk_debug(debug, + "ERROR: Failed to get EP11 functions from library"); + return rc; + } + + dinf_len = sizeof(dinf); + rv = ep11.dll_m_get_xcp_info(&dinf, &dinf_len, CK_IBM_XCPQ_DOMAIN, 0, + ep11_lib->target); + if (rv != CKR_OK) { + sk_debug(debug, "Failed to query domain information for " + "APQN %02X.%04X: m_get_xcp_info rc: 0x%lx", + card, domain, rv); + return -EIO; + } + + if ((dinf.flags & CK_IBM_DOM_COMMITTED_NWK) == 0) { + sk_debug(debug, "The NEW master key register of APQN %02X.%04X " + "is not in COMMITTED state", card, domain); + return -ENODEV; + } + + rb.domain = domain; + lrb.domain = domain; + + resp_len = sizeof(resp); + req_len = ep11.dll_xcpa_cmdblock(req, sizeof(req), XCP_ADM_REENCRYPT, + &rb, NULL, blob, + hdr->len - sizeof(*hdr)); + if (req_len < 0) { + sk_debug(debug, "Failed to build XCP command block"); + return -EIO; + } + + rv = ep11.dll_m_admin(resp, &resp_len, NULL, NULL, req, req_len, NULL, + 0, ep11_lib->target); + if (rv != CKR_OK || resp_len == 0) { + sk_debug(debug, "Command XCP_ADM_REENCRYPT failed. " + "rc = 0x%lx, resp_len = %ld", rv, resp_len); + return -EIO; + } + + rc = ep11.dll_xcpa_internal_rv(resp, resp_len, &lrb, &rv); + if (rc != 0) { + sk_debug(debug, "Failed to parse response. rc = %d", rc); + return -EIO; + } + + if (rv != CKR_OK) { + sk_debug(debug, "Failed to re-encrypt the EP11 secure key. " + "rc = 0x%lx", rv); + switch (rv) { + case CKR_IBM_WKID_MISMATCH: + sk_debug(debug, "The EP11 secure key is currently " + "encrypted under a different master that does " + "not match the master key in the CURRENT " + "master key register of APQN %02X.%04X", + card, domain); + break; + } + return -EIO; + } + + if (hdr->len - sizeof(*hdr) != lrb.pllen) { + sk_debug(debug, "Re-encrypted EP11 secure key size has " + "changed: org-len: %lu, new-len: %lu", + hdr->len - sizeof(*hdr), lrb.pllen); + return -EIO; + } + + return 0; +} + + diff --git a/libseckey/sk_openssl.c b/libseckey/sk_openssl.c new file mode 100644 index 00000000..7a19f528 --- /dev/null +++ b/libseckey/sk_openssl.c @@ -0,0 +1,894 @@ +/* + * libseckey - Secure key library + * + * Copyright IBM Corp. 2021 + * + * s390-tools is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "lib/zt_common.h" + +#include "libseckey/sk_openssl.h" +#include "libseckey/sk_utilities.h" +#include "libseckey/sk_cca.h" +#include "libseckey/sk_ep11.h" + +#define SERIAL_NUMBER_BIT_SIZE 159 + +int sk_openssl_get_pkey_ec(const unsigned char *secure_key, + size_t secure_key_size, int nid, size_t prime_len, + const unsigned char *x, const unsigned char *y, + const struct sk_funcs *sk_funcs, const void *private, + EVP_PKEY **pkey, bool debug); + +int sk_openssl_get_pkey_rsa(const unsigned char *secure_key, + size_t secure_key_size, + const unsigned char *modulus, size_t modulus_length, + const unsigned char *pub_exp, size_t pub_exp_length, + int pkey_type, const struct sk_funcs *sk_funcs, + const void *private, EVP_PKEY **pkey, bool debug); + +/** + * Generate a secure key using the specified secure key crypto library. + * + * @param secure_key A buffer where the secure key is stored to. If NULL, + * the required buffer size is returned in + * secure_key_size (size query). + * @param secure_key_size On entry, the size of the buffer specified with + * secure_key (ignored if secure_key is NULL), + * on exit the size of the secure key. + * @param info key generation info, such as key type (EC or RSA) + * and key parameters. + * @param ext_lib External secure key crypto library to use + * @param debug if true, debug messages are printed + * + * @returns a negative errno in case of an error, 0 if success. + */ +int SK_OPENSSL_generate_secure_key(unsigned char *secure_key, + size_t *secure_key_size, + const struct sk_key_gen_info *info, + const struct sk_ext_lib *ext_lib, bool debug) +{ + int rc; + + if (info == NULL || ext_lib == NULL || secure_key_size == NULL) + return -EINVAL; + + sk_debug(debug, "ext-lib type: %d key type: %d", ext_lib->type, + info->type); + + switch (ext_lib->type) { + case SK_EXT_LIB_CCA: + switch (info->type) { + case SK_KEY_TYPE_EC: + rc = SK_CCA_generate_ec_key_pair(ext_lib->cca, + info->ec.curve_nid, + secure_key, secure_key_size, + debug); + break; + case SK_KEY_TYPE_RSA: + rc = SK_CCA_generate_rsa_key_pair(ext_lib->cca, + info->rsa.modulus_bits, + info->rsa.pub_exp, + secure_key, secure_key_size, + debug); + break; + default: + sk_debug(debug, "ERROR: Invalid key type: %d", + info->type); + return -EINVAL; + } + break; + + case SK_EXT_LIB_EP11: + switch (info->type) { + case SK_KEY_TYPE_EC: + rc = SK_EP11_generate_ec_key_pair(ext_lib->ep11, + info->ec.curve_nid, + secure_key, secure_key_size, + debug); + break; + case SK_KEY_TYPE_RSA: + rc = SK_EP11_generate_rsa_key_pair(ext_lib->ep11, + info->rsa.modulus_bits, + info->rsa.pub_exp, + info->rsa.x9_31, + secure_key, secure_key_size, + debug); + break; + default: + sk_debug(debug, "ERROR: Invalid key type: %d", + info->type); + return -EINVAL; + } + break; + + default: + sk_debug(debug, "ERROR: Invalid ext-lib type: %d", + ext_lib->type); + return -EINVAL; + } + + if (rc != 0) { + sk_debug(debug, "ERROR: Failed to generate a key: rc: %d - %s", + rc, strerror(-rc)); + return rc; + } + + return 0; +} + +/** + * Reenciphers a secure key with a new master key using the specified secure + * key crypto library. + * + * @param secure_key the key token containing an secure key + * @param secure_key_size the size of the key token + * @param to_new if true, reencipher with the MK in then NEW register + * @param ext_lib External secure key crypto library to use + * @param debug if true, debug messages are printed + * + * @returns a negative errno in case of an error, 0 if success. + */ +int SK_OPENSSL_reencipher_secure_key(unsigned char *secure_key, + size_t secure_key_size, bool to_new, + const struct sk_ext_lib *ext_lib, + bool debug) +{ + int rc; + + if (ext_lib == NULL || secure_key == NULL) + return -EINVAL; + + sk_debug(debug, "ext-lib type: %d to_new: %d", ext_lib->type, to_new); + + switch (ext_lib->type) { + case SK_EXT_LIB_CCA: + rc = SK_CCA_reencipher_key(ext_lib->cca, secure_key, + secure_key_size, to_new, debug); + break; + + case SK_EXT_LIB_EP11: + rc = SK_EP11_reencipher_key(ext_lib->ep11, secure_key, + secure_key_size, debug); + break; + + default: + sk_debug(debug, "ERROR: Invalid ext lib type: %d", + ext_lib->type); + return -EINVAL; + } + + if (rc != 0) { + sk_debug(debug, + "ERROR: Failed to reencipher a key: rc: %d - %s", + rc, strerror(-rc)); + return rc; + } + + return 0; +} + +/** + * Extracts the public key from a secure key, and returns it as OpenSSL PKEY. + * + * @param secure_key the key token containing an secure key + * @param secure_key_size the size of the key token + * @param rsa_pss if the secure key is a RSA key, return a PKEY of + * type EVP_PKEY_RSA_PSS + * @param pkey On return: a PKEY containing the public key + * @param ext_lib External secure key crypto library to use + * @param debug if true, debug messages are printed + * + * @returns a negative errno in case of an error, 0 if success. + */ +int SK_OPENSSL_get_secure_key_as_pkey(const unsigned char *secure_key, + size_t secure_key_size, bool rsa_pss, + EVP_PKEY **pkey, + const struct sk_ext_lib *ext_lib, + bool debug) +{ + int rc; + + if (ext_lib == NULL || secure_key == NULL || pkey == NULL) + return -EINVAL; + + sk_debug(debug, "ext-lib type: %d rsa_pss: %d", ext_lib->type, rsa_pss); + + switch (ext_lib->type) { + case SK_EXT_LIB_CCA: + rc = SK_CCA_get_secure_key_as_pkey(ext_lib->cca, secure_key, + secure_key_size, rsa_pss, + pkey, debug); + break; + + case SK_EXT_LIB_EP11: + rc = SK_EP11_get_secure_key_as_pkey(ext_lib->ep11, secure_key, + secure_key_size, rsa_pss, + pkey, debug); + break; + + default: + sk_debug(debug, "ERROR: Invalid ext lib type: %d", + ext_lib->type); + return -EINVAL; + } + + if (rc != 0) { + sk_debug(debug, "ERROR: Failed to get PKEY: rc: %d - %s", + rc, strerror(-rc)); + return rc; + } + + sk_debug(debug, "pkey: %p", *pkey); + return 0; +} + +/** + * Extracts the public key from a secure key, and calls the specified callback + * function with the public key information. + * + * @param secure_key the key token containing an secure key + * @param secure_key_size the size of the key token + * @param pub_key_cb the callback function to call with the public key + * @param private a private pointer passed as is to the callback + * @param ext_lib External secure key crypto library to use + * @param debug if true, debug messages are printed + * + * @returns a negative errno in case of an error, 0 if success. + */ +int SK_OPENSSL_get_public_from_secure_key(const unsigned char *secure_key, + size_t secure_key_size, + sk_pub_key_func_t pub_key_cb, + void *private, + const struct sk_ext_lib *ext_lib, + bool debug) +{ + int rc; + + if (ext_lib == NULL || secure_key == NULL || pub_key_cb == NULL) + return -EINVAL; + + sk_debug(debug, "ext-lib type: %d", ext_lib->type); + + switch (ext_lib->type) { + case SK_EXT_LIB_CCA: + rc = SK_CCA_get_public_from_secure_key(secure_key, + secure_key_size, + pub_key_cb, private, + debug); + break; + + case SK_EXT_LIB_EP11: + rc = SK_EP11_get_public_from_secure_key(secure_key, + secure_key_size, + pub_key_cb, private, + debug); + break; + + default: + sk_debug(debug, "ERROR: Invalid ext lib type: %d", + ext_lib->type); + return -EINVAL; + } + + if (rc != 0) { + sk_debug(debug, "ERROR: Failed to get PKEY: rc: %d - %s", + rc, strerror(-rc)); + return rc; + } + + return 0; +} + +/** + * Converts a key given by the public key infos into an OpenSSL PKEY and + * attaches the secure key together with secure key functions and private + * pointer to it. If no secure key is provided, a public key only PKEY is + * returned. + * + * @param secure_key the secure key blob. + * If NULL, a clear key PKEY is created. + * @param secure_key_size the size of the secure key blob (ignored if + * secure_key is NULL) + * @param pub_key the public key info + * @param rsa_pss For RSA public keys: create a RSA-PSS type PKEY + * @param sk_funcs the secure key functions to operate with the key. + * Ignored if secure_key is NULL, required otherwise. + * @param private a private pointer that is passed to the secure key + * functions (can be NULL) + * @param pkey On return: A PKEY containing the EC public key. + * @param debug true to enable internal debugging + * + * @returns zero for success, a negative errno in case of an error: + * -EINVAL: a function parameter is invalid + * -ENOMEM: failed to allocate memory + * -EIO: OpenSSL failed to generate the PKEY + * -ENOENT: OpenSSL does not know/support the curve (nid) + */ +int SK_OPENSSL_get_pkey(const unsigned char *secure_key, size_t secure_key_size, + const struct sk_pub_key_info *pub_key, bool rsa_pss, + const struct sk_funcs *sk_funcs, const void *private, + EVP_PKEY **pkey, bool debug) +{ + int rc; + + if (pub_key == NULL) + return -EINVAL; + + sk_debug(debug, "key type: %d", pub_key->type); + + switch (pub_key->type) { + case SK_KEY_TYPE_EC: + rc = sk_openssl_get_pkey_ec(secure_key, secure_key_size, + pub_key->ec.curve_nid, + pub_key->ec.prime_len, + pub_key->ec.x, pub_key->ec.y, + sk_funcs, private, + pkey, debug); + if (rc != 0) { + sk_debug(debug, + "ERROR: SK_OPENSSL_get_pkey_ec failed"); + return rc; + } + break; + case SK_KEY_TYPE_RSA: + rc = sk_openssl_get_pkey_rsa(secure_key, secure_key_size, + pub_key->rsa.modulus, + pub_key->rsa.modulus_len, + pub_key->rsa.pub_exp, + pub_key->rsa.pub_exp_len, + rsa_pss ? EVP_PKEY_RSA_PSS : + EVP_PKEY_RSA, + sk_funcs, private, + pkey, debug); + if (rc != 0) { + sk_debug(debug, + "ERROR: SK_OPENSSL_get_pkey_rsa failed"); + return rc; + } + break; + default: + sk_debug(debug, "ERROR: Invalid key type: %d", + pub_key->type); + return -EINVAL; + } + + sk_debug(debug, "pkey: %p", pkey); + + return 0; +} + +/** + * Sets up a digest sign context with the specified PKEY. + * + * @param pkey the PKEY to use + * @param verify if true a verify context is created, otherwise a + * sign context + * @param digest_nid the NID of the digest algorithm to use. If + * NID_undef, then the PKEY's default digest is used. + * @param rsa_pss_params RSA-PSS parameters (if PKEY is a RSA-PSS key) + * @param md_ctx On return: the MD context that was set up + * @param pkey_ctx On return: the PKEY context that was set up. + * Note: The PKEY context value returned must not be + * freed by the application, it will be freed + * automatically when the MD context is freed. + * @param debug if true, debug messages are printed + * + * @returns a negative errno in case of an error, 0 if success. + */ +int SK_OPENSSL_setup_sign_context(EVP_PKEY *pkey, bool verify, int digest_nid, + struct sk_rsa_pss_params *rsa_pss_params, + EVP_MD_CTX **md_ctx, EVP_PKEY_CTX **pkey_ctx, + bool debug) +{ + EVP_PKEY_CTX *pctx = NULL; + const EVP_MD *md = NULL; + int rc, default_nid; + EVP_MD_CTX *ctx; + + sk_debug(debug, "pkey: %p verify: %d digest_nid: %d", pkey, verify, + digest_nid); + + ctx = EVP_MD_CTX_new(); + if (ctx == NULL) { + sk_debug(debug, "ERROR: Failed to allocate the digest context"); + rc = -ENOMEM; + goto out; + } + + if (digest_nid != NID_undef) { + md = EVP_get_digestbynid(digest_nid); + if (md == NULL) { + sk_debug(debug, + "ERROR: Requested digest not supported"); + rc = -ENOTSUP; + goto out; + } + + if (EVP_PKEY_get_default_digest_nid(pkey, &default_nid) == 2 && + default_nid == 0) { + sk_debug(debug, "ERROR: The signing algorithm requires " + "there to be no digest"); + md = NULL; + } + } + + if (verify) + rc = EVP_DigestVerifyInit(ctx, &pctx, md, NULL, pkey); + else + rc = EVP_DigestSignInit(ctx, &pctx, md, NULL, pkey); + if (rc != 1) { + sk_debug(debug, "ERROR: Failed to initialize the signing " + "operation"); + rc = -EIO; + goto out; + } + + if (EVP_PKEY_id(pkey) == EVP_PKEY_RSA_PSS && rsa_pss_params != NULL) { + sk_debug(debug, "RSA-PSS: saltlen: %d mgf_digest_nid: %d", + rsa_pss_params->salt_len, + rsa_pss_params->mgf_digest_nid); + + rc = EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING); + if (rc != 1) { + sk_debug(debug, + "ERROR: Failed to set the PSS padding mode"); + rc = -EIO; + goto out; + } + + rc = EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, + rsa_pss_params->salt_len); + if (rc != 1) { + sk_debug(debug, + "ERROR: Failed to set the PSS salt length"); + rc = -EIO; + goto out; + } + + if (rsa_pss_params->mgf_digest_nid != 0) { + md = EVP_get_digestbynid( + rsa_pss_params->mgf_digest_nid); + if (md == NULL) { + sk_debug(debug, + "ERROR: Requested MGF digest not " + "supported"); + rc = -ENOENT; + goto out; + } + + rc = EVP_PKEY_CTX_set_rsa_mgf1_md(pctx, md); + if (rc != 1) { + sk_debug(debug, + "ERROR: Failed to set the MGF md"); + rc = -EIO; + goto out; + } + } + } + + rc = 0; + *md_ctx = ctx; + *pkey_ctx = pctx; + +out: + if (rc != 0 && ctx != NULL) + EVP_MD_CTX_free(ctx); + + return rc; +} + +/** + * Generate a certificate signing request using the secure key with the + * specified subject name, certificate extensions (if any), and writes the + * CSR to the specified file in PEM format. + * + * To renew an existing certificate, specify the existing certificate file with + * renew_cert_filename, and the subject name is extracted from it. Any specified + * subject name RDNs are added to the CSR. Also, the extensions are taken from + * the existing certificate, and any specified extensions are added to the CSR. + * + * The CSR is signed using the secure key with an signing algorithm matching + * the secure key type (ECDSA, RSA-PKCS, or RSA-PSS if rsa_pss_params is not + * NULL), and the specified digest. If the digest nid is NID_undef, then a + * default digest is used. + * + * @param secure_key the key token containing an secure key + * @param secure_key_size the size of the key token + * @param subject_rdns an array of strings, each string representing an + * RDN in the form '[+]type=value'. If the type is + * prepended with a '+', then this RDN is added to the + * previous one. + * @param num_subject_rdns number of RDN elements in the array. + * @param subject_utf8 if true, RDNs of type MBSTRING_UTF8 are created, + * otherwise type is MBSTRING_ASC is used. + * @param renew_cert if not NULL, specifies the an existing certificate + * that is renewed. + * @param extensions an array of strings, each string representing an + * certificate extension in the form 'type=value'. + * @param num_extensions number of extension elements in the array. + * @param digest_nid the OpenSSL digest nid to use with the signature + * algorithm, or NID_undef to use the default + * @param rsa_pss_params if not NULL and the secure key is an RSA key, then + * the CSR is signed with RSA-PSS using the specified + * PSS parameters. Ignored if the secure key is an EC + * key + * @param csr On return: the generated CSR. Must be freed by the + * caller using X509_REQ_free. + * @param ext_lib External secure key crypto library to use + * @param debug if true, debug messages are printed + * + * @returns a negative errno in case of an error, 0 if success: + * -EINVAL: invalid parameter + * -ENOMEM: Failed to allocate memory + * -EBADMSG: an RDN or extension is not formatted correctly + * -EIO: OpenSSL failed to create the CSR + * -EEXIST: if one of the RDN name entries or extensions to add is a + * duplicate + * -ENOTSUP: the specified digest is not supported + * any other errno from file I/O routines + */ +int SK_OPENSSL_generate_csr(const unsigned char *secure_key, + size_t secure_key_size, + const char *subject_rdns[], size_t num_subject_rdns, + bool subject_utf8, const X509 *renew_cert, + const char *extensions[], size_t num_extensions, + int digest_nid, + struct sk_rsa_pss_params *rsa_pss_params, + X509_REQ **csr, + const struct sk_ext_lib *ext_lib, bool debug) +{ + const STACK_OF(X509_EXTENSION) *cert_exts = NULL; + X509_NAME *subject_name = NULL; + EVP_PKEY_CTX *pkey_ctx = NULL; + EVP_MD_CTX *md_ctx = NULL; + EVP_PKEY *pkey = NULL; + X509_REQ *req = NULL; + int rc; + + if (secure_key == NULL || ext_lib == NULL || csr == NULL) + return -EINVAL; + if (renew_cert == NULL && + (subject_rdns == NULL || num_subject_rdns == 0)) + return -EINVAL; + if (num_extensions != 0 && extensions == NULL) + return -EINVAL; + + rc = SK_OPENSSL_get_secure_key_as_pkey(secure_key, secure_key_size, + rsa_pss_params != NULL, &pkey, + ext_lib, debug); + if (rc != 0) { + sk_debug(debug, + "ERROR: SK_OPENSSL_get_secure_key_as_pkey failed"); + goto out; + } + + req = X509_REQ_new(); + if (req == NULL) { + sk_debug(debug, "ERROR: X509_REQ_new failed"); + rc = -ENOMEM; + goto out; + } + + rc = X509_REQ_set_version(req, 0L); + if (rc != 1) { + sk_debug(debug, "ERROR: X509_REQ_set_version failed: rc: %d", + rc); + rc = -EIO; + goto out; + } + + if (renew_cert != NULL) { + subject_name = X509_NAME_dup(X509_get_subject_name(renew_cert)); + cert_exts = X509_get0_extensions(renew_cert); + } + + if (subject_rdns != NULL && num_subject_rdns > 0) { + rc = SK_UTIL_build_subject_name(&subject_name, subject_rdns, + num_subject_rdns, subject_utf8); + if (rc != 0) { + sk_debug(debug, "ERROR: Failed to parse the subject " + "name RDNs: %s", strerror(-rc)); + goto out; + } + } + + if (subject_name == NULL) { + rc = -EINVAL; + sk_debug(debug, "ERROR: Subject name can not be empty"); + goto out; + } + + rc = X509_REQ_set_subject_name(req, subject_name); + if (rc != 1) { + rc = -EIO; + sk_debug(debug, + "ERROR: Failed to set subject name into request"); + goto out; + } + + rc = SK_UTIL_build_certificate_extensions(NULL, req, extensions, + num_extensions, cert_exts); + if (rc != 0) { + sk_debug(debug, "ERROR: Failed to parse the extensions: " + "%s", strerror(-rc)); + goto out; + } + + rc = X509_REQ_set_pubkey(req, pkey); + if (rc != 1) { + sk_debug(debug, "ERROR: Failed to set the public key"); + rc = -EIO; + goto out; + } + + rc = SK_OPENSSL_setup_sign_context(pkey, false, digest_nid, + rsa_pss_params, &md_ctx, &pkey_ctx, + debug); + if (rc != 0) { + sk_debug(debug, "ERROR: SK_OPENSSL_setup_sign_context failed"); + goto out; + } + + rc = X509_REQ_sign_ctx(req, md_ctx); + if (rc <= 0) { + sk_debug(debug, + "ERROR: Failed to perform the signing operation"); + rc = -EIO; + goto out; + } + + if (debug) { + sk_debug(debug, "Certificate Signing Request created:"); + X509_REQ_print_fp(stderr, req); + } + + *csr = req; + req = NULL; + rc = 0; + +out: + if (md_ctx != NULL) + EVP_MD_CTX_free(md_ctx); + if (subject_name != NULL) + X509_NAME_free(subject_name); + if (req != NULL) + X509_REQ_free(req); + if (pkey != NULL) + EVP_PKEY_free(pkey); + + return rc; +} + +/** + * Generate a self signed certificate using the secure key with the + * specified subject name, certificate extensions (if any), and writes the + * certificate to the specified file in PEM format. + * + * To renew an existing certificate, specify the existing certificate file with + * renew_cert_filename, and the subject name is extracted from it. Any specified + * subject name RDNs are added to the certificate. Also, the extensions are + * taken from the existing certificate, and any specified extensions are added + * to the certificate. + * + * The certificate is signed using the secure key with an signing algorithm + * matching the secure key type (ECDSA, RSA-PKCS, or RSA-PSS if rsa_pss_params + * is not NULL), and the specified digest. If the digest nid is NID_undef, then + * a default digest is used. + * + * @param secure_key the key token containing an secure key + * @param secure_key_size the size of the key token + * @param subject_rdns an array of strings, each string representing an + * RDN in the form '[+]type=value'. If the type is + * prepended with a '+', then this RDN is added to the + * previous one. + * @param num_subject_rdns number of RDN elements in the array. + * @param subject_utf8 if true, RDNs of type MBSTRING_UTF8 are created, + * otherwise type is MBSTRING_ASC is used. + * @param renew_cert if not NULL, specifies an existing certificate that + * is renewed + * @param extensions an array of strings, each string representing an + * certificate extension in the form 'type=value'. + * @param num_extensions number of extension elements in the array. + * @param validity_days number if day from the current date how long the + * certificate is valid. + * @param digest_nid the OpenSSL digest nid to use with the signature + * algorithm, or NID_undef to use the default + * @param rsa_pss_params if not NULL and the secure key is an RSA key, then + * the certificate is signed with RSA-PSS using the + * specified PSS parameters. Ignored if the secure + * key is an EC key + * @param ss_cert On return: The generated self signed certificate. + * Must be freed by the caller using X509_free. + * @param ext_lib External secure key crypto library to use + * @param debug if true, debug messages are printed + * + * @returns a negative errno in case of an error, 0 if success. + * -EINVAL: invalid parameter + * -ENOMEM: Failed to allocate memory + * -EBADMSG: an RDN or extension is not formatted correctly + * -EIO: OpenSSL failed to create the certificate + * -EEXIST: if one of the RDN name entries or extensions to add is a + * duplicate + * -ENOTSUP: the specified digest is not supported + * any other errno from file I/O routines + */ +int SK_OPENSSL_generate_ss_cert(const unsigned char *secure_key, + size_t secure_key_size, + const char *subject_rdns[], + size_t num_subject_rdns, bool subject_utf8, + const X509 *renew_cert, + const char *extensions[], size_t num_extensions, + int validity_days, int digest_nid, + struct sk_rsa_pss_params *rsa_pss_params, + X509 **ss_cert, + const struct sk_ext_lib *ext_lib, bool debug) +{ + const STACK_OF(X509_EXTENSION) *cert_exts = NULL; + X509_NAME *subject_name = NULL; + EVP_PKEY_CTX *pkey_ctx = NULL; + EVP_MD_CTX *md_ctx = NULL; + EVP_PKEY *pkey = NULL; + X509 *cert = NULL; + int rc; + + if (ext_lib == NULL || ss_cert == NULL) + return -EINVAL; + if (secure_key == NULL) + return -EINVAL; + if (renew_cert == NULL && + (subject_rdns == NULL || num_subject_rdns == 0)) + return -EINVAL; + if (num_extensions != 0 && extensions == NULL) + return -EINVAL; + + rc = SK_OPENSSL_get_secure_key_as_pkey(secure_key, secure_key_size, + rsa_pss_params != NULL, &pkey, + ext_lib, debug); + if (rc != 0) { + sk_debug(debug, + "ERROR: SK_OPENSSL_get_secure_key_as_pkey failed"); + goto out; + } + + cert = X509_new(); + if (cert == NULL) { + sk_debug(debug, "ERROR: X509_new failed"); + rc = -ENOMEM; + goto out; + } + + rc = X509_set_version(cert, 2L); + if (rc != 1) { + sk_debug(debug, "ERROR: X509_set_version failed: rc: %d", rc); + rc = -EIO; + goto out; + } + + rc = SK_UTIL_generate_x509_serial_number(cert, SERIAL_NUMBER_BIT_SIZE); + if (rc != 0) { + sk_debug(debug, "ERROR: Failed to set the serial number: %s", + strerror(-rc)); + goto out; + } + + if (renew_cert != NULL) { + subject_name = X509_NAME_dup(X509_get_subject_name(renew_cert)); + cert_exts = X509_get0_extensions(renew_cert); + } + + + if (subject_rdns != NULL && num_subject_rdns > 0) { + rc = SK_UTIL_build_subject_name(&subject_name, subject_rdns, + num_subject_rdns, subject_utf8); + if (rc != 0) { + sk_debug(debug, "ERROR: Failed to parse the subject " + "name RDNs: %s", strerror(-rc)); + goto out; + } + } + + if (subject_name == NULL) { + rc = -EINVAL; + sk_debug(debug, "ERROR: Subject name can not be empty"); + goto out; + } + + rc = X509_set_subject_name(cert, subject_name); + if (rc != 1) { + rc = -EIO; + sk_debug(debug, "ERROR: Failed to set subject name into cert"); + goto out; + } + + rc = X509_set_issuer_name(cert, subject_name); + if (rc != 1) { + rc = -EIO; + sk_debug(debug, "ERROR: Failed to set issuer name into cert"); + goto out; + } + + rc = SK_UTIL_build_certificate_extensions(cert, NULL, extensions, + num_extensions, cert_exts); + if (rc != 0) { + sk_debug(debug, "ERROR: Failed to parse the extensions: " + "%s", strerror(-rc)); + goto out; + } + + if (X509_gmtime_adj(X509_getm_notBefore(cert), 0) == NULL) { + rc = -EIO; + sk_debug(debug, + "ERROR: Failed to set notBefore time inti cert"); + goto out; + } + + if (X509_time_adj_ex(X509_getm_notAfter(cert), + validity_days, 0, NULL) == NULL) { + rc = -EIO; + sk_debug(debug, "ERROR: Failed to set notAfter time into cert"); + goto out; + } + + rc = X509_set_pubkey(cert, pkey); + if (rc != 1) { + sk_debug(debug, "ERROR: Failed to set the public key"); + rc = -EIO; + goto out; + } + + rc = SK_OPENSSL_setup_sign_context(pkey, false, digest_nid, + rsa_pss_params, &md_ctx, &pkey_ctx, + debug); + if (rc != 0) { + sk_debug(debug, "ERROR: SK_OPENSSL_setup_sign_context failed"); + goto out; + } + + rc = X509_sign_ctx(cert, md_ctx); + if (rc <= 0) { + sk_debug(debug, + "ERROR: Failed to perform the signing operation"); + rc = -EIO; + goto out; + } + + if (debug) { + sk_debug(debug, "Self-signed Certificate created:"); + X509_print_fp(stderr, cert); + } + + *ss_cert = cert; + cert = NULL; + rc = 0; + +out: + if (md_ctx != NULL) + EVP_MD_CTX_free(md_ctx); + if (subject_name != NULL) + X509_NAME_free(subject_name); + if (cert != NULL) + X509_free(cert); + if (pkey != NULL) + EVP_PKEY_free(pkey); + + return rc; +} + diff --git a/libseckey/sk_pkeymeth.c b/libseckey/sk_pkeymeth.c new file mode 100644 index 00000000..5a73f0cc --- /dev/null +++ b/libseckey/sk_pkeymeth.c @@ -0,0 +1,1111 @@ +/* + * libseckey - Secure key library + * + * Copyright IBM Corp. 2021 + * + * s390-tools is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lib/zt_common.h" + +#include "libseckey/sk_openssl.h" +#include "libseckey/sk_utilities.h" + +/* + * This source file is only used with OpenSSL < 3.0. + * PKEY method functions are deprecated since OpenSSL 3.0 + */ +#if !OPENSSL_VERSION_PREREQ(3, 0) + +static int sk_pkey_data_ec_index = -1; +static int sk_pkey_data_rsa_index = -1; + +static const EVP_PKEY_METHOD *sk_pkey_meth_default_method_ec; +static const EVP_PKEY_METHOD *sk_pkey_meth_default_method_rsa; +static const EVP_PKEY_METHOD *sk_pkey_meth_default_method_rsa_pss; + +static int sk_pkey_meth_lib = -1; + +int sk_openssl_get_pkey_ec(const unsigned char *secure_key, + size_t secure_key_size, int nid, size_t prime_len, + const unsigned char *x, const unsigned char *y, + const struct sk_funcs *sk_funcs, const void *private, + EVP_PKEY **pkey, bool debug); + +int sk_openssl_get_pkey_rsa(const unsigned char *secure_key, + size_t secure_key_size, + const unsigned char *modulus, size_t modulus_length, + const unsigned char *pub_exp, size_t pub_exp_length, + int pkey_type, const struct sk_funcs *sk_funcs, + const void *private, EVP_PKEY **pkey, bool debug); + +#define sk_debug_data(data, fmt...) sk_debug(data->debug, fmt) + +static void sk_pkey_meth_put_error(int err, const char *file, int line, + char *fmt, ...) +{ + char text[200]; + va_list ap; + + va_start(ap, fmt); + ERR_put_error(sk_pkey_meth_lib, 0, err, file, line); + vsnprintf(text, sizeof(text), fmt, ap); + ERR_add_error_data(1, text); + va_end(ap); +} + +#define put_error_data(data, err, fmt...) \ + do { \ + sk_debug_data(data, "ERROR: "fmt); \ + sk_pkey_meth_put_error(err, __FILE__, __LINE__, \ + fmt); \ + } while (0) + + +/* Secure key PKEY extra data */ +struct sk_pkey_data { + unsigned char *key_blob; + size_t key_blob_size; + struct sk_funcs *funcs; + void *private; + bool debug; +}; + +/* + * Free secure key PKEY data attached to a PKEY + */ +static void sk_pkey_data_free(void *UNUSED(parent), void *ptr, + CRYPTO_EX_DATA *UNUSED(ad), int UNUSED(idx), + long UNUSED(argl), void *UNUSED(argp)) +{ + struct sk_pkey_data *data = ptr; + + if (data == NULL) + return; + + sk_debug_data(data, "data: %p", data); + + if (data != NULL) { + if (data->key_blob != NULL) + OPENSSL_free(data->key_blob); + OPENSSL_free(data); + } +} + +/* + * Duplicate secure key PKEY data attached to a PKEY + */ +static int sk_pkey_data_dup(CRYPTO_EX_DATA *UNUSED(to), + const CRYPTO_EX_DATA *UNUSED(from), + void *from_d, int UNUSED(idx), long UNUSED(argl), + void *UNUSED(argp)) +{ + struct sk_pkey_data *from_data; + struct sk_pkey_data *data; + void **pptr = from_d; + + from_data = *pptr; + + sk_debug_data(from_data, "from_data: %p", from_data); + + data = OPENSSL_zalloc(sizeof(struct sk_pkey_data)); + if (data == NULL) { + put_error_data(from_data, ERR_R_MALLOC_FAILURE, + "OPENSSL_malloc failed"); + return 0; + } + memcpy(data, from_data, sizeof(struct sk_pkey_data)); + + data->key_blob = OPENSSL_malloc(data->key_blob_size); + if (data->key_blob == NULL) { + put_error_data(from_data, ERR_R_MALLOC_FAILURE, + "OPENSSL_malloc failed"); + OPENSSL_free(data); + return 0; + } + memcpy(data->key_blob, from_data->key_blob, data->key_blob_size); + + *pptr = data; + sk_debug_data(from_data, "data: %p", data); + return 1; +} + +/* + * Get the default PKEY method for a POKEY id + */ +static const EVP_PKEY_METHOD *sk_pkey_meth_get_default_method(int pkey_id) +{ + switch (pkey_id) { + case EVP_PKEY_EC: + return sk_pkey_meth_default_method_ec; + case EVP_PKEY_RSA: + return sk_pkey_meth_default_method_rsa; + case EVP_PKEY_RSA_PSS: + return sk_pkey_meth_default_method_rsa_pss; + default: + return NULL; + } +} + +static int sk_pkey_meth_get_pkey_data(EVP_PKEY_CTX *ctx, EVP_PKEY **pkey, + struct sk_pkey_data **sk_data) +{ + *pkey = EVP_PKEY_CTX_get0_pkey(ctx); + if (*pkey == NULL) + return 0; + + switch (EVP_PKEY_id(*pkey)) { + case EVP_PKEY_EC: + *sk_data = EC_KEY_get_ex_data(EVP_PKEY_get0_EC_KEY(*pkey), + sk_pkey_data_ec_index); + break; + case EVP_PKEY_RSA: + case EVP_PKEY_RSA_PSS: + *sk_data = RSA_get_ex_data(EVP_PKEY_get0_RSA(*pkey), + sk_pkey_data_rsa_index); + break; + default: + return 0; + } + + return 1; +} + +static int sk_pkey_meth_sign_init(EVP_PKEY_CTX *ctx) +{ + int (*sign_init)(EVP_PKEY_CTX *ctx); + const EVP_PKEY_METHOD *default_meth; + struct sk_pkey_data *sk_data; + EVP_PKEY *pkey; + + if (!sk_pkey_meth_get_pkey_data(ctx, &pkey, &sk_data)) + return 0; + + if (sk_data == NULL) { + /* If no secure key is attached, call default implementation */ + default_meth = sk_pkey_meth_get_default_method( + EVP_PKEY_id(pkey)); + if (default_meth == NULL) + return 0; + + EVP_PKEY_meth_get_sign(default_meth, &sign_init, NULL); + if (sign_init == NULL) + return 1; + + return sign_init(ctx); + } + + sk_debug_data(sk_data, "sk_data: %p pkey: %p type: %d", sk_data, pkey, + EVP_PKEY_id(pkey)); + return 1; +} + +static int sk_pkey_meth_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, + size_t *siglen, const unsigned char *tbs, + size_t tbslen) +{ + int (*sign)(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen, + const unsigned char *tbs, size_t tbslen); + int sig_sz, pad_mode, hlen, saltlen, max_saltlen; + const EVP_PKEY_METHOD *default_meth; + struct sk_pkey_data *sk_data; + int rc, md_type, mgf_md_type; + const EVP_MD *sigmd, *mgf1md; + const EC_KEY *ec; + EVP_PKEY *pkey; + + if (!sk_pkey_meth_get_pkey_data(ctx, &pkey, &sk_data)) + return 0; + + if (sk_data == NULL) { + /* If no secure key is attached, call default implementation */ + default_meth = sk_pkey_meth_get_default_method( + EVP_PKEY_id(pkey)); + if (default_meth == NULL) + return 0; + + EVP_PKEY_meth_get_sign(default_meth, NULL, &sign); + if (sign == NULL) + return 0; + + return sign(ctx, sig, siglen, tbs, tbslen); + } + + /* A secure key is attached, implement secure key sign */ + sk_debug_data(sk_data, "sk_data: %p pkey: %p type: %d", sk_data, pkey, + EVP_PKEY_id(pkey)); + + if (sig == NULL) { + *siglen = EVP_PKEY_size(pkey); + sk_debug_data(sk_data, "siglen: %lu", *siglen); + return 1; + } + + if (*siglen < (size_t)EVP_PKEY_size(pkey)) { + put_error_data(sk_data, ERR_R_PASSED_INVALID_ARGUMENT, + "signature buffer too small"); + return 0; + } + + *siglen = EVP_PKEY_size(pkey); + + if (sk_data->funcs == NULL) { + put_error_data(sk_data, ERR_R_PASSED_NULL_PARAMETER, + "no secure key funcs"); + return 0; + } + + if (EVP_PKEY_CTX_get_signature_md(ctx, &sigmd) != 1) + return 0; + md_type = sigmd != NULL ? EVP_MD_type(sigmd) : NID_sha1; + + switch (EVP_PKEY_id(pkey)) { + case EVP_PKEY_RSA: + if (sk_data->funcs->rsa_sign == NULL) { + put_error_data(sk_data, ERR_R_PASSED_NULL_PARAMETER, + "no secure key sign function"); + return 0; + } + + if (EVP_PKEY_CTX_get_rsa_padding(ctx, &pad_mode) != 1) + return 0; + + rc = sk_data->funcs->rsa_sign(sk_data->key_blob, + sk_data->key_blob_size, + sig, siglen, tbs, tbslen, + pad_mode, md_type, + sk_data->private, + sk_data->debug); + break; + + case EVP_PKEY_RSA_PSS: + if (sk_data->funcs->rsa_pss_sign == NULL) { + put_error_data(sk_data, ERR_R_PASSED_NULL_PARAMETER, + "no secure key sign function"); + return 0; + } + + if (EVP_PKEY_CTX_get_rsa_padding(ctx, &pad_mode) != 1) + return 0; + if (EVP_PKEY_CTX_get_rsa_mgf1_md(ctx, &mgf1md) != 1) + return 0; + if (EVP_PKEY_CTX_get_rsa_pss_saltlen(ctx, &saltlen) != 1) + return 0; + + hlen = sigmd != NULL ? EVP_MD_size(sigmd) : SHA_DIGEST_LENGTH; + if (mgf1md != NULL) { + mgf_md_type = EVP_MD_type(mgf1md); + hlen = EVP_MD_size(mgf1md); + } else { + mgf_md_type = md_type; + } + + /* + * We should be using RSA_bits(EVP_PKEY_get0_RSA(pkey)) here, + * but EVP_PKEY_get0_RSA(pkey) does not work with PKEY/type + * EVP_PKEY_RSA_PSS on older OpenSSL versions, so we fall back + * on EVP_PKEY_bits in this case. + */ + max_saltlen = (EVP_PKEY_get0_RSA(pkey) != NULL ? + RSA_bits(EVP_PKEY_get0_RSA(pkey)) : + EVP_PKEY_bits(pkey)) / 8 - hlen - 2; + + switch (saltlen) { + case RSA_PSS_SALTLEN_DIGEST: + saltlen = hlen; + break; + case RSA_PSS_SALTLEN_AUTO: + case RSA_PSS_SALTLEN_MAX: + saltlen = max_saltlen; + break; + default: + break; + } + + if (saltlen > max_saltlen || saltlen < 0) { + put_error_data(sk_data, ERR_R_PASSED_INVALID_ARGUMENT, + "invalid salt length: %d", saltlen); + return 0; + } + + rc = sk_data->funcs->rsa_pss_sign(sk_data->key_blob, + sk_data->key_blob_size, + sig, siglen, tbs, tbslen, + md_type, mgf_md_type, saltlen, + sk_data->private, + sk_data->debug); + break; + + case EVP_PKEY_EC: + ec = EVP_PKEY_get0_EC_KEY(pkey); + if (ec == NULL) { + put_error_data(sk_data, ERR_R_INTERNAL_ERROR, + "EVP_PKEY_get0_EC_KEY failed"); + return 0; + } + + sig_sz = ECDSA_size(ec); + if (sig == NULL) { + *siglen = (size_t)sig_sz; + return 0; + } + if (*siglen < (size_t)sig_sz) { + put_error_data(sk_data, ERR_R_PASSED_INVALID_ARGUMENT, + "siglen too small"); + return 0; + } + + if (sk_data->funcs->ecdsa_sign == NULL) { + put_error_data(sk_data, ERR_R_PASSED_NULL_PARAMETER, + "no secure key sign function"); + return 0; + } + + rc = sk_data->funcs->ecdsa_sign(sk_data->key_blob, + sk_data->key_blob_size, + sig, siglen, + tbs, tbslen, md_type, + sk_data->private, + sk_data->debug); + break; + + default: + rc = -1; + put_error_data(sk_data, ERR_R_INTERNAL_ERROR, + "invalid PKEY type"); + } + + if (rc != 0) { + put_error_data(sk_data, ERR_R_OPERATION_FAIL, + "secure key sign operation failed"); + return 0; + } + + sk_debug_data(sk_data, "siglen: %lu", *siglen); + + return 1; +} + +static int sk_pkey_meth_decrypt_init(EVP_PKEY_CTX *ctx) +{ + int (*decrypt_init)(EVP_PKEY_CTX *ctx); + const EVP_PKEY_METHOD *default_meth; + struct sk_pkey_data *sk_data; + EVP_PKEY *pkey; + + if (!sk_pkey_meth_get_pkey_data(ctx, &pkey, &sk_data)) + return 0; + + if (sk_data == NULL) { + /* If no secure key is attached, call default implementation */ + default_meth = sk_pkey_meth_get_default_method( + EVP_PKEY_id(pkey)); + if (default_meth == NULL) + return 0; + + EVP_PKEY_meth_get_decrypt(default_meth, &decrypt_init, NULL); + if (decrypt_init == NULL) + return 1; + + return decrypt_init(ctx); + } + + sk_debug_data(sk_data, "sk_data: %p pkey: %p type: %d", sk_data, pkey, + EVP_PKEY_id(pkey)); + return 1; +} + +static int sk_pkey_meth_decrypt(EVP_PKEY_CTX *ctx, unsigned char *out, + size_t *outlen, const unsigned char *in, + size_t inlen) +{ + int (*decrypt)(EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen, + const unsigned char *in, size_t inlen); + int rc, pad_mode, label_len, md_type, mgfmd_type; + const EVP_PKEY_METHOD *default_meth; + struct sk_pkey_data *sk_data; + const EVP_MD *md, *mgf1md; + unsigned char *label; + EVP_PKEY *pkey; + + if (!sk_pkey_meth_get_pkey_data(ctx, &pkey, &sk_data)) + return 0; + + if (sk_data == NULL) { + /* If no secure key is attached, call default implementation */ + default_meth = sk_pkey_meth_get_default_method( + EVP_PKEY_id(pkey)); + if (default_meth == NULL) + return 0; + + EVP_PKEY_meth_get_decrypt(default_meth, NULL, &decrypt); + if (decrypt == NULL) + return 0; + + return decrypt(ctx, out, outlen, in, inlen); + } + + /* A secure key is attached, implement secure key decrypt */ + sk_debug_data(sk_data, "sk_data: %p pkey: %p type: %d", sk_data, pkey, + EVP_PKEY_id(pkey)); + + if (out == NULL) { + *outlen = EVP_PKEY_size(pkey); + sk_debug_data(sk_data, "outlen: %lu", *outlen); + return 1; + } + + if (*outlen < (size_t)EVP_PKEY_size(pkey)) { + put_error_data(sk_data, ERR_R_PASSED_INVALID_ARGUMENT, + "output buffer too small"); + return 0; + } + + *outlen = EVP_PKEY_size(pkey); + + if (sk_data->funcs == NULL) { + put_error_data(sk_data, ERR_R_PASSED_NULL_PARAMETER, + "no secure key funcs"); + return 0; + } + + switch (EVP_PKEY_id(pkey)) { + case EVP_PKEY_RSA: + if (EVP_PKEY_CTX_get_rsa_padding(ctx, &pad_mode) != 1) + return 0; + + switch (pad_mode) { + case RSA_PKCS1_OAEP_PADDING: + label_len = EVP_PKEY_CTX_get0_rsa_oaep_label(ctx, + &label); + if (label_len < 0) + return 0; + if (EVP_PKEY_CTX_get_rsa_oaep_md(ctx, &md) != 1) + return 0; + if (EVP_PKEY_CTX_get_rsa_mgf1_md(ctx, &mgf1md) != 1) + return 0; + + md_type = md != NULL ? EVP_MD_type(md) : NID_sha1; + mgfmd_type = mgf1md != NULL ? + EVP_MD_type(mgf1md) : md_type; + + if (sk_data->funcs->rsa_decrypt_oaep == NULL) { + put_error_data(sk_data, + ERR_R_PASSED_NULL_PARAMETER, + "no secure key decrypt function"); + return 0; + } + + rc = sk_data->funcs->rsa_decrypt_oaep( + sk_data->key_blob, + sk_data->key_blob_size, + out, outlen, in, inlen, + md_type, mgfmd_type, + label, label_len, + sk_data->private, + sk_data->debug); + break; + + default: + if (sk_data->funcs->rsa_decrypt == NULL) { + put_error_data(sk_data, + ERR_R_PASSED_NULL_PARAMETER, + "no secure key decrypt function"); + return 0; + } + + rc = sk_data->funcs->rsa_decrypt(sk_data->key_blob, + sk_data->key_blob_size, + out, outlen, in, inlen, + pad_mode, + sk_data->private, + sk_data->debug); + break; + } + break; + + case EVP_PKEY_RSA_PSS: + case EVP_PKEY_EC: + default: + /* encrypt not supported */ + put_error_data(sk_data, ERR_R_INTERNAL_ERROR, + "invalid PKEY type"); + return 0; + } + + if (rc != 0) { + put_error_data(sk_data, ERR_R_OPERATION_FAIL, + "secure key decrypt operation failed"); + return 0; + } + + sk_debug_data(sk_data, "outlen: %lu", *outlen); + + return 1; + +} + +static int sk_pkey_meth_derive_init(EVP_PKEY_CTX *ctx) +{ + int (*derive_init)(EVP_PKEY_CTX *ctx); + const EVP_PKEY_METHOD *default_meth; + struct sk_pkey_data *sk_data; + EVP_PKEY *pkey; + + if (!sk_pkey_meth_get_pkey_data(ctx, &pkey, &sk_data)) + return 0; + + if (sk_data == NULL) { + /* If no secure key is attached, call default implementation */ + default_meth = sk_pkey_meth_get_default_method( + EVP_PKEY_id(pkey)); + if (default_meth == NULL) + return 0; + + EVP_PKEY_meth_get_derive(default_meth, &derive_init, NULL); + if (derive_init == NULL) + return 1; + + return derive_init(ctx); + } + + /* We can not derive with a secure key attached */ + put_error_data(sk_data, ERR_R_OPERATION_FAIL, + "secure key derive not supported"); + return 0; +} + +static int sk_pkey_meth_derive(EVP_PKEY_CTX *ctx, unsigned char *key, + size_t *keylen) +{ + int (*derive)(EVP_PKEY_CTX *ctx, unsigned char *key, size_t *keylen); + const EVP_PKEY_METHOD *default_meth; + struct sk_pkey_data *sk_data; + EVP_PKEY *pkey; + + if (!sk_pkey_meth_get_pkey_data(ctx, &pkey, &sk_data)) + return 0; + + if (sk_data == NULL) { + /* If no secure key is attached, call default implementation */ + default_meth = sk_pkey_meth_get_default_method( + EVP_PKEY_id(pkey)); + if (default_meth == NULL) + return 0; + + EVP_PKEY_meth_get_derive(default_meth, NULL, &derive); + if (derive == NULL) + return 1; + + return derive(ctx, key, keylen); + } + + /* We can not derive with a secure key attached */ + put_error_data(sk_data, ERR_R_OPERATION_FAIL, + "secure key derive not supported"); + return 0; +} + +static int sk_pkey_meth_setup(int pkey_id, bool debug) +{ + const EVP_PKEY_METHOD *default_meth; + EVP_PKEY_METHOD *pkey_meth; + int flags = 0; + + sk_debug(debug, "pkey_id: %d", pkey_id); + + default_meth = sk_pkey_meth_get_default_method(pkey_id); + if (default_meth == NULL) { + sk_debug(debug, "ERROR: get_default_pkey_method failed"); + return -EIO; + } + + EVP_PKEY_meth_get0_info(NULL, &flags, default_meth); + pkey_meth = EVP_PKEY_meth_new(pkey_id, flags); + if (pkey_meth == NULL) { + sk_debug(debug, "ERROR: EVP_PKEY_meth_new failed"); + return -ENOMEM; + } + + /* Inherit all functions from the default PKEY method */ + EVP_PKEY_meth_copy(pkey_meth, default_meth); + + /* Override private key operations */ + EVP_PKEY_meth_set_sign(pkey_meth, sk_pkey_meth_sign_init, + sk_pkey_meth_sign); + EVP_PKEY_meth_set_decrypt(pkey_meth, sk_pkey_meth_decrypt_init, + sk_pkey_meth_decrypt); + EVP_PKEY_meth_set_derive(pkey_meth, sk_pkey_meth_derive_init, + sk_pkey_meth_derive); + + /* + * Do not provide signctx and digestsign functions, even if the default + * method would support them. If not available, it will fall back to + * regular sign function usage, which we do override. + */ + EVP_PKEY_meth_set_signctx(pkey_meth, NULL, NULL); + EVP_PKEY_meth_set_digestsign(pkey_meth, NULL); + + /* Add this as the preferred PKEY method */ + if (EVP_PKEY_meth_add0(pkey_meth) != 1) { + sk_debug(debug, "ERROR: EVP_PKEY_meth_add0 failed"); + return -EIO; + } + + return 0; +} + +static int sk_pkey_meth_cleanup(int pkey_id) +{ + const EVP_PKEY_METHOD *pkey_meth; + + pkey_meth = EVP_PKEY_meth_find(pkey_id); + if (pkey_meth == NULL) + return -ENOENT; + + if (EVP_PKEY_meth_remove(pkey_meth) != 1) + return -EIO; + + EVP_PKEY_meth_free((EVP_PKEY_METHOD *)pkey_meth); + + return 0; +} + +static int sk_pkey_meth_setup_pkey(EVP_PKEY *pkey, + const unsigned char *secure_key, + size_t secure_key_size, + const struct sk_funcs *funcs, + const void *private, bool debug) +{ + struct sk_pkey_data *data; + EC_KEY *ec; + RSA *rsa; + + if (pkey == NULL || secure_key == NULL || secure_key_size == 0 || + funcs == NULL) + return -EINVAL; + + sk_debug(debug, "pkey: %p type: %d", pkey, EVP_PKEY_id(pkey)); + + if (sk_pkey_data_ec_index < 0 || sk_pkey_data_rsa_index < 0) { + sk_debug(debug, "sk_pkey_meth support not initialized"); + return -ENODEV; + } + + data = OPENSSL_zalloc(sizeof(struct sk_pkey_data)); + if (data == NULL) { + sk_debug(debug, "OPENSSL_zalloc failed"); + return -ENOMEM; + } + + data->key_blob = OPENSSL_malloc(secure_key_size); + if (data->key_blob == NULL) { + sk_debug(debug, "OPENSSL_malloc failed"); + OPENSSL_free(data); + return -ENOMEM; + } + memcpy(data->key_blob, secure_key, secure_key_size); + data->key_blob_size = secure_key_size; + data->funcs = (struct sk_funcs *)funcs; + data->private = (void *)private; + data->debug = debug; + + switch (EVP_PKEY_id(pkey)) { + case EVP_PKEY_EC: + ec = EVP_PKEY_get0_EC_KEY(pkey); + if (ec == NULL) { + sk_debug(debug, "EVP_PKEY_get0_EC_KEY failed"); + return -EIO; + } + + if (!EC_KEY_set_ex_data(ec, sk_pkey_data_ec_index, data)) { + sk_debug(debug, "EC_KEY_set_ex_data failed"); + return -EIO; + } + break; + case EVP_PKEY_RSA: + case EVP_PKEY_RSA_PSS: + rsa = EVP_PKEY_get0_RSA(pkey); + if (rsa == NULL) { + sk_debug(debug, "EVP_PKEY_get0_RSA failed"); + return -EIO; + } + + if (!RSA_set_ex_data(rsa, sk_pkey_data_rsa_index, data)) { + sk_debug(debug, "RSA_set_ex_data failed"); + return -EIO; + } + break; + } + + return 0; +} + +/** + * Initializes the secure key support for OpenSSL. + * + * @param debug true to enable internal debugging + * + * @returns zero for success, a negative errno in case of an error: + * -ENOMEM: failed to allocate memory + * -EIO: OpenSSL failed for various reasons + */ +int SK_OPENSSL_init(bool debug) +{ + int rc; + + if (sk_pkey_data_ec_index < 0) + sk_pkey_data_ec_index = CRYPTO_get_ex_new_index( + CRYPTO_EX_INDEX_EC_KEY, 0, NULL, + NULL, sk_pkey_data_dup, sk_pkey_data_free); + if (sk_pkey_data_ec_index < 0) { + sk_debug(debug, "ERROR: CRYPTO_get_ex_new_index(EC) failed"); + return -EIO; + } + + if (sk_pkey_data_rsa_index < 0) + sk_pkey_data_rsa_index = CRYPTO_get_ex_new_index( + CRYPTO_EX_INDEX_RSA, 0, NULL, + NULL, sk_pkey_data_dup, sk_pkey_data_free); + if (sk_pkey_data_rsa_index < 0) { + sk_debug(debug, "ERROR: CRYPTO_get_ex_new_index(RSA) failed"); + return -EIO; + } + + sk_pkey_meth_default_method_ec = EVP_PKEY_meth_find(EVP_PKEY_EC); + if (sk_pkey_meth_default_method_ec == NULL) { + sk_debug(debug, "ERROR: EVP_PKEY_meth_find(EC) failed"); + return -EIO; + } + sk_pkey_meth_default_method_rsa = EVP_PKEY_meth_find(EVP_PKEY_RSA); + if (sk_pkey_meth_default_method_ec == NULL) { + sk_debug(debug, "ERROR: EVP_PKEY_meth_find(RSA) failed"); + return -EIO; + } + sk_pkey_meth_default_method_rsa_pss = + EVP_PKEY_meth_find(EVP_PKEY_RSA_PSS); + if (sk_pkey_meth_default_method_ec == NULL) { + sk_debug(debug, "ERROR: EVP_PKEY_meth_find(RSA-PSS) failed"); + return -EIO; + } + + rc = sk_pkey_meth_setup(EVP_PKEY_EC, debug); + if (rc != 0) { + sk_debug(debug, "ERROR: sk_pkey_meth_setup(EC) " + "failed"); + return rc; + } + rc = sk_pkey_meth_setup(EVP_PKEY_RSA, debug); + if (rc != 0) { + sk_debug(debug, "ERROR: sk_pkey_meth_setup(RSA) " + "failed"); + return rc; + } + rc = sk_pkey_meth_setup(EVP_PKEY_RSA_PSS, debug); + if (rc != 0) { + sk_debug(debug, "ERROR: sk_pkey_meth_setup(RSA-PSS) " + "failed"); + return rc; + } + + if (sk_pkey_meth_lib <= 0) + sk_pkey_meth_lib = ERR_get_next_error_library(); + if (sk_pkey_meth_lib <= 0) { + sk_debug(debug, "ERROR: ERR_get_next_error_library failed"); + return -EIO; + } + + sk_debug(debug, "sk_pkey_meth support initialized"); + + return 0; +} + +/** + * Terminate the secure key support for OpenSSL. + */ +void SK_OPENSSL_term(void) +{ + if (sk_pkey_data_ec_index >= 0) + CRYPTO_free_ex_index(CRYPTO_EX_INDEX_EC_KEY, + sk_pkey_data_ec_index); + sk_pkey_data_ec_index = -1; + + if (sk_pkey_data_rsa_index >= 0) + CRYPTO_free_ex_index(CRYPTO_EX_INDEX_RSA, + sk_pkey_data_rsa_index); + sk_pkey_data_rsa_index = -1; + + + sk_pkey_meth_cleanup(EVP_PKEY_EC); + sk_pkey_meth_cleanup(EVP_PKEY_RSA); + sk_pkey_meth_cleanup(EVP_PKEY_RSA_PSS); + + sk_pkey_meth_default_method_ec = NULL; + sk_pkey_meth_default_method_rsa = NULL; + sk_pkey_meth_default_method_rsa_pss = NULL; + + sk_pkey_meth_lib = -1; +} + +/** + * Converts an EC key given by the nid and the x and y coordinates into an + * OpenSSL PKEY and attaches the secure key together with secure key functions + * and private pointer to it. If no secure key is provided, a public EC key + * only PKEY is returned. + * + * @param secure_key the secure key blob. + * If NULL, a clear key PKEY is created. + * @param secure_key_size the size of the secure key blob (ignored if + * secure_key is NULL) + * @param nid the OpenSSL nid of the EC curve used + * @param prime_len the length of the prime in bytes. This is also the + * length of the x and y coordinates. + * @param x the x coordinate as big endian binary number in + * prime_len size + * @param y the y coordinate as big endian binary number in + * prime_len size + * @param sk_funcs the secure key functions to operate with the key. + * Ignored if secure_key is NULL, required otherwise. + * @param private a private pointer that is passed to the secure key + * functions (can be NULL) + * @param pkey On return: A PKEY containing the EC public key. + * @param debug true to enable internal debugging + * + * @returns zero for success, a negative errno in case of an error: + * -EINVAL: a function parameter is invalid + * -ENOMEM: failed to allocate memory + * -EIO: OpenSSL failed to generate the PKEY + * -ENOENT: OpenSSL does not know/support the curve (nid) + */ +int sk_openssl_get_pkey_ec(const unsigned char *secure_key, + size_t secure_key_size, int nid, size_t prime_len, + const unsigned char *x, const unsigned char *y, + const struct sk_funcs *sk_funcs, const void *private, + EVP_PKEY **pkey, bool debug) +{ + BIGNUM *bn_x = NULL, *bn_y = NULL; + EC_GROUP *group = NULL; + EC_KEY *ec = NULL; + int rc; + + if (pkey == NULL || x == NULL || y == NULL) + return -EINVAL; + if (secure_key != NULL && (secure_key_size == 0 || sk_funcs == NULL)) + return -EINVAL; + + *pkey = NULL; + + bn_x = BN_bin2bn(x, prime_len, NULL); + bn_y = BN_bin2bn(y, prime_len, NULL); + if (bn_x == NULL || bn_y == NULL) { + sk_debug(debug, "ERROR: BN_bin2bn failed"); + rc = -ENOMEM; + goto out; + } + + group = EC_GROUP_new_by_curve_name(nid); + if (group == NULL) { + sk_debug(debug, "ERROR: EC_GROUP_new_by_curve_name failed"); + rc = -ENOENT; + goto out; + } + + ec = EC_KEY_new(); + if (ec == NULL) { + sk_debug(debug, "ERROR: EC_KEY_new failed"); + rc = -ENOMEM; + goto out; + } + + rc = EC_KEY_set_group(ec, group); + if (rc != 1) { + sk_debug(debug, "ERROR: EC_KEY_set_group failed"); + rc = -EIO; + goto out; + } + + rc = EC_KEY_set_public_key_affine_coordinates(ec, bn_x, bn_y); + if (rc != 1) { + sk_debug(debug, + "ERROR: EC_KEY_set_public_key_affine_coordinates failed"); + rc = -EIO; + goto out; + } + + *pkey = EVP_PKEY_new(); + if (*pkey == NULL) { + sk_debug(debug, "ERROR: EVP_PKEY_new failed"); + rc = -ENOMEM; + goto out; + } + + rc = EVP_PKEY_assign_EC_KEY(*pkey, ec); + if (rc != 1) { + sk_debug(debug, "ERROR: EVP_PKEY_assign_EC_KEY failed"); + rc = -EIO; + goto out; + } + + if (secure_key != NULL) { + rc = sk_pkey_meth_setup_pkey(*pkey, secure_key, secure_key_size, + sk_funcs, private, debug); + if (rc != 0) { + sk_debug(debug, + "ERROR: sk_pkey_meth_setup_pkey failed"); + goto out; + } + } + + rc = 0; + sk_debug(debug, "pkey created: %p", *pkey); + +out: + if (bn_x != NULL) + BN_free(bn_x); + if (bn_y != NULL) + BN_free(bn_y); + if (group != NULL) + EC_GROUP_free(group); + if (rc != 0 && *pkey != NULL) { + EVP_PKEY_free(*pkey); + *pkey = NULL; + } + + return rc; +} + +/** + * Converts an RSA key given by the modulus and public exponent into an + * OpenSSL PKEY and attaches the secure key together with secure key functions + * and private pointer to it. If no secure key is provided, a public RSA key + * only PKEY is returned. + * + * @param secure_key the secure key blob. + * If NULL, a clear key PKEY is created. + * @param secure_key_size the size of the secure key blob (ignored if + * secure_key is NULL) + * @param modulus the modulus as big endian number + * @param modulus_length the length of the modulus in bytes + * @param pub_exp the public exponent as big endian number + * @param pub_exp_length the length of the public exponent in bytes + * @param pkey_type the PKEY type (EVP_PKEY_RSA or EVP_PKEY_RSA_PSS) + * @param sk_funcs the secure key functions to operate with the key. + * Ignored if secure_key is NULL, required otherwise. + * @param private a private pointer that is passed to the secure key + * functions (can be NULL) + * @param pkey On return: A PKEY containing the RSA public key. + * @param debug true to enable internal debugging + * + * @returns zero for success, a negative errno in case of an error: + * -EINVAL: a function parameter is invalid + * -ENOMEM: failed to allocate memory + * -EIO: OpenSSL failed to generate the PKEY + */ +int sk_openssl_get_pkey_rsa(const unsigned char *secure_key, + size_t secure_key_size, + const unsigned char *modulus, size_t modulus_length, + const unsigned char *pub_exp, size_t pub_exp_length, + int pkey_type, const struct sk_funcs *sk_funcs, + const void *private, EVP_PKEY **pkey, bool debug) +{ + BIGNUM *bn_modulus = NULL, *bn_pub_exp = NULL; + RSA *rsa; + int rc; + + if (pkey == NULL || modulus == NULL || pub_exp == NULL) + return -EINVAL; + if (secure_key != NULL && (secure_key_size == 0 || sk_funcs == NULL)) + return -EINVAL; + if (pkey_type != EVP_PKEY_RSA && pkey_type != EVP_PKEY_RSA_PSS) + return -EINVAL; + + *pkey = NULL; + + bn_modulus = BN_bin2bn(modulus, modulus_length, NULL); + bn_pub_exp = BN_bin2bn(pub_exp, pub_exp_length, NULL); + if (bn_modulus == NULL || bn_pub_exp == NULL) { + sk_debug(debug, "ERROR: BN_bin2bn failed"); + rc = -ENOMEM; + goto out; + } + + rsa = RSA_new(); + if (rsa == NULL) { + sk_debug(debug, "ERROR: RSA_new failed"); + rc = -ENOMEM; + goto out; + } + + rc = RSA_set0_key(rsa, bn_modulus, bn_pub_exp, NULL); + if (rc != 1) { + sk_debug(debug, "ERROR: RSA_set0_key failed"); + rc = -EIO; + goto out; + } + + *pkey = EVP_PKEY_new(); + if (*pkey == NULL) { + sk_debug(debug, "ERROR: EVP_PKEY_new failed"); + rc = -ENOMEM; + goto out; + } + + rc = EVP_PKEY_assign(*pkey, pkey_type, rsa); + if (rc != 1) { + sk_debug(debug, "ERROR: EVP_PKEY_assign failed"); + rc = -EIO; + goto out; + } + + if (secure_key != NULL) { + rc = sk_pkey_meth_setup_pkey(*pkey, secure_key, secure_key_size, + sk_funcs, private, debug); + if (rc != 0) { + sk_debug(debug, + "ERROR: sk_pkey_meth_setup_pkey failed"); + goto out; + } + } + + rc = 0; + sk_debug(debug, "pkey created: %p", *pkey); + +out: + if (rc != 0 && bn_modulus != NULL) + BN_free(bn_modulus); + if (rc != 0 && bn_pub_exp != NULL) + BN_free(bn_pub_exp); + return rc; +} + +/** + * Get the curve NID of the EC pkey + */ +int SK_OPENSSL_get_curve_from_ec_pkey(EVP_PKEY *pkey) +{ + if (EVP_PKEY_id(pkey) != EVP_PKEY_EC) + return NID_undef; + + return EC_GROUP_get_curve_name(EC_KEY_get0_group( + EVP_PKEY_get0_EC_KEY(pkey))); +} + +#endif diff --git a/libseckey/sk_provider.c b/libseckey/sk_provider.c new file mode 100644 index 00000000..10f56c08 --- /dev/null +++ b/libseckey/sk_provider.c @@ -0,0 +1,5079 @@ +/* + * libseckey - Secure key library + * + * Copyright IBM Corp. 2021 + * + * s390-tools is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "lib/zt_common.h" + +#include "libseckey/sk_openssl.h" +#include "libseckey/sk_utilities.h" + +/* + * This source file is only used with OpenSSL >= 3.0 + */ +#if OPENSSL_VERSION_PREREQ(3, 0) + +#include +#include +#include +#include +#include +#include "openssl/param_build.h" +#include + +static OSSL_LIB_CTX *sk_prov_securekey_libctx; +static OSSL_LIB_CTX *sk_prov_previous_libctx; +static OSSL_PROVIDER *sk_prov_securekey_provider; +static OSSL_PROVIDER *sk_prov_default_provider; + +#define SK_PROV_NAME "securekey" +#define SK_PROV_DESCRIPTION "Secure key provider" +#define SK_PROV_VERSION "1.0" + +#define SK_PROV_RSA_DEFAULT_MD "SHA-1" +#define SK_PROV_EC_DEFAULT_MD_NID "SHA-1" +#define SK_PROV_PKEY_PARAM_SK_BLOB "sk-blob" +#define SK_PROV_PKEY_PARAM_SK_FUNCS "sk-funcs" +#define SK_PROV_PKEY_PARAM_SK_PRIVATE "sk-private" + +#define SK_CONF_CACHED_PARAMS_OP_KEY_EXPORT 0 /* 16 possible selections */ +#define SK_CONF_CACHED_PARAMS_OP_KEY_IMPORT 16 /* 16 possible selections */ +#define SK_CONF_CACHED_PARAMS_OP_KEY_GET 32 +#define SK_CONF_CACHED_PARAMS_OP_KEY_SET 33 +#define SK_CONF_CACHED_PARAMS_OP_COUNT 34 + +#define SK_CONF_CACHED_PARAMS_ALGO_RSA 0 +#define SK_CONF_CACHED_PARAMS_ALGO_RSA_PSS 1 +#define SK_CONF_CACHED_PARAMS_ALGO_EC 2 +#define SK_CONF_CACHED_PARAMS_ALGO_COUNT 3 + +#define SK_PROV_CACHED_PARAMS_COUNT \ + (SK_CONF_CACHED_PARAMS_ALGO_COUNT * SK_CONF_CACHED_PARAMS_OP_COUNT) + +struct sk_prov_ctx { + const OSSL_CORE_HANDLE *handle; + OSSL_FUNC_core_get_libctx_fn *c_get_libctx; + OSSL_FUNC_core_new_error_fn *c_new_error; + OSSL_FUNC_core_set_error_debug_fn *c_set_error_debug; + OSSL_FUNC_core_vset_error_fn *c_vset_error; + OSSL_PROVIDER *default_provider; + void *default_provctx; + const OSSL_ALGORITHM *cached_default_algos[OSSL_OP__HIGHEST]; + const OSSL_PARAM *cached_parms[SK_PROV_CACHED_PARAMS_COUNT]; + bool debug; +}; + +struct sk_prov_key { + struct sk_prov_ctx *provctx; + int type; /* EVP_PKEY_xxx types */ + void *default_key; /* shadow key of default provider */ + unsigned char *secure_key; + size_t secure_key_size; + struct sk_funcs *funcs; + void *private; + unsigned int ref_count; +}; + +struct sk_prov_op_ctx { + struct sk_prov_ctx *provctx; + int type; /* EVP_PKEY_xxx types */ + const char *propq; + void *default_op_ctx; /* shadow context of default provider */ + void (*default_op_ctx_free)(void *default_op_ctx); + struct sk_prov_key *key; + int operation; + int (*sign_fn)(struct sk_prov_op_ctx *ctx, unsigned char *sig, + size_t *siglen, size_t sigsize, const unsigned char *tbs, + size_t tbslen); + EVP_MD_CTX *mdctx; + EVP_MD *md; +}; + +#define sk_debug_ctx(ctx, fmt...) sk_debug(ctx->debug, fmt) +#define sk_debug_key(key, fmt...) sk_debug(key->provctx->debug, fmt) +#define sk_debug_op_ctx(ctx, fmt...) sk_debug(ctx->provctx->debug, fmt) + +int sk_openssl_get_pkey_ec(const unsigned char *secure_key, + size_t secure_key_size, int nid, size_t prime_len, + const unsigned char *x, const unsigned char *y, + const struct sk_funcs *sk_funcs, const void *private, + EVP_PKEY **pkey, bool debug); + +int sk_openssl_get_pkey_rsa(const unsigned char *secure_key, + size_t secure_key_size, + const unsigned char *modulus, size_t modulus_length, + const unsigned char *pub_exp, size_t pub_exp_length, + int pkey_type, const struct sk_funcs *sk_funcs, + const void *private, EVP_PKEY **pkey, bool debug); + +#define SK_PROV_ERR_INTERNAL_ERROR 1 +#define SK_PROV_ERR_MALLOC_FAILED 2 +#define SK_PROV_ERR_INVALID_PARAM 3 +#define SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING 4 +#define SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED 5 +#define SK_PROV_ERR_OPRATION_NOT_INITIALIZED 6 +#define SK_PROV_ERR_MISSING_PARAMETER 7 +#define SK_PROV_ERR_INVALID_PADDING 8 +#define SK_PROV_ERR_INVALID_MD 9 +#define SK_PROV_ERR_INVALID_SALTLEN 10 +#define SK_PROV_ERR_SECURE_KEY_FUNC_FAILED 11 + +static const OSSL_ITEM sk_prov_reason_strings[] = { + { SK_PROV_ERR_INTERNAL_ERROR, "Internal error" }, + { SK_PROV_ERR_MALLOC_FAILED, "Memory allocation failed" }, + { SK_PROV_ERR_INVALID_PARAM, "Invalid parameter encountered" }, + { SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "A function inherited from default provider is missing" }, + { SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "A function inherited from default provider has failed" }, + { SK_PROV_ERR_OPRATION_NOT_INITIALIZED, + "An operation context has not been initialized" }, + { SK_PROV_ERR_MISSING_PARAMETER, + "A parameter of a key or a context is missing" }, + { SK_PROV_ERR_INVALID_PADDING, + "An invalid or unknown padding is used" }, + { SK_PROV_ERR_INVALID_MD, "An invalid or unknown digest is used" }, + { SK_PROV_ERR_INVALID_SALTLEN, "An invalid salt length is used" }, + { SK_PROV_ERR_SECURE_KEY_FUNC_FAILED, + "A secure key function has failed" }, + {0, NULL } +}; + +static void sk_prov_put_error(struct sk_prov_ctx *provctx, int err, + const char *file, int line, const char *func, + char *fmt, ...) +{ + va_list ap; + + if (provctx == NULL) + return; + + va_start(ap, fmt); + provctx->c_new_error(provctx->handle); + provctx->c_set_error_debug(provctx->handle, file, line, func); + provctx->c_vset_error(provctx->handle, err, fmt, ap); + va_end(ap); +} + +#define put_error_ctx(ctx, err, fmt...) \ + do { \ + sk_debug_ctx(ctx, "ERROR: "fmt); \ + sk_prov_put_error(ctx, err, __FILE__, \ + __LINE__, __func__, fmt); \ + } while (0) +#define put_error_key(key, err, fmt...) \ + put_error_ctx(key->provctx, err, fmt) +#define put_error_op_ctx(ctx, err, fmt...) \ + put_error_ctx(ctx->provctx, err, fmt) + +static void sk_prov_keymgmt_upref(struct sk_prov_key *key); +static struct sk_prov_key *sk_prov_keymgmt_new(struct sk_prov_ctx *provctx, + int type); +static void sk_prov_keymgmt_free(struct sk_prov_key *key); +static int sk_prov_keymgmt_get_bits(struct sk_prov_key *key); + +typedef void (*func_t)(void); + +static func_t sk_prov_get_default_func(struct sk_prov_ctx *provctx, + int operation_id, + const char *algorithm, + int function_id) +{ + const OSSL_ALGORITHM *default_algos, *algs; + const OSSL_DISPATCH *default_impl, *impl; + int algolen = strlen(algorithm); + int no_cache = 0, query = 0; + func_t func = NULL; + const char *found; + + if (provctx == NULL || provctx->default_provider == NULL || + operation_id <= 0 || operation_id > OSSL_OP__HIGHEST) + return NULL; + + sk_debug_ctx(provctx, "operation_id: %d, algo: %s, func: %d", + operation_id, algorithm, function_id); + + default_algos = provctx->cached_default_algos[operation_id]; + if (default_algos == NULL) { + default_algos = OSSL_PROVIDER_query_operation( + provctx->default_provider, + operation_id, &no_cache); + query = 1; + } + + for (algs = default_algos; algs != NULL && + algs->algorithm_names != NULL; algs++) { + found = strcasestr(algs->algorithm_names, algorithm); + if (found == NULL) + continue; + if (found[algolen] != '\0' && found[algolen] != ':') + continue; + if (found != algs->algorithm_names && found[-1] != ':') + continue; + + default_impl = algs->implementation; + for (impl = default_impl; impl->function_id != 0; impl++) { + if (impl->function_id == function_id) { + func = impl->function; + break; + } + } + break; + } + + if (query == 1 && default_algos != NULL) + OSSL_PROVIDER_unquery_operation(provctx->default_provider, + operation_id, + default_algos); + + if (no_cache == 0 && + provctx->cached_default_algos[operation_id] == NULL) + provctx->cached_default_algos[operation_id] = default_algos; + + sk_debug_ctx(provctx, "func: %p", func); + return func; +} + +static const char *sk_prov_get_algo(int pkey_type, bool sign) +{ + switch (pkey_type) { + case EVP_PKEY_RSA: + return "RSA"; + case EVP_PKEY_RSA_PSS: + return "RSA-PSS"; + case EVP_PKEY_EC: + if (sign) + return "ECDSA"; + else + return "EC"; + default: + return NULL; + } +} + +static func_t sk_prov_get_default_keymgmt_func(struct sk_prov_ctx *provctx, + int pkey_type, int function_id) +{ + return sk_prov_get_default_func(provctx, OSSL_OP_KEYMGMT, + sk_prov_get_algo(pkey_type, false), + function_id); +} + +static func_t sk_prov_get_default_keyexch_func(struct sk_prov_ctx *provctx, + int function_id) +{ + return sk_prov_get_default_func(provctx, OSSL_OP_KEYEXCH, "ECDH", + function_id); +} + +static func_t sk_prov_get_default_asym_func(struct sk_prov_ctx *provctx, + int pkey_type, int function_id) +{ + return sk_prov_get_default_func(provctx, OSSL_OP_ASYM_CIPHER, + sk_prov_get_algo(pkey_type, false), + function_id); +} + +static func_t sk_prov_get_default_sign_func(struct sk_prov_ctx *provctx, + int pkey_type, int function_id) +{ + return sk_prov_get_default_func(provctx, OSSL_OP_SIGNATURE, + sk_prov_get_algo(pkey_type, true), + function_id); +} + +static int sk_prov_get_cached_params_index(int pkey_type, int operation, + int selection) +{ + int ofs = 0; + + if (operation < 0 || operation >= SK_CONF_CACHED_PARAMS_OP_COUNT) + return -1; + + switch (operation) { + case SK_CONF_CACHED_PARAMS_OP_KEY_EXPORT: + case SK_CONF_CACHED_PARAMS_OP_KEY_IMPORT: + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + ofs += 1; + if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) + ofs += 2; + if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) + ofs += 4; + if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0) + ofs += 8; + break; + } + + switch (pkey_type) { + case EVP_PKEY_RSA: + return (SK_CONF_CACHED_PARAMS_ALGO_RSA * + SK_CONF_CACHED_PARAMS_OP_COUNT) + + operation + ofs; + case EVP_PKEY_RSA_PSS: + return (SK_CONF_CACHED_PARAMS_ALGO_RSA_PSS * + SK_CONF_CACHED_PARAMS_OP_COUNT) + + operation + ofs; + case EVP_PKEY_EC: + return (SK_CONF_CACHED_PARAMS_ALGO_EC * + SK_CONF_CACHED_PARAMS_OP_COUNT) + + operation + ofs; + } + + return -1; +} + +static const OSSL_PARAM *sk_prov_get_cached_params(struct sk_prov_ctx *provctx, + int pkey_type, int operation, + int selection) +{ + int index; + + sk_debug_ctx(provctx, "pkey_type: %d operation: %d selection: %x", + pkey_type, operation, selection); + + index = sk_prov_get_cached_params_index(pkey_type, operation, + selection); + if (index < 0) { + put_error_ctx(provctx, SK_PROV_ERR_INTERNAL_ERROR, + "Invalid type, operation or selection"); + return NULL; + } + + return provctx->cached_parms[index]; +} + +static const OSSL_PARAM *sk_prov_cached_params_build( + struct sk_prov_ctx *provctx, + int pkey_type, + int operation, + int selection, + const OSSL_PARAM *params1, + const OSSL_PARAM *params2) +{ + int index, count = 0, i, k = 0; + OSSL_PARAM *params; + + sk_debug_ctx(provctx, "pkey_type: %d operation: %d selection: %x", + pkey_type, operation, selection); + + index = sk_prov_get_cached_params_index(pkey_type, operation, + selection); + if (index < 0) { + put_error_ctx(provctx, SK_PROV_ERR_INTERNAL_ERROR, + "Invalid type, operation or selection"); + return NULL; + } + + if (provctx->cached_parms[index] != NULL) { + OPENSSL_free((void *)provctx->cached_parms[index]); + provctx->cached_parms[index] = NULL; + } + + for (i = 0; params1 != NULL && params1[i].key != NULL; i++, count++) + ; + for (i = 0; params2 != NULL && params2[i].key != NULL; i++, count++) + ; + sk_debug_ctx(provctx, "count: %d", count); + + count++; /* End marker */ + + params = OPENSSL_zalloc(sizeof(OSSL_PARAM) * count); + if (params == NULL) { + put_error_ctx(provctx, SK_PROV_ERR_MALLOC_FAILED, + "OPENSSL_zalloc failed"); + return NULL; + } + + for (i = 0; params1 != NULL && params1[i].key != NULL; i++, k++) { + params[k] = params1[i]; + sk_debug_ctx(provctx, "param %d: %s", k, params[k].key); + } + for (i = 0; params2 != NULL && params2[i].key != NULL; i++, k++) { + params[k] = params2[i]; + sk_debug_ctx(provctx, "param %d: %s", k, params[k].key); + } + params[k] = OSSL_PARAM_construct_end(); + + provctx->cached_parms[index] = params; + return provctx->cached_parms[index]; +} + +static struct sk_prov_op_ctx *sk_prov_op_newctx(struct sk_prov_ctx *provctx, + const char *propq, + int type) +{ + struct sk_prov_op_ctx *ctx; + + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "propq: %s type: %d", + propq != NULL ? propq : "", type); + + ctx = OPENSSL_zalloc(sizeof(struct sk_prov_op_ctx)); + if (ctx == NULL) { + put_error_ctx(provctx, SK_PROV_ERR_MALLOC_FAILED, + "OPENSSL_zalloc failed"); + return NULL; + } + + ctx->provctx = provctx; + ctx->type = type; + + if (propq != NULL) { + ctx->propq = OPENSSL_strdup(propq); + if (ctx->propq == NULL) { + put_error_ctx(provctx, SK_PROV_ERR_MALLOC_FAILED, + "OPENSSL_strdup failed"); + OPENSSL_free(ctx); + return NULL; + } + } + + sk_debug_ctx(provctx, "ctx: %p", ctx); + return ctx; +} + +static void sk_prov_op_freectx(struct sk_prov_op_ctx *ctx) +{ + if (ctx == NULL) + return; + + sk_debug_op_ctx(ctx, "ctx: %p", ctx); + + if (ctx->default_op_ctx != NULL && ctx->default_op_ctx_free != NULL) + ctx->default_op_ctx_free(ctx->default_op_ctx); + + if (ctx->key != NULL) + sk_prov_keymgmt_free(ctx->key); + + if (ctx->propq != NULL) + OPENSSL_free((void *)ctx->propq); + + if (ctx->mdctx != NULL) + EVP_MD_CTX_free(ctx->mdctx); + if (ctx->md != NULL) + EVP_MD_free(ctx->md); + + OPENSSL_free(ctx); +} + +static struct sk_prov_op_ctx *sk_prov_op_dupctx(struct sk_prov_op_ctx *ctx) +{ + struct sk_prov_op_ctx *new_ctx; + + if (ctx == NULL) + return NULL; + + sk_debug_op_ctx(ctx, "ctx: %p", ctx); + + new_ctx = sk_prov_op_newctx(ctx->provctx, ctx->propq, ctx->type); + if (new_ctx == NULL) { + sk_debug_op_ctx(ctx, "ERROR: sk_prov_op_newctx failed"); + return NULL; + } + + new_ctx->operation = ctx->operation; + new_ctx->default_op_ctx_free = ctx->default_op_ctx_free; + new_ctx->sign_fn = ctx->sign_fn; + + if (ctx->mdctx != NULL) { + new_ctx->mdctx = EVP_MD_CTX_new(); + if (new_ctx->mdctx == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_MALLOC_FAILED, + "EVP_MD_CTX_new failed"); + sk_prov_op_freectx(new_ctx); + return NULL; + } + + if (!EVP_MD_CTX_copy_ex(new_ctx->mdctx, ctx->mdctx)) { + sk_debug_op_ctx(ctx, + "ERROR: EVP_MD_CTX_copy_ex failed"); + sk_prov_op_freectx(new_ctx); + return NULL; + } + }; + + if (ctx->md != NULL) { + new_ctx->md = ctx->md; + EVP_MD_up_ref(ctx->md); + } + + if (ctx->key != NULL) { + new_ctx->key = ctx->key; + sk_prov_keymgmt_upref(ctx->key); + } + + sk_debug_op_ctx(ctx, "new_ctx: %p", new_ctx); + return new_ctx; +} + +static int sk_prov_op_init(struct sk_prov_op_ctx *ctx, struct sk_prov_key *key, + int operation) +{ + if (ctx == NULL) + return 0; + + sk_debug_op_ctx(ctx, "ctx: %p key: %p operation: %d", ctx, key, + operation); + + if (key != NULL) { + switch (ctx->type) { + case EVP_PKEY_RSA: + case EVP_PKEY_RSA_PSS: + if (key->type != EVP_PKEY_RSA && + key->type != EVP_PKEY_RSA_PSS) { + put_error_op_ctx(ctx, + SK_PROV_ERR_INTERNAL_ERROR, + "key type mismatch: ctx type: " + "%d key type: %d", + ctx->type, key->type); + return 0; + } + break; + case EVP_PKEY_EC: + if (key->type != EVP_PKEY_EC) { + put_error_op_ctx(ctx, + SK_PROV_ERR_INTERNAL_ERROR, + "key type mismatch: ctx type: " + "%d key type: %d", + ctx->type, key->type); + return 0; + } + break; + default: + put_error_op_ctx(ctx, SK_PROV_ERR_INTERNAL_ERROR, + "key type unknown: ctx type: " + "%d key type: %d", + ctx->type, key->type); + return 0; + } + } + + if (key != NULL) + sk_prov_keymgmt_upref(key); + + if (ctx->key != NULL) + sk_prov_keymgmt_free(ctx->key); + + ctx->key = key; + ctx->operation = operation; + + return 1; +} + +static struct sk_prov_op_ctx *sk_prov_asym_op_newctx( + struct sk_prov_ctx *provctx, + int pkey_type) +{ + OSSL_FUNC_asym_cipher_freectx_fn *default_freectx_fn; + OSSL_FUNC_asym_cipher_newctx_fn *default_newctx_fn; + struct sk_prov_op_ctx *ctx; + + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "pkey_type: %d", pkey_type); + + default_newctx_fn = (OSSL_FUNC_asym_cipher_newctx_fn *) + sk_prov_get_default_asym_func(provctx, pkey_type, + OSSL_FUNC_ASYM_CIPHER_NEWCTX); + if (default_newctx_fn == NULL) { + put_error_ctx(provctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default newctx_fn"); + return NULL; + } + + default_freectx_fn = (OSSL_FUNC_asym_cipher_freectx_fn *) + sk_prov_get_default_asym_func(provctx, pkey_type, + OSSL_FUNC_ASYM_CIPHER_FREECTX); + if (default_freectx_fn == NULL) { + put_error_ctx(provctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default freectx_fn"); + return NULL; + } + + ctx = sk_prov_op_newctx(provctx, NULL, pkey_type); + if (ctx == NULL) { + sk_debug_ctx(provctx, "ERROR: sk_prov_op_newctx failed"); + return NULL; + } + + ctx->default_op_ctx = default_newctx_fn(provctx->default_provctx); + if (ctx->default_op_ctx == NULL) { + put_error_ctx(provctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_newctx_fn failed"); + sk_prov_op_freectx(ctx); + return NULL; + } + ctx->default_op_ctx_free = default_freectx_fn; + + sk_debug_ctx(provctx, "ctx: %p", ctx); + return ctx; +} + +static struct sk_prov_op_ctx *sk_prov_asym_op_dupctx(struct sk_prov_op_ctx *ctx) +{ + OSSL_FUNC_asym_cipher_dupctx_fn *default_dupctx_fn; + struct sk_prov_op_ctx *new_ctx; + + if (ctx == NULL) + return NULL; + + sk_debug_op_ctx(ctx, "ctx: %p", ctx); + + default_dupctx_fn = (OSSL_FUNC_asym_cipher_dupctx_fn *) + sk_prov_get_default_asym_func(ctx->provctx, + ctx->type, OSSL_FUNC_ASYM_CIPHER_DUPCTX); + if (default_dupctx_fn == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default dupctx_fn"); + return NULL; + } + + new_ctx = sk_prov_op_dupctx(ctx); + if (new_ctx == NULL) { + sk_debug_op_ctx(ctx, "ERROR: sk_prov_op_dupctx failed"); + return NULL; + } + + new_ctx->default_op_ctx = default_dupctx_fn(ctx->default_op_ctx); + if (new_ctx->default_op_ctx == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_dupctx_fn failed"); + sk_prov_op_freectx(new_ctx); + return NULL; + } + + sk_debug_op_ctx(ctx, "new_ctx: %p", new_ctx); + return new_ctx; +} + +static int sk_prov_asym_op_get_ctx_params(struct sk_prov_op_ctx *ctx, + OSSL_PARAM params[]) +{ + OSSL_FUNC_asym_cipher_get_ctx_params_fn *default_get_params_fn; + const OSSL_PARAM *p; + + if (ctx == NULL) + return 0; + + sk_debug_op_ctx(ctx, "ctx: %p", ctx); + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_op_ctx(ctx, "param: %s", p->key); + + default_get_params_fn = (OSSL_FUNC_asym_cipher_get_ctx_params_fn *) + sk_prov_get_default_asym_func(ctx->provctx, + ctx->type, + OSSL_FUNC_ASYM_CIPHER_GET_CTX_PARAMS); + + /* default_get_params_fn is optional */ + if (default_get_params_fn != NULL) { + if (!default_get_params_fn(ctx->default_op_ctx, params)) { + put_error_op_ctx(ctx, + SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_get_params_fn failed"); + return 0; + } + } + + return 1; +} + +static int sk_prov_asym_op_set_ctx_params(struct sk_prov_op_ctx *ctx, + const OSSL_PARAM params[]) +{ + OSSL_FUNC_asym_cipher_set_ctx_params_fn *default_set_params_fn; + const OSSL_PARAM *p; + + if (ctx == NULL) + return 0; + + sk_debug_op_ctx(ctx, "ctx: %p", ctx); + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_op_ctx(ctx, "param: %s", p->key); + + default_set_params_fn = (OSSL_FUNC_asym_cipher_set_ctx_params_fn *) + sk_prov_get_default_asym_func(ctx->provctx, + ctx->type, + OSSL_FUNC_ASYM_CIPHER_SET_CTX_PARAMS); + + /* default_set_params_fn is optional */ + if (default_set_params_fn != NULL) { + if (!default_set_params_fn(ctx->default_op_ctx, params)) { + put_error_op_ctx(ctx, + SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_set_params_fn failed"); + return 0; + } + } + + return 1; +} + +static const OSSL_PARAM *sk_prov_asym_op_gettable_ctx_params( + struct sk_prov_op_ctx *ctx, + struct sk_prov_ctx *provctx, int pkey_type) +{ + OSSL_FUNC_asym_cipher_gettable_ctx_params_fn + *default_gettable_params_fn; + const OSSL_PARAM *params = NULL, *p; + + if (ctx == NULL || provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "pkey_type: %d", pkey_type); + + default_gettable_params_fn = + (OSSL_FUNC_asym_cipher_gettable_ctx_params_fn *) + sk_prov_get_default_asym_func(provctx, pkey_type, + OSSL_FUNC_ASYM_CIPHER_GETTABLE_CTX_PARAMS); + + /* default_gettable_params_fn is optional */ + if (default_gettable_params_fn != NULL) + params = default_gettable_params_fn(ctx->default_op_ctx, + provctx->default_provctx); + + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_ctx(provctx, "param: %s", p->key); + + return params; +} + +static const OSSL_PARAM *sk_prov_asym_op_settable_ctx_params( + struct sk_prov_op_ctx *ctx, + struct sk_prov_ctx *provctx, int pkey_type) +{ + OSSL_FUNC_asym_cipher_settable_ctx_params_fn + *default_settable_params_fn; + const OSSL_PARAM *params = NULL, *p; + + if (ctx == NULL || provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "pkey_type: %d", pkey_type); + + default_settable_params_fn = + (OSSL_FUNC_asym_cipher_settable_ctx_params_fn *) + sk_prov_get_default_asym_func(provctx, pkey_type, + OSSL_FUNC_ASYM_CIPHER_SETTABLE_CTX_PARAMS); + + /* default_settable_params_fn is optional */ + if (default_settable_params_fn != NULL) + params = default_settable_params_fn(ctx->default_op_ctx, + provctx->default_provctx); + + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_ctx(provctx, "param: %s", p->key); + + return params; +} + +static EVP_MD *sk_prov_asym_op_get_oaep_md(struct sk_prov_op_ctx *ctx) +{ + char mdprops[256], mdname[50]; + OSSL_PARAM ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST, + &mdname, sizeof(mdname)), + OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST_PROPS, + &mdprops, sizeof(mdprops)), + OSSL_PARAM_END + }; + EVP_MD *md; + + sk_debug_op_ctx(ctx, "ctx: %p", ctx); + + if (!sk_prov_asym_op_get_ctx_params(ctx, ctx_params) || + !OSSL_PARAM_modified(&ctx_params[0]) || + !OSSL_PARAM_modified(&ctx_params[1])) { + sk_debug_op_ctx(ctx, "sk_prov_asym_op_get_ctx_params failed"); + if (ctx->md != NULL) { + sk_debug_op_ctx(ctx, "use digest from context: %s", + EVP_MD_name(ctx->md)); + EVP_MD_up_ref(ctx->md); + return ctx->md; + } + + sk_debug_op_ctx(ctx, "use default"); + strcpy(mdname, SK_PROV_RSA_DEFAULT_MD); + strcpy(mdprops, ""); + } + + md = EVP_MD_fetch((OSSL_LIB_CTX *)ctx->provctx->c_get_libctx( + ctx->provctx->handle), + mdname, mdprops[0] != '\0' ? mdprops : ctx->propq); + if (md == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, + "EVP_MD_fetch failed to fetch '%s' using " + "property query '%s'", mdname, + mdprops[0] != '\0' ? mdprops : + ctx->propq != NULL ? ctx->propq : ""); + return NULL; + } + + sk_debug_op_ctx(ctx, "md: %s", EVP_MD_name(md)); + return md; +} + +static EVP_MD *sk_prov_asym_op_get_mgf_md(struct sk_prov_op_ctx *ctx) +{ + char mdprops[256], mdname[50]; + OSSL_PARAM ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST, + &mdname, sizeof(mdname)), + OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST_PROPS, + &mdprops, sizeof(mdprops)), + OSSL_PARAM_END + }; + EVP_MD *md; + + sk_debug_op_ctx(ctx, "ctx: %p", ctx); + + if (!sk_prov_asym_op_get_ctx_params(ctx, ctx_params) || + !OSSL_PARAM_modified(&ctx_params[0]) || + !OSSL_PARAM_modified(&ctx_params[1])) { + sk_debug_op_ctx(ctx, "sk_prov_asym_op_get_ctx_params failed, " + "using oaep digest"); + return sk_prov_asym_op_get_oaep_md(ctx); + } + + md = EVP_MD_fetch((OSSL_LIB_CTX *)ctx->provctx->c_get_libctx( + ctx->provctx->handle), + mdname, mdprops[0] != '\0' ? mdprops : ctx->propq); + if (md == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, + "EVP_MD_fetch failed to fetch '%s' using " + "property query '%s'", mdname, + mdprops[0] != '\0' ? mdprops : + ctx->propq != NULL ? ctx->propq : ""); + return NULL; + } + + sk_debug_op_ctx(ctx, "md: %s", EVP_MD_name(md)); + return md; +} + +static int sk_prov_asym_op_get_oaep_label(struct sk_prov_op_ctx *ctx, + unsigned char **label) +{ + OSSL_PARAM ctx_params[] = { + OSSL_PARAM_octet_ptr(OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL, + label, 0), + OSSL_PARAM_END + }; + int oaep_label_len; + + + sk_debug_op_ctx(ctx, "ctx: %p", ctx); + + if (!sk_prov_asym_op_get_ctx_params(ctx, ctx_params) || + !OSSL_PARAM_modified(&ctx_params[0])) { + put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, + "sk_prov_asym_op_get_ctx_params failed to " + "get OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL"); + return -1; + } + + oaep_label_len = ctx_params[0].return_size; + sk_debug_op_ctx(ctx, "oaep_label: %p oaep_label_len: %d", *label, + oaep_label_len); + + return oaep_label_len; +} + +static int sk_prov_parse_padding(const char *padding) +{ + if (strcmp(padding, OSSL_PKEY_RSA_PAD_MODE_NONE) == 0) + return RSA_NO_PADDING; + if (strcmp(padding, OSSL_PKEY_RSA_PAD_MODE_PKCSV15) == 0) + return RSA_PKCS1_PADDING; + if (strcmp(padding, OSSL_PKEY_RSA_PAD_MODE_OAEP) == 0) + return RSA_PKCS1_OAEP_PADDING; + if (strcmp(padding, OSSL_PKEY_RSA_PAD_MODE_X931) == 0) + return RSA_X931_PADDING; + if (strcmp(padding, OSSL_PKEY_RSA_PAD_MODE_PSS) == 0) + return RSA_PKCS1_PSS_PADDING; + + return -1; +} + +static int sk_prov_asym_op_get_padding(struct sk_prov_op_ctx *ctx) +{ + char padding[50]; + OSSL_PARAM ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_PAD_MODE, + &padding, sizeof(padding)), + OSSL_PARAM_END + }; + + sk_debug_op_ctx(ctx, "ctx: %p", ctx); + + if (!sk_prov_asym_op_get_ctx_params(ctx, ctx_params) || + !OSSL_PARAM_modified(&ctx_params[0])) { + put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, + "sk_prov_asym_op_get_ctx_params failed to " + "get OSSL_PKEY_PARAM_PAD_MODE"); + return -1; + } + + sk_debug_op_ctx(ctx, "padding: %s", padding); + + return sk_prov_parse_padding(padding); +} + +static int sk_prov_asym_op_encrypt_init(struct sk_prov_op_ctx *ctx, + struct sk_prov_key *key, + const OSSL_PARAM params[]) +{ + OSSL_FUNC_asym_cipher_encrypt_init_fn *default_encrypt_init_fn; + const OSSL_PARAM *p; + + if (ctx == NULL || key == NULL) + return 0; + + sk_debug_op_ctx(ctx, "ctx: %p key: %p", ctx, key); + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_op_ctx(ctx, "param: %s", p->key); + + default_encrypt_init_fn = (OSSL_FUNC_asym_cipher_encrypt_init_fn *) + sk_prov_get_default_asym_func(ctx->provctx, + ctx->type, + OSSL_FUNC_ASYM_CIPHER_ENCRYPT_INIT); + if (default_encrypt_init_fn == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default encrypt_init_fn"); + return 0; + } + + if (!sk_prov_op_init(ctx, key, EVP_PKEY_OP_ENCRYPT)) { + sk_debug_op_ctx(ctx, "ERROR: sk_prov_op_init failed"); + return 0; + } + + if (!default_encrypt_init_fn(ctx->default_op_ctx, key->default_key, + params)) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_encrypt_init_fn failed"); + return 0; + } + + return 1; +} + +static int sk_prov_asym_op_decrypt_init(struct sk_prov_op_ctx *ctx, + struct sk_prov_key *key, + const OSSL_PARAM params[]) +{ + OSSL_FUNC_asym_cipher_decrypt_init_fn *default_decrypt_init_fn; + const OSSL_PARAM *p; + + if (ctx == NULL || key == NULL) + return 0; + + sk_debug_op_ctx(ctx, "ctx: %p key: %p", ctx, key); + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_op_ctx(ctx, "param: %s", p->key); + + default_decrypt_init_fn = (OSSL_FUNC_asym_cipher_decrypt_init_fn *) + sk_prov_get_default_asym_func(ctx->provctx, + ctx->type, + OSSL_FUNC_ASYM_CIPHER_DECRYPT_INIT); + if (default_decrypt_init_fn == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default decrypt_init_fn"); + return 0; + } + + if (!sk_prov_op_init(ctx, key, EVP_PKEY_OP_DECRYPT)) { + sk_debug_op_ctx(ctx, "ERROR: sk_prov_op_init failed"); + return 0; + } + + if (!default_decrypt_init_fn(ctx->default_op_ctx, key->default_key, + params)) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_decrypt_init_fn failed"); + return 0; + } + + return 1; +} + +static int sk_prov_asym_op_encrypt(struct sk_prov_op_ctx *ctx, + unsigned char *out, size_t *outlen, + size_t outsize, const unsigned char *in, + size_t inlen) +{ + OSSL_FUNC_asym_cipher_encrypt_fn *default_encrypt_fn; + + if (ctx == NULL || in == NULL || outlen == NULL) + return 0; + + sk_debug_op_ctx(ctx, "ctx: %p key: %p inlen: %lu outsize: %lu", + ctx, ctx->key, inlen, outsize); + + default_encrypt_fn = (OSSL_FUNC_asym_cipher_encrypt_fn *) + sk_prov_get_default_asym_func(ctx->provctx, + ctx->type, OSSL_FUNC_ASYM_CIPHER_ENCRYPT); + if (default_encrypt_fn == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default encrypt_fn"); + return 0; + } + + if (!default_encrypt_fn(ctx->default_op_ctx, out, outlen, outsize, + in, inlen)) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_encrypt_fn failed"); + return 0; + } + + sk_debug_op_ctx(ctx, "outlen: %lu", *outlen); + + return 1; +} + +static int sk_prov_asym_op_decrypt(struct sk_prov_op_ctx *ctx, + unsigned char *out, size_t *outlen, + size_t outsize, const unsigned char *in, + size_t inlen) +{ + OSSL_FUNC_asym_cipher_decrypt_fn *default_decrypt_fn; + + if (ctx == NULL || in == NULL || outlen == NULL) + return 0; + + sk_debug_op_ctx(ctx, "ctx: %p key: %p inlen: %lu outsize: %lu", + ctx, ctx->key, inlen, outsize); + + default_decrypt_fn = (OSSL_FUNC_asym_cipher_decrypt_fn *) + sk_prov_get_default_asym_func(ctx->provctx, + ctx->type, OSSL_FUNC_ASYM_CIPHER_DECRYPT); + if (default_decrypt_fn == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default decrypt_fn"); + return 0; + } + + if (!default_decrypt_fn(ctx->default_op_ctx, out, outlen, outsize, + in, inlen)) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_decrypt_fn failed"); + return 0; + } + + sk_debug_op_ctx(ctx, "outlen: %lu", *outlen); + + return 1; +} + +static struct sk_prov_op_ctx *sk_prov_sign_op_newctx( + struct sk_prov_ctx *provctx, + const char *propq, + int pkey_type) +{ + OSSL_FUNC_signature_freectx_fn *default_freectx_fn; + OSSL_FUNC_signature_newctx_fn *default_newctx_fn; + struct sk_prov_op_ctx *ctx; + + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "propq: %s pkey_type: %d", + propq != NULL ? propq : "", pkey_type); + + default_newctx_fn = (OSSL_FUNC_signature_newctx_fn *) + sk_prov_get_default_sign_func(provctx, pkey_type, + OSSL_FUNC_SIGNATURE_NEWCTX); + if (default_newctx_fn == NULL) { + put_error_ctx(provctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default newctx_fn"); + return NULL; + } + + default_freectx_fn = (OSSL_FUNC_signature_freectx_fn *) + sk_prov_get_default_sign_func(provctx, pkey_type, + OSSL_FUNC_SIGNATURE_FREECTX); + if (default_freectx_fn == NULL) { + put_error_ctx(provctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default freectx_fn"); + return NULL; + } + + ctx = sk_prov_op_newctx(provctx, propq, pkey_type); + if (ctx == NULL) { + sk_debug_ctx(provctx, "ERROR: sk_prov_op_newctx failed"); + return NULL; + } + + ctx->default_op_ctx = default_newctx_fn(provctx->default_provctx, + propq); + if (ctx->default_op_ctx == NULL) { + put_error_ctx(provctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_newctx_fn failed"); + sk_prov_op_freectx(ctx); + return NULL; + } + ctx->default_op_ctx_free = default_freectx_fn; + + sk_debug_ctx(provctx, "ctx: %p", ctx); + return ctx; +} + +static struct sk_prov_op_ctx *sk_prov_sign_op_dupctx(struct sk_prov_op_ctx *ctx) +{ + OSSL_FUNC_signature_dupctx_fn *default_dupctx_fn; + struct sk_prov_op_ctx *new_ctx; + + if (ctx == NULL) + return NULL; + + sk_debug_op_ctx(ctx, "ctx: %p", ctx); + + default_dupctx_fn = (OSSL_FUNC_signature_dupctx_fn *) + sk_prov_get_default_sign_func(ctx->provctx, + ctx->type, OSSL_FUNC_SIGNATURE_DUPCTX); + if (default_dupctx_fn == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default dupctx_fn"); + return NULL; + } + + new_ctx = sk_prov_op_dupctx(ctx); + if (new_ctx == NULL) { + sk_debug_op_ctx(ctx, "ERROR: sk_prov_op_dupctx failed"); + return NULL; + } + + new_ctx->default_op_ctx = default_dupctx_fn(ctx->default_op_ctx); + if (new_ctx->default_op_ctx == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_dupctx_fn failed"); + sk_prov_op_freectx(new_ctx); + return NULL; + } + + sk_debug_op_ctx(ctx, "new_ctx: %p", new_ctx); + return new_ctx; +} + +static int sk_prov_sign_op_get_ctx_params(struct sk_prov_op_ctx *ctx, + OSSL_PARAM params[]) +{ + OSSL_FUNC_signature_get_ctx_params_fn *default_get_params_fn; + const OSSL_PARAM *p; + + if (ctx == NULL) + return 0; + + sk_debug_op_ctx(ctx, "ctx: %p", ctx); + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_op_ctx(ctx, "param: %s", p->key); + + default_get_params_fn = (OSSL_FUNC_signature_get_ctx_params_fn *) + sk_prov_get_default_sign_func(ctx->provctx, + ctx->type, OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS); + + /* default_get_params_fn is optional */ + if (default_get_params_fn != NULL) { + if (!default_get_params_fn(ctx->default_op_ctx, params)) { + put_error_op_ctx(ctx, + SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_get_params_fn failed"); + return 0; + } + } + + return 1; +} + +static int sk_prov_sign_op_set_ctx_params(struct sk_prov_op_ctx *ctx, + const OSSL_PARAM params[]) +{ + OSSL_FUNC_signature_set_ctx_params_fn *default_set_params_fn; + const OSSL_PARAM *p; + + if (ctx == NULL) + return 0; + + sk_debug_op_ctx(ctx, "ctx: %p", ctx); + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_op_ctx(ctx, "param: %s", p->key); + + default_set_params_fn = (OSSL_FUNC_signature_set_ctx_params_fn *) + sk_prov_get_default_sign_func(ctx->provctx, + ctx->type, OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS); + + /* default_set_params_fn is optional */ + if (default_set_params_fn != NULL) { + if (!default_set_params_fn(ctx->default_op_ctx, params)) { + put_error_op_ctx(ctx, + SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_set_params_fn failed"); + return 0; + } + } + + return 1; +} + +static const OSSL_PARAM *sk_prov_sign_op_gettable_ctx_params( + struct sk_prov_op_ctx *ctx, + struct sk_prov_ctx *provctx, int pkey_type) +{ + OSSL_FUNC_signature_gettable_ctx_params_fn *default_gettable_params_fn; + const OSSL_PARAM *params = NULL, *p; + + if (ctx == NULL || provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "pkey_type: %d", pkey_type); + + default_gettable_params_fn = + (OSSL_FUNC_signature_gettable_ctx_params_fn *) + sk_prov_get_default_sign_func(provctx, pkey_type, + OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS); + + /* default_gettable_params_fn is optional */ + if (default_gettable_params_fn != NULL) + params = default_gettable_params_fn(ctx->default_op_ctx, + provctx->default_provctx); + + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_ctx(provctx, "param: %s", p->key); + + return params; +} + +static const OSSL_PARAM *sk_prov_sign_op_settable_ctx_params( + struct sk_prov_op_ctx *ctx, + struct sk_prov_ctx *provctx, int pkey_type) +{ + OSSL_FUNC_signature_settable_ctx_params_fn *default_settable_params_fn; + const OSSL_PARAM *params = NULL, *p; + + if (ctx == NULL || provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "pkey_type: %d", pkey_type); + + default_settable_params_fn = + (OSSL_FUNC_signature_settable_ctx_params_fn *) + sk_prov_get_default_sign_func(provctx, pkey_type, + OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS); + + /* default_settable_params_fn is optional */ + if (default_settable_params_fn != NULL) + params = default_settable_params_fn(ctx->default_op_ctx, + provctx->default_provctx); + + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_ctx(provctx, "param: %s", p->key); + + return params; +} + +static int sk_prov_sign_op_get_ctx_md_params(struct sk_prov_op_ctx *ctx, + OSSL_PARAM params[]) +{ + OSSL_FUNC_signature_get_ctx_md_params_fn *default_get_md_params_fn; + const OSSL_PARAM *p; + + if (ctx == NULL) + return 0; + + sk_debug_op_ctx(ctx, "ctx: %p", ctx); + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_op_ctx(ctx, "param: %s", p->key); + + default_get_md_params_fn = (OSSL_FUNC_signature_get_ctx_md_params_fn *) + sk_prov_get_default_sign_func(ctx->provctx, + ctx->type, + OSSL_FUNC_SIGNATURE_GET_CTX_MD_PARAMS); + + /* default_get_md_params_fn is optional */ + if (default_get_md_params_fn != NULL) { + if (!default_get_md_params_fn(ctx->default_op_ctx, params)) { + put_error_op_ctx(ctx, + SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_get_md_params_fn failed"); + return 0; + } + } + + return 1; +} + +static int sk_prov_sign_op_set_ctx_md_params(struct sk_prov_op_ctx *ctx, + const OSSL_PARAM params[]) +{ + OSSL_FUNC_signature_set_ctx_md_params_fn *default_set_md_params_fn; + const OSSL_PARAM *p; + + if (ctx == NULL) + return 0; + + sk_debug_op_ctx(ctx, "ctx: %p", ctx); + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_op_ctx(ctx, "param: %s", p->key); + + default_set_md_params_fn = (OSSL_FUNC_signature_set_ctx_md_params_fn *) + sk_prov_get_default_sign_func(ctx->provctx, + ctx->type, + OSSL_FUNC_SIGNATURE_SET_CTX_MD_PARAMS); + + /* default_set_md_params_fn is optional */ + if (default_set_md_params_fn != NULL) { + if (!default_set_md_params_fn(ctx->default_op_ctx, params)) { + put_error_op_ctx(ctx, + SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_set_md_params_fn failed"); + return 0; + } + } + + /* Also set parameters in own MD context */ + if (ctx->mdctx) + return EVP_MD_CTX_set_params(ctx->mdctx, params); + + return 1; +} + +static const OSSL_PARAM *sk_prov_sign_op_gettable_ctx_md_params( + struct sk_prov_op_ctx *ctx, int pkey_type) +{ + OSSL_FUNC_signature_gettable_ctx_md_params_fn + *default_gettable_md_params_fn; + const OSSL_PARAM *params = NULL, *p; + + if (ctx == NULL) + return NULL; + + sk_debug_op_ctx(ctx, "pkey_type: %d", pkey_type); + + default_gettable_md_params_fn = + (OSSL_FUNC_signature_gettable_ctx_md_params_fn *) + sk_prov_get_default_sign_func(ctx->provctx, pkey_type, + OSSL_FUNC_SIGNATURE_GETTABLE_CTX_MD_PARAMS); + + /* default_gettable_params_fn is optional */ + if (default_gettable_md_params_fn != NULL) + params = default_gettable_md_params_fn(ctx->default_op_ctx); + + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_op_ctx(ctx, "param: %s", p->key); + + return params; +} + +static const OSSL_PARAM *sk_prov_sign_op_settable_ctx_md_params( + struct sk_prov_op_ctx *ctx, int pkey_type) +{ + OSSL_FUNC_signature_settable_ctx_md_params_fn + *default_settable_md_params_fn; + const OSSL_PARAM *params = NULL, *p; + + if (ctx == NULL) + return NULL; + + sk_debug_op_ctx(ctx, "pkey_type: %d", pkey_type); + + default_settable_md_params_fn = + (OSSL_FUNC_signature_settable_ctx_md_params_fn *) + sk_prov_get_default_sign_func(ctx->provctx, pkey_type, + OSSL_FUNC_SIGNATURE_SETTABLE_CTX_MD_PARAMS); + + /* default_settable_md_params_fn is optional */ + if (default_settable_md_params_fn != NULL) + params = default_settable_md_params_fn(ctx->default_op_ctx); + + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_op_ctx(ctx, "param: %s", p->key); + + return params; +} + +static EVP_MD *sk_prov_sign_op_get_md(struct sk_prov_op_ctx *ctx) +{ + char mdprops[256], mdname[50]; + OSSL_PARAM ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, + &mdname, sizeof(mdname)), + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PROPERTIES, + &mdprops, sizeof(mdprops)), + OSSL_PARAM_END + }; + EVP_MD *md; + + sk_debug_op_ctx(ctx, "ctx: %p", ctx); + + if (!sk_prov_sign_op_get_ctx_params(ctx, ctx_params) || + !OSSL_PARAM_modified(&ctx_params[0]) || + !OSSL_PARAM_modified(&ctx_params[1])) { + sk_debug_op_ctx(ctx, "sk_prov_sign_op_get_ctx_params failed"); + if (ctx->md != NULL) { + sk_debug_op_ctx(ctx, "use digest from context: %s", + EVP_MD_name(ctx->md)); + EVP_MD_up_ref(ctx->md); + return ctx->md; + } + + sk_debug_op_ctx(ctx, "use default"); + strcpy(mdname, SK_PROV_RSA_DEFAULT_MD); + strcpy(mdprops, ""); + } + + md = EVP_MD_fetch((OSSL_LIB_CTX *)ctx->provctx->c_get_libctx( + ctx->provctx->handle), + mdname, mdprops[0] != '\0' ? mdprops : ctx->propq); + if (md == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, + "EVP_MD_fetch failed to fetch '%s' using " + "property query '%s'", mdname, + mdprops[0] != '\0' ? mdprops : + ctx->propq != NULL ? ctx->propq : ""); + return NULL; + } + + sk_debug_op_ctx(ctx, "md: %s", EVP_MD_name(md)); + return md; +} + +static EVP_MD *sk_prov_sign_op_get_mgf_md(struct sk_prov_op_ctx *ctx) +{ + char mdprops[256], mdname[50]; + OSSL_PARAM ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_MGF1_DIGEST, + &mdname, sizeof(mdname)), + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_MGF1_PROPERTIES, + &mdprops, sizeof(mdprops)), + OSSL_PARAM_END + }; + EVP_MD *md; + + sk_debug_op_ctx(ctx, "ctx: %p", ctx); + + if (!sk_prov_sign_op_get_ctx_params(ctx, ctx_params) || + !OSSL_PARAM_modified(&ctx_params[0]) || + !OSSL_PARAM_modified(&ctx_params[1])) { + sk_debug_op_ctx(ctx, "sk_prov_sign_op_get_ctx_params failed, " + "using signature digest"); + return sk_prov_sign_op_get_md(ctx); + } + + md = EVP_MD_fetch((OSSL_LIB_CTX *)ctx->provctx->c_get_libctx( + ctx->provctx->handle), + mdname, mdprops[0] != '\0' ? mdprops : ctx->propq); + if (md == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, + "EVP_MD_fetch failed to fetch '%s' using " + "property query '%s'", mdname, + mdprops[0] != '\0' ? mdprops : + ctx->propq != NULL ? ctx->propq : ""); + return NULL; + } + + sk_debug_op_ctx(ctx, "md: %s", EVP_MD_name(md)); + return md; +} + +static int sk_prov_sign_op_get_padding(struct sk_prov_op_ctx *ctx) +{ + char padding[50]; + OSSL_PARAM ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_PAD_MODE, + &padding, sizeof(padding)), + OSSL_PARAM_END + }; + + sk_debug_op_ctx(ctx, "ctx: %p", ctx); + + if (!sk_prov_sign_op_get_ctx_params(ctx, ctx_params) || + !OSSL_PARAM_modified(&ctx_params[0])) { + put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, + "sk_prov_sign_op_get_ctx_params failed to " + "get OSSL_PKEY_PARAM_PAD_MODE"); + return -1; + } + + sk_debug_op_ctx(ctx, "padding: %s", padding); + return sk_prov_parse_padding(padding); +} + +static int sk_prov_sign_op_get_pss_saltlen(struct sk_prov_op_ctx *ctx, + struct sk_prov_key *key, + EVP_MD *mgf_md) +{ + char saltlen[50]; + OSSL_PARAM ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PSS_SALTLEN, + &saltlen, sizeof(saltlen)), + OSSL_PARAM_END + }; + int salt_len, rsa_bits, max_saltlen; + + sk_debug_op_ctx(ctx, "ctx: %p", ctx); + + if (!sk_prov_sign_op_get_ctx_params(ctx, ctx_params) || + !OSSL_PARAM_modified(&ctx_params[0])) { + put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, + "sk_prov_sign_op_get_ctx_params failed to " + "get OSSL_SIGNATURE_PARAM_PSS_SALTLEN"); + return -1; + } + + sk_debug_op_ctx(ctx, "saltlen: %s", saltlen); + + rsa_bits = sk_prov_keymgmt_get_bits(key); + if (rsa_bits <= 0) { + sk_debug_op_ctx(ctx, + "ERROR: sk_prov_keymgmt_get_bits failed"); + return -1; + } + + max_saltlen = rsa_bits / 8 - EVP_MD_size(mgf_md) - 2; + + if (strcmp(saltlen, OSSL_PKEY_RSA_PSS_SALT_LEN_DIGEST) == 0) + salt_len = EVP_MD_size(mgf_md); + else if (strcmp(saltlen, OSSL_PKEY_RSA_PSS_SALT_LEN_MAX) == 0) + salt_len = max_saltlen; + else if (strcmp(saltlen, OSSL_PKEY_RSA_PSS_SALT_LEN_AUTO) == 0) + salt_len = max_saltlen; + else + salt_len = atoi(saltlen); + + if (salt_len > max_saltlen || salt_len < 0) { + put_error_op_ctx(ctx, SK_PROV_ERR_INVALID_SALTLEN, + "invalid salt len: %d", saltlen); + return -1; + } + + sk_debug_op_ctx(ctx, "salt_len: %d", salt_len); + return salt_len; +} + +static int sk_prov_sign_op_sign_init(struct sk_prov_op_ctx *ctx, + struct sk_prov_key *key, + const OSSL_PARAM params[]) +{ + OSSL_FUNC_signature_sign_init_fn *default_sign_init_fn; + const OSSL_PARAM *p; + + if (ctx == NULL || key == NULL) + return 0; + + sk_debug_op_ctx(ctx, "ctx: %p key: %p", ctx, key); + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_op_ctx(ctx, "param: %s", p->key); + + default_sign_init_fn = (OSSL_FUNC_signature_sign_init_fn *) + sk_prov_get_default_sign_func(ctx->provctx, + ctx->type, + OSSL_FUNC_SIGNATURE_SIGN_INIT); + if (default_sign_init_fn == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default sign_init_fn"); + return 0; + } + + if (!sk_prov_op_init(ctx, key, EVP_PKEY_OP_SIGN)) { + sk_debug_op_ctx(ctx, "ERROR: sk_prov_op_init failed"); + return 0; + } + + if (!default_sign_init_fn(ctx->default_op_ctx, key->default_key, + params)) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_sign_init_fn failed"); + return 0; + } + + return 1; +} + +static int sk_prov_sign_op_verify_init(struct sk_prov_op_ctx *ctx, + struct sk_prov_key *key, + const OSSL_PARAM params[]) +{ + OSSL_FUNC_signature_verify_init_fn *default_verify_init_fn; + const OSSL_PARAM *p; + + if (ctx == NULL || key == NULL) + return 0; + + sk_debug_op_ctx(ctx, "ctx: %p key: %p", ctx, key); + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_op_ctx(ctx, "param: %s", p->key); + + default_verify_init_fn = (OSSL_FUNC_signature_verify_init_fn *) + sk_prov_get_default_sign_func(ctx->provctx, + ctx->type, OSSL_FUNC_SIGNATURE_VERIFY_INIT); + if (default_verify_init_fn == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default verify_init_fn"); + return 0; + } + + if (!sk_prov_op_init(ctx, key, EVP_PKEY_OP_VERIFY)) { + sk_debug_op_ctx(ctx, "ERROR: sk_prov_op_init failed"); + return 0; + } + + if (!default_verify_init_fn(ctx->default_op_ctx, key->default_key, + params)) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_verify_init_fn failed"); + return 0; + } + + return 1; +} + +static int sk_prov_sign_op_verify_recover_init(struct sk_prov_op_ctx *ctx, + struct sk_prov_key *key, + const OSSL_PARAM params[]) +{ + OSSL_FUNC_signature_verify_recover_init_fn + *default_verify_recover_init_fn; + const OSSL_PARAM *p; + + if (ctx == NULL || key == NULL) + return 0; + + sk_debug_op_ctx(ctx, "ctx: %p key: %p", ctx, key); + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_op_ctx(ctx, "param: %s", p->key); + + default_verify_recover_init_fn = + (OSSL_FUNC_signature_verify_recover_init_fn *) + sk_prov_get_default_sign_func(ctx->provctx, + ctx->type, + OSSL_FUNC_SIGNATURE_VERIFY_RECOVER_INIT); + if (default_verify_recover_init_fn == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default verify_recover_init_fn"); + return 0; + } + + if (!sk_prov_op_init(ctx, key, EVP_PKEY_OP_VERIFYRECOVER)) { + sk_debug_op_ctx(ctx, "ERROR: sk_prov_op_init failed"); + return 0; + } + + if (!default_verify_recover_init_fn(ctx->default_op_ctx, + key->default_key, + params)) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_verify_recover_init_fn failed"); + return 0; + } + + return 1; +} + +static int sk_prov_sign_op_sign(struct sk_prov_op_ctx *ctx, + unsigned char *sig, size_t *siglen, + size_t sigsize, + const unsigned char *tbs, size_t tbslen) +{ + OSSL_FUNC_signature_sign_fn *default_sign_fn; + + if (ctx == NULL || tbs == NULL || siglen == NULL) + return 0; + + sk_debug_op_ctx(ctx, "ctx: %p key: %p tbslen: %lu sigsize: %lu", + ctx, ctx->key, tbslen, sigsize); + + default_sign_fn = (OSSL_FUNC_signature_sign_fn *) + sk_prov_get_default_sign_func(ctx->provctx, + ctx->type, OSSL_FUNC_SIGNATURE_SIGN); + if (default_sign_fn == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default sign_fn"); + return 0; + } + + if (!default_sign_fn(ctx->default_op_ctx, sig, siglen, sigsize, + tbs, tbslen)) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_sign_fn failed"); + return 0; + } + + sk_debug_op_ctx(ctx, "siglen: %lu", *siglen); + + return 1; +} + +static int sk_prov_sign_op_verify(struct sk_prov_op_ctx *ctx, + unsigned char *sig, size_t siglen, + const unsigned char *tbs, size_t tbslen) +{ + OSSL_FUNC_signature_verify_fn *default_verify_fn; + + if (ctx == NULL || tbs == NULL || sig == NULL) + return 0; + + sk_debug_op_ctx(ctx, "ctx: %p key: %p tbslen: %lu siglen: %lu", + ctx, ctx->key, tbslen, siglen); + + default_verify_fn = (OSSL_FUNC_signature_verify_fn *) + sk_prov_get_default_sign_func(ctx->provctx, + ctx->type, OSSL_FUNC_SIGNATURE_VERIFY); + if (default_verify_fn == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default verify_fn"); + return 0; + } + + if (!default_verify_fn(ctx->default_op_ctx, sig, siglen, tbs, tbslen)) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_verify_fn failed"); + return 0; + } + + return 1; +} + +static int sk_prov_sign_op_verify_recover(struct sk_prov_op_ctx *ctx, + unsigned char *rout, size_t *routlen, + size_t routsize, + const unsigned char *sig, + size_t siglen) +{ + OSSL_FUNC_signature_verify_recover_fn *default_verify_recover_fn; + + if (ctx == NULL || routlen == NULL || sig == NULL) + return 0; + + sk_debug_op_ctx(ctx, + "ctx: %p key: %p routsize: %lu siglen: %lu", + ctx, ctx->key, routsize, siglen); + + default_verify_recover_fn = (OSSL_FUNC_signature_verify_recover_fn *) + sk_prov_get_default_sign_func(ctx->provctx, + ctx->type, OSSL_FUNC_SIGNATURE_VERIFY_RECOVER); + if (default_verify_recover_fn == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default verify_recover_fn"); + return 0; + } + + if (!default_verify_recover_fn(ctx->default_op_ctx, rout, routlen, + routsize, sig, siglen)) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_verify_recover_fn failed"); + return 0; + } + + sk_debug_op_ctx(ctx, "routlen: %lu", *routlen); + + return 1; +} + +static int sk_prov_sign_op_digest_sign_init(struct sk_prov_op_ctx *ctx, + const char *mdname, + struct sk_prov_key *key, + const OSSL_PARAM params[], + int (*sign_fn) + (struct sk_prov_op_ctx *, + unsigned char *, size_t *, + size_t, const unsigned char *, + size_t)) +{ + OSSL_FUNC_signature_digest_sign_init_fn *default_digest_sign_init_fn; + const OSSL_PARAM *p; + + if (ctx == NULL || key == NULL || sign_fn == NULL) + return 0; + + sk_debug_op_ctx(ctx, "ctx: %p mdname: %s key: %p", ctx, + mdname != NULL ? mdname : "", key); + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_op_ctx(ctx, "param: %s", p->key); + + default_digest_sign_init_fn = + (OSSL_FUNC_signature_digest_sign_init_fn *) + sk_prov_get_default_sign_func(ctx->provctx, + ctx->type, + OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT); + if (default_digest_sign_init_fn == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default digest_sign_init_fn"); + return 0; + } + + if (!sk_prov_op_init(ctx, key, EVP_PKEY_OP_SIGN)) { + sk_debug_op_ctx(ctx, "ERROR: sk_prov_op_init failed"); + return 0; + } + + if (!default_digest_sign_init_fn(ctx->default_op_ctx, mdname, + key->default_key, params)) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_digest_sign_init_fn failed"); + return 0; + } + + /* For clear key, the default provider has already handled it */ + if (ctx->key->secure_key == NULL) + return 1; + + ctx->sign_fn = sign_fn; + + if (ctx->mdctx != NULL) + EVP_MD_CTX_free(ctx->mdctx); + ctx->mdctx = EVP_MD_CTX_new(); + if (ctx->mdctx == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_MALLOC_FAILED, + "EVP_MD_CTX_new failed"); + return 0; + } + + if (ctx->md != NULL) + EVP_MD_free(ctx->md); + if (mdname != NULL) + ctx->md = EVP_MD_fetch( + (OSSL_LIB_CTX *)ctx->provctx->c_get_libctx( + ctx->provctx->handle), + mdname, ctx->propq); + else + ctx->md = sk_prov_sign_op_get_md(ctx); + if (ctx->md == NULL) { + sk_debug_op_ctx(ctx, "ERROR: Failed to get digest sign digest"); + EVP_MD_CTX_free(ctx->mdctx); + ctx->mdctx = NULL; + return 0; + } + + return EVP_DigestInit_ex2(ctx->mdctx, ctx->md, params); +} + +static int sk_prov_sign_op_digest_sign_update(struct sk_prov_op_ctx *ctx, + const unsigned char *data, + size_t datalen) +{ + OSSL_FUNC_signature_digest_sign_update_fn + *default_digest_sign_update_fn; + + if (ctx == NULL) + return 0; + + sk_debug_op_ctx(ctx, "ctx: %p key: %p datalen: %lu", ctx, ctx->key, + datalen); + + if (ctx->key == NULL || ctx->operation != EVP_PKEY_OP_SIGN) { + put_error_op_ctx(ctx, SK_PROV_ERR_OPRATION_NOT_INITIALIZED, + "digest sign operation not initialized"); + return 0; + } + + /* For secure key, don't pass it to the default provider */ + if (ctx->key->secure_key != NULL) + goto secure_key; + + default_digest_sign_update_fn = + (OSSL_FUNC_signature_digest_sign_update_fn *) + sk_prov_get_default_sign_func(ctx->provctx, + ctx->type, + OSSL_FUNC_SIGNATURE_DIGEST_SIGN_UPDATE); + if (default_digest_sign_update_fn == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default digest_sign_update_fn"); + return 0; + } + + if (!default_digest_sign_update_fn(ctx->default_op_ctx, data, + datalen)) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_digest_sign_update_fn failed"); + return 0; + } + + return 1; + +secure_key: + if (ctx->mdctx == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_OPRATION_NOT_INITIALIZED, + "digest sign operation not initialized"); + return 0; + } + + return EVP_DigestUpdate(ctx->mdctx, data, datalen); +} + +static int sk_prov_sign_op_digest_sign_final(struct sk_prov_op_ctx *ctx, + unsigned char *sig, + size_t *siglen, size_t sigsize) +{ + OSSL_FUNC_signature_digest_sign_final_fn *default_digest_sign_final_fn; + unsigned char digest[EVP_MAX_MD_SIZE]; + unsigned int dlen = 0; + + if (ctx == NULL || siglen == NULL) + return 0; + + sk_debug_op_ctx(ctx, "ctx: %p key: %p sigsize: %lu", ctx, ctx->key, + sigsize); + + if (ctx->key == NULL || ctx->operation != EVP_PKEY_OP_SIGN) { + put_error_op_ctx(ctx, SK_PROV_ERR_OPRATION_NOT_INITIALIZED, + "digest sign operation not initialized"); + return 0; + } + + /* For secure key, don't pass it to the default provider */ + if (ctx->key->secure_key != NULL) + goto secure_key; + + default_digest_sign_final_fn = + (OSSL_FUNC_signature_digest_sign_final_fn *) + sk_prov_get_default_sign_func(ctx->provctx, + ctx->type, + OSSL_FUNC_SIGNATURE_DIGEST_SIGN_FINAL); + if (default_digest_sign_final_fn == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default digest_sign_final_fn"); + return 0; + } + + if (!default_digest_sign_final_fn(ctx->default_op_ctx, sig, siglen, + sigsize)) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_digest_sign_final_fn failed"); + return 0; + } + + sk_debug_op_ctx(ctx, "siglen: %lu", *siglen); + return 1; + +secure_key: + if (ctx->mdctx == NULL || ctx->sign_fn == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_OPRATION_NOT_INITIALIZED, + "digest sign operation not initialized"); + return 0; + } + + if (sig != NULL) { + if (!EVP_DigestFinal_ex(ctx->mdctx, digest, &dlen)) { + sk_debug_op_ctx(ctx, + "ERROR: EVP_DigestFinal_ex failed"); + return 0; + } + } + + if (!ctx->sign_fn(ctx, sig, siglen, sigsize, digest, (size_t)dlen)) { + sk_debug_op_ctx(ctx, "ERROR: sign_fn failed"); + return 0; + } + + sk_debug_op_ctx(ctx, "siglen: %lu", *siglen); + return 1; +} + +static int sk_prov_sign_op_digest_verify_init(struct sk_prov_op_ctx *ctx, + const char *mdname, + struct sk_prov_key *key, + const OSSL_PARAM params[]) +{ + OSSL_FUNC_signature_digest_verify_init_fn + *default_digest_verify_init_fn; + const OSSL_PARAM *p; + + if (ctx == NULL || key == NULL) + return 0; + + sk_debug_op_ctx(ctx, "ctx: %p mdname: %s key: %p", ctx, + mdname != NULL ? mdname : "", key); + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_op_ctx(ctx, "param: %s", p->key); + + default_digest_verify_init_fn = + (OSSL_FUNC_signature_digest_verify_init_fn *) + sk_prov_get_default_sign_func(ctx->provctx, + ctx->type, + OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_INIT); + if (default_digest_verify_init_fn == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default digest_verify_init_fn"); + return 0; + } + + if (!sk_prov_op_init(ctx, key, EVP_PKEY_OP_VERIFY)) { + sk_debug_op_ctx(ctx, "ERROR: sk_prov_op_init failed"); + return 0; + } + + if (!default_digest_verify_init_fn(ctx->default_op_ctx, mdname, + key->default_key, params)) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_digest_verify_init_fn failed"); + return 0; + } + + return 1; +} + +static int sk_prov_sign_op_digest_verify_update(struct sk_prov_op_ctx *ctx, + const unsigned char *data, + size_t datalen) +{ + OSSL_FUNC_signature_digest_verify_update_fn + *default_digest_verify_update_fn; + + if (ctx == NULL) + return 0; + + sk_debug_op_ctx(ctx, "ctx: %p key: %p datalen: %lu", ctx, ctx->key, + datalen); + + default_digest_verify_update_fn = + (OSSL_FUNC_signature_digest_verify_update_fn *) + sk_prov_get_default_sign_func(ctx->provctx, ctx->type, + OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_UPDATE); + if (default_digest_verify_update_fn == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default digest_verify_update_fn"); + return 0; + } + + if (!default_digest_verify_update_fn(ctx->default_op_ctx, + data, datalen)) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_digest_verify_update_fn failed"); + return 0; + } + + return 1; +} + +static int sk_prov_sign_op_digest_verify_final(struct sk_prov_op_ctx *ctx, + const unsigned char *sig, + size_t siglen) +{ + OSSL_FUNC_signature_digest_verify_final_fn + *default_digest_verify_final_fn; + + if (ctx == NULL || sig == NULL) + return 0; + + sk_debug_op_ctx(ctx, "ctx: %p key: %p siglen: %lu", ctx, ctx->key, + siglen); + + default_digest_verify_final_fn = + (OSSL_FUNC_signature_digest_verify_final_fn *) + sk_prov_get_default_sign_func(ctx->provctx, ctx->type, + OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_FINAL); + if (default_digest_verify_final_fn == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default digest_verify_final_fn"); + return 0; + } + + if (!default_digest_verify_final_fn(ctx->default_op_ctx, sig, siglen)) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_digest_verify_final_fn failed"); + return 0; + } + + return 1; +} + + +static void sk_prov_keymgmt_upref(struct sk_prov_key *key) +{ + sk_debug_key(key, "key: %p", key); + + key->ref_count++; + + sk_debug_key(key, "ref_count: %u", key->ref_count); +} + +static unsigned int sk_prov_keymgmt_downref(struct sk_prov_key *key) +{ + sk_debug_key(key, "key: %p ", key); + + if (key->ref_count > 0) + key->ref_count--; + + sk_debug_key(key, "ref_count: %u", key->ref_count); + + return key->ref_count; +} + +static struct sk_prov_key *sk_prov_keymgmt_new(struct sk_prov_ctx *provctx, + int type) +{ + OSSL_FUNC_keymgmt_new_fn *default_new_fn; + struct sk_prov_key *key; + + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "provctx: %p type: %d", provctx, + type); + + default_new_fn = (OSSL_FUNC_keymgmt_new_fn *) + sk_prov_get_default_keymgmt_func(provctx, type, + OSSL_FUNC_KEYMGMT_NEW); + if (default_new_fn == NULL) { + put_error_ctx(provctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default new_fn"); + return NULL; + } + + key = OPENSSL_zalloc(sizeof(struct sk_prov_key)); + if (key == NULL) { + put_error_ctx(provctx, SK_PROV_ERR_MALLOC_FAILED, + "OPENSSL_zalloc failed"); + return NULL; + } + + key->provctx = provctx; + key->type = type; + + key->default_key = default_new_fn(provctx->default_provctx); + if (key->default_key == NULL) { + put_error_ctx(provctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_new_fn failed"); + OPENSSL_free(key); + return NULL; + } + + sk_prov_keymgmt_upref(key); + + sk_debug_ctx(provctx, "key: %p", key); + + return key; +} + +static void sk_prov_keymgmt_free(struct sk_prov_key *key) +{ + OSSL_FUNC_keymgmt_free_fn *default_free_fn; + + if (key == NULL) + return; + + sk_debug_key(key, "key: %p", key); + + if (sk_prov_keymgmt_downref(key) > 0) + return; + + sk_debug_key(key, "free key: %p", key); + + default_free_fn = (OSSL_FUNC_keymgmt_free_fn *) + sk_prov_get_default_keymgmt_func(key->provctx, + key->type, OSSL_FUNC_KEYMGMT_FREE); + if (default_free_fn == NULL) + sk_debug_key(key, "no default free_fn"); + else + default_free_fn(key->default_key); + + if (key->secure_key != NULL) + OPENSSL_free(key->secure_key); + OPENSSL_free(key); +} + +static int sk_prov_keymgmt_match(const struct sk_prov_key *key1, + const struct sk_prov_key *key2, + int selection) +{ + OSSL_FUNC_keymgmt_match_fn *default_match_fn; + + if (key1 == NULL || key2 == NULL) + return 0; + + sk_debug_key(key1, "key1: %p key2: %p", key1, key2); + + default_match_fn = (OSSL_FUNC_keymgmt_match_fn *) + sk_prov_get_default_keymgmt_func(key1->provctx, + key1->type, OSSL_FUNC_KEYMGMT_MATCH); + if (default_match_fn == NULL) { + put_error_key(key1, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default match_fn"); + return 0; + } + + if (key1->type != key2->type) + return 0; + if (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) { + if (key1->secure_key_size != key2->secure_key_size) + return 0; + if (key1->secure_key_size > 0 && + memcmp(key1->secure_key, key2->secure_key, + key1->secure_key_size) != 0) + return 0; + } + + return default_match_fn(key1->default_key, key2->default_key, + selection); +} + +static int sk_prov_keymgmt_validate(const struct sk_prov_key *key, + int selection, int checktype) +{ + OSSL_FUNC_keymgmt_validate_fn *default_validate_fn; + int default_selection = selection; + + if (key == NULL) + return 0; + + sk_debug_key(key, "key: %p selection: %x checktype: %x", key, + selection, checktype); + + default_validate_fn = (OSSL_FUNC_keymgmt_validate_fn *) + sk_prov_get_default_keymgmt_func(key->provctx, + key->type, OSSL_FUNC_KEYMGMT_VALIDATE); + if (default_validate_fn == NULL) { + put_error_key(key, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default validate_fn"); + return 0; + } + + /* A secure key doesn't have the private parts in the default key */ + if (key->secure_key != NULL) + default_selection &= (~OSSL_KEYMGMT_SELECT_PRIVATE_KEY); + + return default_validate_fn(key->default_key, default_selection, + checktype); +} + +static int sk_prov_keymgmt_get_params(struct sk_prov_key *key, + OSSL_PARAM params[]) +{ + OSSL_FUNC_keymgmt_get_params_fn *default_get_params_fn; + OSSL_PARAM *p; + + if (key == NULL) + return 0; + + sk_debug_key(key, "key: %p", key); + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_key(key, "param: %s", p->key); + + default_get_params_fn = (OSSL_FUNC_keymgmt_get_params_fn *) + sk_prov_get_default_keymgmt_func(key->provctx, + key->type, OSSL_FUNC_KEYMGMT_GET_PARAMS); + + /* default_get_params_fn is optional */ + if (default_get_params_fn != NULL) { + if (!default_get_params_fn(key->default_key, params)) { + put_error_key(key, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_get_params_fn failed"); + return 0; + } + } + + if (key->secure_key == NULL) + return 1; + + p = OSSL_PARAM_locate(params, SK_PROV_PKEY_PARAM_SK_BLOB); + if (p != NULL && !OSSL_PARAM_set_octet_string(p, key->secure_key, + key->secure_key_size)) { + put_error_key(key, SK_PROV_ERR_INTERNAL_ERROR, + "OSSL_PARAM_set_octet_string failed"); + return 0; + } + p = OSSL_PARAM_locate(params, SK_PROV_PKEY_PARAM_SK_FUNCS); + if (p != NULL && !OSSL_PARAM_set_octet_ptr(p, key->funcs, + sizeof(struct sk_funcs))) { + put_error_key(key, SK_PROV_ERR_INTERNAL_ERROR, + "OSSL_PARAM_set_octet_ptr failed"); + return 0; + } + p = OSSL_PARAM_locate(params, SK_PROV_PKEY_PARAM_SK_PRIVATE); + if (p != NULL && !OSSL_PARAM_set_octet_ptr(p, key->private, 0)) { + put_error_key(key, SK_PROV_ERR_INTERNAL_ERROR, + "OSSL_PARAM_set_octet_ptr failed"); + return 0; + } + + return 1; +} + + +static int sk_prov_keymgmt_set_params(struct sk_prov_key *key, + OSSL_PARAM params[]) +{ + OSSL_FUNC_keymgmt_set_params_fn *default_set_params_fn; + const OSSL_PARAM *p; + size_t len; + + if (key == NULL) + return 0; + + sk_debug_key(key, "key: %p", key); + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_key(key, "param: %s", p->key); + + default_set_params_fn = (OSSL_FUNC_keymgmt_set_params_fn *) + sk_prov_get_default_keymgmt_func(key->provctx, + key->type, OSSL_FUNC_KEYMGMT_SET_PARAMS); + + /* default_set_params_fn is optional */ + if (default_set_params_fn != NULL) { + if (!default_set_params_fn(key->default_key, params)) { + put_error_key(key, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_set_params_fn failed"); + return 0; + } + } + + if (key->secure_key == NULL) + return 1; + + p = OSSL_PARAM_locate_const(params, SK_PROV_PKEY_PARAM_SK_FUNCS); + if (p != NULL && !OSSL_PARAM_get_octet_string_ptr(p, + (const void **)&key->funcs, &len)) { + put_error_key(key, SK_PROV_ERR_INTERNAL_ERROR, + "OSSL_PARAM_get_octet_string_ptr failed"); + return 0; + } + + p = OSSL_PARAM_locate_const(params, SK_PROV_PKEY_PARAM_SK_PRIVATE); + if (p != NULL && !OSSL_PARAM_get_octet_string_ptr(p, + (const void **)&key->private, &len)) { + put_error_key(key, SK_PROV_ERR_INTERNAL_ERROR, + "OSSL_PARAM_get_octet_string_ptr failed"); + return 0; + } + + return 1; +} + +#define SK_PROV_SECURE_KEY_FUNC_PARMS \ + OSSL_PARAM_octet_ptr(SK_PROV_PKEY_PARAM_SK_FUNCS, NULL, 0), \ + OSSL_PARAM_octet_ptr(SK_PROV_PKEY_PARAM_SK_PRIVATE, NULL, 0) + +#define SK_PROV_SECURE_KEY_PARMS \ + OSSL_PARAM_octet_string(SK_PROV_PKEY_PARAM_SK_BLOB, NULL, 0), \ + SK_PROV_SECURE_KEY_FUNC_PARMS + + +static const OSSL_PARAM sk_prov_key_settable_params[] = { + SK_PROV_SECURE_KEY_FUNC_PARMS, + OSSL_PARAM_END +}; + +static const OSSL_PARAM sk_prov_key_gettable_params[] = { + SK_PROV_SECURE_KEY_PARMS, + OSSL_PARAM_END +}; + +static const OSSL_PARAM *sk_prov_keymgmt_gettable_params( + struct sk_prov_ctx *provctx, int pkey_type) +{ + OSSL_FUNC_keymgmt_gettable_params_fn *default_gettable_params_fn; + const OSSL_PARAM *default_parms = NULL, *params, *p; + + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "pkey_type: %d", pkey_type); + + params = sk_prov_get_cached_params(provctx, pkey_type, + SK_CONF_CACHED_PARAMS_OP_KEY_GET, 0); + if (params != NULL) { + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_ctx(provctx, "param: %s", p->key); + return params; + } + + default_gettable_params_fn = (OSSL_FUNC_keymgmt_gettable_params_fn *) + sk_prov_get_default_keymgmt_func(provctx, pkey_type, + OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS); + + /* default_gettable_params_fn is optional */ + if (default_gettable_params_fn != NULL) + default_parms = + default_gettable_params_fn(provctx->default_provctx); + + return sk_prov_cached_params_build(provctx, pkey_type, + SK_CONF_CACHED_PARAMS_OP_KEY_GET, 0, + default_parms, + sk_prov_key_gettable_params); +} + +static const OSSL_PARAM *sk_prov_keymgmt_settable_params( + struct sk_prov_ctx *provctx, int pkey_type) +{ + OSSL_FUNC_keymgmt_settable_params_fn *default_settable_params_fn; + const OSSL_PARAM *default_parms = NULL, *params, *p; + + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "pkey_type: %d", pkey_type); + + params = sk_prov_get_cached_params(provctx, pkey_type, + SK_CONF_CACHED_PARAMS_OP_KEY_SET, 0); + if (params != NULL) { + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_ctx(provctx, "param: %s", p->key); + return params; + } + + default_settable_params_fn = (OSSL_FUNC_keymgmt_settable_params_fn *) + sk_prov_get_default_keymgmt_func(provctx, pkey_type, + OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS); + + /* default_settable_params_fn is optional */ + if (default_settable_params_fn != NULL) + default_parms = + default_settable_params_fn(provctx->default_provctx); + + return sk_prov_cached_params_build(provctx, pkey_type, + SK_CONF_CACHED_PARAMS_OP_KEY_SET, 0, + default_parms, + sk_prov_key_settable_params); +} + +static int sk_prov_keymgmt_has(const struct sk_prov_key *key, int selection) +{ + OSSL_FUNC_keymgmt_has_fn *default_has_fn; + int default_selection = selection; + + if (key == NULL) + return 0; + + sk_debug_key(key, "key: %p selection: %x", key, + selection); + + default_has_fn = (OSSL_FUNC_keymgmt_has_fn *) + sk_prov_get_default_keymgmt_func(key->provctx, + key->type, OSSL_FUNC_KEYMGMT_HAS); + if (default_has_fn == NULL) { + put_error_key(key, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default has_fn"); + return 0; + } + + /* A secure key doesn't have the private parts in the default key */ + if (key->secure_key != NULL) + default_selection &= (~OSSL_KEYMGMT_SELECT_PRIVATE_KEY); + + return default_has_fn(key->default_key, default_selection); +} + +struct sk_prov_export_cb { + struct sk_prov_key *key; + OSSL_CALLBACK *param_callback; + void *cbarg; +}; + +static int sk_prov_keymgmt_export_cb(const OSSL_PARAM params[], void *arg) +{ + struct sk_prov_export_cb *cb_data = arg; + OSSL_PARAM *np, *new_params; + struct sk_prov_key *key; + const OSSL_PARAM *p; + int rc, count, i; + + if (cb_data == NULL) + return 0; + + key = cb_data->key; + + sk_debug_key(key, "key: %p", key); + + for (count = 0, p = params; p != NULL && p->key != NULL; p++, count++) + ; + count += 3 + 1; /* 3 addl params plus end marker */ + + new_params = OPENSSL_zalloc(count * sizeof(OSSL_PARAM)); + if (new_params == NULL) { + put_error_key(key, SK_PROV_ERR_MALLOC_FAILED, + "OPENSSL_zalloc failed"); + return 0; + } + + for (i = 0, p = params; p != NULL && p->key != NULL; p++, i++) { + sk_debug_key(key, "param: key: %s", p->key); + new_params[i] = *p; + } + + np = &new_params[i++]; + *np = OSSL_PARAM_construct_octet_string(SK_PROV_PKEY_PARAM_SK_BLOB, + key->secure_key, + key->secure_key_size); + sk_debug_key(key, "param: key: %s", np->key); + + np = &new_params[i++]; + *np = OSSL_PARAM_construct_octet_ptr(SK_PROV_PKEY_PARAM_SK_FUNCS, + (void **)key->funcs, + sizeof(struct sk_funcs)); + sk_debug_key(key, "param: key: %s", np->key); + + np = &new_params[i++]; + *np = OSSL_PARAM_construct_octet_ptr(SK_PROV_PKEY_PARAM_SK_PRIVATE, + key->private, 0); + sk_debug_key(key, "param: key: %s", np->key); + + np = &new_params[i++]; + *np = OSSL_PARAM_construct_end(); + + rc = cb_data->param_callback(new_params, cb_data->cbarg); + if (rc != 1) + sk_debug_key(key, "ERROR: param_callback failed"); + + OPENSSL_free(new_params); + + return rc; +} + +static int sk_prov_keymgmt_export(struct sk_prov_key *key, int selection, + OSSL_CALLBACK *param_callback, void *cbarg) +{ + OSSL_FUNC_keymgmt_export_fn *default_export_fn; + struct sk_prov_export_cb cb_data; + + if (key == NULL || param_callback == NULL) + return 0; + + sk_debug_key(key, "key: %p selection: %x", key, selection); + + default_export_fn = (OSSL_FUNC_keymgmt_export_fn *) + sk_prov_get_default_keymgmt_func(key->provctx, + key->type, OSSL_FUNC_KEYMGMT_EXPORT); + if (default_export_fn == NULL) { + put_error_key(key, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default export_fn"); + return 0; + } + + if (key->secure_key == NULL || + (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) == 0) { + /* + * Clear key, or no private key selected, call default_export_fn + * with original callback + */ + if (!default_export_fn(key->default_key, selection, + param_callback, cbarg)) { + put_error_key(key, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_export_fn failed"); + return 0; + } + return 1; + } + + /* Let the callback add our 3 addl. params */ + cb_data.key = key; + cb_data.param_callback = param_callback; + cb_data.cbarg = cbarg; + if (!default_export_fn(key->default_key, selection, + sk_prov_keymgmt_export_cb, &cb_data)) { + put_error_key(key, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_export_fn failed"); + return 0; + } + + return 1; +} + +static int sk_prov_keymgmt_import(struct sk_prov_key *key, int selection, + const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p_blob, *p_funcs, *p_private, *p; + OSSL_FUNC_keymgmt_import_fn *default_import_fn; + size_t len; + + if (key == NULL) + return 0; + + sk_debug_key(key, "key: %p selection: %x", key, + selection); + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_key(key, "param: %s", p->key); + + default_import_fn = (OSSL_FUNC_keymgmt_import_fn *) + sk_prov_get_default_keymgmt_func(key->provctx, + key->type, OSSL_FUNC_KEYMGMT_IMPORT); + if (default_import_fn == NULL) { + put_error_key(key, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default import_fn"); + return 0; + } + + if (!default_import_fn(key->default_key, selection, params)) { + put_error_key(key, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_import_fn failed"); + return 0; + } + + if (key->secure_key != NULL) + OPENSSL_free(key->secure_key); + key->secure_key = NULL; + key->secure_key_size = 0; + key->funcs = NULL; + key->private = NULL; + + p_blob = OSSL_PARAM_locate_const(params, SK_PROV_PKEY_PARAM_SK_BLOB); + p_funcs = OSSL_PARAM_locate_const(params, SK_PROV_PKEY_PARAM_SK_FUNCS); + p_private = OSSL_PARAM_locate_const(params, + SK_PROV_PKEY_PARAM_SK_PRIVATE); + + if (p_blob != NULL && p_funcs != NULL && p_private != NULL) { + if (!OSSL_PARAM_get_octet_string(p_blob, + (void **)&key->secure_key, 0, + &key->secure_key_size)) { + put_error_key(key, SK_PROV_ERR_INTERNAL_ERROR, + "OSSL_PARAM_get_octet_string failed"); + return 0; + } + + if (!OSSL_PARAM_get_octet_string_ptr(p_funcs, + (const void **)&key->funcs, &len)) { + put_error_key(key, SK_PROV_ERR_INTERNAL_ERROR, + "OSSL_PARAM_get_octet_string_ptr failed"); + return 0; + } + + if (!OSSL_PARAM_get_octet_string_ptr(p_private, + (const void **)&key->private, &len)) { + put_error_key(key, SK_PROV_ERR_INTERNAL_ERROR, + "OSSL_PARAM_get_octet_string_ptr failed"); + return 0; + } + } + + return 1; +} + +static const OSSL_PARAM sk_prov_imexport_types[] = { + SK_PROV_SECURE_KEY_PARMS, + OSSL_PARAM_END +}; + +static const OSSL_PARAM *sk_prov_keymgmt_export_types(int selection, + int pkey_type) +{ + OSSL_FUNC_keymgmt_export_types_fn *default_export_types_fn; + const OSSL_PARAM *default_parms = NULL, *params; + struct sk_prov_ctx *provctx; + + provctx = OSSL_PROVIDER_get0_provider_ctx(sk_prov_securekey_provider); + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "selection: %d pkey_type: %d", selection, + pkey_type); + + params = sk_prov_get_cached_params(provctx, pkey_type, + SK_CONF_CACHED_PARAMS_OP_KEY_EXPORT, + selection); + if (params != NULL) + return params; + + default_export_types_fn = (OSSL_FUNC_keymgmt_export_types_fn *) + sk_prov_get_default_keymgmt_func(provctx, pkey_type, + OSSL_FUNC_KEYMGMT_EXPORT_TYPES); + + /* default_export_types_fn is optional */ + if (default_export_types_fn != NULL) + default_parms = default_export_types_fn(selection); + + return sk_prov_cached_params_build(provctx, pkey_type, + SK_CONF_CACHED_PARAMS_OP_KEY_EXPORT, + selection, + default_parms, + sk_prov_imexport_types); +} + +static const OSSL_PARAM *sk_prov_keymgmt_import_types(int selection, + int pkey_type) +{ + OSSL_FUNC_keymgmt_import_types_fn *default_import_types_fn; + const OSSL_PARAM *default_parms = NULL, *params; + struct sk_prov_ctx *provctx; + + provctx = OSSL_PROVIDER_get0_provider_ctx(sk_prov_securekey_provider); + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "selection: %d pkey_type: %d", selection, + pkey_type); + + params = sk_prov_get_cached_params(provctx, pkey_type, + SK_CONF_CACHED_PARAMS_OP_KEY_IMPORT, + selection); + if (params != NULL) + return params; + + default_import_types_fn = (OSSL_FUNC_keymgmt_import_types_fn *) + sk_prov_get_default_keymgmt_func(provctx, pkey_type, + OSSL_FUNC_KEYMGMT_EXPORT_TYPES); + + /* default_import_types_fn is optional */ + if (default_import_types_fn != NULL) + default_parms = default_import_types_fn(selection); + + return sk_prov_cached_params_build(provctx, pkey_type, + SK_CONF_CACHED_PARAMS_OP_KEY_IMPORT, + selection, + default_parms, + sk_prov_imexport_types); +} + +static struct sk_prov_op_ctx *sk_prov_keymgmt_gen_init( + struct sk_prov_ctx *provctx, int selection, + const OSSL_PARAM params[], int pkey_type) +{ + OSSL_FUNC_keymgmt_gen_cleanup_fn *default_gen_cleanup_fn; + OSSL_FUNC_keymgmt_gen_init_fn *default_gen_init_fn; + struct sk_prov_op_ctx *genctx; + const OSSL_PARAM *p; + + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "selection: %x type: %d", selection, pkey_type); + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_ctx(provctx, "param: %s", p->key); + + default_gen_init_fn = (OSSL_FUNC_keymgmt_gen_init_fn *) + sk_prov_get_default_keymgmt_func(provctx, pkey_type, + OSSL_FUNC_KEYMGMT_GEN_INIT); + if (default_gen_init_fn == NULL) { + put_error_ctx(provctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default gen_init_fn"); + return NULL; + } + + default_gen_cleanup_fn = (OSSL_FUNC_keymgmt_gen_cleanup_fn *) + sk_prov_get_default_keymgmt_func(provctx, pkey_type, + OSSL_FUNC_KEYMGMT_GEN_CLEANUP); + if (default_gen_cleanup_fn == NULL) { + put_error_ctx(provctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default gen_cleanup_fn"); + return NULL; + } + + genctx = sk_prov_op_newctx(provctx, NULL, pkey_type); + if (genctx == NULL) { + put_error_ctx(provctx, SK_PROV_ERR_INTERNAL_ERROR, + "sk_prov_op_newctx failed"); + return NULL; + } + + if (!sk_prov_op_init(genctx, NULL, EVP_PKEY_OP_KEYGEN)) { + put_error_ctx(provctx, SK_PROV_ERR_INTERNAL_ERROR, + "sk_prov_op_init failed"); + sk_prov_op_freectx(genctx); + return NULL; + } + + genctx->default_op_ctx = default_gen_init_fn(provctx->default_provctx, + selection, params); + if (genctx->default_op_ctx == NULL) { + put_error_ctx(provctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_gen_init_fn failed"); + sk_prov_op_freectx(genctx); + return NULL; + } + genctx->default_op_ctx_free = default_gen_cleanup_fn; + + sk_debug_ctx(provctx, "genctx: %p", genctx); + return genctx; +} + +static void sk_prov_keymgmt_gen_cleanup(struct sk_prov_op_ctx *genctx) +{ + if (genctx == NULL) + return; + + sk_debug_op_ctx(genctx, "genctx: %p", genctx); + sk_prov_op_freectx(genctx); +} + +static int sk_prov_keymgmt_gen_set_params(struct sk_prov_op_ctx *genctx, + const OSSL_PARAM params[]) +{ + OSSL_FUNC_keymgmt_gen_set_params_fn *default_gen_set_params_fn; + const OSSL_PARAM *p; + + if (genctx == NULL) + return 0; + + sk_debug_op_ctx(genctx, "genctx: %p", genctx); + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_op_ctx(genctx, "param: %s", p->key); + + default_gen_set_params_fn = (OSSL_FUNC_keymgmt_gen_set_params_fn *) + sk_prov_get_default_keymgmt_func(genctx->provctx, + genctx->type, OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS); + + /* default_gen_set_params_fn is optional */ + if (default_gen_set_params_fn != NULL) { + if (!default_gen_set_params_fn(genctx->default_op_ctx, + params)) { + put_error_op_ctx(genctx, + SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_gen_set_params_fn failed"); + return 0; + } + } + + return 1; +} + +static const OSSL_PARAM *sk_prov_keymgmt_gen_settable_params( + struct sk_prov_op_ctx *genctx, + struct sk_prov_ctx *provctx, + int pkey_type) +{ + OSSL_FUNC_keymgmt_gen_settable_params_fn + *default_gen_settable_params_fn; + const OSSL_PARAM *params = NULL, *p; + + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "pkey_type: %d", pkey_type); + + default_gen_settable_params_fn = + (OSSL_FUNC_keymgmt_gen_settable_params_fn *) + sk_prov_get_default_keymgmt_func(provctx, + pkey_type, + OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS); + + /* default_gen_settable_params_fn is optional */ + if (default_gen_settable_params_fn != NULL) + params = default_gen_settable_params_fn(genctx->default_op_ctx, + provctx->default_provctx); + + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_ctx(provctx, "param: %s", p->key); + + return params; +} + +static int sk_prov_keymgmt_gen_set_template(struct sk_prov_op_ctx *genctx, + struct sk_prov_key *templ) +{ + OSSL_FUNC_keymgmt_gen_set_template_fn *default_gen_set_template_fn; + + if (genctx == NULL || templ == NULL) + return 0; + + sk_debug_op_ctx(genctx, "genctx: %p templ: %p", genctx, templ); + + default_gen_set_template_fn = (OSSL_FUNC_keymgmt_gen_set_template_fn *) + sk_prov_get_default_keymgmt_func(genctx->provctx, + genctx->type, + OSSL_FUNC_KEYMGMT_GEN_SET_TEMPLATE); + + if (default_gen_set_template_fn == NULL) { + put_error_op_ctx(genctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default get_set_template_fn"); + return 0; + } + + return default_gen_set_template_fn(genctx->default_op_ctx, + templ->default_key); +} + +static struct sk_prov_key *sk_prov_keymgmt_gen(struct sk_prov_op_ctx *genctx, + OSSL_CALLBACK *osslcb, void *cbarg) +{ + OSSL_FUNC_keymgmt_gen_fn *default_gen_fn; + struct sk_prov_key *key; + + if (genctx == NULL) + return NULL; + + sk_debug_op_ctx(genctx, "genctx: %p", genctx); + + default_gen_fn = (OSSL_FUNC_keymgmt_gen_fn *) + sk_prov_get_default_keymgmt_func(genctx->provctx, + genctx->type, OSSL_FUNC_KEYMGMT_GEN); + + if (default_gen_fn == NULL) { + put_error_op_ctx(genctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default gen_fn"); + return NULL; + } + + key = OPENSSL_zalloc(sizeof(struct sk_prov_key)); + if (key == NULL) { + put_error_op_ctx(genctx, SK_PROV_ERR_MALLOC_FAILED, + "OPENSSL_zalloc failed"); + return NULL; + } + + key->provctx = genctx->provctx; + key->type = genctx->type; + + key->default_key = default_gen_fn(genctx->default_op_ctx, + osslcb, cbarg); + if (key->default_key == NULL) { + put_error_op_ctx(genctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_gen_fn failed"); + OPENSSL_free(key); + return NULL; + } + + sk_prov_keymgmt_upref(key); + + sk_debug_op_ctx(genctx, "key: %p", key); + + return key; +} + +static struct sk_prov_key *sk_prov_keymgmt_load(const void *reference, + size_t reference_sz) +{ + struct sk_prov_key *key; + + if (reference == NULL) + return NULL; + + if (reference_sz == sizeof(struct sk_prov_key)) { + /* The contents of the reference is the address to our object */ + key = *(struct sk_prov_key **)reference; + + /* We grabbed, so we detach it */ + *(struct sk_prov_key **)reference = NULL; + return key; + } + + return NULL; +} + +static int sk_prov_keymgmt_get_size(struct sk_prov_key *key) +{ + int size = 0; + OSSL_PARAM key_params[] = { + OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, &size), + OSSL_PARAM_END + }; + + sk_debug_key(key, "key: %p", key); + + if (!sk_prov_keymgmt_get_params(key, key_params) || + !OSSL_PARAM_modified(&key_params[0]) || + size <= 0) { + put_error_key(key, SK_PROV_ERR_MISSING_PARAMETER, + "sk_prov_keymgmt_get_params failed to " + "get OSSL_PKEY_PARAM_MAX_SIZE"); + return -1; + } + + sk_debug_key(key, "size: %d", size); + return size; +} + +static int sk_prov_keymgmt_get_bits(struct sk_prov_key *key) +{ + int bits = 0; + OSSL_PARAM key_params[] = { + OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, &bits), + OSSL_PARAM_END + }; + + sk_debug_key(key, "key: %p", key); + + if (!sk_prov_keymgmt_get_params(key, key_params) || + !OSSL_PARAM_modified(&key_params[0]) || + bits <= 0) { + put_error_key(key, SK_PROV_ERR_MISSING_PARAMETER, + "sk_prov_keymgmt_get_params failed to " + "get OSSL_PKEY_PARAM_BITS"); + return -1; + } + + sk_debug_key(key, "bits: %d", bits); + return bits; +} + +static void *sk_prov_keymgmt_rsa_new(struct sk_prov_ctx *provctx) +{ + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "provctx: %p", provctx); + + return sk_prov_keymgmt_new(provctx, EVP_PKEY_RSA); +} + +static const char *sk_prov_keymgmt_rsa_query_operation_name(int operation_id) +{ + switch (operation_id) { + case OSSL_OP_SIGNATURE: + case OSSL_OP_ASYM_CIPHER: + return "RSA"; + } + + return NULL; +} + +static const OSSL_PARAM *sk_prov_keymgmt_rsa_gettable_params( + struct sk_prov_ctx *provctx) +{ + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "provctx: %p", provctx); + return sk_prov_keymgmt_gettable_params(provctx, EVP_PKEY_RSA); +} + +static const OSSL_PARAM *sk_prov_keymgmt_rsa_settable_params( + struct sk_prov_ctx *provctx) +{ + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "provctx: %p", provctx); + return sk_prov_keymgmt_settable_params(provctx, EVP_PKEY_RSA); +} + +static const OSSL_PARAM *sk_prov_keymgmt_rsa_export_types(int selection) +{ + return sk_prov_keymgmt_export_types(selection, EVP_PKEY_RSA); +} + +static const OSSL_PARAM *sk_prov_keymgmt_rsa_import_types(int selection) +{ + return sk_prov_keymgmt_import_types(selection, EVP_PKEY_RSA); +} + +static struct sk_prov_op_ctx *sk_prov_keymgmt_rsa_gen_init( + struct sk_prov_ctx *provctx, int selection, + const OSSL_PARAM params[]) +{ + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "provctx: %p", provctx); + return sk_prov_keymgmt_gen_init(provctx, selection, params, + EVP_PKEY_RSA); +} + +static const OSSL_PARAM *sk_prov_keymgmt_rsa_gen_settable_params( + struct sk_prov_op_ctx *genctx, + struct sk_prov_ctx *provctx) +{ + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "provctx: %p", provctx); + return sk_prov_keymgmt_gen_settable_params(genctx, provctx, + EVP_PKEY_RSA); +} + +static void *sk_prov_keymgmt_rsa_pss_new(struct sk_prov_ctx *provctx) +{ + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "provctx: %p", provctx); + return sk_prov_keymgmt_new(provctx, EVP_PKEY_RSA_PSS); +} + +static const OSSL_PARAM *sk_prov_keymgmt_rsa_pss_gettable_params( + struct sk_prov_ctx *provctx) +{ + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "provctx: %p", provctx); + return sk_prov_keymgmt_gettable_params(provctx, EVP_PKEY_RSA_PSS); +} + +static const OSSL_PARAM *sk_prov_keymgmt_rsa_pss_settable_params( + struct sk_prov_ctx *provctx) +{ + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "provctx: %p", provctx); + return sk_prov_keymgmt_settable_params(provctx, EVP_PKEY_RSA_PSS); +} + +static const OSSL_PARAM *sk_prov_keymgmt_rsa_pss_export_types(int selection) +{ + return sk_prov_keymgmt_export_types(selection, EVP_PKEY_RSA_PSS); +} + +static const OSSL_PARAM *sk_prov_keymgmt_rsa_pss_import_types(int selection) +{ + return sk_prov_keymgmt_import_types(selection, EVP_PKEY_RSA_PSS); +} + +static struct sk_prov_op_ctx *sk_prov_keymgmt_rsa_pss_gen_init( + struct sk_prov_ctx *provctx, int selection, + const OSSL_PARAM params[]) +{ + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "provctx: %p", provctx); + return sk_prov_keymgmt_gen_init(provctx, selection, params, + EVP_PKEY_RSA_PSS); +} + +static const OSSL_PARAM *sk_prov_keymgmt_rsa_pss_gen_settable_params( + struct sk_prov_op_ctx *genctx, + struct sk_prov_ctx *provctx) +{ + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "provctx: %p", provctx); + return sk_prov_keymgmt_gen_settable_params(genctx, provctx, + EVP_PKEY_RSA_PSS); +} + +static void *sk_prov_keymgmt_ec_new(struct sk_prov_ctx *provctx) +{ + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "provctx: %p", provctx); + return sk_prov_keymgmt_new(provctx, EVP_PKEY_EC); +} + +static const char *sk_prov_keymgmt_ec_query_operation_name(int operation_id) +{ + switch (operation_id) { + case OSSL_OP_KEYEXCH: + return "ECDH"; + case OSSL_OP_SIGNATURE: + return "ECDSA"; + } + + return NULL; +} + +static const OSSL_PARAM *sk_prov_keymgmt_ec_gettable_params( + struct sk_prov_ctx *provctx) +{ + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "provctx: %p", provctx); + return sk_prov_keymgmt_gettable_params(provctx, EVP_PKEY_EC); +} + +static const OSSL_PARAM *sk_prov_keymgmt_ec_settable_params( + struct sk_prov_ctx *provctx) +{ + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "provctx: %p", provctx); + return sk_prov_keymgmt_settable_params(provctx, EVP_PKEY_EC); +} + +static const OSSL_PARAM *sk_prov_keymgmt_ec_export_types(int selection) +{ + return sk_prov_keymgmt_export_types(selection, EVP_PKEY_EC); +} + +static const OSSL_PARAM *sk_prov_keymgmt_ec_import_types(int selection) +{ + return sk_prov_keymgmt_import_types(selection, EVP_PKEY_EC); +} + +static struct sk_prov_op_ctx *sk_prov_keymgmt_ec_gen_init( + struct sk_prov_ctx *provctx, int selection, + const OSSL_PARAM params[]) +{ + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "provctx: %p", provctx); + return sk_prov_keymgmt_gen_init(provctx, selection, params, + EVP_PKEY_EC); +} + +static const OSSL_PARAM *sk_prov_keymgmt_ec_gen_settable_params( + struct sk_prov_op_ctx *genctx, + struct sk_prov_ctx *provctx) +{ + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "provctx: %p", provctx); + return sk_prov_keymgmt_gen_settable_params(genctx, provctx, + EVP_PKEY_EC); +} + +static struct sk_prov_op_ctx *sk_prov_keyexch_ec_newctx( + struct sk_prov_ctx *provctx) +{ + OSSL_FUNC_keyexch_freectx_fn *default_freectx_fn; + OSSL_FUNC_keyexch_newctx_fn *default_newctx_fn; + struct sk_prov_op_ctx *ctx; + + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "provctx: %p", provctx); + + default_newctx_fn = (OSSL_FUNC_keyexch_newctx_fn *) + sk_prov_get_default_keyexch_func(provctx, + OSSL_FUNC_KEYEXCH_NEWCTX); + if (default_newctx_fn == NULL) { + put_error_ctx(provctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default newctx_fn"); + return NULL; + } + + default_freectx_fn = (OSSL_FUNC_keyexch_freectx_fn *) + sk_prov_get_default_keyexch_func(provctx, + OSSL_FUNC_KEYEXCH_FREECTX); + if (default_freectx_fn == NULL) { + put_error_ctx(provctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default freectx_fn"); + return NULL; + } + + ctx = sk_prov_op_newctx(provctx, NULL, EVP_PKEY_EC); + if (ctx == NULL) { + sk_debug_ctx(provctx, "ERROR: sk_prov_op_newctx failed"); + return NULL; + } + + ctx->default_op_ctx = default_newctx_fn(provctx->default_provctx); + if (ctx->default_op_ctx == NULL) { + put_error_ctx(provctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_newctx_fn failed"); + sk_prov_op_freectx(ctx); + return NULL; + } + ctx->default_op_ctx_free = default_freectx_fn; + + sk_debug_ctx(provctx, "ctx: %p", ctx); + + return ctx; +} + +static void *sk_prov_keyexch_ec_dupctx(struct sk_prov_op_ctx *ctx) +{ + OSSL_FUNC_keyexch_dupctx_fn *default_dupctx_fn; + struct sk_prov_op_ctx *new_ctx; + + if (ctx == NULL) + return NULL; + + sk_debug_op_ctx(ctx, "ctx: %p", ctx); + + default_dupctx_fn = (OSSL_FUNC_keyexch_dupctx_fn *) + sk_prov_get_default_keyexch_func(ctx->provctx, + OSSL_FUNC_KEYEXCH_DUPCTX); + if (default_dupctx_fn == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default dupctx_fn"); + return NULL; + } + + new_ctx = sk_prov_op_dupctx(ctx); + if (new_ctx == NULL) { + sk_debug_op_ctx(ctx, "ERROR: sk_prov_op_dupctx failed"); + return NULL; + } + + new_ctx->default_op_ctx = default_dupctx_fn(ctx->default_op_ctx); + if (new_ctx->default_op_ctx == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_dupctx_fn failed"); + sk_prov_op_freectx(new_ctx); + return NULL; + } + + sk_debug_op_ctx(ctx, "new_ctx: %p", new_ctx); + return new_ctx; +} + +static int sk_prov_keyexch_ec_init(struct sk_prov_op_ctx *ctx, + struct sk_prov_key *key, + const OSSL_PARAM params[]) +{ + OSSL_FUNC_keyexch_init_fn *default_init_fn; + const OSSL_PARAM *p; + + if (ctx == NULL || key == NULL) + return 0; + + sk_debug_op_ctx(ctx, "ctx: %p key: %p", ctx, key); + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_op_ctx(ctx, "param: %s", p->key); + + default_init_fn = (OSSL_FUNC_keyexch_init_fn *) + sk_prov_get_default_keyexch_func(ctx->provctx, + OSSL_FUNC_KEYEXCH_INIT); + if (default_init_fn == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default init_fn"); + return 0; + } + + if (!sk_prov_op_init(ctx, key, EVP_PKEY_OP_DERIVE)) { + sk_debug_op_ctx(ctx, "ERROR: sk_prov_op_init failed"); + return 0; + } + + if (!default_init_fn(ctx->default_op_ctx, key->default_key, params)) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_init_fn failed"); + return 0; + } + + return 1; +} + +static int sk_prov_keyexch_ec_set_peer(struct sk_prov_op_ctx *ctx, + struct sk_prov_key *peerkey) + +{ + OSSL_FUNC_keyexch_set_peer_fn *default_set_peer_fn; + + if (ctx == NULL || peerkey == NULL) + return 0; + + sk_debug_op_ctx(ctx, "ctx: %p key: %p peerkey: %p", ctx, ctx->key, + peerkey); + + default_set_peer_fn = (OSSL_FUNC_keyexch_set_peer_fn *) + sk_prov_get_default_keyexch_func(ctx->provctx, + OSSL_FUNC_KEYEXCH_SET_PEER); + if (default_set_peer_fn == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default set_peer_fn"); + return 0; + } + + if (ctx->key == NULL || ctx->operation != EVP_PKEY_OP_DERIVE) { + put_error_op_ctx(ctx, SK_PROV_ERR_OPRATION_NOT_INITIALIZED, + "derive operation not initialized"); + return 0; + } + + if (!default_set_peer_fn(ctx->default_op_ctx, peerkey->default_key)) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_set_peer_fn failed"); + return 0; + } + + return 1; +} + +static int sk_prov_keyexch_ec_derive(struct sk_prov_op_ctx *ctx, + unsigned char *secret, size_t *secretlen, + size_t outlen) +{ + OSSL_FUNC_keyexch_derive_fn *default_derive_fn; + + if (ctx == NULL || secretlen == NULL) + return 0; + + sk_debug_op_ctx(ctx, "ctx: %p key: %p outlen: %lu", ctx, ctx->key, + outlen); + + default_derive_fn = (OSSL_FUNC_keyexch_derive_fn *) + sk_prov_get_default_keyexch_func(ctx->provctx, + OSSL_FUNC_KEYEXCH_DERIVE); + if (default_derive_fn == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, + "no default derive_fn"); + return 0; + } + + if (ctx->key == NULL || ctx->operation != EVP_PKEY_OP_DERIVE) { + put_error_op_ctx(ctx, SK_PROV_ERR_OPRATION_NOT_INITIALIZED, + "derive operation not initialized"); + return 0; + } + + if (!default_derive_fn(ctx->default_op_ctx, secret, secretlen, + outlen)) { + put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_derive_fn failed"); + return 0; + } + + sk_debug_op_ctx(ctx, "secretlen: %lu", *secretlen); + + return 1; +} + +static int sk_prov_keyexch_ec_set_ctx_params(struct sk_prov_op_ctx *ctx, + const OSSL_PARAM params[]) +{ + OSSL_FUNC_keyexch_set_ctx_params_fn *default_set_params_fn; + const OSSL_PARAM *p; + + if (ctx == NULL) + return 0; + + sk_debug_op_ctx(ctx, "ctx: %p", ctx); + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_op_ctx(ctx, "param: %s", p->key); + + default_set_params_fn = (OSSL_FUNC_keyexch_set_ctx_params_fn *) + sk_prov_get_default_keyexch_func(ctx->provctx, + OSSL_FUNC_KEYEXCH_SET_CTX_PARAMS); + + /* default_set_params_fn is optional */ + if (default_set_params_fn != NULL) { + if (!default_set_params_fn(ctx->default_op_ctx, params)) { + put_error_op_ctx(ctx, + SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_set_params_fn failed"); + return 0; + } + } + + return 1; + +} + +static const OSSL_PARAM *sk_prov_keyexch_ec_settable_ctx_params( + struct sk_prov_op_ctx *ctx, + struct sk_prov_ctx *provctx) +{ + OSSL_FUNC_keyexch_settable_ctx_params_fn + *default_settable_params_fn; + const OSSL_PARAM *params = NULL, *p; + + if (ctx == NULL || provctx == NULL) + return NULL; + + default_settable_params_fn = + (OSSL_FUNC_keyexch_settable_ctx_params_fn *) + sk_prov_get_default_keyexch_func(provctx, + OSSL_FUNC_KEYEXCH_SETTABLE_CTX_PARAMS); + + /* default_settable_params_fn is optional */ + if (default_settable_params_fn != NULL) + params = default_settable_params_fn(ctx->default_op_ctx, + provctx->default_provctx); + + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_ctx(provctx, "param: %s", p->key); + + return params; +} + +static int sk_prov_keyexch_ec_get_ctx_params(struct sk_prov_op_ctx *ctx, + OSSL_PARAM params[]) +{ + OSSL_FUNC_keyexch_get_ctx_params_fn *default_get_params_fn; + const OSSL_PARAM *p; + + if (ctx == NULL) + return 0; + + sk_debug_op_ctx(ctx, "ctx: %p", ctx); + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_op_ctx(ctx, "param: %s", p->key); + + default_get_params_fn = (OSSL_FUNC_keyexch_get_ctx_params_fn *) + sk_prov_get_default_keyexch_func(ctx->provctx, + OSSL_FUNC_KEYEXCH_GET_CTX_PARAMS); + + /* default_get_params_fn is optional */ + if (default_get_params_fn != NULL) { + if (!default_get_params_fn(ctx->default_op_ctx, params)) { + put_error_op_ctx(ctx, + SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, + "default_get_params_fn failed"); + return 0; + } + } + + return 1; +} + +static const OSSL_PARAM *sk_prov_keyexch_ec_gettable_ctx_params( + struct sk_prov_op_ctx *ctx, + struct sk_prov_ctx *provctx) +{ + OSSL_FUNC_keyexch_gettable_ctx_params_fn + *default_gettable_params_fn; + const OSSL_PARAM *params = NULL, *p; + + if (ctx == NULL || provctx == NULL) + return NULL; + + default_gettable_params_fn = + (OSSL_FUNC_keyexch_gettable_ctx_params_fn *) + sk_prov_get_default_keyexch_func(provctx, + OSSL_FUNC_KEYEXCH_GETTABLE_CTX_PARAMS); + + /* default_settable_params_fn is optional */ + if (default_gettable_params_fn != NULL) + params = default_gettable_params_fn(ctx->default_op_ctx, + provctx->default_provctx); + + for (p = params; p != NULL && p->key != NULL; p++) + sk_debug_ctx(provctx, "param: %s", p->key); + + return params; +} + +static struct sk_prov_op_ctx *sk_prov_asym_rsa_newctx( + struct sk_prov_ctx *provctx) +{ + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "provctx: %p", provctx); + return sk_prov_asym_op_newctx(provctx, EVP_PKEY_RSA); +} + +static const OSSL_PARAM *sk_prov_asym_rsa_gettable_ctx_params( + struct sk_prov_op_ctx *ctx, + struct sk_prov_ctx *provctx) +{ + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "provctx: %p", provctx); + return sk_prov_asym_op_gettable_ctx_params(ctx, provctx, EVP_PKEY_RSA); +} + +static const OSSL_PARAM *sk_prov_asym_rsa_settable_ctx_params( + struct sk_prov_op_ctx *ctx, + struct sk_prov_ctx *provctx) +{ + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "provctx: %p", provctx); + return sk_prov_asym_op_settable_ctx_params(ctx, provctx, EVP_PKEY_RSA); +} + +static int sk_prov_asym_rsa_decrypt(struct sk_prov_op_ctx *ctx, + unsigned char *out, size_t *outlen, + size_t outsize, const unsigned char *in, + size_t inlen) +{ + int rsa_size, pad_mode, oaep_label_len = 0, rc; + EVP_MD *oaep_md = NULL, *mgf_md = NULL; + unsigned char *oaep_label = NULL; + unsigned char *tmp = NULL; + struct sk_prov_key *key; + struct sk_funcs *funcs; + + if (ctx == NULL || in == NULL || outlen == NULL) + return 0; + + sk_debug_op_ctx(ctx, "ctx: %p key: %p inlen: %lu outsize: %lu", + ctx, ctx->key, inlen, outsize); + + if (ctx->key == NULL || ctx->operation != EVP_PKEY_OP_DECRYPT) { + put_error_op_ctx(ctx, SK_PROV_ERR_OPRATION_NOT_INITIALIZED, + "decrypt operation not initialized"); + return 0; + } + + /* For clear key, let the default provider handle it */ + if (ctx->key->secure_key == NULL) + return sk_prov_asym_op_decrypt(ctx, out, outlen, outsize, + in, inlen); + + funcs = ctx->key->funcs; + if (funcs == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, + "no secure key funcs"); + return 0; + } + + key = ctx->key; + + rsa_size = sk_prov_keymgmt_get_size(key); + if (rsa_size <= 0) { + sk_debug_op_ctx(ctx, "sk_prov_keymgmt_get_size failed"); + return 0; + } + + if (out == NULL) { + tmp = OPENSSL_zalloc(rsa_size); + if (tmp == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_MALLOC_FAILED, + "OPENSSL_zalloc failed"); + return 0; + } + out = tmp; + outsize = rsa_size; + } + + if (outsize < (size_t)rsa_size) { + put_error_op_ctx(ctx, SK_PROV_ERR_INVALID_PARAM, + "output buffer length invalid"); + return 0; + } + + pad_mode = sk_prov_asym_op_get_padding(ctx); + switch (pad_mode) { + case RSA_NO_PADDING: + case RSA_PKCS1_PADDING: + case RSA_X931_PADDING: + break; + + case RSA_PKCS1_OAEP_PADDING: + oaep_label_len = sk_prov_asym_op_get_oaep_label(ctx, + &oaep_label); + if (oaep_label_len < 0) { + sk_debug_op_ctx(ctx, + "ERROR: sk_prov_rsa_asym_get_oaep_label failed"); + rc = 0; + goto out; + } + + oaep_md = sk_prov_asym_op_get_oaep_md(ctx); + if (oaep_md == NULL) { + sk_debug_op_ctx(ctx, + "ERROR: sk_prov_asym_op_get_oaep_md failed"); + rc = 0; + goto out; + } + + mgf_md = sk_prov_asym_op_get_mgf_md(ctx); + if (mgf_md == NULL) { + sk_debug_op_ctx(ctx, + "ERROR: sk_prov_asym_op_get_mgf_md failed"); + rc = 0; + goto out; + } + break; + default: + put_error_op_ctx(ctx, SK_PROV_ERR_INVALID_PADDING, + "unknown/unsupported padding: %d", pad_mode); + return 0; + } + + *outlen = outsize; + + switch (pad_mode) { + case RSA_PKCS1_OAEP_PADDING: + if (funcs->rsa_decrypt_oaep == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, + "no secure key decrypt function"); + rc = 0; + goto out; + } + + rc = funcs->rsa_decrypt_oaep(key->secure_key, + key->secure_key_size, + out, outlen, in, inlen, + EVP_MD_type(oaep_md), + EVP_MD_type(mgf_md), + oaep_label, oaep_label_len, + key->private, ctx->provctx->debug); + break; + + default: + if (funcs->rsa_decrypt == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, + "no secure key decrypt function"); + rc = 0; + goto out; + } + + rc = funcs->rsa_decrypt(key->secure_key, key->secure_key_size, + out, outlen, in, inlen, pad_mode, + key->private, ctx->provctx->debug); + break; + } + + if (tmp != NULL) { + OPENSSL_cleanse(tmp, outsize); + OPENSSL_free(tmp); + } + + if (rc != 0) { + put_error_op_ctx(ctx, SK_PROV_ERR_SECURE_KEY_FUNC_FAILED, + "Secure key encrypt operation failed: rc: %d", + rc); + rc = 0; + goto out; + } + + rc = 1; + + sk_debug_op_ctx(ctx, "outlen: %lu", *outlen); + +out: + if (oaep_md != NULL) + EVP_MD_free(oaep_md); + if (mgf_md != NULL) + EVP_MD_free(mgf_md); + + return rc; +} + +static struct sk_prov_op_ctx *sk_prov_sign_rsa_newctx( + struct sk_prov_ctx *provctx, + const char *propq) +{ + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "provctx: %p propq: %s", provctx, + propq != NULL ? propq : ""); + return sk_prov_sign_op_newctx(provctx, propq, EVP_PKEY_RSA); +} + +static const OSSL_PARAM *sk_prov_sign_rsa_gettable_ctx_params( + struct sk_prov_op_ctx *ctx, + struct sk_prov_ctx *provctx) +{ + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "provctx: %p", provctx); + return sk_prov_sign_op_gettable_ctx_params(ctx, provctx, EVP_PKEY_RSA); +} + +static const OSSL_PARAM *sk_prov_sign_rsa_settable_ctx_params( + struct sk_prov_op_ctx *ctx, + struct sk_prov_ctx *provctx) +{ + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "provctx: %p", provctx); + return sk_prov_sign_op_settable_ctx_params(ctx, provctx, EVP_PKEY_RSA); +} + +static const OSSL_PARAM *sk_prov_sign_rsa_gettable_ctx_md_params( + struct sk_prov_op_ctx *ctx) +{ + if (ctx == NULL) + return NULL; + + sk_debug_op_ctx(ctx, "ctx: %p", ctx); + return sk_prov_sign_op_gettable_ctx_md_params(ctx, EVP_PKEY_RSA); +} + +static const OSSL_PARAM *sk_prov_sign_rsa_settable_ctx_md_params( + struct sk_prov_op_ctx *ctx) +{ + if (ctx == NULL) + return NULL; + + sk_debug_op_ctx(ctx, "ctx: %p", ctx); + return sk_prov_sign_op_settable_ctx_md_params(ctx, EVP_PKEY_RSA); +} + +static int sk_prov_sign_rsa_sign(struct sk_prov_op_ctx *ctx, + unsigned char *sig, size_t *siglen, + size_t sigsize, + const unsigned char *tbs, size_t tbslen) +{ + EVP_MD *sign_md = NULL, *mgf_md = NULL; + int rsa_size, pad_mode, salt_len, rc; + struct sk_prov_key *key; + struct sk_funcs *funcs; + + if (ctx == NULL || siglen == NULL || tbs == NULL) + return 0; + + sk_debug_op_ctx(ctx, "ctx: %p key: %p tbslen: %lu sigsize: %lu", + ctx, ctx->key, tbslen, sigsize); + + if (ctx->key == NULL || ctx->operation != EVP_PKEY_OP_SIGN) { + put_error_op_ctx(ctx, SK_PROV_ERR_OPRATION_NOT_INITIALIZED, + "sign operation not initialized"); + return 0; + } + + /* For clear key, let the default provider handle it */ + if (ctx->key->secure_key == NULL) + return sk_prov_sign_op_sign(ctx, sig, siglen, sigsize, + tbs, tbslen); + + key = ctx->key; + + rsa_size = sk_prov_keymgmt_get_size(key); + if (rsa_size <= 0) { + sk_debug_op_ctx(ctx, "ERROR: sk_prov_keymgmt_get_size failed"); + return 0; + } + + if (sig == NULL) { + *siglen = rsa_size; + sk_debug_op_ctx(ctx, "siglen: %lu", *siglen); + return 1; + } + + if (sigsize < (size_t)rsa_size) { + put_error_op_ctx(ctx, SK_PROV_ERR_INVALID_PARAM, + "signature length invalid"); + return 0; + } + + funcs = ctx->key->funcs; + if (funcs == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, + "no secure key funcs"); + return 0; + } + + sign_md = sk_prov_sign_op_get_md(ctx); + pad_mode = sk_prov_sign_op_get_padding(ctx); + + if (sign_md == NULL) { + /* Sign without a signature digest, fall back to no padding */ + pad_mode = RSA_NO_PADDING; + } else { + if (tbslen != (size_t)EVP_MD_size(sign_md)) { + put_error_op_ctx(ctx, SK_PROV_ERR_INVALID_PARAM, + "tbslen must be size of digest"); + rc = 0; + goto out; + } + } + + *siglen = rsa_size; + + switch (pad_mode) { + case RSA_PKCS1_PADDING: + case RSA_X931_PADDING: + if (sign_md == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, + "padding needs a signature digest"); + rc = 0; + goto out; + } + /* fall through */ + + case RSA_NO_PADDING: + if (funcs->rsa_sign == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, + "no secure key sign function"); + rc = 0; + goto out; + + } + + rc = funcs->rsa_sign(key->secure_key, key->secure_key_size, + sig, siglen, tbs, tbslen, pad_mode, + sign_md != NULL ? + EVP_MD_type(sign_md) : NID_undef, + key->private, ctx->provctx->debug); + break; + + case RSA_PKCS1_PSS_PADDING: + if (sign_md == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, + "PSS padding needs a signature digest"); + rc = 0; + goto out; + } + + mgf_md = sk_prov_sign_op_get_mgf_md(ctx); + if (mgf_md == NULL) { + sk_debug_op_ctx(ctx, + "ERROR sk_prov_sign_op_get_mgf_md failed"); + rc = 0; + goto out; + } + + salt_len = sk_prov_sign_op_get_pss_saltlen(ctx, key, mgf_md); + if (salt_len < 0) { + sk_debug_op_ctx(ctx, + "ERROR: sk_prov_sign_op_get_pss_saltlen failed"); + rc = 0; + goto out; + } + + if (funcs->rsa_pss_sign == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, + "no secure key sign function"); + rc = 0; + goto out; + } + + rc = funcs->rsa_pss_sign(key->secure_key, key->secure_key_size, + sig, siglen, tbs, tbslen, + EVP_MD_type(sign_md), + EVP_MD_type(mgf_md), + salt_len, key->private, + ctx->provctx->debug); + break; + default: + put_error_op_ctx(ctx, SK_PROV_ERR_INVALID_PADDING, + "unknown/unsupported padding: %d", pad_mode); + rc = 0; + goto out; + } + + if (rc != 0) { + put_error_op_ctx(ctx, SK_PROV_ERR_SECURE_KEY_FUNC_FAILED, + "Secure key sign operation failed: rc: %d", + rc); + rc = 0; + goto out; + } + + rc = 1; + + sk_debug_op_ctx(ctx, "siglen: %lu", *siglen); + +out: + if (sign_md != NULL) + EVP_MD_free(sign_md); + if (mgf_md != NULL) + EVP_MD_free(mgf_md); + + return rc; +} + +static int sk_prov_sign_rsa_digest_sign_init(struct sk_prov_op_ctx *ctx, + const char *mdname, + struct sk_prov_key *key, + const OSSL_PARAM params[]) +{ + sk_debug_op_ctx(ctx, "ctx: %p mdname: %s key: %p", ctx, + mdname != NULL ? mdname : "", key); + return sk_prov_sign_op_digest_sign_init(ctx, mdname, key, params, + sk_prov_sign_rsa_sign); +} + +static struct sk_prov_op_ctx *sk_prov_sign_ec_newctx( + struct sk_prov_ctx *provctx, + const char *propq) +{ + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "provctx: %p propq: %s", provctx, + propq != NULL ? propq : ""); + return sk_prov_sign_op_newctx(provctx, propq, EVP_PKEY_EC); +} + +static const OSSL_PARAM *sk_prov_sign_ec_gettable_ctx_params( + struct sk_prov_op_ctx *ctx, + struct sk_prov_ctx *provctx) +{ + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "provctx: %p", provctx); + return sk_prov_sign_op_gettable_ctx_params(ctx, provctx, EVP_PKEY_EC); +} + +static const OSSL_PARAM *sk_prov_sign_ec_settable_ctx_params( + struct sk_prov_op_ctx *ctx, + struct sk_prov_ctx *provctx) +{ + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "provctx: %p", provctx); + return sk_prov_sign_op_settable_ctx_params(ctx, provctx, EVP_PKEY_EC); +} + +static const OSSL_PARAM *sk_prov_sign_ec_gettable_ctx_md_params( + struct sk_prov_op_ctx *ctx) +{ + if (ctx == NULL) + return NULL; + + sk_debug_op_ctx(ctx, "ctx: %p", ctx); + return sk_prov_sign_op_gettable_ctx_md_params(ctx, EVP_PKEY_EC); +} + +static const OSSL_PARAM *sk_prov_sign_ec_settable_ctx_md_params( + struct sk_prov_op_ctx *ctx) +{ + if (ctx == NULL) + return NULL; + + sk_debug_op_ctx(ctx, "ctx: %p", ctx); + return sk_prov_sign_op_settable_ctx_md_params(ctx, EVP_PKEY_EC); +} + +static int sk_prov_sign_ec_sign(struct sk_prov_op_ctx *ctx, + unsigned char *sig, size_t *siglen, + size_t sigsize, + const unsigned char *tbs, size_t tbslen) +{ + struct sk_prov_key *key; + EVP_MD *sign_md = NULL; + struct sk_funcs *funcs; + int ec_size, rc; + + if (ctx == NULL || siglen == NULL || tbs == NULL) + return 0; + + sk_debug_op_ctx(ctx, "ctx: %p key: %p tbslen: %lu sigsize: %lu", + ctx, ctx->key, tbslen, sigsize); + + if (ctx->key == NULL || ctx->operation != EVP_PKEY_OP_SIGN) { + put_error_op_ctx(ctx, SK_PROV_ERR_OPRATION_NOT_INITIALIZED, + "sign operation not initialized"); + return 0; + } + + /* For clear key, let the default provider handle it */ + if (ctx->key->secure_key == NULL) + return sk_prov_sign_op_sign(ctx, sig, siglen, sigsize, + tbs, tbslen); + + key = ctx->key; + + ec_size = sk_prov_keymgmt_get_size(key); + if (ec_size <= 0) { + sk_debug_op_ctx(ctx, "ERROR: sk_prov_keymgmt_get_size failed"); + return 0; + } + + if (sig == NULL) { + *siglen = ec_size; + sk_debug_op_ctx(ctx, "siglen: %lu", *siglen); + return 1; + } + + if (sigsize < (size_t)ec_size) { + put_error_op_ctx(ctx, SK_PROV_ERR_INVALID_PARAM, + "signature length invalid"); + return 0; + } + + funcs = ctx->key->funcs; + if (funcs == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, + "no secure key funcs"); + return 0; + } + + sign_md = sk_prov_sign_op_get_md(ctx); + if (sign_md == NULL) { + sk_debug_op_ctx(ctx, "ERROR: sk_prov_sign_op_get_md failed"); + return 0; + } + + if (tbslen != (size_t)EVP_MD_size(sign_md)) { + put_error_op_ctx(ctx, SK_PROV_ERR_INVALID_PARAM, + "tbslen must be size of digest"); + rc = 0; + goto out; + } + + *siglen = ec_size; + + if (funcs->ecdsa_sign == NULL) { + put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, + "no secure key sign function"); + rc = 0; + goto out; + } + + rc = funcs->ecdsa_sign(key->secure_key, key->secure_key_size, + sig, siglen, tbs, tbslen, + EVP_MD_type(sign_md), key->private, + ctx->provctx->debug); + if (rc != 0) { + put_error_op_ctx(ctx, SK_PROV_ERR_SECURE_KEY_FUNC_FAILED, + "Secure key sign operation failed: rc: %d", + rc); + rc = 0; + goto out; + } + + rc = 1; + + sk_debug_op_ctx(ctx, "siglen: %lu", *siglen); + +out: + if (sign_md != NULL) + EVP_MD_free(sign_md); + + return rc; +} + +static int sk_prov_sign_ec_digest_sign_init(struct sk_prov_op_ctx *ctx, + const char *mdname, + struct sk_prov_key *key, + const OSSL_PARAM params[]) +{ + sk_debug_op_ctx(ctx, "ctx: %p mdname: %s key: %p", ctx, + mdname != NULL ? mdname : "", key); + return sk_prov_sign_op_digest_sign_init(ctx, mdname, key, params, + sk_prov_sign_ec_sign); +} + +static const OSSL_DISPATCH sk_prov_rsa_signature_functions[] = { + /* Signature context constructor, descructor */ + { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))sk_prov_sign_rsa_newctx }, + { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))sk_prov_op_freectx }, + { OSSL_FUNC_SIGNATURE_DUPCTX, (void (*)(void))sk_prov_sign_op_dupctx }, + /* Signing */ + { OSSL_FUNC_SIGNATURE_SIGN_INIT, + (void (*)(void))sk_prov_sign_op_sign_init }, + { OSSL_FUNC_SIGNATURE_SIGN, (void (*)(void))sk_prov_sign_rsa_sign }, + /* Verifying */ + { OSSL_FUNC_SIGNATURE_VERIFY_INIT, + (void (*)(void))sk_prov_sign_op_verify_init }, + { OSSL_FUNC_SIGNATURE_VERIFY, (void (*)(void))sk_prov_sign_op_verify }, + /* Verify recover */ + { OSSL_FUNC_SIGNATURE_VERIFY_RECOVER_INIT, + (void (*)(void))sk_prov_sign_op_verify_recover_init }, + { OSSL_FUNC_SIGNATURE_VERIFY_RECOVER, + (void (*)(void))sk_prov_sign_op_verify_recover }, + /* Digest Sign */ + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT, + (void (*)(void))sk_prov_sign_rsa_digest_sign_init }, + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_UPDATE, + (void (*)(void))sk_prov_sign_op_digest_sign_update }, + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_FINAL, + (void (*)(void))sk_prov_sign_op_digest_sign_final }, + /* Digest Verify */ + { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_INIT, + (void (*)(void))sk_prov_sign_op_digest_verify_init }, + { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_UPDATE, + (void (*)(void))sk_prov_sign_op_digest_verify_update }, + { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_FINAL, + (void (*)(void))sk_prov_sign_op_digest_verify_final }, + /* Signature parameters */ + { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, + (void (*)(void))sk_prov_sign_op_get_ctx_params }, + { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, + (void (*)(void))sk_prov_sign_rsa_gettable_ctx_params }, + { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, (void + (*)(void))sk_prov_sign_op_set_ctx_params }, + { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, + (void (*)(void))sk_prov_sign_rsa_settable_ctx_params }, + /* MD parameters */ + { OSSL_FUNC_SIGNATURE_GET_CTX_MD_PARAMS, + (void (*)(void))sk_prov_sign_op_get_ctx_md_params }, + { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_MD_PARAMS, + (void (*)(void))sk_prov_sign_rsa_gettable_ctx_md_params }, + { OSSL_FUNC_SIGNATURE_SET_CTX_MD_PARAMS, + (void (*)(void))sk_prov_sign_op_set_ctx_md_params }, + { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_MD_PARAMS, + (void (*)(void))sk_prov_sign_rsa_settable_ctx_md_params }, + { 0, NULL } +}; + +static const OSSL_DISPATCH sk_prov_ecdsa_signature_functions[] = { + /* Signature context constructor, descructor */ + { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))sk_prov_sign_ec_newctx }, + { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))sk_prov_op_freectx }, + { OSSL_FUNC_SIGNATURE_DUPCTX, (void (*)(void))sk_prov_sign_op_dupctx }, + /* Signing */ + { OSSL_FUNC_SIGNATURE_SIGN_INIT, + (void (*)(void))sk_prov_sign_op_sign_init }, + { OSSL_FUNC_SIGNATURE_SIGN, (void (*)(void))sk_prov_sign_ec_sign }, + /* Verifying */ + { OSSL_FUNC_SIGNATURE_VERIFY_INIT, + (void (*)(void))sk_prov_sign_op_verify_init }, + { OSSL_FUNC_SIGNATURE_VERIFY, (void (*)(void))sk_prov_sign_op_verify }, + /* Verify recover */ + { OSSL_FUNC_SIGNATURE_VERIFY_RECOVER_INIT, + (void (*)(void))sk_prov_sign_op_verify_recover_init }, + { OSSL_FUNC_SIGNATURE_VERIFY_RECOVER, + (void (*)(void))sk_prov_sign_op_verify_recover }, + /* Digest Sign */ + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT, + (void (*)(void))sk_prov_sign_ec_digest_sign_init }, + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_UPDATE, + (void (*)(void))sk_prov_sign_op_digest_sign_update }, + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_FINAL, + (void (*)(void))sk_prov_sign_op_digest_sign_final }, + /* Digest Verify */ + { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_INIT, + (void (*)(void))sk_prov_sign_op_digest_verify_init }, + { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_UPDATE, + (void (*)(void))sk_prov_sign_op_digest_verify_update }, + { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_FINAL, + (void (*)(void))sk_prov_sign_op_digest_verify_final }, + /* Signature parameters */ + { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, + (void (*)(void))sk_prov_sign_op_get_ctx_params }, + { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, + (void (*)(void))sk_prov_sign_ec_gettable_ctx_params }, + { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, (void + (*)(void))sk_prov_sign_op_set_ctx_params }, + { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, + (void (*)(void))sk_prov_sign_ec_settable_ctx_params }, + /* MD parameters */ + { OSSL_FUNC_SIGNATURE_GET_CTX_MD_PARAMS, + (void (*)(void))sk_prov_sign_op_get_ctx_md_params }, + { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_MD_PARAMS, + (void (*)(void))sk_prov_sign_ec_gettable_ctx_md_params }, + { OSSL_FUNC_SIGNATURE_SET_CTX_MD_PARAMS, + (void (*)(void))sk_prov_sign_op_set_ctx_md_params }, + { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_MD_PARAMS, + (void (*)(void))sk_prov_sign_ec_settable_ctx_md_params }, + { 0, NULL } +}; + +static const OSSL_ALGORITHM sk_prov_signature[] = { + { "RSA:rsaEncryption", "provider="SK_PROV_NAME, + sk_prov_rsa_signature_functions, NULL }, + { "ECDSA", "provider="SK_PROV_NAME, + sk_prov_ecdsa_signature_functions, NULL }, + { NULL, NULL, NULL, NULL } +}; + +static const OSSL_DISPATCH sk_prov_rsa_asym_cipher_functions[] = { + /* RSA context constructor, descructor */ + { OSSL_FUNC_ASYM_CIPHER_NEWCTX, + (void (*)(void))sk_prov_asym_rsa_newctx }, + { OSSL_FUNC_ASYM_CIPHER_FREECTX, (void (*)(void))sk_prov_op_freectx }, + { OSSL_FUNC_ASYM_CIPHER_DUPCTX, + (void (*)(void))sk_prov_asym_op_dupctx }, + /* RSA context set/get parameters */ + { OSSL_FUNC_ASYM_CIPHER_GET_CTX_PARAMS, + (void (*)(void))sk_prov_asym_op_get_ctx_params }, + { OSSL_FUNC_ASYM_CIPHER_GETTABLE_CTX_PARAMS, + (void (*)(void))sk_prov_asym_rsa_gettable_ctx_params }, + { OSSL_FUNC_ASYM_CIPHER_SET_CTX_PARAMS, + (void (*)(void))sk_prov_asym_op_set_ctx_params }, + { OSSL_FUNC_ASYM_CIPHER_SETTABLE_CTX_PARAMS, + (void (*)(void))sk_prov_asym_rsa_settable_ctx_params }, + /* RSA encrypt */ + { OSSL_FUNC_ASYM_CIPHER_ENCRYPT_INIT, + (void (*)(void))sk_prov_asym_op_encrypt_init }, + { OSSL_FUNC_ASYM_CIPHER_ENCRYPT, + (void (*)(void))sk_prov_asym_op_encrypt }, + /* RSA decrypt */ + { OSSL_FUNC_ASYM_CIPHER_DECRYPT_INIT, + (void (*)(void))sk_prov_asym_op_decrypt_init }, + { OSSL_FUNC_ASYM_CIPHER_DECRYPT, + (void (*)(void))sk_prov_asym_rsa_decrypt }, + { 0, NULL } +}; + +static const OSSL_ALGORITHM sk_prov_asym_cipher[] = { + { "RSA:rsaEncryption", "provider="SK_PROV_NAME, + sk_prov_rsa_asym_cipher_functions, NULL }, + { NULL, NULL, NULL, NULL } +}; + + +static const OSSL_DISPATCH sk_prov_rsa_keymgmt_functions[] = { + /* Constructor, destructor */ + { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))sk_prov_keymgmt_rsa_new }, + { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))sk_prov_keymgmt_free }, + + /* Key generation and loading */ + { OSSL_FUNC_KEYMGMT_GEN_INIT, + (void (*)(void))sk_prov_keymgmt_rsa_gen_init }, + { OSSL_FUNC_KEYMGMT_GEN_SET_TEMPLATE, + (void (*)(void))sk_prov_keymgmt_gen_set_template }, + { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, + (void (*)(void))sk_prov_keymgmt_gen_set_params }, + { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, + (void (*)(void))sk_prov_keymgmt_rsa_gen_settable_params }, + { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))sk_prov_keymgmt_gen }, + { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, + (void (*)(void))sk_prov_keymgmt_gen_cleanup }, + { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))sk_prov_keymgmt_load }, + + /* Key object checking */ + { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))sk_prov_keymgmt_has }, + { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))sk_prov_keymgmt_match }, + { OSSL_FUNC_KEYMGMT_VALIDATE, + (void (*)(void))sk_prov_keymgmt_validate }, + { OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME, + (void (*)(void))sk_prov_keymgmt_rsa_query_operation_name }, + + /* Key object information */ + { OSSL_FUNC_KEYMGMT_GET_PARAMS, + (void (*) (void))sk_prov_keymgmt_get_params }, + { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, + (void (*) (void))sk_prov_keymgmt_rsa_gettable_params }, + { OSSL_FUNC_KEYMGMT_SET_PARAMS, + (void (*) (void))sk_prov_keymgmt_set_params }, + { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, + (void (*) (void))sk_prov_keymgmt_rsa_settable_params }, + + /* Import and export routines */ + { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))sk_prov_keymgmt_export }, + { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, + (void (*)(void))sk_prov_keymgmt_rsa_export_types }, + { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))sk_prov_keymgmt_import }, + { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, + (void (*)(void))sk_prov_keymgmt_rsa_import_types }, + /* No copy function, OpenSSL will use export/import to copy instead */ + + { 0, NULL } +}; + +static const OSSL_DISPATCH sk_prov_rsapss_keymgmt_functions[] = { + /* Constructor, destructor */ + { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))sk_prov_keymgmt_rsa_pss_new }, + { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))sk_prov_keymgmt_free }, + + /* Key generation and loading */ + { OSSL_FUNC_KEYMGMT_GEN_INIT, + (void (*)(void))sk_prov_keymgmt_rsa_pss_gen_init }, + { OSSL_FUNC_KEYMGMT_GEN_SET_TEMPLATE, + (void (*)(void))sk_prov_keymgmt_gen_set_template }, + { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, + (void (*)(void))sk_prov_keymgmt_gen_set_params }, + { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, + (void (*)(void))sk_prov_keymgmt_rsa_pss_gen_settable_params }, + { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))sk_prov_keymgmt_gen }, + { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, + (void (*)(void))sk_prov_keymgmt_gen_cleanup }, + { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))sk_prov_keymgmt_load }, + + /* Key object checking */ + { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))sk_prov_keymgmt_has }, + { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))sk_prov_keymgmt_match }, + { OSSL_FUNC_KEYMGMT_VALIDATE, + (void (*)(void))sk_prov_keymgmt_validate }, + { OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME, + (void (*)(void))sk_prov_keymgmt_rsa_query_operation_name }, + + /* Key object information */ + { OSSL_FUNC_KEYMGMT_GET_PARAMS, + (void (*) (void))sk_prov_keymgmt_get_params }, + { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, + (void (*) (void))sk_prov_keymgmt_rsa_pss_gettable_params }, + { OSSL_FUNC_KEYMGMT_SET_PARAMS, + (void (*) (void))sk_prov_keymgmt_set_params }, + { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, + (void (*) (void))sk_prov_keymgmt_rsa_pss_settable_params }, + + /* Import and export routines */ + { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))sk_prov_keymgmt_export }, + { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, + (void (*)(void))sk_prov_keymgmt_rsa_pss_export_types }, + { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))sk_prov_keymgmt_import }, + { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, + (void (*)(void))sk_prov_keymgmt_rsa_pss_import_types }, + /* No copy function, OpenSSL will use export/import to copy instead */ + + { 0, NULL } +}; + +static const OSSL_DISPATCH sk_prov_ec_keymgmt_functions[] = { + /* Constructor, destructor */ + { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))sk_prov_keymgmt_ec_new }, + { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))sk_prov_keymgmt_free }, + + /* Key generation and loading */ + { OSSL_FUNC_KEYMGMT_GEN_INIT, + (void (*)(void))sk_prov_keymgmt_ec_gen_init }, + { OSSL_FUNC_KEYMGMT_GEN_SET_TEMPLATE, + (void (*)(void))sk_prov_keymgmt_gen_set_template }, + { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, + (void (*)(void))sk_prov_keymgmt_gen_set_params }, + { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, + (void (*)(void))sk_prov_keymgmt_ec_gen_settable_params }, + { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))sk_prov_keymgmt_gen }, + { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, + (void (*)(void))sk_prov_keymgmt_gen_cleanup }, + { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))sk_prov_keymgmt_load }, + + /* Key object checking */ + { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))sk_prov_keymgmt_has }, + { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))sk_prov_keymgmt_match }, + { OSSL_FUNC_KEYMGMT_VALIDATE, + (void (*)(void))sk_prov_keymgmt_validate }, + { OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME, + (void (*)(void))sk_prov_keymgmt_ec_query_operation_name }, + + /* Key object information */ + { OSSL_FUNC_KEYMGMT_GET_PARAMS, + (void (*) (void))sk_prov_keymgmt_get_params }, + { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, + (void (*) (void))sk_prov_keymgmt_ec_gettable_params }, + { OSSL_FUNC_KEYMGMT_SET_PARAMS, + (void (*) (void))sk_prov_keymgmt_set_params }, + { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, + (void (*) (void))sk_prov_keymgmt_ec_settable_params }, + + /* Import and export routines */ + { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))sk_prov_keymgmt_export }, + { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, + (void (*)(void))sk_prov_keymgmt_ec_export_types }, + { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))sk_prov_keymgmt_import }, + { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, + (void (*)(void))sk_prov_keymgmt_ec_import_types }, + /* No copy function, OpenSSL will use export/import to copy instead */ + + { 0, NULL } +}; + +static const OSSL_ALGORITHM sk_prov_keymgmt[] = { + { "RSA:rsaEncryption", "provider="SK_PROV_NAME, + sk_prov_rsa_keymgmt_functions, NULL }, + { "RSA-PSS:RSASSA-PSS", "provider="SK_PROV_NAME, + sk_prov_rsapss_keymgmt_functions, NULL }, + { "EC:id-ecPublicKey", "provider="SK_PROV_NAME, + sk_prov_ec_keymgmt_functions, NULL }, + { NULL, NULL, NULL, NULL } +}; + +static const OSSL_DISPATCH sk_prov_ec_keyexch_functions[] = { + /* Context management */ + { OSSL_FUNC_KEYEXCH_NEWCTX, (void (*)(void))sk_prov_keyexch_ec_newctx }, + { OSSL_FUNC_KEYEXCH_FREECTX, (void (*)(void))sk_prov_op_freectx }, + { OSSL_FUNC_KEYEXCH_DUPCTX, (void (*)(void))sk_prov_keyexch_ec_dupctx }, + + /* Shared secret derivation */ + { OSSL_FUNC_KEYEXCH_INIT, (void (*)(void))sk_prov_keyexch_ec_init }, + { OSSL_FUNC_KEYEXCH_SET_PEER, + (void (*)(void))sk_prov_keyexch_ec_set_peer }, + { OSSL_FUNC_KEYEXCH_DERIVE, (void (*)(void))sk_prov_keyexch_ec_derive }, + + /* Key Exchange parameters */ + { OSSL_FUNC_KEYEXCH_SET_CTX_PARAMS, + (void (*)(void))sk_prov_keyexch_ec_set_ctx_params }, + { OSSL_FUNC_KEYEXCH_SETTABLE_CTX_PARAMS, + (void (*)(void))sk_prov_keyexch_ec_settable_ctx_params }, + { OSSL_FUNC_KEYEXCH_GET_CTX_PARAMS, + (void (*)(void))sk_prov_keyexch_ec_get_ctx_params }, + { OSSL_FUNC_KEYEXCH_GETTABLE_CTX_PARAMS, + (void (*)(void))sk_prov_keyexch_ec_gettable_ctx_params }, + + { 0, NULL } +}; + +/* + * Although ECDH key derivation is not supported for secure keys (would result + * in a secure symmetric key, which OpenSSL can't handle), the provider still + * must implement the ECDH key exchange functions and proxy them all to the + * default provider. OpenSSL common code requires that the key management + * provider and the key exchange provider for a derive operation is the same. + * So for clear EC keys created with this provider, we do support the ECDH + * operation by proxy'ing it to the default provider. + */ +static const OSSL_ALGORITHM sk_prov_keyexch[] = { + { "ECDH", "provider="SK_PROV_NAME, sk_prov_ec_keyexch_functions, NULL }, + { NULL, NULL, NULL, NULL } +}; + +static const OSSL_PARAM sk_prov_param_types[] = { + OSSL_PARAM_DEFN(OSSL_PROV_PARAM_NAME, OSSL_PARAM_UTF8_PTR, NULL, 0), + OSSL_PARAM_DEFN(OSSL_PROV_PARAM_VERSION, OSSL_PARAM_UTF8_PTR, NULL, 0), + OSSL_PARAM_DEFN(OSSL_PROV_PARAM_BUILDINFO, OSSL_PARAM_UTF8_PTR, NULL, + 0), + OSSL_PARAM_DEFN(OSSL_PROV_PARAM_STATUS, OSSL_PARAM_INTEGER, NULL, 0), + OSSL_PARAM_END +}; + +static const OSSL_PARAM *sk_prov_gettable_params(struct sk_prov_ctx *provctx) +{ + if (provctx == NULL) + return NULL; + + sk_debug_ctx(provctx, "provctx: %p", provctx); + return sk_prov_param_types; +} + +static int sk_prov_get_params(struct sk_prov_ctx *provctx, OSSL_PARAM params[]) +{ + OSSL_PARAM *p; + + if (provctx == NULL) + return 0; + + sk_debug_ctx(provctx, "provctx: %p", provctx); + + p = OSSL_PARAM_locate(params, OSSL_PROV_PARAM_NAME); + if (p != NULL && !OSSL_PARAM_set_utf8_ptr(p, SK_PROV_DESCRIPTION)) { + put_error_ctx(provctx, SK_PROV_ERR_INTERNAL_ERROR, + "OSSL_PARAM_set_utf8_ptr failed"); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_PROV_PARAM_VERSION); + if (p != NULL && !OSSL_PARAM_set_utf8_ptr(p, SK_PROV_VERSION)) { + put_error_ctx(provctx, SK_PROV_ERR_INTERNAL_ERROR, + "OSSL_PARAM_set_utf8_ptr failed"); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_PROV_PARAM_BUILDINFO); + if (p != NULL && !OSSL_PARAM_set_utf8_ptr(p, SK_PROV_VERSION)) { + put_error_ctx(provctx, SK_PROV_ERR_INTERNAL_ERROR, + "OSSL_PARAM_set_utf8_ptr failed"); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_PROV_PARAM_STATUS); + if (p != NULL && !OSSL_PARAM_set_int(p, 1)) { + put_error_ctx(provctx, SK_PROV_ERR_INTERNAL_ERROR, + "OSSL_PARAM_set_int failed"); + return 0; + } + + return 1; +} + +static const OSSL_ALGORITHM *sk_prov_query(struct sk_prov_ctx *provctx, + int operation_id, int *no_cache) +{ + if (provctx == NULL) + return NULL; + + *no_cache = 0; + + sk_debug_ctx(provctx, "provctx: %p operation_id: %d", provctx, + operation_id); + + switch (operation_id) { + case OSSL_OP_KEYMGMT: + return sk_prov_keymgmt; + case OSSL_OP_KEYEXCH: + return sk_prov_keyexch; + case OSSL_OP_SIGNATURE: + return sk_prov_signature; + case OSSL_OP_ASYM_CIPHER: + return sk_prov_asym_cipher; + } + + return NULL; +} + +static void sk_prov_teardown(struct sk_prov_ctx *provctx) +{ + int i; + + if (provctx == NULL) + return; + + sk_debug_ctx(provctx, "provctx: %p", provctx); + + for (i = 0; i < SK_PROV_CACHED_PARAMS_COUNT; i++) { + if (provctx->cached_parms[i] != NULL) + OPENSSL_free((void *)provctx->cached_parms[i]); + } + + OPENSSL_free(provctx); +} + +static const OSSL_ITEM *sk_prov_get_reason_strings(struct sk_prov_ctx *provctx) +{ + sk_debug_ctx(provctx, "provctx: %p", provctx); + return sk_prov_reason_strings; +} + +static int sk_prov_prov_get_capabilities(struct sk_prov_ctx *provctx, + const char *capability, + OSSL_CALLBACK *cb, void *arg) +{ + sk_debug_ctx(provctx, "provctx: %p capability: %s", provctx, + capability); + + if (provctx->default_provider == NULL) + return 0; + + return OSSL_PROVIDER_get_capabilities(provctx->default_provider, + capability, cb, arg); +} + +/* Functions we provide to the core */ +static const OSSL_DISPATCH sk_prov_dispatch_table[] = { + { OSSL_FUNC_PROVIDER_TEARDOWN, (void (*)(void))sk_prov_teardown }, + { OSSL_FUNC_PROVIDER_GETTABLE_PARAMS, + (void (*)(void))sk_prov_gettable_params }, + { OSSL_FUNC_PROVIDER_GET_PARAMS, (void (*)(void))sk_prov_get_params }, + { OSSL_FUNC_PROVIDER_QUERY_OPERATION, (void (*)(void))sk_prov_query }, + { OSSL_FUNC_PROVIDER_GET_REASON_STRINGS, + (void (*)(void))sk_prov_get_reason_strings }, + { OSSL_FUNC_PROVIDER_GET_CAPABILITIES, + (void (*)(void))sk_prov_prov_get_capabilities }, + { 0, NULL } +}; + +static int sk_provider_init(const OSSL_CORE_HANDLE *handle, + const OSSL_DISPATCH *in, + const OSSL_DISPATCH **out, void **provctx) +{ + OSSL_FUNC_core_set_error_debug_fn *c_set_error_debug = NULL; + OSSL_FUNC_core_get_libctx_fn *c_get_libctx = NULL; + OSSL_FUNC_core_vset_error_fn *c_vset_error = NULL; + OSSL_FUNC_core_new_error_fn *c_new_error = NULL; + struct sk_prov_ctx *ctx; + + if (handle == NULL || in == NULL || out == NULL || provctx == NULL) + return 0; + + for (; in->function_id != 0; in++) { + switch (in->function_id) { + case OSSL_FUNC_CORE_GET_LIBCTX: + c_get_libctx = OSSL_FUNC_core_get_libctx(in); + break; + case OSSL_FUNC_CORE_NEW_ERROR: + c_new_error = OSSL_FUNC_core_new_error(in); + break; + case OSSL_FUNC_CORE_SET_ERROR_DEBUG: + c_set_error_debug = OSSL_FUNC_core_set_error_debug(in); + break; + case OSSL_FUNC_CORE_VSET_ERROR: + c_vset_error = OSSL_FUNC_core_vset_error(in); + break; + default: + /* Just ignore anything we don't understand */ + break; + } + } + + if (c_get_libctx == NULL) + return 0; + + ctx = OPENSSL_zalloc(sizeof(struct sk_prov_ctx)); + if (ctx == NULL) { + c_new_error(handle); + c_set_error_debug(handle, __FILE__, __LINE__, __func__); + c_vset_error(handle, SK_PROV_ERR_MALLOC_FAILED, + "Failed to allocate provider context", NULL); + return 0; + } + + ctx->handle = handle; + ctx->c_get_libctx = c_get_libctx; + ctx->c_new_error = c_new_error; + ctx->c_set_error_debug = c_set_error_debug; + ctx->c_vset_error = c_vset_error; + *provctx = ctx; + + *out = sk_prov_dispatch_table; + return 1; +} + +/** + * Initializes the secure key support for OpenSSL. + * + * @param debug true to enable internal debugging + * + * @returns zero for success, a negative errno in case of an error: + * -ENOMEM: failed to allocate memory + * -EIO: OpenSSL failed for various reasons + */ +int SK_OPENSSL_init(bool debug) +{ + struct sk_prov_ctx *provctx; + + if (sk_prov_securekey_libctx == NULL) + sk_prov_securekey_libctx = OSSL_LIB_CTX_new(); + if (sk_prov_securekey_libctx == NULL) { + sk_debug(debug, "ERROR: OSSL_LIB_CTX_new failed"); + return -ENOMEM; + } + + if (OSSL_PROVIDER_add_builtin(sk_prov_securekey_libctx, SK_PROV_NAME, + sk_provider_init) != 1) { + sk_debug(debug, "ERROR: OSSL_PROVIDER_add_builtin failed"); + return -EIO; + } + + if (sk_prov_securekey_provider == NULL) { + sk_prov_securekey_provider = + OSSL_PROVIDER_load(sk_prov_securekey_libctx, + SK_PROV_NAME); + if (sk_prov_securekey_provider == NULL) { + sk_debug(debug, "ERROR: OSSL_PROVIDER_load(" + SK_PROV_NAME") failed"); + return -EIO; + } + } + provctx = OSSL_PROVIDER_get0_provider_ctx(sk_prov_securekey_provider); + provctx->debug = debug; + + if (sk_prov_default_provider == NULL) { + sk_prov_default_provider = + OSSL_PROVIDER_load(sk_prov_securekey_libctx, + "default"); + if (sk_prov_default_provider == NULL) { + sk_debug(debug, + "ERROR: OSSL_PROVIDER_load(default) failed"); + SK_OPENSSL_term(); + return -EIO; + } + } + provctx->default_provider = sk_prov_default_provider; + provctx->default_provctx = + OSSL_PROVIDER_get0_provider_ctx(sk_prov_default_provider); + + sk_prov_previous_libctx = + OSSL_LIB_CTX_set0_default(sk_prov_securekey_libctx); + if (sk_prov_previous_libctx == NULL) { + sk_debug(debug, "ERROR: OSSL_LIB_CTX_set0_default failed"); + SK_OPENSSL_term(); + return -EIO; + } + + /* Prefer the secure key provider, but allow to fall back to default */ + if (!EVP_set_default_properties(sk_prov_securekey_libctx, + "?provider="SK_PROV_NAME)) { + sk_debug(debug, "ERROR: EVP_set_default_properties failed"); + SK_OPENSSL_term(); + return -EIO; + } + + sk_debug(debug, "sk_provider support initialized, provctx: %p", + provctx); + return 0; +} + +/** + * Terminate the secure key support for OpenSSL. + */ +void SK_OPENSSL_term(void) +{ + if (sk_prov_securekey_provider != NULL) + OSSL_PROVIDER_unload(sk_prov_securekey_provider); + sk_prov_securekey_provider = NULL; + + if (sk_prov_default_provider != NULL) + OSSL_PROVIDER_unload(sk_prov_default_provider); + sk_prov_default_provider = NULL; + + if (sk_prov_previous_libctx != NULL) + OSSL_LIB_CTX_set0_default(sk_prov_previous_libctx); + sk_prov_previous_libctx = NULL; + + if (sk_prov_securekey_libctx != NULL) + OSSL_LIB_CTX_free(sk_prov_securekey_libctx); + sk_prov_securekey_libctx = NULL; +} + +static int sk_openssl_pkey_from_data(OSSL_PARAM_BLD *bld, int pkey_type, + EVP_PKEY **pkey, bool debug) +{ + OSSL_PARAM *params = NULL; + EVP_PKEY_CTX *pctx = NULL; + const char *key_name; + int rc = 0; + + switch (pkey_type) { + case EVP_PKEY_EC: + key_name = "EC"; + break; + case EVP_PKEY_RSA: + key_name = "RSA"; + break; + case EVP_PKEY_RSA_PSS: + key_name = "RSA-PSS"; + break; + default: + sk_debug(debug, "ERROR: unsupported PKEY type"); + return -EINVAL; + } + + params = OSSL_PARAM_BLD_to_param(bld); + if (params == NULL) { + sk_debug(debug, "ERROR: OSSL_PARAM_BLD_to_param failed"); + rc = -EIO; + goto out; + } + + pctx = EVP_PKEY_CTX_new_from_name(NULL, key_name, + "provider="SK_PROV_NAME); + if (pctx == NULL) { + sk_debug(debug, "ERROR: EVP_PKEY_CTX_new_from_name failed"); + return -EIO; + } + + if (EVP_PKEY_fromdata_init(pctx) <= 0) { + sk_debug(debug, "ERROR: EVP_PKEY_fromdata_init failed"); + rc = -EIO; + goto out; + } + + if (EVP_PKEY_fromdata(pctx, pkey, EVP_PKEY_KEYPAIR, params) <= 0) { + sk_debug(debug, "ERROR: EVP_PKEY_fromdata failed"); + rc = -EIO; + goto out; + } + +out: + if (pctx != NULL) + EVP_PKEY_CTX_free(pctx); + if (params != NULL) + OSSL_PARAM_free(params); + + return rc; +} + +/** + * Converts an EC key given by the nid and the x and y coordinates into an + * OpenSSL PKEY and attaches the secure key together with secure key functions + * and private pointer to it. If no secure key is provided, a public EC key + * only PKEY is returned. + * + * @param secure_key the secure key blob. + * If NULL, a clear key PKEY is created. + * @param secure_key_size the size of the secure key blob (ignored if + * secure_key is NULL) + * @param nid the OpenSSL nid of the EC curve used + * @param prime_len the length of the prime in bytes. This is also the + * length of the x and y coordinates. + * @param x the x coordinate as big endian binary number in + * prime_len size + * @param y the y coordinate as big endian binary number in + * prime_len size + * @param sk_funcs the secure key functions to operate with the key. + * Ignored if secure_key is NULL, required otherwise. + * @param private a private pointer that is passed to the secure key + * functions (can be NULL) + * @param pkey On return: A PKEY containing the EC public key. + * @param debug true to enable internal debugging + * + * @returns zero for success, a negative errno in case of an error: + * -EINVAL: a function parameter is invalid + * -ENOMEM: failed to allocate memory + * -EIO: OpenSSL failed to generate the PKEY + * -ENOENT: OpenSSL does not know/support the curve (nid) + */ +int sk_openssl_get_pkey_ec(const unsigned char *secure_key, + size_t secure_key_size, int nid, size_t prime_len, + const unsigned char *x, const unsigned char *y, + const struct sk_funcs *sk_funcs, const void *private, + EVP_PKEY **pkey, bool debug) +{ + BIGNUM *bn_x = NULL, *bn_y = NULL; + point_conversion_form_t form; + unsigned char *pub_key = NULL; + OSSL_PARAM_BLD *bld = NULL; + EC_GROUP *group = NULL; + EC_POINT *point = NULL; + size_t pub_key_len; + int rc; + + if (pkey == NULL || x == NULL || y == NULL) + return -EINVAL; + if (secure_key != NULL && (secure_key_size == 0 || sk_funcs == NULL)) + return -EINVAL; + + *pkey = NULL; + + group = EC_GROUP_new_by_curve_name(nid); + if (group == NULL) { + sk_debug(debug, "ERROR: EC_GROUP_new_by_curve_name failed"); + rc = -ENOENT; + goto out; + } + + bn_x = BN_bin2bn(x, prime_len, NULL); + bn_y = BN_bin2bn(y, prime_len, NULL); + if (bn_x == NULL || bn_y == NULL) { + sk_debug(debug, "ERROR: BN_bin2bn failed"); + rc = -ENOMEM; + goto out; + } + + point = EC_POINT_new(group); + if (point == NULL) { + sk_debug(debug, "ERROR: EC_POINT_new failed"); + rc = -ENOMEM; + goto out; + } + + if (!EC_POINT_set_affine_coordinates(group, point, bn_x, bn_y, NULL)) { + sk_debug(debug, "ERROR: EC_POINT_set_affine_coordinates failed"); + rc = -EIO; + goto out; + } + + form = EC_GROUP_get_point_conversion_form(group); + pub_key_len = EC_POINT_point2buf(group, point, form, &pub_key, NULL); + if (pub_key_len == 0) { + sk_debug(debug, "ERROR: EC_POINT_point2buf failed"); + rc = -EIO; + goto out; + } + + bld = OSSL_PARAM_BLD_new(); + if (bld == NULL) { + sk_debug(debug, "ERROR: OSSL_PARAM_BLD_new failed"); + rc = -ENOMEM; + goto out; + } + + if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_GROUP_NAME, + OBJ_nid2sn(nid), 0) + || !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_PUB_X, bn_x) + || !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_PUB_Y, bn_y) + || !OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, + pub_key, pub_key_len)) { + sk_debug(debug, "ERROR: OSSL_PARAM_BLD_push_xxx failed"); + rc = -EIO; + goto out; + } + + if (secure_key != NULL) { + if (!OSSL_PARAM_BLD_push_octet_string(bld, + SK_PROV_PKEY_PARAM_SK_BLOB, + secure_key, secure_key_size) + || !OSSL_PARAM_BLD_push_octet_ptr(bld, + SK_PROV_PKEY_PARAM_SK_FUNCS, + (void *)sk_funcs, + sizeof(struct sk_funcs)) + || !OSSL_PARAM_BLD_push_octet_ptr(bld, + SK_PROV_PKEY_PARAM_SK_PRIVATE, + (void *)private, 0)) { + sk_debug(debug, + "ERROR: OSSL_PARAM_BLD_push_xxx failed"); + rc = -EIO; + goto out; + } + } + + rc = sk_openssl_pkey_from_data(bld, EVP_PKEY_EC, pkey, debug); + if (rc != 0) + goto out; + + rc = 0; + sk_debug(debug, "pkey created: %p", *pkey); + +out: + if (group != NULL) + EC_GROUP_free(group); + if (point != NULL) + EC_POINT_free(point); + if (bn_x != NULL) + BN_free(bn_x); + if (bn_y != NULL) + BN_free(bn_y); + if (bld != NULL) + OSSL_PARAM_BLD_free(bld); + if (pub_key != NULL) + OPENSSL_free(pub_key); + + return rc; +} + +/** + * Converts an RSA key given by the modulus and public exponent into an + * OpenSSL PKEY and attaches the secure key together with secure key functions + * and private pointer to it. If no secure key is provided, a public RSA key + * only PKEY is returned. + * + * @param secure_key the secure key blob. + * If NULL, a clear key PKEY is created. + * @param secure_key_size the size of the secure key blob (ignored if + * secure_key is NULL) + * @param modulus the modulus as big endian number + * @param modulus_length the length of the modulus in bytes + * @param pub_exp the public exponent as big endian number + * @param pub_exp_length the length of the public exponent in bytes + * @param pkey_type the PKEY type (EVP_PKEY_RSA or EVP_PKEY_RSA_PSS) + * @param sk_funcs the secure key functions to operate with the key. + * Ignored if secure_key is NULL, required otherwise. + * @param private a private pointer that is passed to the secure key + * functions (can be NULL) + * @param pkey On return: A PKEY containing the RSA public key. + * @param debug true to enable internal debugging + * + * @returns zero for success, a negative errno in case of an error: + * -EINVAL: a function parameter is invalid + * -ENOMEM: failed to allocate memory + * -EIO: OpenSSL failed to generate the PKEY + */ +int sk_openssl_get_pkey_rsa(const unsigned char *secure_key, + size_t secure_key_size, + const unsigned char *modulus, size_t modulus_length, + const unsigned char *pub_exp, size_t pub_exp_length, + int pkey_type, const struct sk_funcs *sk_funcs, + const void *private, EVP_PKEY **pkey, bool debug) +{ + BIGNUM *bn_modulus = NULL, *bn_pub_exp = NULL; + OSSL_PARAM_BLD *bld = NULL; + int rc; + + if (pkey == NULL || modulus == NULL || pub_exp == NULL) + return -EINVAL; + if (secure_key != NULL && (secure_key_size == 0 || sk_funcs == NULL)) + return -EINVAL; + if (pkey_type != EVP_PKEY_RSA && pkey_type != EVP_PKEY_RSA_PSS) + return -EINVAL; + + *pkey = NULL; + + bn_modulus = BN_bin2bn(modulus, modulus_length, NULL); + bn_pub_exp = BN_bin2bn(pub_exp, pub_exp_length, NULL); + if (bn_modulus == NULL || bn_pub_exp == NULL) { + sk_debug(debug, "ERROR: BN_bin2bn failed"); + rc = -ENOMEM; + goto out; + } + + bld = OSSL_PARAM_BLD_new(); + if (bld == NULL) { + sk_debug(debug, "ERROR: OSSL_PARAM_BLD_new failed"); + rc = -ENOMEM; + goto out; + } + + if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_N, bn_modulus) || + !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_E, bn_pub_exp)) { + sk_debug(debug, "ERROR: OSSL_PARAM_BLD_push_xxx failed"); + rc = -EIO; + goto out; + } + + if (secure_key != NULL) { + if (!OSSL_PARAM_BLD_push_octet_string(bld, + SK_PROV_PKEY_PARAM_SK_BLOB, + secure_key, secure_key_size) + || !OSSL_PARAM_BLD_push_octet_ptr(bld, + SK_PROV_PKEY_PARAM_SK_FUNCS, + (void *)sk_funcs, + sizeof(struct sk_funcs)) + || !OSSL_PARAM_BLD_push_octet_ptr(bld, + SK_PROV_PKEY_PARAM_SK_PRIVATE, + (void *)private, 0)) { + sk_debug(debug, + "ERROR: OSSL_PARAM_BLD_push_xxx failed"); + rc = -EIO; + goto out; + } + } + + rc = sk_openssl_pkey_from_data(bld, pkey_type, pkey, debug); + if (rc != 0) + goto out; + + rc = 0; + sk_debug(debug, "pkey created: %p", *pkey); + +out: + if (bn_modulus != NULL) + BN_free(bn_modulus); + if (bn_pub_exp != NULL) + BN_free(bn_pub_exp); + if (bld != NULL) + OSSL_PARAM_BLD_free(bld); + + return rc; +} + +/** + * Get the curve NID of the EC pkey + */ +int SK_OPENSSL_get_curve_from_ec_pkey(EVP_PKEY *pkey) +{ + size_t curve_len; + char curve[80]; + + if (EVP_PKEY_id(pkey) != EVP_PKEY_EC) + return NID_undef; + + if (!EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME, + curve, sizeof(curve), &curve_len)) + return NID_undef; + + return OBJ_sn2nid(curve); +} + +#endif diff --git a/libseckey/sk_utilities.c b/libseckey/sk_utilities.c new file mode 100644 index 00000000..16f88d85 --- /dev/null +++ b/libseckey/sk_utilities.c @@ -0,0 +1,1058 @@ +/* + * libseckey - Secure key library + * + * Copyright IBM Corp. 2021 + * + * s390-tools is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "lib/zt_common.h" + +#include "libseckey/sk_utilities.h" +#include "libseckey/sk_ep11.h" + +void SK_UTIL_warnx(const char *func, const char *fmt, ...) +{ + char tmp_fmt[200]; + va_list ap; + + if (snprintf(tmp_fmt, sizeof(tmp_fmt), "DBG: %s: %s", func, fmt) > + (int)sizeof(tmp_fmt)) + return; + + va_start(ap, fmt); + vwarnx(tmp_fmt, ap); + va_end(ap); +} + +static const unsigned char der_prime192v1[] = { + 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x01 +}; +static const unsigned char der_secp224r1[] = { + 0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x21 +}; +static const unsigned char der_prime256v1[] = { + 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07 +}; +static const unsigned char der_secp384r1[] = { + 0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22 +}; +static const unsigned char der_secp521r1[] = { + 0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x23 +}; +static const unsigned char der_brainpoolP160r1[] = { + 0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x01 +}; +static const unsigned char der_brainpoolP192r1[] = { + 0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x03 +}; +static const unsigned char der_brainpoolP224r1[] = { + 0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x05 +}; +static const unsigned char der_brainpoolP256r1[] = { + 0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07 +}; +static const unsigned char der_brainpoolP320r1[] = { + 0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x09 +}; +static const unsigned char der_brainpoolP384r1[] = { + 0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B +}; +static const unsigned char der_brainpoolP512r1[] = { + 0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D +}; + +static const struct sk_ec_curve_info ec_curve_list[] = { + { .curve_nid = NID_X9_62_prime192v1, .type = SK_EC_TYPE_PRIME, + .prime_bits = 192, .prime_len = 24, .der = der_prime192v1, + .der_size = sizeof(der_prime192v1) }, + { .curve_nid = NID_secp224r1, .type = SK_EC_TYPE_PRIME, + .prime_bits = 224, .prime_len = 28, .der = der_secp224r1, + .der_size = sizeof(der_secp224r1)}, + { .curve_nid = NID_X9_62_prime256v1, .type = SK_EC_TYPE_PRIME, + .prime_bits = 256, .prime_len = 32, .der = der_prime256v1, + .der_size = sizeof(der_prime256v1)}, + { .curve_nid = NID_secp384r1, .type = SK_EC_TYPE_PRIME, + .prime_bits = 384, .prime_len = 48, .der = der_secp384r1, + .der_size = sizeof(der_secp384r1)}, + { .curve_nid = NID_secp521r1, .type = SK_EC_TYPE_PRIME, + .prime_bits = 521, .prime_len = 66, .der = der_secp521r1, + .der_size = sizeof(der_secp521r1)}, + { .curve_nid = NID_brainpoolP160r1, .type = SK_EC_TYPE_BRAINPOOL, + .prime_bits = 160, .prime_len = 20, .der = der_brainpoolP160r1, + .der_size = sizeof(der_brainpoolP160r1)}, + { .curve_nid = NID_brainpoolP192r1, .type = SK_EC_TYPE_BRAINPOOL, + .prime_bits = 192, .prime_len = 24, .der = der_brainpoolP192r1, + .der_size = sizeof(der_brainpoolP192r1)}, + { .curve_nid = NID_brainpoolP224r1, .type = SK_EC_TYPE_BRAINPOOL, + .prime_bits = 224, .prime_len = 28, .der = der_brainpoolP224r1, + .der_size = sizeof(der_brainpoolP224r1)}, + { .curve_nid = NID_brainpoolP256r1, .type = SK_EC_TYPE_BRAINPOOL, + .prime_bits = 256, .prime_len = 32, .der = der_brainpoolP256r1, + .der_size = sizeof(der_brainpoolP256r1)}, + { .curve_nid = NID_brainpoolP320r1, .type = SK_EC_TYPE_BRAINPOOL, + .prime_bits = 320, .prime_len = 40, .der = der_brainpoolP320r1, + .der_size = sizeof(der_brainpoolP320r1)}, + { .curve_nid = NID_brainpoolP384r1, .type = SK_EC_TYPE_BRAINPOOL, + .prime_bits = 384, .prime_len = 48, .der = der_brainpoolP384r1, + .der_size = sizeof(der_brainpoolP384r1)}, + { .curve_nid = NID_brainpoolP512r1, .type = SK_EC_TYPE_BRAINPOOL, + .prime_bits = 512, .prime_len = 64, .der = der_brainpoolP512r1, + .der_size = sizeof(der_brainpoolP512r1)}, +}; + +static const int ec_curve_num = + sizeof(ec_curve_list) / sizeof(struct sk_ec_curve_info); + +/** + * Returns the curve info of the specified curve, or NULL if the curve + * is not known. + * + * @param nid the OpenSSL nid of the EC curve + * + * @returns the address of the curve info or NULL if the curve was not found + */ +const struct sk_ec_curve_info *SK_UTIL_ec_get_curve_info(int curve_nid) +{ + int i; + + for (i = 0; i < ec_curve_num; i++) { + if (ec_curve_list[i].curve_nid == curve_nid) + return &ec_curve_list[i]; + } + return NULL; +} + +/** + * Returns the nid of the Prime curve by its specified prime bit size, or 0 + * if the curve is not known. + * + * @param prime_bits the prime bit size of the curve to search for + * + * @returns the OpenSSL nid of the EC curve or 0 if the curve was not found + */ +int SK_UTIL_ec_get_prime_curve_by_prime_bits(size_t prime_bits) +{ + int i; + + for (i = 0; i < ec_curve_num; i++) { + if (ec_curve_list[i].type == SK_EC_TYPE_PRIME && + ec_curve_list[i].prime_bits == prime_bits) + return ec_curve_list[i].curve_nid; + } + return 0; +} + +/** + * Returns the nid of the Brainpool curve by its specified prime bit size, or 0 + * if the curve is not known. + * + * @param prime_bits the prime bit size of the curve to search for + * + * @returns the OpenSSL nid of the EC curve or 0 if the curve was not found + */ +int SK_UTIL_ec_get_brainpool_curve_by_prime_bits(size_t prime_bits) +{ + int i; + + for (i = 0; i < ec_curve_num; i++) { + if (ec_curve_list[i].type == SK_EC_TYPE_BRAINPOOL && + ec_curve_list[i].prime_bits == prime_bits) + return ec_curve_list[i].curve_nid; + } + return 0; +} + +/** + * Calculates the y coordinate of a point on an EC curve using the x coordinate + * and the y bit. x and y must be supplied by the caller with prime_len bytes. + * On return y contains the calculated y coordinate. + * + * @param nid the OpenSSL nid of the EC curve used + * @param prime_len the length of the prime in bytes. This is also the + * length of the x and y coordinates. + * @param x the x coordinate as big endian binary number in + * prime_len size + * @param y_bit the y-bit to identify which of the two possible + * values for y should be used + * @param y buffer to store the y coordinate as big endian + * binary number in prime_len size. + * @returns zero for success, a negative errno in case of an error: + * -EINVAL: a function parameter is invalid + * -ENOMEM: failed to allocate memory + * -EIO: OpenSSL failed to calculate the y coordinate + * -ENOENT: OpenSSL does not know/support the curve (nid) + */ +int SK_UTIL_ec_calculate_y_coordinate(int nid, size_t prime_len, + const unsigned char *x, int y_bit, + unsigned char *y) +{ + EC_GROUP *group = NULL; + EC_POINT *point = NULL; + BIGNUM *bn_x = NULL; + BIGNUM *bn_y = NULL; + BN_CTX *ctx = NULL; + int rc = 0; + + if (x == NULL || y == NULL) + return -EINVAL; + + bn_x = BN_bin2bn(x, prime_len, NULL); + if (bn_x == NULL) { + rc = -EIO; + goto out; + } + + group = EC_GROUP_new_by_curve_name(nid); + if (group == NULL) { + rc = -ENOENT; + goto out; + } + + point = EC_POINT_new(group); + if (point == NULL) { + rc = -EIO; + goto out; + } + + bn_y = BN_new(); + if (bn_y == NULL) { + rc = -ENOMEM; + goto out; + } + + ctx = BN_CTX_new(); + if (ctx == NULL) { + rc = -ENOMEM; + goto out; + } + + if (!EC_POINT_set_compressed_coordinates(group, point, bn_x, + y_bit, ctx)) { + rc = -EIO; + goto out; + } + + if (!EC_POINT_is_on_curve(group, point, ctx)) { + rc = -EIO; + goto out; + } + + if (!EC_POINT_get_affine_coordinates(group, point, bn_x, bn_y, + ctx)) { + rc = -EIO; + goto out; + } + + if (BN_bn2binpad(bn_y, y, prime_len) <= 0) { + rc = -EIO; + goto out; + } + +out: + if (ctx != NULL) + BN_CTX_free(ctx); + if (point != NULL) + EC_POINT_free(point); + if (group != NULL) + EC_GROUP_free(group); + if (bn_x != NULL) + BN_free(bn_x); + if (bn_y != NULL) + BN_free(bn_y); + + return rc; +} + +static const unsigned char der_DigestInfo_SHA1[] = { + 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, + 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14, }; +static const unsigned char der_DigestInfo_SHA224[] = { + 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, + 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, + 0x00, 0x04, 0x1C, }; +static const unsigned char der_DigestInfo_SHA256[] = { + 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, + 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, + 0x00, 0x04, 0x20, }; +static const unsigned char der_DigestInfo_SHA384[] = { + 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, + 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, + 0x00, 0x04, 0x30, }; +static const unsigned char der_DigestInfo_SHA512[] = { + 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, + 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, + 0x00, 0x04, 0x40, }; +static const unsigned char der_DigestInfo_SHA3_224[] = { + 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, + 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x07, 0x05, + 0x00, 0x04, 0x1C, }; +static const unsigned char der_DigestInfo_SHA3_256[] = { + 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, + 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x08, 0x05, + 0x00, 0x04, 0x20, }; +static const unsigned char der_DigestInfo_SHA3_384[] = { + 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, + 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x09, 0x05, + 0x00, 0x04, 0x30, }; +static const unsigned char der_DigestInfo_SHA3_512[] = { + 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, + 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0a, 0x05, + 0x00, 0x04, 0x40, }; + +static const struct sk_digest_info digest_list[] = { + { .digest_nid = NID_sha1, .digest_size = SHA_DIGEST_LENGTH, + .cca_keyword = "SHA-1 ", .der = der_DigestInfo_SHA1, + .der_size = sizeof(der_DigestInfo_SHA1), + .pkcs11_mech = CKM_SHA_1, .pkcs11_mgf = CKG_MGF1_SHA1, + .x9_31_md = 0x33, }, + { .digest_nid = NID_sha224, .digest_size = SHA224_DIGEST_LENGTH, + .cca_keyword = "SHA-224 ", .der = der_DigestInfo_SHA224, + .der_size = sizeof(der_DigestInfo_SHA224), + .pkcs11_mech = CKM_SHA224, .pkcs11_mgf = CKG_MGF1_SHA224, + .x9_31_md = 0, }, + { .digest_nid = NID_sha256, .digest_size = SHA256_DIGEST_LENGTH, + .cca_keyword = "SHA-256 ", .der = der_DigestInfo_SHA256, + .der_size = sizeof(der_DigestInfo_SHA256), + .pkcs11_mech = CKM_SHA256, .pkcs11_mgf = CKG_MGF1_SHA256, + .x9_31_md = 0x34, }, + { .digest_nid = NID_sha384, .digest_size = SHA384_DIGEST_LENGTH, + .cca_keyword = "SHA-384 ", .der = der_DigestInfo_SHA384, + .der_size = sizeof(der_DigestInfo_SHA384), + .pkcs11_mech = CKM_SHA384, .pkcs11_mgf = CKG_MGF1_SHA384, + .x9_31_md = 0x36, }, + { .digest_nid = NID_sha512, .digest_size = SHA512_DIGEST_LENGTH, + .cca_keyword = "SHA-512 ", .der = der_DigestInfo_SHA512, + .der_size = sizeof(der_DigestInfo_SHA512), + .pkcs11_mech = CKM_SHA512, .pkcs11_mgf = CKG_MGF1_SHA512, + .x9_31_md = 0x35, }, + { .digest_nid = NID_sha3_224, .digest_size = SHA224_DIGEST_LENGTH, + .cca_keyword = NULL, .der = der_DigestInfo_SHA3_224, + .der_size = sizeof(der_DigestInfo_SHA3_224), + .pkcs11_mech = CKM_IBM_SHA3_224, .pkcs11_mgf = CKG_IBM_MGF1_SHA3_224, + .x9_31_md = 0, }, + { .digest_nid = NID_sha3_256, .digest_size = SHA256_DIGEST_LENGTH, + .cca_keyword = NULL, .der = der_DigestInfo_SHA3_256, + .der_size = sizeof(der_DigestInfo_SHA3_256), + .pkcs11_mech = CKM_IBM_SHA3_256, .pkcs11_mgf = CKG_IBM_MGF1_SHA3_256, + .x9_31_md = 0, }, + { .digest_nid = NID_sha3_384, .digest_size = SHA384_DIGEST_LENGTH, + .cca_keyword = NULL, .der = der_DigestInfo_SHA3_384, + .der_size = sizeof(der_DigestInfo_SHA3_384), + .pkcs11_mech = CKM_IBM_SHA3_384, .pkcs11_mgf = CKG_IBM_MGF1_SHA3_384, + .x9_31_md = 0, }, + { .digest_nid = NID_sha3_512, .digest_size = SHA512_DIGEST_LENGTH, + .cca_keyword = NULL, .der = der_DigestInfo_SHA3_512, + .der_size = sizeof(der_DigestInfo_SHA3_512), + .pkcs11_mech = CKM_IBM_SHA3_512, .pkcs11_mgf = CKG_IBM_MGF1_SHA3_512, + .x9_31_md = 0, }, +}; + +static const int digest_list_num = sizeof(digest_list) / + sizeof(struct sk_digest_info); + +/** + * Returns the digest info of the specified digest nid, or NULL if the digest + * is not known. + * + * @param nid the OpenSSL nid of the digest + * + * @returns the address of the digest info or NULL if the digest was not found + */ +const struct sk_digest_info *SK_UTIL_get_digest_info(int digest_nid) +{ + int i; + + for (i = 0; i < digest_list_num; i++) { + if (digest_list[i].digest_nid == digest_nid) + return &digest_list[i]; + } + + return NULL; +} + + + +/** + * Checks if an exact duplicate of the name entry is part of the name already. + */ +static bool SK_UTILS_is_duplicate_name_entry(const X509_NAME *name, + const X509_NAME_ENTRY *entry) +{ + X509_NAME_ENTRY *ne; + int count, i; + + count = X509_NAME_entry_count(name); + for (i = 0; i < count; i++) { + ne = X509_NAME_get_entry(name, i); + if (ne == NULL) + break; + + if (OBJ_cmp(X509_NAME_ENTRY_get_object(entry), + X509_NAME_ENTRY_get_object(ne)) == 0 && + ASN1_STRING_cmp(X509_NAME_ENTRY_get_data(entry), + X509_NAME_ENTRY_get_data(ne)) == 0) + return true; + } + + return false; +} + +/** + * Parse an array of relative distinguished names and builds an X.509 subject + * name. The RDNs are created with type MBSTRING_ASC, unless utf8 is requested, + * then they are created with MBSTRING_UTF8. + * To create a multiple-RDS name, prepend the RDS to add to the previous RDS + * with a '+' character. + * + * @param name the X.509 name created. If *name is not NULL, then + * the RDNs are added to the existing X.509 name. + * @param rdns an array of strings, each string representing an + * RDN in the form '[+]type=value'. If the type is + * prepended with a '+', then this RDN is added to the + * previous one. + * @param num_rdns number of elements in the array. + * @param utf8 if true, RDNs of type MBSTRING_UTF8 are created, + * otherwise type is MBSTRING_ASC is used. + * + * @returns zero for success, a negative errno in case of an error: + * -EINVAL: invalid parameter + * -EBADMSG: an RDN is not formatted correctly + * -EIO: OpenSSL failed to create an X.509 name entry + * -EEXIST: if one of the name entries to add is a duplicate + */ +int SK_UTIL_build_subject_name(X509_NAME **name, const char *rdns[], + size_t num_rdns, bool utf8) +{ + char *rdn, *type, *value; + X509_NAME_ENTRY *ne; + X509_NAME *n; + int rc = 0; + bool multi; + size_t i; + + if (name == NULL || rdns == NULL) + return -EINVAL; + + if (*name != NULL) + n = *name; + else + n = X509_NAME_new(); + if (n == NULL) + return -ENOMEM; + + for (i = 0; i < num_rdns; i++) { + if (rdns[i] == NULL) { + rc = -EINVAL; + break; + } + + rdn = strdup(rdns[i]); + if (rdn == NULL) { + rc = -ENOMEM; + break; + } + + multi = (rdn[0] == '+'); + type = &rdn[multi ? 1 : 0]; + + for (value = type; *value != '=' && *value != '\0'; value++) + ; + if (*value != '=') { + rc = -EBADMSG; + free(rdn); + break; + } + *value = '\0'; + value++; + + ne = X509_NAME_ENTRY_create_by_txt(NULL, type, + utf8 ? MBSTRING_UTF8 : + MBSTRING_ASC, + (unsigned char *)value, -1); + if (ne == NULL) { + rc = -EBADMSG; + free(rdn); + break; + } + + if (SK_UTILS_is_duplicate_name_entry(n, ne)) { + rc = -EEXIST; + X509_NAME_ENTRY_free(ne); + free(rdn); + break; + } + + rc = X509_NAME_add_entry(n, ne, -1, multi ? -1 : 0); + + free(rdn); + X509_NAME_ENTRY_free(ne); + + if (rc != 1) { + rc = -EIO; + break; + } + rc = 0; + } + + if (rc == 0) + *name = n; + else if (*name == NULL) + X509_NAME_free(n); + + return rc; +} + +/** + * Compares X509 Extensions by their nid + */ +static int X509_EXTENSION_compfunc(const X509_EXTENSION * const *a, + const X509_EXTENSION * const *b) +{ + + return (OBJ_obj2nid(X509_EXTENSION_get_object((X509_EXTENSION *)*a)) - + OBJ_obj2nid(X509_EXTENSION_get_object((X509_EXTENSION *)*b))); +} + +/** + * Parse an array of textual X.509 certificate extensions and adds them to + * either an X.509 certificate signing request, or an X.509 certificate. + * + * When adding extensions, a check is performed if an extension with the same + * nid is already added. If so, a duplicate extension is not added, even if + * its value is different from the existing one. + * + * @param cert the X.509 certificate to add the extensions to. + * Either req or cert can be specified. + * @param req the X.509 certificate signing request to add the + * extensions to. Either req or cert can be specified. + * @param exts an array of strings, each string representing an + * certificate extension in the form 'type=value'. + * can be NULL if num_exts is zero. + * @param num_exts number of elements in the array. + * @param addl_exts a stack of extensions to add (can be NULL) + * + * @returns zero for success, a negative errno in case of an error: + * -EINVAL: invalid parameter + * -EBADMSG: an extension is not formatted correctly + * -EIO: OpenSSL failed to create an X.509 extension + * -EEXIST: if one of the extensions to add is a duplicate + */ +int SK_UTIL_build_certificate_extensions(X509 *cert, X509_REQ *req, + const char *exts[], size_t num_exts, + const STACK_OF(X509_EXTENSION) + *addl_exts) +{ + STACK_OF(X509_EXTENSION) *sk_ext; + char *ext, *type, *value; + X509V3_CTX x509v3_ctx; + int count, k, rc = 0; + X509_EXTENSION *ex; + size_t i; + + if (num_exts > 0 && exts == NULL) + return -EINVAL; + if (cert == NULL && req == NULL) + return -EINVAL; + if (cert != NULL && req != NULL) + return -EINVAL; + + sk_ext = sk_X509_EXTENSION_new_null(); + if (sk_ext == NULL) + return -ENOMEM; + + sk_X509_EXTENSION_set_cmp_func(sk_ext, X509_EXTENSION_compfunc); + + for (i = 0; exts != NULL && i < num_exts; i++) { + if (exts[i] == NULL) { + rc = -EINVAL; + break; + } + + ext = strdup(exts[i]); + if (ext == NULL) { + rc = -ENOMEM; + break; + } + + type = &ext[0]; + + for (value = type; *value != '=' && *value != '\0'; value++) + ; + if (*value != '=') { + rc = -EBADMSG; + free(ext); + break; + } + *value = '\0'; + value++; + + rc = -EBADMSG; + ex = X509V3_EXT_conf(NULL, NULL, type, value); + if (ex != NULL) { + if (sk_X509_EXTENSION_find(sk_ext, ex) >= 0) { + rc = -EEXIST; + X509_EXTENSION_free(ex); + free(ext); + break; + } + + rc = sk_X509_EXTENSION_push(sk_ext, ex); + if (rc < 1) { + rc = -EIO; + X509_EXTENSION_free(ex); + free(ext); + break; + } + rc = 0; + } + + free(ext); + } + + if (rc != 0) + goto out; + + if (addl_exts != NULL) { + count = sk_X509_EXTENSION_num(addl_exts); + for (k = 0; k < count; k++) { + ex = sk_X509_EXTENSION_value(addl_exts, k); + if (ex != NULL) { + if (sk_X509_EXTENSION_find(sk_ext, ex) >= 0) { + rc = -EEXIST; + break; + } + + rc = sk_X509_EXTENSION_push(sk_ext, + X509_EXTENSION_dup(ex)); + if (rc < 1) { + rc = -EIO; + break; + } + rc = 0; + } + } + } + + if (rc != 0) + goto out; + + if (req != NULL && sk_X509_EXTENSION_num(sk_ext) > 0) { + if (X509_REQ_add_extensions(req, sk_ext) != 1) + rc = -EIO; + sk_X509_EXTENSION_pop_free(sk_ext, X509_EXTENSION_free); + sk_ext = NULL; + goto out; + } + + if (cert != NULL && sk_X509_EXTENSION_num(sk_ext) > 0) { + X509V3_set_ctx_nodb(&x509v3_ctx); + X509V3_set_ctx(&x509v3_ctx, cert, cert, NULL, NULL, 0); + + rc = 0; + while ((ex = sk_X509_EXTENSION_pop(sk_ext)) != NULL) { + if (rc == 0) { + if (X509_add_ext(cert, ex, -1) != 1) + rc = -EIO; + } + X509_EXTENSION_free(ex); + } + } + +out: + if (sk_ext != NULL) + sk_X509_EXTENSION_pop_free(sk_ext, X509_EXTENSION_free); + return rc; +} + +/** + * Generates a serial number of a specified bit size by random and sets it + * as serial number into the certificate. + * + * @param cert the certificate to set the serial number for + * @param sn_bit_size the size of the serial number in bits + * + * @returns zero for success, a negative errno in case of an error: + * -EINVAL: invalid parameter + * -EIO: error during serial number generation + */ +int SK_UTIL_generate_x509_serial_number(X509 *cert, size_t sn_bit_size) +{ + ASN1_INTEGER *ai = NULL; + BIGNUM *bn = NULL; + int rc; + + if (cert == NULL) + return -EINVAL; + + bn = BN_new(); + if (bn == NULL) + return -ENOMEM; + + rc = BN_rand(bn, sn_bit_size, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY); + if (rc != 1) { + rc = -EIO; + goto out; + } + + ai = X509_get_serialNumber(cert); + if (ai == NULL) { + rc = -EIO; + goto out; + } + + if (BN_to_ASN1_INTEGER(bn, ai) == NULL) { + rc = -EIO; + goto out; + } + + rc = 0; + +out: + if (bn != NULL) + BN_free(bn); + + return rc; +} + +/** + * Builds an DER encoded signature from a raw signature. + * + * @param raw_sig the raw signature to encode + * @param raw_sig_len the size of the raw signature (2 times prime len) + * @param sig a buffer for storing he encoded signature. If + * NULL, then required size is returend in sig_len. + * @param sig_len On entry: the size of the buffer in sig. + * On exit: the size of the encoded sigature. + * + * @returns zero for success, a negative errno in case of an error: + * -ERANGE: signature buffer is too small + * -EIO: error during signature encoding + */ +int SK_UTIL_build_ecdsa_signature(const unsigned char *raw_sig, + size_t raw_sig_len, + unsigned char *sig, size_t *sig_len) +{ + unsigned char *der = NULL; + ECDSA_SIG *ec_sig = NULL; + BIGNUM *bn_r = NULL; + BIGNUM *bn_s = NULL; + int rc = 0, der_len; + + ec_sig = ECDSA_SIG_new(); + if (ec_sig == NULL) { + rc = -ENOMEM; + goto out; + } + + bn_r = BN_bin2bn(raw_sig, raw_sig_len / 2, NULL); + bn_s = BN_bin2bn(raw_sig + raw_sig_len / 2, raw_sig_len / 2, NULL); + if (bn_r == NULL || bn_s == NULL) { + rc = -EIO; + goto out; + } + + if (ECDSA_SIG_set0(ec_sig, bn_r, bn_s) != 1) { + rc = -EIO; + goto out; + } + bn_r = NULL; + bn_s = NULL; + + der_len = i2d_ECDSA_SIG(ec_sig, NULL); + + if (sig == NULL) { + *sig_len = der_len; + goto out; + } + if (der_len > (int)*sig_len) { + rc = -ERANGE; + goto out; + } + + memset(sig, 0, *sig_len); + der = sig; + *sig_len = i2d_ECDSA_SIG(ec_sig, &der); + + if (*sig_len == 0) { + rc = -EIO; + goto out; + } + +out: + if (ec_sig != NULL) + ECDSA_SIG_free(ec_sig); + if (bn_r != NULL) + BN_free(bn_r); + if (bn_s != NULL) + BN_free(bn_s); + + return rc; +} + + +/** + * Reads a X.509 certificate from the specified PEM file. + * + * @param pem_filename the name of the PEM file to read + * @param cert on Return: the X.509 certificate object + * + * @returns zero for success, a negative errno in case of an error: + * -EINVAL: invalid parameter + * -EIO: error during reading in the certificate + * any other errno as returned by fopen + */ +int SK_UTIL_read_x509_certificate(const char *pem_filename, X509 **cert) +{ + FILE *fp; + + if (pem_filename == NULL || cert == NULL) + return -EINVAL; + + fp = fopen(pem_filename, "r"); + if (fp == NULL) + return -errno; + + *cert = PEM_read_X509(fp, NULL, NULL, NULL); + + fclose(fp); + + if (*cert == NULL) + return -EIO; + + return 0; +} + +/** + * Writes a X.509 certificate to the specified PEM file. + * + * @param pem_filename the name of the PEM file to write to + * @param cert the X.509 certificate object to write + * + * @returns zero for success, a negative errno in case of an error: + * -EINVAL: invalid parameter + * -EIO: error during writing out the certificate + * any other errno as returned by fopen + */ +int SK_UTIL_write_x509_certificate(const char *pem_filename, X509 *cert) +{ + FILE *fp; + int rc; + + if (pem_filename == NULL || cert == NULL) + return -EINVAL; + + fp = fopen(pem_filename, "w"); + if (fp == NULL) + return -errno; + + rc = PEM_write_X509(fp, cert); + + fclose(fp); + + if (rc != 1) + return -EIO; + + return 0; +} + +/** + * Writes a X.509 certificate signing request to the specified PEM file. + * + * @param pem_filename the name of the PEM file to write to + * @param req the X.509 request object to write + * @param new_hdr if true, output "NEW" in the PEM header lines + * + * @returns zero for success, a negative errno in case of an error: + * -EINVAL: invalid parameter + * -EIO: error during writing out the certificate + * any other errno as returned by fopen + */ +int SK_UTIL_write_x509_request(const char *pem_filename, X509_REQ *req, + bool new_hdr) +{ + FILE *fp; + int rc; + + if (pem_filename == NULL || req == NULL) + return -EINVAL; + + fp = fopen(pem_filename, "w"); + if (fp == NULL) + return -errno; + + if (new_hdr) + rc = PEM_write_X509_REQ_NEW(fp, req); + else + rc = PEM_write_X509_REQ(fp, req); + + fclose(fp); + + if (rc != 1) + return -EIO; + + return 0; +} + +/** + * Reads a secure key from the specified file. + * + * @param filename the name of the file to read + * @param key_blob on Return: the key blob + * @param key_blob_len on Entry: the size of the buffer, + * on Return: the size of the key blob read + * + * @returns zero for success, a negative errno in case of an error + */ +int SK_UTIL_read_key_blob(const char *filename, unsigned char *key_blob, + size_t *key_blob_len) +{ + size_t count, size; + struct stat sb; + FILE *fp; + + if (filename == NULL || key_blob_len == NULL) + return -EINVAL; + + if (stat(filename, &sb)) + return -errno; + size = sb.st_size; + + if (key_blob == NULL) { + *key_blob_len = size; + return 0; + } + + if (size > *key_blob_len) { + *key_blob_len = size; + return -ERANGE; + } + + fp = fopen(filename, "r"); + if (fp == NULL) + return -errno; + + count = fread(key_blob, 1, size, fp); + if (count != size) { + fclose(fp); + return -EIO; + } + + *key_blob_len = size; + fclose(fp); + return 0; +} + +/** + * Writes a secure key to the specified file. + * + * @param filename the name of the file to write + * @param key_blob the key blob + * @param key_blob_len the size of the key blob + * + * @returns zero for success, a negative errno in case of an error + */ +int SK_UTIL_write_key_blob(const char *filename, unsigned char *key_blob, + size_t key_blob_len) +{ + size_t count; + FILE *fp; + + if (filename == NULL || key_blob == NULL || key_blob_len == 0) + return -EINVAL; + + fp = fopen(filename, "w"); + if (fp == NULL) + return -errno; + + count = fwrite(key_blob, 1, key_blob_len, fp); + if (count != key_blob_len) { + fclose(fp); + return -EIO; + } + + fclose(fp); + return 0; +} + +/** + * Reads a public key from the specified PEM file. + * + * @param pem_filename the name of the PEM file to read + * @param pkey on Return: the PKEY object + * + * @returns zero for success, a negative errno in case of an error: + * -EINVAL: invalid parameter + * -EIO: error during reading in the certificate + * any other errno as returned by fopen + */ +int SK_UTIL_read_public_key(const char *pem_filename, EVP_PKEY **pkey) +{ + FILE *fp; + + if (pem_filename == NULL || pkey == NULL) + return -EINVAL; + + fp = fopen(pem_filename, "r"); + if (fp == NULL) + return -errno; + + *pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL); + + fclose(fp); + + if (*pkey == NULL) + return -EIO; + + return 0; +} + +/** + * Writes a public key to the specified PEM file. + * + * @param pem_filename the name of the PEM file to write to + * @param pkey the PKEY object to write + * + * @returns zero for success, a negative errno in case of an error: + * -EINVAL: invalid parameter + * -EIO: error during writing out the certificate + * any other errno as returned by fopen + */ +int SK_UTIL_write_public_key(const char *pem_filename, EVP_PKEY *pkey) +{ + FILE *fp; + int rc; + + if (pem_filename == NULL || pkey == NULL) + return -EINVAL; + + fp = fopen(pem_filename, "w"); + if (fp == NULL) + return -errno; + + rc = PEM_write_PUBKEY(fp, pkey); + + fclose(fp); + + if (rc != 1) + return -EIO; + + return 0; +} +