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.
- {artemis_framework-0.1.3 → artemis_framework-0.1.4}/PKG-INFO +1 -1
- artemis_framework-0.1.4/artemis_framework/asphodel/core/sign_verify.py +257 -0
- {artemis_framework-0.1.3 → artemis_framework-0.1.4}/pyproject.toml +1 -1
- {artemis_framework-0.1.3 → artemis_framework-0.1.4}/LICENSE +0 -0
- {artemis_framework-0.1.3 → artemis_framework-0.1.4}/README.md +0 -0
- {artemis_framework-0.1.3 → artemis_framework-0.1.4}/artemis_framework/__init__.py +0 -0
- {artemis_framework-0.1.3 → artemis_framework-0.1.4}/artemis_framework/__main__.py +0 -0
- {artemis_framework-0.1.3 → artemis_framework-0.1.4}/artemis_framework/asphodel/__init__.py +0 -0
- {artemis_framework-0.1.3 → artemis_framework-0.1.4}/artemis_framework/asphodel/core/__init__.py +0 -0
- {artemis_framework-0.1.3 → artemis_framework-0.1.4}/artemis_framework/asphodel/core/encrypt_decrypt.py +0 -0
- {artemis_framework-0.1.3 → artemis_framework-0.1.4}/artemis_framework/asphodel/core/gpg_context.py +0 -0
- {artemis_framework-0.1.3 → artemis_framework-0.1.4}/artemis_framework/asphodel/core/key_management.py +0 -0
- {artemis_framework-0.1.3 → artemis_framework-0.1.4}/artemis_framework/elysium/__ini +0 -0
- {artemis_framework-0.1.3 → artemis_framework-0.1.4}/artemis_framework/elysium/__init__.py +0 -0
- {artemis_framework-0.1.3 → artemis_framework-0.1.4}/artemis_framework/tartarus/__init__.py +0 -0
|
@@ -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
|
+
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{artemis_framework-0.1.3 → artemis_framework-0.1.4}/artemis_framework/asphodel/core/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{artemis_framework-0.1.3 → artemis_framework-0.1.4}/artemis_framework/asphodel/core/gpg_context.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|