keyringctl: Write User IDs to separate files

keyringctl:
Add `persist_uids()` to write User ID related packets: User-ID and
PositiveCertifications (UID binding signatures).
Rename `persist_basic_key()` to `persist_public_key()` and change it to
only persist the PublicKey packet.
Change `persist_{certifications,revocations}()` to persist the
certificates to a key-specific 'uids' subdirectory per PublicKey.
Change `convert_certificate()` to rename `uid_binding_sig` to
`uid_bind_sigs`. Simplify the logic for signature related data
assignments.
This commit is contained in:
David Runge 2021-10-09 23:42:13 +02:00 committed by Levente Polyak
parent 7e04c50a16
commit 3c31230eb2
No known key found for this signature in database
GPG Key ID: FC1B547C8D8172C8

View File

@ -84,7 +84,7 @@ def convert_certificate(working_dir: Path, certificate: Path, name_override: Opt
current_packet_mode: Optional[str] = None current_packet_mode: Optional[str] = None
current_packet_key: Optional[str] = None current_packet_key: Optional[str] = None
uids: Dict[str, Path] = {} uids: Dict[str, Path] = {}
uid_binding_sig: Dict[str, Path] = {} uid_binding_sigs: Dict[str, Path] = {}
subkeys: Dict[str, Dict[str, Path]] = {} subkeys: Dict[str, Dict[str, Path]] = {}
subkey_binding_sigs: Dict[str, Dict[str, Path]] = {} subkey_binding_sigs: Dict[str, Dict[str, Path]] = {}
subkey_revocations: Dict[str, Dict[str, Path]] = {} subkey_revocations: Dict[str, Dict[str, Path]] = {}
@ -134,8 +134,7 @@ def convert_certificate(working_dir: Path, certificate: Path, name_override: Opt
current_packet_mode = 'pubkey' current_packet_mode = 'pubkey'
current_packet_key = certificate_fingerprint current_packet_key = certificate_fingerprint
elif packet.name.endswith('--UserID'): elif packet.name.endswith('--UserID'):
value = packet_dump_field(packet, 'Value') value = simplify_user_id(packet_dump_field(packet, 'Value'))
value = simplify_user_id(value)
current_packet_mode = 'uid' current_packet_mode = 'uid'
current_packet_key = value current_packet_key = value
uids[value] = packet uids[value] = packet
@ -161,53 +160,32 @@ def convert_certificate(working_dir: Path, certificate: Path, name_override: Opt
issuer = packet_dump_field(packet, 'Issuer') issuer = packet_dump_field(packet, 'Issuer')
signature_type = packet_dump_field(packet, 'Type') signature_type = packet_dump_field(packet, 'Type')
if signature_type == 'DirectKey': if current_packet_mode == 'pubkey':
direct_sigs = add_packet_to_direct_sigs( if signature_type == 'KeyRevocation' and certificate_fingerprint.endswith(issuer):
direct_sigs=direct_sigs, direct_revocations = add_packet_to_direct_sigs(
issuer=issuer, direct_sigs=direct_revocations,
packet_key=current_packet_key, issuer=issuer,
packet=packet, packet_key=current_packet_key,
) packet=packet,
continue )
elif signature_type in ['DirectKey', 'GenericCertification']:
if not current_packet_key: direct_sigs = add_packet_to_direct_sigs(
# TODO GenericCertification PersonaCertification CasualCertification PositiveCertification direct_sigs=direct_sigs,
raise Exception(f'unknown packet key for "{packet.name}"') issuer=issuer,
packet_key=current_packet_key,
if current_packet_mode == 'uid' or current_packet_mode == 'pubkey': packet=packet,
if certificate_fingerprint.endswith(issuer): )
if signature_type == 'PositiveCertification':
uid_binding_sig[current_packet_key] = packet
elif signature_type == 'CertificationRevocation':
revocations[current_packet_key].append(packet)
elif signature_type == 'KeyRevocation':
direct_revocations = add_packet_to_direct_sigs(
direct_sigs=direct_revocations,
issuer=issuer,
packet_key=current_packet_key,
packet=packet,
)
else:
raise Exception(f'unknown signature type: {signature_type}')
else: else:
if signature_type.endswith('Certification'): raise Exception(f'unknown signature type: {signature_type}')
# NOTE: here we are only considering signatures directly on the root key elif current_packet_mode == 'uid':
# signatures on a User ID, that are not tied to it via a SubkeyBinding are not addressed if signature_type == 'CertificationRevocation':
if current_packet_key not in uids: revocations[current_packet_key].append(packet)
direct_sigs = add_packet_to_direct_sigs( elif signature_type == 'PositiveCertification' and certificate_fingerprint.endswith(issuer):
direct_sigs=direct_sigs, uid_binding_sigs[current_packet_key] = packet
issuer=issuer, elif signature_type.endswith('Certification'):
packet_key=current_packet_key, certifications[current_packet_key].append(packet)
packet=packet, else:
) raise Exception(f'unknown signature type: {signature_type}')
# NOTE: here we address all signatures on User IDs (those that are tied to it with a
# SubkeyBinding and those that are not)
else:
certifications[current_packet_key].append(packet)
elif signature_type == 'CertificationRevocation':
revocations[current_packet_key].append(packet)
else:
raise Exception(f'unknown signature type: {signature_type}')
elif current_packet_mode == 'subkey': elif current_packet_mode == 'subkey':
if signature_type == 'SubkeyBinding': if signature_type == 'SubkeyBinding':
if not subkey_binding_sigs.get(certificate_fingerprint): if not subkey_binding_sigs.get(certificate_fingerprint):
@ -236,11 +214,15 @@ def convert_certificate(working_dir: Path, certificate: Path, name_override: Opt
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)
persist_basic_key( persist_public_key(
certificate_fingerprint=certificate_fingerprint, certificate_fingerprint=certificate_fingerprint,
pubkey=pubkey, pubkey=pubkey,
key_dir=key_dir, key_dir=key_dir,
uid_binding_sig=uid_binding_sig, )
persist_uids(
key_dir=key_dir,
uid_binding_sigs=uid_binding_sigs,
uids=uids, uids=uids,
) )
@ -274,7 +256,7 @@ def convert_certificate(working_dir: Path, certificate: Path, name_override: Opt
certifications=certifications, certifications=certifications,
pubkey=pubkey, pubkey=pubkey,
key_dir=key_dir, key_dir=key_dir,
uid_binding_sig=uid_binding_sig, uid_binding_sig=uid_binding_sigs,
uids=uids, uids=uids,
) )
@ -282,25 +264,19 @@ def convert_certificate(working_dir: Path, certificate: Path, name_override: Opt
pubkey=pubkey, pubkey=pubkey,
revocations=revocations, revocations=revocations,
key_dir=key_dir, key_dir=key_dir,
uid_binding_sig=uid_binding_sig, uid_binding_sig=uid_binding_sigs,
uids=uids, uids=uids,
) )
return user_dir return user_dir
def persist_basic_key( def persist_public_key(
certificate_fingerprint: str, certificate_fingerprint: str,
pubkey: Path, pubkey: Path,
key_dir: Path, key_dir: Path,
uid_binding_sig: Dict[str, Path],
uids: Dict[str, Path],
) -> None: ) -> None:
"""Persist the basic key material of a root key to file """Persist the Public-Key packet
The basic key material consists of the root key's public key, any PublicSubkeys and their SubkeyBindings, all User
IDs and the per User ID PositiveCertifications.
The file is written to key_dir and is named after the root key's certificate fingerprint.
Parameters Parameters
---------- ----------
@ -310,19 +286,40 @@ def persist_basic_key(
The path to the public key of the root key The path to the public key of the root key
key_dir: Path key_dir: Path
The root directory below which the basic key material is persisted The root directory below which the basic key material is persisted
uid_binding_sig: Dict[str, Path] """
packets: List[Path] = [pubkey]
output_file = key_dir / f'{certificate_fingerprint}.asc'
debug(f'Writing file {output_file} from {[str(packet) for packet in packets]}')
packet_join(packets, output_file)
def persist_uids(
key_dir: Path,
uid_binding_sigs: Dict[str, Path],
uids: Dict[str, Path],
) -> None:
"""Persist the User IDs that belong to a PublicKey
The User ID material consists of PublicSubkeys and their SubkeyBindings.
The files are written to a UID specific directory and file below key_dir/uids.
Parameters
----------
key_dir: Path
The root directory below which the basic key material is persisted
uid_binding_sigs: Dict[str, Path]
The PositiveCertifications of a User ID and Public-Key packet The PositiveCertifications of a User ID and Public-Key packet
uids: Dict[str, Path] uids: Dict[str, Path]
The User IDs of a Public-Key (the root key) The User IDs of a Public-Key (the root key)
""" """
packets: List[Path] = [pubkey] for key in uid_binding_sigs.keys():
for key in uid_binding_sig.keys(): packets = [uids[key], uid_binding_sigs[key]]
packets.extend([uids[key], uid_binding_sig[key]]) output_file = key_dir / f'uids/{key}/{key}.asc'
(key_dir / Path(f'uids/{key}')).mkdir(parents=True, exist_ok=True)
output_file = key_dir / f'{certificate_fingerprint}.asc' debug(f'Writing file {output_file} from {[str(packet) for packet in packets]}')
debug(f'Writing file {output_file} from {[str(packet) for packet in packets]}') packet_join(packets, output_file)
packet_join(packets, output_file)
def persist_subkeys( def persist_subkeys(
@ -441,7 +438,7 @@ def persist_certifications(
for key, current_certifications in certifications.items(): for key, current_certifications in certifications.items():
for certification in current_certifications: for certification in current_certifications:
certification_dir = key_dir / key / 'certification' certification_dir = key_dir / Path("uids") / key / 'certification'
certification_dir.mkdir(parents=True, exist_ok=True) certification_dir.mkdir(parents=True, exist_ok=True)
issuer = packet_dump_field(certification, 'Issuer') issuer = packet_dump_field(certification, 'Issuer')
@ -479,7 +476,7 @@ def persist_revocations(
for key, current_revocations in revocations.items(): for key, current_revocations in revocations.items():
for revocation in current_revocations: for revocation in current_revocations:
revocation_dir = key_dir / key / 'revocation' revocation_dir = key_dir / Path("uids") / key / 'revocation'
revocation_dir.mkdir(parents=True, exist_ok=True) revocation_dir.mkdir(parents=True, exist_ok=True)
issuer = packet_dump_field(revocation, 'Issuer') issuer = packet_dump_field(revocation, 'Issuer')