swarmauri_crypto_pgp 0.3.0.dev5__tar.gz → 0.11.0.dev1__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,47 +1,47 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: swarmauri_crypto_pgp
3
- Version: 0.3.0.dev5
3
+ Version: 0.11.0.dev1
4
4
  Summary: OpenPGP (GnuPG) + AES-GCM crypto provider for Swarmauri
5
5
  License-Expression: Apache-2.0
6
6
  License-File: LICENSE
7
7
  Keywords: swarmauri,sdk,standards,crypto,pgp,cryptography
8
8
  Author: Jacob Stewart
9
9
  Author-email: jacob@swarmauri.com
10
- Requires-Python: >=3.10,<3.13
10
+ Requires-Python: >=3.10,<3.15
11
11
  Classifier: License :: OSI Approved :: Apache Software License
12
12
  Classifier: Natural Language :: English
13
- Classifier: Programming Language :: Python :: 3.10
14
- Classifier: Programming Language :: Python :: 3.11
15
- Classifier: Programming Language :: Python :: 3.12
16
- Classifier: Programming Language :: Python :: 3.13
17
13
  Classifier: Development Status :: 3 - Alpha
18
14
  Classifier: Topic :: Security :: Cryptography
19
15
  Classifier: Intended Audience :: Developers
20
16
  Classifier: Programming Language :: Python
21
17
  Classifier: Programming Language :: Python :: 3
22
18
  Classifier: Programming Language :: Python :: 3 :: Only
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Programming Language :: Python :: 3.14
23
24
  Requires-Dist: cryptography (>=41)
24
25
  Requires-Dist: python-gnupg (>=0.5.0)
25
26
  Requires-Dist: swarmauri_base
26
27
  Requires-Dist: swarmauri_core
27
28
  Description-Content-Type: text/markdown
28
29
 
29
- ![Swarmauri Logo](https://github.com/swarmauri/swarmauri-sdk/blob/3d4d1cfa949399d7019ae9d8f296afba773dfb7f/assets/swarmauri.brand.theme.svg)
30
+ ![Swarmauri Logo](https://raw.githubusercontent.com/swarmauri/swarmauri-sdk/master/assets/swarmauri_sdk_brand.png)
30
31
 
31
32
  <p align="center">
32
- <a href="https://pypi.org/project/swarmauri_crypto_pgp/">
33
- <img src="https://img.shields.io/pypi/dm/swarmauri_crypto_pgp" alt="PyPI - Downloads"/></a>
33
+ <a href="https://pepy.tech/project/swarmauri_crypto_pgp/">
34
+ <img src="https://static.pepy.tech/badge/swarmauri_crypto_pgp/month" alt="PyPI - Downloads"/></a>
34
35
  <a href="https://hits.sh/github.com/swarmauri/swarmauri-sdk/tree/master/pkgs/standards/swarmauri_crypto_pgp/">
35
36
  <img alt="Hits" src="https://hits.sh/github.com/swarmauri/swarmauri-sdk/tree/master/pkgs/standards/swarmauri_crypto_pgp.svg"/></a>
36
37
  <a href="https://pypi.org/project/swarmauri_crypto_pgp/">
37
- <img src="https://img.shields.io/pypi/pyversions/swarmauri_crypto_pgp" alt="PyPI - Python Version"/></a>
38
+ <img src="https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-blue" alt="PyPI - Python Version"/></a>
38
39
  <a href="https://pypi.org/project/swarmauri_crypto_pgp/">
39
40
  <img src="https://img.shields.io/pypi/l/swarmauri_crypto_pgp" alt="PyPI - License"/></a>
40
41
  <a href="https://pypi.org/project/swarmauri_crypto_pgp/">
41
42
  <img src="https://img.shields.io/pypi/v/swarmauri_crypto_pgp?label=swarmauri_crypto_pgp&color=green" alt="PyPI - swarmauri_crypto_pgp"/></a>
42
- </p>
43
-
44
- ---
43
+ <a href="https://discord.gg/N4UpBuQv8T">
44
+ <img src="https://img.shields.io/badge/Discord-Join%20Chat-5865F2?logo=discord&logoColor=white" alt="Discord"/></a></p>
45
45
 
46
46
  ## Swarmauri Crypto PGP
47
47
 
@@ -52,18 +52,18 @@ symmetrical encryption, public-key key wrapping, and hybrid envelopes.
52
52
 
53
53
  ### Features at a glance
54
54
 
55
- - **Symmetric AEAD** AES-256-GCM powers `encrypt` and `decrypt`.
56
- - **Key wrapping** `wrap` and `unwrap` delegate to GnuPG to protect random or
55
+ - **Symmetric AEAD** ? AES-256-GCM powers `encrypt` and `decrypt`.
56
+ - **Key wrapping** ? `wrap` and `unwrap` delegate to GnuPG to protect random or
57
57
  supplied key material with a recipient's public/private key pair.
58
- - **Hybrid envelopes** `encrypt_for_many` supports both traditional
58
+ - **Hybrid envelopes** ? `encrypt_for_many` supports both traditional
59
59
  KEM+AEAD (shared ciphertext + wrapped session key) and OpenPGP sealed mode for
60
60
  per-recipient ciphertexts.
61
- - **Sealing convenience** `seal` and `unseal` provide single-recipient
61
+ - **Sealing convenience** ? `seal` and `unseal` provide single-recipient
62
62
  OpenPGP public-key encryption without managing the envelope structure.
63
63
 
64
64
  ### System requirements
65
65
 
66
- - Python 3.10 3.13.
66
+ - Python 3.10 ? 3.13.
67
67
  - [GnuPG](https://gnupg.org/) available on the `PATH` (required by
68
68
  `python-gnupg`).
69
69
 
@@ -144,3 +144,5 @@ The provider is registered under the `swarmauri.cryptos` entry-point as
144
144
  If you want to contribute to swarmauri-sdk, read up on our
145
145
  [guidelines for contributing](https://github.com/swarmauri/swarmauri-sdk/blob/master/CONTRIBUTING.md)
146
146
  that will help you get started.
147
+
148
+
@@ -1,19 +1,18 @@
1
- ![Swarmauri Logo](https://github.com/swarmauri/swarmauri-sdk/blob/3d4d1cfa949399d7019ae9d8f296afba773dfb7f/assets/swarmauri.brand.theme.svg)
1
+ ![Swarmauri Logo](https://raw.githubusercontent.com/swarmauri/swarmauri-sdk/master/assets/swarmauri_sdk_brand.png)
2
2
 
3
3
  <p align="center">
4
- <a href="https://pypi.org/project/swarmauri_crypto_pgp/">
5
- <img src="https://img.shields.io/pypi/dm/swarmauri_crypto_pgp" alt="PyPI - Downloads"/></a>
4
+ <a href="https://pepy.tech/project/swarmauri_crypto_pgp/">
5
+ <img src="https://static.pepy.tech/badge/swarmauri_crypto_pgp/month" alt="PyPI - Downloads"/></a>
6
6
  <a href="https://hits.sh/github.com/swarmauri/swarmauri-sdk/tree/master/pkgs/standards/swarmauri_crypto_pgp/">
7
7
  <img alt="Hits" src="https://hits.sh/github.com/swarmauri/swarmauri-sdk/tree/master/pkgs/standards/swarmauri_crypto_pgp.svg"/></a>
8
8
  <a href="https://pypi.org/project/swarmauri_crypto_pgp/">
9
- <img src="https://img.shields.io/pypi/pyversions/swarmauri_crypto_pgp" alt="PyPI - Python Version"/></a>
9
+ <img src="https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-blue" alt="PyPI - Python Version"/></a>
10
10
  <a href="https://pypi.org/project/swarmauri_crypto_pgp/">
11
11
  <img src="https://img.shields.io/pypi/l/swarmauri_crypto_pgp" alt="PyPI - License"/></a>
12
12
  <a href="https://pypi.org/project/swarmauri_crypto_pgp/">
13
13
  <img src="https://img.shields.io/pypi/v/swarmauri_crypto_pgp?label=swarmauri_crypto_pgp&color=green" alt="PyPI - swarmauri_crypto_pgp"/></a>
14
- </p>
15
-
16
- ---
14
+ <a href="https://discord.gg/N4UpBuQv8T">
15
+ <img src="https://img.shields.io/badge/Discord-Join%20Chat-5865F2?logo=discord&logoColor=white" alt="Discord"/></a></p>
17
16
 
18
17
  ## Swarmauri Crypto PGP
19
18
 
@@ -24,18 +23,18 @@ symmetrical encryption, public-key key wrapping, and hybrid envelopes.
24
23
 
25
24
  ### Features at a glance
26
25
 
27
- - **Symmetric AEAD** AES-256-GCM powers `encrypt` and `decrypt`.
28
- - **Key wrapping** `wrap` and `unwrap` delegate to GnuPG to protect random or
26
+ - **Symmetric AEAD** ? AES-256-GCM powers `encrypt` and `decrypt`.
27
+ - **Key wrapping** ? `wrap` and `unwrap` delegate to GnuPG to protect random or
29
28
  supplied key material with a recipient's public/private key pair.
30
- - **Hybrid envelopes** `encrypt_for_many` supports both traditional
29
+ - **Hybrid envelopes** ? `encrypt_for_many` supports both traditional
31
30
  KEM+AEAD (shared ciphertext + wrapped session key) and OpenPGP sealed mode for
32
31
  per-recipient ciphertexts.
33
- - **Sealing convenience** `seal` and `unseal` provide single-recipient
32
+ - **Sealing convenience** ? `seal` and `unseal` provide single-recipient
34
33
  OpenPGP public-key encryption without managing the envelope structure.
35
34
 
36
35
  ### System requirements
37
36
 
38
- - Python 3.10 3.13.
37
+ - Python 3.10 ? 3.13.
39
38
  - [GnuPG](https://gnupg.org/) available on the `PATH` (required by
40
39
  `python-gnupg`).
41
40
 
@@ -115,4 +114,5 @@ The provider is registered under the `swarmauri.cryptos` entry-point as
115
114
 
116
115
  If you want to contribute to swarmauri-sdk, read up on our
117
116
  [guidelines for contributing](https://github.com/swarmauri/swarmauri-sdk/blob/master/CONTRIBUTING.md)
118
- that will help you get started.
117
+ that will help you get started.
118
+
@@ -1,24 +1,25 @@
1
1
  [project]
2
2
  name = "swarmauri_crypto_pgp"
3
- version = "0.3.0.dev5"
3
+ version = "0.11.0.dev1"
4
4
  description = "OpenPGP (GnuPG) + AES-GCM crypto provider for Swarmauri"
5
5
  license = "Apache-2.0"
6
6
  readme = "README.md"
7
- requires-python = ">=3.10,<3.13"
7
+ requires-python = ">=3.10,<3.15"
8
8
  authors = [{ name = "Jacob Stewart", email = "jacob@swarmauri.com" }]
9
9
  classifiers = [
10
10
  "License :: OSI Approved :: Apache Software License",
11
11
  "Natural Language :: English",
12
- "Programming Language :: Python :: 3.10",
13
- "Programming Language :: Python :: 3.11",
14
- "Programming Language :: Python :: 3.12",
15
- "Programming Language :: Python :: 3.13",
16
12
  "Development Status :: 3 - Alpha",
17
13
  "Topic :: Security :: Cryptography",
18
14
  "Intended Audience :: Developers",
19
15
  "Programming Language :: Python",
20
16
  "Programming Language :: Python :: 3",
21
17
  "Programming Language :: Python :: 3 :: Only",
18
+ "Programming Language :: Python :: 3.10",
19
+ "Programming Language :: Python :: 3.11",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Programming Language :: Python :: 3.13",
22
+ "Programming Language :: Python :: 3.14",
22
23
  ]
23
24
  dependencies = [
24
25
  "swarmauri_core",
@@ -4,11 +4,13 @@ Implements the ICrypto contract using:
4
4
  - AES-256-GCM for symmetric encrypt/decrypt (AEAD)
5
5
  - OpenPGP public-key encryption for:
6
6
  • wrapping the session key to many recipients (KEM+AEAD mode)
7
- • sealing/unsealing (direct public-key encryption of plaintext) for one or many
7
+ • sealing/unsealing (direct public-key encryption of plaintext) for one or
8
+ many
8
9
 
9
10
  Notes
10
11
  -----
11
- - For wrap/encrypt_for_many/seal, ``KeyRef.public`` must contain an ASCII-armored
12
+ - For wrap/encrypt_for_many/seal, ``KeyRef.public`` must contain an
13
+ ASCII-armored
12
14
  OpenPGP public key (as produced by ``gpg --armor --export``)
13
15
  - For unwrap/unseal, ``KeyRef.material`` must contain an ASCII-armored OpenPGP
14
16
  private key (as produced by ``gpg --armor --export-secret-keys``)
@@ -47,7 +49,8 @@ _SEAL_ALG = "OpenPGP-SEAL"
47
49
 
48
50
  @ComponentBase.register_type(CryptoBase, "PGPCrypto")
49
51
  class PGPCrypto(CryptoBase):
50
- """Concrete implementation of the ICrypto contract using AES-GCM and OpenPGP."""
52
+ """Concrete implementation of the ICrypto contract using AES-GCM and
53
+ OpenPGP."""
51
54
 
52
55
  type: Literal["PGPCrypto"] = "PGPCrypto"
53
56
 
@@ -92,7 +95,9 @@ class PGPCrypto(CryptoBase):
92
95
  "KeyRef.material must contain symmetric key bytes for AEAD"
93
96
  )
94
97
  if len(key.material) not in (16, 24, 32):
95
- raise ValueError("KeyRef.material must be 16/24/32 bytes for AES-GCM")
98
+ raise ValueError(
99
+ "KeyRef.material must be 16/24/32 bytes for AES-GCM"
100
+ )
96
101
 
97
102
  nonce = nonce or secrets.token_bytes(12)
98
103
  aead = AESGCM(key.material)
@@ -128,7 +133,8 @@ class PGPCrypto(CryptoBase):
128
133
  return aead.decrypt(ct.nonce, blob, aad or ct.aad)
129
134
 
130
135
  # ─────────────────────────── sealing ───────────────────────────
131
- # (OpenPGP public-key encryption of plaintext; gpg handles hybrid internally)
136
+ # (OpenPGP public-key encryption of plaintext; gpg handles hybrid
137
+ # internally)
132
138
 
133
139
  async def seal(
134
140
  self,
@@ -152,7 +158,9 @@ class PGPCrypto(CryptoBase):
152
158
  else recipient.public
153
159
  )
154
160
  if not import_result.fingerprints:
155
- raise RuntimeError("Failed to import recipient OpenPGP public key")
161
+ raise RuntimeError(
162
+ "Failed to import recipient OpenPGP public key"
163
+ )
156
164
  fp = import_result.fingerprints[0]
157
165
 
158
166
  enc = gpg.encrypt(pt, fp, armor=False, always_trust=True)
@@ -171,7 +179,11 @@ class PGPCrypto(CryptoBase):
171
179
  raise UnsupportedAlgorithm(f"Unsupported seal alg: {alg}")
172
180
  if recipient_priv.material is None:
173
181
  raise ValueError(
174
- "KeyRef.material must contain ASCII-armored OpenPGP private key"
182
+ (
183
+ "KeyRef.material must contain ASCII-armored OpenPGP "
184
+ "private "
185
+ "key"
186
+ )
175
187
  )
176
188
 
177
189
  passphrase = None
@@ -190,7 +202,9 @@ class PGPCrypto(CryptoBase):
190
202
 
191
203
  dec = gpg.decrypt(sealed, passphrase=passphrase or "")
192
204
  if not dec.ok:
193
- raise RuntimeError(f"GPG decrypt (unseal) failed: {dec.status}")
205
+ raise RuntimeError(
206
+ f"GPG decrypt (unseal) failed: {dec.status}"
207
+ )
194
208
  return bytes(dec.data)
195
209
 
196
210
  # ─────────── hybrid encrypt-for-many via OpenPGP (KEM+AEAD) ───────────
@@ -213,10 +227,16 @@ class PGPCrypto(CryptoBase):
213
227
  for r in recipients:
214
228
  if r.public is None:
215
229
  raise ValueError(
216
- "Recipient KeyRef.public must contain ASCII-armored OpenPGP public key"
230
+ (
231
+ "Recipient KeyRef.public must contain "
232
+ "ASCII-armored OpenPGP "
233
+ "public key"
234
+ )
217
235
  )
218
236
  import_result = gpg.import_keys(
219
- r.public.decode() if isinstance(r.public, bytes) else r.public
237
+ r.public.decode()
238
+ if isinstance(r.public, bytes)
239
+ else r.public
220
240
  )
221
241
  if not import_result.fingerprints:
222
242
  raise RuntimeError(
@@ -250,7 +270,8 @@ class PGPCrypto(CryptoBase):
250
270
  aad=None, # AAD is not bound in OpenPGP-seal mode
251
271
  )
252
272
 
253
- # 2) Default KEM+AEAD path (shared AES-GCM ct + PGP-wrapped session key)
273
+ # 2) Default KEM+AEAD path (shared AES-GCM ct + PGP-wrapped session
274
+ # key)
254
275
  enc_alg = self._normalize_aead_alg(enc_alg)
255
276
  if enc_alg != _AEAD_DEFAULT:
256
277
  raise UnsupportedAlgorithm(f"Unsupported enc_alg: {enc_alg}")
@@ -270,13 +291,21 @@ class PGPCrypto(CryptoBase):
270
291
  for r in recipients:
271
292
  if r.public is None:
272
293
  raise ValueError(
273
- "Recipient KeyRef.public must contain ASCII-armored OpenPGP public key"
294
+ (
295
+ "Recipient KeyRef.public must contain "
296
+ "ASCII-armored OpenPGP "
297
+ "public key"
298
+ )
274
299
  )
275
300
  import_result = gpg.import_keys(
276
- r.public.decode() if isinstance(r.public, bytes) else r.public
301
+ r.public.decode()
302
+ if isinstance(r.public, bytes)
303
+ else r.public
277
304
  )
278
305
  if not import_result.fingerprints:
279
- raise RuntimeError("Failed to import recipient OpenPGP public key")
306
+ raise RuntimeError(
307
+ "Failed to import recipient OpenPGP public key"
308
+ )
280
309
  fp = import_result.fingerprints[0]
281
310
 
282
311
  enc = gpg.encrypt(k, fp, armor=False, always_trust=True)
@@ -323,14 +352,20 @@ class PGPCrypto(CryptoBase):
323
352
  with tempfile.TemporaryDirectory() as gpg_home:
324
353
  gpg = gnupg.GPG(gnupghome=gpg_home)
325
354
  import_result = gpg.import_keys(
326
- kek.public.decode() if isinstance(kek.public, bytes) else kek.public
355
+ kek.public.decode()
356
+ if isinstance(kek.public, bytes)
357
+ else kek.public
327
358
  )
328
359
  if not import_result.fingerprints:
329
- raise RuntimeError("Failed to import recipient OpenPGP public key")
360
+ raise RuntimeError(
361
+ "Failed to import recipient OpenPGP public key"
362
+ )
330
363
  recipient_fp = import_result.fingerprints[0]
331
364
 
332
365
  dek = dek or secrets.token_bytes(32)
333
- enc = gpg.encrypt(dek, recipient_fp, armor=False, always_trust=True)
366
+ enc = gpg.encrypt(
367
+ dek, recipient_fp, armor=False, always_trust=True
368
+ )
334
369
  if not enc.ok:
335
370
  raise RuntimeError(f"GPG encrypt failed: {enc.status}")
336
371
 
@@ -344,10 +379,16 @@ class PGPCrypto(CryptoBase):
344
379
 
345
380
  async def unwrap(self, kek: KeyRef, wrapped: WrappedKey) -> bytes:
346
381
  if wrapped.wrap_alg != _WRAP_ALG:
347
- raise UnsupportedAlgorithm(f"Unsupported wrap_alg: {wrapped.wrap_alg}")
382
+ raise UnsupportedAlgorithm(
383
+ f"Unsupported wrap_alg: {wrapped.wrap_alg}"
384
+ )
348
385
  if kek.material is None:
349
386
  raise ValueError(
350
- "KeyRef.material must contain ASCII-armored OpenPGP private key"
387
+ (
388
+ "KeyRef.material must contain ASCII-armored OpenPGP "
389
+ "private "
390
+ "key"
391
+ )
351
392
  )
352
393
 
353
394
  passphrase = None