artemis_framework 0.1.1__tar.gz → 0.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: artemis_framework
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: A package for doing great things!
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -0,0 +1,193 @@
1
+ from __future__ import annotations
2
+
3
+ import sys
4
+ import textwrap
5
+ from pathlib import Path
6
+
7
+ from artemis_framework.asphodel.core.gpg_context import check_result
8
+
9
+
10
+ RSA_KEY_PARAMS = textwrap.dedent("""\
11
+ Key-Type: RSA
12
+ Key-Length: 4096
13
+ Subkey-Type: RSA
14
+ Subkey-Length: 4096
15
+ Name-Real: {name}
16
+ Name-Email: {email}
17
+ Name-Comment: {comment}
18
+ Expire-Date: {expire}
19
+ %no-protection
20
+ """)
21
+
22
+ ECC_KEY_PARAMS = textwrap.dedent("""\
23
+ Key-Type: EDDSA
24
+ Key-Curve: ed25519
25
+ Subkey-Type: ECDH
26
+ Subkey-Curve: cv25519
27
+ Name-Real: {name}
28
+ Name-Email: {email}
29
+ Name-Comment: {comment}
30
+ Expire-Date: {expire}
31
+ %no-protection
32
+ """)
33
+
34
+
35
+ def generate_key(
36
+ gpg: "gnupg.GPG", # type: ignore[name-defined]
37
+ name: str,
38
+ email: str,
39
+ *,
40
+ comment: str = "",
41
+ expire: str = "2y",
42
+ algorithm: str = "rsa",
43
+ passphrase: str | None = None
44
+ ) -> str | None:
45
+ template = RSA_KEY_PARAMS if algorithm.lower() == "rsa" else ECC_KEY_PARAMS
46
+
47
+ params = template.format(
48
+ name=name,
49
+ email=email,
50
+ comment=comment,
51
+ expire=expire
52
+ )
53
+
54
+ if passphrase:
55
+ params = params.replace(
56
+ "%no-protection",
57
+ f"Passphrase: {passphrase}"
58
+ )
59
+
60
+ print(f"Generating {algorithm.upper()} key for <{email}> … (this may take a moment)")
61
+ result = gpg.gen_key(params)
62
+
63
+ if check_result(result, label=f"gen_key({email})"):
64
+ return result.fingerprint
65
+ return None
66
+
67
+
68
+ def list_keys_verbose(gpg: "gnupg.GPG", *, secret: bool = False) -> list[dict]: # type: ignore[name-defined]
69
+ keys = gpg.list_keys(secret)
70
+ kind = "secret" if secret else "public"
71
+
72
+ print(f"\n{'='*60}")
73
+ print(f"{kind.upper()} KEYS ({len(keys)} total)")
74
+ print(f"{'='*60}")
75
+
76
+ for key in keys:
77
+ uid_str = "; ".join(key.get("uids", ["<no UID>"]))
78
+ print("\n")
79
+
80
+ print(f"Fingerprint : {key['fingerprint']}")
81
+ print(f"Key ID : {key['keyid']}")
82
+ print(f"UID(s) : {uid_str}")
83
+ print(f"Type/Length : {key.get('type', '?')} / {key.get('length', '?')}")
84
+ print(f"Trust : {key.get('trust', '?')}")
85
+ print(f"Expires : {key.get('expires', 'never') or 'never'}")
86
+ print(f"Subkeys : {len(key.get('subkeys', []))}")
87
+
88
+ return list(keys)
89
+
90
+
91
+ def find_key(gpg: "gnupg.GPG", identifier: str, *, secret: bool = False) -> dict | None: # type: ignore[name-defined]
92
+ keys = gpg.list_keys(secret)
93
+ ident_lower = identifier.lower()
94
+
95
+ for key in keys:
96
+ if ident_lower in key.get("fingerprint", "").lower():
97
+ return dict(key)
98
+ for uid in key.get("uids", []):
99
+ if ident_lower in uid.lower():
100
+ return dict(key)
101
+ return None
102
+
103
+
104
+ def export_public_key(
105
+ gpg: "gnupg.GPG", # type: ignore[name-defined]
106
+ fingerprint: str,
107
+ *,
108
+ armor: bool = True,
109
+ output_path: Path | None = None
110
+ ) -> str | bytes:
111
+ data = gpg.export_keys(fingerprint, armor=armor)
112
+
113
+ if not data:
114
+ print(f"[ERR] export_public_key: no data returned for {fingerprint}")
115
+ return b"" if not armor else ""
116
+
117
+ if output_path:
118
+ Path(output_path).write_text(data) if armor else Path(output_path).write_bytes(data.encode())
119
+ print(f"[OK] Public key written to » {output_path}")
120
+ else:
121
+ print(f"[OK] export_public_key({fingerprint[:16]}…)")
122
+
123
+ return data
124
+
125
+
126
+ def export_secret_key(
127
+ gpg: "gnupg.GPG", # type: ignore[name-defined]
128
+ fingerprint: str,
129
+ *,
130
+ armor: bool = True,
131
+ passphrase: str | None = None,
132
+ output_path: Path | None = None
133
+ ) -> str | bytes:
134
+ data = gpg.export_keys(
135
+ fingerprint,
136
+ secret=True,
137
+ armor=armor,
138
+ passphrase=passphrase
139
+ )
140
+
141
+ if not data:
142
+ print(f"[ERR] export_secret_key: no data returned for {fingerprint}")
143
+ return b"" if not armor else ""
144
+
145
+ if output_path:
146
+ Path(output_path).write_text(data) if armor else Path(output_path).write_bytes(data.encode())
147
+ print(f"[OK] Secret key written to » {output_path}")
148
+ else:
149
+ print(f"[OK] secret_key_export({fingerprint[:16]}…)")
150
+
151
+ return data
152
+
153
+
154
+ def import_key_data(
155
+ gpg: "gnupg.GPG", # type: ignore[name-defined],
156
+ key_data: str | bytes,
157
+ *,
158
+ label: str = "import"
159
+ ) -> list[str]:
160
+ result = gpg.import_keys(key_data)
161
+ check_result(result, label=label)
162
+
163
+ fps = result.fingerprints
164
+ print(f" imported : {len(fps)} key(s)")
165
+ for fp in fps:
166
+ print(f" {fp}")
167
+
168
+ return fps
169
+
170
+
171
+ def import_key_file(
172
+ gpg: "gnupg.GPG", # type: ignore[name-defined],
173
+ path: Path | str,
174
+ *,
175
+ label: str | None = None
176
+ ) -> list[str]:
177
+ p = Path(path)
178
+ label = label or f"import_key_file({p.name})"
179
+ key_data = p.read_text() if p.suffix in (".asc", ".txt") else p.read_bytes()
180
+ return import_key_data(gpg, key_data, label=label)
181
+
182
+
183
+ def delete_key(
184
+ gpg: "gnupg.GPG", # type: ignore
185
+ fingerprint: str,
186
+ *,
187
+ secret: bool = False,
188
+ passphrase: str | None = None
189
+ ) -> bool:
190
+ kind = "secret" if secret else "public"
191
+ result = gpg.delete_keys(fingerprint, secret=secret, passphrase=passphrase)
192
+ return check_result(result, label=f"delete_key({kind}, {fingerprint[:16]}…)")
193
+
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "artemis_framework"
3
- version = "0.1.1"
3
+ version = "0.1.2"
4
4
  description = "A package for doing great things!"
5
5
  authors = ["Dylan Garrett"]
6
6
  license = "MIT"