Repository: freedomofpress/securedrop

Issue:
Have securedrop-admin reject Submission Keys with SHA-1 signatures
## Description

securedrop-admin should reject Submission Keys that have SHA-1 signatures

https://sequoia-pgp.org/blog/2023/02/01/202302-happy-sha1-day/

We should install `sq-keyring-linter` as part of the Tails Admin Workstation setup, and then check the key in https://github.com/freedomofpress/securedrop/blob/develop/admin/bin/validate-gpg-key.sh, erroring if it fails the linter



Discussion:
We currently run [all the pubkeys](https://github.com/freedomofpress/securedrop/blob/ba04ad7000f8e11474397dfb054c46e726c0383b/admin/securedrop_admin/__init__.py#L583-L586) (submission key, ossec alert key, journalist key if applicable)  through our validation script.

Question for team: Should we fail on _any_ key using SHA-1 in its binding signature, or just on the Submission Key? 

Resolving patch:
diff --git a/admin/bootstrap.py b/admin/bootstrap.py
--- a/admin/bootstrap.py
+++ b/admin/bootstrap.py
@@ -29,6 +29,16 @@
 DIR = os.path.dirname(os.path.realpath(__file__))
 VENV_DIR = os.path.join(DIR, ".venv3")
 
+# Space-separated list of apt dependencies
+APT_DEPENDENCIES_STR = "python3-virtualenv \
+                   python3-yaml \
+                   python3-pip \
+                   virtualenv \
+                   libffi-dev \
+                   libssl-dev \
+                   libpython3-dev \
+                   sq-keyring-linter"
+
 
 def setup_logger(verbose: bool = False) -> None:
     """Configure logging handler"""
@@ -95,6 +105,37 @@ def checkenv(args: argparse.Namespace) -> None:
         sys.exit(1)
 
 
+def is_missing_dependency() -> bool:
+    """
+    Check if there are any missing apt dependencies.
+    This applies to existing Tails systems where `securedrop-setup` may not have been
+    run recently.
+    """
+    # apt-cache -q0 policy $dependency1 $dependency2 $dependency3 | grep "Installed: (none)"
+    apt_query = f"apt-cache -q0 policy {APT_DEPENDENCIES_STR}".split(" ")
+    grep_command = ["grep", "Installed: (none)"]
+
+    try:
+        sdlog.info("Checking apt dependencies are installed")
+        apt_process = subprocess.Popen(apt_query, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+
+        grep_process = subprocess.Popen(
+            grep_command, stdin=apt_process.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+        )
+
+        # Wait for the process to complete before checking the returncode
+        grep_process.communicate()
+        returncode = grep_process.returncode
+
+        # If the above command returns 0, then one or more packages are not installed.
+        return returncode == 0
+
+    except subprocess.CalledProcessError as e:
+        sdlog.error("Error checking apt dependencies")
+        sdlog.debug(e.output)
+        raise
+
+
 def maybe_torify() -> List[str]:
     if is_tails():
         return ["torify"]
@@ -117,15 +158,8 @@ def install_apt_dependencies(args: argparse.Namespace) -> None:
         "sudo",
         "su",
         "-c",
-        "apt-get update && \
-                   apt-get -q -o=Dpkg::Use-Pty=0 install -y \
-                   python3-virtualenv \
-                   python3-yaml \
-                   python3-pip \
-                   virtualenv \
-                   libffi-dev \
-                   libssl-dev \
-                   libpython3-dev",
+        f"apt-get update && \
+                   apt-get -q -o=Dpkg::Use-Pty=0 install -y {APT_DEPENDENCIES_STR}",
     ]
 
     try:
@@ -133,6 +167,7 @@ def install_apt_dependencies(args: argparse.Namespace) -> None:
         # of progress during long-running command.
         for output_line in run_command(apt_command):
             print(output_line.decode("utf-8").rstrip())
+
     except subprocess.CalledProcessError:
         # Tails supports apt persistence, which was used by SecureDrop
         # under Tails 2.x. If updates are being applied, don't try to pile
@@ -158,11 +193,13 @@ def envsetup(args: argparse.Namespace, virtualenv_dir: str = VENV_DIR) -> None:
     # clean up old Tails venv on major upgrades
     clean_up_old_tails_venv(virtualenv_dir)
 
+    # Check apt dependencies and ensure all are present.
+    if is_missing_dependency():
+        install_apt_dependencies(args)
+
     # virtualenv doesnt exist? Install dependencies and create
     if not os.path.exists(virtualenv_dir):
 
-        install_apt_dependencies(args)
-
         # Technically you can create a virtualenv from within python
         # but pip can only be run over Tor on Tails, and debugging that
         # along with instaling a third-party dependency is not worth
diff --git a/admin/securedrop_admin/__init__.py b/admin/securedrop_admin/__init__.py
--- a/admin/securedrop_admin/__init__.py
+++ b/admin/securedrop_admin/__init__.py
@@ -599,11 +599,23 @@ def validate_gpg_keys(self) -> bool:
                 )
             except subprocess.CalledProcessError as e:
                 sdlog.debug(e.output)
-                raise FingerprintException(
-                    f"fingerprint {fingerprint} "
-                    + "does not match "
-                    + f"the public key {public_key}"
-                )
+                message = f"{fingerprint}: Fingerprint validation failed"
+
+                # The validation script returns different error codes depending on what
+                # the cause of the validation failure was. See `admin/bin/validate-gpg-key.sh`
+                if e.returncode == 1:
+                    message = (
+                        f"fingerprint {fingerprint} does not match "
+                        + f"the public key {public_key}"
+                    )
+                elif e.returncode == 2:
+                    message = (
+                        f"fingerprint {fingerprint} "
+                        + "failed sq-keyring-linter check. You may be using an older key that "
+                        + "needs to be updated. Please contact your SecureDrop administrator, or "
+                        + "https://support.freedom.press for assistance."
+                    )
+                raise FingerprintException(message)
         return True
 
     def validate_journalist_alert_email(self) -> bool:


Test patch:
diff --git a/admin/tests/files/ossec.pub b/admin/tests/files/ossec.pub
--- a/admin/tests/files/ossec.pub
+++ b/admin/tests/files/ossec.pub
@@ -1,5 +1,4 @@
 -----BEGIN PGP PUBLIC KEY BLOCK-----
-Version: GnuPG v2.0.19 (GNU/Linux)
 
 mQINBFJZi2ABEACZJJA53+pEAdkZyD99nxB995ZVTBw60SQ/6E/gws4kInv+YS7t
 wSMXGa5bR4SD9voWxzLgyulqbM93jUFKn5GcsSh2O/lxAvEDKsPmXCRP1eBg3pjU
@@ -25,28 +24,56 @@ iNl4NCgd26iRgTLhfMXpTjRyOb2RvFdzLByDEWIbvu5kCh247UFYSL0llk+suNh3
 cm0mOUdL1nZuEo4EyEF1dq+1opMfDMF98q0660wZdwvwUQIXBt/yK3FH0BGA66ai
 R78Z4pH1JqtYvzfDJx+XP8O2N9GYGd7kpak/5C2BTJzLVyzagB1yi8SmiYna5yQj
 EqW5Txeq0GGd2H4KtUETUevU4x0Rw3luHToaDd9d5sioF48o87PlGwk+OCofPfLj
-LnwFPNZcuQINBFJZi2ABEADzfv+9Ogb4KEWFom9zMF+xg8bcd/Ct72/sWLQW6Pz6
-+SkmLEHuklTO+k7xiQ6jdzXzj1rTfy317L7G51naBSb6Ekfv8mu2ogOwrvtgYnGC
-vfCpooUSxcfi+aEJzIJL29TAi1RCLZm15KRbkvEl8wS93BSLiag5w4/8eP1vXebq
-95GrCZwiNZdhdQs3qn4j3VRvTW/SZHIAdJY+mMfUMPjq4c4sA82os6kVrEnWeLGf
-T9d+knfm9J/2Rumy90bLAY6SFmRZ9/DxwKwbIsVy8CRvU3RVFSX8HCBQepRCQkls
-9r7KVBqYE2Wh+0a+9wHHHNI7VBxKGXPflrirxY1AB5vjLcX1hmXbCoyf4ytgdHyC
-KDz9Oc+xkgJeyVW6XwSqc5EhuNFXp3+C7BF7eQZ1REJLbL6CtEkeF0jHBaTeKM/p
-N4fVhjPiU/FsNmZGKxxLyxDnnDI5pY8bhphVxwBRZ5GtVNqiVNDw+rRACQalpT21
-OcAgLP+Rz+qf3TPyEZN6WPEx8/76ILuSHb8mpOH7W/514f5NuFaAlgmUnO3cT10h
-h4IwOQ+kvj0qMww8fASI9DJExXUYb3xDSCmOkJPhu1/Drr3gdFBha4/jAz7jBWls
-Vr2RLJzilf8Mi9j8WpHIfP+WXtwWz3+iYPS0SPoB7g9DA0+Ei760pJJf73AEjD+f
-FwARAQABiQIfBBgBAgAJBQJSWYtgAhsMAAoJEMxA7xIoJxRBp/cP/3lJx9z5yzZA
-6UvLQR6pK+V1iy2hvZ+S+EwYRCiTgYTXekHzLXWwjWGfUYDTHMeaS9O9BMRMGOU3
-inyb47GZSoQ0N0bRVTzrY6/0ifhUSJ00MemOodI1bz4pAMk3uR8iWyhlaGn7JAIA
-KmCm+K0qkeJd61S9iyrx7s9QmaNPnupm5pc+bpOAkbKyq7sEFpWM5Qx82n1tVMtn
-IW2OoRPbz80JkkQB2pl6SjskXqZ89jcFWGI6IChYENKc65xafDt4uFuHU+5j4j2f
-4ySYSwfoWC97MOgJLqA/WimxeeNCYFhykUDWrL5mKBTgMXgH/sYk3GDo7fssaYbK
-n1xbbX4GXQl3+ru4zT6/F7CxZErjLb+evShyf4itM+5AdbKRiRzoraqKblBa4TfJ
-BSqHisdcxdZeBe19+jyY6a8ZMcGhrQeksiKxTRh7ylAk7CLVgLEIHLxXzHoZ0oAF
-z2ulG+zH9KS9Pe8MQxHCrlyfoQElQuJoYbrYBOu28itvGPgz6+5xgvZROvPoqIkI
-k8DYt9lJqUFBeZuFJd5W1TuHKLxueVYvSKeG+e3TjOYdJFvDZInM4cNWr8N92mYS
-iphljiHAKVTQeIf1ma07QUH/ul3YC+g07F+BLonIIXA6uQVebv5iLxTgOzIQwHTJ
-Vu4MPiQNn1h4dk1RonfV/aJ+de1+qjA8
-=XVz8
+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
+caA76EYZ2Htu8g==
+=LPJh
 -----END PGP PUBLIC KEY BLOCK-----
diff --git a/admin/tests/files/test_journalist_key.pub b/admin/tests/files/test_journalist_key.pub
--- a/admin/tests/files/test_journalist_key.pub
+++ b/admin/tests/files/test_journalist_key.pub
@@ -1,5 +1,4 @@
 -----BEGIN PGP PUBLIC KEY BLOCK-----
-Version: GnuPG v2.0.19 (GNU/Linux)
 
 mQINBFJZi2ABEACZJJA53+pEAdkZyD99nxB995ZVTBw60SQ/6E/gws4kInv+YS7t
 wSMXGa5bR4SD9voWxzLgyulqbM93jUFKn5GcsSh2O/lxAvEDKsPmXCRP1eBg3pjU
@@ -25,28 +24,56 @@ iNl4NCgd26iRgTLhfMXpTjRyOb2RvFdzLByDEWIbvu5kCh247UFYSL0llk+suNh3
 cm0mOUdL1nZuEo4EyEF1dq+1opMfDMF98q0660wZdwvwUQIXBt/yK3FH0BGA66ai
 R78Z4pH1JqtYvzfDJx+XP8O2N9GYGd7kpak/5C2BTJzLVyzagB1yi8SmiYna5yQj
 EqW5Txeq0GGd2H4KtUETUevU4x0Rw3luHToaDd9d5sioF48o87PlGwk+OCofPfLj
-LnwFPNZcuQINBFJZi2ABEADzfv+9Ogb4KEWFom9zMF+xg8bcd/Ct72/sWLQW6Pz6
-+SkmLEHuklTO+k7xiQ6jdzXzj1rTfy317L7G51naBSb6Ekfv8mu2ogOwrvtgYnGC
-vfCpooUSxcfi+aEJzIJL29TAi1RCLZm15KRbkvEl8wS93BSLiag5w4/8eP1vXebq
-95GrCZwiNZdhdQs3qn4j3VRvTW/SZHIAdJY+mMfUMPjq4c4sA82os6kVrEnWeLGf
-T9d+knfm9J/2Rumy90bLAY6SFmRZ9/DxwKwbIsVy8CRvU3RVFSX8HCBQepRCQkls
-9r7KVBqYE2Wh+0a+9wHHHNI7VBxKGXPflrirxY1AB5vjLcX1hmXbCoyf4ytgdHyC
-KDz9Oc+xkgJeyVW6XwSqc5EhuNFXp3+C7BF7eQZ1REJLbL6CtEkeF0jHBaTeKM/p
-N4fVhjPiU/FsNmZGKxxLyxDnnDI5pY8bhphVxwBRZ5GtVNqiVNDw+rRACQalpT21
-OcAgLP+Rz+qf3TPyEZN6WPEx8/76ILuSHb8mpOH7W/514f5NuFaAlgmUnO3cT10h
-h4IwOQ+kvj0qMww8fASI9DJExXUYb3xDSCmOkJPhu1/Drr3gdFBha4/jAz7jBWls
-Vr2RLJzilf8Mi9j8WpHIfP+WXtwWz3+iYPS0SPoB7g9DA0+Ei760pJJf73AEjD+f
-FwARAQABiQIfBBgBAgAJBQJSWYtgAhsMAAoJEMxA7xIoJxRBp/cP/3lJx9z5yzZA
-6UvLQR6pK+V1iy2hvZ+S+EwYRCiTgYTXekHzLXWwjWGfUYDTHMeaS9O9BMRMGOU3
-inyb47GZSoQ0N0bRVTzrY6/0ifhUSJ00MemOodI1bz4pAMk3uR8iWyhlaGn7JAIA
-KmCm+K0qkeJd61S9iyrx7s9QmaNPnupm5pc+bpOAkbKyq7sEFpWM5Qx82n1tVMtn
-IW2OoRPbz80JkkQB2pl6SjskXqZ89jcFWGI6IChYENKc65xafDt4uFuHU+5j4j2f
-4ySYSwfoWC97MOgJLqA/WimxeeNCYFhykUDWrL5mKBTgMXgH/sYk3GDo7fssaYbK
-n1xbbX4GXQl3+ru4zT6/F7CxZErjLb+evShyf4itM+5AdbKRiRzoraqKblBa4TfJ
-BSqHisdcxdZeBe19+jyY6a8ZMcGhrQeksiKxTRh7ylAk7CLVgLEIHLxXzHoZ0oAF
-z2ulG+zH9KS9Pe8MQxHCrlyfoQElQuJoYbrYBOu28itvGPgz6+5xgvZROvPoqIkI
-k8DYt9lJqUFBeZuFJd5W1TuHKLxueVYvSKeG+e3TjOYdJFvDZInM4cNWr8N92mYS
-iphljiHAKVTQeIf1ma07QUH/ul3YC+g07F+BLonIIXA6uQVebv5iLxTgOzIQwHTJ
-Vu4MPiQNn1h4dk1RonfV/aJ+de1+qjA8
-=XVz8
+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
+caA76EYZ2Htu8g==
+=LPJh
 -----END PGP PUBLIC KEY BLOCK-----
diff --git a/admin/tests/files/weak_test_key_should_fail_sqlinter.asc b/admin/tests/files/weak_test_key_should_fail_sqlinter.asc
new file mode 100644
--- /dev/null
+++ b/admin/tests/files/weak_test_key_should_fail_sqlinter.asc
@@ -0,0 +1,20 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mI0EZOPgWwEEALRtUxob9g8IYRBVGZfsIM79e7OuqCUNnnpcNkNZ4tiKyd8yjjfD
+2t4OZ5WAv7VuseDThwGnDoCUJ3ZZXtKTtJITtYvtHsQox3BZoz5wSWRSJDO8npKU
+Zv0j7Dy8uqv0n69J402+3Fq9mELyekH9/j29UqLdUTzRQgH+ZkXAH27DABEBAAG0
+O0JBREtFWSAoU2hvdWxkIGZhaWwgc3Eta2V5cmluZy1saW50ZXIpIDxCQURLRVlA
+Tk9SRVBMWS5ORVQ+iM4EEwEKADgWIQRA8cF7fngm2rQLFK53hrAA5tCnbgUCZOPg
+WwIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRB3hrAA5tCnboiIA/oDjkPm
+fhg9/2XdylpdB0WhY55vNLweSyQW6tWEgoL4IMl2HJx38WiMsNbqgWfsw/cLHrRW
+eQfPJiB4n8jbucSFmutO9nlKT+yN/G1eVrSikz0nZ4OlLtW5Jy2T70LVzo3Lb6fE
+Ve9zkVp+AaOxgsxlLj8aEX8E9tdD7EdmMKSJIbiNBGTj4FsBBACt+L8aGNutQfqK
+iqqtwncUGBWdXNZgy+2SCmNF6QGwj0m8AlgBjERfbeBcYo3Mw2PIPM1r5UlXFiMy
+blF32L7kZGxy5ETYaADHilGoJHCubtpBH4hDRsmt9OKydFSQvE01+CHLmAfiXBzx
+KK48B2nVTseLIPhdxOW15GGd9QwLNwARAQABiLYEGAEKACAWIQRA8cF7fngm2rQL
+FK53hrAA5tCnbgUCZOPgWwIbDAAKCRB3hrAA5tCnbqQ1A/9JSda8nzav4lgBw8co
+dbB0s9AdvGymtlTWLUFdfHRaNYwHInUtXIagDhgJNLaa75xd/WNLvvjPcV3SoaOC
+hGVDM/BkMb87VxjeYgHzKdN5MxMrPvITS5Y0EGB0ITvG1MTqHWalhY99pyRqeCRA
+2BWLtQSMMPWQxML48db4EtfTQw==
+=rpbe
+-----END PGP PUBLIC KEY BLOCK-----
\ No newline at end of file
diff --git a/admin/tests/test_securedrop-admin.py b/admin/tests/test_securedrop-admin.py
--- a/admin/tests/test_securedrop-admin.py
+++ b/admin/tests/test_securedrop-admin.py
@@ -716,6 +716,21 @@ def test_validate_gpg_key(self, tmpdir, caplog):
                 site_config.validate_gpg_keys()
             assert "FAIL does not match" in str(e)
 
+        # Test a key with matching fingerprint but that fails sq-keyring-linter
+        invalid_config = {
+            # Correct key fingerprint but weak 1024-bit RSA key with SHA-1 self signature
+            "securedrop_app_gpg_public_key": "weak_test_key_should_fail_sqlinter.asc",
+            "securedrop_app_gpg_fingerprint": "40F1C17B7E7826DAB40B14AE7786B000E6D0A76E",
+            "ossec_alert_gpg_public_key": "test_journalist_key.pub",
+            "ossec_gpg_fpr": "65A1B5FF195B56353CC63DFFCC40EF1228271441",
+            "journalist_alert_gpg_public_key": "test_journalist_key.pub",
+            "journalist_gpg_fpr": "65A1B5FF195B56353CC63DFFCC40EF1228271441",
+        }
+        site_config.config = invalid_config
+        with pytest.raises(securedrop_admin.FingerprintException) as e:
+            site_config.validate_gpg_keys()
+        assert "failed sq-keyring-linter check" in str(e)
+
     def test_journalist_alert_email(self, tmpdir):
         args = argparse.Namespace(
             site_config="INVALID",
diff --git a/install_files/ansible-base/roles/app/files/test_journalist_key.pub b/install_files/ansible-base/roles/app/files/test_journalist_key.pub
--- a/install_files/ansible-base/roles/app/files/test_journalist_key.pub
+++ b/install_files/ansible-base/roles/app/files/test_journalist_key.pub
@@ -1,5 +1,4 @@
 -----BEGIN PGP PUBLIC KEY BLOCK-----
-Version: GnuPG v2.0.19 (GNU/Linux)
 
 mQINBFJZi2ABEACZJJA53+pEAdkZyD99nxB995ZVTBw60SQ/6E/gws4kInv+YS7t
 wSMXGa5bR4SD9voWxzLgyulqbM93jUFKn5GcsSh2O/lxAvEDKsPmXCRP1eBg3pjU
@@ -25,28 +24,56 @@ iNl4NCgd26iRgTLhfMXpTjRyOb2RvFdzLByDEWIbvu5kCh247UFYSL0llk+suNh3
 cm0mOUdL1nZuEo4EyEF1dq+1opMfDMF98q0660wZdwvwUQIXBt/yK3FH0BGA66ai
 R78Z4pH1JqtYvzfDJx+XP8O2N9GYGd7kpak/5C2BTJzLVyzagB1yi8SmiYna5yQj
 EqW5Txeq0GGd2H4KtUETUevU4x0Rw3luHToaDd9d5sioF48o87PlGwk+OCofPfLj
-LnwFPNZcuQINBFJZi2ABEADzfv+9Ogb4KEWFom9zMF+xg8bcd/Ct72/sWLQW6Pz6
-+SkmLEHuklTO+k7xiQ6jdzXzj1rTfy317L7G51naBSb6Ekfv8mu2ogOwrvtgYnGC
-vfCpooUSxcfi+aEJzIJL29TAi1RCLZm15KRbkvEl8wS93BSLiag5w4/8eP1vXebq
-95GrCZwiNZdhdQs3qn4j3VRvTW/SZHIAdJY+mMfUMPjq4c4sA82os6kVrEnWeLGf
-T9d+knfm9J/2Rumy90bLAY6SFmRZ9/DxwKwbIsVy8CRvU3RVFSX8HCBQepRCQkls
-9r7KVBqYE2Wh+0a+9wHHHNI7VBxKGXPflrirxY1AB5vjLcX1hmXbCoyf4ytgdHyC
-KDz9Oc+xkgJeyVW6XwSqc5EhuNFXp3+C7BF7eQZ1REJLbL6CtEkeF0jHBaTeKM/p
-N4fVhjPiU/FsNmZGKxxLyxDnnDI5pY8bhphVxwBRZ5GtVNqiVNDw+rRACQalpT21
-OcAgLP+Rz+qf3TPyEZN6WPEx8/76ILuSHb8mpOH7W/514f5NuFaAlgmUnO3cT10h
-h4IwOQ+kvj0qMww8fASI9DJExXUYb3xDSCmOkJPhu1/Drr3gdFBha4/jAz7jBWls
-Vr2RLJzilf8Mi9j8WpHIfP+WXtwWz3+iYPS0SPoB7g9DA0+Ei760pJJf73AEjD+f
-FwARAQABiQIfBBgBAgAJBQJSWYtgAhsMAAoJEMxA7xIoJxRBp/cP/3lJx9z5yzZA
-6UvLQR6pK+V1iy2hvZ+S+EwYRCiTgYTXekHzLXWwjWGfUYDTHMeaS9O9BMRMGOU3
-inyb47GZSoQ0N0bRVTzrY6/0ifhUSJ00MemOodI1bz4pAMk3uR8iWyhlaGn7JAIA
-KmCm+K0qkeJd61S9iyrx7s9QmaNPnupm5pc+bpOAkbKyq7sEFpWM5Qx82n1tVMtn
-IW2OoRPbz80JkkQB2pl6SjskXqZ89jcFWGI6IChYENKc65xafDt4uFuHU+5j4j2f
-4ySYSwfoWC97MOgJLqA/WimxeeNCYFhykUDWrL5mKBTgMXgH/sYk3GDo7fssaYbK
-n1xbbX4GXQl3+ru4zT6/F7CxZErjLb+evShyf4itM+5AdbKRiRzoraqKblBa4TfJ
-BSqHisdcxdZeBe19+jyY6a8ZMcGhrQeksiKxTRh7ylAk7CLVgLEIHLxXzHoZ0oAF
-z2ulG+zH9KS9Pe8MQxHCrlyfoQElQuJoYbrYBOu28itvGPgz6+5xgvZROvPoqIkI
-k8DYt9lJqUFBeZuFJd5W1TuHKLxueVYvSKeG+e3TjOYdJFvDZInM4cNWr8N92mYS
-iphljiHAKVTQeIf1ma07QUH/ul3YC+g07F+BLonIIXA6uQVebv5iLxTgOzIQwHTJ
-Vu4MPiQNn1h4dk1RonfV/aJ+de1+qjA8
-=XVz8
+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
+caA76EYZ2Htu8g==
+=LPJh
 -----END PGP PUBLIC KEY BLOCK-----