1# Copyright 2014 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""
6Provides a utility function for working with TPM DAM logic.
7
8Dictionary Attack Mitigation (DAM) logic causes TPMs to enter a locked down
9state to defend against dictionary attacks. Authentication failures cause a
10counter to increment and when the counter exceeds some threshold, the defense
11mechanism is triggered.
12"""
13
14import os, re
15
16from autotest_lib.client.common_lib import utils
17from autotest_lib.client.cros import service_stopper
18
19def get_dictionary_attack_counter():
20    """Returns the current dictionary attack counter."""
21    tpm_command_info = {
22        '0x49465800': {  # Infineon
23            'command': ('00 c1 '         # Tag = TPM_TAG_RQU_COMMAND
24                        '00 00 00 16 '   # Size = 22
25                        '00 00 00 65 '   # Ordinal = TPM_ORD_GetCapability
26                        '00 00 00 10 '   # Capability Area = TPM_CAP_MFR
27                        '00 00 00 04 '   # Size = 4
28                        '00 00 08 02'),  # Vendor-specific
29            'response_offset': 23},      # Vendor-specific
30        '0x57454300': {  # Nuvoton
31            'command': ('00 c1 '         # Tag = TPM_TAG_RQU_COMMAND
32                        '00 00 00 14 '   # Size = 20
33                        '00 00 00 65 '   # Ordinal = TPM_ORD_GetCapability
34                        '00 00 00 19 '   # Capability Area = TPM_CAP_DA_LOGIC
35                        '00 00 00 02 '   # Size = 2
36                        '00 04'),        # Entity Type = TPM_ET_SRK
37            'response_offset': 18},      # TPM_DA_INFO.currentCount LSB
38        '0x53544d20': {  # STMicro
39            'command': ('00 c1 '         # Tag = TPM_TAG_RQU_COMMAND
40                        '00 00 00 14 '   # Size = 20
41                        '00 00 00 65 '   # Ordinal = TPM_ORD_GetCapability
42                        '00 00 00 19 '   # Capability Area = TPM_CAP_DA_LOGIC
43                        '00 00 00 02 '   # Size = 2
44                        '00 04'),        # Entity Type = TPM_ET_SRK
45            'response_offset': 18}}      # TPM_DA_INFO.currentCount LSB
46    caps_file='/sys/class/misc/tpm0/device/caps'
47    if not os.path.exists(caps_file):
48        caps_file='/sys/class/tpm/tpm0/device/caps'
49    try:
50        with open(caps_file, 'r') as fp:
51            caps = fp.read()
52    except IOError:
53        return 'Could not read TPM device caps.'
54    match = re.search(r'Manufacturer: (0x[0-9A-Fa-f]*)', caps)
55    if not match:
56        return 'Could not find TPM manufacturer.'
57    manufacturer = match.group(1)
58    if manufacturer not in tpm_command_info:
59        return 'TPM manufacturer not supported.'
60    with service_stopper.ServiceStopper(['cryptohomed',
61                                         'chapsd',
62                                         'tcsd']):
63        # The output of 'tpmc raw' is a series of bytes in the form
64        # '0x00 0x01 0x02 ...'.
65        tpm_response = utils.system_output(
66                'tpmc raw %s' % tpm_command_info[manufacturer]['command'],
67                ignore_status=True).split()
68    offset = tpm_command_info[manufacturer]['response_offset']
69    if (len(tpm_response) <= offset):
70        return 'Unexpected TPM response (length = %d).' % len(tpm_response)
71    return int(tpm_response[offset], base=16)
72
73
74