otdf-python 0.3.5__py3-none-any.whl → 0.4.1__py3-none-any.whl

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.
Files changed (52) hide show
  1. otdf_python/__init__.py +1 -2
  2. otdf_python/__main__.py +1 -2
  3. otdf_python/address_normalizer.py +8 -10
  4. otdf_python/aesgcm.py +8 -0
  5. otdf_python/assertion_config.py +21 -0
  6. otdf_python/asym_crypto.py +18 -22
  7. otdf_python/auth_headers.py +7 -6
  8. otdf_python/autoconfigure_utils.py +22 -6
  9. otdf_python/cli.py +5 -5
  10. otdf_python/collection_store.py +13 -0
  11. otdf_python/collection_store_impl.py +5 -0
  12. otdf_python/config.py +13 -0
  13. otdf_python/connect_client.py +1 -0
  14. otdf_python/constants.py +2 -0
  15. otdf_python/crypto_utils.py +4 -0
  16. otdf_python/dpop.py +3 -5
  17. otdf_python/ecc_constants.py +12 -14
  18. otdf_python/ecc_mode.py +7 -2
  19. otdf_python/ecdh.py +24 -25
  20. otdf_python/eckeypair.py +5 -0
  21. otdf_python/header.py +5 -0
  22. otdf_python/invalid_zip_exception.py +6 -2
  23. otdf_python/kas_client.py +48 -55
  24. otdf_python/kas_connect_rpc_client.py +16 -19
  25. otdf_python/kas_info.py +4 -3
  26. otdf_python/kas_key_cache.py +10 -9
  27. otdf_python/key_type.py +4 -0
  28. otdf_python/key_type_constants.py +4 -11
  29. otdf_python/manifest.py +24 -0
  30. otdf_python/nanotdf.py +34 -24
  31. otdf_python/nanotdf_ecdsa_struct.py +5 -9
  32. otdf_python/nanotdf_type.py +12 -0
  33. otdf_python/policy_binding_serializer.py +6 -4
  34. otdf_python/policy_info.py +6 -0
  35. otdf_python/policy_object.py +8 -0
  36. otdf_python/policy_stub.py +2 -0
  37. otdf_python/resource_locator.py +22 -13
  38. otdf_python/sdk.py +49 -57
  39. otdf_python/sdk_builder.py +58 -41
  40. otdf_python/sdk_exceptions.py +11 -1
  41. otdf_python/symmetric_and_payload_config.py +6 -0
  42. otdf_python/tdf.py +47 -10
  43. otdf_python/tdf_reader.py +10 -13
  44. otdf_python/tdf_writer.py +5 -0
  45. otdf_python/token_source.py +4 -3
  46. otdf_python/version.py +5 -0
  47. otdf_python/zip_reader.py +10 -2
  48. otdf_python/zip_writer.py +11 -0
  49. {otdf_python-0.3.5.dist-info → otdf_python-0.4.1.dist-info}/METADATA +19 -2
  50. {otdf_python-0.3.5.dist-info → otdf_python-0.4.1.dist-info}/RECORD +52 -52
  51. {otdf_python-0.3.5.dist-info → otdf_python-0.4.1.dist-info}/WHEEL +1 -1
  52. {otdf_python-0.3.5.dist-info → otdf_python-0.4.1.dist-info}/licenses/LICENSE +0 -0
otdf_python/kas_client.py CHANGED
@@ -1,6 +1,4 @@
1
- """
2
- KASClient: Handles communication with the Key Access Service (KAS).
3
- """
1
+ """KASClient: Handles communication with the Key Access Service (KAS)."""
4
2
 
5
3
  import base64
6
4
  import hashlib
@@ -22,6 +20,8 @@ from .sdk_exceptions import SDKException
22
20
 
23
21
  @dataclass
24
22
  class KeyAccess:
23
+ """Key access response from KAS."""
24
+
25
25
  url: str
26
26
  wrapped_key: str
27
27
  ephemeral_public_key: str | None = None
@@ -29,6 +29,8 @@ class KeyAccess:
29
29
 
30
30
 
31
31
  class KASClient:
32
+ """Client for communicating with the Key Access Service (KAS)."""
33
+
32
34
  def __init__(
33
35
  self,
34
36
  kas_url=None,
@@ -37,6 +39,7 @@ class KASClient:
37
39
  use_plaintext=False,
38
40
  verify_ssl=True,
39
41
  ):
42
+ """Initialize KAS client."""
40
43
  self.kas_url = kas_url
41
44
  self.token_source = token_source
42
45
  self.cache = cache or KASKeyCache()
@@ -63,14 +66,14 @@ class KASClient:
63
66
  )
64
67
 
65
68
  def _normalize_kas_url(self, url: str) -> str:
66
- """
67
- Normalize KAS URLs based on client security settings.
69
+ """Normalize KAS URLs based on client security settings.
68
70
 
69
71
  Args:
70
72
  url: The KAS URL to normalize
71
73
 
72
74
  Returns:
73
75
  Normalized URL with appropriate protocol and port
76
+
74
77
  """
75
78
  from urllib.parse import urlparse
76
79
 
@@ -78,7 +81,7 @@ class KASClient:
78
81
  # Parse the URL
79
82
  parsed = urlparse(url)
80
83
  except Exception as e:
81
- raise SDKException(f"error trying to parse URL [{url}]", e)
84
+ raise SDKException(f"error trying to parse URL [{url}]: {e}") from e
82
85
 
83
86
  # Check if we have a host or if this is likely a hostname:port combination
84
87
  if parsed.hostname is None:
@@ -100,10 +103,10 @@ class KASClient:
100
103
  try:
101
104
  port = int(port_str)
102
105
  return f"{scheme}://{host}:{port}"
103
- except ValueError:
106
+ except ValueError as err:
104
107
  raise SDKException(
105
108
  f"error trying to create URL for host and port [{url}]"
106
- )
109
+ ) from err
107
110
  else:
108
111
  # Hostname with or without path, add default port
109
112
  if "/" in url:
@@ -116,7 +119,7 @@ class KASClient:
116
119
  except Exception as e:
117
120
  raise SDKException(
118
121
  f"error trying to create URL for host and port [{url}]", e
119
- )
122
+ ) from e
120
123
 
121
124
  def _handle_existing_scheme(self, parsed) -> str:
122
125
  """Handle URLs with existing scheme by normalizing protocol and port."""
@@ -138,17 +141,17 @@ class KASClient:
138
141
  logging.debug(f"normalized url [{parsed.geturl()}] to [{normalized_url}]")
139
142
  return normalized_url
140
143
  except Exception as e:
141
- raise SDKException("error creating KAS address", e)
144
+ raise SDKException(f"error creating KAS address: {e}") from e
142
145
 
143
146
  def _get_wrapped_key_base64(self, key_access):
144
- """
145
- Extract and normalize the wrapped key to base64-encoded string.
147
+ """Extract and normalize the wrapped key to base64-encoded string.
146
148
 
147
149
  Args:
148
150
  key_access: KeyAccess object
149
151
 
150
152
  Returns:
151
153
  Base64-encoded wrapped key string
154
+
152
155
  """
153
156
  wrapped_key = getattr(key_access, "wrappedKey", None) or getattr(
154
157
  key_access, "wrapped_key", None
@@ -166,14 +169,14 @@ class KASClient:
166
169
  return wrapped_key
167
170
 
168
171
  def _build_key_access_dict(self, key_access):
169
- """
170
- Build key access dictionary from KeyAccess object, handling both old and new field names.
172
+ """Build key access dictionary from KeyAccess object, handling both old and new field names.
171
173
 
172
174
  Args:
173
175
  key_access: KeyAccess object
174
176
 
175
177
  Returns:
176
178
  Dictionary with key access information
179
+
177
180
  """
178
181
  wrapped_key = self._get_wrapped_key_base64(key_access)
179
182
 
@@ -197,12 +200,12 @@ class KASClient:
197
200
  return key_access_dict
198
201
 
199
202
  def _add_optional_fields(self, key_access_dict, key_access):
200
- """
201
- Add optional fields to key access dictionary.
203
+ """Add optional fields to key access dictionary.
202
204
 
203
205
  Args:
204
206
  key_access_dict: Dictionary to add fields to
205
207
  key_access: KeyAccess object to extract fields from
208
+
206
209
  """
207
210
  # Policy binding
208
211
  policy_binding = getattr(key_access, "policyBinding", None) or getattr(
@@ -244,14 +247,14 @@ class KASClient:
244
247
  key_access_dict["header"] = base64.b64encode(header).decode("utf-8")
245
248
 
246
249
  def _get_algorithm_from_session_key_type(self, session_key_type):
247
- """
248
- Convert session key type to algorithm string for KAS.
250
+ """Convert session key type to algorithm string for KAS.
249
251
 
250
252
  Args:
251
253
  session_key_type: Session key type (EC_KEY_TYPE or RSA_KEY_TYPE)
252
254
 
253
255
  Returns:
254
256
  Algorithm string or None
257
+
255
258
  """
256
259
  if session_key_type == EC_KEY_TYPE:
257
260
  return "ec:secp256r1" # Default EC curve for NanoTDF
@@ -262,8 +265,7 @@ class KASClient:
262
265
  def _build_rewrap_request(
263
266
  self, policy_json, client_public_key, key_access_dict, algorithm, has_header
264
267
  ):
265
- """
266
- Build the unsigned rewrap request structure.
268
+ """Build the unsigned rewrap request structure.
267
269
 
268
270
  Args:
269
271
  policy_json: Policy JSON string
@@ -274,6 +276,7 @@ class KASClient:
274
276
 
275
277
  Returns:
276
278
  Dictionary with unsigned rewrap request
279
+
277
280
  """
278
281
  import json
279
282
 
@@ -316,8 +319,7 @@ class KASClient:
316
319
  def _create_signed_request_jwt(
317
320
  self, policy_json, client_public_key, key_access, session_key_type=None
318
321
  ):
319
- """
320
- Create a signed JWT for the rewrap request.
322
+ """Create a signed JWT for the rewrap request.
321
323
  The JWT is signed with the DPoP private key.
322
324
 
323
325
  Args:
@@ -325,6 +327,7 @@ class KASClient:
325
327
  client_public_key: Client public key PEM string
326
328
  key_access: KeyAccess object
327
329
  session_key_type: Optional session key type (RSA_KEY_TYPE or EC_KEY_TYPE)
330
+
328
331
  """
329
332
  # Build key access dictionary handling both old and new field names
330
333
  key_access_dict = self._build_key_access_dict(key_access)
@@ -354,8 +357,7 @@ class KASClient:
354
357
  return jwt.encode(payload, self._dpop_private_key_pem, algorithm="RS256")
355
358
 
356
359
  def _create_connect_rpc_signed_token(self, key_access, policy_json):
357
- """
358
- Create a signed token specifically for Connect RPC requests.
360
+ """Create a signed token specifically for Connect RPC requests.
359
361
  For now, this delegates to the existing JWT creation method.
360
362
  """
361
363
  return self._create_signed_request_jwt(
@@ -363,8 +365,7 @@ class KASClient:
363
365
  )
364
366
 
365
367
  def _create_dpop_proof(self, method, url, access_token=None):
366
- """
367
- Create a DPoP proof JWT as per RFC 9449.
368
+ """Create a DPoP proof JWT as per RFC 9449.
368
369
 
369
370
  Args:
370
371
  method: HTTP method (e.g., "POST")
@@ -373,6 +374,7 @@ class KASClient:
373
374
 
374
375
  Returns:
375
376
  DPoP proof JWT string
377
+
376
378
  """
377
379
  now = int(time.time())
378
380
 
@@ -424,8 +426,7 @@ class KASClient:
424
426
  )
425
427
 
426
428
  def get_public_key(self, kas_info):
427
- """
428
- Get KAS public key using Connect RPC.
429
+ """Get KAS public key using Connect RPC.
429
430
  Checks cache first if available.
430
431
  """
431
432
  try:
@@ -448,10 +449,7 @@ class KASClient:
448
449
  raise
449
450
 
450
451
  def _get_public_key_with_connect_rpc(self, kas_info):
451
- """
452
- Get KAS public key using Connect RPC.
453
- """
454
-
452
+ """Get KAS public key using Connect RPC."""
455
453
  # Get access token for authentication if token source is available
456
454
  access_token = None
457
455
  if self.token_source:
@@ -483,17 +481,17 @@ class KASClient:
483
481
  f"Connect RPC public key request failed: {type(e).__name__}: {e}"
484
482
  )
485
483
  logging.error(f"Full traceback: {error_details}")
486
- raise SDKException(f"Connect RPC public key request failed: {e}")
484
+ raise SDKException(f"Connect RPC public key request failed: {e}") from e
487
485
 
488
486
  def _normalize_session_key_type(self, session_key_type):
489
- """
490
- Normalize session key type to the appropriate enum value.
487
+ """Normalize session key type to the appropriate enum value.
491
488
 
492
489
  Args:
493
490
  session_key_type: Type of the session key (KeyType enum or string "RSA"/"EC")
494
491
 
495
492
  Returns:
496
493
  Normalized key type enum
494
+
497
495
  """
498
496
  if isinstance(session_key_type, str):
499
497
  if session_key_type.upper() == "RSA":
@@ -511,14 +509,14 @@ class KASClient:
511
509
  return session_key_type
512
510
 
513
511
  def _prepare_ec_keypair(self, session_key_type):
514
- """
515
- Prepare EC key pair for unwrapping.
512
+ """Prepare EC key pair for unwrapping.
516
513
 
517
514
  Args:
518
515
  session_key_type: EC key type with curve information
519
516
 
520
517
  Returns:
521
518
  ECKeyPair instance and client public key
519
+
522
520
  """
523
521
  from .eckeypair import ECKeyPair
524
522
 
@@ -528,12 +526,12 @@ class KASClient:
528
526
  return ec_key_pair, client_public_key
529
527
 
530
528
  def _prepare_rsa_keypair(self):
531
- """
532
- Prepare RSA key pair for unwrapping, reusing if possible.
529
+ """Prepare RSA key pair for unwrapping, reusing if possible.
533
530
  Uses separate ephemeral keys for encryption (not DPoP keys).
534
531
 
535
532
  Returns:
536
533
  Client public key PEM for the ephemeral encryption key
534
+
537
535
  """
538
536
  if self.decryptor is None:
539
537
  # Generate ephemeral keys for encryption (separate from DPoP keys)
@@ -543,8 +541,7 @@ class KASClient:
543
541
  return self.client_public_key
544
542
 
545
543
  def _unwrap_with_ec(self, wrapped_key, ec_key_pair, response_data):
546
- """
547
- Unwrap a key using EC cryptography.
544
+ """Unwrap a key using EC cryptography.
548
545
 
549
546
  Args:
550
547
  wrapped_key: The wrapped key to decrypt
@@ -553,6 +550,7 @@ class KASClient:
553
550
 
554
551
  Returns:
555
552
  Unwrapped key as bytes
553
+
556
554
  """
557
555
  if ec_key_pair is None:
558
556
  raise SDKException(
@@ -581,9 +579,7 @@ class KASClient:
581
579
  return gcm.decrypt(wrapped_key)
582
580
 
583
581
  def _ensure_client_keypair(self, session_key_type):
584
- """
585
- Ensure client keypair is generated and stored.
586
- """
582
+ """Ensure client keypair is generated and stored."""
587
583
  if session_key_type == RSA_KEY_TYPE:
588
584
  if self.decryptor is None:
589
585
  private_key, public_key = CryptoUtils.generate_rsa_keypair()
@@ -600,15 +596,13 @@ class KASClient:
600
596
  self.client_public_key = CryptoUtils.get_rsa_public_key_pem(public_key)
601
597
 
602
598
  def _parse_and_decrypt_response(self, response):
603
- """
604
- Parse JSON response and decrypt the wrapped key.
605
- """
599
+ """Parse JSON response and decrypt the wrapped key."""
606
600
  try:
607
601
  response_data = response.json()
608
602
  except Exception as e:
609
603
  logging.error(f"Failed to parse JSON response: {e}")
610
604
  logging.error(f"Raw response content: {response.content}")
611
- raise SDKException(f"Invalid JSON response from KAS: {e}")
605
+ raise SDKException(f"Invalid JSON response from KAS: {e}") from e
612
606
 
613
607
  entity_wrapped_key = response_data.get("entityWrappedKey")
614
608
  if not entity_wrapped_key:
@@ -621,8 +615,7 @@ class KASClient:
621
615
  return self.decryptor.decrypt(encrypted_key)
622
616
 
623
617
  def unwrap(self, key_access, policy_json, session_key_type=None) -> bytes:
624
- """
625
- Unwrap a key using Connect RPC.
618
+ """Unwrap a key using Connect RPC.
626
619
 
627
620
  Args:
628
621
  key_access: Key access information
@@ -631,6 +624,7 @@ class KASClient:
631
624
 
632
625
  Returns:
633
626
  Unwrapped key bytes
627
+
634
628
  """
635
629
  # Default to RSA if not specified
636
630
  if session_key_type is None:
@@ -655,15 +649,14 @@ class KASClient:
655
649
  def _unwrap_with_connect_rpc(
656
650
  self, key_access, signed_token, session_key_type=None
657
651
  ) -> bytes:
658
- """
659
- Connect RPC method for unwrapping keys.
652
+ """Connect RPC method for unwrapping keys.
660
653
 
661
654
  Args:
662
655
  key_access: KeyAccess object
663
656
  signed_token: Signed JWT token
664
657
  session_key_type: Optional session key type (RSA_KEY_TYPE or EC_KEY_TYPE)
665
- """
666
658
 
659
+ """
667
660
  # Get access token for authentication if token source is available
668
661
  access_token = None
669
662
  if self.token_source:
@@ -702,8 +695,8 @@ class KASClient:
702
695
 
703
696
  except Exception as e:
704
697
  logging.error(f"Connect RPC rewrap failed: {e}")
705
- raise SDKException(f"Connect RPC rewrap failed: {e}")
698
+ raise SDKException(f"Connect RPC rewrap failed: {e}") from e
706
699
 
707
700
  def get_key_cache(self) -> KASKeyCache:
708
- """Returns the KAS key cache used for storing and retrieving encryption keys."""
701
+ """Return the KAS key cache used for storing and retrieving encryption keys."""
709
702
  return self.cache
@@ -1,5 +1,4 @@
1
- """
2
- KASConnectRPCClient: Handles Connect RPC communication with the Key Access Service (KAS).
1
+ """KASConnectRPCClient: Handles Connect RPC communication with the Key Access Service (KAS).
3
2
  This class encapsulates all interactions with otdf_python_proto.
4
3
  """
5
4
 
@@ -15,27 +14,25 @@ from .sdk_exceptions import SDKException
15
14
 
16
15
 
17
16
  class KASConnectRPCClient:
18
- """
19
- Handles Connect RPC communication with KAS service using otdf_python_proto.
20
- """
17
+ """Handles Connect RPC communication with KAS service using otdf_python_proto."""
21
18
 
22
19
  def __init__(self, use_plaintext=False, verify_ssl=True):
23
- """
24
- Initialize the Connect RPC client.
20
+ """Initialize the Connect RPC client.
25
21
 
26
22
  Args:
27
23
  use_plaintext: Whether to use plaintext (HTTP) connections
28
24
  verify_ssl: Whether to verify SSL certificates
25
+
29
26
  """
30
27
  self.use_plaintext = use_plaintext
31
28
  self.verify_ssl = verify_ssl
32
29
 
33
30
  def _create_http_client(self):
34
- """
35
- Create HTTP client with SSL verification configuration.
31
+ """Create HTTP client with SSL verification configuration.
36
32
 
37
33
  Returns:
38
34
  urllib3.PoolManager configured for SSL verification settings
35
+
39
36
  """
40
37
  if self.verify_ssl:
41
38
  logging.info("Using SSL verification enabled HTTP client")
@@ -46,14 +43,14 @@ class KASConnectRPCClient:
46
43
  return urllib3.PoolManager(cert_reqs="CERT_NONE")
47
44
 
48
45
  def _prepare_connect_rpc_url(self, kas_url):
49
- """
50
- Prepare the base URL for Connect RPC client.
46
+ """Prepare the base URL for Connect RPC client.
51
47
 
52
48
  Args:
53
49
  kas_url: The normalized KAS URL
54
50
 
55
51
  Returns:
56
52
  Base URL for Connect RPC client (without /kas suffix)
53
+
57
54
  """
58
55
  connect_rpc_base_url = kas_url
59
56
  # Remove /kas suffix, if present
@@ -61,14 +58,14 @@ class KASConnectRPCClient:
61
58
  return connect_rpc_base_url
62
59
 
63
60
  def _prepare_auth_headers(self, access_token):
64
- """
65
- Prepare authentication headers if access token is available.
61
+ """Prepare authentication headers if access token is available.
66
62
 
67
63
  Args:
68
64
  access_token: Bearer token for authentication
69
65
 
70
66
  Returns:
71
67
  Dictionary with authentication headers or None
68
+
72
69
  """
73
70
  if access_token:
74
71
  auth_headers = AuthHeaders(
@@ -79,8 +76,7 @@ class KASConnectRPCClient:
79
76
  return None
80
77
 
81
78
  def get_public_key(self, normalized_kas_url, kas_info, access_token=None):
82
- """
83
- Get KAS public key using Connect RPC.
79
+ """Get KAS public key using Connect RPC.
84
80
 
85
81
  Args:
86
82
  normalized_kas_url: The normalized KAS URL
@@ -89,6 +85,7 @@ class KASConnectRPCClient:
89
85
 
90
86
  Returns:
91
87
  Updated kas_info with public_key and kid
88
+
92
89
  """
93
90
  logging.info(
94
91
  f"KAS Connect RPC client settings for public key retrieval: "
@@ -138,13 +135,12 @@ class KASConnectRPCClient:
138
135
  f"Connect RPC public key request failed: {type(e).__name__}: {e}"
139
136
  )
140
137
  logging.error(f"Full traceback: {error_details}")
141
- raise SDKException(f"Connect RPC public key request failed: {e}")
138
+ raise SDKException(f"Connect RPC public key request failed: {e}") from e
142
139
 
143
140
  def unwrap_key(
144
141
  self, normalized_kas_url, key_access, signed_token, access_token=None
145
142
  ):
146
- """
147
- Unwrap a key using Connect RPC.
143
+ """Unwrap a key using Connect RPC.
148
144
 
149
145
  Args:
150
146
  normalized_kas_url: The normalized KAS URL
@@ -154,6 +150,7 @@ class KASConnectRPCClient:
154
150
 
155
151
  Returns:
156
152
  Unwrapped key bytes from the response
153
+
157
154
  """
158
155
  logging.info(
159
156
  f"KAS Connect RPC client settings for unwrap: "
@@ -210,4 +207,4 @@ class KASConnectRPCClient:
210
207
 
211
208
  except Exception as e:
212
209
  logging.error(f"Connect RPC rewrap failed: {e}")
213
- raise SDKException(f"Connect RPC rewrap failed: {e}")
210
+ raise SDKException(f"Connect RPC rewrap failed: {e}") from e
otdf_python/kas_info.py CHANGED
@@ -1,10 +1,11 @@
1
+ """Key Access Service information and configuration."""
2
+
1
3
  from dataclasses import dataclass
2
4
 
3
5
 
4
6
  @dataclass
5
7
  class KASInfo:
6
- """
7
- Configuration for Key Access Server (KAS) information.
8
+ """Configuration for Key Access Server (KAS) information.
8
9
  This class stores details about a Key Access Server including its URL,
9
10
  public key, key ID, default status, and cryptographic algorithm.
10
11
  """
@@ -16,7 +17,7 @@ class KASInfo:
16
17
  algorithm: str | None = None
17
18
 
18
19
  def clone(self):
19
- """Creates a copy of this KASInfo object."""
20
+ """Create a copy of this KASInfo object."""
20
21
  from copy import copy
21
22
 
22
23
  return copy(self)
@@ -1,19 +1,19 @@
1
- """
2
- KASKeyCache: In-memory cache for KAS (Key Access Service) public keys and info.
3
- """
1
+ """KASKeyCache: In-memory cache for KAS (Key Access Service) public keys and info."""
4
2
 
5
3
  import threading
6
4
  from typing import Any
7
5
 
8
6
 
9
7
  class KASKeyCache:
8
+ """In-memory cache for KAS public keys and information."""
9
+
10
10
  def __init__(self):
11
+ """Initialize KAS key cache."""
11
12
  self._cache = {}
12
13
  self._lock = threading.Lock()
13
14
 
14
15
  def get(self, url: str, algorithm: str | None = None) -> Any | None:
15
- """
16
- Gets a KASInfo object from the cache based on URL and algorithm.
16
+ """Get a KASInfo object from cache based on URL and algorithm.
17
17
 
18
18
  Args:
19
19
  url: The URL of the KAS
@@ -21,17 +21,18 @@ class KASKeyCache:
21
21
 
22
22
  Returns:
23
23
  The cached KASInfo object, or None if not found
24
+
24
25
  """
25
26
  cache_key = self._make_key(url, algorithm)
26
27
  with self._lock:
27
28
  return self._cache.get(cache_key)
28
29
 
29
30
  def store(self, kas_info) -> None:
30
- """
31
- Stores a KASInfo object in the cache.
31
+ """Store a KASInfo object in cache.
32
32
 
33
33
  Args:
34
34
  kas_info: The KASInfo object to store
35
+
35
36
  """
36
37
  cache_key = self._make_key(kas_info.url, getattr(kas_info, "algorithm", None))
37
38
  with self._lock:
@@ -43,10 +44,10 @@ class KASKeyCache:
43
44
  self._cache[key] = value
44
45
 
45
46
  def clear(self):
46
- """Clears the cache"""
47
+ """Clear the cache."""
47
48
  with self._lock:
48
49
  self._cache.clear()
49
50
 
50
51
  def _make_key(self, url: str, algorithm: str | None = None) -> str:
51
- """Creates a cache key from URL and algorithm"""
52
+ """Create a cache key from URL and algorithm."""
52
53
  return f"{url}:{algorithm or ''}"
otdf_python/key_type.py CHANGED
@@ -1,7 +1,11 @@
1
+ """Key type constants for RSA and EC encryption."""
2
+
1
3
  from enum import Enum
2
4
 
3
5
 
4
6
  class KeyType(Enum):
7
+ """Key type enumeration for encryption algorithms."""
8
+
5
9
  RSA2048Key = "rsa:2048"
6
10
  EC256Key = "ec:secp256r1"
7
11
  EC384Key = "ec:secp384r1"
@@ -1,5 +1,4 @@
1
- """
2
- Constants for session key types used in the KAS client.
1
+ """Constants for session key types used in the KAS client.
3
2
  This matches the Java SDK's KeyType enum pattern.
4
3
  """
5
4
 
@@ -7,9 +6,7 @@ from enum import Enum, auto
7
6
 
8
7
 
9
8
  class KeyType(Enum):
10
- """
11
- Enum for key types used in the KAS client.
12
- """
9
+ """Enum for key types used in the KAS client."""
13
10
 
14
11
  RSA2048 = auto()
15
12
  EC_P256 = auto()
@@ -18,16 +15,12 @@ class KeyType(Enum):
18
15
 
19
16
  @property
20
17
  def is_ec(self):
21
- """
22
- Returns True if this key type is an EC key, False otherwise.
23
- """
18
+ """Returns True if this key type is an EC key, False otherwise."""
24
19
  return self in [KeyType.EC_P256, KeyType.EC_P384, KeyType.EC_P521]
25
20
 
26
21
  @property
27
22
  def curve_name(self):
28
- """
29
- Returns the curve name for EC keys.
30
- """
23
+ """Returns the curve name for EC keys."""
31
24
  if self == KeyType.EC_P256:
32
25
  return "P-256"
33
26
  elif self == KeyType.EC_P384: