otdf-python 0.3.4__py3-none-any.whl → 0.4.0__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.
- otdf_python/asym_crypto.py +135 -22
- otdf_python/cli.py +8 -1
- otdf_python/ecc_constants.py +176 -0
- otdf_python/ecc_mode.py +60 -9
- otdf_python/ecdh.py +317 -0
- otdf_python/header.py +38 -0
- otdf_python/kas_client.py +172 -66
- otdf_python/nanotdf.py +445 -135
- otdf_python/policy_info.py +5 -28
- otdf_python/resource_locator.py +149 -21
- otdf_python/sdk.py +1 -1
- otdf_python/tdf.py +4 -3
- {otdf_python-0.3.4.dist-info → otdf_python-0.4.0.dist-info}/METADATA +19 -2
- {otdf_python-0.3.4.dist-info → otdf_python-0.4.0.dist-info}/RECORD +16 -16
- otdf_python/asym_decryption.py +0 -53
- otdf_python/asym_encryption.py +0 -75
- {otdf_python-0.3.4.dist-info → otdf_python-0.4.0.dist-info}/WHEEL +0 -0
- {otdf_python-0.3.4.dist-info → otdf_python-0.4.0.dist-info}/licenses/LICENSE +0 -0
otdf_python/policy_info.py
CHANGED
|
@@ -2,14 +2,10 @@ class PolicyInfo:
|
|
|
2
2
|
def __init__(
|
|
3
3
|
self,
|
|
4
4
|
policy_type: int = 0,
|
|
5
|
-
has_ecdsa_binding: bool = False,
|
|
6
5
|
body: bytes | None = None,
|
|
7
|
-
binding: bytes | None = None,
|
|
8
6
|
):
|
|
9
7
|
self.policy_type = policy_type
|
|
10
|
-
self.has_ecdsa_binding = has_ecdsa_binding
|
|
11
8
|
self.body = body
|
|
12
|
-
self.binding = binding
|
|
13
9
|
|
|
14
10
|
def set_embedded_plain_text_policy(self, body: bytes):
|
|
15
11
|
self.body = body
|
|
@@ -19,21 +15,13 @@ class PolicyInfo:
|
|
|
19
15
|
self.body = body
|
|
20
16
|
self.policy_type = 2 # Placeholder for EMBEDDED_POLICY_ENCRYPTED
|
|
21
17
|
|
|
22
|
-
def set_policy_binding(self, binding: bytes):
|
|
23
|
-
self.binding = binding
|
|
24
|
-
|
|
25
18
|
def get_body(self) -> bytes | None:
|
|
26
19
|
return self.body
|
|
27
20
|
|
|
28
|
-
def get_binding(self) -> bytes | None:
|
|
29
|
-
return self.binding
|
|
30
|
-
|
|
31
21
|
def get_total_size(self) -> int:
|
|
32
22
|
size = 1 # policy_type
|
|
33
23
|
size += 2 # body_len
|
|
34
24
|
size += len(self.body) if self.body else 0
|
|
35
|
-
size += 1 # binding_len
|
|
36
|
-
size += len(self.binding) if self.binding else 0
|
|
37
25
|
return size
|
|
38
26
|
|
|
39
27
|
def write_into_buffer(self, buffer: bytearray, offset: int = 0) -> int:
|
|
@@ -46,33 +34,22 @@ class PolicyInfo:
|
|
|
46
34
|
if self.body:
|
|
47
35
|
buffer[offset : offset + body_len] = self.body
|
|
48
36
|
offset += body_len
|
|
49
|
-
binding_len = len(self.binding) if self.binding else 0
|
|
50
|
-
buffer[offset] = binding_len
|
|
51
|
-
offset += 1
|
|
52
|
-
if self.binding:
|
|
53
|
-
buffer[offset : offset + binding_len] = self.binding
|
|
54
|
-
offset += binding_len
|
|
55
37
|
return offset - start
|
|
56
38
|
|
|
57
39
|
@staticmethod
|
|
58
40
|
def from_bytes_with_size(buffer: bytes, ecc_mode):
|
|
59
|
-
#
|
|
41
|
+
# Parse policy_type (1 byte), body_len (2 bytes), body
|
|
42
|
+
# Note: binding is NOT part of PolicyInfo - it's read separately in Header
|
|
60
43
|
offset = 0
|
|
61
|
-
if len(buffer) <
|
|
44
|
+
if len(buffer) < 3:
|
|
62
45
|
raise ValueError("Buffer too short for PolicyInfo header")
|
|
63
46
|
policy_type = buffer[offset]
|
|
64
47
|
offset += 1
|
|
65
48
|
body_len = int.from_bytes(buffer[offset : offset + 2], "big")
|
|
66
49
|
offset += 2
|
|
67
|
-
if len(buffer) < offset + body_len
|
|
50
|
+
if len(buffer) < offset + body_len:
|
|
68
51
|
raise ValueError("Buffer too short for PolicyInfo body")
|
|
69
52
|
body = buffer[offset : offset + body_len]
|
|
70
53
|
offset += body_len
|
|
71
|
-
|
|
72
|
-
offset += 1
|
|
73
|
-
if len(buffer) < offset + binding_len:
|
|
74
|
-
raise ValueError("Buffer too short for PolicyInfo binding")
|
|
75
|
-
binding = buffer[offset : offset + binding_len]
|
|
76
|
-
offset += binding_len
|
|
77
|
-
pi = PolicyInfo(policy_type=policy_type, body=body, binding=binding)
|
|
54
|
+
pi = PolicyInfo(policy_type=policy_type, body=body)
|
|
78
55
|
return pi, offset
|
otdf_python/resource_locator.py
CHANGED
|
@@ -1,7 +1,31 @@
|
|
|
1
1
|
class ResourceLocator:
|
|
2
|
+
"""
|
|
3
|
+
NanoTDF Resource Locator per the spec:
|
|
4
|
+
https://github.com/opentdf/spec/blob/main/schema/nanotdf/README.md
|
|
5
|
+
|
|
6
|
+
Format:
|
|
7
|
+
- Byte 0: Protocol Enum (bits 0-3) + Identifier Length (bits 4-7)
|
|
8
|
+
- Protocol: 0x0=HTTP, 0x1=HTTPS, 0xF=Shared Resource Directory
|
|
9
|
+
- Identifier: 0x0=None, 0x1=2 bytes, 0x2=8 bytes, 0x3=32 bytes
|
|
10
|
+
- Byte 1: Body Length (1-255 bytes)
|
|
11
|
+
- Bytes 2-N: Body (URL path)
|
|
12
|
+
- Bytes N+1-M: Identifier (optional, 0/2/8/32 bytes)
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
# Protocol enum values
|
|
16
|
+
PROTOCOL_HTTP = 0x0
|
|
17
|
+
PROTOCOL_HTTPS = 0x1
|
|
18
|
+
PROTOCOL_SHARED_RESOURCE_DIR = 0xF
|
|
19
|
+
|
|
20
|
+
# Identifier length enum values (in bits 4-7)
|
|
21
|
+
IDENTIFIER_NONE = 0x0
|
|
22
|
+
IDENTIFIER_2_BYTES = 0x1
|
|
23
|
+
IDENTIFIER_8_BYTES = 0x2
|
|
24
|
+
IDENTIFIER_32_BYTES = 0x3
|
|
25
|
+
|
|
2
26
|
def __init__(self, resource_url: str | None = None, identifier: str | None = None):
|
|
3
|
-
self.resource_url = resource_url
|
|
4
|
-
self.identifier = identifier
|
|
27
|
+
self.resource_url = resource_url or ""
|
|
28
|
+
self.identifier = identifier or ""
|
|
5
29
|
|
|
6
30
|
def get_resource_url(self):
|
|
7
31
|
return self.resource_url
|
|
@@ -9,13 +33,68 @@ class ResourceLocator:
|
|
|
9
33
|
def get_identifier(self):
|
|
10
34
|
return self.identifier
|
|
11
35
|
|
|
36
|
+
def _parse_url(self):
|
|
37
|
+
"""Parse URL to extract protocol and body (path)."""
|
|
38
|
+
url = self.resource_url
|
|
39
|
+
if url.startswith("https://"):
|
|
40
|
+
protocol = self.PROTOCOL_HTTPS
|
|
41
|
+
body = url[8:] # Remove "https://"
|
|
42
|
+
elif url.startswith("http://"):
|
|
43
|
+
protocol = self.PROTOCOL_HTTP
|
|
44
|
+
body = url[7:] # Remove "http://"
|
|
45
|
+
else:
|
|
46
|
+
# Default to HTTP
|
|
47
|
+
protocol = self.PROTOCOL_HTTP
|
|
48
|
+
body = url
|
|
49
|
+
return protocol, body.encode()
|
|
50
|
+
|
|
51
|
+
def _get_identifier_bytes(self):
|
|
52
|
+
"""Get identifier bytes and determine identifier length enum."""
|
|
53
|
+
if not self.identifier:
|
|
54
|
+
return self.IDENTIFIER_NONE, b""
|
|
55
|
+
|
|
56
|
+
id_bytes = self.identifier.encode()
|
|
57
|
+
id_len = len(id_bytes)
|
|
58
|
+
|
|
59
|
+
if id_len == 0:
|
|
60
|
+
return self.IDENTIFIER_NONE, b""
|
|
61
|
+
elif id_len <= 2:
|
|
62
|
+
# Pad to 2 bytes
|
|
63
|
+
return self.IDENTIFIER_2_BYTES, id_bytes.ljust(2, b"\x00")
|
|
64
|
+
elif id_len <= 8:
|
|
65
|
+
# Pad to 8 bytes
|
|
66
|
+
return self.IDENTIFIER_8_BYTES, id_bytes.ljust(8, b"\x00")
|
|
67
|
+
elif id_len <= 32:
|
|
68
|
+
# Pad to 32 bytes
|
|
69
|
+
return self.IDENTIFIER_32_BYTES, id_bytes.ljust(32, b"\x00")
|
|
70
|
+
else:
|
|
71
|
+
raise ValueError(f"Identifier too long: {id_len} bytes (max 32)")
|
|
72
|
+
|
|
12
73
|
def to_bytes(self):
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
74
|
+
"""
|
|
75
|
+
Convert to NanoTDF Resource Locator format per spec.
|
|
76
|
+
|
|
77
|
+
Format:
|
|
78
|
+
- Byte 0: Protocol Enum (bits 0-3) + Identifier Length (bits 4-7)
|
|
79
|
+
- Byte 1: Body Length
|
|
80
|
+
- Bytes 2-N: Body (URL path)
|
|
81
|
+
- Bytes N+1-M: Identifier (0/2/8/32 bytes)
|
|
82
|
+
"""
|
|
83
|
+
protocol, body_bytes = self._parse_url()
|
|
84
|
+
identifier_enum, identifier_bytes = self._get_identifier_bytes()
|
|
85
|
+
|
|
86
|
+
if len(body_bytes) > 255:
|
|
87
|
+
raise ValueError(
|
|
88
|
+
f"Resource Locator body too long: {len(body_bytes)} bytes (max 255)"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# Byte 0: protocol in bits 0-3, identifier length in bits 4-7
|
|
92
|
+
protocol_and_id = (identifier_enum << 4) | protocol
|
|
93
|
+
|
|
94
|
+
# Byte 1: body length
|
|
95
|
+
body_len = len(body_bytes)
|
|
96
|
+
|
|
97
|
+
return bytes([protocol_and_id, body_len]) + body_bytes + identifier_bytes
|
|
19
98
|
|
|
20
99
|
def get_total_size(self) -> int:
|
|
21
100
|
return len(self.to_bytes())
|
|
@@ -26,19 +105,68 @@ class ResourceLocator:
|
|
|
26
105
|
return len(data)
|
|
27
106
|
|
|
28
107
|
@staticmethod
|
|
29
|
-
def from_bytes_with_size(buffer: bytes):
|
|
30
|
-
|
|
108
|
+
def from_bytes_with_size(buffer: bytes): # noqa: C901
|
|
109
|
+
"""
|
|
110
|
+
Parse NanoTDF Resource Locator from bytes per spec.
|
|
111
|
+
|
|
112
|
+
Format:
|
|
113
|
+
- Byte 0: Protocol Enum (bits 0-3) + Identifier Length (bits 4-7)
|
|
114
|
+
- Byte 1: Body Length
|
|
115
|
+
- Bytes 2-N: Body (URL path)
|
|
116
|
+
- Bytes N+1-M: Identifier (0/2/8/32 bytes)
|
|
117
|
+
"""
|
|
31
118
|
if len(buffer) < 2:
|
|
32
119
|
raise ValueError("Buffer too short for ResourceLocator")
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
120
|
+
|
|
121
|
+
# Parse byte 0: protocol and identifier length
|
|
122
|
+
protocol_and_id = buffer[0]
|
|
123
|
+
protocol = protocol_and_id & 0x0F # Bits 0-3
|
|
124
|
+
identifier_enum = (protocol_and_id >> 4) & 0x0F # Bits 4-7
|
|
125
|
+
|
|
126
|
+
# Parse byte 1: body length
|
|
127
|
+
body_len = buffer[1]
|
|
128
|
+
|
|
129
|
+
if len(buffer) < 2 + body_len:
|
|
130
|
+
raise ValueError(
|
|
131
|
+
f"Buffer too short for ResourceLocator body (need {2 + body_len}, have {len(buffer)})"
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
# Parse body (URL path)
|
|
135
|
+
body_bytes = buffer[2 : 2 + body_len]
|
|
136
|
+
body = body_bytes.decode()
|
|
137
|
+
|
|
138
|
+
# Reconstruct full URL with protocol
|
|
139
|
+
if protocol == ResourceLocator.PROTOCOL_HTTPS:
|
|
140
|
+
resource_url = f"https://{body}"
|
|
141
|
+
elif protocol == ResourceLocator.PROTOCOL_HTTP:
|
|
142
|
+
resource_url = f"http://{body}"
|
|
143
|
+
else:
|
|
144
|
+
resource_url = body
|
|
145
|
+
|
|
146
|
+
# Parse identifier based on identifier_enum
|
|
147
|
+
offset = 2 + body_len
|
|
148
|
+
if identifier_enum == ResourceLocator.IDENTIFIER_NONE:
|
|
149
|
+
identifier_len = 0
|
|
150
|
+
elif identifier_enum == ResourceLocator.IDENTIFIER_2_BYTES:
|
|
151
|
+
identifier_len = 2
|
|
152
|
+
elif identifier_enum == ResourceLocator.IDENTIFIER_8_BYTES:
|
|
153
|
+
identifier_len = 8
|
|
154
|
+
elif identifier_enum == ResourceLocator.IDENTIFIER_32_BYTES:
|
|
155
|
+
identifier_len = 32
|
|
156
|
+
else:
|
|
157
|
+
raise ValueError(f"Invalid identifier length enum: {identifier_enum}")
|
|
158
|
+
|
|
159
|
+
if len(buffer) < offset + identifier_len:
|
|
160
|
+
raise ValueError(
|
|
161
|
+
f"Buffer too short for ResourceLocator identifier (need {offset + identifier_len}, have {len(buffer)})"
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
if identifier_len > 0:
|
|
165
|
+
identifier_bytes = buffer[offset : offset + identifier_len]
|
|
166
|
+
# Remove padding
|
|
167
|
+
identifier = identifier_bytes.rstrip(b"\x00").decode()
|
|
168
|
+
else:
|
|
169
|
+
identifier = ""
|
|
170
|
+
|
|
171
|
+
size = 2 + body_len + identifier_len
|
|
44
172
|
return ResourceLocator(resource_url, identifier), size
|
otdf_python/sdk.py
CHANGED
|
@@ -119,7 +119,7 @@ class KAS(AbstractContextManager):
|
|
|
119
119
|
Unwrapped key as bytes
|
|
120
120
|
"""
|
|
121
121
|
if mock and wrapped_key and kas_private_key:
|
|
122
|
-
from .
|
|
122
|
+
from .asym_crypto import AsymDecryption
|
|
123
123
|
|
|
124
124
|
asym = AsymDecryption(private_key_pem=kas_private_key)
|
|
125
125
|
return asym.decrypt(wrapped_key)
|
otdf_python/tdf.py
CHANGED
|
@@ -87,7 +87,7 @@ class TDF:
|
|
|
87
87
|
import hashlib
|
|
88
88
|
import hmac
|
|
89
89
|
|
|
90
|
-
from
|
|
90
|
+
from .asym_crypto import AsymEncryption
|
|
91
91
|
|
|
92
92
|
key_access_objs = []
|
|
93
93
|
for kas in kas_infos:
|
|
@@ -188,7 +188,7 @@ class TDF:
|
|
|
188
188
|
"""
|
|
189
189
|
Unwraps the key locally using a provided private key (used for testing)
|
|
190
190
|
"""
|
|
191
|
-
from
|
|
191
|
+
from .asym_crypto import AsymDecryption
|
|
192
192
|
|
|
193
193
|
key = None
|
|
194
194
|
for ka in key_access_objs:
|
|
@@ -430,7 +430,8 @@ class TDF:
|
|
|
430
430
|
import zipfile
|
|
431
431
|
|
|
432
432
|
from otdf_python.aesgcm import AesGcm
|
|
433
|
-
|
|
433
|
+
|
|
434
|
+
from .asym_crypto import AsymDecryption
|
|
434
435
|
|
|
435
436
|
with zipfile.ZipFile(io.BytesIO(tdf_bytes), "r") as z:
|
|
436
437
|
manifest_json = z.read("0.manifest.json").decode()
|
|
@@ -1,11 +1,28 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: otdf-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Unofficial OpenTDF SDK for Python
|
|
5
|
+
Project-URL: Homepage, https://github.com/b-long/opentdf-python-sdk
|
|
6
|
+
Project-URL: Repository, https://github.com/b-long/opentdf-python-sdk
|
|
7
|
+
Project-URL: Issues, https://github.com/b-long/opentdf-python-sdk/issues
|
|
5
8
|
Author-email: b-long <b-long@users.noreply.github.com>
|
|
6
9
|
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Topic :: Security :: Cryptography
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Classifier: Typing :: Typed
|
|
7
24
|
Requires-Python: >=3.10
|
|
8
|
-
Requires-Dist: connect-python[compiler]
|
|
25
|
+
Requires-Dist: connect-python[compiler]<0.5,>=0.4.2
|
|
9
26
|
Requires-Dist: cryptography>=45.0.4
|
|
10
27
|
Requires-Dist: grpcio-status>=1.74.0
|
|
11
28
|
Requires-Dist: grpcio-tools>=1.74.0
|
|
@@ -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=
|
|
7
|
-
otdf_python/asym_decryption.py,sha256=eVfgfzFHMK4ni3g-u8fBe3sdF02eo_msZouo19UBlh4,1981
|
|
8
|
-
otdf_python/asym_encryption.py,sha256=ex-S_PvBnWKSSvbocTjJ_p5VQjjeiThcc9bMRZqvifw,2932
|
|
6
|
+
otdf_python/asym_crypto.py,sha256=dwx3lUxUt3e8JA9z2lQrpsOBA3x36DN07bQbcVGZBvI,7251
|
|
9
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=
|
|
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/
|
|
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=
|
|
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=
|
|
23
|
+
otdf_python/kas_client.py,sha256=NP8T6G5SWuciJbYnX0QNrGGlpMhqTYbcjED2tfiTr8U,25938
|
|
24
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=
|
|
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=
|
|
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=
|
|
38
|
-
otdf_python/sdk.py,sha256=
|
|
37
|
+
otdf_python/resource_locator.py,sha256=vrTWtkIh82Ba4KRS6k6Pm92lQG5KazIQ18xX4VoX86Y,6050
|
|
38
|
+
otdf_python/sdk.py,sha256=7KKsjlUXL8_rUFVKoHFW37jvzS9Pi8Cww86NPyp3Kjw,14207
|
|
39
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=
|
|
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.
|
|
135
|
-
otdf_python-0.
|
|
136
|
-
otdf_python-0.
|
|
137
|
-
otdf_python-0.
|
|
134
|
+
otdf_python-0.4.0.dist-info/METADATA,sha256=Hpm2WiRbzRfr38wswo45o8X-p-R2Nlt8AAap09WxKiQ,5132
|
|
135
|
+
otdf_python-0.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
136
|
+
otdf_python-0.4.0.dist-info/licenses/LICENSE,sha256=DPrPHdI6tfZcqk9kzQ37vh1Ftk7LJYdMrUtwKl7L3Pw,1074
|
|
137
|
+
otdf_python-0.4.0.dist-info/RECORD,,
|
otdf_python/asym_decryption.py
DELETED
|
@@ -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}")
|
otdf_python/asym_encryption.py
DELETED
|
@@ -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}")
|
|
File without changes
|
|
File without changes
|