[Autotest][PY3] cros/cryptohome rework proposal

tldr, crpytohome has 2 things going  for it:
1.) Functions which do things related to cryptohome, that don't need any
special libs (they run using utils.run).
2.) Some stuff with gobjects, dbus, mainloop, etc

The second item is not easy at all to move to py3. gobject is not in
py3, the replacement (    from gi.repository import GObject as gobject)
isn't in tauto deps. uprevving packages is not easy, but can be done.

So right now my proposal is to remove the functions out of cryptohome
that are completely stand alone, and use it instead, with the migration
of main cryptohome to happen later.

The utils are on the semi-critical path to tauto in py3, but the full
lib is not.

Open to suggestions!!!

Change-Id: I3e83342343141840d610e550bba4c8d40f117411
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/autotest/+/2587471
Commit-Queue: Derek Beckett <dbeckett@chromium.org>
Tested-by: Derek Beckett <dbeckett@chromium.org>
Reviewed-by: Prathmesh Prabhu <pprabhu@google.com>
diff --git a/client/bin/site_sysinfo.py b/client/bin/site_sysinfo.py
index 94454f0..cd0c4ee 100755
--- a/client/bin/site_sysinfo.py
+++ b/client/bin/site_sysinfo.py
@@ -11,6 +11,8 @@
 from autotest_lib.client.common_lib import error, utils, global_config
 from autotest_lib.client.bin import base_sysinfo, utils
 from autotest_lib.client.cros import constants
+from autotest_lib.client.cros import tpm
+
 
 get_value = global_config.global_config.get_config_value
 collect_corefiles = get_value('CLIENT', 'collect_corefiles',
@@ -435,16 +437,10 @@
         keyval["CHROME_VERSION"], keyval["MILESTONE"] = (
                 self._get_chrome_version())
 
-        # TODO(kinaba): crbug.com/707448 Import at the head of this file.
-        # Currently a server-side script server/server_job.py is indirectly
-        # importing this file, so we cannot globaly import cryptohome that
-        # has dependency to a client-only library.
-        from autotest_lib.client.cros import cryptohome
         # Get the dictionary attack counter.
         keyval["TPM_DICTIONARY_ATTACK_COUNTER"] = (
-                cryptohome.get_tpm_more_status().get(
-                    'dictionary_attack_counter', 'Failed to query cryptohome'))
-
+            tpm.get_tpm_more_status().get(
+                'dictionary_attack_counter', 'Failed to query cryptohome'))
         # Return the updated keyvals.
         return keyval
 
diff --git a/client/cros/cryptohome.py b/client/cros/cryptohome.py
index 904046d..7ed56de 100644
--- a/client/cros/cryptohome.py
+++ b/client/cros/cryptohome.py
@@ -16,6 +16,7 @@
 from autotest_lib.client.cros import constants
 from autotest_lib.client.bin import utils
 from autotest_lib.client.common_lib import error
+from autotest_lib.client.cros.tpm import *
 from autotest_lib.client.cros.cros_disks import DBusClient
 
 CRYPTOHOME_CMD = '/usr/sbin/cryptohome'
@@ -28,14 +29,6 @@
 DBUS_PROTOS_DEP = 'dbus_protos'
 
 
-class ChromiumOSError(error.TestError):
-    """Generic error for ChromiumOS-specific exceptions."""
-    pass
-
-def __run_cmd(cmd):
-    return utils.system_output(cmd + ' 2>&1', retain_output=True,
-                               ignore_status=True).strip()
-
 def get_user_hash(user):
     """Get the user hash for the given user."""
     return utils.system_output(['cryptohome', '--action=obfuscate_user',
@@ -81,73 +74,6 @@
     mount_vault(user, password, create=True)
 
 
-def get_tpm_status():
-    """Get the TPM status.
-
-    Returns:
-        A TPM status dictionary, for example:
-        { 'Enabled': True,
-          'Owned': True,
-          'Being Owned': False,
-          'Ready': True,
-          'Password': ''
-        }
-    """
-    out = __run_cmd(CRYPTOHOME_CMD + ' --action=tpm_status')
-    status = {}
-    for field in ['Enabled', 'Owned', 'Being Owned', 'Ready']:
-        match = re.search('TPM %s: (true|false)' % field, out)
-        if not match:
-            raise ChromiumOSError('Invalid TPM status: "%s".' % out)
-        status[field] = match.group(1) == 'true'
-    match = re.search('TPM Password: (\w*)', out)
-    status['Password'] = ''
-    if match:
-        status['Password'] = match.group(1)
-    return status
-
-
-def get_tpm_more_status():
-    """Get more of the TPM status.
-
-    Returns:
-        A TPM more status dictionary, for example:
-        { 'dictionary_attack_lockout_in_effect': False,
-          'attestation_prepared': False,
-          'boot_lockbox_finalized': False,
-          'enabled': True,
-          'owned': True,
-          'owner_password': ''
-          'dictionary_attack_counter': 0,
-          'dictionary_attack_lockout_seconds_remaining': 0,
-          'dictionary_attack_threshold': 10,
-          'attestation_enrolled': False,
-          'initialized': True,
-          'verified_boot_measured': False,
-          'install_lockbox_finalized': True
-        }
-        An empty dictionary is returned if the command is not supported.
-    """
-    status = {}
-    out = __run_cmd(CRYPTOHOME_CMD + ' --action=tpm_more_status | grep :')
-    if out.startswith(UNAVAILABLE_ACTION):
-        # --action=tpm_more_status only exists >= 41.
-        logging.info('Method not supported!')
-        return status
-    for line in out.splitlines():
-        items = line.strip().split(':')
-        if items[1].strip() == 'false':
-            value = False
-        elif items[1].strip() == 'true':
-            value = True
-        elif items[1].strip().isdigit():
-            value = int(items[1].strip())
-        else:
-            value = items[1].strip(' "')
-        status[items[0]] = value
-    return status
-
-
 def get_fwmp(cleared_fwmp=False):
     """Get the firmware management parameters.
 
diff --git a/client/cros/tpm.py b/client/cros/tpm.py
new file mode 100644
index 0000000..e7c15fa
--- /dev/null
+++ b/client/cros/tpm.py
@@ -0,0 +1,95 @@
+# Copyright (c) 2021 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.
+
+"""Utilities to interact with the TPM on a CrOS device."""
+
+import logging
+import re
+
+import common
+
+from autotest_lib.client.bin import utils
+from autotest_lib.client.common_lib import error
+
+CRYPTOHOME_CMD = '/usr/sbin/cryptohome'
+UNAVAILABLE_ACTION = 'Unknown action or no action given.'
+
+
+class ChromiumOSError(error.TestError):
+    """Generic error for ChromiumOS-specific exceptions."""
+
+    pass
+
+
+def get_tpm_status():
+    """Get the TPM status.
+
+    Returns:
+        A TPM status dictionary, for example:
+        { 'Enabled': True,
+          'Owned': True,
+          'Being Owned': False,
+          'Ready': True,
+          'Password': ''
+        }
+    """
+    out = __run_cmd(CRYPTOHOME_CMD + ' --action=tpm_status')
+    status = {}
+    for field in ['Enabled', 'Owned', 'Being Owned', 'Ready']:
+        match = re.search('TPM %s: (true|false)' % field, out)
+        if not match:
+            raise ChromiumOSError('Invalid TPM status: "%s".' % out)
+        status[field] = match.group(1) == 'true'
+    match = re.search('TPM Password: (\w*)', out)
+    status['Password'] = ''
+    if match:
+        status['Password'] = match.group(1)
+    return status
+
+
+def get_tpm_more_status():
+    """Get more of the TPM status.
+
+    Returns:
+        A TPM more status dictionary, for example:
+        { 'dictionary_attack_lockout_in_effect': False,
+          'attestation_prepared': False,
+          'boot_lockbox_finalized': False,
+          'enabled': True,
+          'owned': True,
+          'owner_password': ''
+          'dictionary_attack_counter': 0,
+          'dictionary_attack_lockout_seconds_remaining': 0,
+          'dictionary_attack_threshold': 10,
+          'attestation_enrolled': False,
+          'initialized': True,
+          'verified_boot_measured': False,
+          'install_lockbox_finalized': True
+        }
+        An empty dictionary is returned if the command is not supported.
+    """
+    status = {}
+    out = __run_cmd(CRYPTOHOME_CMD + ' --action=tpm_more_status | grep :')
+    if out.startswith(UNAVAILABLE_ACTION):
+        # --action=tpm_more_status only exists >= 41.
+        logging.info('Method not supported!')
+        return status
+    for line in out.splitlines():
+        items = line.strip().split(':')
+        if items[1].strip() == 'false':
+            value = False
+        elif items[1].strip() == 'true':
+            value = True
+        elif items[1].strip().isdigit():
+            value = int(items[1].strip())
+        else:
+            value = items[1].strip(' "')
+        status[items[0]] = value
+    return status
+
+
+def __run_cmd(cmd):
+    """Run a command on utils.system_output, and append '2>&1'."""
+    return utils.system_output(cmd + ' 2>&1', retain_output=True,
+                               ignore_status=True).strip()