artemis_framework 0.1.3__tar.gz → 0.1.4__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.3
3
+ Version: 0.1.4
4
4
  Summary: A package for doing great things!
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -0,0 +1,257 @@
1
+ from __future__ import annotations
2
+
3
+ import sys
4
+ from dataclasses import dataclass
5
+ from pathlib import Path
6
+
7
+ from artemis_framework.asphodel.core.gpg_context import build_gpg, check_result
8
+
9
+
10
+ @dataclass
11
+ class VerificationResult:
12
+ valid: bool
13
+ fingerprint: str
14
+ key_id: str
15
+ username: str
16
+ status: str
17
+ timestamp: int | None
18
+ expire_timestamp: int | None
19
+ trust_level: str
20
+ stderr: str
21
+
22
+ def __str__(self) -> str:
23
+ lines = (
24
+ f" valid : {self.valid}",
25
+ f" fingerprint : {self.fingerprint}",
26
+ f" key_id : {self.key_id}",
27
+ f" username : {self.username}",
28
+ f" status : {self.status}",
29
+ f" timestamp : {self.timestamp}",
30
+ f" trust_level : {self.trust_level}",
31
+ )
32
+ return "\n".join(lines)
33
+
34
+ @classmethod
35
+ def from_gnupg(cls, result) -> "VerificationResult":
36
+ sigs = getattr(result, "sig_info", {})
37
+
38
+ if sigs:
39
+ fp, info = next(iter(sigs.items()))
40
+ key_id = info.get("keyid", "")
41
+ username = info.get("username", "")
42
+ status = info.get("status", "")
43
+ ts = info.get("timestamp")
44
+ exp_ts = info.get("expire_timestamp")
45
+ trust = info.get("trust_level", "")
46
+ else:
47
+ fp = getattr(result, "fingerprint", "") or ""
48
+ key_id = getattr(result, "key_id", "") or ""
49
+ username = getattr(result, "username", "") or ""
50
+ status = getattr(result, "status", "") or ""
51
+ ts = getattr(result, "timestamp", None)
52
+ exp_ts = None
53
+ trust = getattr(result, "trust_level", "") or ""
54
+ valid = bool(getattr(result, "valid", False))
55
+
56
+ return cls(
57
+ valid=valid,
58
+ fingerprint=fp,
59
+ key_id=key_id,
60
+ username=username,
61
+ status=status,
62
+ timestamp=int(ts) if ts else None,
63
+ expire_timestamp=int(exp_ts) if exp_ts else None,
64
+ trust_level=trust,
65
+ stderr=getattr(result, "stderr", "") or ""
66
+ )
67
+
68
+
69
+ def clearsign(
70
+ gpg,
71
+ message: str | bytes,
72
+ signing_fingerprint: str,
73
+ *,
74
+ passphrase: str | None = None,
75
+ detach: bool = False
76
+ ) -> str | None:
77
+ result = gpg.sign(
78
+ message,
79
+ keyid=signing_fingerprint,
80
+ passphrase=passphrase,
81
+ clearsign=True
82
+ )
83
+
84
+ if not result:
85
+ print(f"[ERR] clearsign failed for <{signing_fingerprint[:16]}…>")
86
+ print(f" stderr: {result.stderr}")
87
+ return None
88
+
89
+ print(f"[OK] clearsign({signing_fingerprint[:16]}…)")
90
+ return str(result)
91
+
92
+
93
+ def sign_detached(
94
+ gpg,
95
+ message: str | bytes,
96
+ signing_fingerprint: str,
97
+ *,
98
+ passphrase: str | None = None,
99
+ armor: bool = True
100
+ ) -> str | bytes | None:
101
+ result = gpg.sign(
102
+ message,
103
+ keyid=signing_fingerprint,
104
+ passphrase=passphrase,
105
+ detach=True,
106
+ clearsign=False
107
+ )
108
+
109
+ if not result:
110
+ print(f"[ERR] sign_detached failed for <{signing_fingerprint[:16]}…>")
111
+ print(f" stderr: {result.stderr}")
112
+ return None
113
+
114
+ sig_data = str(result)
115
+ print(f"[OK] sign_detached({signing_fingerprint[:16]}…, armor={armor})")
116
+
117
+ if armor:
118
+ return sig_data
119
+
120
+ return sig_data
121
+
122
+
123
+ def sign_inline(
124
+ gpg,
125
+ message: str | bytes,
126
+ signing_fingerprint: str,
127
+ *,
128
+ passphrase: str | None = None
129
+ ) -> bytes | None:
130
+ result = gpg.sign(
131
+ message,
132
+ keyid=signing_fingerprint,
133
+ passphrase=passphrase,
134
+ detach=False,
135
+ clearsign=False,
136
+ binary=True
137
+ )
138
+
139
+ if not result:
140
+ print(f"[ERR] sign_inline failed for <{signing_fingerprint[:16]}…>")
141
+ return None
142
+
143
+ print(f"[OK] sign_inline({signing_fingerprint[:16]}…)")
144
+ return result.data
145
+
146
+
147
+ def sign_file_detached(
148
+ gpg,
149
+ file_path: Path | str,
150
+ signing_fingerprint: str,
151
+ *,
152
+ passphrase: str | None = None,
153
+ output_path: Path | str | None = None,
154
+ armor: bool = True
155
+ ) -> Path | None:
156
+ src = Path(file_path)
157
+ if not src.exists():
158
+ print(f"[ERR] sign_file_detached: {src} not found.")
159
+ return None
160
+
161
+ ext = ".asc" if armor else ".sig"
162
+ dst = Path(output_path) if output_path else src.with_suffix(src.sufix + ext)
163
+
164
+ with open(src, "rb") as file:
165
+ result = gpg.sign_file(
166
+ file,
167
+ keyid=signing_fingerprint,
168
+ passphrase=passphrase,
169
+ detach=True,
170
+ clearsign=False,
171
+ output=str(dst)
172
+ )
173
+
174
+ if not result:
175
+ print(f"[ERR] sign_file_detached({src.name}): {result.stderr}")
176
+ return None
177
+
178
+ print(f"[OK] sign_file_detached » {dst}")
179
+ return dst
180
+
181
+
182
+ def verify_clearsign(
183
+ gpg,
184
+ signed_message: str,
185
+ *,
186
+ always_trust: bool = True
187
+ ) -> VerificationResult:
188
+ result = gpg.verify(signed_message)
189
+ vr = VerificationResult.from_gnupg(result)
190
+ label = "verify_clearsign"
191
+ if vr.valid:
192
+ print(f"[OK] {label}")
193
+ else:
194
+ print(f"[ERR] {label}")
195
+ print(vr)
196
+ return vr
197
+
198
+
199
+ def verify_detached(
200
+ gpg,
201
+ message: str | bytes,
202
+ signature: str | bytes
203
+ ) -> VerificationResult:
204
+ import tempfile, os
205
+
206
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".asc") as tmp:
207
+ if isinstance(signature, str):
208
+ tmp.write(signature.encode())
209
+ else:
210
+ tmp.write(signature)
211
+ tmp_path = tmp.name
212
+
213
+ try:
214
+ if isinstance(message, str):
215
+ message = message.encode()
216
+
217
+ result = gpg.verify_data(tmp_path, message)
218
+ finally:
219
+ os.unlink(tmp_path)
220
+
221
+ vr = VerificationResult.from_gnupg(result)
222
+ label = "verify_detached"
223
+ if vr.valid:
224
+ print(f"[OK] {label}")
225
+ else:
226
+ print(f"[ERR] {label}")
227
+ print(vr)
228
+ return vr
229
+
230
+
231
+ def verify_detached_file(
232
+ gpg,
233
+ file_path: Path | str,
234
+ sig_path: Path | str
235
+ ) -> VerificationResult:
236
+ src = Path(file_path)
237
+ sig = Path(sig_path)
238
+
239
+ message_bytes = src.read_bytes()
240
+ sig_bytes = sig.read_bytes()
241
+
242
+ return verify_detached(gpg, message_bytes, sig_bytes)
243
+
244
+
245
+ def verify_inline(gpg, signed_data: bytes) -> VerificationResult:
246
+ result = gpg.decrypt(signed_data, always_trust=True)
247
+
248
+ vr = VerificationResult.from_gnupg(result)
249
+ label = "verify_inline"
250
+ if vr.valid:
251
+ print(f"[OK] {label}")
252
+ else:
253
+ print(f"[WARN] {label} - no valid signature found (or unsigned data)")
254
+ print(vr)
255
+ return vr
256
+
257
+
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "artemis_framework"
3
- version = "0.1.3"
3
+ version = "0.1.4"
4
4
  description = "A package for doing great things!"
5
5
  authors = ["Dylan Garrett"]
6
6
  license = "MIT"