diff --git a/CMakeLists.txt b/CMakeLists.txt index e1c5fd8f41..5a27c8a595 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,16 +65,28 @@ set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin ) set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib ) set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib ) +if(DEFINED ROOT_CA_CERT_PATH) + string(REPLACE ";" "\\$" ROOT_CA_CERT_PATH "${ROOT_CA_CERT_PATH}") +endif() + +if(DEFINED CLIENT_CERT_PATH) + string(REPLACE ";" "\\$" CLIENT_CERT_PATH "${CLIENT_CERT_PATH}") +endif() + +if(DEFINED CLIENT_PRIVATE_KEY_PATH) + string(REPLACE ";" "\\$" CLIENT_PRIVATE_KEY_PATH "${CLIENT_PRIVATE_KEY_PATH}") +endif() + # Set prefix to PWD if any path flags are relative. # PWD is set to the path where you run the cmake command. if(DEFINED ENV{PWD}) - if(ROOT_CA_CERT_PATH AND NOT IS_ABSOLUTE ${ROOT_CA_CERT_PATH}) + if(DEFINED ROOT_CA_CERT_PATH AND NOT ROOT_CA_CERT_PATH MATCHES "^pkcs11.+$" AND NOT IS_ABSOLUTE ${ROOT_CA_CERT_PATH}) set(ROOT_CA_CERT_PATH "$ENV{PWD}/${ROOT_CA_CERT_PATH}") endif() - if(CLIENT_CERT_PATH AND NOT IS_ABSOLUTE ${CLIENT_CERT_PATH}) + if(DEFINED CLIENT_CERT_PATH AND NOT CLIENT_CERT_PATH MATCHES "^pkcs11.+$" AND NOT IS_ABSOLUTE ${CLIENT_CERT_PATH}) set(CLIENT_CERT_PATH "$ENV{PWD}/${CLIENT_CERT_PATH}") endif() - if(CLIENT_PRIVATE_KEY_PATH AND NOT IS_ABSOLUTE ${CLIENT_PRIVATE_KEY_PATH}) + if(DEFINED CLIENT_PRIVATE_KEY_PATH AND NOT CLIENT_PRIVATE_KEY_PATH MATCHES "^pkcs11.+$" AND NOT IS_ABSOLUTE ${CLIENT_PRIVATE_KEY_PATH}) set(CLIENT_PRIVATE_KEY_PATH "$ENV{PWD}/${CLIENT_PRIVATE_KEY_PATH}") endif() endif() diff --git a/docs/README_openssl_pkcs11.md b/docs/README_openssl_pkcs11.md new file mode 100644 index 0000000000..c741bfc7ab --- /dev/null +++ b/docs/README_openssl_pkcs11.md @@ -0,0 +1,149 @@ +# Install Prerequisites +Install the prerequisites for using openssl with the libp11 pkcs11 engine: +``` +sudo apt install softhsm2 gnutls-bin openssl libengine-pkcs11-openssl libp11-kit0 libp11-kit-dev p11-kit p11-kit-modules +``` + +[SoftHSM2](https://github.com/opendnssec/SoftHSMv2) is used as the pkcs11 module in this documentation. It should be replaced with the actual pkcs11 module you plan to use in production. + +If running softhsm on a linux system, be sure to add your user to the "softhsm" group and log-out/log-in if required by your distribution. +``` +sudo adduser $(whoami) softhsm +``` + +# Initialize token and list objects +List existing tokens: +``` +export GNUTLS_SO_PIN=0000 +export GNUTLS_PIN=0000 +p11tool --login --list-all +``` + +## SoftHSM2 +SoftHSM2 is used as an example PKCS#11 module for testing purposes. Depending on your device security model, it may or may not be suitable for use in produiction. + + +Delete any existing tokens if desired: +``` +softhsm2-util --delete-token --token default +``` +Initalize a new token: +``` +softhsm2-util --init-token --free --label default --pin 0000 --so-pin 0000 +``` + +Check that the new token is accessible: +``` +softhsm2-util --show-slots +``` + +When using p11-kit-proxy, SoftHSM should be automatically detected by the openssl pkcs11 engine. + +## p11-kit-trust +P11-Kit includes a Turst Policy module which exposes the trusted CA certificates installed on a system via PKCS#11. + +Note: By default, this module has the "disable-in: p11-kit-proxy" configuration option set. This will cause p11-kit-proxy to disregard this module altogether. If you plan to use PKCS#11 to access to the system trust store, you may want to disable this confgiuration option to allow access to both the system trust store and the PKCS11 module containing your private key. + +## Microchip CryptoAuthLib for ECC608 +[cryptoauthlib](https://github.com/MicrochipTech/cryptoauthlib) + +## p11-kit-proxy +On linux systems, it is quite common to use the [p11-kit-proxy](https://p11-glue.github.io/p11-glue/p11-kit.html) PKCS#11 module to arbitrate access to one or more other PKCS#11 modules from one or more threads. + +p11-kit and other p11-kit aware packages install default "module" configuration files in /usr/share/p11-kit/modules/. p11-kit also searches /etc/pkcs11/modules and ~/.config/pkcs11/modules for additional user-defined module configurations. + +The Openssl PKCS#11 module is configured to use p11-kit-proxy by default in most linux distributions. + +# Generate a new Key +Generate a new public/private key pair if one has not been pre-provisioned in your hsm. +ECC/ECDSA keys typically have a lower computational overhead, so are preferred on low-resource devices. +Generate either an RSA or ECDSA key. + +## Delete any existing key objects +``` +yes | p11tool --login --delete "pkcs11:object=aws_iot_pk" +``` +## RSA +``` +p11tool --login --generate-rsa --bits 2048 --label "aws_iot_pk" pkcs11:token=default +``` +## ECC / ECDSA +``` +p11tool --login --generate-ecc --curve secp256r1 --label "aws_iot_pk" pkcs11:token=default +``` + +# Generate and register a certificate +A certificate can be generated in any of the three ways listed in the following sections. Choose the option that best fits your device provisioning strategy. + +## Self-Signed Certificate +``` +openssl req -new -x509 -subj '/CN=TestIotDevice01/' -days 30 -sha256 -engine pkcs11 -keyform engine -key "pkcs11:object=aws_iot_pk" -outform pem -out aws_iot_pk.crt -passin pass:0000 +``` +Review the contents of the self-signed certificate: +``` +openssl x509 -in aws_iot_pk.crt -noout -text +``` +Register the self-signed certificate with AWS IoT Core +``` +aws iot register-certificate-without-ca --status=ACTIVE --certificate-pem file://aws_iot_pk.crt +``` +## Certificate Signed by a private CA registered with IoT Core +Follow the guides listed below to generate a new Certificate Authority and register that CA with AWS IoT Core. + +[Generate a new CA](https://docs.aws.amazon.com/iot/latest/developerguide/create-your-CA-cert.html) + +[Register your new CA with AWS IoT Core](https://docs.aws.amazon.com/iot/latest/developerguide/register-CA-cert.html) + +Generate a new CSR +``` +openssl req -new -subj '/CN=TestIotDevice01/' -sha256 -engine pkcs11 -keyform engine -key "pkcs11:object=aws_iot_pk" -outform pem -out aws_iot_pk.csr -passin pass:0000 +``` +View the contents of the generated CSR +``` +openssl x509 -in aws_iot_pk.csr -noout -text +``` +Issue a Certificate from you CA + +Follow the instructions for your particular CA managerment software or using something similar to the openssl command below: +``` +openssl x509 -req \ + -in device_cert_csr_filename \ + -CA root_CA_pem_filename \ + -CAkey root_CA_key_filename \ + -CAcreateserial \ + -out device_cert_pem_filename \ + -days 500 -sha256 +``` + +## Certificate Signed by AWS IoT Core +Generate a new CSR +``` +openssl req -new -subj '/CN=TestIotDevice01/' -sha256 -engine pkcs11 -keyform engine -key "pkcs11:object=aws_iot_pk" -outform pem -out aws_iot_pk.csr -passin pass:0000 +``` +View the contents of the generated CSR +``` +openssl x509 -in aws_iot_pk.csr -noout -text +``` +Submit the CSR to IoT Core, receive the new certificate and register it via the aws cli +``` +aws iot create-certificate-from-csr --set-as-active --certificate-signing-request file://aws_iot_pk.csr --certificate-pem-outfile aws_iot_pk.crt +``` + +# Import certificate back into HSM +Next, import the generated certificate back into your HSM / pkcs11 module: +``` +p11tool --login --load-certificate=aws_iot_pk.crt --write --label=aws_iot_pk "pkcs11:token=default" +``` + +# Build the C-SDK demos: + +Use a cmake command similar to the one listed below to build the desired demos: + +``` +cmake -B build2 -DBUILD_DEMOS=1 -DBUILD_TESTS=0 \ +-DAWS_IOT_ENDPOINT="XXXXXXXXXXXXXX-ats.iot.XX-XXXX-X.amazonaws.com" \ +-DCLIENT_CERT_PATH="pkcs11:token=default;object=aws_iot_pk" \ +-DCLIENT_PRIVATE_KEY_PATH="pkcs11:token=default;object=aws_iot_pk?pin-value=0000" \ +-DROOT_CA_CERT_PATH="pkcs11:token=System%20Trust;object=Amazon%20Root%20CA%201" \ +-DBUILD_CLONE_SUBMODULES=0 +``` \ No newline at end of file diff --git a/platform/lexicon.txt b/platform/lexicon.txt index dd456f2945..245a21592f 100644 --- a/platform/lexicon.txt +++ b/platform/lexicon.txt @@ -16,7 +16,6 @@ basedefs bio bitmasking blocksize -blocksize bool bootable bootloader @@ -35,7 +34,6 @@ clienthello cmock com config -config connectsuccessindex const corehttp @@ -104,7 +102,6 @@ int io iot ip -ip keytype lfilecloseresult linux @@ -147,7 +144,6 @@ otaimagestateaccepted otaimagestateinvalid otaimagestatependingcommit otaimagestaterejected -otaimagestaterejected otaimagestatetesting otaimagestateunknown otalastimagestate @@ -168,20 +164,12 @@ otapalimagestatependingcommit otapalimagestateunknown otapalimagestatevalid otapalnullfilecontext -otapalnullfilecontext -otapaloutofmemory otapaloutofmemory otapalrejectfailed -otapalrejectfailed -otapalrxfilecreatefailed otapalrxfilecreatefailed otapalrxfiletoolarge -otapalrxfiletoolarge -otapalsignaturecheckfailed otapalsignaturecheckfailed otapalsuccess -otapalsuccess -otapaluninitialized otapaluninitialized paddrinfo palpnprotos @@ -191,16 +179,17 @@ pbuffer pcdata pcertfilepath pcertificatecontext +pcerturi pclientcertlabel pclientcertpath +pclientcerturi pcontext pctx pdata -pdata pem +pengine pfile pfilecontext -pfilecontext pfilepath pformat phash @@ -209,7 +198,6 @@ pkcs plabelname plaintext platformimagestate -platformimagestate plisthead pnetworkcontext png @@ -218,10 +206,12 @@ pollpri popensslcredentials popensslparams posix +ppengine ppkey pplatformimagestate pprivatekeylabel pprivatekeypath +pprivatekeyuri prandom pre pretryparams @@ -230,6 +220,7 @@ privkeyinfo prng prngcontext prootcapath +prootcauri pserverinfo psig psiglen @@ -300,6 +291,7 @@ ulblockindex ulblocksize uloffset unistd +uri utest utils v1 diff --git a/platform/posix/transport/include/openssl_posix.h b/platform/posix/transport/include/openssl_posix.h index ea45367204..e51b98bea1 100644 --- a/platform/posix/transport/include/openssl_posix.h +++ b/platform/posix/transport/include/openssl_posix.h @@ -69,7 +69,7 @@ * implementation that uses OpenSSL and POSIX sockets. * * @note For this transport implementation, the socket descriptor and - * SSL context is used. + * SSL context are kept. */ typedef struct OpensslParams { @@ -135,9 +135,9 @@ typedef struct OpensslCredentials * * @note These strings must be NULL-terminated because the OpenSSL API requires them to be. */ - const char * pRootCaPath; /**< @brief Filepath string to the trusted server root CA. */ - const char * pClientCertPath; /**< @brief Filepath string to the client certificate. */ - const char * pPrivateKeyPath; /**< @brief Filepath string to the client certificate's private key. */ + const char * pRootCaPath; /**< @brief File path or PKCS#11 URI to the trusted server root CA certificate. */ + const char * pClientCertPath; /**< @brief File path or PKCS#11 URI to the tls client certificate. */ + const char * pPrivateKeyPath; /**< @brief File path or PKCS#11 URI to the tls client private key. */ } OpensslCredentials_t; /** diff --git a/platform/posix/transport/src/openssl_posix.c b/platform/posix/transport/src/openssl_posix.c index ec598aeb2a..5229cf6cf0 100644 --- a/platform/posix/transport/src/openssl_posix.c +++ b/platform/posix/transport/src/openssl_posix.c @@ -24,6 +24,7 @@ /* Standard includes. */ #include #include +#include /* POSIX socket includes. */ #include @@ -34,6 +35,8 @@ #include "openssl_posix.h" #include +#include +#include /*-----------------------------------------------------------*/ @@ -52,6 +55,14 @@ */ #define CLIENT_KEY_LABEL "client's key" +/** + * @brief Openssl engine_id corresponding to the libp11 engine. + */ +#define PKCS11_ENGINE_ID "pkcs11" + + +#define PKCS11_URI_PREFIX "pkcs11:" + /*-----------------------------------------------------------*/ /* Each compilation unit must define the NetworkContext struct. */ @@ -73,12 +84,27 @@ struct NetworkContext const char * fileType ); #endif /* #if ( LIBRARY_LOG_LEVEL == LOG_DEBUG ) */ + +/** + * @brief Log the last openssl error. + */ +static int32_t opensslError( void ); + /** - * @brief Add X509 certificate to the trusted list of root certificates. + * @brief Load a certificate with the given PKCS#11 URI and return the resulting openssl X509 object. * - * OpenSSL does not provide a single function for reading and loading - * certificates from files into stores, so the file API must be called. Start - * with the root certificate. + * @param[out] ppX509Cert Location to store a pointer to the created X509 certificate object. + * @param[in] pEngine Pointer to the pre-initialized openssl PKCS11 engine. + * @param[in] pCertURI PKCS#11 URI for the desired certificate. + * + * @return 1 on success; -1, 0 on failure. + */ +static int32_t loadCertificateFromPkcs11( X509 ** ppX509Cert, + ENGINE * pEngine, + const char * pCertURI ); + +/** + * @brief Add X509 certificate from a file to the trusted list of root certificates. * * @param[out] pSslContext SSL context to which the trusted server root CA is to * be added. @@ -86,12 +112,24 @@ struct NetworkContext * * @return 1 on success; -1, 0 on failure. */ -static int32_t setRootCa( const SSL_CTX * pSslContext, - const char * pRootCaPath ); +static int32_t setRootCaFromFile( const SSL_CTX * pSslContext, + const char * pRootCaPath ); /** - * @brief Set X509 certificate as client certificate for the server to - * authenticate. + * @brief Add X509 certificate from a PKCS#11 token to the trusted list of root certificates. + * + * @param[out] pSslContext SSL context to which the trusted server root CA is to + * be added. + * @param[in] pRootCaURI Filepath string to the trusted server root CA. + * + * @return 1 on success; -1, 0 on failure. + */ +static int32_t setRootCaFromPkcs11( const SSL_CTX * pSslContext, + ENGINE * pEngine, + const char * pRootCaURI ); + +/** + * @brief Load an X509 client certificate from a given file. * * @param[out] pSslContext SSL context to which the client certificate is to be * set. @@ -99,19 +137,54 @@ static int32_t setRootCa( const SSL_CTX * pSslContext, * * @return 1 on success; 0 failure. */ -static int32_t setClientCertificate( SSL_CTX * pSslContext, - const char * pClientCertPath ); +static int32_t setClientCertificateFromFile( SSL_CTX * pSslContext, + const char * pClientCertPath ); + +/** + * @brief Load an X509 client certificate from a PKCS11 module via a given engine. + * + * @param[out] pSslContext SSL context to which the client certificate is to be + * set. + * @param[in] pClientCertURI PKCS11 URI string for the client certificate object. + * + * @return 1 on success; 0 failure. + */ +static int32_t setClientCertificateFromPkcs11( SSL_CTX * pSslContext, + ENGINE * pEngine, + const char * pClientCertURI ); + +/** + * @brief Load a private key into the ssl context from a given file path. + * + * @param[out] pSslContext SSL context to add the private key to. + * @param[in] pPrivateKeyPath Path to the client private key file in PEM format. + * + * @return 1 on success; 0 on failure. + */ +static int32_t setPrivateKeyFromFile( SSL_CTX * pSslContext, + const char * pPrivateKeyPath ); /** * @brief Set private key for the client's certificate. * - * @param[out] pSslContext SSL context to which the private key is to be added. - * @param[in] pPrivateKeyPath Filepath string to the client private key. + * @param[out] pSslContext SSL context to add the private key to. + * @param[in] pEngine Openssl engine handle to read the key from. + * @param[in] pPrivateKeyURI PKCS#11 URI to load the private key from. + * + * @return 1 on success; 0 on failure. + */ +static int32_t setPrivateKeyFromPkcs11( SSL_CTX * pSslContext, + ENGINE * pEngine, + const char * pPrivateKeyURI ); + +/** + * @brief Initialize the openssl pkcs11 engine. + * + * @param[out] ppEngine Pointer to write the resulting ENGINE object pointer to. * * @return 1 on success; 0 on failure. */ -static int32_t setPrivateKey( SSL_CTX * pSslContext, - const char * pPrivateKeyPath ); +static int32_t initializePkcs11Engine( ENGINE ** ppEngine ); /** * @brief Passes TLS credentials to the OpenSSL library. @@ -297,9 +370,10 @@ static OpensslStatus_t tlsHandshake( const ServerInfo_t * pServerInfo, return returnStatus; } +/*-----------------------------------------------------------*/ -static int32_t setRootCa( const SSL_CTX * pSslContext, - const char * pRootCaPath ) +static int32_t setRootCaFromFile( const SSL_CTX * pSslContext, + const char * pRootCaPath ) { int32_t sslStatus = 1; FILE * pRootCaFile = NULL; @@ -387,10 +461,86 @@ static int32_t setRootCa( const SSL_CTX * pSslContext, } /*-----------------------------------------------------------*/ -static int32_t setClientCertificate( SSL_CTX * pSslContext, - const char * pClientCertPath ) +static int32_t loadCertificateFromPkcs11( X509 ** ppX509Cert, + ENGINE * pEngine, + const char * pCertURI ) { - int32_t sslStatus = -1; + int32_t sslStatus = 1; + + struct + { + const char * pCertURI; + X509 * pX509Cert; + } + loadCertParams; + + assert( ppX509Cert != NULL ); + assert( pEngine != NULL ); + assert( pCertURI != NULL ); + + loadCertParams.pCertURI = pCertURI; + loadCertParams.pX509Cert = NULL; + + sslStatus = ENGINE_ctrl_cmd( pEngine, "LOAD_CERT_CTRL", 0, + &loadCertParams, NULL, 0 ); + + if( loadCertParams.pX509Cert == NULL ) + { + LogError( ( "ENGINE_ctrl_cmd returned an empty certificate object " + "from URI: %s .", pCertURI ) ); + sslStatus = 0; + } + else if( sslStatus != 1 ) + { + LogError( ( "ENGINE_ctrl_cmd failed to read " + "certificate from URI: %s .", pCertURI ) ); + sslStatus = opensslError(); + } + else + { + *ppX509Cert = loadCertParams.pX509Cert; + } + + return sslStatus; +} +/*-----------------------------------------------------------*/ + +static int32_t setRootCaFromPkcs11( const SSL_CTX * pSslContext, + ENGINE * pEngine, + const char * pRootCaURI ) +{ + int32_t sslStatus = 1; + X509 * pRootCa = NULL; + + assert( pSslContext != NULL ); + assert( pEngine != NULL ); + assert( pRootCaURI != NULL ); + + sslStatus = loadCertificateFromPkcs11( &pRootCa, pEngine, pRootCaURI ); + + if( sslStatus == 1 ) + { + LogInfo( ( "Successfully read client certificate from URI: %s .", + pClientCertURI ) ); + + /* Import cert into context */ + sslStatus = X509_STORE_add_cert( SSL_CTX_get_cert_store( pSslContext ), pRootCa ); + + if( sslStatus != 1 ) + { + LogError( ( "Failed to add Root CA certificate to SSL context from URI : %s .", + pRootCaURI ) ); + } + } + + return sslStatus; +} +/*-----------------------------------------------------------*/ + +static int32_t setClientCertificateFromFile( SSL_CTX * pSslContext, + const char * pClientCertPath ) +{ + int32_t sslStatus = 0; assert( pSslContext != NULL ); assert( pClientCertPath != NULL ); @@ -400,12 +550,13 @@ static int32_t setClientCertificate( SSL_CTX * pSslContext, #endif /* Import the client certificate. */ - sslStatus = SSL_CTX_use_certificate_chain_file( pSslContext, pClientCertPath ); + sslStatus = SSL_CTX_use_certificate_file( pSslContext, pClientCertPath, + SSL_FILETYPE_PEM ); if( sslStatus != 1 ) { - LogError( ( "SSL_CTX_use_certificate_chain_file failed to import " - "client certificate at %s.", + LogError( ( "SSL_CTX_use_certificate_file failed to read " + "client certificate from path %s.", pClientCertPath ) ); } else @@ -417,10 +568,42 @@ static int32_t setClientCertificate( SSL_CTX * pSslContext, } /*-----------------------------------------------------------*/ -static int32_t setPrivateKey( SSL_CTX * pSslContext, - const char * pPrivateKeyPath ) +static int32_t setClientCertificateFromPkcs11( SSL_CTX * pSslContext, + ENGINE * pEngine, + const char * pClientCertURI ) { - int32_t sslStatus = -1; + int32_t sslStatus = 1; + X509 * pX509Cert = NULL; + + assert( pSslContext != NULL ); + assert( pEngine != NULL ); + assert( pClientCertURI != NULL ); + + sslStatus = loadCertificateFromPkcs11( &pX509Cert, pEngine, pClientCertURI ); + + if( sslStatus == 1 ) + { + LogInfo( ( "Successfully read client certificate from URI: %s .", + pClientCertURI ) ); + + /* Import cert into context */ + sslStatus = SSL_CTX_use_certificate( pSslContext, pX509Cert ); + + if( sslStatus != 1 ) + { + LogError( ( "Failed to copy certificate into SSL context from URI : %s .", + pClientCertURI ) ); + } + } + + return sslStatus; +} +/*-----------------------------------------------------------*/ + +static int32_t setPrivateKeyFromFile( SSL_CTX * pSslContext, + const char * pPrivateKeyPath ) +{ + int32_t sslStatus = 0; assert( pSslContext != NULL ); assert( pPrivateKeyPath != NULL ); @@ -429,7 +612,7 @@ static int32_t setPrivateKey( SSL_CTX * pSslContext, logPath( pPrivateKeyPath, CLIENT_KEY_LABEL ); #endif - /* Import the client certificate private key. */ + /* Read the private key from a file */ sslStatus = SSL_CTX_use_PrivateKey_file( pSslContext, pPrivateKeyPath, SSL_FILETYPE_PEM ); @@ -448,29 +631,221 @@ static int32_t setPrivateKey( SSL_CTX * pSslContext, } /*-----------------------------------------------------------*/ +static int32_t setPrivateKeyFromPkcs11( SSL_CTX * pSslContext, + ENGINE * pEngine, + const char * pPrivateKeyURI ) +{ + int32_t sslStatus = 0; + EVP_PKEY * pPrivateKey = NULL; + + assert( pSslContext != NULL ); + assert( pEngine != NULL ); + assert( pPrivateKeyURI != NULL ); + + #if ( LIBRARY_LOG_LEVEL == LOG_DEBUG ) + logPath( pPrivateKeyURI, CLIENT_KEY_LABEL ); + #endif + + pPrivateKey = ENGINE_load_private_key( pEngine, pPrivateKeyURI, NULL, NULL ); + + if( pPrivateKey == NULL ) + { + LogError( ( "ENGINE_load_private_key failed to load private key at uri: %s.", + pPrivateKeyURI ) ); + } + + /* Import the client certificate private key. */ + sslStatus = SSL_CTX_use_PrivateKey( pSslContext, pPrivateKey ); + + if( sslStatus != 1 ) + { + LogError( ( "SSL_CTX_use_PrivateKey failed to load private key " + "with URI: %s.", pPrivateKeyURI ) ); + } + else + { + LogDebug( ( "Successfully loaded client private key." ) ); + } + + return sslStatus; +} +/*-----------------------------------------------------------*/ + +static int32_t opensslError( void ) +{ + #if LIBRARY_LOG_LEVEL >= LOG_ERROR + int32_t errorCode = -1; + const char * pFile = NULL; + const char * pEmptyString = ""; + int line = 0; + char * pErrorString = NULL; + + errorCode = ( int32_t ) ERR_peek_last_error_line_data( &pFile, &line, NULL, NULL ); + + if( pFile == NULL ) + { + pFile = pEmptyString; + } + + pErrorString = ERR_error_string( errorCode, NULL ); + + SdkLog( ( "[ERROR] " "[openssl] [%s:%d] %d %s\r\n", + pFile, line, errorCode, pErrorString ) ); + + return errorCode; + #else /* if LIBRARY_LOG_LEVEL >= LOG_ERROR */ + return ( int32_t ) ERR_peek_last_error(); + #endif /* if LIBRARY_LOG_LEVEL >= LOG_ERROR */ +} + +/*-----------------------------------------------------------*/ +static int32_t initializePkcs11Engine( ENGINE ** ppEngine ) +{ + int32_t sslStatus = 1; + ENGINE * pEngine = NULL; + + assert( ppEngine != NULL ); + + ENGINE_load_builtin_engines(); + + /* Acquire a structural reference for the pkcs11 engine */ + pEngine = ENGINE_by_id( PKCS11_ENGINE_ID ); + + if( pEngine == NULL ) + { + LogError( ( "Failed to load the openssl pkcs11 engine." ) ); + sslStatus = opensslError(); + } + + /* Increase log level if necessary */ + #if LIBRARY_LOG_LEVEL >= LOG_INFO + if( ( sslStatus == 1 ) && + ( ENGINE_ctrl_cmd_string( pEngine, "VERBOSE", NULL, 0 ) != 1 ) ) + { + LogError( ( "Failed to increment the pkcs11 engine verbosity level." ) ); + sslStatus = opensslError(); + } + #endif + + if( sslStatus == 1 ) + { + /* Initialize the pkcs11 engine and acquire a functional reference to it */ + if( ENGINE_init( pEngine ) != 1 ) + { + LogError( ( "Failed to initialize the openssl pkcs11 engine." ) ); + sslStatus = opensslError(); + } + } + + if( sslStatus == 1 ) + { + *ppEngine = pEngine; + } + else + { + ( void ) ENGINE_finish( pEngine ); + } + + return sslStatus; +} + +/*-----------------------------------------------------------*/ static int32_t setCredentials( SSL_CTX * pSslContext, const OpensslCredentials_t * pOpensslCredentials ) { - int32_t sslStatus = 0; + int32_t sslStatus = 1; + ENGINE * pEngine = NULL; + bool certFromP11 = false; + bool pkeyFromP11 = false; + bool rootCaFromP11 = false; assert( pSslContext != NULL ); assert( pOpensslCredentials != NULL ); - if( pOpensslCredentials->pRootCaPath != NULL ) + /* Initialize the pkcs11 engine if needed */ + if( ( pOpensslCredentials->pPrivateKeyPath != NULL ) && + ( pOpensslCredentials->pClientCertPath != NULL ) ) { - sslStatus = setRootCa( pSslContext, pOpensslCredentials->pRootCaPath ); + if( strncmp( pOpensslCredentials->pPrivateKeyPath, + PKCS11_URI_PREFIX, sizeof( PKCS11_URI_PREFIX ) - 1 ) == 0 ) + { + pkeyFromP11 = true; + } + + if( strncmp( pOpensslCredentials->pRootCaPath, + PKCS11_URI_PREFIX, sizeof( PKCS11_URI_PREFIX ) - 1 ) == 0 ) + { + rootCaFromP11 = true; + } + + if( strncmp( pOpensslCredentials->pClientCertPath, + PKCS11_URI_PREFIX, sizeof( PKCS11_URI_PREFIX ) - 1 ) == 0 ) + { + certFromP11 = true; + } + + if( ( pkeyFromP11 == true ) || ( certFromP11 == true ) || ( rootCaFromP11 == true ) ) + { + sslStatus = initializePkcs11Engine( &pEngine ); + } + } + + if( ( sslStatus == 1 ) && ( pOpensslCredentials->pRootCaPath != NULL ) ) + { + if( rootCaFromP11 == true ) + { + sslStatus = setRootCaFromPkcs11( pSslContext, + pEngine, + pOpensslCredentials->pRootCaPath ); + } + else + { + sslStatus = setRootCaFromFile( pSslContext, + pOpensslCredentials->pRootCaPath ); + } } if( ( sslStatus == 1 ) && ( pOpensslCredentials->pClientCertPath != NULL ) ) { - sslStatus = - setClientCertificate( pSslContext, pOpensslCredentials->pClientCertPath ); + if( pkeyFromP11 == true ) + { + sslStatus = setClientCertificateFromPkcs11( pSslContext, + pEngine, + pOpensslCredentials->pClientCertPath ); + } + else + { + sslStatus = setClientCertificateFromFile( pSslContext, + pOpensslCredentials->pClientCertPath ); + } } if( ( sslStatus == 1 ) && ( pOpensslCredentials->pPrivateKeyPath != NULL ) ) { - sslStatus = - setPrivateKey( pSslContext, pOpensslCredentials->pPrivateKeyPath ); + if( pkeyFromP11 == true ) + { + sslStatus = setPrivateKeyFromPkcs11( pSslContext, + pEngine, + pOpensslCredentials->pPrivateKeyPath ); + } + else + { + sslStatus = setPrivateKeyFromFile( pSslContext, + pOpensslCredentials->pPrivateKeyPath ); + } + } + + /* Free structural and functional references to the engine */ + if( pEngine != NULL ) + { + sslStatus = ENGINE_finish( pEngine ); + #if LIBRARY_LOG_LEVEL >= LOG_ERROR + if( sslStatus != 1 ) + { + LogError( ( "Failed to free the openssl pkcs11 engine reference." ) ); + ( void ) opensslError(); + } + #endif } return sslStatus; @@ -686,6 +1061,7 @@ OpensslStatus_t Openssl_Connect( NetworkContext_t * pNetworkContext, /* Clean up on error. */ if( ( returnStatus != OPENSSL_SUCCESS ) && ( sslObjectCreated == 1u ) ) { + /* Free SSL connection structure */ SSL_free( pOpensslParams->pSsl ); pOpensslParams->pSsl = NULL; } diff --git a/tools/cmake/utility.cmake b/tools/cmake/utility.cmake index 275db06cd8..cee6c84592 100644 --- a/tools/cmake/utility.cmake +++ b/tools/cmake/utility.cmake @@ -29,7 +29,7 @@ function(set_macro_definitions) # Compile the application with the macro definition if it is defined. target_compile_definitions( ${application_target} PRIVATE - ${optional_macro_definition}="${${optional_macro_definition}}" + "${optional_macro_definition}=\"${${optional_macro_definition}}\"" ) list(APPEND DEFINED_MACROS_FROM_CMAKE "${optional_macro_definition}") endif() @@ -41,7 +41,7 @@ function(set_macro_definitions) if(DEFINED ${required_macro_definition}) target_compile_definitions( ${application_target} PRIVATE - ${required_macro_definition}="${${required_macro_definition}}" + "${required_macro_definition}=\"${${required_macro_definition}}\"" ) # This variable adds definitions to the file being run against `check_symbol_exists`. list(APPEND CMAKE_REQUIRED_DEFINITIONS -D${required_macro_definition})