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
@@ -1,5 +1,4 @@
1
- """
2
- Elliptic Curve Constants for NanoTDF.
1
+ """Elliptic Curve Constants for NanoTDF.
3
2
 
4
3
  This module defines shared constants for elliptic curve operations used across
5
4
  the SDK, particularly for NanoTDF encryption/decryption.
@@ -14,8 +13,7 @@ from cryptography.hazmat.primitives.asymmetric import ec
14
13
 
15
14
 
16
15
  class ECCConstants:
17
- """
18
- Centralized constants for elliptic curve cryptography operations.
16
+ """Centralized constants for elliptic curve cryptography operations.
19
17
 
20
18
  This class provides mappings between curve names, curve type integers,
21
19
  cryptography curve objects, and compressed public key sizes.
@@ -67,8 +65,7 @@ class ECCConstants:
67
65
 
68
66
  @classmethod
69
67
  def get_curve_name(cls, curve_type: int) -> str:
70
- """
71
- Get curve name from curve type integer.
68
+ """Get curve name from curve type integer.
72
69
 
73
70
  Args:
74
71
  curve_type: Curve type (0=secp256r1, 1=secp384r1, 2=secp521r1, 3=secp256k1)
@@ -78,6 +75,7 @@ class ECCConstants:
78
75
 
79
76
  Raises:
80
77
  ValueError: If curve_type is not supported
78
+
81
79
  """
82
80
  name = cls.CURVE_TYPE_TO_NAME.get(curve_type)
83
81
  if name is None:
@@ -89,8 +87,7 @@ class ECCConstants:
89
87
 
90
88
  @classmethod
91
89
  def get_curve_type(cls, curve_name: str) -> int:
92
- """
93
- Get curve type integer from curve name.
90
+ """Get curve type integer from curve name.
94
91
 
95
92
  Args:
96
93
  curve_name: Curve name (e.g., "secp256r1")
@@ -100,6 +97,7 @@ class ECCConstants:
100
97
 
101
98
  Raises:
102
99
  ValueError: If curve_name is not supported
100
+
103
101
  """
104
102
  curve_type = cls.CURVE_NAME_TO_TYPE.get(curve_name.lower())
105
103
  if curve_type is None:
@@ -111,8 +109,7 @@ class ECCConstants:
111
109
 
112
110
  @classmethod
113
111
  def get_compressed_key_size_by_type(cls, curve_type: int) -> int:
114
- """
115
- Get compressed public key size from curve type integer.
112
+ """Get compressed public key size from curve type integer.
116
113
 
117
114
  Args:
118
115
  curve_type: Curve type (0=secp256r1, 1=secp384r1, 2=secp521r1, 3=secp256k1)
@@ -122,6 +119,7 @@ class ECCConstants:
122
119
 
123
120
  Raises:
124
121
  ValueError: If curve_type is not supported
122
+
125
123
  """
126
124
  size = cls.COMPRESSED_KEY_SIZE_BY_TYPE.get(curve_type)
127
125
  if size is None:
@@ -133,8 +131,7 @@ class ECCConstants:
133
131
 
134
132
  @classmethod
135
133
  def get_compressed_key_size_by_name(cls, curve_name: str) -> int:
136
- """
137
- Get compressed public key size from curve name.
134
+ """Get compressed public key size from curve name.
138
135
 
139
136
  Args:
140
137
  curve_name: Curve name (e.g., "secp256r1")
@@ -144,6 +141,7 @@ class ECCConstants:
144
141
 
145
142
  Raises:
146
143
  ValueError: If curve_name is not supported
144
+
147
145
  """
148
146
  size = cls.COMPRESSED_KEY_SIZE_BY_NAME.get(curve_name.lower())
149
147
  if size is None:
@@ -155,8 +153,7 @@ class ECCConstants:
155
153
 
156
154
  @classmethod
157
155
  def get_curve_object(cls, curve_name: str) -> ec.EllipticCurve:
158
- """
159
- Get cryptography library curve object from curve name.
156
+ """Get cryptography library curve object from curve name.
160
157
 
161
158
  Args:
162
159
  curve_name: Curve name (e.g., "secp256r1")
@@ -166,6 +163,7 @@ class ECCConstants:
166
163
 
167
164
  Raises:
168
165
  ValueError: If curve_name is not supported
166
+
169
167
  """
170
168
  curve = cls.CURVE_OBJECTS.get(curve_name.lower())
171
169
  if curve is None:
otdf_python/ecc_mode.py CHANGED
@@ -1,9 +1,10 @@
1
+ """Elliptic Curve Cryptography mode enumeration."""
2
+
1
3
  from otdf_python.ecc_constants import ECCConstants
2
4
 
3
5
 
4
6
  class ECCMode:
5
- """
6
- ECC (Elliptic Curve Cryptography) mode configuration for NanoTDF.
7
+ """ECC (Elliptic Curve Cryptography) mode configuration for NanoTDF.
7
8
 
8
9
  This class encapsulates the curve type and policy binding mode (GMAC vs ECDSA)
9
10
  that are encoded in the NanoTDF header. It delegates to ECCConstants for
@@ -11,6 +12,7 @@ class ECCMode:
11
12
  """
12
13
 
13
14
  def __init__(self, curve_mode: int = 0, use_ecdsa_binding: bool = False):
15
+ """Initialize ECC mode."""
14
16
  self.curve_mode = curve_mode
15
17
  self.use_ecdsa_binding = use_ecdsa_binding
16
18
 
@@ -34,6 +36,7 @@ class ECCMode:
34
36
 
35
37
  Raises:
36
38
  ValueError: If curve_mode is not supported
39
+
37
40
  """
38
41
  # Delegate to ECCConstants for the authoritative mapping
39
42
  return ECCConstants.get_curve_name(self.curve_mode)
@@ -50,6 +53,7 @@ class ECCMode:
50
53
 
51
54
  Raises:
52
55
  ValueError: If curve_type is not supported
56
+
53
57
  """
54
58
  # Delegate to ECCConstants for the authoritative mapping
55
59
  return ECCConstants.get_compressed_key_size_by_type(curve_type)
@@ -71,6 +75,7 @@ class ECCMode:
71
75
 
72
76
  Raises:
73
77
  ValueError: If curve_str is not a supported curve or binding type
78
+
74
79
  """
75
80
  # Handle policy binding types (always use secp256r1 as default curve)
76
81
  if curve_str.lower() == "gmac":
otdf_python/ecdh.py CHANGED
@@ -1,5 +1,4 @@
1
- """
2
- ECDH (Elliptic Curve Diffie-Hellman) key exchange for NanoTDF.
1
+ """ECDH (Elliptic Curve Diffie-Hellman) key exchange for NanoTDF.
3
2
 
4
3
  This module implements the ECDH key exchange protocol with HKDF key derivation
5
4
  as specified in the NanoTDF spec. It supports the following curves:
@@ -52,8 +51,7 @@ class InvalidKeyError(ECDHError):
52
51
 
53
52
 
54
53
  def get_curve(curve_name: str) -> ec.EllipticCurve:
55
- """
56
- Get the cryptography curve object for a given curve name.
54
+ """Get the cryptography curve object for a given curve name.
57
55
 
58
56
  Args:
59
57
  curve_name: Name of the curve (e.g., "secp256r1")
@@ -63,6 +61,7 @@ def get_curve(curve_name: str) -> ec.EllipticCurve:
63
61
 
64
62
  Raises:
65
63
  UnsupportedCurveError: If the curve is not supported
64
+
66
65
  """
67
66
  try:
68
67
  # Delegate to ECCConstants for the authoritative mapping
@@ -72,8 +71,7 @@ def get_curve(curve_name: str) -> ec.EllipticCurve:
72
71
 
73
72
 
74
73
  def get_compressed_key_size(curve_name: str) -> int:
75
- """
76
- Get the size of a compressed public key for a given curve.
74
+ """Get the size of a compressed public key for a given curve.
77
75
 
78
76
  Args:
79
77
  curve_name: Name of the curve (e.g., "secp256r1")
@@ -83,6 +81,7 @@ def get_compressed_key_size(curve_name: str) -> int:
83
81
 
84
82
  Raises:
85
83
  UnsupportedCurveError: If the curve is not supported
84
+
86
85
  """
87
86
  try:
88
87
  # Delegate to ECCConstants for the authoritative mapping
@@ -94,8 +93,7 @@ def get_compressed_key_size(curve_name: str) -> int:
94
93
  def generate_ephemeral_keypair(
95
94
  curve_name: str,
96
95
  ) -> tuple[ec.EllipticCurvePrivateKey, ec.EllipticCurvePublicKey]:
97
- """
98
- Generate an ephemeral keypair for ECDH.
96
+ """Generate an ephemeral keypair for ECDH.
99
97
 
100
98
  Args:
101
99
  curve_name: Name of the curve (e.g., "secp256r1")
@@ -105,6 +103,7 @@ def generate_ephemeral_keypair(
105
103
 
106
104
  Raises:
107
105
  UnsupportedCurveError: If the curve is not supported
106
+
108
107
  """
109
108
  curve = get_curve(curve_name)
110
109
  private_key = ec.generate_private_key(curve, default_backend())
@@ -113,14 +112,14 @@ def generate_ephemeral_keypair(
113
112
 
114
113
 
115
114
  def compress_public_key(public_key: ec.EllipticCurvePublicKey) -> bytes:
116
- """
117
- Compress an EC public key to compressed point format.
115
+ """Compress an EC public key to compressed point format.
118
116
 
119
117
  Args:
120
118
  public_key: The EC public key to compress
121
119
 
122
120
  Returns:
123
121
  bytes: Compressed public key (33-67 bytes depending on curve)
122
+
124
123
  """
125
124
  return public_key.public_bytes(
126
125
  encoding=Encoding.X962, format=PublicFormat.CompressedPoint
@@ -130,8 +129,7 @@ def compress_public_key(public_key: ec.EllipticCurvePublicKey) -> bytes:
130
129
  def decompress_public_key(
131
130
  compressed_key: bytes, curve_name: str
132
131
  ) -> ec.EllipticCurvePublicKey:
133
- """
134
- Decompress a public key from compressed point format.
132
+ """Decompress a public key from compressed point format.
135
133
 
136
134
  Args:
137
135
  compressed_key: The compressed public key bytes
@@ -143,6 +141,7 @@ def decompress_public_key(
143
141
  Raises:
144
142
  InvalidKeyError: If the key cannot be decompressed
145
143
  UnsupportedCurveError: If the curve is not supported
144
+
146
145
  """
147
146
  try:
148
147
  curve = get_curve(curve_name)
@@ -156,14 +155,13 @@ def decompress_public_key(
156
155
 
157
156
  return ec.EllipticCurvePublicKey.from_encoded_point(curve, compressed_key)
158
157
  except (ValueError, TypeError) as e:
159
- raise InvalidKeyError(f"Failed to decompress public key: {e}")
158
+ raise InvalidKeyError(f"Failed to decompress public key: {e}") from e
160
159
 
161
160
 
162
161
  def derive_shared_secret(
163
162
  private_key: ec.EllipticCurvePrivateKey, public_key: ec.EllipticCurvePublicKey
164
163
  ) -> bytes:
165
- """
166
- Derive a shared secret using ECDH.
164
+ """Derive a shared secret using ECDH.
167
165
 
168
166
  Args:
169
167
  private_key: The private key (can be ephemeral or recipient's key)
@@ -174,12 +172,13 @@ def derive_shared_secret(
174
172
 
175
173
  Raises:
176
174
  ECDHError: If ECDH fails
175
+
177
176
  """
178
177
  try:
179
178
  shared_secret = private_key.exchange(ec.ECDH(), public_key)
180
179
  return shared_secret
181
180
  except Exception as e:
182
- raise ECDHError(f"Failed to derive shared secret: {e}")
181
+ raise ECDHError(f"Failed to derive shared secret: {e}") from e
183
182
 
184
183
 
185
184
  def derive_key_from_shared_secret(
@@ -188,8 +187,7 @@ def derive_key_from_shared_secret(
188
187
  salt: bytes | None = None,
189
188
  info: bytes = b"",
190
189
  ) -> bytes:
191
- """
192
- Derive a symmetric encryption key from the ECDH shared secret using HKDF.
190
+ """Derive a symmetric encryption key from the ECDH shared secret using HKDF.
193
191
 
194
192
  Args:
195
193
  shared_secret: The raw ECDH shared secret
@@ -202,6 +200,7 @@ def derive_key_from_shared_secret(
202
200
 
203
201
  Raises:
204
202
  ECDHError: If key derivation fails
203
+
205
204
  """
206
205
  if salt is None:
207
206
  salt = NANOTDF_HKDF_SALT
@@ -216,14 +215,13 @@ def derive_key_from_shared_secret(
216
215
  )
217
216
  return hkdf.derive(shared_secret)
218
217
  except Exception as e:
219
- raise ECDHError(f"Failed to derive key from shared secret: {e}")
218
+ raise ECDHError(f"Failed to derive key from shared secret: {e}") from e
220
219
 
221
220
 
222
221
  def encrypt_key_with_ecdh(
223
222
  recipient_public_key_pem: str, curve_name: str = "secp256r1"
224
223
  ) -> tuple[bytes, bytes]:
225
- """
226
- High-level function: Generate ephemeral keypair and derive encryption key.
224
+ """High-level function: Generate ephemeral keypair and derive encryption key.
227
225
 
228
226
  This is used during NanoTDF encryption to derive the key that will be used
229
227
  to encrypt the payload. The ephemeral public key must be stored in the
@@ -242,6 +240,7 @@ def encrypt_key_with_ecdh(
242
240
  ECDHError: If key derivation fails
243
241
  InvalidKeyError: If recipient's public key is invalid
244
242
  UnsupportedCurveError: If the curve is not supported
243
+
245
244
  """
246
245
  # Load recipient's public key
247
246
  try:
@@ -251,7 +250,7 @@ def encrypt_key_with_ecdh(
251
250
  if not isinstance(recipient_public_key, ec.EllipticCurvePublicKey):
252
251
  raise InvalidKeyError("Recipient's public key is not an EC key")
253
252
  except Exception as e:
254
- raise InvalidKeyError(f"Failed to load recipient's public key: {e}")
253
+ raise InvalidKeyError(f"Failed to load recipient's public key: {e}") from e
255
254
 
256
255
  # Generate ephemeral keypair
257
256
  ephemeral_private_key, ephemeral_public_key = generate_ephemeral_keypair(curve_name)
@@ -273,8 +272,7 @@ def decrypt_key_with_ecdh(
273
272
  compressed_ephemeral_public_key: bytes,
274
273
  curve_name: str = "secp256r1",
275
274
  ) -> bytes:
276
- """
277
- High-level function: Derive decryption key from ephemeral public key and recipient's private key.
275
+ """High-level function: Derive decryption key from ephemeral public key and recipient's private key.
278
276
 
279
277
  This is used during NanoTDF decryption to derive the same key that was used
280
278
  to encrypt the payload. The ephemeral public key is extracted from the
@@ -292,6 +290,7 @@ def decrypt_key_with_ecdh(
292
290
  ECDHError: If key derivation fails
293
291
  InvalidKeyError: If keys are invalid
294
292
  UnsupportedCurveError: If the curve is not supported
293
+
295
294
  """
296
295
  # Load recipient's private key
297
296
  try:
@@ -301,7 +300,7 @@ def decrypt_key_with_ecdh(
301
300
  if not isinstance(recipient_private_key, ec.EllipticCurvePrivateKey):
302
301
  raise InvalidKeyError("Recipient's private key is not an EC key")
303
302
  except Exception as e:
304
- raise InvalidKeyError(f"Failed to load recipient's private key: {e}")
303
+ raise InvalidKeyError(f"Failed to load recipient's private key: {e}") from e
305
304
 
306
305
  # Decompress ephemeral public key
307
306
  ephemeral_public_key = decompress_public_key(
otdf_python/eckeypair.py CHANGED
@@ -1,3 +1,5 @@
1
+ """Elliptic Curve key pair management."""
2
+
1
3
  from cryptography.exceptions import InvalidSignature
2
4
  from cryptography.hazmat.backends import default_backend
3
5
  from cryptography.hazmat.primitives import hashes, serialization
@@ -12,7 +14,10 @@ from cryptography.hazmat.primitives.serialization import (
12
14
 
13
15
 
14
16
  class ECKeyPair:
17
+ """Elliptic Curve key pair for cryptographic operations."""
18
+
15
19
  def __init__(self, curve=None):
20
+ """Initialize EC key pair."""
16
21
  if curve is None:
17
22
  curve = ec.SECP256R1()
18
23
  self.private_key = ec.generate_private_key(curve, default_backend())
otdf_python/header.py CHANGED
@@ -1,3 +1,5 @@
1
+ """TDF header parsing and serialization."""
2
+
1
3
  from otdf_python.constants import MAGIC_NUMBER_AND_VERSION
2
4
  from otdf_python.ecc_mode import ECCMode
3
5
  from otdf_python.policy_info import PolicyInfo
@@ -6,10 +8,13 @@ from otdf_python.symmetric_and_payload_config import SymmetricAndPayloadConfig
6
8
 
7
9
 
8
10
  class Header:
11
+ """TDF header with encryption and policy information."""
12
+
9
13
  # Size of GMAC (Galois Message Authentication Code) for policy binding
10
14
  GMAC_SIZE = 8
11
15
 
12
16
  def __init__(self):
17
+ """Initialize TDF header."""
13
18
  self.kas_locator: ResourceLocator | None = None
14
19
  self.ecc_mode: ECCMode | None = None
15
20
  self.payload_config: SymmetricAndPayloadConfig | None = None
@@ -1,8 +1,12 @@
1
+ """Exception for invalid ZIP file errors."""
2
+
3
+
1
4
  class InvalidZipException(Exception):
2
- """
3
- Raised when a ZIP file is invalid or corrupted.
5
+ """Raised when a ZIP file is invalid or corrupted.
6
+
4
7
  Based on Java implementation.
5
8
  """
6
9
 
7
10
  def __init__(self, message: str):
11
+ """Initialize exception."""
8
12
  super().__init__(message)