hmac-otp Context Variables

Context variables for HMAC-based One-Time Password (HOTP and TOTP) authentication. These variables are populated by backends and used by hotp and totp authentication actions.

hmac-otp.secret

The shared secret used for generating one-time passwords. Required for both HOTP and TOTP authentication.

Type: String (hex or base32 encoded)

Set by: Backend mapping

Format: Determined by the secret_type parameter in the authentication action:

  • Hexadecimal (default): "0123456789ABCDEF"
  • Base32 (RFC 4648): "JBSWY3DPEHPK3PXP"
  • Auto-detect: Automatically determines encoding

Example:

backends {
    sqlite "USERS" {
        query "FIND_USER" {
            mapping {
                hmac-otp.secret = totp_secret;
            }
        }
    }
}

Security: The secret is the cryptographic key for OTP generation. It must be:

  • Randomly generated (minimum 128 bits, preferably 160 bits for SHA1)
  • Stored securely in the backend
  • Never logged or transmitted in cleartext
  • Unique per user

hmac-otp.counter

The current counter value for HOTP authentication. Required for HOTP, not used for TOTP.

Type: Integer (unsigned 64-bit)

Set by: Backend mapping (initial value), updated by system after successful authentication

Read after authentication: Contains the next counter value to persist

Example:

backends {
    sqlite "USERS" {
        query "FIND_USER" {
            mapping {
                hmac-otp.counter = hotp_counter;
            }
        }
        query "UPDATE_HOTP_COUNTER" {
            bindings {
                hmac-otp.counter;  # Updated counter after auth
                user.username;
            }
        }
    }
}

Important: Must be persisted after successful authentication to prevent replay attacks and enable subsequent logins.

hmac-otp.digits

The number of digits in the one-time password code.

Type: Integer (typically 6-8)

Default: 6

Set by: Backend mapping (optional)

Example:

mapping {
    hmac-otp.digits = otp_digits;  # 6, 7, or 8
}

Standards:

  • RFC 4226 (HOTP) recommends 6 digits minimum
  • RFC 6238 (TOTP) uses 6 digits by default
  • 8 digits provides higher security but requires more storage/display

hmac-otp.timestep

The time window duration in seconds for TOTP authentication. Not used for HOTP.

Type: Integer (seconds)

Default: 30

Set by: Backend mapping (optional)

Example:

mapping {
    hmac-otp.timestep = totp_timestep;  # 30, 60, etc.
}

Common values:

  • 30 seconds (RFC 6238 default, used by Google Authenticator, Microsoft Authenticator)
  • 60 seconds (some corporate systems)

Operational impact: Shorter timesteps provide better security but require tighter clock synchronization.

hmac-otp.timestep.origin

The Unix timestamp origin for TOTP counter calculation (T0 in RFC 6238).

Type: Integer (Unix timestamp)

Default: 0 (January 1, 1970 00:00:00 UTC)

Set by: Backend mapping (optional, rarely changed)

Example:

mapping {
    hmac-otp.timestep.origin = totp_origin;
}

Usage: Rarely modified. The default of 0 is compatible with all standard TOTP implementations. Custom origins are used only in specialized scenarios with non-standard TOTP implementations.

hmac-otp.timestep.last

The last successfully used timestep value for TOTP replay attack prevention.

Type: Integer (timestep counter)

Set by: Backend mapping (initial value), updated by system after successful authentication

Read after authentication: Contains the timestep value that was just used

Example:

# Load previous timestep
mapping {
    hmac-otp.timestep.last = totp_last_timestep;
}

# In policy - prevent replay
authentication {
    if any {
        cache.last_timestep[aaa.identity] != none;
    } then {
        modify {
            hmac-otp.timestep.last = cache.last_timestep[aaa.identity][0];
        }
    }

    totp {
        resync_window 1 0;
    }

    # Save current timestep
    modify {
        cache.last_timestep[aaa.identity] = hmac-otp.timestep.last;
    }
}

# Or persist to database
query "UPDATE_TOTP_TIMESTEP" {
    bindings {
        hmac-otp.timestep.last;
        user.username;
    }
}

Replay protection: Tracking the last used timestep prevents an attacker from reusing a captured TOTP code within the same time window.

Context Variable Lifecycle

HOTP Flow

  1. Backend populates hmac-otp.secret and hmac-otp.counter
  2. Authentication validates code against current counter and lookahead window
  3. On success, hmac-otp.counter is incremented
  4. Updated counter must be persisted to backend in post-authentication

TOTP Flow

  1. Backend populates hmac-otp.secret (and optionally hmac-otp.timestep, hmac-otp.timestep.last)
  2. Authentication validates code against current time window and resync windows
  3. On success, hmac-otp.timestep.last is updated to current timestep
  4. Optionally persist timestep to backend or cache for replay protection

Security Considerations

Secret Management

  • Generate secrets with cryptographic random number generators
  • Minimum 128-bit entropy (160 bits recommended for SHA1)
  • Store encrypted in backend if possible
  • Rotate secrets periodically (requires user re-enrollment)

Counter/Timestep Protection

  • HOTP: Always persist counter after successful authentication
  • TOTP: Persist or cache last timestep to prevent replay within same window
  • Use database transactions to ensure atomicity

Resync Windows

  • HOTP: Larger windows (10-20) tolerate missed button presses but increase attack surface
  • TOTP: Larger windows (2-3) tolerate clock drift but allow longer replay windows
  • Balance security and usability based on deployment requirements