/**
 * @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 <tee_internal_api.h>

#include <persistent_object.h>
#include <storage.h>
#include "serialise_attr.h"

#ifndef PLATFORM_LOG_TAG
#define PLATFORM_LOG_TAG  "LIBGPAPI_PERSISTENT_OBJECT"
#endif

#include <tees_log.h>
#include <tees_secure_object.h>

#define CLOSE_CORRUPT_OBJECT(ret, obj)          \
  do {                                          \
    if (TEE_ERROR_CORRUPT_OBJECT == ret) {      \
      TEE_CloseAndDeletePersistentObject1(obj); \
    }                                           \
  } while (0)

/*
 * These buffers are necessary for wrapping/unwrapping and reading of
 * persistent objects. We cannot allocate them on stack or heap because
 * in TAs we have limited stack and heap, so reducing both significantly
 * inside internal API implementation may cause problems during writing
 * the application.
 */
uint8_t g_po_data_buffer[MAX_PERSISTENT_OBJECT_DATA_LEN];
uint8_t g_po_wrapped_data_buffer[MAX_PERSISTENT_OBJECT_DATA_LEN];

static TEE_Result EncryptPoData(const uint8_t *data,
                                uint32_t data_len,
                                uint8_t *encrypted_data,
                                uint32_t *encrypted_data_len) {
  SO_AccessControlInfoType ac;

  ac.access_flags = TA_ID_AC;

  return TEES_WrapSecureObject(data, data_len,
                               encrypted_data, encrypted_data_len, &ac);
}

static TEE_Result DecryptPoData(const uint8_t *encrypted_data,
                                uint32_t encrypted_data_len,
                                uint8_t *data, uint32_t *data_len) {
  return TEES_UnwrapSecureObject(encrypted_data,
                                 encrypted_data_len, data, data_len);
}

static TEE_Result ReadObjectMetadata(const void *objectID, uint32_t objectIDLen,
                                     TEE_ObjectInfo *info) {
  TEE_Result result = TEE_ERROR_GENERIC;
  uint32_t wrapped_data_len = sizeof(g_po_wrapped_data_buffer);
  uint32_t data_len = sizeof(TEE_ObjectInfo);

  result = ReadFromPO(objectID, objectIDLen,
                      g_po_wrapped_data_buffer, &wrapped_data_len,
                      MB_FS_STORE_METADATA);

  if (TEE_SUCCESS != result) {
    goto exit;
  }

  result = DecryptPoData(g_po_wrapped_data_buffer,
                         wrapped_data_len, (uint8_t *)info, &data_len);

  if (TEE_SUCCESS != result || data_len != sizeof(TEE_ObjectInfo)) {
    result = TEE_ERROR_CORRUPT_OBJECT;
    goto exit;
  }

exit:
  return result;
}

static TEE_Result ReadObjectData(const void *objectID, uint32_t objectIDLen,
                                 void *data, uint32_t *data_len) {
  TEE_Result result = TEE_ERROR_GENERIC;
  uint32_t wrapped_data_len = sizeof(g_po_wrapped_data_buffer);

  result = ReadFromPO(objectID, objectIDLen,
                      g_po_wrapped_data_buffer, &wrapped_data_len,
                      MB_FS_STORE_DATA);

  if (TEE_SUCCESS != result) {
    goto exit;
  }

  result = DecryptPoData(g_po_wrapped_data_buffer,
                         wrapped_data_len, data, data_len);

  if (TEE_SUCCESS != result) {
    result = TEE_ERROR_CORRUPT_OBJECT;
    goto exit;
  }

exit:
  return result;
}

static TEE_Result WriteObjectMetadata(const void *objectID,
                                      uint32_t objectIDLen,
                                      TEE_ObjectInfo *info) {
  TEE_Result result = TEE_ERROR_GENERIC;
  uint32_t wrapped_data_len = sizeof(g_po_wrapped_data_buffer);

  result = EncryptPoData((uint8_t *)info, sizeof(TEE_ObjectInfo),
                         g_po_wrapped_data_buffer, &wrapped_data_len);

  if (TEE_SUCCESS != result) {
    goto exit;
  }

  result = WriteToPO(objectID, objectIDLen,
                     g_po_wrapped_data_buffer, wrapped_data_len,
                     MB_FS_STORE_METADATA);

exit:
  return result;
}

static TEE_Result WriteObjectAttributes(const void *objectID,
                                        uint32_t objectIDLen,
                                        struct TransientObject *tr) {
  TEE_Result result = TEE_ERROR_BAD_PARAMETERS;
  uint32_t wrapped_data_len = sizeof(g_po_wrapped_data_buffer);

  uint32_t attr_len = 0;
  uint32_t so_attr_len = 0;

  // first - serialize atrributes
  attr_len = (uint32_t)calc_attr_size(tr);
  result = serialise_attr(tr, (char *)g_po_wrapped_data_buffer);
  if (TEE_SUCCESS != result) {
    goto exit;
  }
  // get the size of wrapped data
  EncryptPoData(g_po_wrapped_data_buffer, attr_len,
                NULL, &so_attr_len);
  if (!so_attr_len) {
    MB_LOGE("Panic reason: can't compute the size of wrapped data\n");
    TEE_Panic(0);
  }
  if (so_attr_len + attr_len > wrapped_data_len) {
    MB_LOGE("Panic reason: buffer too small to fit the attributes\n");
    TEE_Panic(0);
  }

  result = EncryptPoData(g_po_wrapped_data_buffer, attr_len,
                         g_po_wrapped_data_buffer + attr_len, &so_attr_len);
  if (TEE_SUCCESS != result) {
    goto exit;
  }

  result = WriteToPO(objectID, objectIDLen,
                     g_po_wrapped_data_buffer + attr_len, so_attr_len,
                     MB_FS_STORE_ATTRIB);
exit:
  return result;
}

static TEE_Result ReadObjectAttributes(const void *objectID,
                                       uint32_t objectIDLen,
                                       struct TransientObject *tr) {
  TEE_Result result = TEE_ERROR_GENERIC;
  uint32_t wrapped_data_len = sizeof(g_po_wrapped_data_buffer);
  uint32_t src_data_len = 0;

  result = ReadFromPO(objectID, objectIDLen,
                      g_po_wrapped_data_buffer, &wrapped_data_len,
                      MB_FS_STORE_ATTRIB);
  if (TEE_SUCCESS != result) {
    goto exit;
  }
  // to avoid computing unwrapped data size
  src_data_len = wrapped_data_len;
  if (wrapped_data_len + src_data_len > sizeof(g_po_wrapped_data_buffer)) {
    MB_LOGE("Panic reason: buffer too small to fit the attributes\n");
    TEE_Panic(0);
  }
  result = DecryptPoData(g_po_wrapped_data_buffer,
                         wrapped_data_len,
                         g_po_wrapped_data_buffer + wrapped_data_len,
                         &src_data_len);
  if (TEE_SUCCESS != result) {

    result = TEE_ERROR_CORRUPT_OBJECT;
    goto exit;
  }
  result = deserialise_attr((char *)g_po_wrapped_data_buffer + wrapped_data_len,
                            tr);

exit:
  return result;
}

static TEE_Result WriteObjectData(const void *objectID, uint32_t objectIDLen,
                                  const void *data, uint32_t data_len) {
  TEE_Result result = TEE_ERROR_GENERIC;
  uint32_t wrapped_data_len = sizeof(g_po_wrapped_data_buffer);

  result = EncryptPoData(data, data_len,
                         g_po_wrapped_data_buffer, &wrapped_data_len);

  if (TEE_SUCCESS != result) {
    goto exit;
  }

  result = WriteToPO(objectID, objectIDLen,
                     g_po_wrapped_data_buffer, wrapped_data_len,
                     MB_FS_STORE_DATA);

exit:
  return result;
}

static TEE_Result DeleteObject(const void *objectID, uint32_t objectIDLen) {
  return RemovePO(objectID, objectIDLen);
}

static void ResyncPo(struct PersistentObject *po) {
  if (TEE_SUCCESS != ReadObjectMetadata(po->id, po->id_len, &po->tr_obj.info)) {
    MB_LOGE("Panic reason: Cannot rollback Persistent object changes!\n");
    TEE_Panic(0);
  }
}

static void free_persistent_object(struct PersistentObject *po) {
  if (!po) {
    MB_LOGE("Input parameter error: Invalid pointer.\n");
    return;
  }

  if (po->tr_obj.attr.buf_len) {
    free_transient_object((TEE_ObjectHandle) & po->tr_obj);
  }

  TEE_Free(po);
}

void close_persistent_object(struct PersistentObject *po) {
  free_persistent_object(po);
}

TEE_Result TEE_OpenPersistentObject(uint32_t storageID, const void *objectID,
                                    uint32_t objectIDLen, uint32_t flags,
                                    TEE_ObjectHandle *object) {
  struct PersistentObject *po = NULL;
  TEE_Result ret;

  if (!objectID) {
    MB_LOGE("Panic reason: Bad objectID\n");
    TEE_Panic(ID_TEE_OpenPersistentObject);
  }

  if (objectIDLen > TEE_OBJECT_ID_MAX_LEN) {
    MB_LOGE("Panic reason: Object ID (%d) length bigger than max "
            "allowed(%d)\n", objectIDLen, TEE_OBJECT_ID_MAX_LEN);
    TEE_Panic(ID_TEE_OpenPersistentObject);
  }

  if (storageID != TEE_STORAGE_PRIVATE) {
    return TEE_ERROR_ITEM_NOT_FOUND;
  }

  if (object) {
    *object = TEE_HANDLE_NULL;
  } else {
    return TEE_ERROR_BAD_PARAMETERS;
  }

  /* Allocate po structure */
  po = (struct PersistentObject *)TEE_Malloc(sizeof(struct PersistentObject),
                                             HINT_FILL_WITH_ZEROS);

  if (!po) {
    ret = TEE_ERROR_OUT_OF_MEMORY;
    goto exit;
  }

  /* Try to read object metadata */
  ret = ReadObjectMetadata(objectID, objectIDLen, &po->tr_obj.info);
  if (TEE_SUCCESS != ret) {
    goto exit;
  }
  po->tr_obj.info.dataPosition = 0;
  po->tr_obj.info.handleFlags = flags | TEE_HANDLE_FLAG_PERSISTENT |
                                TEE_HANDLE_FLAG_INITIALIZED;

  /* check if object has attributes */
  if (po->tr_obj.info.objectType != TEE_TYPE_DATA) {
    /* load object attributes */
    ret = ReadObjectAttributes(objectID, objectIDLen, &po->tr_obj);
    if (TEE_SUCCESS != ret) {
      goto exit;
    }
  }
  TEE_MemMove(po->id, objectID, objectIDLen);
  po->id_len = objectIDLen;

  *object = (TEE_ObjectHandle) & po->tr_obj;
  ret = TEE_SUCCESS;

exit:

  if (TEE_SUCCESS != ret && po) {
    TEE_Free(po);
  }
  return ret;
}

TEE_Result TEE_CreatePersistentObject(uint32_t storageID, const void *objectID,
                                      uint32_t objectIDLen,
                                      uint32_t flags, TEE_ObjectHandle attr,
                                      const void *initialData,
                                      uint32_t initialDataLen,
                                      TEE_ObjectHandle *object) {
  struct TransientObject *src = NULL;
  struct PersistentObject *po = NULL;
  TEE_Result ret;

  if (storageID != TEE_STORAGE_PRIVATE) {
    return TEE_ERROR_ITEM_NOT_FOUND;
  }

  if (!objectID) {
    MB_LOGE("Panic reason: Bad objectID\n");
    TEE_Panic(ID_TEE_OpenPersistentObject);
  }

  if (objectIDLen > TEE_OBJECT_ID_MAX_LEN) {
    MB_LOGE("Panic reason: Object ID (%d) length bigger than max "
            "allowed(%d)\n", objectIDLen, TEE_OBJECT_ID_MAX_LEN);
    TEE_Panic(ID_TEE_CreatePersistentObject);
  }

  *object = TEE_HANDLE_NULL;

  /* Allocate po structure */
  po = (struct PersistentObject *)TEE_Malloc(sizeof(struct PersistentObject),
                                             HINT_FILL_WITH_ZEROS);

  if (!po) {
    return TEE_ERROR_OUT_OF_MEMORY;
  }

  /* optional attr */
  if (attr != TEE_HANDLE_NULL) {
    src = &attr->tr;
    if (!(src->info.handleFlags & TEE_HANDLE_FLAG_INITIALIZED)) {
      TEE_Panic(ID_TEE_CreatePersistentObject);
    }

    ret = allocate_transient_object(&po->tr_obj, src->info.objectType,
                                    src->info.maxKeySize);
    if (ret != TEE_SUCCESS) {
      ret = TEE_ERROR_OUT_OF_MEMORY;
      goto exit;
    }

    ret = TEE_CopyObjectAttributes1((TEE_ObjectHandle) & po->tr_obj, attr);
    if (TEE_SUCCESS != ret) {
      MB_LOGE("TEE_CopyObjectAttributes failed with error code: 0x%x\n", ret);
      goto exit;
    }
  }

  /* If the object exists and TEE_DATA_FLAG_OVERWRITE is not set, throw error */
  if (TEE_SUCCESS ==
      ReadObjectMetadata(objectID, objectIDLen,
                         &po->tr_obj.info) &&
      !(flags & TEE_DATA_FLAG_OVERWRITE)) {
    ret = TEE_ERROR_ACCESS_CONFLICT;
    goto exit;
  }

  /* fill Object Info */
  po->tr_obj.info.dataSize = initialData ? initialDataLen : 0;
  po->tr_obj.info.objectType = attr ==
                               TEE_HANDLE_NULL ? TEE_TYPE_DATA : attr->tr.info.
                               objectType;
  po->tr_obj.info.objectUsage = attr ==
                                TEE_HANDLE_NULL ? 0 : attr->tr.info.objectUsage;
  po->tr_obj.info.keySize = attr == TEE_HANDLE_NULL ? 0 : attr->tr.info.keySize;

  po->tr_obj.info.dataPosition = 0;
  po->tr_obj.info.handleFlags = flags | TEE_HANDLE_FLAG_PERSISTENT |
                                TEE_HANDLE_FLAG_INITIALIZED;

  ret = StartObjectUpdate(objectID, objectIDLen);

  if (TEE_SUCCESS != ret) {
    goto exit;
  }

  /* Here write data to object buffer */
  if (initialData) {
    po->tr_obj.info.dataPosition = 0;
    ret = WriteObjectData(objectID, objectIDLen, initialData, initialDataLen);

    if (TEE_SUCCESS != ret) {
      AbortObjectUpdate(objectID, objectIDLen);
      goto exit;
    }
  }

  ret = WriteObjectMetadata(objectID, objectIDLen, &po->tr_obj.info);

  if (TEE_SUCCESS != ret) {
    AbortObjectUpdate(objectID, objectIDLen);
    goto exit;
  }

  if (TEE_TYPE_DATA != po->tr_obj.info.objectType) {
    ret = WriteObjectAttributes(objectID, objectIDLen, &po->tr_obj);
    if (TEE_SUCCESS != ret) {
      AbortObjectUpdate(objectID, objectIDLen);
      goto exit;
    }
  }

  ret = CommitObjectUpdate(objectID, objectIDLen);

  if (TEE_SUCCESS != ret) {
    AbortObjectUpdate(objectID, objectIDLen);
    goto exit;
  }

  TEE_MemMove(po->id, objectID, objectIDLen);
  po->id_len = objectIDLen;

  *object = (TEE_ObjectHandle) & po->tr_obj;
  ret = TEE_SUCCESS;

exit:
  if (TEE_SUCCESS != ret && po) {
    TEE_Free(po);
  }

  return ret;
}

TEE_Result TEE_CloseAndDeletePersistentObject1(TEE_ObjectHandle object) {
  struct PersistentObject *po;
  TEE_Result ret;

  if (object == TEE_HANDLE_NULL) {
    return TEE_SUCCESS;
  }

  if (!(object->tr.info.handleFlags & TEE_HANDLE_FLAG_PERSISTENT)) {
    TEE_Panic(ID_TEE_CloseAndDeletePersistentObject1);
  }

  po = (struct PersistentObject *)object;

  if (!(po->tr_obj.info.handleFlags & TEE_DATA_FLAG_ACCESS_WRITE_META)) {
    TEE_Panic(ID_TEE_CloseAndDeletePersistentObject1);
  }

  /* delete object from NWd file */
  ret = DeleteObject(po->id, po->id_len);

  close_persistent_object(po);

  switch (ret) {
    case TEE_SUCCESS: {
      break;
    }
    case TEE_ERROR_STORAGE_NOT_AVAILABLE:
    case TEE_ERROR_OUT_OF_MEMORY: {
      break;
    }
    default: {
      MB_LOGE("Panic reason: msg_delete_object return %x\n", ret);
      TEE_Panic(ID_TEE_CloseAndDeletePersistentObject1);
    }
  }

  return ret;
}

/* Deprecated in GP Internal Core API v1.1 */
void TEE_CloseAndDeletePersistentObject(TEE_ObjectHandle object) {
  TEE_Result ret = TEE_CloseAndDeletePersistentObject1(object);

  switch (ret) {
    case TEE_SUCCESS:
    case TEE_ERROR_OUT_OF_MEMORY: {
      return;
    }
    default: {
      MB_LOGE("Panic reason: TEE_CloseAndDeletePersistentObject1 "
              "return %x\n", ret);
      TEE_Panic(ID_TEE_CloseAndDeletePersistentObject);
    }
  }
}


TEE_Result TEE_RenamePersistentObject(TEE_ObjectHandle object,
                                      const void *newObjectID,
                                      uint32_t newObjectIDLen) {
  struct PersistentObject *po;
  TEE_Result ret;

  if (object == TEE_HANDLE_NULL) {
    MB_LOGE("Panic reason: Object handle is NULL\n");
    TEE_Panic(ID_TEE_RenamePersistentObject);
  }

  if (!(object->tr.info.handleFlags & TEE_HANDLE_FLAG_PERSISTENT)) {
    MB_LOGE("Panic Reason: object isn't persistent\n");
    TEE_Panic(ID_TEE_RenamePersistentObject);
  }

  po = (struct PersistentObject *)object;
  if (!(po->tr_obj.info.handleFlags & TEE_DATA_FLAG_ACCESS_WRITE_META)) {
    MB_LOGE("Panic Reason: object does not have access to write meta\n");
    TEE_Panic(ID_TEE_RenamePersistentObject);
  }

  if (newObjectIDLen > TEE_OBJECT_ID_MAX_LEN) {
    MB_LOGE("Panic reason: objectIDLen bigger than max allowed\n");
    TEE_Panic(ID_TEE_RenamePersistentObject);
  }

  ret = StartObjectUpdate(newObjectID, newObjectIDLen);

  if (TEE_SUCCESS != ret) {
    goto exit;
  }

  ret = WriteObjectMetadata(newObjectID, newObjectIDLen, &po->tr_obj.info);

  if (TEE_SUCCESS != ret) {
    AbortObjectUpdate(newObjectID, newObjectIDLen);
    goto exit;
  }

  if (po->tr_obj.info.objectType != TEE_TYPE_DATA) {
    ret = WriteObjectAttributes(newObjectID, newObjectIDLen, &po->tr_obj);
    if (TEE_SUCCESS != ret) {
      AbortObjectUpdate(newObjectID, newObjectIDLen);
      goto exit;
    }
  }

  po->tr_obj.info.dataSize = sizeof(g_po_data_buffer);
  ret = ReadObjectData(po->id, po->id_len,
                       g_po_data_buffer, &po->tr_obj.info.dataSize);

  if (TEE_SUCCESS != ret) {
    AbortObjectUpdate(newObjectID, newObjectIDLen);
    goto exit;
  }

  ret = WriteObjectData(newObjectID, newObjectIDLen,
                        g_po_data_buffer, po->tr_obj.info.dataSize);

  if (TEE_SUCCESS != ret) {
    AbortObjectUpdate(newObjectID, newObjectIDLen);
    goto exit;
  }

  ret = CommitObjectUpdate(newObjectID, newObjectIDLen);

  if (TEE_SUCCESS != ret) {
    AbortObjectUpdate(newObjectID, newObjectIDLen);
    goto exit;
  }

  if (TEE_SUCCESS != DeleteObject(po->id, po->id_len)) {
    MB_LOGE("Panic reason: Failed to remove old object!\n");
    TEE_Panic(ID_TEE_RenamePersistentObject);
  }

  TEE_MemMove(po->id, newObjectID, newObjectIDLen);
  po->id_len = newObjectIDLen;
  ret = TEE_SUCCESS;

exit:
  CLOSE_CORRUPT_OBJECT(ret, object);
  return ret;
}

TEE_Result TEE_ReadObjectData(TEE_ObjectHandle object, void *buffer,
                              uint32_t size, uint32_t *count) {
  struct PersistentObject *po = NULL;
  uint32_t bytes_to_read = 0;
  TEE_Result ret = TEE_ERROR_BAD_PARAMETERS;

  if (!buffer) {
    goto exit;
  }

  if (object == TEE_HANDLE_NULL) {
    TEE_Panic(ID_TEE_ReadObjectData);
  }

  if (count == TEE_HANDLE_NULL) {
    TEE_Panic(ID_TEE_ReadObjectData);
  }

  if (!(object->tr.info.handleFlags & TEE_HANDLE_FLAG_PERSISTENT)) {
    TEE_Panic(ID_TEE_ReadObjectData);
  }

  po = (struct PersistentObject *)object;
  if (!(po->tr_obj.info.handleFlags & TEE_DATA_FLAG_ACCESS_READ)) {
    TEE_Panic(ID_TEE_ReadObjectData);
  }

  if (size == 0) {
    *count = 0;
    return TEE_SUCCESS;
  }

  /* read object data via position & size */
  if (po->tr_obj.info.dataSize < po->tr_obj.info.dataPosition) {
    TEE_Panic(ID_TEE_ReadObjectData);
  }

  bytes_to_read = (po->tr_obj.info.dataSize - po->tr_obj.info.dataPosition);

  if (size < bytes_to_read) {
    bytes_to_read = size;
  }

  po->tr_obj.info.dataSize = sizeof(g_po_data_buffer);
  ret = ReadObjectData(po->id, po->id_len,
                       g_po_data_buffer, &po->tr_obj.info.dataSize);

  if (TEE_SUCCESS != ret) {
    goto exit;
  }

  TEE_MemMove(buffer,
              g_po_data_buffer + po->tr_obj.info.dataPosition, bytes_to_read);
  po->tr_obj.info.dataPosition += bytes_to_read;

  *count = bytes_to_read;
  ret = TEE_SUCCESS;

exit:
  CLOSE_CORRUPT_OBJECT(ret, object);
  return ret;
}

static TEE_Result WriteObjectDataInternal(TEE_ObjectHandle object,
                                          const void *buffer, uint32_t size) {
  struct PersistentObject *po = NULL;
  TEE_Result ret;

  if (object == TEE_HANDLE_NULL) {
    TEE_Panic(0);
  }

  if (buffer == TEE_HANDLE_NULL) {
    TEE_Panic(0);
  }

  if (!(object->tr.info.handleFlags & TEE_HANDLE_FLAG_PERSISTENT)) {
    TEE_Panic(0);
  }

  po = (struct PersistentObject *)object;
  if (!(po->tr_obj.info.handleFlags & TEE_DATA_FLAG_ACCESS_WRITE)) {
    TEE_Panic(0);
  }

  if (size == 0) {
    return TEE_SUCCESS;
  }

  /* check overflow */
  if (size >= MAX_PERSISTENT_OBJECT_DATA_LEN - po->tr_obj.info.dataPosition) {
    return TEE_ERROR_OVERFLOW;
  }

  po->tr_obj.info.dataSize = sizeof(g_po_data_buffer);
  ret = ReadObjectData(po->id, po->id_len,
                       g_po_data_buffer, &po->tr_obj.info.dataSize);

  if (TEE_SUCCESS != ret) {
    goto exit;
  }

  TEE_MemMove(g_po_data_buffer + po->tr_obj.info.dataPosition, buffer, size);
  po->tr_obj.info.dataPosition += size;

  /* Update data size if increased */
  if (po->tr_obj.info.dataPosition > po->tr_obj.info.dataSize) {
    po->tr_obj.info.dataSize = po->tr_obj.info.dataPosition;
  }

  ret = StartObjectUpdate(po->id, po->id_len);

  if (TEE_SUCCESS != ret) {
    goto exit;
  }

  ret = WriteObjectData(po->id, po->id_len,
                        g_po_data_buffer, po->tr_obj.info.dataSize);

  if (TEE_SUCCESS != ret) {
    AbortObjectUpdate(po->id, po->id_len);
    goto exit;
  }

  ret = WriteObjectMetadata(po->id, po->id_len, &po->tr_obj.info);
  if (TEE_SUCCESS != ret) {
    AbortObjectUpdate(po->id, po->id_len);
    goto exit;
  }

  if (TEE_TYPE_DATA != po->tr_obj.info.objectType) {
    ret = WriteObjectAttributes(po->id, po->id_len, &po->tr_obj);
    if (TEE_SUCCESS != ret) {
      AbortObjectUpdate(po->id, po->id_len);
      goto exit;
    }
  }
  ret = CommitObjectUpdate(po->id, po->id_len);

  if (TEE_SUCCESS != ret) {
    AbortObjectUpdate(po->id, po->id_len);
  }

exit:
  if (TEE_SUCCESS != ret) {
    ResyncPo(po);
  }
  CLOSE_CORRUPT_OBJECT(ret, object);
  return ret;
}

TEE_Result TEE_WriteObjectData(TEE_ObjectHandle object,
                               const void *buffer, uint32_t size) {
  return WriteObjectDataInternal(object, buffer, size);
}

TEE_Result TEE_TruncateObjectData(TEE_ObjectHandle object, uint32_t size) {
  struct PersistentObject *po;
  TEE_Result ret;

  if (object == TEE_HANDLE_NULL) {
    TEE_Panic(ID_TEE_TruncateObjectData);
  }

  if (!(object->tr.info.handleFlags & TEE_HANDLE_FLAG_PERSISTENT)) {
    TEE_Panic(ID_TEE_TruncateObjectData);
  }

  po = (struct PersistentObject *)object;
  if (!(po->tr_obj.info.handleFlags & TEE_DATA_FLAG_ACCESS_WRITE)) {
    TEE_Panic(ID_TEE_TruncateObjectData);
  }

  if (size == po->tr_obj.info.dataSize) {
    return TEE_SUCCESS;
  }

  po->tr_obj.info.dataSize = sizeof(g_po_data_buffer);
  ret = ReadObjectData(po->id, po->id_len,
                       g_po_data_buffer, &po->tr_obj.info.dataSize);

  if (TEE_SUCCESS != ret) {
    goto exit;
  }

  if (size > po->tr_obj.info.dataSize) {/* add 0s */
    TEE_MemFill(g_po_data_buffer + po->tr_obj.info.dataSize,
                0x00, size - po->tr_obj.info.dataSize);
  }
  /* Update object data length */
  po->tr_obj.info.dataSize = size;

  ret = StartObjectUpdate(po->id, po->id_len);

  if (TEE_SUCCESS != ret) {
    goto exit;
  }
  /* Write Object */
  ret = WriteObjectData(po->id, po->id_len,
                        g_po_data_buffer, po->tr_obj.info.dataSize);

  if (TEE_SUCCESS != ret) {
    AbortObjectUpdate(po->id, po->id_len);
    goto exit;
  }

  ret = WriteObjectMetadata(po->id, po->id_len, &po->tr_obj.info);

  if (TEE_SUCCESS != ret) {
    AbortObjectUpdate(po->id, po->id_len);
    goto exit;
  }

  if (TEE_TYPE_DATA != po->tr_obj.info.objectType) {
    ret = WriteObjectAttributes(po->id, po->id_len, &po->tr_obj);
    if (TEE_SUCCESS != ret) {
      AbortObjectUpdate(po->id, po->id_len);
      goto exit;
    }
  }

  ret = CommitObjectUpdate(po->id, po->id_len);

  if (TEE_SUCCESS != ret) {
    AbortObjectUpdate(po->id, po->id_len);
  }

exit:
  if (TEE_SUCCESS != ret) {
    ResyncPo(po);
  }
  CLOSE_CORRUPT_OBJECT(ret, object);
  return ret;
}

TEE_Result TEE_SeekObjectData(TEE_ObjectHandle object,
                              int32_t offset, TEE_Whence whence) {
  struct PersistentObject *po;
  int32_t new_pos = 0;

  if (object == TEE_HANDLE_NULL) {
    TEE_Panic(ID_TEE_SeekObjectData);
  }

  if (!(object->tr.info.handleFlags & TEE_HANDLE_FLAG_PERSISTENT)) {
    TEE_Panic(ID_TEE_SeekObjectData);
  }

  po = (struct PersistentObject *)object;

  switch (whence) {
    case TEE_DATA_SEEK_SET: {
      if (offset > 0 && (uint32_t)offset >= MAX_PERSISTENT_OBJECT_DATA_LEN) {
        return TEE_ERROR_OVERFLOW;
      }
      new_pos = offset < 0 ? 0 : offset;
      break;
    }
    case TEE_DATA_SEEK_CUR: {
      if (offset > 0 &&
          (uint32_t)offset >=
          MAX_PERSISTENT_OBJECT_DATA_LEN - po->tr_obj.info.dataPosition) {
        return TEE_ERROR_OVERFLOW;
      }
      if (offset < 0 && (int32_t)po->tr_obj.info.dataPosition < -offset) {
        new_pos = 0;
      } else {
        new_pos = po->tr_obj.info.dataPosition + offset;
      }
      break;
    }
    case TEE_DATA_SEEK_END: {
      if (offset > 0 &&
          (uint32_t)offset >= MAX_PERSISTENT_OBJECT_DATA_LEN - po->tr_obj.info.dataSize) {
        return TEE_ERROR_OVERFLOW;
      }
      if (offset < 0 && (int32_t)po->tr_obj.info.dataSize < offset) {
        new_pos = 0;
      } else {
        new_pos = po->tr_obj.info.dataSize + offset;
      }
      break;
    }
    default: {
      MB_LOGE("Panic reason: TEE_SeekObjectData whence = %u\n", whence);
      TEE_Panic(ID_TEE_SeekObjectData);
    }
  }

  po->tr_obj.info.dataPosition = new_pos;

  return TEE_SUCCESS;
}

TEE_Result TEE_GetObjectInfo1(TEE_ObjectHandle object,
                              TEE_ObjectInfo *objectInfo) {
  TEE_Result ret = TEE_SUCCESS;
  struct PersistentObject *po = (struct PersistentObject *)object;
  uint32_t object_data_position = object->tr.info.dataPosition;

  if (object->tr.info.handleFlags & TEE_HANDLE_FLAG_PERSISTENT) {
    /* Try to read object metadata */
    ret = ReadObjectMetadata(po->id, po->id_len, &po->tr_obj.info);

    if (TEE_SUCCESS != ret) {
      CLOSE_CORRUPT_OBJECT(ret, object);
      return ret;
    }
    po->tr_obj.info.dataPosition = object_data_position;
  }

  objectInfo->objectType = object->tr.info.objectType;
  objectInfo->keySize = object->tr.info.keySize;
  objectInfo->maxKeySize = object->tr.info.maxKeySize;
  objectInfo->objectUsage = object->tr.info.objectUsage;
  objectInfo->dataSize = object->tr.info.dataSize;
  objectInfo->dataPosition = object->tr.info.dataPosition;
  objectInfo->handleFlags = object->tr.info.handleFlags;

  return TEE_SUCCESS;
}

TEE_Result TEE_AllocatePersistentObjectEnumerator(
  TEE_ObjectEnumHandle *objectEnumerator) {
  struct __TEE_ObjectEnumHandle *obj;

  obj = TEE_Malloc(sizeof(struct __TEE_ObjectEnumHandle), HINT_FILL_WITH_ZEROS);

  if (!obj) {
    return TEE_ERROR_OUT_OF_MEMORY;
  }

  TEE_MemFill(obj, 0, sizeof(struct __TEE_ObjectEnumHandle));

  *objectEnumerator = obj;

  return TEE_SUCCESS;
}

void TEE_FreePersistentObjectEnumerator(TEE_ObjectEnumHandle objectEnumerator) {
  struct __TEE_ObjectEnumHandle *obj = objectEnumerator;

  if (objectEnumerator == TEE_HANDLE_NULL) {
    return;
  }

  if (obj->data_buffer) {
    TEE_Free(obj->data_buffer);
    obj->data_buffer = NULL;
  }

  TEE_Free(objectEnumerator);
}

void TEE_ResetPersistentObjectEnumerator(TEE_ObjectEnumHandle objectEnumerator)
{
  struct __TEE_ObjectEnumHandle *obj = objectEnumerator;

  if (objectEnumerator == TEE_HANDLE_NULL) {
    return;
  }

  if (obj->data_buffer) {
    TEE_Free(obj->data_buffer);
    obj->data_buffer = NULL;
  }

  obj->current_ptr = obj->data_buffer;
  obj->data_len = 0;
}

TEE_Result TEE_StartPersistentObjectEnumerator(
  TEE_ObjectEnumHandle objectEnumerator,
  uint32_t storageID) {
  TEE_Result ret = TEE_ERROR_GENERIC;
  struct __TEE_ObjectEnumHandle *obj = objectEnumerator;
  uint32_t blob_len = sizeof(g_po_data_buffer);
  uint32_t po_count = 0;

  if (storageID != TEE_STORAGE_PRIVATE) {
    return TEE_ERROR_ITEM_NOT_FOUND;
  }

  if (objectEnumerator == TEE_HANDLE_NULL) {
    TEE_Panic(ID_TEE_StartPersistentObjectEnumerator);
  }

  /* Obtaining persistent objects list blob */
  ret = ListObjects(g_po_data_buffer, &blob_len);

  if (TEE_SUCCESS != ret) {
    goto exit;
  }

  if (blob_len < sizeof(uint32_t)) {
    TEE_Panic(ID_TEE_StartPersistentObjectEnumerator);
  }

  obj->data_len = (uint32_t)(blob_len - sizeof(uint32_t));
  obj->data_buffer = TEE_Malloc(obj->data_len, HINT_FILL_WITH_ZEROS);
  if (!obj->data_buffer) {
    ret = TEE_ERROR_OUT_OF_MEMORY;
    goto exit;
  }

  TEE_MemMove(&po_count, g_po_data_buffer, sizeof(uint32_t));
  obj->po_count = po_count;
  TEE_MemMove(obj->data_buffer,
              g_po_data_buffer + sizeof(uint32_t), obj->data_len);

  /* Set current pointer to NULL to mark -1st element */
  obj->current_ptr = NULL;
  ret = TEE_SUCCESS;

exit:
  return ret;
}

TEE_Result TEE_GetNextPersistentObject(TEE_ObjectEnumHandle objectEnumerator,
                                       TEE_ObjectInfo *objectInfo,
                                       void *objectID, uint32_t *objectIDLen) {
  TEE_Result ret = TEE_SUCCESS;
  TEE_ObjectInfo tmp_obj_info;
  uint32_t po_id_len = 0;
  uint8_t po_id[TEE_OBJECT_ID_MAX_LEN] = { 0 };
  struct __TEE_ObjectEnumHandle *obj = objectEnumerator;

  if (objectEnumerator == TEE_HANDLE_NULL) {
    TEE_Panic(ID_TEE_GetNextPersistentObject);
  }

  if (objectID == TEE_HANDLE_NULL || objectIDLen == TEE_HANDLE_NULL) {
    TEE_Panic(ID_TEE_GetNextPersistentObject);
  }

  if (!obj->data_buffer) {
    TEE_Panic(ID_TEE_GetNextPersistentObject);
  }

  if (!obj->current_ptr) { /* -1st element set, go to 0*/
    obj->current_ptr = obj->data_buffer;
  } else {
    /* Calculate offset and go to next element */
    TEE_MemMove(&po_id_len, obj->current_ptr, sizeof(uint32_t));
    if (po_id_len > TEE_OBJECT_ID_MAX_LEN) {
      TEE_Panic(ID_TEE_GetNextPersistentObject);
    }
    obj->current_ptr += (po_id_len + sizeof(po_id_len));
  }

  /* Handle last element */
  if (obj->current_ptr == obj->data_buffer + obj->data_len) {
    ret = TEE_ERROR_ITEM_NOT_FOUND;
    goto exit;
  }

  TEE_MemMove(&po_id_len, obj->current_ptr, sizeof(uint32_t));

  if (po_id_len > TEE_OBJECT_ID_MAX_LEN) {
    TEE_Panic(ID_TEE_GetNextPersistentObject);
  }

  TEE_MemMove(po_id, obj->current_ptr + sizeof(uint32_t), po_id_len);

  ret = ReadObjectMetadata(po_id, po_id_len, &tmp_obj_info);

  if (objectInfo) {
    if (TEE_ERROR_CORRUPT_OBJECT == ret) {
      TEE_MemFill(objectInfo, 0, sizeof(TEE_ObjectInfo));
    } else if (TEE_SUCCESS == ret) {
      TEE_MemMove(objectInfo, &tmp_obj_info, sizeof(TEE_ObjectInfo));
    }
  }

  *objectIDLen = po_id_len;
  TEE_MemMove(objectID, po_id, po_id_len);

  ret = TEE_SUCCESS;

exit:
  return ret;
}
TEE_Result TEE_RestrictObjectUsage1(TEE_ObjectHandle object,
                                    uint32_t objectUsage) {
  TEE_Result ret = TEE_SUCCESS;
  struct PersistentObject *po = (struct PersistentObject *)object;

  if (object->tr.info.handleFlags & TEE_HANDLE_FLAG_PERSISTENT) {
    TEE_ObjectInfo info = { 0 };
    info = po->tr_obj.info;
    info.objectUsage &= objectUsage;


    po->tr_obj.info.dataSize = sizeof(g_po_data_buffer);
    ret = ReadObjectData(po->id, po->id_len,
                         g_po_data_buffer, &po->tr_obj.info.dataSize);
    if (TEE_SUCCESS != ret) {
      po->tr_obj.info.dataSize = 0;
    }

    ret = StartObjectUpdate(po->id, po->id_len);
    if (TEE_SUCCESS != ret) {
      goto exit;
    }
    if (po->tr_obj.info.dataSize) {
      ret = WriteObjectData(po->id, po->id_len,
                            g_po_data_buffer, po->tr_obj.info.dataSize);
      if (TEE_SUCCESS != ret) {
        AbortObjectUpdate(po->id, po->id_len);
        goto exit;
      }
    }
    ret = WriteObjectMetadata(po->id, po->id_len, &info);
    if (TEE_SUCCESS != ret) {
      AbortObjectUpdate(po->id, po->id_len);
      goto exit;
    }

    if (TEE_TYPE_DATA != po->tr_obj.info.objectType) {
      ret = WriteObjectAttributes(po->id, po->id_len, &po->tr_obj);
      if (TEE_SUCCESS != ret) {
        AbortObjectUpdate(po->id, po->id_len);
        goto exit;
      }
    }
    ret = CommitObjectUpdate(po->id, po->id_len);
    if (TEE_SUCCESS != ret) {
      AbortObjectUpdate(po->id, po->id_len);
      goto exit;
    }
  }

  object->tr.info.objectUsage &= objectUsage;
exit:
  CLOSE_CORRUPT_OBJECT(ret, object);
  return ret;
}
