feature(keyringctl): write all packet files with full issuer fingerprint

This avoids collision between same issuer using key-id and full
fingerprint in different versions of a packet, like signature.
This commit is contained in:
Levente Polyak 2021-11-07 21:52:33 +01:00
parent 279765b22a
commit d9e9453d84
No known key found for this signature in database
GPG Key ID: FC1B547C8D8172C8
2 changed files with 29 additions and 3 deletions

View File

@ -35,6 +35,7 @@ from .types import Username
from .util import filter_fingerprints_by_trust from .util import filter_fingerprints_by_trust
from .util import get_cert_paths from .util import get_cert_paths
from .util import system from .util import system
from .util import get_fingerprint_from_partial
from .util import transform_fd_to_tmpfile from .util import transform_fd_to_tmpfile
@ -176,7 +177,13 @@ def convert_certificate( # noqa: ignore=C901
if not certificate_fingerprint: if not certificate_fingerprint:
raise Exception('missing certificate fingerprint for "{packet.name}"') raise Exception('missing certificate fingerprint for "{packet.name}"')
issuer: Fingerprint = Fingerprint(packet_dump_field(packet, "Issuer")) issuer = get_fingerprint_from_partial(
fingerprint_filter or set(), Fingerprint(packet_dump_field(packet, "Issuer"))
)
if not issuer:
debug(f"failed to resolve partial fingerprint {issuer}, skipping packet")
continue
signature_type = packet_dump_field(packet, "Type") signature_type = packet_dump_field(packet, "Type")
if current_packet_mode == "pubkey": if current_packet_mode == "pubkey":
@ -209,6 +216,12 @@ def convert_certificate( # noqa: ignore=C901
if not current_packet_fingerprint: if not current_packet_fingerprint:
raise Exception('missing current packet fingerprint for "{packet.name}"') raise Exception('missing current packet fingerprint for "{packet.name}"')
issuer = get_fingerprint_from_partial(
fingerprint_filter or set(), Fingerprint(packet_dump_field(packet, "Issuer"))
)
if issuer != certificate_fingerprint:
raise Exception(f"subkey packet does not belong to {certificate_fingerprint}, issuer: {issuer}")
if signature_type == "SubkeyBinding": if signature_type == "SubkeyBinding":
subkey_bindings[current_packet_fingerprint].append(packet) subkey_bindings[current_packet_fingerprint].append(packet)
elif signature_type == "SubkeyRevocation": elif signature_type == "SubkeyRevocation":
@ -260,11 +273,13 @@ def convert_certificate( # noqa: ignore=C901
persist_subkey_bindings( persist_subkey_bindings(
key_dir=key_dir, key_dir=key_dir,
subkey_bindings=subkey_bindings, subkey_bindings=subkey_bindings,
issuer=certificate_fingerprint,
) )
persist_subkey_revocations( persist_subkey_revocations(
key_dir=key_dir, key_dir=key_dir,
subkey_revocations=subkey_revocations, subkey_revocations=subkey_revocations,
issuer=certificate_fingerprint,
) )
persist_uids( persist_uids(
@ -350,6 +365,7 @@ def persist_subkeys(
def persist_subkey_bindings( def persist_subkey_bindings(
key_dir: Path, key_dir: Path,
subkey_bindings: Dict[Fingerprint, List[Path]], subkey_bindings: Dict[Fingerprint, List[Path]],
issuer: Fingerprint,
) -> None: ) -> None:
"""Persist all SubkeyBinding of a root key file to file(s) """Persist all SubkeyBinding of a root key file to file(s)
@ -357,11 +373,11 @@ def persist_subkey_bindings(
---------- ----------
key_dir: The root directory below which the basic key material is persisted key_dir: The root directory below which the basic key material is persisted
subkey_bindings: The SubkeyBinding signatures of a Public-Subkey subkey_bindings: The SubkeyBinding signatures of a Public-Subkey
issuer: Fingerprint of the issuer
""" """
for fingerprint, bindings in subkey_bindings.items(): for fingerprint, bindings in subkey_bindings.items():
subkey_binding = latest_certification(bindings) subkey_binding = latest_certification(bindings)
issuer = packet_dump_field(subkey_binding, "Issuer")
output_file = key_dir / "subkey" / fingerprint / "certification" / f"{issuer}.asc" output_file = key_dir / "subkey" / fingerprint / "certification" / f"{issuer}.asc"
output_file.parent.mkdir(parents=True, exist_ok=True) output_file.parent.mkdir(parents=True, exist_ok=True)
debug(f"Writing file {output_file} from {str(subkey_binding)}") debug(f"Writing file {output_file} from {str(subkey_binding)}")
@ -371,6 +387,7 @@ def persist_subkey_bindings(
def persist_subkey_revocations( def persist_subkey_revocations(
key_dir: Path, key_dir: Path,
subkey_revocations: Dict[Fingerprint, List[Path]], subkey_revocations: Dict[Fingerprint, List[Path]],
issuer: Fingerprint,
) -> None: ) -> None:
"""Persist the SubkeyRevocations of all Public-Subkeys of a root key to file(s) """Persist the SubkeyRevocations of all Public-Subkeys of a root key to file(s)
@ -378,11 +395,11 @@ def persist_subkey_revocations(
---------- ----------
key_dir: The root directory below which the basic key material is persisted key_dir: The root directory below which the basic key material is persisted
subkey_revocations: The SubkeyRevocations of PublicSubkeys of a key subkey_revocations: The SubkeyRevocations of PublicSubkeys of a key
issuer: Fingerprint of the issuer
""" """
for fingerprint, revocations in subkey_revocations.items(): for fingerprint, revocations in subkey_revocations.items():
revocation = latest_certification(revocations) revocation = latest_certification(revocations)
issuer = packet_dump_field(revocation, "Issuer")
output_file = key_dir / "subkey" / fingerprint / "revocation" / f"{issuer}.asc" output_file = key_dir / "subkey" / fingerprint / "revocation" / f"{issuer}.asc"
output_file.parent.mkdir(parents=True, exist_ok=True) output_file.parent.mkdir(parents=True, exist_ok=True)
debug(f"Writing file {output_file} from {revocation}") debug(f"Writing file {output_file} from {revocation}")

View File

@ -18,6 +18,7 @@ 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 export
from libkeyringctl.keyring import get_fingerprints_from_keyring_files
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
@ -86,10 +87,16 @@ def create_certificate(
target_dir = keyring_root / keyring_type target_dir = keyring_root / keyring_type
for fingerprint in get_fingerprints_from_keyring_files(
working_dir=working_dir, source=[certificate_file]
).keys():
test_all_fingerprints.add(fingerprint)
decomposed_path: Path = convert_certificate( decomposed_path: Path = convert_certificate(
working_dir=working_dir, working_dir=working_dir,
certificate=certificate_file, certificate=certificate_file,
keyring_dir=keyring_root / keyring_type, keyring_dir=keyring_root / keyring_type,
fingerprint_filter=test_all_fingerprints,
) )
user_dir = decomposed_path.parent user_dir = decomposed_path.parent
(target_dir / user_dir.name).mkdir(parents=True, exist_ok=True) (target_dir / user_dir.name).mkdir(parents=True, exist_ok=True)
@ -165,6 +172,7 @@ def create_key_revocation(
working_dir=working_dir, working_dir=working_dir,
certificate=revocation, certificate=revocation,
keyring_dir=keyring_root / keyring_type, keyring_dir=keyring_root / keyring_type,
fingerprint_filter=test_all_fingerprints,
) )
user_dir = decomposed_path.parent user_dir = decomposed_path.parent
(target_dir / user_dir.name).mkdir(parents=True, exist_ok=True) (target_dir / user_dir.name).mkdir(parents=True, exist_ok=True)
@ -254,6 +262,7 @@ def create_signature_revocation(
working_dir=working_dir, working_dir=working_dir,
certificate=certificate_path, certificate=certificate_path,
keyring_dir=target_dir, keyring_dir=target_dir,
fingerprint_filter=test_all_fingerprints,
) )
user_dir = decomposed_path.parent user_dir = decomposed_path.parent
(target_dir / user_dir.name).mkdir(parents=True, exist_ok=True) (target_dir / user_dir.name).mkdir(parents=True, exist_ok=True)