feature(keyringctl): avoid splitting a certificate multiple times

Move the name cascade to derive the username into the
`convert_certificate` function which allows to use the
certificate_fingerprint directly instead of trying to find it by
splitting the certificate one more time before converting.
This commit is contained in:
Levente Polyak 2021-10-19 19:10:51 +02:00
parent cf6bac5fd9
commit 60ee3d6d94
No known key found for this signature in database
GPG Key ID: FC1B547C8D8172C8

View File

@ -139,12 +139,14 @@ def system(cmd: List[str], exit_on_error: bool = True) -> str:
def convert_certificate( # noqa: ignore=C901 def convert_certificate( # noqa: ignore=C901
working_dir: Path, working_dir: Path,
certificate: Path, certificate: Path,
keyring_dir: Path,
name_override: Optional[str] = None, name_override: Optional[str] = None,
fingerprint_filter: Optional[Set[Fingerprint]] = None, fingerprint_filter: Optional[Set[Fingerprint]] = None,
) -> Path: ) -> Path:
"""Convert a single file public key certificate into a decomposed directory structure of multiple PGP packets """Convert a single file public key certificate into a decomposed directory structure of multiple PGP packets
The output directory structure is created per user. The username is derived from the certificate (or overridden). The output directory structure is created per user. The username is derived from the certificate via
`derive_username_from_fingerprint` or overridden via `name_override`.
Below the username directory a directory tree describes the public keys components split up into certifications Below the username directory a directory tree describes the public keys components split up into certifications
and revocations, as well as per subkey and per uid certifications and revocations. and revocations, as well as per subkey and per uid certifications and revocations.
@ -154,6 +156,8 @@ def convert_certificate( # noqa: ignore=C901
The path of the working directory below which to create split certificates The path of the working directory below which to create split certificates
certificate: Path certificate: Path
The path to a public key certificate The path to a public key certificate
keyring_dir: Path
The path of the keyring used to try to derive the username from the public key fingerprint
name_override: Optional[str] name_override: Optional[str]
An optional string to override the username in the to be created output directory structure An optional string to override the username in the to be created output directory structure
fingerprint_filter: Optional[Set[Fingerprint]] fingerprint_filter: Optional[Set[Fingerprint]]
@ -189,7 +193,6 @@ def convert_certificate( # noqa: ignore=C901
revocations: Dict[Uid, List[Path]] = defaultdict(list) revocations: Dict[Uid, List[Path]] = defaultdict(list)
# intermediate variables # intermediate variables
username: str = name_override or certificate.stem
current_packet_mode: Optional[str] = None current_packet_mode: Optional[str] = None
current_packet_fingerprint: Optional[Fingerprint] = None current_packet_fingerprint: Optional[Fingerprint] = None
current_packet_uid: Optional[Uid] = None current_packet_uid: Optional[Uid] = None
@ -275,7 +278,10 @@ def convert_certificate( # noqa: ignore=C901
if not pubkey: if not pubkey:
raise Exception("missing certificate public-key") raise Exception("missing certificate public-key")
user_dir = working_dir / username name_override = name_override or derive_username_from_fingerprint(keyring_dir=keyring_dir,
certificate_fingerprint=certificate_fingerprint) or certificate.stem
user_dir = working_dir / name_override
key_dir = user_dir / certificate_fingerprint key_dir = user_dir / certificate_fingerprint
key_dir.mkdir(parents=True, exist_ok=True) key_dir.mkdir(parents=True, exist_ok=True)
@ -691,17 +697,15 @@ def simplify_user_id(user_id: Uid) -> Uid:
return Uid(user_id_str) return Uid(user_id_str)
def derive_user_from_target(working_dir: Path, target_dir: Path, certificate: Path) -> Optional[str]: def derive_username_from_fingerprint(keyring_dir: Path, certificate_fingerprint: Fingerprint) -> Optional[str]:
"""Attempt to derive the username of a public key from a target directory """Attempt to derive the username of a public key fingerprint from a keyring directory
Parameters Parameters
---------- ----------
working_dir: Path keyring_dir: Path
A directory to use for temporary files
target_dir: Path
The directory in which to look up a username The directory in which to look up a username
certificate: Path certificate_fingerprint: Fingerprint
A public key file The public key fingerprint to derive the username from
Raises Raises
------ ------
@ -714,52 +718,18 @@ def derive_user_from_target(working_dir: Path, target_dir: Path, certificate: Pa
A string representing the username a public key certificate belongs to, None otherwise A string representing the username a public key certificate belongs to, None otherwise
""" """
def get_certificate_fingerprint(working_dir: Path, certificate: Path) -> str: matches = list(keyring_dir.glob(f"*/{certificate_fingerprint}"))
"""Get the certificate fingerprint from a PGP public key file
Parameters
----------
working_dir: Path
A directory to use for temporary files
certificate: Path
A PGP public key file
Raises
------
Exception
If no fingerprint can be found in the provided PGP public key file
Returns
-------
str
The fingerprint of the PGP public key file
"""
certificate_fingerprint = ""
for packet in packet_split(working_dir=working_dir, certificate=certificate):
if packet.name.endswith("--PublicKey"):
certificate_fingerprint = packet_dump_field(packet, "Fingerprint")
if not certificate_fingerprint:
raise Exception(f"The public key file {certificate} does not provide a PublicKey fingerprint.")
return certificate_fingerprint
certificate_fingerprint = get_certificate_fingerprint(working_dir=working_dir, certificate=certificate)
debug(f"Derived fingerprint '{certificate_fingerprint}' from {certificate}")
matches = list(target_dir.glob(f"*/{certificate_fingerprint}"))
if len(matches) > 1: if len(matches) > 1:
raise Exception( raise Exception(
f"More than one username found in {target_dir} when probing for fingerprint '{certificate_fingerprint}'!" f"More than one username found in {keyring_dir} when probing for fingerprint '{certificate_fingerprint}': {matches}"
) )
elif len(matches) == 0: elif not matches:
debug(f"Can not derive username from target directory for certificate {certificate}") debug(f"Can not derive username from target directory for fingerprint {certificate_fingerprint}")
return None return None
else: else:
username = matches[0].parent.stem username = matches[0].parent.stem
debug(f"Successfully derived username '{username}' from target directory for certificate {certificate}") debug(f"Successfully derived username '{username}' from target directory for fingerprint {certificate_fingerprint}")
return username return username
@ -798,15 +768,12 @@ def convert(
for key in keys: for key in keys:
for cert in keyring_split(working_dir=working_dir, keyring=key): for cert in keyring_split(working_dir=working_dir, keyring=key):
name = (
name_override or derive_user_from_target(working_dir=working_dir, target_dir=target_dir,
certificate=cert) or key.stem
)
directories.append( directories.append(
convert_certificate( convert_certificate(
working_dir=working_dir, working_dir=working_dir,
certificate=cert, certificate=cert,
name_override=name, keyring_dir=target_dir,
name_override=name_override,
fingerprint_filter=fingerprint_filter, fingerprint_filter=fingerprint_filter,
) )
) )