/*
 * FreeRTOS V202212.00
 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * https://www.FreeRTOS.org
 * https://github.com/FreeRTOS
 *
 */

/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"

/* Standard include. */
#include "stdio.h"

/* PKCS #11 includes. */
#include "core_pkcs11_config.h"
#include "core_pkcs11.h"
#include "pkcs11.h"

/* mbed TLS includes. */
#include "mbedtls/pk.h"

/* Demo includes. */
#include "demo_helpers.h"
#include "pkcs11_demos.h"

/* RSA certificate that has been generated off the device.
 * This key will be used as an example for importing an object onto the device.
 * This is useful when the device itself cannot create credentials or for storing
 * a well known CA certificate.
 */
#define pkcs11demo_RSA_CERTIFICATE                                       \
    ""                                                                   \
    "-----BEGIN CERTIFICATE-----\n"                                      \
    "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\n" \
    "xg07nhvDeydwB48xXrPQ1KutrRyh\n"                                     \
    "-----END CERTIFICATE-----"

/* This function can be found in
 * FreeRTOS/FreeRTOS-Plus/Source/FreeRTOS-Plus-PKCS11/3rdparty/mbedtls_utils/mbedtls_utils.c.
 * It will be used to convert the RSA certificate from PEM format
 * to DER format. */
extern int convert_pem_to_der( const unsigned char * pucInput,
                               size_t xLen,
                               unsigned char * pucOutput,
                               size_t * pxOlen );
/*-----------------------------------------------------------*/


/**
 * prvObjectImporting covers how to import a RSA certificate that was
 * not generated by the Cryptoki library.
 *
 */
static void prvObjectImporting( void );

/**
 * prvObjectGeneration covers how to create a public key and private key pair
 * with Cryptoki defined attributes using C_GenerateKeyPair.
 *
 * Note: The "sign-verify.c" demo has a dependency on the objects created
 * in this function, and will not work without first running this function.
 */
static void prvObjectGeneration( void );


/**
 * This function details how to use the PKCS #11 "Object" functions to
 * manage the objects abstracted by cryptoki.
 *
 * http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/os/pkcs11-base-v2.40-os.html
 * please consult the standard for more information.
 *
 * The standard has grouped the functions presented in this demo as:
 * Object Management Functions.
 *
 */
void vPKCS11ObjectDemo( void )
{
    configPRINTF( ( "\r\nStarting PKCS #11 Objects Demo.\r\n" ) );

    /* PKCS #11 defines objects as "An item that is stored on a token. May be
     * data, a certificate, or a key." This demo will show how to create objects
     * that are managed by Cryptoki. */
    prvObjectImporting();
    prvObjectGeneration();
    configPRINTF( ( "\r\nFinished PKCS #11 Objects Demo.\r\n" ) );
}

static void prvObjectImporting( void )
{
    configPRINTF( ( "---------Importing Objects---------\r\n" ) );
    configPRINTF( ( "Importing RSA Certificate...\r\n" ) );

    /* Helper variables and variables that have been covered. */
    CK_RV xResult = CKR_OK;
    CK_SESSION_HANDLE hSession = CK_INVALID_HANDLE;
    CK_SLOT_ID * pxSlotId = 0;
    CK_FUNCTION_LIST_PTR pxFunctionList = NULL;
    uint8_t * pucDerObject = NULL;
    int32_t lConversionReturn = 0;
    size_t xDerLen = 0;
    CK_BBOOL xTokenStorage = CK_TRUE;
    CK_OBJECT_HANDLE xCertHandle = CK_INVALID_HANDLE;
    CK_BYTE xSubject[] = "TestSubject";


    /* The PKCS11_CertificateTemplate_t is a custom struct defined in "core_pkcs11.h"
     * in order to make it easier to import a certificate. This struct will be
     * populated with the parameters necessary to import the certificate into the
     * Cryptoki library.
     */
    PKCS11_CertificateTemplate_t xCertificateTemplate;

    /* The object class is specified as a certificate to help the Cryptoki library
     * parse the arguments.
     */
    CK_OBJECT_CLASS xCertificateClass = CKO_CERTIFICATE;

    /* The certificate type is an x509 certificate, which is the only type
     * supported by this stack. To read more about x509 certificates one can
     * read the following:
     *
     * https://en.wikipedia.org/wiki/X.509
     * https://www.ssl.com/faqs/what-is-an-x-509-certificate/
     *
     */
    CK_CERTIFICATE_TYPE xCertificateType = CKC_X_509;

    /* The label will help the application identify which object it would like
     * to access.
     */
    CK_BYTE pucLabel[] = pkcs11configLABEL_DEVICE_CERTIFICATE_FOR_TLS;

    /* Specify certificate class. */
    xCertificateTemplate.xObjectClass.type = CKA_CLASS;
    xCertificateTemplate.xObjectClass.pValue = &xCertificateClass;
    xCertificateTemplate.xObjectClass.ulValueLen = sizeof( xCertificateClass );

    /* Specify certificate subject. */
    xCertificateTemplate.xSubject.type = CKA_SUBJECT;
    xCertificateTemplate.xSubject.pValue = xSubject;
    xCertificateTemplate.xSubject.ulValueLen = sizeof( xSubject ) - 1UL;

    /* Point to contents of certificate. */
    xCertificateTemplate.xValue.type = CKA_VALUE;
    xCertificateTemplate.xValue.pValue = ( CK_VOID_PTR ) pkcs11demo_RSA_CERTIFICATE;
    xCertificateTemplate.xValue.ulValueLen = ( CK_ULONG ) sizeof( pkcs11demo_RSA_CERTIFICATE ) - 1UL;

    /* Specify certificate label. */
    xCertificateTemplate.xLabel.type = CKA_LABEL;
    xCertificateTemplate.xLabel.pValue = ( CK_VOID_PTR ) pucLabel;
    xCertificateTemplate.xLabel.ulValueLen = sizeof( pucLabel ) - 1UL;

    /* Specify certificate type as x509. */
    xCertificateTemplate.xCertificateType.type = CKA_CERTIFICATE_TYPE;
    xCertificateTemplate.xCertificateType.pValue = &xCertificateType;
    xCertificateTemplate.xCertificateType.ulValueLen = sizeof( CK_CERTIFICATE_TYPE );

    /* Specify that the certificate should be on a token. */
    xCertificateTemplate.xTokenObject.type = CKA_TOKEN;
    xCertificateTemplate.xTokenObject.pValue = &xTokenStorage;
    xCertificateTemplate.xTokenObject.ulValueLen = sizeof( xTokenStorage );

    vStart( &hSession, &pxSlotId );

    /* Ensure the Cryptoki library has the necessary functions implemented. */
    xResult = C_GetFunctionList( &pxFunctionList );
    configASSERT( xResult == CKR_OK );
    configASSERT( pxFunctionList->C_CreateObject != NULL );

    /* Convert the certificate to DER format if it was in PEM. The DER key
     * should be about 3/4 the size of the PEM key, so mallocing the PEM key
     * size is sufficient. */
    pucDerObject = pvPortMalloc( xCertificateTemplate.xValue.ulValueLen );
    configASSERT( pucDerObject != NULL );

    xDerLen = xCertificateTemplate.xValue.ulValueLen;
    lConversionReturn = convert_pem_to_der( xCertificateTemplate.xValue.pValue,
                                            xCertificateTemplate.xValue.ulValueLen,
                                            pucDerObject,
                                            &xDerLen );

    configASSERT( 0 == lConversionReturn );

    /* Set the template pointers to refer to the DER converted objects. */
    xCertificateTemplate.xValue.pValue = pucDerObject;
    xCertificateTemplate.xValue.ulValueLen = xDerLen;

    /* Create an object using the encoded client certificate. */
    configPRINTF( ( "Creating x509 certificate with label: %s \r\n",
                    pkcs11configLABEL_DEVICE_CERTIFICATE_FOR_TLS ) );

    /* Once the Cryptoki library has finished importing the new x509 certificate
     * a CK_OBJECT_HANDLE is associated with it. The application can now use this
     * to refer to the object in following operations.
     *
     * xCertHandle in the below example will have it's value modified to
     * be the CK_OBJECT_HANDLE.
     *
     * Compare the hard coded x509, in PEM format, with the DER formatted
     * x509 certificate that is created by the Cryptoki library, with the following
     * OpenSSL command:
     * "$ openssl x509 -in corePKCS11_Certificate.dat -inform der -text"
     *
     * See this explanation for the difference between the PEM format and the
     * DER format:
     * https://stackoverflow.com/questions/22743415/what-are-the-differences-between-pem-cer-and-der/22743616
     *
     */
    xResult = pxFunctionList->C_CreateObject( hSession,
                                              ( CK_ATTRIBUTE_PTR ) &xCertificateTemplate,
                                              sizeof( xCertificateTemplate ) / sizeof( CK_ATTRIBUTE ),
                                              &xCertHandle );

    configASSERT( xResult == CKR_OK );
    configASSERT( xCertHandle != CK_INVALID_HANDLE );

    configPRINTF( ( "corePKCS11_Certificate.dat has been created in the Visual Studio" \
                    " Solution directory\r\n" ) );

    vPortFree( pucDerObject );
    vEnd( hSession, pxSlotId );
    configPRINTF( ( "Finished Importing RSA Certificate.\r\n" ) );
    configPRINTF( ( "---------Finished Importing Objects---------\r\n" ) );
}

static void prvObjectGeneration( void )
{
    configPRINTF( ( "---------Generating Objects---------\r\n" ) );

    /* Helper variables. */
    CK_RV xResult = CKR_OK;
    CK_SESSION_HANDLE hSession = CK_INVALID_HANDLE;
    CK_SLOT_ID * pxSlotId = 0;
    CK_FUNCTION_LIST_PTR pxFunctionList = NULL;
    CK_BYTE * pxDerPublicKey = NULL;
    CK_ULONG ulDerPublicKeyLength = 0;
    CK_BBOOL xTrue = CK_TRUE;

    /* Specify the mechanism to use in the key pair generation. Mechanisms are
     * previously explained in the "mechanims_and_digests.c" demo. */
    CK_MECHANISM xMechanism =
    {
        CKM_EC_KEY_PAIR_GEN, NULL_PTR, 0
    };

    /* The EC curve used in this demo will be the named EC curve prime256v1.
     * For further explanations of EC Cryptography please see the following:
     * https://en.wikipedia.org/wiki/Elliptic-curve_cryptography
     * https://wiki.openssl.org/index.php/Elliptic_Curve_Cryptography
     */
    CK_BYTE xEcParams[] = pkcs11DER_ENCODED_OID_P256;

    /* Specify the key type to be EC. */
    CK_KEY_TYPE xKeyType = CKK_EC;

    /* Object handles are a token specific identifier for an object. They are
     * used so the application's sessions can specify which object to interact
     * with. Non-zero values are valid, 0 is always invalid, and is defined as
     * CK_INVALID_HANDLE
     *
     * The lifetime of the handle is not necessarily the same as the lifetime of
     * the object.
     */
    CK_OBJECT_HANDLE xPrivateKeyHandle = CK_INVALID_HANDLE;
    CK_OBJECT_HANDLE xPublicKeyHandle = CK_INVALID_HANDLE;


    /* Labels are application defined strings that are used to identify an
     * object. It should not be NULL terminated. */
    CK_BYTE pucPublicKeyLabel[] = { pkcs11configLABEL_DEVICE_PUBLIC_KEY_FOR_TLS };
    CK_BYTE pucPrivateKeyLabel[] = { pkcs11configLABEL_DEVICE_PRIVATE_KEY_FOR_TLS };

    /* CK_ATTRIBUTE's contain an attribute type, a value, and the length of
     * the value. An array of CK_ATTRIBUTEs is called a template. They are used
     * for creating, searching, and manipulating for objects. The order of the
     * template does not matter.
     *
     * In the below template we are creating a public key:
     *      Specify the key type as EC.
     *      The key will be able to verify a message.
     *      Specify the EC Curve.
     *      Assign a label to the object that will be created.
     */
    CK_ATTRIBUTE xPublicKeyTemplate[] =
    {
        { CKA_KEY_TYPE,  &xKeyType,         sizeof( xKeyType )              },
        { CKA_VERIFY,    &xTrue,            sizeof( xTrue )                 },
        { CKA_EC_PARAMS, xEcParams,         sizeof( xEcParams )             },
        { CKA_LABEL,     pucPublicKeyLabel, sizeof( pucPublicKeyLabel ) - 1 }
    };

    /* In the below template we are creating a private key:
     *      The key type is EC.
     *      The key is a token object.
     *      The key will be a private key.
     *      The key will be able to sign messages.
     *      Assign a label to the object that will be created.
     */
    CK_ATTRIBUTE xPrivateKeyTemplate[] =
    {
        { CKA_KEY_TYPE, &xKeyType,          sizeof( xKeyType )               },
        { CKA_TOKEN,    &xTrue,             sizeof( xTrue )                  },
        { CKA_PRIVATE,  &xTrue,             sizeof( xTrue )                  },
        { CKA_SIGN,     &xTrue,             sizeof( xTrue )                  },
        { CKA_LABEL,    pucPrivateKeyLabel, sizeof( pucPrivateKeyLabel ) - 1 }
    };

    vStart( &hSession, &pxSlotId );

    xResult = C_GetFunctionList( &pxFunctionList );
    configASSERT( xResult == CKR_OK );

    configPRINTF( ( "Creating private key with label: %s \r\n",
                    pkcs11configLABEL_DEVICE_PRIVATE_KEY_FOR_TLS ) );
    configPRINTF( ( "Creating public key with label: %s \r\n",
                    pkcs11configLABEL_DEVICE_PUBLIC_KEY_FOR_TLS ) );

    /* This function will generate a new EC private and public key pair. You can
     * use " $openssl ec -inform der -in corePKCS11_Key.dat -text " to see
     * the structure of the keys that were generated.
     */
    xResult = pxFunctionList->C_GenerateKeyPair( hSession,
                                                 &xMechanism,
                                                 xPublicKeyTemplate,
                                                 sizeof( xPublicKeyTemplate ) / sizeof( CK_ATTRIBUTE ),
                                                 xPrivateKeyTemplate,
                                                 sizeof( xPrivateKeyTemplate ) / sizeof( CK_ATTRIBUTE ),
                                                 &xPublicKeyHandle,
                                                 &xPrivateKeyHandle );
    configASSERT( xResult == CKR_OK );
    configPRINTF( ( "corePKCS11_Key.dat has been created in the Visual Studio" \
                    " Solution directory\r\n" ) );
    configPRINTF( ( "Extracting public key bytes...\r\n" ) );

    /* Export public key as hex bytes and print the hex representation of the
     * public key. */
    vExportPublicKey( hSession,
                      xPublicKeyHandle,
                      &pxDerPublicKey,
                      &ulDerPublicKeyLength );
    vWriteHexBytesToConsole( "Public Key in Hex Format",
                             pxDerPublicKey,
                             ulDerPublicKeyLength );
    configPRINTF( ( "---------Finished Generating Objects---------" ) );
    vEnd( hSession, pxSlotId );
}
