otdf-python 0.3.0__py3-none-any.whl → 0.3.5__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: otdf-python
3
- Version: 0.3.0
3
+ Version: 0.3.5
4
4
  Summary: Unofficial OpenTDF SDK for Python
5
5
  Author-email: b-long <b-long@users.noreply.github.com>
6
6
  License-File: LICENSE
@@ -32,65 +32,6 @@ Unofficial OpenTDF SDK for Python
32
32
 
33
33
  A legacy version (0.2.x) of this project is available for users who need the previous implementation. For more information, see [LEGACY_VERSION.md](docs/LEGACY_VERSION.md) or visit the [legacy branch on GitHub](https://github.com/b-long/opentdf-python-sdk/tree/0.2.x).
34
34
 
35
- ## Prerequisites
36
-
37
- This project uses [uv](https://docs.astral.sh/uv/) for dependency management and task running.
38
-
39
- ### Installing uv
40
-
41
- Install `uv` using one of the following methods:
42
-
43
- **macOS/Linux:**
44
- ```bash
45
- curl -LsSf https://astral.sh/uv/install.sh | sh
46
- ```
47
-
48
- **Windows:**
49
- ```powershell
50
- powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
51
- ```
52
-
53
- **Using Homebrew (macOS):**
54
- ```bash
55
- brew install uv
56
- ```
57
-
58
- For more installation options, see the [uv installation guide](https://docs.astral.sh/uv/getting-started/installation/).
59
-
60
- ## Development Setup
61
-
62
- 1. Clone the repository:
63
- ```bash
64
- git clone <repository-url>
65
- cd opentdf-python-sdk
66
- ```
67
-
68
- 2. Install dependencies:
69
- ```bash
70
- uv sync
71
- ```
72
-
73
- ## Running Tests
74
-
75
- Run the full test suite:
76
- ```bash
77
- uv run pytest tests/
78
- ```
79
-
80
- Run specific test files:
81
- ```bash
82
- uv run pytest tests/test_sdk.py
83
- ```
84
-
85
- Run tests with verbose output:
86
- ```bash
87
- uv run pytest tests/ -v
88
- ```
89
-
90
- Run integration tests only:
91
- ```bash
92
- uv run pytest tests/ -m integration
93
- ```
94
35
 
95
36
  ## Installation
96
37
 
@@ -99,21 +40,6 @@ Install from PyPI:
99
40
  pip install otdf-python
100
41
  ```
101
42
 
102
-
103
- ## Protobuf & Connect RPC Generation
104
-
105
- This project uses a dedicated submodule, `otdf-python-proto/`, for generating Python protobuf files and Connect RPC clients from OpenTDF platform proto definitions.
106
-
107
- ### Regenerating Protobuf & Connect RPC Files
108
-
109
- From the submodule:
110
- ```bash
111
- cd otdf-python-proto
112
- uv run python scripts/generate_connect_proto.py
113
- ```
114
-
115
- See [`otdf-python-proto/README.md`](otdf-python-proto/README.md) and [`PROTOBUF_SETUP.md`](PROTOBUF_SETUP.md) for details.
116
-
117
43
  ## Quick Start
118
44
 
119
45
  ### Basic Configuration
@@ -160,7 +86,7 @@ sdk = builder.build()
160
86
  from io import BytesIO
161
87
 
162
88
  # Create TDF configuration with attributes
163
- config = sdk.new_tdf_config(attributes=["https://example.com/attr/classification/value/public"])
89
+ config = sdk.new_tdf_config(attributes=["https://example.net/attr/attr1/value/value1"])
164
90
 
165
91
  # Encrypt data to TDF format
166
92
  input_data = b"Hello, World!"
@@ -176,23 +102,18 @@ with open("encrypted.tdf", "wb") as f:
176
102
  ### Decrypt Data
177
103
 
178
104
  ```python
179
- from otdf_python.tdf import TDFReaderConfig
180
-
181
105
  # Read encrypted TDF file
182
106
  with open("encrypted.tdf", "rb") as f:
183
107
  encrypted_data = f.read()
184
108
 
185
109
  # Decrypt TDF
186
- reader_config = TDFReaderConfig()
187
- tdf_reader = sdk.load_tdf(encrypted_data, reader_config)
110
+ tdf_reader = sdk.load_tdf(encrypted_data)
188
111
  decrypted_data = tdf_reader.payload
189
112
 
190
113
  # Save decrypted data
191
114
  with open("decrypted.txt", "wb") as f:
192
115
  f.write(decrypted_data)
193
116
 
194
- # Don't forget to close the SDK when done
195
- sdk.close()
196
117
  ```
197
118
 
198
119
  ## Project Structure
@@ -208,6 +129,7 @@ src/otdf_python/
208
129
  └── ... # Additional modules
209
130
  tests/
210
131
  └── ... # Various tests
132
+ ```
211
133
 
212
134
  ## Contributing
213
135
 
@@ -215,14 +137,14 @@ tests/
215
137
  2. Create a feature branch: `git checkout -b feature-name`
216
138
  3. Make your changes
217
139
  4. Run tests: `uv run pytest tests/`
218
- 5. Commit your changes: `git commit -am 'Add feature'`
140
+ 5. Commit your changes: `git commit -am 'feat: add feature'`
219
141
  6. Push to the branch: `git push origin feature-name`
220
142
  7. Submit a pull request
221
143
 
222
144
  ### Release Process
223
145
 
224
146
  For maintainers and contributors working on releases:
225
- - See [RELEASES.md](RELEASES.md) for comprehensive release documentation
147
+ - See [RELEASES.md](docs/RELEASES.md) for comprehensive release documentation
226
148
  - Feature branch alpha releases available for testing changes before merge
227
149
  - Automated releases via Release Please on the main branch
228
150
 
@@ -3,12 +3,10 @@ otdf_python/__main__.py,sha256=V9cmvhX9Ht2wpPOHGrw_onBogStJnuWTx9eyiezEDsQ,276
3
3
  otdf_python/address_normalizer.py,sha256=pv7RoG4Dwac6so7NK1zCsVZBNFCcQK3cj_4iW6FPnRw,2815
4
4
  otdf_python/aesgcm.py,sha256=KhMabIUYxxp1gUwdwNLiwD-SvmJPqW2PunbgeguMEh8,1666
5
5
  otdf_python/assertion_config.py,sha256=P2Pc1OxMk9Ln6bvp1ZI8j66Q7uoZoYB7oMB6WLG38Y8,1763
6
- otdf_python/asym_crypto.py,sha256=GzijIWYhWeazEwXYw-s8fRHVS7JLxDqHVcl7oRSOx_A,3008
7
- otdf_python/asym_decryption.py,sha256=eVfgfzFHMK4ni3g-u8fBe3sdF02eo_msZouo19UBlh4,1981
8
- otdf_python/asym_encryption.py,sha256=ex-S_PvBnWKSSvbocTjJ_p5VQjjeiThcc9bMRZqvifw,2932
9
- otdf_python/auth_headers.py,sha256=LFjfWiobNo9DNaPgcm-aJANzHZh7_u9oHTxnEzEq-Ro,562
6
+ otdf_python/asym_crypto.py,sha256=dwx3lUxUt3e8JA9z2lQrpsOBA3x36DN07bQbcVGZBvI,7251
7
+ otdf_python/auth_headers.py,sha256=a0TQD36QKXO1G4swume2wx3Sk9zQsSWEAQJnST2DlH0,966
10
8
  otdf_python/autoconfigure_utils.py,sha256=NiNtbapBoIO-6kM59HRvX3Kj_Z0IRMrTkFlXjwuNfPc,3236
11
- otdf_python/cli.py,sha256=GoDmar7E4TG_kMTTa121TFL2aQGksrF_rQ4j9dPhuxQ,19838
9
+ otdf_python/cli.py,sha256=0wXan-QrKQllIlSCnXDsFAT1EQpH7gv7g6TZKIVGI8E,19883
12
10
  otdf_python/collection_store.py,sha256=MH1RxlevRVFj5lBS_6DN3zz5ZgOhBjD0P7AYBLBqS0o,1004
13
11
  otdf_python/collection_store_impl.py,sha256=g3YeSwMeXr1BNmwmFr75fv9ptPjieC36Bhct-Jf1trw,613
14
12
  otdf_python/config.py,sha256=uGYVByTWl7pWcKRER41ef-ay5VevIbOyUAEd6BIArVg,2185
@@ -16,30 +14,32 @@ otdf_python/connect_client.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU
16
14
  otdf_python/constants.py,sha256=SFGjrzB1GK4CkSZgNlEUIAZMY37MelTxgc8ajXIAhoc,53
17
15
  otdf_python/crypto_utils.py,sha256=KsNss9EC-wHs1nCiKivDnxJSMv0uxGlWLDcCowadcV8,2837
18
16
  otdf_python/dpop.py,sha256=ssKXTGydJS--rBTCqX9Y8qy-c5Um4pIZPG3DZcj0rWU,2301
19
- otdf_python/ecc_mode.py,sha256=PUJJ6n0jk0IKKNVZ1_tWGPPujGR4fny4m5JBkUhPimo,1085
17
+ otdf_python/ecc_constants.py,sha256=uZkYeuhSnqCLjnrINOsaQQhdCKUTKLwL0t2Cswou4BA,5892
18
+ otdf_python/ecc_mode.py,sha256=8q-R_XR9WWmuzwI0gI6RQpsEzK8PD8Hi8uuDCvSsGXY,3077
19
+ otdf_python/ecdh.py,sha256=D8jHIZHYeQwCGCtjd3FJLXqIXnnYeEqSRLiWoiHZm1k,10233
20
20
  otdf_python/eckeypair.py,sha256=4uZfdJaqSxW605FhU58Gko06mixSNoDOGBgVpP5VlSQ,2324
21
- otdf_python/header.py,sha256=YMmsBdiHM8rxLCtezIf_Cnrg2idoGpvq--WMxYqZ-no,5377
21
+ otdf_python/header.py,sha256=0p259_GNO9aumUg0KOBZkiawKZTdFzoYVcyHaMCMVC8,7045
22
22
  otdf_python/invalid_zip_exception.py,sha256=YEU20hM5_yE-RWJncPZobcqj4a90RAuahB54VjAS2SU,213
23
- otdf_python/kas_client.py,sha256=vjOP3o7ip2cWqfE4FB1sHWqXWWY-aXPI-PnaKsPPEF4,22464
24
- otdf_python/kas_connect_rpc_client.py,sha256=asxKKSQokUa9kgtU3s3yhKrx3Nug9bK696YFsNjz_vE,7645
23
+ otdf_python/kas_client.py,sha256=NP8T6G5SWuciJbYnX0QNrGGlpMhqTYbcjED2tfiTr8U,25938
24
+ otdf_python/kas_connect_rpc_client.py,sha256=LRhdFF6d8qz_BDFN4WMu0Kn-ZynvKMEWmuHYPj09A6Q,7862
25
25
  otdf_python/kas_info.py,sha256=STnyPo-Vu_rzVQRdQl8mUGPK8eZE2vY4oPkI-PqrZJM,687
26
26
  otdf_python/kas_key_cache.py,sha256=Ems2NyKC_3yh_xs8k4_n2gA3Jb-9nCeRRjIdz1LRC9I,1472
27
27
  otdf_python/key_type.py,sha256=cwhlh6DOFG5C8JlCxFqXYi0SJDlOLUkxceg7L8arFNk,816
28
28
  otdf_python/key_type_constants.py,sha256=ghridYdhFMLKDa_9Q5cplrl8DqdEgApd--20bJ-MBtA,1001
29
29
  otdf_python/manifest.py,sha256=Uv-Hvo9hOPLrSPTeKQpDvNeHzq7qSmJ5HYtkgGwVppE,6419
30
- otdf_python/nanotdf.py,sha256=s7kE5HhHOq1CEZW1gbkKMwMHh6eCNqd2Ln3SZGkfRTA,20213
30
+ otdf_python/nanotdf.py,sha256=3pQ5Lip_pExhdfj2wqm58RS1Tb7A9f7dcxGIzVXSDuQ,33828
31
31
  otdf_python/nanotdf_ecdsa_struct.py,sha256=nRScIJLeNcbxBBE9DlG15I7EXGTkH0ITib1ra-C1-uQ,4100
32
32
  otdf_python/nanotdf_type.py,sha256=40PyDd62iDwcfmS7rhUeQE2B0W6FYIeCpi5cDmMO7d4,819
33
33
  otdf_python/policy_binding_serializer.py,sha256=8d9MizhLTdy14ghdAvhXRBA8KzKt6Nf9kmmU9hoWhrQ,1169
34
- otdf_python/policy_info.py,sha256=n9hgdQrTRqPO7O1R90EUtIyoWNlaABAF7mRfbjIzaX8,2861
34
+ otdf_python/policy_info.py,sha256=HGAOkxyB1I0N8_nHpnFkJXYJaycYsetqvWFNTxwwPi0,1951
35
35
  otdf_python/policy_object.py,sha256=zzwHk6jhZwpiX2BWxTJ1kDl3cgFijQfQrKJP3OqI35Q,380
36
36
  otdf_python/policy_stub.py,sha256=BQn06Ye5MwCu9feJZFPmO_UTfhugtiQa539iBkSQwhQ,117
37
- otdf_python/resource_locator.py,sha256=zYN5yd9cGwN9SRQO2tUD1UXj2zkPl2apd-X4E1MrnDg,1879
38
- otdf_python/sdk.py,sha256=u-mYUv615AMxlh3yGaMADAwqmS2uEsIDBkZ9jUzmutI,17507
39
- otdf_python/sdk_builder.py,sha256=QEu9y5I8xLPBuR8O2AG7AWInqO7g4Q5-WGW0sIVsOmQ,15803
37
+ otdf_python/resource_locator.py,sha256=vrTWtkIh82Ba4KRS6k6Pm92lQG5KazIQ18xX4VoX86Y,6050
38
+ otdf_python/sdk.py,sha256=7KKsjlUXL8_rUFVKoHFW37jvzS9Pi8Cww86NPyp3Kjw,14207
39
+ otdf_python/sdk_builder.py,sha256=jgswTxnAfKXDGqEOIQwEiE3S0Jh0w5HMtPxHwUFIu-U,14655
40
40
  otdf_python/sdk_exceptions.py,sha256=L_bYkkyF-hnEBFXfjT5C41i5lM-t8OJB5mU_1zYvfP8,479
41
41
  otdf_python/symmetric_and_payload_config.py,sha256=D_LArhk1gA5-tpUiaUZTTM90ZKdc3DCq1a6X8etCir8,992
42
- otdf_python/tdf.py,sha256=T4Wr7cc-QcQ607F4RfqWieTpQdaeOndGnLqx4Di9B_M,19648
42
+ otdf_python/tdf.py,sha256=WYfCVn7BAEBKTVEPT0SSYuCI0S6S_TgYB3C3FbHProU,19612
43
43
  otdf_python/tdf_reader.py,sha256=XQ3CuIXSOVRuBMYv2YIYlyaY-tHQA85HHHqNBoXNH9o,5267
44
44
  otdf_python/tdf_writer.py,sha256=DVEGEl8pyBm1JK2K-9BJKmT2TqvQXokPkthAQMwgCkk,645
45
45
  otdf_python/token_source.py,sha256=tfHWcIpPQ3FR45DUDiKz6q_vBaHH_HNifnnIpDVbMD8,923
@@ -131,7 +131,7 @@ otdf_python_proto/wellknownconfiguration/__init__.py,sha256=X3GeZJ1mG_N1MViryjkq
131
131
  otdf_python_proto/wellknownconfiguration/wellknown_configuration_pb2.py,sha256=g9xSm9TxX0IPMqiFCaridJvI2TrL8PrXVFPgu8tX9VM,3863
132
132
  otdf_python_proto/wellknownconfiguration/wellknown_configuration_pb2.pyi,sha256=Zw4vROvTgomnFqsalJrYda632ojXH0FVXSzTXxerybw,1490
133
133
  otdf_python_proto/wellknownconfiguration/wellknown_configuration_pb2_connect.py,sha256=i9rGG2mgQZfk6xGCp1ywu4QqKWSiwpuLoNKGUwl43t8,5346
134
- otdf_python-0.3.0.dist-info/METADATA,sha256=Yw5tEoyutj2ksFzPFnr9vdjGAUf7tuvFU4ey5bOdFgM,5822
135
- otdf_python-0.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
136
- otdf_python-0.3.0.dist-info/licenses/LICENSE,sha256=DPrPHdI6tfZcqk9kzQ37vh1Ftk7LJYdMrUtwKl7L3Pw,1074
137
- otdf_python-0.3.0.dist-info/RECORD,,
134
+ otdf_python-0.3.5.dist-info/METADATA,sha256=i1HQiMcbs71lXuIu0HCWxMo7EYlN130f95Mv0JulNsM,4225
135
+ otdf_python-0.3.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
136
+ otdf_python-0.3.5.dist-info/licenses/LICENSE,sha256=DPrPHdI6tfZcqk9kzQ37vh1Ftk7LJYdMrUtwKl7L3Pw,1074
137
+ otdf_python-0.3.5.dist-info/RECORD,,
@@ -1,53 +0,0 @@
1
- import base64
2
-
3
- from cryptography.hazmat.backends import default_backend
4
- from cryptography.hazmat.primitives import hashes, serialization
5
- from cryptography.hazmat.primitives.asymmetric import padding
6
-
7
- from .sdk_exceptions import SDKException
8
-
9
-
10
- class AsymDecryption:
11
- """
12
- Class providing functionality for asymmetric decryption using an RSA private key.
13
- """
14
-
15
- CIPHER_TRANSFORM = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding"
16
- PRIVATE_KEY_HEADER = "-----BEGIN PRIVATE KEY-----"
17
- PRIVATE_KEY_FOOTER = "-----END PRIVATE KEY-----"
18
-
19
- def __init__(self, private_key_pem: str | None = None, private_key_obj=None):
20
- if private_key_obj is not None:
21
- self.private_key = private_key_obj
22
- elif private_key_pem is not None:
23
- try:
24
- private_key_pem = (
25
- private_key_pem.replace(self.PRIVATE_KEY_HEADER, "")
26
- .replace(self.PRIVATE_KEY_FOOTER, "")
27
- .replace("\n", "")
28
- .replace("\r", "")
29
- .replace(" ", "")
30
- )
31
- decoded = base64.b64decode(private_key_pem)
32
- self.private_key = serialization.load_der_private_key(
33
- decoded, password=None, backend=default_backend()
34
- )
35
- except Exception as e:
36
- raise SDKException(f"Failed to load private key: {e}")
37
- else:
38
- self.private_key = None
39
-
40
- def decrypt(self, data: bytes) -> bytes:
41
- if self.private_key is None:
42
- raise SDKException("Failed to decrypt, private key is empty")
43
- try:
44
- return self.private_key.decrypt(
45
- data,
46
- padding.OAEP(
47
- mgf=padding.MGF1(algorithm=hashes.SHA1()),
48
- algorithm=hashes.SHA1(),
49
- label=None,
50
- ),
51
- )
52
- except Exception as e:
53
- raise SDKException(f"Error performing decryption: {e}")
@@ -1,75 +0,0 @@
1
- import base64
2
- import re
3
-
4
- from cryptography.hazmat.backends import default_backend
5
- from cryptography.hazmat.primitives import hashes, serialization
6
- from cryptography.hazmat.primitives.asymmetric import padding
7
- from cryptography.x509 import load_pem_x509_certificate
8
-
9
- from .sdk_exceptions import SDKException
10
-
11
-
12
- class AsymEncryption:
13
- """
14
- Provides methods for asymmetric encryption and handling public keys in PEM format.
15
- """
16
-
17
- PUBLIC_KEY_HEADER = "-----BEGIN PUBLIC KEY-----"
18
- PUBLIC_KEY_FOOTER = "-----END PUBLIC KEY-----"
19
- CIPHER_TRANSFORM = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding"
20
-
21
- def __init__(self, public_key_pem: str | None = None, public_key_obj=None):
22
- if public_key_obj is not None:
23
- self.public_key = public_key_obj
24
- elif public_key_pem is not None:
25
- try:
26
- if "BEGIN CERTIFICATE" in public_key_pem:
27
- cert = load_pem_x509_certificate(
28
- public_key_pem.encode(), default_backend()
29
- )
30
- self.public_key = cert.public_key()
31
- else:
32
- # Remove PEM headers/footers and whitespace
33
- pem_body = re.sub(r"-----BEGIN (.*)-----", "", public_key_pem)
34
- pem_body = re.sub(r"-----END (.*)-----", "", pem_body)
35
- pem_body = re.sub(r"\s", "", pem_body)
36
- decoded = base64.b64decode(pem_body)
37
- self.public_key = serialization.load_der_public_key(
38
- decoded, backend=default_backend()
39
- )
40
- except Exception as e:
41
- raise SDKException(f"Failed to load public key: {e}")
42
- else:
43
- self.public_key = None
44
-
45
- from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey
46
-
47
- if self.public_key is not None and not isinstance(
48
- self.public_key, RSAPublicKey
49
- ):
50
- raise SDKException("Not an RSA PEM formatted public key")
51
-
52
- def encrypt(self, data: bytes) -> bytes:
53
- if self.public_key is None:
54
- raise SDKException("Failed to encrypt, public key is empty")
55
- try:
56
- return self.public_key.encrypt(
57
- data,
58
- padding.OAEP(
59
- mgf=padding.MGF1(algorithm=hashes.SHA1()),
60
- algorithm=hashes.SHA1(),
61
- label=None,
62
- ),
63
- )
64
- except Exception as e:
65
- raise SDKException(f"Error performing encryption: {e}")
66
-
67
- def public_key_in_pem_format(self) -> str:
68
- try:
69
- pem = self.public_key.public_bytes(
70
- encoding=serialization.Encoding.PEM,
71
- format=serialization.PublicFormat.SubjectPublicKeyInfo,
72
- )
73
- return pem.decode()
74
- except Exception as e:
75
- raise SDKException(f"Error exporting public key to PEM: {e}")