/* Software-based Mobile Trusted Module (MTM) Emulator
 * Copyright (C) 2004-2010 Mario Strasser <mast@gmx.net>
 *
 * This module is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation; either version 2 of the License,
 * or (at your option) any later version.
 *
 * This module is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * $Id$
 */

#include "mtm_structures.h"
#include "mtm_marshalling.h"
#include "mtm_data.h"
#include "tpm/tpm_data.h"
#include "tpm/tpm_commands.h"

static inline TPM_RESULT return_UINT32(UINT32 *respSize, BYTE **resp, UINT32 value)
{
  UINT32 len = *respSize = 4;
  BYTE *ptr = *resp = tpm_malloc(*respSize);
  if (ptr == NULL || tpm_marshal_UINT32(&ptr, &len, value)) {
    tpm_free(*resp);
    return TPM_FAIL;
  }
  return TPM_SUCCESS;
}

static inline TPM_RESULT return_BOOL(UINT32 *respSize, BYTE **resp, BOOL value)
{
  UINT32 len = *respSize = 1;
  BYTE *ptr = *resp = tpm_malloc(*respSize);
  if (ptr == NULL || tpm_marshal_BOOL(&ptr, &len, value)) {
    tpm_free(*resp);
    return TPM_FAIL;
  }
  return TPM_SUCCESS;
}

#define return_BYTE return_BOOL

static TPM_RESULT cap_ord(UINT32 subCapSize, BYTE *subCap,
                          UINT32 *respSize, BYTE **resp)
{
  TPM_COMMAND_CODE ord;
  if (tpm_unmarshal_TPM_COMMAND_CODE(&subCap, &subCapSize, &ord))
    return TPM_BAD_MODE;
  switch (ord) {
    case MTM_ORD_InstallRIM:
    case MTM_ORD_LoadVerificationKey:
    case MTM_ORD_LoadVerificationRootKeyDisable:
    case MTM_ORD_VerifyRIMCert:
    case MTM_ORD_VerifyRIMCertAndExtend:
    case MTM_ORD_IncrementBootstrapCounter:
    case MTM_ORD_SetVerifiedPCRSelection:
      return return_BOOL(respSize, resp, TRUE);
    default:
      return return_BOOL(respSize, resp, FALSE);
  }
}

static TPM_RESULT cap_mtm_permanent_data(UINT32 subCapSize, BYTE *subCap,
                                         UINT32 *respSize, BYTE **resp)
{
  UINT32 subCapVal, len;
  BYTE* ptr;

/* unmarshal subCap */
  if (tpm_unmarshal_UINT32(&subCap, &subCapSize, &subCapVal) != 0)
    return TPM_BAD_PARAMETER;
  switch (subCapVal) {

    case 1:
      return TPM_FAIL;
      break;

    case 2:
      *respSize = len = sizeof_TPM_PCR_SELECTION(mtmData.permanent.data.verifiedPCRs);
      *resp = ptr = tpm_malloc(*respSize);
      if (*resp == NULL 
          || tpm_marshal_TPM_PCR_SELECTION(&ptr, &len, &mtmData.permanent.data.verifiedPCRs)) {
        tpm_free(*resp);
        return TPM_FAIL;
      }

    case 3:
      return return_UINT32(respSize, resp,
        tpmData.permanent.data.counters[MTM_COUNTER_SELECT_BOOTSTRAP].counter);

    case 4:
      return return_UINT32(respSize, resp, mtmData.permanent.data.counterRimProtectId);

    case 5:
      return return_UINT32(respSize, resp, mtmData.permanent.data.counterStorageProtectId);

    case 6:
      return return_BYTE(respSize, resp, mtmData.permanent.data.specMajor);

    case 7:
      return return_BYTE(respSize, resp, mtmData.permanent.data.specMinor);

    case 8:
      return return_BYTE(respSize, resp, mtmData.permanent.data.loadVerificationKeyMethods);

    default:
      return TPM_BAD_PARAMETER;

  }
  return TPM_SUCCESS;
}

TPM_RESULT MTM_GetCapability(TPM_CAPABILITY_AREA capArea, UINT32 subCapSize,
                             BYTE *subCap, UINT32 *respSize, BYTE **resp)
{
  info("MTM_GetCapability()");
  switch (capArea) {

    case TPM_CAP_ORD:
      debug("[MTM_CAP_ORD]");
      TPM_RESULT res = cap_ord(subCapSize, subCap, respSize, resp);
      if (res == TPM_SUCCESS && resp[0] == FALSE) {
        res = TPM_GetCapability(capArea, subCapSize, subCap, respSize, resp);
      }
      return res;

    case TPM_CAP_MTM_PERMANENT_DATA:
      debug("[TPM_CAP_MTM_PERMANENT_DATA]");
      return cap_mtm_permanent_data(subCapSize, subCap, respSize, resp);

    default:
      return TPM_GetCapability(capArea, subCapSize, subCap, respSize, resp);

  }
}

