From e70cde2c5deed453273de7b3652dfae3858684ca Mon Sep 17 00:00:00 2001 From: Ingo Franzki Date: Thu, 8 Apr 2021 11:33:53 +0200 Subject: [PATCH] libseckey: Add a secure key library MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The libseckey is a secure key library to perform secure key operations with OpenSSL. It provides a framework to create OpenSSL PKEYs with a secure key attached. Such a PKEY contains the public key parts in clear, but the private key as secure key blob. Only the private key operations are actually performed with the secure key, public key operations are performed in software by OpenSSL. It supports CCA and EP11 secure keys for RSA and ECC crypto operations. Because many PKEY method related functions are deprecated since OpenSSL 3.0, two versions of the OpenSSL secure key support are needed. One (using a PKEY method override) for OpenSSL 1.1.1, and another one (using an own OpenSSL provider) for OpenSSL 3.0 and later. The desired implementation is selected automatically at compile time, using OpenSSL version defines. The interface of both implementations is the same, so a user does not need to care which one is used. Reviewed-by: Juergen Christ Signed-off-by: Ingo Franzki Signed-off-by: Jan Höppner --- .gitignore | 2 + Makefile | 3 +- common.mak | 4 + include/libseckey/sk_cca.h | 48 + include/libseckey/sk_ep11.h | 177 ++ include/libseckey/sk_openssl.h | 234 ++ include/libseckey/sk_utilities.h | 89 + libseckey/Makefile | 68 + libseckey/sk_cca.c | 1580 ++++++++++ libseckey/sk_ep11.c | 1555 +++++++++ libseckey/sk_openssl.c | 894 ++++++ libseckey/sk_pkeymeth.c | 1111 +++++++ libseckey/sk_provider.c | 5079 ++++++++++++++++++++++++++++++ libseckey/sk_utilities.c | 1058 +++++++ 14 files changed, 11901 insertions(+), 1 deletion(-) create mode 100644 include/libseckey/sk_cca.h create mode 100644 include/libseckey/sk_ep11.h create mode 100644 include/libseckey/sk_openssl.h create mode 100644 include/libseckey/sk_utilities.h create mode 100644 libseckey/Makefile create mode 100644 libseckey/sk_cca.c create mode 100644 libseckey/sk_ep11.c create mode 100644 libseckey/sk_openssl.c create mode 100644 libseckey/sk_pkeymeth.c create mode 100644 libseckey/sk_provider.c create mode 100644 libseckey/sk_utilities.c 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; +} +