/**
 * @file persistent_object.c
 * @brief Multibuild's GP persistent object functionality implementation
 * @author Iaroslav Makarchuk (i.makarchuk@samsung.com)
 * @date Created Oct 3, 2016
 * @par In Samsung Ukraine R&D Center (SURC) under a contract between
 * @par LLC "Samsung Electronics Ukraine Company" (Kiev, Ukraine) and
 * @par "Samsung Elecrtronics Co", Ltd (Seoul, Republic of Korea)
 * @par Copyright: (c) Samsung Electronics Co, Ltd 2015. All rights reserved.
 *
 * This software is proprietary of Samsung Electronics.
 * No part of this software, either material or conceptual may be copied
 * or distributed, transmitted, transcribed, stored in a retrieval system
 * or translated into any human or computer language in any form by any means,
 * electronic, mechanical, manual or otherwise, or disclosed to third parties
 * without the express written permission of Samsung Electronics.
 */

#include <serialise_attr.h>
#include <openssl/bn.h>
#include <openssl/ec.h>
#include <openssl/dh.h>
#include <openssl/rsa.h>
#include <openssl/dsa.h>
#include <openssl/err.h>
#include <openssl/ecdsa.h>

#ifndef PLATFORM_LOG_TAG
#define PLATFORM_LOG_TAG  "LIBGPAPI_SERIALISE_ATTR"
#endif

#include <tees_log.h>
#include <tees_secure_object.h>

#define MB_PRINT_OSSL_ERROR()                                                 \
  do {                                                                        \
    if (0 != ERR_peek_error()) {                                              \
      MB_LOGE("Internal OPENSSL error: \n");                                  \
      MB_LOGE("   OSSL ERR_GET_LIB - %u\n", ERR_GET_LIB(ERR_peek_error()));   \
      MB_LOGE("   OSSL ERR_GET_FUNC - %u\n", ERR_GET_FUNC(ERR_peek_error())); \
      MB_LOGE("   OSSL ERR_GET_REASON - %u\n",                                \
              ERR_GET_REASON(ERR_peek_error()));                              \
    }                                                                         \
  } while (0)

extern int ec_determine_nid_by_tee_id(uint32_t ec_tee_curve_id, int *out_nid);
/*
 * Put/get 4 byte value in LE byte order.
 */
static int put_le32(void *p, uint32_t a) {
  ((unsigned char *)p)[0] = (a) & 0xff;
  ((unsigned char *)p)[1] = (a >> 8) & 0xff;
  ((unsigned char *)p)[2] = (a >> 16) & 0xff;
  ((unsigned char *)p)[3] = (a >> 24) & 0xff;
  return 4;
}

static int get_le32(const void *p, uint32_t *dest) {
  const unsigned char *pp = p;

  *dest = (uint32_t)((int)pp[0] | ((int)pp[1] << 8)
                     | ((long)pp[2] << 16) | ((long)pp[3] << 24));
  return 4;
}

/* Get 4 byte value in BE byte order */
#define CC_MPI_GET32_BE(p)  (((long)(p)[0] << 24) | ((long)(p)[1] << 16) \
                             | ((int)(p)[2] << 8) | (int)(p)[3])
#define CC_MPI_SIZE_LEN  4

static int get_bn_inc_pointer(BIGNUM **bn_out, char **mpi) {
  uint32_t len;
  BIGNUM *bn;

  len = (uint32_t)CC_MPI_GET32_BE(*mpi);

  bn = BN_mpi2bn((unsigned char *)*mpi, len + CC_MPI_SIZE_LEN, *bn_out);
  if (!bn) {
    return 0;
  }
  *bn_out = bn;
  *mpi += BN_bn2mpi(bn, NULL);

  return 1;
}

/* Transient Object -> buffer*/
TEE_Result serialise_attr(const struct TransientObject *tr, char *buf) {
  char *p;
  int i;

  p = buf;

  p += put_le32(p, (uint32_t)tr->attr.attr_number); /* number of attrs*/

  /* AttrID for each array element*/
  for (i = 0; i < tr->attr.attr_number; i++) {
    p += put_le32(p, tr->attr.attr_array[i].attributeID);
  }

  switch (tr->info.objectType) {
    /* simple objects with 1 attribute*/
    case TEE_TYPE_AES:
    case TEE_TYPE_DES3:
    case TEE_TYPE_HMAC_SHA1:
    case TEE_TYPE_HMAC_SHA224:
    case TEE_TYPE_HMAC_SHA256:
    case TEE_TYPE_HMAC_SHA384:
    case TEE_TYPE_HMAC_SHA512:
    case TEE_TYPE_GENERIC_SECRET: {
      /* Only one TEE_ATTR_SECRET_VALUE attribute is allowed for
       * this object type */
      if (tr->attr.attr_number != 1) {
        MB_LOGE("Panic Reason: Only one TEE_ATTR_SECRET_VALUE"
                " attribute is allowed\n");
        TEE_Panic(0);
      }
      if (tr->attr.attr_array[0].attributeID != TEE_ATTR_SECRET_VALUE) {
        MB_LOGE("Panic Reason: wrong attribute"
                " (needs TEE_ATTR_SECRET_VALUE)\n");
        TEE_Panic(0);
      }
      /* lengths of Attribute in bits*/
      p += put_le32(p, tr->attr.attr_array[0].content.ref.length);
      p += put_le32(p, tr->attr.buf_len); /* buf len in bytes*/
      TEE_MemMove(p, tr->attr.buffer, tr->attr.buf_len);
    }
    break;

    case TEE_TYPE_RSA_PUBLIC_KEY:
    case TEE_TYPE_RSA_KEYPAIR: {
      RSA *rsa = (RSA *)tr->attr.buffer;
      if (!rsa) {
        MB_LOGE("Panic Reason: ossl RSA handler is NULL");
        TEE_Panic(0);
      }

      const BIGNUM *bn = NULL;

      for (i = 0; i < tr->attr.attr_number; i++) {
        switch (tr->attr.attr_array[i].attributeID) {
          case TEE_ATTR_RSA_MODULUS:
          case TEE_ATTR_RSA_PUBLIC_EXPONENT:
          case TEE_ATTR_RSA_PRIVATE_EXPONENT:
          case TEE_ATTR_RSA_PRIME1:
          case TEE_ATTR_RSA_PRIME2:
          case TEE_ATTR_RSA_EXPONENT1:
          case TEE_ATTR_RSA_EXPONENT2:
          case TEE_ATTR_RSA_COEFFICIENT: {
            bn = tr->attr.attr_array[i].content.ref.buffer;
          }
          break;
          default: {
            MB_LOGE("Panic Reason: wrong attribute ID = 0x%x\n",
                    tr->attr.attr_array[i].attributeID);
            TEE_Panic(0);
          }
          break;
        }
        if (!bn) {
          MB_LOGE("Panic Reason: BN is 0\n");
          TEE_Panic(0);
        }
        p += BN_bn2mpi(bn, (unsigned char *)p);
      }
      break;
    }

    case TEE_TYPE_DSA_PUBLIC_KEY:
    case TEE_TYPE_DSA_KEYPAIR: {
      DSA *dsa = (DSA *)tr->attr.buffer;
      if (!dsa) {
        MB_LOGE("Panic Reason: ossl DSA handler is NULL");
        TEE_Panic(0);
      }

      const BIGNUM *bn = NULL;

      for (i = 0; i < tr->attr.attr_number; i++) {
        switch (tr->attr.attr_array[i].attributeID) {
          case TEE_ATTR_DSA_PRIME:
          case TEE_ATTR_DSA_SUBPRIME:
          case TEE_ATTR_DSA_BASE:
          case TEE_ATTR_DSA_PUBLIC_VALUE:
          case TEE_ATTR_DSA_PRIVATE_VALUE: {
            bn = tr->attr.attr_array[i].content.ref.buffer;
          }
          break;
          default: {
            MB_LOGE("Panic Reason: wrong attribute ID = 0x%x\n",
                    tr->attr.attr_array[i].attributeID);
            TEE_Panic(0);
          }
          break;
        }
        if (!bn) {
          MB_LOGE("Panic Reason: BN is 0\n");
          TEE_Panic(0);
        }
        p += BN_bn2mpi(bn, (unsigned char *)p);
      }
      break;
    }

#ifdef ECC_IMPLEMENTATION
    case TEE_TYPE_ECDH_KEYPAIR:
    case TEE_TYPE_ECDH_PUBLIC_KEY:
    case TEE_TYPE_ECDSA_KEYPAIR:
    case TEE_TYPE_ECDSA_PUBLIC_KEY: {
      EC_KEY *ec_key = (EC_KEY *)tr->attr.buffer;
      if (!ec_key) {
        MB_LOGE("Panic reason: key is NULL\n");
        TEE_Panic(0);
      }

      const BIGNUM *bn = NULL;

      for (i = 0; i < tr->attr.attr_number; i++) {
        switch (tr->attr.attr_array[i].attributeID) {
          case TEE_ATTR_ECC_PUBLIC_VALUE_X:
          case TEE_ATTR_ECC_PUBLIC_VALUE_Y:
          case TEE_ATTR_ECC_PRIVATE_VALUE: {
            bn = tr->attr.attr_array[i].content.ref.buffer;
          }
          break;
          case TEE_ATTR_ECC_CURVE: {
            p += put_le32(p, tr->attr.attr_array[i].content.value.a);
            continue;
          }
          default: {
            MB_LOGE("Panic Reason: wrong attribute ID = 0x%x\n",
                    tr->attr.attr_array[i].attributeID);
            TEE_Panic(0);
          }
          break;
        }
        if (!bn) {
          MB_LOGE("Panic Reason: BN is 0\n");
          TEE_Panic(0);
        }
        p += BN_bn2mpi(bn, (unsigned char *)p);
      }
      break;
    }
#endif /* ECC_IMPLEMENTATION*/

    case TEE_TYPE_DH_KEYPAIR: {
      DH *dh = (DH *)tr->attr.buffer;
      if (!dh) {
        MB_LOGE("Panic Reason: ossl DH handler is NULL");
        TEE_Panic(0);
      }

      const BIGNUM *bn = NULL;

      for (i = 0; i < tr->attr.attr_number; i++) {
        switch (tr->attr.attr_array[i].attributeID) {
          case TEE_ATTR_DH_PRIME:
          case TEE_ATTR_DH_SUBPRIME:
          case TEE_ATTR_DH_BASE:
          case TEE_ATTR_DH_PUBLIC_VALUE:
          case TEE_ATTR_DH_PRIVATE_VALUE: {
            bn = tr->attr.attr_array[i].content.ref.buffer;
          }
          break;
          case TEE_ATTR_DH_X_BITS: {
            p += put_le32(p, tr->attr.attr_array[i].content.value.a);
          }
          break;
          default: {
            MB_LOGE("Panic Reason: wrong attribute ID = 0x%x\n",
                    tr->attr.attr_array[i].attributeID);
            TEE_Panic(0);
          }
          break;
        }
        if (!bn) {
          MB_LOGE("Panic Reason: BN is 0\n");
          TEE_Panic(0);
        }
        p += BN_bn2mpi(bn, (unsigned char *)p);
      }
      break;
    }
    /* serialisation of Certificate objects is not supported.
     * They must be saved in a file (not parsed) format*/
    case TEE_TYPE_CERT_ROOT_GSL:
    case TEE_TYPE_CERT_ROOT_AP:
    case TEE_TYPE_CERT_GENERAL_AP1:
    case TEE_TYPE_CERT_ROOT_CRL: {
      MB_LOGE("Panic Reason: Serialization of Certificate objects"
              " is not supported\n");
      TEE_Panic(0);
    }
    break;

    default:
      MB_LOGE("Panic Reason: Serialization of Certificate objects"
              " is not supported for objectType = %d\n",
              tr->info.objectType);
      TEE_Panic(0);
  }

  return TEE_SUCCESS;
}

/* buffer -> Transient Object*/
TEE_Result deserialise_attr(char *buf, struct TransientObject *tr) {
  char *p;
  int i;

  TEE_MemFill(&tr->attr, 0, sizeof(struct __TEE_Attributees));
  p = buf;
  p += get_le32(p, (uint32_t *)&tr->attr.attr_number); // number of attrs

  for (i = 0; i < tr->attr.attr_number; i++) {
    p += get_le32(p, &tr->attr.attr_array[i].attributeID); // AttrID for each
                                                           // array element
  }
  switch (tr->info.objectType) {
    /* simple objects with 1 attribute*/
    case TEE_TYPE_AES:
    case TEE_TYPE_DES3:
    case TEE_TYPE_HMAC_SHA1:
    case TEE_TYPE_HMAC_SHA224:
    case TEE_TYPE_HMAC_SHA256:
    case TEE_TYPE_HMAC_SHA384:
    case TEE_TYPE_HMAC_SHA512:
    case TEE_TYPE_GENERIC_SECRET: {
      /* Only one TEE_ATTR_SECRET_VALUE attribute is allowed for this object
       * type */
      if (tr->attr.attr_number != 1) {
        MB_LOGE("Panic Reason: Only one TEE_ATTR_SECRET_VALUE"
                " attribute is allowed\n");
        TEE_Panic(0);
      }
      if (tr->attr.attr_array[0].attributeID != TEE_ATTR_SECRET_VALUE) {
        MB_LOGE("Panic Reason: wrong attribute"
                " (needs TEE_ATTR_SECRET_VALUE)\n");
        TEE_Panic(0);
      }
      p += get_le32(p, &tr->attr.attr_array[0].content.ref.length); /* lengths
                                                                     * of
                                                                     * Attribute
                                                                     * in bits*/
      p += get_le32(p, &tr->attr.buf_len);                          /* buffer
                                                                     * length in
                                                                     * bytes*/
      tr->attr.buffer = TEE_Malloc(tr->attr.buf_len, HINT_FILL_WITH_ZEROS);
      tr->attr.attr_array[0].content.ref.buffer = tr->attr.buffer;
      if (!tr->attr.buffer) {
        return TEE_ERROR_OUT_OF_MEMORY;
      }
      TEE_MemMove(tr->attr.buffer, p, tr->attr.buf_len);
    }
    break;

    case TEE_TYPE_RSA_PUBLIC_KEY:
    case TEE_TYPE_RSA_KEYPAIR: {
      RSA *rsa = RSA_new();
      BIGNUM *bn = NULL;
      if (!rsa) {
        return TEE_ERROR_OUT_OF_MEMORY;
      }

// was made by analogy - see alocate_transient_object
// todo: check it again later
#ifndef BORING_SSL
      //if (!RSA_set_method(rsa, RSA_PKCS1_SSLeay_ex())) goto rsa_err;
      if (!RSA_set_method(rsa, RSA_PKCS1_SSLeay())) {
        goto rsa_err;
      }
#endif

      for (i = 0; i < tr->attr.attr_number; i++) {
        switch (tr->attr.attr_array[i].attributeID) {
          case TEE_ATTR_RSA_MODULUS: {
            if (!get_bn_inc_pointer(&rsa->n, &p)) {
              goto rsa_err;
            }
          }
          break;
          case TEE_ATTR_RSA_PUBLIC_EXPONENT: {
            if (!get_bn_inc_pointer(&rsa->e, &p)) {
              goto rsa_err;
            }
          }
          break;
          case TEE_ATTR_RSA_PRIVATE_EXPONENT: {
            if (!get_bn_inc_pointer(&rsa->d, &p)) {
              goto rsa_err;
            }
          }
          break;
          case TEE_ATTR_RSA_PRIME1: {
            if (!get_bn_inc_pointer(&rsa->p, &p)) {
              goto rsa_err;
            }
          }
          break;
          case TEE_ATTR_RSA_PRIME2: {
            if (!get_bn_inc_pointer(&rsa->q, &p)) {
              goto rsa_err;
            }
          }
          break;
          case TEE_ATTR_RSA_EXPONENT1: {
            if (!get_bn_inc_pointer(&rsa->dmp1, &p)) {
              goto rsa_err;
            }
          }
          break;
          case TEE_ATTR_RSA_EXPONENT2: {
            if (!get_bn_inc_pointer(&rsa->dmq1, &p)) {
              goto rsa_err;
            }
          }
          break;
          case TEE_ATTR_RSA_COEFFICIENT: {
            if (!get_bn_inc_pointer(&rsa->iqmp, &p)) {
              goto rsa_err;
            }
          }
          break;
          default: {
            goto rsa_err;
          }
          break;
        }
      }

      for (i = 0; i < tr->attr.attr_number; i++) {
        switch (tr->attr.attr_array[i].attributeID) {
          case TEE_ATTR_RSA_MODULUS: {
            bn = rsa->n;
          }
          break;
          case TEE_ATTR_RSA_PUBLIC_EXPONENT: {
            bn = rsa->e;
          }
          break;
          case TEE_ATTR_RSA_PRIVATE_EXPONENT: {
            bn = rsa->d;
          }
          break;
          case TEE_ATTR_RSA_PRIME1: {
            bn = rsa->p;
          }
          break;
          case TEE_ATTR_RSA_PRIME2: {
            bn = rsa->q;
          }
          break;
          case TEE_ATTR_RSA_EXPONENT1: {
            bn = rsa->dmp1;
          }
          break;
          case TEE_ATTR_RSA_EXPONENT2: {
            bn = rsa->dmq1;
          }
          break;
          case TEE_ATTR_RSA_COEFFICIENT: {
            bn = rsa->iqmp;
          }
          break;
        }
        tr->attr.attr_array[i].content.ref.buffer = bn;
        tr->attr.attr_array[i].content.ref.length = BN_num_bytes(bn);
      }
      tr->attr.buffer = rsa;
      tr->attr.buf_len = sizeof(RSA);
      break;

rsa_err:
      MB_LOGE("Panic Reason: RSA handle error\n");
      RSA_free(rsa);
      TEE_Panic(0);
      break;
    }

#ifndef OPENSSL_NO_DSA
    case TEE_TYPE_DSA_PUBLIC_KEY:
    case TEE_TYPE_DSA_KEYPAIR: {
      DSA *dsa = (DSA *)DSA_new();
      BIGNUM *bn = NULL;
      if (!dsa) {
        return TEE_ERROR_OUT_OF_MEMORY;
      }

      for (i = 0; i < tr->attr.attr_number; i++) {
        switch (tr->attr.attr_array[i].attributeID) {
          case TEE_ATTR_DSA_PRIME: {
            if (!get_bn_inc_pointer(&dsa->p, &p)) {
              goto dsa_err;
            }
          }
          break;
          case TEE_ATTR_DSA_SUBPRIME: {
            if (!get_bn_inc_pointer(&dsa->q, &p)) {
              goto dsa_err;
            }
          }
          break;
          case TEE_ATTR_DSA_BASE: {
            if (!get_bn_inc_pointer(&dsa->g, &p)) {
              goto dsa_err;
            }
          }
          break;
          case TEE_ATTR_DSA_PUBLIC_VALUE: {
            if (!get_bn_inc_pointer(&dsa->pub_key, &p)) {
              goto dsa_err;
            }
          }
          break;
          case TEE_ATTR_DSA_PRIVATE_VALUE: {
            if (!get_bn_inc_pointer(&dsa->priv_key, &p)) {
              goto dsa_err;
            }
          }
          break;
          default: {
            goto dsa_err;
          }
          break;
        }
      }

      for (i = 0; i < tr->attr.attr_number; i++) {
        switch (tr->attr.attr_array[i].attributeID) {
          case TEE_ATTR_DSA_PRIME: {
            bn = dsa->p;
          }
          break;
          case TEE_ATTR_DSA_SUBPRIME: {
            bn = dsa->q;
          }
          break;
          case TEE_ATTR_DSA_BASE: {
            bn = dsa->g;
          }
          break;
          case TEE_ATTR_DSA_PUBLIC_VALUE: {
            bn = dsa->pub_key;
          }
          break;
          case TEE_ATTR_DSA_PRIVATE_VALUE: {
            bn = dsa->priv_key;
          }
          break;
        }
        tr->attr.attr_array[i].content.ref.buffer = bn;
        tr->attr.attr_array[i].content.ref.length = BN_num_bytes(bn);
      }
      tr->attr.buffer = dsa;
      tr->attr.buf_len = sizeof(DSA);
      break;

dsa_err:
      MB_LOGE("Panic Reason: DSA handle error\n");
      DSA_free(dsa);
      TEE_Panic(0);
      break;
    }
#endif /* OPENSSL_NO_DSA */

#ifdef ECC_IMPLEMENTATION
    case TEE_TYPE_ECDH_KEYPAIR:
    case TEE_TYPE_ECDH_PUBLIC_KEY:
    case TEE_TYPE_ECDSA_KEYPAIR:
    case TEE_TYPE_ECDSA_PUBLIC_KEY: {
      EC_GROUP *ec_group;
      EC_KEY *ec_key = EC_KEY_new();
      if (!ec_key) {
        return TEE_ERROR_OUT_OF_MEMORY;
      }

      BIGNUM *bn_Qx = NULL;
      BIGNUM *bn_Qy = NULL;
      BIGNUM *bn_d = NULL;
      BIGNUM *bn = NULL;
      uint32_t curve_id = 0;
      int nid;
      int rv;

      int curve_set = 0;
      int public_set = 0;
      int private_set = 0;

      ERR_clear_error();

      for (i = 0; i < tr->attr.attr_number; i++) {
        switch (tr->attr.attr_array[i].attributeID) {
          case TEE_ATTR_ECC_PUBLIC_VALUE_X: {
            if (!get_bn_inc_pointer(&bn_Qx, &p)) {
              goto ec_err;
            }
            public_set = 1;
          }
          break;
          case TEE_ATTR_ECC_PUBLIC_VALUE_Y: {
            if (!get_bn_inc_pointer(&bn_Qy, &p)) {
              goto ec_err;
            }
            public_set = 1;
          }
          break;
          case TEE_ATTR_ECC_PRIVATE_VALUE: {
            if (!get_bn_inc_pointer(&bn_d, &p)) {
              goto ec_err;
            }
            private_set = 1;
          }
          break;
          case TEE_ATTR_ECC_CURVE: {
            p += get_le32(p, &curve_id);
            curve_set = 1;
          }
          break;
          default:
            goto ec_err;
        }
      }

      if (!curve_set) {
        goto ec_err;
      }

      if (!ec_determine_nid_by_tee_id(curve_id, &nid)) {
        goto ec_err;
      }

      ec_group = EC_GROUP_new_by_curve_name(nid);
      if (!ec_group) {
        goto ec_err;
      }

      rv = EC_KEY_set_group(ec_key, ec_group);
      EC_GROUP_free(ec_group);

      if (!rv) {
        goto ec_err;
      }

      /* Set private key */
      if (private_set) {
        /* Check that private key is set */
        if (!bn_d) {
          goto ec_err;
        }

        if (!EC_KEY_set_private_key(ec_key, bn_d)) {
          goto ec_err;
        }
      }

      /* Set public key */
      if (public_set) {
        /* Check that public key is set */
        if (!bn_Qx || !bn_Qy) {
          goto ec_err;
        }

        if (!EC_KEY_set_public_key_affine_coordinates(ec_key, bn_Qx, bn_Qy)) {
          goto ec_err;
        }
      }

      if (!EC_KEY_check_key(ec_key)) {
        goto ec_err;
      }

      /* EC_KEY is extracted successfully */



      for (i = 0; i < tr->attr.attr_number; i++) {
        switch (tr->attr.attr_array[i].attributeID) {
          case TEE_ATTR_ECC_PUBLIC_VALUE_X: {
            bn = bn_Qx;
          }
          break;
          case TEE_ATTR_ECC_PUBLIC_VALUE_Y: {
            bn = bn_Qy;
          }
          break;
          case TEE_ATTR_ECC_PRIVATE_VALUE: {
            bn = bn_d;
          }
          break;
          case TEE_ATTR_ECC_CURVE:
            tr->attr.attr_array[i].content.value.a = curve_id;
            tr->attr.attr_array[i].content.value.b = 0;
            continue;
        }
        tr->attr.attr_array[i].content.ref.buffer = bn;
        tr->attr.attr_array[i].content.ref.length = BN_num_bytes(bn);
      }
      tr->attr.buf_len = ECDSA_size(ec_key);
      tr->attr.buffer = ec_key;
      break;

ec_err:
      EC_KEY_free(ec_key);
      if (bn_Qx) {
        BN_free(bn_Qx);
      }
      if (bn_Qy) {
        BN_free(bn_Qy);
      }
      if (bn_d) {
        BN_clear_free(bn_d);
      }
      if (ERR_R_MALLOC_FAILURE == ERR_GET_REASON(ERR_peek_error())) {
        return TEE_ERROR_OUT_OF_MEMORY;
      }
      MB_PRINT_OSSL_ERROR();
      MB_LOGE("Panic Reason: EC handle error\n");
      TEE_Panic(0);
      break;
    }
#endif /* ECC_IMPLEMENTATION*/

#ifndef OPENSSL_NO_DH
    case TEE_TYPE_DH_KEYPAIR: {
      DH *dh = (DH *)DH_new();
      BIGNUM *bn = NULL;
      if (!dh) {
        return TEE_ERROR_OUT_OF_MEMORY;
      }

      uint32_t dh_x_bits = 0;

      for (i = 0; i < tr->attr.attr_number; i++) {
        switch (tr->attr.attr_array[i].attributeID) {
          case TEE_ATTR_DH_PRIME: {
            if (!get_bn_inc_pointer(&dh->p, &p)) {
              goto dh_err;
            }
          }
          break;
          case TEE_ATTR_DH_SUBPRIME: {
            if (!get_bn_inc_pointer(&dh->q, &p)) {
              goto dh_err;
            }
          }
          break;
          case TEE_ATTR_DH_BASE: {
            if (!get_bn_inc_pointer(&dh->g, &p)) {
              goto dh_err;
            }
          }
          break;
          case TEE_ATTR_DH_PUBLIC_VALUE: {
            if (!get_bn_inc_pointer(&dh->pub_key, &p)) {
              goto dh_err;
            }
          }
          break;
          case TEE_ATTR_DH_PRIVATE_VALUE: {
            if (!get_bn_inc_pointer(&dh->priv_key, &p)) {
              goto dh_err;
            }
          }
          break;
          case TEE_ATTR_DH_X_BITS: {
            p += get_le32(p, &dh_x_bits);
          }
          break;
          default: {
            goto dh_err;
          }
          break;
        }
      }



      for (i = 0; i < tr->attr.attr_number; i++) {
        switch (tr->attr.attr_array[i].attributeID) {
          case TEE_ATTR_DH_PRIME: {
            bn = dh->p;
          }
          break;
          case TEE_ATTR_DH_SUBPRIME: {
            bn = dh->q;
          }
          break;
          case TEE_ATTR_DH_BASE: {
            bn = dh->g;
          }
          break;
          case TEE_ATTR_DH_PUBLIC_VALUE: {
            bn = dh->pub_key;
          }
          break;
          case TEE_ATTR_DH_PRIVATE_VALUE: {
            bn = dh->priv_key;
          }
          break;
          case TEE_ATTR_DH_X_BITS:
            tr->attr.attr_array[i].content.value.a = dh_x_bits;
            tr->attr.attr_array[i].content.value.b = 0;
            continue;
        }
        tr->attr.attr_array[i].content.ref.buffer = bn;
        tr->attr.attr_array[i].content.ref.length = BN_num_bytes(bn);
      }
      tr->attr.buffer = dh;
      tr->attr.buf_len = sizeof(DH);
      break;
dh_err:
      MB_LOGE("Panic Reason: DH handle error\n");
      DH_free(dh);
      TEE_Panic(0);
      break;
    }
#endif /* OPENSSL_NO_DH */

    /* serialisation of Certificate objects is not supported. They must be saved
     * in a file (not parsed) format*/
    case TEE_TYPE_CERT_ROOT_GSL:
    case TEE_TYPE_CERT_ROOT_AP:
    case TEE_TYPE_CERT_GENERAL_AP1:
    case TEE_TYPE_CERT_ROOT_CRL: {
      MB_LOGE("Panic Reason: Serialization of Certificate objects"
              " is not supported\n");
      TEE_Panic(0);
    }
    break;

    default:
      MB_LOGE("Panic Reason: Serialization of Certificate objects"
              " is not supported for objectType = %d\n",
              tr->info.objectType);
      TEE_Panic(0);
  }
  return TEE_SUCCESS;
}
/* serialized data
 | size | object info | attrs number | Attr ID | length in bits | buf len in
 |bytes | buf with attr |   OR
 | size | object info | attrs number | Attr ID | ... | Attr ID | RSA or DSA or
 |DH or Certificates structure with mpi limbs inplace |
 */
size_t calc_attr_size(const struct TransientObject *tr) {
  size_t size = 0;                                 /* serialised object size*/

  size += sizeof(uint32_t);                        /* number of attrs*/

  size += tr->attr.attr_number * sizeof(uint32_t); /* AttrID for each array
                                                    * element*/

  switch (tr->info.objectType) {
    /* simple objects with 1 attribute*/
    case TEE_TYPE_AES:
    case TEE_TYPE_DES3:
    case TEE_TYPE_HMAC_SHA1:
    case TEE_TYPE_HMAC_SHA224:
    case TEE_TYPE_HMAC_SHA256:
    case TEE_TYPE_HMAC_SHA384:
    case TEE_TYPE_HMAC_SHA512:
    case TEE_TYPE_GENERIC_SECRET: {
      /* Only one TEE_ATTR_SECRET_VALUE attribute is allowed for this object
       * type */
      if (tr->attr.attr_number != 1) {
        MB_LOGE("Panic Reason: wrong number of attributes."
                " Need only one TEE_ATTR_SECRET_VALUE attribute\n");
        TEE_Panic(0);
      }
      if (tr->attr.attr_array[0].attributeID != TEE_ATTR_SECRET_VALUE) {
        MB_LOGE("Panic Reason: wrong attribute"
                " (needs TEE_ATTR_SECRET_VALUE)\n");
        TEE_Panic(0);
      }

      size += sizeof(uint32_t);       /* lengths of Attribute in bits*/
      size += sizeof(uint32_t);       /* buf len in bytes*/
      size += tr->attr.buf_len;       /* attributtes buffer length in bytes*/
    }
    break;

    case TEE_TYPE_RSA_PUBLIC_KEY:
    case TEE_TYPE_RSA_KEYPAIR: {
      RSA *rsa = (RSA *)tr->attr.buffer;
      if (!rsa) {
        MB_LOGE("Panic Reason: ossl RSA handler is NULL");
        TEE_Panic(0);
      }

      const BIGNUM *bn = NULL;
      int i;

      for (i = 0; i < tr->attr.attr_number; i++) {
        switch (tr->attr.attr_array[i].attributeID) {
          case TEE_ATTR_RSA_MODULUS:
          case TEE_ATTR_RSA_PUBLIC_EXPONENT:
          case TEE_ATTR_RSA_PRIVATE_EXPONENT:
          case TEE_ATTR_RSA_PRIME1:
          case TEE_ATTR_RSA_PRIME2:
          case TEE_ATTR_RSA_EXPONENT1:
          case TEE_ATTR_RSA_EXPONENT2:
          case TEE_ATTR_RSA_COEFFICIENT: {
            bn = tr->attr.attr_array[i].content.ref.buffer;
          }
          break;
          default: {
            MB_LOGE("Panic Reason: wrong attribute ID = 0x%x\n",
                    tr->attr.attr_array[i].attributeID);
            TEE_Panic(0);
          }
          break;
        }
        if (!bn) {
          MB_LOGE("Panic Reason: BN is 0\n");
          TEE_Panic(0);
        }
        size += BN_bn2mpi(bn, NULL);
      }
      break;
    }

    case TEE_TYPE_DSA_PUBLIC_KEY:
    case TEE_TYPE_DSA_KEYPAIR: {
      DSA *dsa = (DSA *)tr->attr.buffer;
      if (!dsa) {
        MB_LOGE("Panic Reason: ossl DSA handler is NULL");
        TEE_Panic(0);
      }

      const BIGNUM *bn = NULL;
      int i;

      for (i = 0; i < tr->attr.attr_number; i++) {
        switch (tr->attr.attr_array[i].attributeID) {
          case TEE_ATTR_DSA_PRIME:
          case TEE_ATTR_DSA_SUBPRIME:
          case TEE_ATTR_DSA_BASE:
          case TEE_ATTR_DSA_PUBLIC_VALUE:
          case TEE_ATTR_DSA_PRIVATE_VALUE: {
            bn = tr->attr.attr_array[i].content.ref.buffer;
          }
          break;
          default: {
            MB_LOGE("Panic Reason: wrong attribute ID = 0x%x\n",
                    tr->attr.attr_array[i].attributeID);
            TEE_Panic(0);
          }
          break;
        }
        if (!bn) {
          MB_LOGE("Panic Reason: BN is 0\n");
          TEE_Panic(0);
        }
        size += BN_bn2mpi(bn, NULL);
      }
      break;
    }

#ifdef ECC_IMPLEMENTATION
    case TEE_TYPE_ECDH_KEYPAIR:
    case TEE_TYPE_ECDH_PUBLIC_KEY:
    case TEE_TYPE_ECDSA_KEYPAIR:
    case TEE_TYPE_ECDSA_PUBLIC_KEY: {
      EC_KEY *ec_key = (EC_KEY *)tr->attr.buffer;
      if (!ec_key) {
        MB_LOGE("Panic reason: key is NULL\n");
        TEE_Panic(0);
      }

      const BIGNUM *bn = NULL;
      int i;

      for (i = 0; i < tr->attr.attr_number; i++) {
        switch (tr->attr.attr_array[i].attributeID) {
          case TEE_ATTR_ECC_PUBLIC_VALUE_X:
          case TEE_ATTR_ECC_PUBLIC_VALUE_Y:
          case TEE_ATTR_ECC_PRIVATE_VALUE: {
            bn = tr->attr.attr_array[i].content.ref.buffer;
          }
          break;
          case TEE_ATTR_ECC_CURVE: {
            size += CC_MPI_SIZE_LEN;
            continue;
          }
          default: {
            MB_LOGE("Panic Reason: wrong attribute ID = 0x%x\n",
                    tr->attr.attr_array[i].attributeID);
            TEE_Panic(0);
          }
          break;
        }
        if (!bn) {
          MB_LOGE("Panic Reason: BN is 0\n");
          TEE_Panic(0);
        }
        size += BN_bn2mpi(bn, NULL);
      }
      break;
    }
#endif /* ECC_IMPLEMENTATION*/

    case TEE_TYPE_DH_KEYPAIR: {
      DH *dh = (DH *)tr->attr.buffer;
      if (!dh) {
        MB_LOGE("Panic Reason: ossl DH handler is NULL\n");
        TEE_Panic(0);
      }

      const BIGNUM *bn = NULL;
      int i;

      for (i = 0; i < tr->attr.attr_number; i++) {
        switch (tr->attr.attr_array[i].attributeID) {
          case TEE_ATTR_DH_PRIME:
          case TEE_ATTR_DH_SUBPRIME:
          case TEE_ATTR_DH_BASE:
          case TEE_ATTR_DH_PUBLIC_VALUE:
          case TEE_ATTR_DH_PRIVATE_VALUE: {
            bn = tr->attr.attr_array[i].content.ref.buffer;
          }
          break;
          case TEE_ATTR_DH_X_BITS: {
            size += CC_MPI_SIZE_LEN;
          }
          break;
          default: {
            MB_LOGE("Panic Reason: wrong attribute ID = 0x%x\n",
                    tr->attr.attr_array[i].attributeID);
            TEE_Panic(0);
          }
          break;
        }
        if (!bn) {
          MB_LOGE("Panic Reason: BN is 0\n");
          TEE_Panic(0);
        }
        size += BN_bn2mpi(bn, NULL);
      }
      break;
    }
    /* Serialization of Certificate objects is not supported.
     * They must be saved in a file (not parsed) format*/
    case TEE_TYPE_CERT_ROOT_GSL:
    case TEE_TYPE_CERT_ROOT_AP:
    case TEE_TYPE_CERT_GENERAL_AP1:
    case TEE_TYPE_CERT_ROOT_CRL: {
      MB_LOGE("Panic Reason: Serialization of Certificate objects"
              " is not supported\n");
      TEE_Panic(0);
    }
    break;

    default:
      MB_LOGE("Panic Reason: Serialization of Certificate objects"
              " is not supported for objectType = %d\n",
              tr->info.objectType);
      TEE_Panic(0);
  }

  return size;
}
