chore(keyringctl): increase test coverage and fix trust expectations
This commit is contained in:
parent
7513e71b3f
commit
cd585f4be2
@ -196,7 +196,9 @@ def convert_certificate( # noqa: ignore=C901
|
|||||||
if signature_type == "CertificationRevocation":
|
if signature_type == "CertificationRevocation":
|
||||||
revocations[current_packet_uid][issuer].append(packet)
|
revocations[current_packet_uid][issuer].append(packet)
|
||||||
elif signature_type.endswith("Certification"):
|
elif signature_type.endswith("Certification"):
|
||||||
if fingerprint_filter is not None and any([fp.endswith(issuer) for fp in fingerprint_filter]):
|
# TODO: extend fp filter to all certifications
|
||||||
|
# TODO: use contains_fingerprint
|
||||||
|
if fingerprint_filter is None or any([fp.endswith(issuer) for fp in fingerprint_filter]):
|
||||||
debug(f"The certification by issuer {issuer} is appended as it is found in the filter.")
|
debug(f"The certification by issuer {issuer} is appended as it is found in the filter.")
|
||||||
certifications[current_packet_uid][issuer].append(packet)
|
certifications[current_packet_uid][issuer].append(packet)
|
||||||
else:
|
else:
|
||||||
@ -587,7 +589,7 @@ def convert(
|
|||||||
return target_dir
|
return target_dir
|
||||||
|
|
||||||
|
|
||||||
def export_ownertrust(certs: List[Path], output: Path) -> List[Fingerprint]:
|
def export_ownertrust(certs: List[Path], keyring_root: Path, output: Path) -> List[Fingerprint]:
|
||||||
"""Export ownertrust from a set of keys and return the trusted and revoked fingerprints
|
"""Export ownertrust from a set of keys and return the trusted and revoked fingerprints
|
||||||
|
|
||||||
The output file format is compatible with `gpg --import-ownertrust` and lists the main fingerprint ID of all
|
The output file format is compatible with `gpg --import-ownertrust` and lists the main fingerprint ID of all
|
||||||
@ -598,6 +600,7 @@ def export_ownertrust(certs: List[Path], output: Path) -> List[Fingerprint]:
|
|||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
certs: The certificates to trust
|
certs: The certificates to trust
|
||||||
|
keyring_root: The keyring root directory to get all accepted fingerprints from
|
||||||
output: The file path to write to
|
output: The file path to write to
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
@ -605,7 +608,11 @@ def export_ownertrust(certs: List[Path], output: Path) -> List[Fingerprint]:
|
|||||||
List of ownertrust fingerprints
|
List of ownertrust fingerprints
|
||||||
"""
|
"""
|
||||||
|
|
||||||
main_trusts = certificate_trust_from_paths(sources=certs, main_keys=get_fingerprints_from_paths(sources=certs))
|
main_trusts = certificate_trust_from_paths(
|
||||||
|
sources=certs,
|
||||||
|
main_keys=get_fingerprints_from_paths(sources=certs),
|
||||||
|
all_fingerprints=get_fingerprints_from_paths([keyring_root]),
|
||||||
|
)
|
||||||
trusted_certs: List[Fingerprint] = filter_fingerprints_by_trust(main_trusts, Trust.full)
|
trusted_certs: List[Fingerprint] = filter_fingerprints_by_trust(main_trusts, Trust.full)
|
||||||
|
|
||||||
with open(file=output, mode="w") as trusted_certs_file:
|
with open(file=output, mode="w") as trusted_certs_file:
|
||||||
@ -616,7 +623,7 @@ def export_ownertrust(certs: List[Path], output: Path) -> List[Fingerprint]:
|
|||||||
return trusted_certs
|
return trusted_certs
|
||||||
|
|
||||||
|
|
||||||
def export_revoked(certs: List[Path], main_keys: Set[Fingerprint], output: Path) -> None:
|
def export_revoked(certs: List[Path], keyring_root: Path, main_keys: Set[Fingerprint], output: Path) -> None:
|
||||||
"""Export the PGP revoked status from a set of keys
|
"""Export the PGP revoked status from a set of keys
|
||||||
|
|
||||||
The output file contains the fingerprints of all self-revoked keys and all keys for which at least two revocations
|
The output file contains the fingerprints of all self-revoked keys and all keys for which at least two revocations
|
||||||
@ -627,11 +634,16 @@ def export_revoked(certs: List[Path], main_keys: Set[Fingerprint], output: Path)
|
|||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
certs: A list of directories with keys to check for their revocation status
|
certs: A list of directories with keys to check for their revocation status
|
||||||
|
keyring_root: The keyring root directory to get all accepted fingerprints from
|
||||||
main_keys: A list of strings representing the fingerprints of (current and/or revoked) main keys
|
main_keys: A list of strings representing the fingerprints of (current and/or revoked) main keys
|
||||||
output: The file path to write to
|
output: The file path to write to
|
||||||
"""
|
"""
|
||||||
|
|
||||||
certificate_trusts = certificate_trust_from_paths(sources=certs, main_keys=main_keys)
|
certificate_trusts = certificate_trust_from_paths(
|
||||||
|
sources=certs,
|
||||||
|
main_keys=main_keys,
|
||||||
|
all_fingerprints=get_fingerprints_from_paths([keyring_root]),
|
||||||
|
)
|
||||||
revoked_certs: List[Fingerprint] = filter_fingerprints_by_trust(certificate_trusts, Trust.revoked)
|
revoked_certs: List[Fingerprint] = filter_fingerprints_by_trust(certificate_trusts, Trust.revoked)
|
||||||
|
|
||||||
with open(file=output, mode="w") as revoked_certs_file:
|
with open(file=output, mode="w") as revoked_certs_file:
|
||||||
@ -839,10 +851,12 @@ def build(
|
|||||||
|
|
||||||
trusted_main_keys = export_ownertrust(
|
trusted_main_keys = export_ownertrust(
|
||||||
certs=[keyring_root / "main"],
|
certs=[keyring_root / "main"],
|
||||||
|
keyring_root=keyring_root,
|
||||||
output=target_dir / "archlinux-trusted",
|
output=target_dir / "archlinux-trusted",
|
||||||
)
|
)
|
||||||
export_revoked(
|
export_revoked(
|
||||||
certs=[keyring_root],
|
certs=[keyring_root],
|
||||||
|
keyring_root=keyring_root,
|
||||||
main_keys=set(trusted_main_keys),
|
main_keys=set(trusted_main_keys),
|
||||||
output=target_dir / "archlinux-revoked",
|
output=target_dir / "archlinux-revoked",
|
||||||
)
|
)
|
||||||
@ -877,7 +891,9 @@ def list_keyring(keyring_root: Path, sources: Optional[List[Path]] = None, main_
|
|||||||
for certificate in sources:
|
for certificate in sources:
|
||||||
username: Username = Username(certificate.parent.name)
|
username: Username = Username(certificate.parent.name)
|
||||||
trust = certificate_trust(
|
trust = certificate_trust(
|
||||||
certificate=certificate, main_keys=get_fingerprints_from_paths([keyring_root / "main"])
|
certificate=certificate,
|
||||||
|
main_keys=get_fingerprints_from_paths([keyring_root / "main"]),
|
||||||
|
all_fingerprints=get_fingerprints_from_paths([keyring_root]),
|
||||||
)
|
)
|
||||||
trust_label = format_trust_label(trust=trust)
|
trust_label = format_trust_label(trust=trust)
|
||||||
print(f"{username:<{username_length}} {certificate.name} {trust_label}")
|
print(f"{username:<{username_length}} {certificate.name} {trust_label}")
|
||||||
|
@ -4,6 +4,7 @@ from logging import debug
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
|
from typing import Optional
|
||||||
from typing import Set
|
from typing import Set
|
||||||
|
|
||||||
from .types import Color
|
from .types import Color
|
||||||
@ -12,9 +13,12 @@ from .types import Trust
|
|||||||
from .types import Uid
|
from .types import Uid
|
||||||
from .util import contains_fingerprint
|
from .util import contains_fingerprint
|
||||||
from .util import get_cert_paths
|
from .util import get_cert_paths
|
||||||
|
from .util import get_fingerprint_from_partial
|
||||||
|
|
||||||
|
|
||||||
def certificate_trust_from_paths(sources: Iterable[Path], main_keys: Set[Fingerprint]) -> Dict[Fingerprint, Trust]:
|
def certificate_trust_from_paths(
|
||||||
|
sources: Iterable[Path], main_keys: Set[Fingerprint], all_fingerprints: Set[Fingerprint]
|
||||||
|
) -> Dict[Fingerprint, Trust]:
|
||||||
"""Get the trust status of all certificates in a list of paths given by main keys.
|
"""Get the trust status of all certificates in a list of paths given by main keys.
|
||||||
|
|
||||||
Uses `get_get_certificate_trust` to determine the trust status.
|
Uses `get_get_certificate_trust` to determine the trust status.
|
||||||
@ -23,6 +27,7 @@ def certificate_trust_from_paths(sources: Iterable[Path], main_keys: Set[Fingerp
|
|||||||
----------
|
----------
|
||||||
sources: Certificates to acquire the trust status from
|
sources: Certificates to acquire the trust status from
|
||||||
main_keys: Fingerprints of trusted keys used to calculate the trust of the certificates from sources
|
main_keys: Fingerprints of trusted keys used to calculate the trust of the certificates from sources
|
||||||
|
all_fingerprints: Fingerprints of all certificates, packager and main, to look up key-ids to full fingerprints
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
@ -34,11 +39,15 @@ def certificate_trust_from_paths(sources: Iterable[Path], main_keys: Set[Fingerp
|
|||||||
|
|
||||||
for certificate in sorted(sources):
|
for certificate in sorted(sources):
|
||||||
fingerprint = Fingerprint(certificate.name)
|
fingerprint = Fingerprint(certificate.name)
|
||||||
certificate_trusts[fingerprint] = certificate_trust(certificate=certificate, main_keys=main_keys)
|
certificate_trusts[fingerprint] = certificate_trust(
|
||||||
|
certificate=certificate, main_keys=main_keys, all_fingerprints=all_fingerprints
|
||||||
|
)
|
||||||
return certificate_trusts
|
return certificate_trusts
|
||||||
|
|
||||||
|
|
||||||
def certificate_trust(certificate: Path, main_keys: Set[Fingerprint]) -> Trust: # noqa: ignore=C901
|
def certificate_trust( # noqa: ignore=C901
|
||||||
|
certificate: Path, main_keys: Set[Fingerprint], all_fingerprints: Set[Fingerprint]
|
||||||
|
) -> Trust:
|
||||||
"""Get the trust status of a certificates given by main keys.
|
"""Get the trust status of a certificates given by main keys.
|
||||||
|
|
||||||
main certificates are:
|
main certificates are:
|
||||||
@ -67,6 +76,7 @@ def certificate_trust(certificate: Path, main_keys: Set[Fingerprint]) -> Trust:
|
|||||||
----------
|
----------
|
||||||
certificate: Certificate to acquire the trust status from
|
certificate: Certificate to acquire the trust status from
|
||||||
main_keys: Fingerprints of trusted keys used to calculate the trust of the certificates from sources
|
main_keys: Fingerprints of trusted keys used to calculate the trust of the certificates from sources
|
||||||
|
all_fingerprints: Fingerprints of all certificates, packager and main, to look up key-ids to full fingerprints
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
@ -78,8 +88,11 @@ def certificate_trust(certificate: Path, main_keys: Set[Fingerprint]) -> Trust:
|
|||||||
revocations: Set[Fingerprint] = set()
|
revocations: Set[Fingerprint] = set()
|
||||||
# TODO: what about direct key revocations/signatures?
|
# TODO: what about direct key revocations/signatures?
|
||||||
for revocation in certificate.glob("revocation/*.asc"):
|
for revocation in certificate.glob("revocation/*.asc"):
|
||||||
issuer: Fingerprint = Fingerprint(revocation.stem)
|
issuer: Optional[Fingerprint] = get_fingerprint_from_partial(all_fingerprints, Fingerprint(revocation.stem))
|
||||||
if fingerprint.endswith(issuer):
|
if not issuer:
|
||||||
|
raise Exception(f"Unknown issuer: {issuer}")
|
||||||
|
if not fingerprint.endswith(issuer):
|
||||||
|
raise Exception(f"Wrong root revocation issuer: {issuer}, expected: {fingerprint}")
|
||||||
debug(f"Revoking {fingerprint} due to self-revocation")
|
debug(f"Revoking {fingerprint} due to self-revocation")
|
||||||
revocations.add(fingerprint)
|
revocations.add(fingerprint)
|
||||||
|
|
||||||
@ -92,26 +105,28 @@ def certificate_trust(certificate: Path, main_keys: Set[Fingerprint]) -> Trust:
|
|||||||
return Trust.full
|
return Trust.full
|
||||||
|
|
||||||
uid_trust: Dict[Uid, Trust] = {}
|
uid_trust: Dict[Uid, Trust] = {}
|
||||||
|
self_revoked_uids: Set[Uid] = set()
|
||||||
uids = certificate / "uid"
|
uids = certificate / "uid"
|
||||||
for uid_path in uids.iterdir():
|
for uid_path in uids.iterdir():
|
||||||
uid: Uid = Uid(uid_path.name)
|
uid: Uid = Uid(uid_path.name)
|
||||||
|
|
||||||
# TODO: convert key-id to fingerprint otherwise it may contain duplicates
|
|
||||||
revocations = set()
|
revocations = set()
|
||||||
self_revoked = False
|
|
||||||
for revocation in uid_path.glob("revocation/*.asc"):
|
for revocation in uid_path.glob("revocation/*.asc"):
|
||||||
issuer = Fingerprint(revocation.stem)
|
issuer = get_fingerprint_from_partial(all_fingerprints, Fingerprint(revocation.stem))
|
||||||
|
if not issuer:
|
||||||
|
raise Exception(f"Unknown issuer: {issuer}")
|
||||||
# self revocation
|
# self revocation
|
||||||
if fingerprint.endswith(issuer):
|
if fingerprint.endswith(issuer):
|
||||||
self_revoked = True
|
self_revoked_uids.add(uid)
|
||||||
# main key revocation
|
# main key revocation
|
||||||
elif contains_fingerprint(fingerprints=main_keys, fingerprint=issuer):
|
elif contains_fingerprint(fingerprints=main_keys, fingerprint=issuer):
|
||||||
revocations.add(issuer)
|
revocations.add(issuer)
|
||||||
|
|
||||||
# TODO: convert key-id to fingerprint otherwise it may contain duplicates
|
|
||||||
certifications: Set[Fingerprint] = set()
|
certifications: Set[Fingerprint] = set()
|
||||||
for certification in uid_path.glob("certification/*.asc"):
|
for certification in uid_path.glob("certification/*.asc"):
|
||||||
issuer = Fingerprint(certification.stem)
|
issuer = get_fingerprint_from_partial(all_fingerprints, Fingerprint(certification.stem))
|
||||||
|
if not issuer:
|
||||||
|
raise Exception(f"Unknown issuer: {issuer}")
|
||||||
# only take main key certifications into account
|
# only take main key certifications into account
|
||||||
if not contains_fingerprint(fingerprints=main_keys, fingerprint=issuer):
|
if not contains_fingerprint(fingerprints=main_keys, fingerprint=issuer):
|
||||||
continue
|
continue
|
||||||
@ -121,7 +136,7 @@ def certificate_trust(certificate: Path, main_keys: Set[Fingerprint]) -> Trust:
|
|||||||
certifications.add(issuer)
|
certifications.add(issuer)
|
||||||
|
|
||||||
# self revoked uid
|
# self revoked uid
|
||||||
if self_revoked:
|
if uid in self_revoked_uids:
|
||||||
debug(f"Certificate {fingerprint} with uid {uid} is self-revoked")
|
debug(f"Certificate {fingerprint} with uid {uid} is self-revoked")
|
||||||
uid_trust[uid] = Trust.revoked
|
uid_trust[uid] = Trust.revoked
|
||||||
continue
|
continue
|
||||||
@ -152,8 +167,7 @@ def certificate_trust(certificate: Path, main_keys: Set[Fingerprint]) -> Trust:
|
|||||||
if any(map(lambda t: Trust.full == t, uid_trust.values())):
|
if any(map(lambda t: Trust.full == t, uid_trust.values())):
|
||||||
trust = Trust.full
|
trust = Trust.full
|
||||||
# no uid has full trust but at least one is revoked
|
# no uid has full trust but at least one is revoked
|
||||||
# TODO: only revoked if it contains main key revocations, not just self-revocation
|
elif any(map(lambda e: Trust.revoked == e[1] and e[0] not in self_revoked_uids, uid_trust.items())):
|
||||||
elif any(map(lambda t: Trust.revoked == t, uid_trust.values())):
|
|
||||||
trust = Trust.revoked
|
trust = Trust.revoked
|
||||||
# no uid has full trust or is revoked
|
# no uid has full trust or is revoked
|
||||||
elif any(map(lambda t: Trust.marginal == t, uid_trust.values())):
|
elif any(map(lambda t: Trust.marginal == t, uid_trust.values())):
|
||||||
|
@ -86,7 +86,12 @@ def natural_sort_path(_list: Iterable[Path]) -> Iterable[Path]:
|
|||||||
return sorted(_list, key=alphanum_key)
|
return sorted(_list, key=alphanum_key)
|
||||||
|
|
||||||
|
|
||||||
def system(cmd: List[str], _stdin: Optional[IO[AnyStr]] = None, exit_on_error: bool = False) -> str:
|
def system(
|
||||||
|
cmd: List[str],
|
||||||
|
_stdin: Optional[IO[AnyStr]] = None,
|
||||||
|
exit_on_error: bool = False,
|
||||||
|
env: Optional[Dict[str, str]] = None,
|
||||||
|
) -> str:
|
||||||
"""Execute a command using check_output
|
"""Execute a command using check_output
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
@ -94,6 +99,7 @@ def system(cmd: List[str], _stdin: Optional[IO[AnyStr]] = None, exit_on_error: b
|
|||||||
cmd: A list of strings to be fed to check_output
|
cmd: A list of strings to be fed to check_output
|
||||||
_stdin: input fd used for the spawned process
|
_stdin: input fd used for the spawned process
|
||||||
exit_on_error: Whether to exit the script when encountering an error (defaults to False)
|
exit_on_error: Whether to exit the script when encountering an error (defaults to False)
|
||||||
|
env: Optional environment vars for the shell invocation
|
||||||
|
|
||||||
Raises
|
Raises
|
||||||
------
|
------
|
||||||
@ -103,9 +109,11 @@ def system(cmd: List[str], _stdin: Optional[IO[AnyStr]] = None, exit_on_error: b
|
|||||||
-------
|
-------
|
||||||
The output of cmd
|
The output of cmd
|
||||||
"""
|
"""
|
||||||
|
if not env:
|
||||||
|
env = {}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return check_output(cmd, stderr=STDOUT, stdin=_stdin).decode()
|
return check_output(cmd, stderr=STDOUT, stdin=_stdin, env=env).decode()
|
||||||
except CalledProcessError as e:
|
except CalledProcessError as e:
|
||||||
stderr.buffer.write(bytes(e.stdout, encoding="utf8"))
|
stderr.buffer.write(bytes(e.stdout, encoding="utf8"))
|
||||||
print_stack()
|
print_stack()
|
||||||
@ -215,6 +223,26 @@ def contains_fingerprint(fingerprints: Iterable[Fingerprint], fingerprint: Finge
|
|||||||
return any(filter(lambda e: str(e).endswith(fingerprint), fingerprints))
|
return any(filter(lambda e: str(e).endswith(fingerprint), fingerprints))
|
||||||
|
|
||||||
|
|
||||||
|
def get_fingerprint_from_partial(
|
||||||
|
fingerprints: Iterable[Fingerprint], fingerprint: Fingerprint
|
||||||
|
) -> Optional[Fingerprint]:
|
||||||
|
"""Returns the full fingerprint looked up from a partial fingerprint like a key-id
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
fingerprints: Iteratable structure of fingerprints that should be searched
|
||||||
|
fingerprint: Partial fingerprint to search for
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
The full fingerprint or None
|
||||||
|
"""
|
||||||
|
|
||||||
|
for fingerprint in filter(lambda e: str(e).endswith(fingerprint), fingerprints):
|
||||||
|
return fingerprint
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def filter_fingerprints_by_trust(trusts: Dict[Fingerprint, Trust], trust: Trust) -> List[Fingerprint]:
|
def filter_fingerprints_by_trust(trusts: Dict[Fingerprint, Trust], trust: Trust) -> List[Fingerprint]:
|
||||||
"""Filters a dict of Fingerprint to Trust by a passed Trust parameter and returns the matching fingerprints.
|
"""Filters a dict of Fingerprint to Trust by a passed Trust parameter and returns the matching fingerprints.
|
||||||
|
|
||||||
|
@ -2,6 +2,9 @@ from collections import defaultdict
|
|||||||
from functools import wraps
|
from functools import wraps
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from shutil import copytree
|
from shutil import copytree
|
||||||
|
from subprocess import PIPE
|
||||||
|
from subprocess import Popen
|
||||||
|
from tempfile import NamedTemporaryFile
|
||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
@ -14,6 +17,7 @@ from typing import Set
|
|||||||
from pytest import fixture
|
from pytest import fixture
|
||||||
|
|
||||||
from libkeyringctl.keyring import convert_certificate
|
from libkeyringctl.keyring import convert_certificate
|
||||||
|
from libkeyringctl.keyring import export
|
||||||
from libkeyringctl.keyring import simplify_user_id
|
from libkeyringctl.keyring import simplify_user_id
|
||||||
from libkeyringctl.sequoia import certify
|
from libkeyringctl.sequoia import certify
|
||||||
from libkeyringctl.sequoia import key_extract_certificate
|
from libkeyringctl.sequoia import key_extract_certificate
|
||||||
@ -24,12 +28,15 @@ from libkeyringctl.types import Fingerprint
|
|||||||
from libkeyringctl.types import Uid
|
from libkeyringctl.types import Uid
|
||||||
from libkeyringctl.types import Username
|
from libkeyringctl.types import Username
|
||||||
from libkeyringctl.util import cwd
|
from libkeyringctl.util import cwd
|
||||||
|
from libkeyringctl.util import system
|
||||||
|
|
||||||
test_keys: Dict[Username, List[Path]] = defaultdict(list)
|
test_keys: Dict[Username, List[Path]] = defaultdict(list)
|
||||||
test_key_revocation: Dict[Username, List[Path]] = defaultdict(list)
|
test_key_revocation: Dict[Username, List[Path]] = defaultdict(list)
|
||||||
test_certificates: Dict[Username, List[Path]] = defaultdict(list)
|
test_certificates: Dict[Username, List[Path]] = defaultdict(list)
|
||||||
|
test_certificate_uids: Dict[Username, List[List[Uid]]] = defaultdict(list)
|
||||||
test_keyring_certificates: Dict[Username, List[Path]] = defaultdict(list)
|
test_keyring_certificates: Dict[Username, List[Path]] = defaultdict(list)
|
||||||
test_main_fingerprints: Set[Fingerprint] = set()
|
test_main_fingerprints: Set[Fingerprint] = set()
|
||||||
|
test_all_fingerprints: Set[Fingerprint] = set()
|
||||||
|
|
||||||
|
|
||||||
@fixture(autouse=True)
|
@fixture(autouse=True)
|
||||||
@ -37,8 +44,10 @@ def reset_storage() -> None:
|
|||||||
test_keys.clear()
|
test_keys.clear()
|
||||||
test_key_revocation.clear()
|
test_key_revocation.clear()
|
||||||
test_certificates.clear()
|
test_certificates.clear()
|
||||||
|
test_certificate_uids.clear()
|
||||||
test_keyring_certificates.clear()
|
test_keyring_certificates.clear()
|
||||||
test_main_fingerprints.clear()
|
test_main_fingerprints.clear()
|
||||||
|
test_all_fingerprints.clear()
|
||||||
|
|
||||||
|
|
||||||
def create_certificate(
|
def create_certificate(
|
||||||
@ -50,16 +59,14 @@ def create_certificate(
|
|||||||
def decorator(decorated_func: Callable[..., None]) -> Callable[..., Any]:
|
def decorator(decorated_func: Callable[..., None]) -> Callable[..., Any]:
|
||||||
@wraps(decorated_func)
|
@wraps(decorated_func)
|
||||||
def wrapper(working_dir: Path, *args: Any, **kwargs: Any) -> None:
|
def wrapper(working_dir: Path, *args: Any, **kwargs: Any) -> None:
|
||||||
print(username)
|
key_directory = working_dir / "secret" / f"{username}"
|
||||||
|
|
||||||
key_directory = working_dir / "secret" / f"{id}"
|
|
||||||
key_directory.mkdir(parents=True, exist_ok=True)
|
key_directory.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
key_file: Path = key_directory / f"{username}.asc"
|
key_file: Path = key_directory / f"{username}.asc"
|
||||||
key_generate(uids=uids, outfile=key_file)
|
key_generate(uids=uids, outfile=key_file)
|
||||||
test_keys[username].append(key_file)
|
test_keys[username].append(key_file)
|
||||||
|
|
||||||
certificate_directory = working_dir / "certificate" / f"{id}"
|
certificate_directory = working_dir / "certificate" / f"{username}"
|
||||||
certificate_directory.mkdir(parents=True, exist_ok=True)
|
certificate_directory.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
keyring_root: Path = working_dir / "keyring"
|
keyring_root: Path = working_dir / "keyring"
|
||||||
@ -68,6 +75,7 @@ def create_certificate(
|
|||||||
|
|
||||||
key_extract_certificate(key=key_file, output=certificate_file)
|
key_extract_certificate(key=key_file, output=certificate_file)
|
||||||
test_certificates[username].append(certificate_file)
|
test_certificates[username].append(certificate_file)
|
||||||
|
test_certificate_uids[username].append(uids)
|
||||||
|
|
||||||
key_revocation_packet = key_file.parent / f"{key_file.name}.rev"
|
key_revocation_packet = key_file.parent / f"{key_file.name}.rev"
|
||||||
key_revocation_joined = key_file.parent / f"{key_file.name}.joined.rev"
|
key_revocation_joined = key_file.parent / f"{key_file.name}.joined.rev"
|
||||||
@ -88,8 +96,10 @@ def create_certificate(
|
|||||||
copytree(src=user_dir, dst=(target_dir / user_dir.name), dirs_exist_ok=True)
|
copytree(src=user_dir, dst=(target_dir / user_dir.name), dirs_exist_ok=True)
|
||||||
test_keyring_certificates[username].append(target_dir / user_dir.name / decomposed_path.name)
|
test_keyring_certificates[username].append(target_dir / user_dir.name / decomposed_path.name)
|
||||||
|
|
||||||
|
certificate_fingerprint: Fingerprint = Fingerprint(decomposed_path.name)
|
||||||
if "main" == keyring_type:
|
if "main" == keyring_type:
|
||||||
test_main_fingerprints.add(Fingerprint(decomposed_path.name))
|
test_main_fingerprints.add(certificate_fingerprint)
|
||||||
|
test_all_fingerprints.add(certificate_fingerprint)
|
||||||
|
|
||||||
decorated_func(working_dir=working_dir, *args, **kwargs)
|
decorated_func(working_dir=working_dir, *args, **kwargs)
|
||||||
|
|
||||||
@ -169,6 +179,95 @@ def create_key_revocation(
|
|||||||
return decorator(func)
|
return decorator(func)
|
||||||
|
|
||||||
|
|
||||||
|
def create_signature_revocation(
|
||||||
|
issuer: Username, certified: Username, uid: Uid, func: Optional[Callable[[Any], None]] = None
|
||||||
|
) -> Callable[..., Any]:
|
||||||
|
def decorator(decorated_func: Callable[..., None]) -> Callable[..., Any]:
|
||||||
|
@wraps(decorated_func)
|
||||||
|
def wrapper(working_dir: Path, *args: Any, **kwargs: Any) -> None:
|
||||||
|
|
||||||
|
issuer_key: Path = test_keys[issuer][0]
|
||||||
|
keyring_root: Path = working_dir / "keyring"
|
||||||
|
|
||||||
|
keyring_certificate: Path = test_keyring_certificates[certified][0]
|
||||||
|
certified_fingerprint = keyring_certificate.name
|
||||||
|
|
||||||
|
with NamedTemporaryFile(dir=str(working_dir), prefix=f"{certified}", suffix=".asc") as certificate:
|
||||||
|
certificate_path: Path = Path(certificate.name)
|
||||||
|
export(
|
||||||
|
working_dir=working_dir,
|
||||||
|
keyring_root=keyring_root,
|
||||||
|
sources=[keyring_certificate],
|
||||||
|
output=certificate_path,
|
||||||
|
)
|
||||||
|
|
||||||
|
with TemporaryDirectory(prefix="gnupg") as gnupg_home:
|
||||||
|
env = {"GNUPGHOME": gnupg_home}
|
||||||
|
|
||||||
|
print(
|
||||||
|
system(
|
||||||
|
[
|
||||||
|
"gpg",
|
||||||
|
"--no-auto-check-trustdb",
|
||||||
|
"--import",
|
||||||
|
f"{str(issuer_key)}",
|
||||||
|
f"{str(certificate_path)}",
|
||||||
|
],
|
||||||
|
env=env,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
uid_confirmations = ""
|
||||||
|
for cert_uid in test_certificate_uids[certified][0]:
|
||||||
|
if uid == cert_uid:
|
||||||
|
uid_confirmations += "y\n"
|
||||||
|
else:
|
||||||
|
uid_confirmations += "n\n"
|
||||||
|
|
||||||
|
commands = Popen(["echo", "-e", f"{uid_confirmations}y\n0\ny\n\ny\ny\nsave\n"], stdout=PIPE)
|
||||||
|
system(
|
||||||
|
[
|
||||||
|
"gpg",
|
||||||
|
"--no-auto-check-trustdb",
|
||||||
|
"--command-fd",
|
||||||
|
"0",
|
||||||
|
"--expert",
|
||||||
|
"--yes",
|
||||||
|
"--batch",
|
||||||
|
"--edit-key",
|
||||||
|
f"{certified_fingerprint}",
|
||||||
|
"revsig",
|
||||||
|
"save",
|
||||||
|
],
|
||||||
|
_stdin=commands.stdout,
|
||||||
|
env=env,
|
||||||
|
)
|
||||||
|
|
||||||
|
revoked_certificate = system(["gpg", "--armor", "--export", f"{certified_fingerprint}"], env=env)
|
||||||
|
certificate.truncate(0)
|
||||||
|
certificate.seek(0)
|
||||||
|
certificate.write(revoked_certificate.encode())
|
||||||
|
certificate.flush()
|
||||||
|
|
||||||
|
target_dir = keyring_root / "packager"
|
||||||
|
decomposed_path: Path = convert_certificate(
|
||||||
|
working_dir=working_dir,
|
||||||
|
certificate=certificate_path,
|
||||||
|
keyring_dir=target_dir,
|
||||||
|
)
|
||||||
|
user_dir = decomposed_path.parent
|
||||||
|
(target_dir / user_dir.name).mkdir(parents=True, exist_ok=True)
|
||||||
|
copytree(src=user_dir, dst=(target_dir / user_dir.name), dirs_exist_ok=True)
|
||||||
|
|
||||||
|
decorated_func(working_dir=working_dir, *args, **kwargs)
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
if not func:
|
||||||
|
return decorator
|
||||||
|
return decorator(func)
|
||||||
|
|
||||||
|
|
||||||
@fixture(scope="function")
|
@fixture(scope="function")
|
||||||
def working_dir() -> Generator[Path, None, None]:
|
def working_dir() -> Generator[Path, None, None]:
|
||||||
with TemporaryDirectory(prefix="arch-keyringctl-test-") as tempdir:
|
with TemporaryDirectory(prefix="arch-keyringctl-test-") as tempdir:
|
||||||
|
@ -55,26 +55,22 @@ def test_keyring_split(mkdtemp_mock: Mock, system_mock: Mock, create_subdir: boo
|
|||||||
|
|
||||||
|
|
||||||
@mark.parametrize(
|
@mark.parametrize(
|
||||||
"force, output",
|
"output",
|
||||||
[
|
[
|
||||||
(True, None),
|
None,
|
||||||
(False, None),
|
Path("output"),
|
||||||
(True, Path("output")),
|
|
||||||
(False, Path("output")),
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@patch("libkeyringctl.sequoia.system")
|
@patch("libkeyringctl.sequoia.system")
|
||||||
def test_keyring_merge(system_mock: Mock, force: bool, output: Optional[Path]) -> None:
|
def test_keyring_merge(system_mock: Mock, output: Optional[Path]) -> None:
|
||||||
certificates = [Path("foo"), Path("bar")]
|
certificates = [Path("foo"), Path("bar")]
|
||||||
system_mock.return_value = "return"
|
system_mock.return_value = "return"
|
||||||
|
|
||||||
assert sequoia.keyring_merge(certificates=certificates, output=output, force=force) == "return"
|
assert sequoia.keyring_merge(certificates=certificates, output=output) == "return"
|
||||||
|
|
||||||
name, args, kwargs = system_mock.mock_calls[0]
|
name, args, kwargs = system_mock.mock_calls[0]
|
||||||
for cert in certificates:
|
for cert in certificates:
|
||||||
assert str(cert) in args[0]
|
assert str(cert) in args[0]
|
||||||
if force:
|
|
||||||
assert "--force" == args[0][1]
|
|
||||||
if output:
|
if output:
|
||||||
assert "--output" in args[0] and str(output) in args[0]
|
assert "--output" in args[0] and str(output) in args[0]
|
||||||
|
|
||||||
|
@ -1,22 +1,68 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import List
|
||||||
|
from unittest.mock import Mock
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from pytest import mark
|
||||||
|
from pytest import raises
|
||||||
|
|
||||||
from libkeyringctl.trust import certificate_trust
|
from libkeyringctl.trust import certificate_trust
|
||||||
|
from libkeyringctl.trust import certificate_trust_from_paths
|
||||||
|
from libkeyringctl.trust import format_trust_label
|
||||||
|
from libkeyringctl.trust import trust_color
|
||||||
|
from libkeyringctl.trust import trust_icon
|
||||||
|
from libkeyringctl.types import Color
|
||||||
|
from libkeyringctl.types import Fingerprint
|
||||||
from libkeyringctl.types import Trust
|
from libkeyringctl.types import Trust
|
||||||
from libkeyringctl.types import Uid
|
from libkeyringctl.types import Uid
|
||||||
from libkeyringctl.types import Username
|
from libkeyringctl.types import Username
|
||||||
|
|
||||||
from .conftest import create_certificate
|
from .conftest import create_certificate
|
||||||
from .conftest import create_key_revocation
|
from .conftest import create_key_revocation
|
||||||
|
from .conftest import create_signature_revocation
|
||||||
from .conftest import create_uid_certification
|
from .conftest import create_uid_certification
|
||||||
|
from .conftest import test_all_fingerprints
|
||||||
from .conftest import test_keyring_certificates
|
from .conftest import test_keyring_certificates
|
||||||
from .conftest import test_main_fingerprints
|
from .conftest import test_main_fingerprints
|
||||||
|
|
||||||
|
|
||||||
|
@mark.parametrize(
|
||||||
|
"sources",
|
||||||
|
[
|
||||||
|
([Path("foobar")]),
|
||||||
|
([Path("foobar"), Path("quxdoo")]),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@patch("libkeyringctl.trust.certificate_trust")
|
||||||
|
def test_certificate_trust_from_paths(
|
||||||
|
certificate_trust_mock: Mock,
|
||||||
|
sources: List[Path],
|
||||||
|
) -> None:
|
||||||
|
certificate_trust_mock.return_value = Trust.full
|
||||||
|
for source in sources:
|
||||||
|
source.mkdir(parents=True, exist_ok=True)
|
||||||
|
cert = source / "foo.asc"
|
||||||
|
cert.touch()
|
||||||
|
|
||||||
|
trusts = certificate_trust_from_paths(
|
||||||
|
sources=sources, main_keys=test_main_fingerprints, all_fingerprints=test_all_fingerprints
|
||||||
|
)
|
||||||
|
for i, source in enumerate(sources):
|
||||||
|
name, args, kwargs = certificate_trust_mock.mock_calls[i]
|
||||||
|
assert kwargs["certificate"] == source
|
||||||
|
assert kwargs["main_keys"] == test_main_fingerprints
|
||||||
|
assert kwargs["all_fingerprints"] == test_all_fingerprints
|
||||||
|
fingerprint = Fingerprint(source.name)
|
||||||
|
assert Trust.full == trusts[fingerprint]
|
||||||
|
assert len(trusts) == len(sources)
|
||||||
|
|
||||||
|
|
||||||
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")], keyring_type="main")
|
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")], keyring_type="main")
|
||||||
def test_certificate_trust_main_key_has_full_trust(working_dir: Path, keyring_dir: Path) -> None:
|
def test_certificate_trust_main_key_has_full_trust(working_dir: Path, keyring_dir: Path) -> None:
|
||||||
trust = certificate_trust(
|
trust = certificate_trust(
|
||||||
test_keyring_certificates[Username("foobar")][0],
|
test_keyring_certificates[Username("foobar")][0],
|
||||||
test_main_fingerprints,
|
test_main_fingerprints,
|
||||||
|
test_all_fingerprints,
|
||||||
)
|
)
|
||||||
assert Trust.full == trust
|
assert Trust.full == trust
|
||||||
|
|
||||||
@ -27,16 +73,46 @@ def test_certificate_trust_main_key_revoked(working_dir: Path, keyring_dir: Path
|
|||||||
trust = certificate_trust(
|
trust = certificate_trust(
|
||||||
test_keyring_certificates[Username("foobar")][0],
|
test_keyring_certificates[Username("foobar")][0],
|
||||||
test_main_fingerprints,
|
test_main_fingerprints,
|
||||||
|
test_all_fingerprints,
|
||||||
)
|
)
|
||||||
assert Trust.revoked == trust
|
assert Trust.revoked == trust
|
||||||
|
|
||||||
|
|
||||||
|
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")], keyring_type="main")
|
||||||
|
@create_key_revocation(username=Username("foobar"), keyring_type="main")
|
||||||
|
def test_certificate_trust_main_key_revoked_unknown_fingerprint_lookup(working_dir: Path, keyring_dir: Path) -> None:
|
||||||
|
fingerprint = Fingerprint(test_keyring_certificates[Username("foobar")][0].name)
|
||||||
|
revocation = list((keyring_dir / "main" / "foobar" / fingerprint / "revocation").iterdir())[0]
|
||||||
|
revocation.rename(revocation.parent / "12341234.asc")
|
||||||
|
with raises(Exception):
|
||||||
|
certificate_trust(
|
||||||
|
test_keyring_certificates[Username("foobar")][0],
|
||||||
|
test_main_fingerprints,
|
||||||
|
{Fingerprint("12341234")},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")], keyring_type="main")
|
||||||
|
@create_key_revocation(username=Username("foobar"), keyring_type="main")
|
||||||
|
def test_certificate_trust_main_key_revoked_unknown_self_revocation(working_dir: Path, keyring_dir: Path) -> None:
|
||||||
|
fingerprint = Fingerprint(test_keyring_certificates[Username("foobar")][0].name)
|
||||||
|
revocation = list((keyring_dir / "main" / "foobar" / fingerprint / "revocation").iterdir())[0]
|
||||||
|
revocation.rename(revocation.parent / "12341234.asc")
|
||||||
|
with raises(Exception):
|
||||||
|
certificate_trust(
|
||||||
|
test_keyring_certificates[Username("foobar")][0],
|
||||||
|
test_main_fingerprints,
|
||||||
|
set(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@create_certificate(username=Username("main"), uids=[Uid("main <foo@bar.xyz>")])
|
@create_certificate(username=Username("main"), uids=[Uid("main <foo@bar.xyz>")])
|
||||||
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")])
|
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")])
|
||||||
def test_certificate_trust_no_signature_is_unknown(working_dir: Path, keyring_dir: Path) -> None:
|
def test_certificate_trust_no_signature_is_unknown(working_dir: Path, keyring_dir: Path) -> None:
|
||||||
trust = certificate_trust(
|
trust = certificate_trust(
|
||||||
test_keyring_certificates[Username("foobar")][0],
|
test_keyring_certificates[Username("foobar")][0],
|
||||||
test_main_fingerprints,
|
test_main_fingerprints,
|
||||||
|
test_all_fingerprints,
|
||||||
)
|
)
|
||||||
assert Trust.unknown == trust
|
assert Trust.unknown == trust
|
||||||
|
|
||||||
@ -48,6 +124,7 @@ def test_certificate_trust_one_signature_is_marginal(working_dir: Path, keyring_
|
|||||||
trust = certificate_trust(
|
trust = certificate_trust(
|
||||||
test_keyring_certificates[Username("foobar")][0],
|
test_keyring_certificates[Username("foobar")][0],
|
||||||
test_main_fingerprints,
|
test_main_fingerprints,
|
||||||
|
test_all_fingerprints,
|
||||||
)
|
)
|
||||||
assert Trust.marginal == trust
|
assert Trust.marginal == trust
|
||||||
|
|
||||||
@ -60,6 +137,7 @@ def test_certificate_trust_one_none_main_signature_gives_no_trust(working_dir: P
|
|||||||
trust = certificate_trust(
|
trust = certificate_trust(
|
||||||
test_keyring_certificates[Username("foobar")][0],
|
test_keyring_certificates[Username("foobar")][0],
|
||||||
test_main_fingerprints,
|
test_main_fingerprints,
|
||||||
|
test_all_fingerprints,
|
||||||
)
|
)
|
||||||
assert Trust.unknown == trust
|
assert Trust.unknown == trust
|
||||||
|
|
||||||
@ -75,16 +153,176 @@ def test_certificate_trust_three_main_signature_gives_full_trust(working_dir: Pa
|
|||||||
trust = certificate_trust(
|
trust = certificate_trust(
|
||||||
test_keyring_certificates[Username("foobar")][0],
|
test_keyring_certificates[Username("foobar")][0],
|
||||||
test_main_fingerprints,
|
test_main_fingerprints,
|
||||||
|
test_all_fingerprints,
|
||||||
)
|
)
|
||||||
assert Trust.full == trust
|
assert Trust.full == trust
|
||||||
|
|
||||||
|
|
||||||
@create_certificate(username=Username("main"), uids=[Uid("main <foo@bar.xyz>")], keyring_type="main")
|
@create_certificate(username=Username("main"), uids=[Uid("main <foo@bar.xyz>")], keyring_type="main")
|
||||||
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")])
|
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")])
|
||||||
@create_key_revocation(username=Username("foobar"), keyring_type="packager")
|
@create_key_revocation(username=Username("foobar"))
|
||||||
def test_certificate_trust_revoked_key(working_dir: Path, keyring_dir: Path) -> None:
|
def test_certificate_trust_revoked_key(working_dir: Path, keyring_dir: Path) -> None:
|
||||||
trust = certificate_trust(
|
trust = certificate_trust(
|
||||||
test_keyring_certificates[Username("foobar")][0],
|
test_keyring_certificates[Username("foobar")][0],
|
||||||
test_main_fingerprints,
|
test_main_fingerprints,
|
||||||
|
test_all_fingerprints,
|
||||||
)
|
)
|
||||||
assert Trust.revoked == trust
|
assert Trust.revoked == trust
|
||||||
|
|
||||||
|
|
||||||
|
@create_certificate(username=Username("main"), uids=[Uid("main <foo@bar.xyz>")], keyring_type="main")
|
||||||
|
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")])
|
||||||
|
@create_uid_certification(issuer=Username("main"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
|
||||||
|
@create_signature_revocation(issuer=Username("main"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
|
||||||
|
def test_certificate_trust_one_signature_revoked(working_dir: Path, keyring_dir: Path) -> None:
|
||||||
|
trust = certificate_trust(
|
||||||
|
test_keyring_certificates[Username("foobar")][0],
|
||||||
|
test_main_fingerprints,
|
||||||
|
test_all_fingerprints,
|
||||||
|
)
|
||||||
|
assert Trust.revoked == trust
|
||||||
|
|
||||||
|
|
||||||
|
@create_certificate(username=Username("main1"), uids=[Uid("main1 <foo@bar.xyz>")], keyring_type="main")
|
||||||
|
@create_certificate(username=Username("main2"), uids=[Uid("main2 <foo@bar.xyz>")], keyring_type="main")
|
||||||
|
@create_certificate(username=Username("main3"), uids=[Uid("main3 <foo@bar.xyz>")], keyring_type="main")
|
||||||
|
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")])
|
||||||
|
@create_uid_certification(issuer=Username("main1"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
|
||||||
|
@create_uid_certification(issuer=Username("main2"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
|
||||||
|
@create_uid_certification(issuer=Username("main3"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
|
||||||
|
@create_signature_revocation(issuer=Username("main3"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
|
||||||
|
def test_certificate_trust_revoked_if_below_full(working_dir: Path, keyring_dir: Path) -> None:
|
||||||
|
trust = certificate_trust(
|
||||||
|
test_keyring_certificates[Username("foobar")][0],
|
||||||
|
test_main_fingerprints,
|
||||||
|
test_all_fingerprints,
|
||||||
|
)
|
||||||
|
assert Trust.revoked == trust
|
||||||
|
|
||||||
|
|
||||||
|
@create_certificate(username=Username("main1"), uids=[Uid("main1 <foo@bar.xyz>")], keyring_type="main")
|
||||||
|
@create_certificate(username=Username("main2"), uids=[Uid("main2 <foo@bar.xyz>")], keyring_type="main")
|
||||||
|
@create_certificate(username=Username("main3"), uids=[Uid("main3 <foo@bar.xyz>")], keyring_type="main")
|
||||||
|
@create_certificate(username=Username("main4"), uids=[Uid("main4 <foo@bar.xyz>")], keyring_type="main")
|
||||||
|
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")])
|
||||||
|
@create_uid_certification(issuer=Username("main1"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
|
||||||
|
@create_uid_certification(issuer=Username("main2"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
|
||||||
|
@create_uid_certification(issuer=Username("main3"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
|
||||||
|
@create_uid_certification(issuer=Username("main4"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
|
||||||
|
@create_signature_revocation(issuer=Username("main4"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
|
||||||
|
def test_certificate_trust_full_remains_if_enough_sigs_present(working_dir: Path, keyring_dir: Path) -> None:
|
||||||
|
trust = certificate_trust(
|
||||||
|
test_keyring_certificates[Username("foobar")][0],
|
||||||
|
test_main_fingerprints,
|
||||||
|
test_all_fingerprints,
|
||||||
|
)
|
||||||
|
assert Trust.full == trust
|
||||||
|
|
||||||
|
|
||||||
|
@create_certificate(username=Username("main1"), uids=[Uid("main1 <foo@bar.xyz>")], keyring_type="main")
|
||||||
|
@create_certificate(username=Username("main2"), uids=[Uid("main2 <foo@bar.xyz>")], keyring_type="main")
|
||||||
|
@create_certificate(username=Username("main3"), uids=[Uid("main3 <foo@bar.xyz>")], keyring_type="main")
|
||||||
|
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>"), Uid("old <old@old.old>")])
|
||||||
|
@create_uid_certification(issuer=Username("main1"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
|
||||||
|
@create_uid_certification(issuer=Username("main2"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
|
||||||
|
@create_signature_revocation(issuer=Username("foobar"), certified=Username("foobar"), uid=Uid("old <old@old.old>"))
|
||||||
|
def test_certificate_trust_not_revoked_if_only_one_uid_is_self_revoked(working_dir: Path, keyring_dir: Path) -> None:
|
||||||
|
trust = certificate_trust(
|
||||||
|
test_keyring_certificates[Username("foobar")][0],
|
||||||
|
test_main_fingerprints,
|
||||||
|
test_all_fingerprints,
|
||||||
|
)
|
||||||
|
assert Trust.marginal == trust
|
||||||
|
|
||||||
|
|
||||||
|
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>"), Uid("old <old@old.old>")])
|
||||||
|
@create_signature_revocation(issuer=Username("foobar"), certified=Username("foobar"), uid=Uid("old <old@old.old>"))
|
||||||
|
def test_certificate_trust_unknown_if_only_contains_self_revoked(working_dir: Path, keyring_dir: Path) -> None:
|
||||||
|
trust = certificate_trust(
|
||||||
|
test_keyring_certificates[Username("foobar")][0],
|
||||||
|
test_main_fingerprints,
|
||||||
|
test_all_fingerprints,
|
||||||
|
)
|
||||||
|
assert Trust.unknown == trust
|
||||||
|
|
||||||
|
|
||||||
|
@create_certificate(username=Username("main1"), uids=[Uid("main1 <foo@bar.xyz>")], keyring_type="main")
|
||||||
|
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>"), Uid("old <old@old.old>")])
|
||||||
|
@create_uid_certification(issuer=Username("main1"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
|
||||||
|
def test_certificate_trust_missing_signature_fingerprint_lookup(working_dir: Path, keyring_dir: Path) -> None:
|
||||||
|
with raises(Exception):
|
||||||
|
certificate_trust(
|
||||||
|
test_keyring_certificates[Username("foobar")][0],
|
||||||
|
test_main_fingerprints,
|
||||||
|
set(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@create_certificate(username=Username("foobar"), uids=[Uid("old <old@old.old>")])
|
||||||
|
@create_signature_revocation(issuer=Username("foobar"), certified=Username("foobar"), uid=Uid("old <old@old.old>"))
|
||||||
|
def test_certificate_trust_missing_revocation_fingerprint_lookup(working_dir: Path, keyring_dir: Path) -> None:
|
||||||
|
with raises(Exception):
|
||||||
|
certificate_trust(
|
||||||
|
test_keyring_certificates[Username("foobar")][0],
|
||||||
|
test_main_fingerprints,
|
||||||
|
set(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@create_certificate(username=Username("main1"), uids=[Uid("main1 <foo@bar.xyz>")], keyring_type="main")
|
||||||
|
@create_certificate(username=Username("foobar"), uids=[Uid("foobar <foo@bar.xyz>")])
|
||||||
|
@create_certificate(username=Username("packager"), uids=[Uid("packager <packager@bar.xyz>")])
|
||||||
|
@create_uid_certification(issuer=Username("main1"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
|
||||||
|
@create_uid_certification(issuer=Username("packager"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
|
||||||
|
@create_signature_revocation(issuer=Username("packager"), certified=Username("foobar"), uid=Uid("foobar <foo@bar.xyz>"))
|
||||||
|
def test_certificate_trust_ignore_3rd_party_revocation(working_dir: Path, keyring_dir: Path) -> None:
|
||||||
|
trust = certificate_trust(
|
||||||
|
test_keyring_certificates[Username("foobar")][0],
|
||||||
|
test_main_fingerprints,
|
||||||
|
test_all_fingerprints,
|
||||||
|
)
|
||||||
|
assert Trust.marginal == trust
|
||||||
|
|
||||||
|
|
||||||
|
@mark.parametrize(
|
||||||
|
"trust, result",
|
||||||
|
[
|
||||||
|
(Trust.revoked, Color.RED),
|
||||||
|
(Trust.full, Color.GREEN),
|
||||||
|
(Trust.marginal, Color.YELLOW),
|
||||||
|
(Trust.unknown, Color.YELLOW),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_trust_color(trust: Trust, result: Color) -> None:
|
||||||
|
assert trust_color(trust) == result
|
||||||
|
|
||||||
|
|
||||||
|
@mark.parametrize(
|
||||||
|
"trust, result",
|
||||||
|
[
|
||||||
|
(Trust.revoked, "✗"),
|
||||||
|
(Trust.full, "✓"),
|
||||||
|
(Trust.marginal, "~"),
|
||||||
|
(Trust.unknown, "~"),
|
||||||
|
(None, "?"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_trust_icon(trust: Trust, result: str) -> None:
|
||||||
|
assert trust_icon(trust) == result
|
||||||
|
|
||||||
|
|
||||||
|
@mark.parametrize(
|
||||||
|
"trust",
|
||||||
|
[
|
||||||
|
Trust.revoked,
|
||||||
|
Trust.full,
|
||||||
|
Trust.marginal,
|
||||||
|
Trust.unknown,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@patch("libkeyringctl.trust.trust_icon")
|
||||||
|
@patch("libkeyringctl.trust.trust_color")
|
||||||
|
def test_format_trust_label(trust_color_mock: Mock, trust_icon_mock: Mock, trust: Trust) -> None:
|
||||||
|
trust_icon_mock.return_value = "ICON"
|
||||||
|
trust_color_mock.return_value = Color.GREEN
|
||||||
|
assert f"{Color.GREEN.value}ICON {trust.name}{Color.RST.value}" == format_trust_label(trust)
|
||||||
|
@ -3,6 +3,7 @@ from os import getcwd
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
|
from typing import Dict
|
||||||
from typing import List
|
from typing import List
|
||||||
from unittest.mock import Mock
|
from unittest.mock import Mock
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
@ -12,6 +13,7 @@ from pytest import raises
|
|||||||
|
|
||||||
from libkeyringctl import util
|
from libkeyringctl import util
|
||||||
from libkeyringctl.types import Fingerprint
|
from libkeyringctl.types import Fingerprint
|
||||||
|
from libkeyringctl.types import Trust
|
||||||
|
|
||||||
|
|
||||||
def test_cwd() -> None:
|
def test_cwd() -> None:
|
||||||
@ -93,7 +95,7 @@ def test_get_cert_paths() -> None:
|
|||||||
cert2 = cert_dir2 / "cert2.asc"
|
cert2 = cert_dir2 / "cert2.asc"
|
||||||
cert2.touch()
|
cert2.touch()
|
||||||
|
|
||||||
assert util.get_cert_paths(paths=[tmp_dir]) == set([cert_dir1, cert_dir2])
|
assert util.get_cert_paths(paths=[tmp_dir]) == {cert_dir1, cert_dir2}
|
||||||
|
|
||||||
|
|
||||||
def test_get_parent_cert_paths() -> None:
|
def test_get_parent_cert_paths() -> None:
|
||||||
@ -112,7 +114,7 @@ def test_get_parent_cert_paths() -> None:
|
|||||||
cert2 = cert_dir2 / "cert2.asc"
|
cert2 = cert_dir2 / "cert2.asc"
|
||||||
cert2.touch()
|
cert2.touch()
|
||||||
|
|
||||||
assert util.get_parent_cert_paths(paths=[cert1, cert2]) == set([cert_dir1])
|
assert util.get_parent_cert_paths(paths=[cert1, cert2]) == {cert_dir1}
|
||||||
|
|
||||||
|
|
||||||
@mark.parametrize(
|
@mark.parametrize(
|
||||||
@ -132,3 +134,50 @@ def test_get_parent_cert_paths() -> None:
|
|||||||
)
|
)
|
||||||
def test_contains_fingerprint(fingerprints: List[Fingerprint], fingerprint: Fingerprint, result: bool) -> None:
|
def test_contains_fingerprint(fingerprints: List[Fingerprint], fingerprint: Fingerprint, result: bool) -> None:
|
||||||
assert util.contains_fingerprint(fingerprints=fingerprints, fingerprint=fingerprint) is result
|
assert util.contains_fingerprint(fingerprints=fingerprints, fingerprint=fingerprint) is result
|
||||||
|
|
||||||
|
|
||||||
|
@mark.parametrize(
|
||||||
|
"fingerprints, fingerprint, result",
|
||||||
|
[
|
||||||
|
([Fingerprint("blahfoo"), Fingerprint("blahbar")], Fingerprint("foo"), Fingerprint("blahfoo")),
|
||||||
|
([Fingerprint("blahfoo"), Fingerprint("blahbar")], Fingerprint("blahfoo"), Fingerprint("blahfoo")),
|
||||||
|
(
|
||||||
|
[Fingerprint("bazfoo"), Fingerprint("bazbar")],
|
||||||
|
Fingerprint("baz"),
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_get_fingerprint_from_partial(fingerprints: List[Fingerprint], fingerprint: Fingerprint, result: bool) -> None:
|
||||||
|
assert util.get_fingerprint_from_partial(fingerprints=fingerprints, fingerprint=fingerprint) is result
|
||||||
|
|
||||||
|
|
||||||
|
@mark.parametrize(
|
||||||
|
"trusts, trust, result",
|
||||||
|
[
|
||||||
|
(
|
||||||
|
{Fingerprint("foo"): Trust.full, Fingerprint("bar"): Trust.marginal},
|
||||||
|
Trust.full,
|
||||||
|
[Fingerprint("foo")],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{Fingerprint("foo"): Trust.full, Fingerprint("bar"): Trust.full},
|
||||||
|
Trust.full,
|
||||||
|
[Fingerprint("foo"), Fingerprint("bar")],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{Fingerprint("foo"): Trust.full, Fingerprint("bar"): Trust.marginal},
|
||||||
|
Trust.unknown,
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{},
|
||||||
|
Trust.unknown,
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_filter_fingerprints_by_trust(
|
||||||
|
trusts: Dict[Fingerprint, Trust], trust: Trust, result: List[Fingerprint]
|
||||||
|
) -> None:
|
||||||
|
assert util.filter_fingerprints_by_trust(trusts=trusts, trust=trust) == result
|
||||||
|
Loading…
Reference in New Issue
Block a user