diff --git a/conda_content_trust/root_signing.py b/conda_content_trust/root_signing.py index a77e792..15191ce 100644 --- a/conda_content_trust/root_signing.py +++ b/conda_content_trust/root_signing.py @@ -30,7 +30,7 @@ from securesystemslib.gpg import functions as gpg_funcs SSLIB_AVAILABLE = True -except ImportError: +except ImportError: # pragma: no cover SSLIB_AVAILABLE = False from .common import ( @@ -234,11 +234,7 @@ def sign_root_metadata_dict_via_gpg(root_signable, gpg_key_fingerprint): # Make sure it's the right format. if not is_a_signable(root_signable): - raise TypeError( - "Expected a signable dictionary; the given file " - + str(root_md_fname) - + " failed the check." - ) + raise TypeError("Expected a signable dictionary.") # TODO: Add root-specific checks. # Canonicalize and serialize the data, putting it in the form we expect to @@ -341,42 +337,6 @@ def fetch_keyval_from_gpg(fingerprint): return key_parameters["keyval"]["public"]["q"] -def _verify_gpg_sig_using_ssl(signature, gpg_key_fingerprint, key_value, data): - """ - THIS IS PROVIDED ONLY FOR TESTING PURPOSES. - We will verify signatures using our own code in conda_content_trust.authentication, not - by using the securesystemslib.gpg.functions.verify_signature call that - sits here. - - Wraps securesystemslib.gpg.functions.verify_signature. to format the - arguments in a manner ssl will like (i.e. conforming to - securesystemslib.formats.GPG_SIGNATURE_SCHEMA). - """ - if not SSLIB_AVAILABLE: - # TODO✅: Consider a missing-optional-dependency exception class. - raise Exception( - "verifygpg_sig_using_ssl requires the securesystemslib " - "library, which appears to be unavailable." - ) - - checkformat_key(key_value) - - # This function validates these two args in the process of formatting them. - ssl_format_key = gpg_pubkey_in_ssl_format(gpg_key_fingerprint, key_value) - - securesystemslib.formats.GPG_SIGNATURE_SCHEMA.check_match(signature) - securesystemslib.formats._GPG_ED25519_PUBKEY_SCHEMA.check_match(ssl_format_key) - - # TODO: ✅ Validate sig (ssl-format gpg sig dict) and content (bytes). - - # Note: if we change the signature format to deviate from what ssl uses, - # then we need to correct it here if we're going to use ssl. - - validity = gpg_funcs.verify_signature(signature, ssl_format_key, data) - - return validity - - def _gpg_pubkey_in_ssl_format(fingerprint, q): """ THIS IS PROVIDED ONLY FOR TESTING PURPOSES. diff --git a/tests/test_root.py b/tests/test_root.py index 1ca44a5..383f2a6 100644 --- a/tests/test_root.py +++ b/tests/test_root.py @@ -13,6 +13,7 @@ pytest tests/test_root.py """ import copy +import json import pytest @@ -118,6 +119,7 @@ def test_gpg_signing_with_unknown_fingerprint(): # testing suite we're using provides. try: gpg_sig = root_signing.sign_via_gpg(b"1234", SAMPLE_UNKNOWN_FINGERPRINT) + assert gpg_sig # minimal "not empty" check except securesystemslib.gpg.exceptions.CommandError as e: # TODO✅: This is a clumsy check. It's a shame we don't get better # than CommandError(), but this will do for now. @@ -164,6 +166,11 @@ def test_root_gen_sign_verify(): gpg_sig = root_signing.sign_via_gpg(canonical_signed_portion, SAMPLE_FINGERPRINT) + gpg_sig_with_fingerprint = root_signing.sign_via_gpg( + canonical_signed_portion, SAMPLE_FINGERPRINT, include_fingerprint=True + ) + assert "see_also" in gpg_sig_with_fingerprint + signed_rmd = copy.deepcopy(rmd) signed_rmd["signatures"][SAMPLE_KEYVAL] = gpg_sig @@ -259,3 +266,41 @@ def test_verify_existing_root_md(): # TODO ✅: Add a v2 of root to this test, and verify static v2 via v1 as # well. Also add failure modes (verifying valid v2 using v0 # expectations.) + + +def test_no_sslib(mocker): + """ + Coverage for "we don't have sslib" exceptions. + """ + mocker.patch("conda_content_trust.root_signing.SSLIB_AVAILABLE", False) + with pytest.raises(Exception, match="securesystemslib"): + root_signing.sign_via_gpg(None, None) # type: ignore + with pytest.raises(Exception, match="securesystemslib"): + root_signing.sign_root_metadata_dict_via_gpg(None, None) # type: ignore + with pytest.raises(Exception, match="securesystemslib"): + root_signing.fetch_keyval_from_gpg(None) # type: ignore + + +def test_sign_root_metadata_dict_via_gpg(): + with pytest.raises(TypeError, match="signable"): + root_signing.sign_root_metadata_dict_via_gpg({}, "") + + signable = signing.wrap_as_signable({}) + root_signing.sign_root_metadata_dict_via_gpg(signable, SAMPLE_FINGERPRINT) + + +def test_sign_root_metadata_via_gpg(tmp_path): + md_path = tmp_path / "metadata.json" + signable = signing.wrap_as_signable({}) + md_path.write_text(json.dumps(signable)) + root_signing.sign_root_metadata_via_gpg(md_path, SAMPLE_FINGERPRINT) + signed = json.loads(md_path.read_text()) + assert signed != signable # at least it was updated? + + +def test_gpg_pubkey_in_ssl_format(): + sample_pubkey = "0f" * 32 + pubkey_formatted = root_signing._gpg_pubkey_in_ssl_format( + SAMPLE_FINGERPRINT, sample_pubkey + ) + assert pubkey_formatted["keyval"]["public"]["q"] == sample_pubkey