fix(keyringctl): avoid simplified uid collisions using a hash

Add a postfix hash of the raw uid data to the filenames to avoid
collisions with the simplified uid.
This commit is contained in:
Levente Polyak 2021-11-25 23:31:36 +01:00
parent 4821087b2b
commit 0ceb6c743e
No known key found for this signature in database
GPG Key ID: FC1B547C8D8172C8
4 changed files with 29 additions and 9 deletions

View File

@ -38,7 +38,7 @@ from .util import contains_fingerprint
from .util import filter_fingerprints_by_trust
from .util import get_cert_paths
from .util import get_fingerprint_from_partial
from .util import simplify_ascii
from .util import simplify_uid
from .util import transform_fd_to_tmpfile
PACKET_FILENAME_DATETIME_FORMAT: str = "%Y-%m-%d_%H-%M-%S"
@ -353,7 +353,7 @@ def convert_certificate(
elif packet.name.endswith("--UserID"):
current_packet_mode = "uid"
current_packet_fingerprint = None
current_packet_uid = Uid(simplify_ascii(packet_dump_field(packet, "Value")))
current_packet_uid = Uid(packet_dump_field(packet, "Value"))
if current_packet_uid in uids:
raise Exception(
@ -549,7 +549,8 @@ def persist_uids(
"""
for uid, uid_packet in uids.items():
output_file = key_dir / "uid" / uid / f"{uid}.asc"
simplified_uid = simplify_uid(uid)
output_file = key_dir / "uid" / simplified_uid / f"{simplified_uid}.asc"
output_file.parent.mkdir(parents=True, exist_ok=True)
debug(f"Writing file {output_file} from {uid_packet}")
packet_join(packets=[uid_packet], output=output_file, force=True)
@ -703,7 +704,7 @@ def persist_uid_certifications(
for uid, uid_certifications in certifications.items():
for issuer, issuer_certifications in uid_certifications.items():
certification_dir = key_dir / "uid" / uid / "certification"
certification_dir = key_dir / "uid" / simplify_uid(uid) / "certification"
certification_dir.mkdir(parents=True, exist_ok=True)
certification = latest_certification(issuer_certifications)
output_file = certification_dir / f"{issuer}.asc"
@ -728,7 +729,7 @@ def persist_uid_revocations(
for uid, uid_revocations in revocations.items():
for issuer, issuer_revocations in uid_revocations.items():
revocation_dir = key_dir / "uid" / uid / "revocation"
revocation_dir = key_dir / "uid" / simplify_uid(uid) / "revocation"
revocation_dir.mkdir(parents=True, exist_ok=True)
revocation = latest_certification(issuer_revocations)
output_file = revocation_dir / f"{issuer}.asc"

View File

@ -2,6 +2,7 @@
from collections.abc import Iterable
from collections.abc import Iterator
from contextlib import contextmanager
from hashlib import sha256
from os import chdir
from os import environ
from os import getcwd
@ -28,6 +29,7 @@ from typing import Union
from libkeyringctl.types import Fingerprint
from libkeyringctl.types import Trust
from libkeyringctl.types import Uid
@contextmanager
@ -312,3 +314,20 @@ def simplify_ascii(_str: str) -> str:
_str = "".join([ascii_mapping_lookup.get(char) or char for char in _str])
_str = sub("[^" + escape(simple_printable) + "]", "_", _str)
return _str
def simplify_uid(uid: Uid, hash_postfix: bool = True) -> str:
"""Simplify a uid to contain more filesystem and printable friendly characters with an optional
collision resistant hash postfix.
Parameters
----------
uid: Uid to simplify (e.g. 'Foobar McFooface <foobar@foo.face>')
hash_postfix: Whether to add a hash of the uid as postfix
Returns
-------
Simplified str representation of uid
"""
_hash = "" if not hash_postfix else f"_{sha256(uid.encode()).hexdigest()[:8]}"
return f"{simplify_ascii(_str=uid)}{_hash}"

View File

@ -18,7 +18,7 @@ from libkeyringctl.types import Fingerprint
from libkeyringctl.types import Uid
from libkeyringctl.util import get_cert_paths
from libkeyringctl.util import get_fingerprint_from_partial
from libkeyringctl.util import simplify_ascii
from libkeyringctl.util import simplify_uid
from libkeyringctl.util import system
@ -123,7 +123,7 @@ def verify_integrity(certificate: Path, all_fingerprints: Set[Fingerprint]) -> N
assert_packet_kind(path=uid_path, expected="User")
uid_value = Uid(simplify_ascii(packet_dump_field(packet=uid_path, field="Value")))
uid_value = simplify_uid(Uid(packet_dump_field(packet=uid_path, field="Value")))
if uid_value != uid.name:
raise Exception(f"Unexpected uid in file {str(uid_path)}: {uid_value}")
elif not uid_path.is_dir():

View File

@ -34,7 +34,7 @@ from libkeyringctl.types import Fingerprint
from libkeyringctl.types import Uid
from libkeyringctl.types import Username
from libkeyringctl.util import cwd
from libkeyringctl.util import simplify_ascii
from libkeyringctl.util import simplify_uid
from libkeyringctl.util import system
test_keys: Dict[Username, List[Path]] = defaultdict(list)
@ -133,7 +133,7 @@ def create_uid_certification(
certificate: Path = test_certificates[certified][0]
fingerprint: Fingerprint = Fingerprint(test_keyring_certificates[certified][0].name)
issuer_fingerprint: Fingerprint = Fingerprint(test_keyring_certificates[issuer][0].name)
simplified_uid = Uid(simplify_ascii(uid))
simplified_uid = simplify_uid(Uid(uid))
output: Path = (
working_dir