swarmauri_crypto_paramiko 0.3.0.dev3__tar.gz → 0.3.0.dev32__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,10 +1,12 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: swarmauri_crypto_paramiko
3
- Version: 0.3.0.dev3
3
+ Version: 0.3.0.dev32
4
4
  Summary: Paramiko-backed RSA + AES-GCM crypto provider for Swarmauri
5
- License: Apache-2.0
6
- Author: Swarmauri
7
- Author-email: opensource@swarmauri.com
5
+ License-Expression: Apache-2.0
6
+ License-File: LICENSE
7
+ Keywords: swarmauri,sdk,standards,crypto,paramiko,cryptography
8
+ Author: Jacob Stewart
9
+ Author-email: jacob@swarmauri.com
8
10
  Requires-Python: >=3.10,<3.13
9
11
  Classifier: License :: OSI Approved :: Apache Software License
10
12
  Classifier: Natural Language :: English
@@ -15,13 +17,16 @@ Classifier: Programming Language :: Python :: 3.13
15
17
  Classifier: Development Status :: 3 - Alpha
16
18
  Classifier: Topic :: Security :: Cryptography
17
19
  Classifier: Intended Audience :: Developers
20
+ Classifier: Programming Language :: Python
21
+ Classifier: Programming Language :: Python :: 3
22
+ Classifier: Programming Language :: Python :: 3 :: Only
18
23
  Requires-Dist: cryptography (>=41)
19
24
  Requires-Dist: paramiko (>=3.4)
20
25
  Requires-Dist: swarmauri_base
21
26
  Requires-Dist: swarmauri_core
22
27
  Description-Content-Type: text/markdown
23
28
 
24
- ![Swamauri Logo](https://res.cloudinary.com/dbjmpekvl/image/upload/v1730099724/Swarmauri-logo-lockup-2048x757_hww01w.png)
29
+ ![Swarmauri Logo](https://github.com/swarmauri/swarmauri-sdk/blob/3d4d1cfa949399d7019ae9d8f296afba773dfb7f/assets/swarmauri.brand.theme.svg)
25
30
 
26
31
  <p align="center">
27
32
  <a href="https://pypi.org/project/swarmauri_crypto_paramiko/">
@@ -40,16 +45,39 @@ Description-Content-Type: text/markdown
40
45
 
41
46
  ## Swarmauri Crypto Paramiko
42
47
 
43
- Paramiko-backed crypto provider implementing the `ICrypto` contract via `CryptoBase`.
48
+ Paramiko-backed crypto provider implementing the `ICrypto` contract via
49
+ `CryptoBase`. Built on top of [`paramiko`](https://www.paramiko.org/) and
50
+ [`cryptography`](https://cryptography.io/), it exposes an asynchronous API for
51
+ several cryptographic primitives using OpenSSH-formatted public keys and
52
+ PEM-encoded private keys supplied through `KeyRef` objects.
44
53
 
45
- - AES-256-GCM symmetric encrypt/decrypt
46
- - RSA-OAEP(SHA-256) wrap/unwrap
54
+ ### Features
55
+
56
+ - AES-256-GCM symmetric encrypt/decrypt (16/24/32 byte keys)
57
+ - RSA-OAEP(SHA-256) wrap/unwrap for OpenSSH RSA key pairs
58
+ - AES-256-GCM key wrap/unwrap when the KEK is symmetric
59
+ - RSA-OAEP(SHA-256) sealing for small payloads
47
60
  - Multi-recipient hybrid envelopes using OpenSSH public keys
48
61
 
62
+ Keys are represented by `KeyRef` objects. Public keys should be provided in
63
+ OpenSSH format via `KeyRef.public`, while private keys are supplied as
64
+ PEM-encoded bytes in `KeyRef.material`. RSA sealing is limited to inputs no
65
+ larger than the modulus-dependent RSA-OAEP bound (`modulus_bytes - 2 * hash_len
66
+ - 2`). For larger payloads use the hybrid envelope mode instead.
67
+
49
68
  ## Installation
50
69
 
70
+ Choose the tool that matches your workflow:
71
+
51
72
  ```bash
73
+ # pip
52
74
  pip install swarmauri_crypto_paramiko
75
+
76
+ # Poetry
77
+ poetry add swarmauri_crypto_paramiko
78
+
79
+ # uv
80
+ uv add swarmauri_crypto_paramiko
53
81
  ```
54
82
 
55
83
  ## Usage
@@ -106,13 +134,49 @@ wrapped = await crypto.wrap(recipient)
106
134
  unwrapped = await crypto.unwrap(recipient, wrapped)
107
135
  ```
108
136
 
137
+ To wrap with a symmetric key-encryption key instead, provide the AES key bytes
138
+ in `KeyRef.material` and set `wrap_alg="AES-256-GCM"`:
139
+
140
+ ```python
141
+ sym_kek = KeyRef(
142
+ kid="kek1",
143
+ version=1,
144
+ type=KeyType.SYMMETRIC,
145
+ uses=(KeyUse.WRAP, KeyUse.UNWRAP),
146
+ export_policy=ExportPolicy.SECRET_WHEN_ALLOWED,
147
+ material=b"\x01" * 32,
148
+ )
149
+
150
+ wrapped = await crypto.wrap(sym_kek, wrap_alg="AES-256-GCM")
151
+ plaintext_key = await crypto.unwrap(sym_kek, wrapped)
152
+ ```
153
+
154
+ ### RSA Sealing for Small Payloads
155
+
156
+ ```python
157
+ # Using the `recipient` defined above
158
+ sealed = await crypto.seal(recipient, b"tiny secret")
159
+ plaintext = await crypto.unseal(recipient, sealed)
160
+ ```
161
+
109
162
  ### Hybrid Envelope for Multiple Recipients
110
163
 
111
164
  ```python
112
165
  env = await crypto.encrypt_for_many([recipient], b"secret")
113
166
  ```
114
167
 
168
+ Calling `encrypt_for_many` without overrides produces an AES-256-GCM ciphertext
169
+ shared by every recipient, while `env.recipients` holds RSA-OAEP-wrapped
170
+ session keys. Use `enc_alg="RSA-OAEP-SHA256-SEAL"` to emit individual RSA-OAEP
171
+ sealed payloads instead of a shared ciphertext when the plaintext fits within
172
+ the sealing size limit.
173
+
115
174
  ## Entry point
116
175
 
117
176
  The provider is registered under the `swarmauri.cryptos` entry-point as `ParamikoCrypto`.
118
177
 
178
+ ## Want to help?
179
+
180
+ If you want to contribute to swarmauri-sdk, read up on our
181
+ [guidelines for contributing](https://github.com/swarmauri/swarmauri-sdk/blob/master/CONTRIBUTING.md)
182
+ that will help you get started.
@@ -1,4 +1,4 @@
1
- ![Swamauri Logo](https://res.cloudinary.com/dbjmpekvl/image/upload/v1730099724/Swarmauri-logo-lockup-2048x757_hww01w.png)
1
+ ![Swarmauri Logo](https://github.com/swarmauri/swarmauri-sdk/blob/3d4d1cfa949399d7019ae9d8f296afba773dfb7f/assets/swarmauri.brand.theme.svg)
2
2
 
3
3
  <p align="center">
4
4
  <a href="https://pypi.org/project/swarmauri_crypto_paramiko/">
@@ -17,16 +17,39 @@
17
17
 
18
18
  ## Swarmauri Crypto Paramiko
19
19
 
20
- Paramiko-backed crypto provider implementing the `ICrypto` contract via `CryptoBase`.
20
+ Paramiko-backed crypto provider implementing the `ICrypto` contract via
21
+ `CryptoBase`. Built on top of [`paramiko`](https://www.paramiko.org/) and
22
+ [`cryptography`](https://cryptography.io/), it exposes an asynchronous API for
23
+ several cryptographic primitives using OpenSSH-formatted public keys and
24
+ PEM-encoded private keys supplied through `KeyRef` objects.
21
25
 
22
- - AES-256-GCM symmetric encrypt/decrypt
23
- - RSA-OAEP(SHA-256) wrap/unwrap
26
+ ### Features
27
+
28
+ - AES-256-GCM symmetric encrypt/decrypt (16/24/32 byte keys)
29
+ - RSA-OAEP(SHA-256) wrap/unwrap for OpenSSH RSA key pairs
30
+ - AES-256-GCM key wrap/unwrap when the KEK is symmetric
31
+ - RSA-OAEP(SHA-256) sealing for small payloads
24
32
  - Multi-recipient hybrid envelopes using OpenSSH public keys
25
33
 
34
+ Keys are represented by `KeyRef` objects. Public keys should be provided in
35
+ OpenSSH format via `KeyRef.public`, while private keys are supplied as
36
+ PEM-encoded bytes in `KeyRef.material`. RSA sealing is limited to inputs no
37
+ larger than the modulus-dependent RSA-OAEP bound (`modulus_bytes - 2 * hash_len
38
+ - 2`). For larger payloads use the hybrid envelope mode instead.
39
+
26
40
  ## Installation
27
41
 
42
+ Choose the tool that matches your workflow:
43
+
28
44
  ```bash
45
+ # pip
29
46
  pip install swarmauri_crypto_paramiko
47
+
48
+ # Poetry
49
+ poetry add swarmauri_crypto_paramiko
50
+
51
+ # uv
52
+ uv add swarmauri_crypto_paramiko
30
53
  ```
31
54
 
32
55
  ## Usage
@@ -83,12 +106,49 @@ wrapped = await crypto.wrap(recipient)
83
106
  unwrapped = await crypto.unwrap(recipient, wrapped)
84
107
  ```
85
108
 
109
+ To wrap with a symmetric key-encryption key instead, provide the AES key bytes
110
+ in `KeyRef.material` and set `wrap_alg="AES-256-GCM"`:
111
+
112
+ ```python
113
+ sym_kek = KeyRef(
114
+ kid="kek1",
115
+ version=1,
116
+ type=KeyType.SYMMETRIC,
117
+ uses=(KeyUse.WRAP, KeyUse.UNWRAP),
118
+ export_policy=ExportPolicy.SECRET_WHEN_ALLOWED,
119
+ material=b"\x01" * 32,
120
+ )
121
+
122
+ wrapped = await crypto.wrap(sym_kek, wrap_alg="AES-256-GCM")
123
+ plaintext_key = await crypto.unwrap(sym_kek, wrapped)
124
+ ```
125
+
126
+ ### RSA Sealing for Small Payloads
127
+
128
+ ```python
129
+ # Using the `recipient` defined above
130
+ sealed = await crypto.seal(recipient, b"tiny secret")
131
+ plaintext = await crypto.unseal(recipient, sealed)
132
+ ```
133
+
86
134
  ### Hybrid Envelope for Multiple Recipients
87
135
 
88
136
  ```python
89
137
  env = await crypto.encrypt_for_many([recipient], b"secret")
90
138
  ```
91
139
 
140
+ Calling `encrypt_for_many` without overrides produces an AES-256-GCM ciphertext
141
+ shared by every recipient, while `env.recipients` holds RSA-OAEP-wrapped
142
+ session keys. Use `enc_alg="RSA-OAEP-SHA256-SEAL"` to emit individual RSA-OAEP
143
+ sealed payloads instead of a shared ciphertext when the plaintext fits within
144
+ the sealing size limit.
145
+
92
146
  ## Entry point
93
147
 
94
148
  The provider is registered under the `swarmauri.cryptos` entry-point as `ParamikoCrypto`.
149
+
150
+ ## Want to help?
151
+
152
+ If you want to contribute to swarmauri-sdk, read up on our
153
+ [guidelines for contributing](https://github.com/swarmauri/swarmauri-sdk/blob/master/CONTRIBUTING.md)
154
+ that will help you get started.
@@ -1,11 +1,11 @@
1
1
  [project]
2
2
  name = "swarmauri_crypto_paramiko"
3
- version = "0.3.0.dev3"
3
+ version = "0.3.0.dev32"
4
4
  description = "Paramiko-backed RSA + AES-GCM crypto provider for Swarmauri"
5
5
  license = "Apache-2.0"
6
6
  readme = "README.md"
7
7
  requires-python = ">=3.10,<3.13"
8
- authors = [{ name = "Swarmauri", email = "opensource@swarmauri.com" }]
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",
@@ -16,6 +16,9 @@ classifiers = [
16
16
  "Development Status :: 3 - Alpha",
17
17
  "Topic :: Security :: Cryptography",
18
18
  "Intended Audience :: Developers",
19
+ "Programming Language :: Python",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3 :: Only",
19
22
  ]
20
23
  dependencies = [
21
24
  "swarmauri_core",
@@ -23,6 +26,14 @@ dependencies = [
23
26
  "cryptography>=41",
24
27
  "paramiko>=3.4",
25
28
  ]
29
+ keywords = [
30
+ 'swarmauri',
31
+ 'sdk',
32
+ 'standards',
33
+ 'crypto',
34
+ 'paramiko',
35
+ 'cryptography',
36
+ ]
26
37
 
27
38
  [tool.uv.sources]
28
39
  swarmauri_core = { workspace = true }
@@ -1,17 +1,16 @@
1
1
  """Paramiko-backed crypto provider with sealing support.
2
2
 
3
- Implements the ICrypto contract using:
4
- - AES-256-GCM for symmetric encrypt/decrypt
5
- - RSA-OAEP(SHA-256) for:
6
- • wrapping the session key to many recipients (KEM+AEAD mode)
7
- • sealing/unsealing (direct public-key encryption of plaintext) for small payloads
8
-
9
- Notes
10
- -----
11
- - This provider expects RSA public keys in OpenSSH format via ``KeyRef.public``.
12
- - For unwrap/unseal, a PEM-encoded RSA private key is expected in ``KeyRef.material``.
13
- - Sealing has a size limit: len(plaintext) <= (modulus_bytes - 2*hash_len - 2).
14
- For larger payloads, use AES-GCM + RSA-OAEP key-wrap (encrypt_for_many with enc_alg="AES-256-GCM").
3
+ This module implements the ``ICrypto`` contract using AES-256-GCM for
4
+ symmetric encryption and RSA-OAEP (SHA-256) for key wrapping and direct
5
+ sealing of small payloads.
6
+
7
+ Notes:
8
+ - RSA public keys must be supplied in OpenSSH format via ``KeyRef.public``.
9
+ - For unwrap and unseal operations, ``KeyRef.material`` must contain a
10
+ PEM-encoded RSA private key.
11
+ - Sealing is limited to inputs no larger than ``modulus_bytes -
12
+ 2*hash_len - 2``. For larger payloads use
13
+ ``encrypt_for_many(enc_alg="AES-256-GCM")``.
15
14
  """
16
15
 
17
16
  from __future__ import annotations
@@ -46,17 +45,27 @@ _AEAD_DEFAULT = "AES-256-GCM"
46
45
 
47
46
  @ComponentBase.register_type(CryptoBase, "ParamikoCrypto")
48
47
  class ParamikoCrypto(CryptoBase):
49
- """Concrete implementation of the ICrypto contract using AES-GCM and RSA-OAEP."""
48
+ """Crypto provider backed by Paramiko and cryptography.
49
+
50
+ Implements AES-256-GCM for symmetric operations and RSA-OAEP for key
51
+ wrapping and sealing.
52
+ """
50
53
 
51
54
  type: Literal["ParamikoCrypto"] = "ParamikoCrypto"
52
55
 
53
56
  def supports(self) -> Dict[str, Iterable[Alg]]:
57
+ """Return the algorithms supported by this provider.
58
+
59
+ Returns:
60
+ Dict[str, Iterable[Alg]]: Mapping of operation names to the
61
+ algorithms the provider can execute.
62
+ """
63
+
54
64
  return {
55
65
  "encrypt": (_AEAD_DEFAULT,),
56
66
  "decrypt": (_AEAD_DEFAULT,),
57
67
  "wrap": (_WRAP_ALG,),
58
68
  "unwrap": (_WRAP_ALG,),
59
- # NEW:
60
69
  "seal": (_SEAL_ALG,),
61
70
  "unseal": (_SEAL_ALG,),
62
71
  }
@@ -104,6 +113,24 @@ class ParamikoCrypto(CryptoBase):
104
113
  aad: Optional[bytes] = None,
105
114
  nonce: Optional[bytes] = None,
106
115
  ) -> AEADCiphertext:
116
+ """Encrypt plaintext with AES-GCM.
117
+
118
+ Args:
119
+ key (KeyRef): Symmetric key reference containing the key bytes.
120
+ pt (bytes): Plaintext to encrypt.
121
+ alg (Optional[Alg]): AEAD algorithm to use. Defaults to
122
+ ``AES-256-GCM`` when not provided.
123
+ aad (Optional[bytes]): Additional authenticated data.
124
+ nonce (Optional[bytes]): Nonce for AES-GCM. Random when omitted.
125
+
126
+ Returns:
127
+ AEADCiphertext: Ciphertext container with nonce and tag.
128
+
129
+ Raises:
130
+ UnsupportedAlgorithm: If an unsupported algorithm is requested.
131
+ ValueError: If the key material is missing or invalid.
132
+ """
133
+
107
134
  alg = self._normalize_aead_alg(alg)
108
135
  if alg != _AEAD_DEFAULT:
109
136
  raise UnsupportedAlgorithm(f"Unsupported AEAD algorithm: {alg}")
@@ -136,6 +163,22 @@ class ParamikoCrypto(CryptoBase):
136
163
  *,
137
164
  aad: Optional[bytes] = None,
138
165
  ) -> bytes:
166
+ """Decrypt ciphertext produced by :meth:`encrypt`.
167
+
168
+ Args:
169
+ key (KeyRef): Symmetric key reference containing the key bytes.
170
+ ct (AEADCiphertext): Ciphertext container to decrypt.
171
+ aad (Optional[bytes]): Additional authenticated data. Defaults to
172
+ the value stored in ``ct`` when omitted.
173
+
174
+ Returns:
175
+ bytes: Decrypted plaintext.
176
+
177
+ Raises:
178
+ UnsupportedAlgorithm: If an unsupported algorithm is requested.
179
+ ValueError: If the key material is missing.
180
+ """
181
+
139
182
  if self._normalize_aead_alg(ct.alg) != _AEAD_DEFAULT:
140
183
  raise UnsupportedAlgorithm(f"Unsupported AEAD algorithm: {ct.alg}")
141
184
  if key.material is None:
@@ -157,6 +200,24 @@ class ParamikoCrypto(CryptoBase):
157
200
  *,
158
201
  alg: Optional[Alg] = _SEAL_ALG,
159
202
  ) -> bytes:
203
+ """Seal a small plaintext to a recipient's RSA public key.
204
+
205
+ Args:
206
+ recipient (KeyRef): Recipient key reference with OpenSSH RSA
207
+ public key bytes in ``KeyRef.public``.
208
+ pt (bytes): Plaintext to encrypt.
209
+ alg (Optional[Alg]): Sealing algorithm identifier. Defaults to
210
+ ``RSA-OAEP-SHA256-SEAL``.
211
+
212
+ Returns:
213
+ bytes: Sealed ciphertext produced by RSA-OAEP.
214
+
215
+ Raises:
216
+ UnsupportedAlgorithm: If an unsupported algorithm is requested.
217
+ ValueError: If the recipient public key is missing.
218
+ IntegrityError: If the plaintext exceeds the RSA-OAEP size limit.
219
+ """
220
+
160
221
  if alg != _SEAL_ALG:
161
222
  raise UnsupportedAlgorithm(f"Unsupported seal alg: {alg}")
162
223
  if recipient.public is None:
@@ -182,6 +243,23 @@ class ParamikoCrypto(CryptoBase):
182
243
  *,
183
244
  alg: Optional[Alg] = _SEAL_ALG,
184
245
  ) -> bytes:
246
+ """Unseal ciphertext using the recipient's RSA private key.
247
+
248
+ Args:
249
+ recipient_priv (KeyRef): Recipient key reference containing a
250
+ PEM-encoded RSA private key in ``KeyRef.material``.
251
+ sealed (bytes): Ciphertext produced by :meth:`seal`.
252
+ alg (Optional[Alg]): Sealing algorithm identifier. Defaults to
253
+ ``RSA-OAEP-SHA256-SEAL``.
254
+
255
+ Returns:
256
+ bytes: Decrypted plaintext.
257
+
258
+ Raises:
259
+ UnsupportedAlgorithm: If an unsupported algorithm is requested.
260
+ ValueError: If the private key material is missing.
261
+ """
262
+
185
263
  if alg != _SEAL_ALG:
186
264
  raise UnsupportedAlgorithm(f"Unsupported seal alg: {alg}")
187
265
  if recipient_priv.material is None:
@@ -211,6 +289,33 @@ class ParamikoCrypto(CryptoBase):
211
289
  aad: Optional[bytes] = None,
212
290
  nonce: Optional[bytes] = None,
213
291
  ) -> MultiRecipientEnvelope:
292
+ """Encrypt plaintext for multiple recipients.
293
+
294
+ Operates in two modes:
295
+ 1. **Sealed**: Each recipient receives an individual RSA-OAEP sealed
296
+ ciphertext when ``enc_alg`` is ``_SEAL_ALG``.
297
+ 2. **KEM+AEAD**: Generates a shared AES-GCM ciphertext and wraps the
298
+ session key for each recipient using RSA-OAEP.
299
+
300
+ Args:
301
+ recipients (Iterable[KeyRef]): Sequence of recipients.
302
+ pt (bytes): Plaintext to encrypt.
303
+ enc_alg (Optional[Alg]): Encryption algorithm for the shared
304
+ ciphertext. Defaults to ``AES-256-GCM``.
305
+ recipient_wrap_alg (Optional[Alg]): Wrapping algorithm for the
306
+ session key. Defaults to ``RSA-OAEP-SHA256``.
307
+ aad (Optional[bytes]): Additional authenticated data for AES-GCM.
308
+ nonce (Optional[bytes]): Nonce for AES-GCM. Random when omitted.
309
+
310
+ Returns:
311
+ MultiRecipientEnvelope: Envelope containing ciphertext and per-
312
+ recipient wrapped keys.
313
+
314
+ Raises:
315
+ UnsupportedAlgorithm: If an unsupported algorithm is requested.
316
+ ValueError: If required key material is missing.
317
+ """
318
+
214
319
  # 1) Sealed-style variant (per-recipient ciphertext; no shared ct)
215
320
  if enc_alg == _SEAL_ALG:
216
321
  recip_infos: list[RecipientInfo] = []
@@ -307,13 +412,28 @@ class ParamikoCrypto(CryptoBase):
307
412
  nonce: Optional[bytes] = None,
308
413
  aad: Optional[bytes] = None,
309
414
  ) -> WrappedKey:
310
- """Wrap a DEK with the given KEK.
311
-
312
- Supports two modes:
313
- * RSA-OAEP when ``wrap_alg`` is ``_WRAP_ALG`` (the legacy behaviour)
314
- * AES-GCM when ``wrap_alg`` matches the default AEAD algorithm. In
315
- this mode ``kek.material`` must provide the symmetric key bytes and
316
- a random nonce will be generated when one isn't supplied.
415
+ """Wrap a data-encryption key with the given key-encryption key.
416
+
417
+ Args:
418
+ kek (KeyRef): Key-encryption key reference. Provide an OpenSSH
419
+ RSA public key in ``KeyRef.public`` for RSA-OAEP wrapping or
420
+ symmetric key bytes in ``KeyRef.material`` for AES-GCM.
421
+ dek (Optional[bytes]): Data-encryption key to wrap. A random key
422
+ is generated when omitted.
423
+ wrap_alg (Optional[Alg]): Wrapping algorithm to use. Defaults to
424
+ ``RSA-OAEP-SHA256``.
425
+ nonce (Optional[bytes]): Nonce for AES-GCM wrapping. Random when
426
+ omitted.
427
+ aad (Optional[bytes]): Additional authenticated data for AES-GCM
428
+ wrapping.
429
+
430
+ Returns:
431
+ WrappedKey: Container with the wrapped key material.
432
+
433
+ Raises:
434
+ UnsupportedAlgorithm: If an unsupported wrapping algorithm is
435
+ requested.
436
+ ValueError: If required key material is missing or invalid.
317
437
  """
318
438
 
319
439
  wrap_alg = wrap_alg or _WRAP_ALG
@@ -372,7 +492,24 @@ class ParamikoCrypto(CryptoBase):
372
492
  *,
373
493
  aad: Optional[bytes] = None,
374
494
  ) -> bytes:
375
- """Unwrap a previously wrapped key."""
495
+ """Unwrap a previously wrapped key.
496
+
497
+ Args:
498
+ kek (KeyRef): Key-encryption key reference. Must contain the RSA
499
+ private key or symmetric key bytes corresponding to the
500
+ wrapping algorithm used.
501
+ wrapped (WrappedKey): Wrapped key to unwrap.
502
+ aad (Optional[bytes]): Additional authenticated data for AES-GCM
503
+ unwrapping.
504
+
505
+ Returns:
506
+ bytes: The unwrapped data-encryption key.
507
+
508
+ Raises:
509
+ UnsupportedAlgorithm: If an unsupported wrapping algorithm is
510
+ requested.
511
+ ValueError: If key material or required fields are missing.
512
+ """
376
513
 
377
514
  if wrapped.wrap_alg == _WRAP_ALG:
378
515
  if kek.material is None: