new version: display info about a key, to show the keyid and recipient name

This commit is contained in:
q
2024-07-19 12:14:09 +03:00
parent 36360a29d8
commit 871369e7c9

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
__version__ = "20240627.1" __version__ = "20240719.0"
import argparse import argparse
import os import os
@@ -8,6 +8,7 @@ import shutil
import sys import sys
import tempfile import tempfile
import time import time
from datetime import datetime
from getpass import getpass from getpass import getpass
try: try:
@@ -149,21 +150,20 @@ Usage
action="store_true", action="store_true",
help="Decrypt files", help="Decrypt files",
) )
commands_grp.add_argument(
"--info",
"-i",
default=False,
action="store_true",
help="Show key info",
)
crypt_grp = parser.add_argument_group("Encryption", "Encryption and Decryption options") crypt_grp = parser.add_argument_group("Encryption", "Encryption and Decryption options")
crypt_grp.add_argument( crypt_grp.add_argument(
"--key", "--key",
help="Path to key file. For keygen: private key to write; encrypt: public key file, decrypt: private key file. If not defined, symmetric password encryption is used.", help="Path to key file. For keygen: private key to write; encrypt: public key file, decrypt: private key file. If not defined, symmetric password encryption is used.",
) )
keygen_grp = parser.add_argument_group(
"Keygen", "Options for keygen. Note: --key is required for keygen, see above."
)
keygen_grp.add_argument(
"--name",
help="Name of the owner of the key (usually email) Default: %(default)s",
required=False,
default="recipient@address",
)
crypt_grp.add_argument( crypt_grp.add_argument(
"--recursive", "--recursive",
"-r", "-r",
@@ -177,6 +177,19 @@ Usage
action="store_true", action="store_true",
help="Overwrite existing files", help="Overwrite existing files",
) )
keygen_grp = parser.add_argument_group(
"Keygen", "Options for keygen. Note: --key is required for keygen, see Encryption."
)
keygen_grp.add_argument(
"--name",
help="Name of the owner of the key (usually email) Default: %(default)s",
required=False,
default="recipient@address",
)
info_grp = parser.add_argument_group(
"Info", "Options for info. Note: --key is required for info. Give the private key file name."
)
misc_grp = parser.add_argument_group("Misc", "Other options") misc_grp = parser.add_argument_group("Misc", "Other options")
misc_grp.add_argument("--width", help="Console width in characters. Defaults to auto detect.") misc_grp.add_argument("--width", help="Console width in characters. Defaults to auto detect.")
misc_grp.add_argument("--no-progress", help="Disable progress meter.", default=False, action="store_true") misc_grp.add_argument("--no-progress", help="Disable progress meter.", default=False, action="store_true")
@@ -191,17 +204,22 @@ Usage
) )
parsed = parser.parse_args() parsed = parser.parse_args()
for cmd in zip(("keygen", "encrypt", "decrypt"), (parsed.keygen, parsed.encrypt, parsed.decrypt)): for cmd in zip(
("keygen", "encrypt", "decrypt", "info"), (parsed.keygen, parsed.encrypt, parsed.decrypt, parsed.info)
):
if cmd[1]: if cmd[1]:
parsed.command = cmd[0] parsed.command = cmd[0]
if parsed.command == "keygen":
if parsed.command in ("keygen", "info"):
if parsed.key is None: if parsed.key is None:
parser.error("--key required for keygen") parser.error(f"--key required for {parsed.command}")
return parsed return parsed
class Collector: class Collector:
"""Data collector for file streaming. Prints progress"""
def __init__(self, filename, width, bufcount): def __init__(self, filename, width, bufcount):
self.progress = MiniProgress(bufcount, width) self.progress = MiniProgress(bufcount, width)
@@ -217,6 +235,8 @@ class Collector:
class Processor: class Processor:
"""Main processor for the program"""
def __init__(self): def __init__(self):
self.opts = get_opts() self.opts = get_opts()
self.homedir = tempfile.TemporaryDirectory() self.homedir = tempfile.TemporaryDirectory()
@@ -230,6 +250,10 @@ class Processor:
self.keygen() self.keygen()
filelist = [] filelist = []
if self.opts.command == "info":
self.key_info()
filelist = []
if self.opts.command in ("encrypt", "decrypt"): if self.opts.command in ("encrypt", "decrypt"):
if not self.symmetric: if not self.symmetric:
import_result = self.gpg.import_keys_file(self.opts.key) import_result = self.gpg.import_keys_file(self.opts.key)
@@ -257,7 +281,7 @@ class Processor:
self.homedir.cleanup() self.homedir.cleanup()
def set_phrase(self, twice=False): def set_phrase(self, twice=False):
"""Sets self.phrase. if `twice` asks user for phrase twice."""
if not self.phrase is None: if not self.phrase is None:
# phrase already set # phrase already set
return return
@@ -281,6 +305,7 @@ class Processor:
sys.exit(1) sys.exit(1)
def get_filelist(self, root, recurse, direction): def get_filelist(self, root, recurse, direction):
"""returns a file list: if encrypting, lists files without .gpg, if decrytping, lists files with .gpg"""
if not recurse: if not recurse:
if os.path.isfile(root): if os.path.isfile(root):
return [root] return [root]
@@ -298,6 +323,7 @@ class Processor:
return filelist return filelist
def keygen(self): def keygen(self):
"""Main key generator function"""
if not self.opts.force: if not self.opts.force:
for f in (self.opts.key, self.opts.key + ".pub"): for f in (self.opts.key, self.opts.key + ".pub"):
if os.path.exists(f): if os.path.exists(f):
@@ -325,7 +351,52 @@ class Processor:
os.chmod(self.opts.key, 0o600) os.chmod(self.opts.key, 0o600)
print(f"Generated {self.opts.key} and {self.opts.key}.pub", file=sys.stderr) print(f"Generated {self.opts.key} and {self.opts.key}.pub", file=sys.stderr)
def key_info(self):
"""Main key info printing function"""
def print_key(key):
key_type = "NA"
if key.get("type") == "sec":
key_type = "Private"
if key.get("type") == "pub":
key_type = "Public"
try:
key_date = datetime.fromtimestamp(int(key["date"])).isoformat()
except Exception as e:
print(e, file=sys.stderr)
key_date = "NA"
print(
f"""Fingerprint: {key.get('fingerprint')}
KeyId: {' '.join(string_chunk(key.get('keyid',''),8))}
Name: {', '.join(key.get('uids'))}
Type: {key_type}
Date: {key_date}
KeyLength: {key.get('length')}"""
)
import_result = self.gpg.import_keys_file(self.opts.key)
if len(import_result.fingerprints) == 0:
self.homedir.cleanup()
print(f"File does not contain private or public GPG keys!", file=sys.stderr)
sys.exit(1)
keys = self.gpg.list_keys(True)
for key in keys:
# Private keys
print_key(key)
if len(keys) > 0:
# if private keys found, do not print public keys
return
for key in self.gpg.list_keys():
# only public keys in the list
print_key(key)
def process_single(self, in_file): def process_single(self, in_file):
"""encrypt or decrypt single file"""
if self.opts.command == "encrypt": if self.opts.command == "encrypt":
auto_path = in_file + self.suffix auto_path = in_file + self.suffix
@@ -368,11 +439,17 @@ class Processor:
def strip_prefix(s, prefix): def strip_prefix(s, prefix):
"""Return string without a prefix"""
if s.startswith(prefix): if s.startswith(prefix):
return s[len(prefix) :] return s[len(prefix) :]
return s[:] return s[:]
def string_chunk(string, length):
"""Split string in to even chunks"""
return (string[0 + i : length + i] for i in range(0, len(string), length))
def main(): def main():
Processor() Processor()