Esempio n. 1
0
static TPM_RESULT execute_TPM_MakeIdentity(TPM_REQUEST *req, TPM_RESPONSE *rsp)
{
  BYTE *ptr;
  UINT32 len;
  TPM_ENCAUTH identityAuth;
  TPM_CHOSENID_HASH labelPrivCADigest;
  TPM_KEY idKeyParams;
  TPM_KEY idKey;
  UINT32 identityBindingSize;
  BYTE *identityBinding = NULL;
  TPM_RESULT res;
  /* compute parameter digest */
  tpm_compute_in_param_digest(req);
  /* unmarshal input */
  ptr = req->param;
  len = req->paramSize;
  if (tpm_unmarshal_TPM_ENCAUTH(&ptr, &len, &identityAuth)
      || tpm_unmarshal_TPM_CHOSENID_HASH(&ptr, &len, &labelPrivCADigest)
      || tpm_unmarshal_TPM_KEY(&ptr, &len, &idKeyParams)
      || len != 0) return TPM_BAD_PARAMETER;
  /* execute command */
  res = TPM_MakeIdentity(&identityAuth, &labelPrivCADigest, &idKeyParams, 
    &req->auth1, &req->auth2, &idKey, &identityBindingSize, &identityBinding);
  if (res != TPM_SUCCESS) return res;
  /* marshal output */
  rsp->paramSize = len = sizeof_TPM_KEY(idKey) + 4 + identityBindingSize;
  rsp->param = ptr = ExtendBuf;
  if (tpm_marshal_TPM_KEY(&ptr, &len, &idKey)
      || tpm_marshal_UINT32(&ptr, &len, identityBindingSize)
      || tpm_marshal_BLOB(&ptr, &len, identityBinding, identityBindingSize)) {
    res = TPM_FAIL;
  }
  return res;
}
Esempio n. 2
0
static TPM_RESULT execute_TPM_GetPubKey(TPM_REQUEST *req, TPM_RESPONSE *rsp)
{
  BYTE *ptr;
  UINT32 len;
  TPM_KEY_HANDLE keyHandle;
  TPM_PUBKEY pubKey;
  TPM_RESULT res;
  /* compute parameter digest */
  tpm_compute_in_param_digest(req);
  /* unmarshal input */
  ptr = req->param;
  len = req->paramSize;
  if (tpm_unmarshal_TPM_KEY_HANDLE(&ptr, &len, &keyHandle)
      || len != 0) return TPM_BAD_PARAMETER;
  /* execute command */
  res = TPM_GetPubKey(keyHandle, &req->auth1, &pubKey);
  if (res != TPM_SUCCESS) return res;
  /* marshal output */
  rsp->paramSize = len = sizeof_TPM_PUBKEY(pubKey);
  rsp->param = ptr = malloc(len);
  if (ptr == NULL
      || tpm_marshal_TPM_PUBKEY(&ptr, &len, &pubKey)) {
    free(rsp->param);
    res = TPM_FAIL;
  }
  free_TPM_PUBKEY(pubKey);
  return res;
}
Esempio n. 3
0
static TPM_RESULT execute_TPM_UnBind(TPM_REQUEST *req, TPM_RESPONSE *rsp)
{
  BYTE *ptr;
  UINT32 len;
  TPM_KEY_HANDLE keyHandle;
  UINT32 inDataSize;
  BYTE *inData;
  UINT32 outDataSize;
  BYTE *outData = NULL;
  TPM_RESULT res;
  /* compute parameter digest */
  tpm_compute_in_param_digest(req);
  /* unmarshal input */
  ptr = req->param;
  len = req->paramSize;
  if (tpm_unmarshal_TPM_KEY_HANDLE(&ptr, &len, &keyHandle)
      || tpm_unmarshal_UINT32(&ptr, &len, &inDataSize)
      || tpm_unmarshal_BLOB(&ptr, &len, &inData, inDataSize)
      || len != 0) return TPM_BAD_PARAMETER;
  /* execute command */
  res = TPM_UnBind(keyHandle, inDataSize, inData, &req->auth1, &outDataSize, &outData);
  if (res != TPM_SUCCESS) return res;
  /* marshal output */
  rsp->paramSize = len = 4 + outDataSize;
  rsp->param = ptr = ExtendBuf;
  if (ptr == NULL
      || tpm_marshal_UINT32(&ptr, &len, outDataSize)
      || tpm_marshal_BLOB(&ptr, &len, outData, outDataSize)) {
    free(rsp->param);
    res = TPM_FAIL;
  }
  free(outData);
  return res;
}
static TPM_RESULT execute_MTM_LoadVerificationKey(TPM_REQUEST *req, TPM_RESPONSE *rsp)
{
  BYTE *ptr;
  UINT32 len;
  TPM_VERIFICATION_KEY_HANDLE parentKey;
  UINT32 verificationKeySize;
  TPM_VERIFICATION_KEY verificationKey;
  TPM_VERIFICATION_KEY_HANDLE verificationKeyHandle;
  BYTE loadMethod;
  TPM_RESULT res;
  /* compute parameter digest */
  tpm_compute_in_param_digest(req);
  /* unmarshal input */
  ptr = req->param;
  len = req->paramSize;
  if (tpm_unmarshal_TPM_VERIFICATION_KEY_HANDLE(&ptr, &len, &parentKey)
      || tpm_unmarshal_UINT32(&ptr, &len, &verificationKeySize)
      || tpm_unmarshal_TPM_VERIFICATION_KEY(&ptr, &len, &verificationKey)
      || len != 0) return TPM_BAD_PARAMETER;
  /* execute command  */
  res = MTM_LoadVerificationKey(parentKey, &verificationKey, &req->auth1,
    &verificationKeyHandle, &loadMethod);
  if (res != TPM_SUCCESS) return res;
  /* marshal output */
  rsp->paramSize = len = 4 + 1;
  rsp->param = ptr = tpm_malloc(len);
  if (ptr == NULL
      || tpm_marshal_TPM_VERIFICATION_KEY_HANDLE(&ptr, &len, verificationKeyHandle)
      || tpm_marshal_BYTE(&ptr, &len, loadMethod)) {
    tpm_free(rsp->param);
    res = TPM_FAIL;
  }
  return res;
}
Esempio n. 5
0
static TPM_RESULT execute_TPM_CreateWrapKey(TPM_REQUEST *req, TPM_RESPONSE *rsp) {
  BYTE *ptr;
  UINT32 len;
  TPM_KEY_HANDLE parentHandle;
  TPM_ENCAUTH dataUsageAuth;
  TPM_ENCAUTH dataMigrationAuth;
  TPM_KEY keyInfo;
  TPM_KEY wrappedKey;
  TPM_RESULT res;
  /* compute parameter digest */
  tpm_compute_in_param_digest(req);
  /* unmarshal input */
  ptr = req->param;
  len = req->paramSize;
  if (tpm_unmarshal_TPM_KEY_HANDLE(&ptr, &len, &parentHandle)
      || tpm_unmarshal_TPM_ENCAUTH(&ptr, &len, &dataUsageAuth)
      || tpm_unmarshal_TPM_ENCAUTH(&ptr, &len, &dataMigrationAuth)
      || tpm_unmarshal_TPM_KEY(&ptr, &len, &keyInfo)
      || len != 0) return TPM_BAD_PARAMETER;
  /* execute command */
  res = TPM_CreateWrapKey(parentHandle, &dataUsageAuth, &dataMigrationAuth, 
    &keyInfo, &req->auth1, &wrappedKey);
  if (res != TPM_SUCCESS) return res;
  /* marshal output */
  rsp->paramSize = len = sizeof_TPM_KEY(wrappedKey);
  //rsp->param = ptr = malloc(len);
  rsp->param = ptr = ExtendBuf;
  if (ptr == NULL
      || tpm_marshal_TPM_KEY(&ptr, &len, &wrappedKey)) {
    free(rsp->param);
    res = TPM_FAIL;
  }
  free_TPM_KEY(wrappedKey);
  return res;
}
static TPM_RESULT execute_MTM_InstallRIM(TPM_REQUEST *req, TPM_RESPONSE *rsp)
{
  BYTE *ptr;
  UINT32 len;
  UINT32 rimCertSize;
  TPM_RIM_CERTIFICATE rimCertIn;
  TPM_RIM_CERTIFICATE rimCertOut;
  TPM_RESULT res;
  /* compute parameter digest */
  tpm_compute_in_param_digest(req);
  /* unmarshal input */
  ptr = req->param;
  len = req->paramSize;
  if (tpm_unmarshal_UINT32(&ptr, &len, &rimCertSize)
      || tpm_unmarshal_TPM_RIM_CERTIFICATE(&ptr, &len, &rimCertIn)
      || len != 0) return TPM_BAD_PARAMETER;
  /* execute command */
  res = MTM_InstallRIM(&rimCertIn, &req->auth1, &rimCertOut);
  if (res != TPM_SUCCESS) return res;
  /* marshal output */
  rsp->paramSize = len = 4 + sizeof_TPM_RIM_CERTIFICATE(rimCertOut);
  rsp->param = ptr = tpm_malloc(len);
  if (ptr == NULL
      || tpm_marshal_UINT32(&ptr, &len, sizeof_TPM_RIM_CERTIFICATE(rimCertOut))
      || tpm_marshal_TPM_RIM_CERTIFICATE(&ptr, &len, &rimCertOut)) {
    tpm_free(rsp->param);
    res = TPM_FAIL;
  }
  free_TPM_RIM_CERTIFICATE(rimCertOut);
  return res;
}
static TPM_RESULT execute_MTM_VerifyRIMCertAndExtend(TPM_REQUEST *req, TPM_RESPONSE *rsp)
{
  BYTE *ptr;
  UINT32 len;
  UINT32 rimCertSize;
  TPM_RIM_CERTIFICATE rimCert;
  TPM_VERIFICATION_KEY_HANDLE rimKey;
  TPM_PCRVALUE outDigest;
  TPM_RESULT res;
  /* compute parameter digest */
  tpm_compute_in_param_digest(req);
  /* unmarshal input */
  ptr = req->param;
  len = req->paramSize;
  if (tpm_unmarshal_UINT32(&ptr, &len, &rimCertSize)
      || tpm_unmarshal_TPM_RIM_CERTIFICATE(&ptr, &len, &rimCert)
      || tpm_unmarshal_TPM_VERIFICATION_KEY_HANDLE(&ptr, &len, &rimKey)
      || len != 0) return TPM_BAD_PARAMETER;
  /* execute command */
  res = MTM_VerifyRIMCertAndExtend(&rimCert, rimKey, &outDigest);
  /* marshal output */
  rsp->paramSize = len = 20;
  rsp->param = ptr = tpm_malloc(len);
  if (ptr == NULL
      || tpm_marshal_TPM_PCRVALUE(&ptr, &len, &outDigest)) {
    tpm_free(rsp->param);
    res = TPM_FAIL;
  }
  return res;
}
static TPM_RESULT execute_MTM_LoadVerificationRootKeyDisable(TPM_REQUEST *req, TPM_RESPONSE *rsp)
{
  TPM_RESULT res;
  /* compute parameter digest */
  tpm_compute_in_param_digest(req);
  /* execute command */
  res = MTM_LoadVerificationRootKeyDisable();
  /* marshal output */
  rsp->paramSize = 0;
  rsp->param = NULL;
  return res;
}
static TPM_RESULT execute_MTM_SetVerifiedPCRSelection(TPM_REQUEST *req, TPM_RESPONSE *rsp)
{
  BYTE *ptr;
  UINT32 len;
  TPM_PCR_SELECTION verifiedSelection;
  TPM_RESULT res;
  /* compute parameter digest */
  tpm_compute_in_param_digest(req);
  /* unmarshal input */
  ptr = req->param;
  len = req->paramSize;
  if (tpm_unmarshal_TPM_PCR_SELECTION(&ptr, &len, &verifiedSelection)
      || len != 0) return TPM_BAD_PARAMETER;
  /* execute command */
  res = MTM_SetVerifiedPCRSelection(&verifiedSelection, &req->auth1);
  /* marshal output */
  rsp->paramSize = len = 0;
  rsp->param = ptr = NULL;
  return res;
}
Esempio n. 10
0
static TPM_RESULT execute_TPM_CertifyKey(TPM_REQUEST *req, TPM_RESPONSE *rsp)
{
  BYTE *ptr;
  UINT32 len;
  TPM_KEY_HANDLE certHandle;
  TPM_KEY_HANDLE keyHandle;
  TPM_NONCE antiReplay;
  TPM_CERTIFY_INFO certifyInfo;
  UINT32 outDataSize;
  BYTE *outData = NULL;
  TPM_RESULT res;
  /* compute parameter digest */
  tpm_compute_in_param_digest(req);
  /* unmarshal input */
  ptr = req->param;
  len = req->paramSize;
  if (tpm_unmarshal_TPM_KEY_HANDLE(&ptr, &len, &certHandle)
      || tpm_unmarshal_TPM_KEY_HANDLE(&ptr, &len, &keyHandle)
      || tpm_unmarshal_TPM_NONCE(&ptr, &len, &antiReplay)
      || len != 0) return TPM_BAD_PARAMETER;
  /* execute command */
  res = TPM_CertifyKey(certHandle, keyHandle, &antiReplay, &req->auth1, 
    &req->auth2, &certifyInfo, &outDataSize, &outData);
  if (res != TPM_SUCCESS) return res;
  /* marshal output */
  rsp->paramSize = len = sizeof_TPM_CERTIFY_INFO(certifyInfo) + 4 + outDataSize;
  rsp->param = ptr = ExtendBuf;
  if (ptr == NULL
      || tpm_marshal_TPM_CERTIFY_INFO(&ptr, &len, &certifyInfo)
      || tpm_marshal_UINT32(&ptr, &len, outDataSize)
      || tpm_marshal_BLOB(&ptr, &len, outData, outDataSize)) {
    free(rsp->param);
    res = TPM_FAIL;
  }
  free_TPM_CERTIFY_INFO(certifyInfo);
  free(outData);
  return res;
}
Esempio n. 11
0
static TPM_RESULT execute_TPM_TakeOwnership(TPM_REQUEST *req, TPM_RESPONSE *rsp) {
   BYTE *ptr;
   UINT32 len;
   TPM_PROTOCOL_ID protocolID;
   UINT32 encOwnerAuthSize;
   BYTE *encOwnerAuth;
   UINT32 encSrkAuthSize;
   BYTE *encSrkAuth;
   TPM_KEY srkParams;
   TPM_KEY srkPub;
   TPM_RESULT res;
   /* compute parameter digest */
   tpm_compute_in_param_digest(req);
   /* unmarshal input */
   ptr = req->param;
   len = req->paramSize;
   if (tpm_unmarshal_TPM_PROTOCOL_ID(&ptr, &len, &protocolID)
       || tpm_unmarshal_UINT32(&ptr, &len, &encOwnerAuthSize)
       || tpm_unmarshal_BLOB(&ptr, &len, &encOwnerAuth, encOwnerAuthSize)
       || tpm_unmarshal_UINT32(&ptr, &len, &encSrkAuthSize)
       || tpm_unmarshal_BLOB(&ptr, &len, &encSrkAuth, encSrkAuthSize)
       || tpm_unmarshal_TPM_KEY(&ptr, &len, &srkParams)
       || len != 0) return TPM_BAD_PARAMETER;
   /* execute command */
   res = TPM_TakeOwnership(protocolID, encOwnerAuthSize, encOwnerAuth, 
     encSrkAuthSize, encSrkAuth, &srkParams, &req->auth1, &srkPub);
   if (res != TPM_SUCCESS) return res;
   /* marshal output */
   rsp->paramSize = len = sizeof_TPM_KEY(srkPub);
   rsp->param = ptr = malloc(len);
   if (ptr == NULL
       || tpm_marshal_TPM_KEY(&ptr, &len, &srkPub)) {
     free(rsp->param);
     res = TPM_FAIL;
   }
   free_TPM_KEY(srkPub);
   return res;
 }
Esempio n. 12
0
static TPM_RESULT execute_TPM_Quote(TPM_REQUEST *req, TPM_RESPONSE *rsp)
{
  BYTE *ptr;
  UINT32 len;
  TPM_KEY_HANDLE keyHandle;
  TPM_NONCE extrnalData;
  TPM_PCR_SELECTION targetPCR;
  TPM_PCR_COMPOSITE *pcrData;
  UINT32 sigSize;
  BYTE *sig = NULL;
  TPM_RESULT res;
  pcrData = (TPM_PCR_COMPOSITE *)InOutBuf;
  /* compute parameter digest */
  tpm_compute_in_param_digest(req);
  /* unmarshal input */
  ptr = req->param;
  len = req->paramSize;
  if (tpm_unmarshal_TPM_KEY_HANDLE(&ptr, &len, &keyHandle)
      || tpm_unmarshal_TPM_NONCE(&ptr, &len, &extrnalData)
      || tpm_unmarshal_TPM_PCR_SELECTION(&ptr, &len, &targetPCR)
      || len != 0) return TPM_BAD_PARAMETER;
  /* execute command */
  res = TPM_Quote(keyHandle, &extrnalData, &targetPCR, &req->auth1, pcrData, &sigSize, &sig);
  if (res != TPM_SUCCESS) return res;
  /* marshal output */
  rsp->paramSize = len = sizeof_TPM_PCR_COMPOSITE((*pcrData)) + 4 + sigSize;
  rsp->param = ptr = ExtendBuf;
  if (ptr == NULL
      || tpm_marshal_TPM_PCR_COMPOSITE(&ptr, &len, pcrData)
      || tpm_marshal_UINT32(&ptr, &len, sigSize)
      || tpm_marshal_BLOB(&ptr, &len, sig, sigSize)) {
    free(rsp->param);
    res = TPM_FAIL;
  }
  free(sig);
  return res;
}
static TPM_RESULT execute_MTM_IncrementBootstrapCounter(TPM_REQUEST *req, TPM_RESPONSE *rsp)
{
  BYTE *ptr;
  UINT32 len;
  UINT32 rimCertSize;
  TPM_RIM_CERTIFICATE rimCert;
  TPM_VERIFICATION_KEY_HANDLE rimKey;
  TPM_RESULT res;
  /* compute parameter digest */
  tpm_compute_in_param_digest(req);
  /* unmarshal input */
  ptr = req->param;
  len = req->paramSize;
  if (tpm_unmarshal_UINT32(&ptr, &len, &rimCertSize)
      || tpm_unmarshal_TPM_RIM_CERTIFICATE(&ptr, &len, &rimCert)
      || tpm_unmarshal_TPM_VERIFICATION_KEY_HANDLE(&ptr, &len, &rimKey)
      || len != 0) return TPM_BAD_PARAMETER;
  /* execute command */
  res = MTM_IncrementBootstrapCounter(&rimCert, rimKey);
  /* marshal output */
  rsp->paramSize = len = 0;
  rsp->param = ptr = NULL;
  return res;
}
Esempio n. 14
0
TPM_RESULT TPM_ExecuteTransport(UINT32 inWrappedCmdSize, BYTE *inWrappedCmd,
                                TPM_AUTH *auth1, UINT64 *currentTicks,
                                TPM_MODIFIER_INDICATOR *locality,
                                UINT32 *outWrappedCmdSize, BYTE **outWrappedCmd)
{
  TPM_RESULT res;
  TPM_SESSION_DATA *session;
  TPM_REQUEST req;
  TPM_RESPONSE rsp;
  BYTE *ptr, buf[4 * 4 + 8 + 20];
  UINT32 len, offset;
  tpm_sha1_ctx_t sha1;
  info("TPM_ExecuteTransport()");
  /* get transport session */
  session = tpm_get_transport(auth1->authHandle);
  if (session == NULL) return TPM_BAD_PARAMETER;
  /* unmarshal wrapped command */
  len = inWrappedCmdSize;
  ptr = inWrappedCmd;
  if (tpm_unmarshal_TPM_REQUEST(&ptr, &len, &req)) return TPM_FAIL;
  /* decrypt wrapped command if needed */
  ptr = tpm_malloc(req.paramSize);
  if (ptr == NULL) return TPM_FAIL;
  memcpy(ptr, req.param, req.paramSize);
  if (session->transInternal.transPublic.transAttributes & TPM_TRANSPORT_ENCRYPT) {
    if (req.ordinal == TPM_ORD_OIAP || req.ordinal == TPM_ORD_OSAP) {
      offset = req.paramSize;
    } else if (req.ordinal == TPM_ORD_DSAP) {
      offset = 30;
    } else {
      offset = tpm_get_in_param_offset(req.ordinal);
    }
    debug("decrypting %d bytes, starting at pos %d", req.paramSize - offset, offset);
    decrypt_wrapped_command(ptr + offset, req.paramSize - offset, auth1, session);
  }
  req.param = ptr;
  /* verify authorization */
  tpm_compute_in_param_digest(&req);
  tpm_sha1_init(&sha1);
  tpm_sha1_update_be32(&sha1, TPM_ORD_ExecuteTransport);
  tpm_sha1_update_be32(&sha1, inWrappedCmdSize);
  tpm_sha1_update(&sha1, req.auth1.digest, sizeof(req.auth1.digest));
  tpm_sha1_final(&sha1, auth1->digest);
  res = tpm_verify_auth(auth1, session->transInternal.authData, TPM_INVALID_HANDLE);
  if (res != TPM_SUCCESS) {
    tpm_free(req.param);
    return res;
  }
  /* nested transport sessions are not allowed */
  if (req.ordinal == TPM_ORD_EstablishTransport
      || req.ordinal == TPM_ORD_ExecuteTransport
      || req.ordinal == TPM_ORD_ReleaseTransportSigned) {
    tpm_free(req.param);
    return TPM_NO_WRAP_TRANSPORT;
  }
  /* log input parameters */
  if (session->transInternal.transPublic.transAttributes & TPM_TRANSPORT_LOG) {
    TPM_DIGEST keyDigest;
    compute_key_digest(&req, &keyDigest);
    transport_log_in(req.auth1.digest, keyDigest.digest,
                     &session->transInternal.transDigest);
  }
  /* execute and audit command*/
  tpm_audit_request(req.ordinal, &req);
  tpm_execute_command(&req, &rsp);
  tpm_audit_response(req.ordinal, &rsp);
  tpm_free(req.param);
  /* get locality and ticks */
  *locality = tpmData.stany.flags.localityModifier;
  *currentTicks = tpmData.stany.data.currentTicks.currentTicks;
  /* if required, compute digest of internal output parameters */
  debug("result = %d", rsp.result);
  if (rsp.result == TPM_SUCCESS) {
    if (rsp.tag == TPM_TAG_RSP_COMMAND) {
      rsp.auth1 = &req.auth1;
      tpm_compute_out_param_digest(req.ordinal, &rsp);
    }
    /* encrypt parameters */
    if (session->transInternal.transPublic.transAttributes & TPM_TRANSPORT_ENCRYPT) {
      if (req.ordinal == TPM_ORD_OIAP || req.ordinal == TPM_ORD_OSAP) {
        offset = rsp.paramSize;
      } else if (req.ordinal == TPM_ORD_DSAP) {
        offset = rsp.paramSize;
      } else {
        offset = tpm_get_out_param_offset(req.ordinal);
      }
      debug("encrypting %d bytes, starting at pos %d", rsp.paramSize - offset, offset);
      encrypt_wrapped_command(rsp.param + offset, rsp.paramSize - offset, auth1, session);
    }
  } else {
    rsp.auth1 = &req.auth1;
    memset(rsp.auth1->digest, 0, sizeof(*rsp.auth1->digest));
  }
  /* marshal response */
  *outWrappedCmdSize = len = rsp.size;
  *outWrappedCmd = ptr = tpm_malloc(len);
  if (ptr == NULL) {
    tpm_free(rsp.param);
    return TPM_FAIL;
  }
  tpm_marshal_TPM_RESPONSE(&ptr, &len, &rsp);
  debug("marshalling done.");
  /* log output parameters */
  if (session->transInternal.transPublic.transAttributes & TPM_TRANSPORT_LOG) {
    transport_log_out(rsp.auth1->digest, &session->transInternal.transDigest);
  }
  tpm_free(rsp.param);
  /* compute digest of output parameters */
  ptr = buf; len = sizeof(buf);
  tpm_marshal_UINT32(&ptr, &len, TPM_SUCCESS);
  tpm_marshal_TPM_COMMAND_CODE(&ptr, &len, TPM_ORD_ExecuteTransport);
  tpm_marshal_UINT64(&ptr, &len, *currentTicks);
  tpm_marshal_TPM_MODIFIER_INDICATOR(&ptr, &len, *locality);
  tpm_marshal_UINT32(&ptr, &len, *outWrappedCmdSize);
  memcpy(ptr, rsp.auth1->digest, sizeof(rsp.auth1->digest));
  tpm_sha1_init(&sha1);
  tpm_sha1_update(&sha1, buf, sizeof(buf));
  tpm_sha1_final(&sha1, auth1->digest);
  return TPM_SUCCESS;
}