/*******************************************************************************
 * Copyright 2017-2018, Fraunhofer SIT sponsored by Infineon Technologies AG
 * All rights reserved.
 * Copyright (c) 2019, Wind River Systems.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. Neither the name of tpm2-tss-engine nor the names of its contributors
 * may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 ******************************************************************************/
#ifndef TPM2_TSS_ENGINE_COMMON_H
#define TPM2_TSS_ENGINE_COMMON_H

#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L \
    && !defined(__STDC_NO_ATOMICS__)
# include <stdatomic.h>
# define TPM2_TSS_ENGINE_HAVE_C11_ATOMICS
typedef _Atomic int T2TE_ATOMIC_INT;
#else
typedef int T2TE_ATOMIC_INT;
#endif

#include <tpm2-tss-engine.h>
#include <tss2/tss2_mu.h>
#include <tss2/tss2_esys.h>

#include "tpm2-tss-engine-err.h"

#include <openssl/asn1t.h>
#include <openssl/asn1.h>
#include <openssl/pem.h>

extern TPM2B_DIGEST ownerauth;
extern TPM2B_DIGEST parentauth;

extern char *tcti_nameconf;

int init_ecc(ENGINE *e);
int init_rand(ENGINE *e);
int init_rsa(ENGINE *e);

TSS2_RC esys_ctx_init (ESYS_CONTEXT **esys_ctx);

TSS2_RC esys_ctx_free (ESYS_CONTEXT **esys_ctx);

TSS2_RC init_tpm_parent (   ESYS_CONTEXT **esys_ctx,
                            TPM2_HANDLE  parentHandle,
                            ESYS_TR      *parent);

TSS2_RC init_tpm_key (  ESYS_CONTEXT **esys_ctx,
                        ESYS_TR      *keyHandle,
                        TPM2_DATA    *tpm2Data);

#define ENGINE_HASH_ALG TPM2_ALG_SHA256

#define TPM2B_PUBLIC_PRIMARY_RSA_TEMPLATE { \
    .publicArea = { \
        .type = TPM2_ALG_RSA, \
        .nameAlg = ENGINE_HASH_ALG, \
        .objectAttributes = (TPMA_OBJECT_USERWITHAUTH | \
                             TPMA_OBJECT_RESTRICTED | \
                             TPMA_OBJECT_DECRYPT | \
                             TPMA_OBJECT_NODA | \
                             TPMA_OBJECT_FIXEDTPM | \
                             TPMA_OBJECT_FIXEDPARENT | \
                             TPMA_OBJECT_SENSITIVEDATAORIGIN), \
        .authPolicy = { \
             .size = 0, \
         }, \
        .parameters.rsaDetail = { \
             .symmetric = { \
                 .algorithm = TPM2_ALG_AES, \
                 .keyBits.aes = 128, \
                 .mode.aes = TPM2_ALG_CFB, \
              }, \
             .scheme = { \
                .scheme = TPM2_ALG_NULL, \
                .details = {} \
             }, \
             .keyBits = 2048, \
             .exponent = 0,\
         }, \
        .unique.rsa = { \
             .size = 0, \
         } \
     } \
}

/*
 * The parameters of this key can never be changed because they are
 * part of the interoperable 'standard' form for TSS2 PEM keys.
 * Where the parent key is ephemeral and generated on demand, it
 * has to be generated precisely the *same* every time or it cannot
 * work. The ECC primary is used for *all* keys regardless of their
 * type.
 */
#define TPM2B_PUBLIC_PRIMARY_ECC_TEMPLATE { \
    .publicArea = { \
        .type = TPM2_ALG_ECC, \
        .nameAlg = ENGINE_HASH_ALG, \
        .objectAttributes = (TPMA_OBJECT_USERWITHAUTH | \
                             TPMA_OBJECT_RESTRICTED | \
                             TPMA_OBJECT_DECRYPT | \
                             TPMA_OBJECT_NODA | \
                             TPMA_OBJECT_FIXEDTPM | \
                             TPMA_OBJECT_FIXEDPARENT | \
                             TPMA_OBJECT_SENSITIVEDATAORIGIN), \
        .authPolicy = { \
             .size = 0, \
         }, \
        .parameters.eccDetail = { \
             .symmetric = { \
                 .algorithm = TPM2_ALG_AES, \
                 .keyBits.aes = 128, \
                 .mode.aes = TPM2_ALG_CFB, \
              }, \
             .scheme = { \
                .scheme = TPM2_ALG_NULL, \
                .details = {} \
             }, \
             .curveID = TPM2_ECC_NIST_P256, \
             .kdf = { \
                .scheme = TPM2_ALG_NULL, \
                .details = {} \
             }, \
         }, \
        .unique.ecc = { \
             .x.size = 0, \
             .y.size = 0 \
         } \
     } \
}

typedef struct {
	ASN1_OBJECT *type;
	ASN1_BOOLEAN emptyAuth;
	ASN1_INTEGER *parent;
	ASN1_OCTET_STRING *pubkey;
	ASN1_OCTET_STRING *privkey;
} TSSPRIVKEY;


DECLARE_ASN1_FUNCTIONS(TSSPRIVKEY);

DECLARE_PEM_write_bio(TSSPRIVKEY, TSSPRIVKEY);
DECLARE_PEM_read_bio(TSSPRIVKEY, TSSPRIVKEY);

#define OID_loadableKey "2.23.133.10.1.3"

typedef struct {
    T2TE_ATOMIC_INT refcount;
    ESYS_CONTEXT *esys_ctx;
    ESYS_TR key_handle;
    int privatetype;
} TPM2_SIG_KEY_CTX;

typedef struct {
    TPM2_SIG_KEY_CTX *key;
    TPM2_ALG_ID hash_alg;
    ESYS_TR seq_handle;
    size_t sig_size;
} TPM2_SIG_DATA;

int
digest_update(EVP_MD_CTX *ctx, const void *data, size_t count);
int
digest_finish(TPM2_SIG_DATA *data, TPM2B_DIGEST **digest,
              TPMT_TK_HASHCHECK **validation);
int
digest_sign_init(EVP_PKEY_CTX *ctx, EVP_MD_CTX *mctx, TPM2_DATA *tpm2data,
                 size_t sig_size);
int
digest_sign_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src);
void
digest_sign_cleanup(EVP_PKEY_CTX *ctx);

#endif /* TPM2_TSS_ENGINE_COMMON_H */
