feature(keyringctl): rework str simplification for printable uids

This commit is contained in:
Levente Polyak 2021-11-09 01:54:06 +01:00
parent 2030de06a0
commit bce5bc550e
No known key found for this signature in database
GPG Key ID: FC1B547C8D8172C8
4 changed files with 58 additions and 26 deletions

View File

@ -5,9 +5,7 @@ from collections.abc import Iterable
from itertools import chain
from logging import debug
from pathlib import Path
from re import escape
from re import match
from re import sub
from shutil import copytree
from tempfile import NamedTemporaryFile
from tempfile import mkdtemp
@ -33,6 +31,7 @@ from .types import Username
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 transform_fd_to_tmpfile
@ -161,7 +160,7 @@ def convert_certificate( # noqa: ignore=C901
elif packet.name.endswith("--UserID"):
current_packet_mode = "uid"
current_packet_fingerprint = None
current_packet_uid = simplify_user_id(Uid(packet_dump_field(packet, "Value")))
current_packet_uid = Uid(simplify_ascii(packet_dump_field(packet, "Value")))
uids[current_packet_uid] = packet
elif packet.name.endswith("--PublicSubkey"):
@ -493,24 +492,6 @@ def persist_uid_revocations(
packet_join(packets=[revocation], output=output_file, force=True)
def simplify_user_id(user_id: Uid) -> Uid:
"""Simplify the User ID string to contain more filesystem friendly characters
Parameters
----------
user_id: A User ID string (e.g. 'Foobar McFooface <foobar@foo.face>')
Returns
-------
The simplified representation of user_id
"""
user_id_str: str = user_id.replace("@", "_at_")
user_id_str = sub("[<>]", "", user_id_str)
user_id_str = sub("[" + escape(r" !@#$%^&*()_-+=[]{}\|;:,.<>/?") + "]", "_", user_id_str)
return Uid(user_id_str)
def derive_username_from_fingerprint(keyring_dir: Path, certificate_fingerprint: Fingerprint) -> Optional[Username]:
"""Attempt to derive the username of a public key fingerprint from a keyring directory

View File

@ -1,5 +1,4 @@
# SPDX-License-Identifier: GPL-3.0-or-later
from collections.abc import Iterable
from collections.abc import Iterator
from contextlib import contextmanager
@ -7,7 +6,11 @@ from os import chdir
from os import environ
from os import getcwd
from pathlib import Path
from re import escape
from re import split
from re import sub
from string import ascii_letters
from string import digits
from subprocess import STDOUT
from subprocess import CalledProcessError
from subprocess import check_output
@ -263,3 +266,51 @@ def filter_fingerprints_by_trust(trusts: Dict[Fingerprint, Trust], trust: Trust)
filter(lambda item: trust == item[1], trusts.items()),
)
)
simple_printable: str = ascii_letters + digits + "_-.+@"
ascii_mapping: Dict[str, str] = {
"àáâãäæąăǎа": "a",
"ćçĉċč": "c",
"ďđ": "d",
"éèêëęēĕėěɇ": "e",
"ĝğġģ": "g",
"ĥħȟ": "h",
"ìíîïĩīĭįıij": "i",
"ĵɉ": "j",
"ķ": "k",
"ł": "l",
"ńņň": "n",
"òóôõöøŏőðȍǿ": "o",
"śș": "s",
"ß": "ss",
"ț": "t",
"úûüȗűȕù": "u",
"ýÿ": "y",
"źż": "z",
}
ascii_mapping_lookup: Dict[str, str] = {}
for key, value in ascii_mapping.items():
for c in key:
if c in ascii_mapping_lookup:
raise Exception(f"duplicate ascii mapping: {c}")
ascii_mapping_lookup[c] = value
ascii_mapping_lookup[c.upper()] = value.upper()
def simplify_ascii(_str: str) -> str:
"""Simplify a string to contain more filesystem and printable friendly characters
Parameters
----------
_str: A string to simplify (e.g. 'Foobar McFooface <foobar@foo.face>')
Returns
-------
The simplified representation of _str
"""
_str = _str.strip("<")
_str = _str.strip(">")
_str = "".join([ascii_mapping_lookup.get(char) or char for char in _str])
_str = sub("[^" + escape(simple_printable) + "]", "_", _str)
return _str

View File

@ -10,7 +10,6 @@ from typing import Set
from libkeyringctl.keyring import export
from libkeyringctl.keyring import get_fingerprints_from_paths
from libkeyringctl.keyring import is_pgp_fingerprint
from libkeyringctl.keyring import simplify_user_id
from libkeyringctl.keyring import transform_fingerprint_to_keyring_path
from libkeyringctl.keyring import transform_username_to_keyring_path
from libkeyringctl.sequoia import packet_dump_field
@ -20,6 +19,7 @@ from libkeyringctl.types import PacketKind
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 system
@ -161,7 +161,7 @@ def verify_integrity(certificate: Path, all_fingerprints: Set[Fingerprint]) -> N
kind = kinds[0]
if kind != "User":
raise Exception(f"Unexpected packet in file {str(uid_path)}: {kind}")
uid_value = simplify_user_id(Uid(packet_dump_field(packet=uid_path, field="Value")))
uid_value = Uid(simplify_ascii(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

@ -25,7 +25,6 @@ from pytest import fixture
from libkeyringctl.keyring import convert_certificate
from libkeyringctl.keyring import export
from libkeyringctl.keyring import get_fingerprints_from_keyring_files
from libkeyringctl.keyring import simplify_user_id
from libkeyringctl.sequoia import certify
from libkeyringctl.sequoia import key_extract_certificate
from libkeyringctl.sequoia import key_generate
@ -35,6 +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 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 = simplify_user_id(uid)
simplified_uid = Uid(simplify_ascii(uid))
output: Path = (
working_dir