pw_package: Add support for downloading CRLSet

Add support in pw_package for downloading CRLSet. The CRLSet will be
used by the pw_tls_client module for certificate revocation checks.

Bug: 396
Change-Id: I36399334c76a00756ecce826e4fedaac7f45bcb1
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/48460
Commit-Queue: Yecheng Zhao <zyecheng@google.com>
Reviewed-by: Ali Zhang <alizhang@google.com>
diff --git a/pw_package/py/BUILD.gn b/pw_package/py/BUILD.gn
index 834d8fd..6cbcdb6 100644
--- a/pw_package/py/BUILD.gn
+++ b/pw_package/py/BUILD.gn
@@ -26,6 +26,7 @@
     "pw_package/packages/arduino_core.py",
     "pw_package/packages/boringssl.py",
     "pw_package/packages/chromium_verifier.py",
+    "pw_package/packages/crlset.py",
     "pw_package/packages/mbedtls.py",
     "pw_package/packages/nanopb.py",
     "pw_package/packages/stm32cube.py",
diff --git a/pw_package/py/pw_package/packages/crlset.py b/pw_package/py/pw_package/packages/crlset.py
new file mode 100644
index 0000000..2830c57
--- /dev/null
+++ b/pw_package/py/pw_package/packages/crlset.py
@@ -0,0 +1,87 @@
+# Copyright 2021 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""Install and check status of CRLSet and download chromium CRLSet."""
+
+import os
+import pathlib
+import subprocess
+from typing import Sequence
+import pw_package.git_repo
+import pw_package.package_manager
+
+
+def crlset_tools_repo_path(path: pathlib.Path) -> pathlib.Path:
+    return path / 'crlset-tools'
+
+
+def crlset_exec_path(path: pathlib.Path) -> pathlib.Path:
+    return crlset_tools_repo_path(path) / 'crlset'
+
+
+def crlset_file_path(path: pathlib.Path) -> pathlib.Path:
+    return path / 'crlset'
+
+
+class CRLSet(pw_package.package_manager.Package):
+    """Install and check status of CRLSet and downloaded CLRSet file."""
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, name='crlset', **kwargs)
+        self._crlset_tools = pw_package.git_repo.GitRepo(
+            name='crlset-tools',
+            url='https://github.com/agl/crlset-tools.git',
+            commit='1a1019bb500f93bc2b847a57cdbaede847649b99',
+        )
+
+    def status(self, path: pathlib.Path) -> bool:
+        if not self._crlset_tools.status(crlset_tools_repo_path(path)):
+            return False
+
+        # The executable should have been built and exist.
+        if not os.path.exists(crlset_exec_path(path)):
+            return False
+
+        # A crlset has been downloaded
+        if not os.path.exists(crlset_file_path(path)):
+            return False
+
+        return True
+
+    def install(self, path: pathlib.Path) -> None:
+        self._crlset_tools.install(crlset_tools_repo_path(path))
+
+        # Build the go tool
+        subprocess.run(['go', 'build', 'crlset.go'],
+                       check=True,
+                       cwd=crlset_tools_repo_path(path))
+
+        crlset_tools_exec = crlset_exec_path(path)
+        if not os.path.exists(crlset_tools_exec):
+            raise FileNotFoundError('Fail to find crlset executable')
+
+        # Download the latest CRLSet with the go tool
+        with open(crlset_file_path(path), 'wb') as crlset_file:
+            fetched = subprocess.run([crlset_exec_path(path), 'fetch'],
+                                     capture_output=True,
+                                     check=True).stdout
+            crlset_file.write(fetched)
+
+    def info(self, path: pathlib.Path) -> Sequence[str]:
+        return (
+            f'{self.name} installed in: {path}',
+            "Enable by running 'gn args out' and adding this line:",
+            f'  pw_tls_client_CRLSET_PATH = "{crlset_file_path(path)}"',
+        )
+
+
+pw_package.package_manager.register(CRLSet)
diff --git a/pw_package/py/pw_package/pigweed_packages.py b/pw_package/py/pw_package/pigweed_packages.py
index 87af569..d4ec09d 100644
--- a/pw_package/py/pw_package/pigweed_packages.py
+++ b/pw_package/py/pw_package/pigweed_packages.py
@@ -22,6 +22,7 @@
 from pw_package.packages import boringssl  # pylint: disable=unused-import
 from pw_package.packages import chromium_verifier  # pylint: disable=unused-import
 from pw_package.packages import mbedtls  # pylint: disable=unused-import
+from pw_package.packages import crlset  # pylint: disable=unused-import
 
 
 def initialize():