# Copyright 2014 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""
Provides a utility function for working with TPM DAM logic.

Dictionary Attack Mitigation (DAM) logic causes TPMs to enter a locked down
state to defend against dictionary attacks. Authentication failures cause a
counter to increment and when the counter exceeds some threshold, the defense
mechanism is triggered.
"""

import os, re

from autotest_lib.client.common_lib import utils
from autotest_lib.client.cros import service_stopper

def get_dictionary_attack_counter():
    """Returns the current dictionary attack counter."""
    tpm_command_info = {
        '0x49465800': {  # Infineon
            'command': ('00 c1 '         # Tag = TPM_TAG_RQU_COMMAND
                        '00 00 00 16 '   # Size = 22
                        '00 00 00 65 '   # Ordinal = TPM_ORD_GetCapability
                        '00 00 00 10 '   # Capability Area = TPM_CAP_MFR
                        '00 00 00 04 '   # Size = 4
                        '00 00 08 02'),  # Vendor-specific
            'response_offset': 23},      # Vendor-specific
        '0x57454300': {  # Nuvoton
            'command': ('00 c1 '         # Tag = TPM_TAG_RQU_COMMAND
                        '00 00 00 14 '   # Size = 20
                        '00 00 00 65 '   # Ordinal = TPM_ORD_GetCapability
                        '00 00 00 19 '   # Capability Area = TPM_CAP_DA_LOGIC
                        '00 00 00 02 '   # Size = 2
                        '00 04'),        # Entity Type = TPM_ET_SRK
            'response_offset': 18},      # TPM_DA_INFO.currentCount LSB
        '0x53544d20': {  # STMicro
            'command': ('00 c1 '         # Tag = TPM_TAG_RQU_COMMAND
                        '00 00 00 14 '   # Size = 20
                        '00 00 00 65 '   # Ordinal = TPM_ORD_GetCapability
                        '00 00 00 19 '   # Capability Area = TPM_CAP_DA_LOGIC
                        '00 00 00 02 '   # Size = 2
                        '00 04'),        # Entity Type = TPM_ET_SRK
            'response_offset': 18}}      # TPM_DA_INFO.currentCount LSB
    caps_file='/sys/class/misc/tpm0/device/caps'
    if not os.path.exists(caps_file):
        caps_file='/sys/class/tpm/tpm0/device/caps'
    try:
        with open(caps_file, 'r') as fp:
            caps = fp.read()
    except IOError:
        return 'Could not read TPM device caps.'
    match = re.search(r'Manufacturer: (0x[0-9A-Fa-f]*)', caps)
    if not match:
        return 'Could not find TPM manufacturer.'
    manufacturer = match.group(1)
    if manufacturer not in tpm_command_info:
        return 'TPM manufacturer not supported.'
    with service_stopper.ServiceStopper(['cryptohomed',
                                         'chapsd',
                                         'tcsd']):
        # The output of 'tpmc raw' is a series of bytes in the form
        # '0x00 0x01 0x02 ...'.
        tpm_response = utils.system_output(
                'tpmc raw %s' % tpm_command_info[manufacturer]['command'],
                ignore_status=True).split()
    offset = tpm_command_info[manufacturer]['response_offset']
    if (len(tpm_response) <= offset):
        return 'Unexpected TPM response (length = %d).' % len(tpm_response)
    return int(tpm_response[offset], base=16)