// This file was extracted from the TCG Published
// Trusted Platform Module Library
// Part 4: Supporting Routines
// Family "2.0"
// Level 00 Revision 01.16
// October 30, 2014

#define DA_C
#include "InternalRoutines.h"
//
//
//           Functions
//
//            DAPreInstall_Init()
//
//      This function initializes the DA parameters to their manufacturer-default values. The default values are
//      determined by a platform-specific specification.
//      This function should not be called outside of a manufacturing or simulation environment.
//      The DA parameters will be restored to these initial values by TPM2_Clear().
//
void
DAPreInstall_Init(
     void
     )
{
     gp.failedTries = 0;
     // TODO(vbendeb): consider finer tuning of this value (crosbug.com/p/55708)
     gp.maxTries = 200;
     gp.recoveryTime = 1000;                  // in seconds (~16.67 minutes)
     gp.lockoutRecovery = 1000;               // in seconds
     gp.lockOutAuthEnabled = TRUE;            // Use of lockoutAuth is enabled
     // Record persistent DA parameter changes to NV
     NvWriteReserved(NV_FAILED_TRIES, &gp.failedTries);
     NvWriteReserved(NV_MAX_TRIES, &gp.maxTries);
     NvWriteReserved(NV_RECOVERY_TIME, &gp.recoveryTime);
     NvWriteReserved(NV_LOCKOUT_RECOVERY, &gp.lockoutRecovery);
     NvWriteReserved(NV_LOCKOUT_AUTH_ENABLED, &gp.lockOutAuthEnabled);
    return;
}
//
//
//          DAStartup()
//
//     This function is called by TPM2_Startup() to initialize the DA parameters. In the case of Startup(CLEAR),
//     use of lockoutAuth will be enabled if the lockout recovery time is 0. Otherwise, lockoutAuth will not be
//     enabled until the TPM has been continuously powered for the lockoutRecovery time.
//     This function requires that NV be available and not rate limiting.
//
void
DAStartup(
    STARTUP_TYPE         type               // IN: startup type
    )
{
    // For TPM Reset, if lockoutRecovery is 0, enable use of lockoutAuth.
    if(type == SU_RESET)
    {
        if(gp.lockoutRecovery == 0)
        {
            gp.lockOutAuthEnabled = TRUE;
            // Record the changes to NV
            NvWriteReserved(NV_LOCKOUT_AUTH_ENABLED, &gp.lockOutAuthEnabled);
        }
    }
    // If DA has not been disabled and the previous shutdown is not orderly
    // failedTries is not already at its maximum then increment 'failedTries'
    if(    gp.recoveryTime != 0
        && g_prevOrderlyState == SHUTDOWN_NONE
        && gp.failedTries < gp.maxTries)
    {
        gp.failedTries++;
        // Record the change to NV
        NvWriteReserved(NV_FAILED_TRIES, &gp.failedTries);
    }
    // Reset self healing timers
    s_selfHealTimer = g_time;
    s_lockoutTimer = g_time;
    return;
}
//
//
//          DARegisterFailure()
//
//     This function is called when a authorization failure occurs on an entity that is subject to dictionary-attack
//     protection. When a DA failure is triggered, register the failure by resetting the relevant self-healing timer
//     to the current time.
//
void
DARegisterFailure(
    TPM_HANDLE           handle             // IN: handle for failure
    )
{
    // Reset the timer associated with lockout if the handle is the lockout auth.
    if(handle == TPM_RH_LOCKOUT)
         s_lockoutTimer = g_time;
    else
         s_selfHealTimer = g_time;
//
   return;
}
//
//
//             DASelfHeal()
//
//      This function is called to check if sufficient time has passed to allow decrement of failedTries or to re-
//      enable use of lockoutAuth.
//      This function should be called when the time interval is updated.
//
void
DASelfHeal(
   void
   )
{
   // Regular auth self healing logic
   // If no failed authorization tries, do nothing. Otherwise, try to
   // decrease failedTries
   if(gp.failedTries != 0)
   {
       // if recovery time is 0, DA logic has been disabled. Clear failed tries
       // immediately
       if(gp.recoveryTime == 0)
       {
            gp.failedTries = 0;
            // Update NV record
            NvWriteReserved(NV_FAILED_TRIES, &gp.failedTries);
       }
       else
       {
            UINT64          decreaseCount;
               // In the unlikely event that failedTries should become larger than
               // maxTries
               if(gp.failedTries > gp.maxTries)
                   gp.failedTries = gp.maxTries;
               // How much can failedTried be decreased
               decreaseCount = ((g_time - s_selfHealTimer) / 1000) / gp.recoveryTime;
               if(gp.failedTries <= (UINT32) decreaseCount)
                   // should not set failedTries below zero
                   gp.failedTries = 0;
               else
                   gp.failedTries -= (UINT32) decreaseCount;
               // the cast prevents overflow of the product
               s_selfHealTimer += (decreaseCount * (UINT64)gp.recoveryTime) * 1000;
               if(decreaseCount != 0)
                   // If there was a change to the failedTries, record the changes
                   // to NV
                   NvWriteReserved(NV_FAILED_TRIES, &gp.failedTries);
         }
   }
   // LockoutAuth self healing logic
   // If lockoutAuth is enabled, do nothing. Otherwise, try to see if we
   // may enable it
   if(!gp.lockOutAuthEnabled)
   {
       // if lockout authorization recovery time is 0, a reboot is required to
       // re-enable use of lockout authorization. Self-healing would not
       // apply in this case.
       if(gp.lockoutRecovery != 0)
//
           {
                 if(((g_time - s_lockoutTimer)/1000) >= gp.lockoutRecovery)
                 {
                     gp.lockOutAuthEnabled = TRUE;
                     // Record the changes to NV
                     NvWriteReserved(NV_LOCKOUT_AUTH_ENABLED, &gp.lockOutAuthEnabled);
                 }
           }
     }
     return;
}