otdf-python 0.11.0__tar.gz → 0.12.0__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.
Files changed (177) hide show
  1. {otdf_python-0.11.0 → otdf_python-0.12.0}/PKG-INFO +9 -3
  2. {otdf_python-0.11.0 → otdf_python-0.12.0}/README.md +6 -0
  3. {otdf_python-0.11.0 → otdf_python-0.12.0}/hatch_build.py +1 -1
  4. {otdf_python-0.11.0 → otdf_python-0.12.0}/pyproject.toml +3 -3
  5. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/aesgcm.py +9 -2
  6. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/cli.py +5 -1
  7. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/collection_store.py +1 -1
  8. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/kas_client.py +1 -1
  9. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/manifest.py +18 -1
  10. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/nanotdf.py +2 -2
  11. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/policy_binding_serializer.py +2 -2
  12. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/policy_info.py +1 -1
  13. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/sdk.py +16 -7
  14. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/sdk_builder.py +3 -1
  15. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/tdf.py +10 -5
  16. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/tdf_reader.py +68 -11
  17. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/tdf_writer.py +3 -1
  18. {otdf_python-0.11.0 → otdf_python-0.12.0}/.gitignore +0 -0
  19. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/__init__.py +0 -0
  20. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/authorization/__init__.py +0 -0
  21. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/authorization/authorization_connect.py +0 -0
  22. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/authorization/authorization_pb2.py +0 -0
  23. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/authorization/authorization_pb2.pyi +0 -0
  24. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/authorization/v2/__init__.py +0 -0
  25. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/authorization/v2/authorization_connect.py +0 -0
  26. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/authorization/v2/authorization_pb2.py +0 -0
  27. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/authorization/v2/authorization_pb2.pyi +0 -0
  28. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/common/__init__.py +0 -0
  29. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/common/common_pb2.py +0 -0
  30. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/common/common_pb2.pyi +0 -0
  31. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/entity/__init__.py +0 -0
  32. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/entity/entity_pb2.py +0 -0
  33. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/entity/entity_pb2.pyi +0 -0
  34. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/entityresolution/__init__.py +0 -0
  35. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/entityresolution/entity_resolution_connect.py +0 -0
  36. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/entityresolution/entity_resolution_pb2.py +0 -0
  37. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/entityresolution/entity_resolution_pb2.pyi +0 -0
  38. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/entityresolution/v2/__init__.py +0 -0
  39. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/entityresolution/v2/entity_resolution_connect.py +0 -0
  40. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/entityresolution/v2/entity_resolution_pb2.py +0 -0
  41. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/entityresolution/v2/entity_resolution_pb2.pyi +0 -0
  42. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/kas/__init__.py +0 -0
  43. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/kas/kas_connect.py +0 -0
  44. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/kas/kas_pb2.py +0 -0
  45. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/kas/kas_pb2.pyi +0 -0
  46. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/__init__.py +0 -0
  47. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/authorization/__init__.py +0 -0
  48. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/authorization/authorization_pb2_grpc.py +0 -0
  49. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/authorization/v2/__init__.py +0 -0
  50. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/authorization/v2/authorization_pb2_grpc.py +0 -0
  51. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/common/__init__.py +0 -0
  52. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/common/common_pb2_grpc.py +0 -0
  53. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/entity/__init__.py +0 -0
  54. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/entity/entity_pb2_grpc.py +0 -0
  55. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/entityresolution/__init__.py +0 -0
  56. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/entityresolution/entity_resolution_pb2_grpc.py +0 -0
  57. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/entityresolution/v2/__init__.py +0 -0
  58. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/entityresolution/v2/entity_resolution_pb2_grpc.py +0 -0
  59. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/kas/__init__.py +0 -0
  60. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/kas/kas_pb2_grpc.py +0 -0
  61. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/logger/__init__.py +0 -0
  62. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/logger/audit/__init__.py +0 -0
  63. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/logger/audit/test_pb2_grpc.py +0 -0
  64. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/policy/__init__.py +0 -0
  65. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/policy/actions/__init__.py +0 -0
  66. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/policy/actions/actions_pb2_grpc.py +0 -0
  67. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/policy/attributes/__init__.py +0 -0
  68. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/policy/attributes/attributes_pb2_grpc.py +0 -0
  69. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/policy/kasregistry/__init__.py +0 -0
  70. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/policy/kasregistry/key_access_server_registry_pb2_grpc.py +0 -0
  71. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/policy/keymanagement/__init__.py +0 -0
  72. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/policy/keymanagement/key_management_pb2_grpc.py +0 -0
  73. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/policy/namespaces/__init__.py +0 -0
  74. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/policy/namespaces/namespaces_pb2_grpc.py +0 -0
  75. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/policy/objects_pb2_grpc.py +0 -0
  76. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/policy/obligations/__init__.py +0 -0
  77. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/policy/obligations/obligations_pb2_grpc.py +0 -0
  78. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/policy/registeredresources/__init__.py +0 -0
  79. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/policy/registeredresources/registered_resources_pb2_grpc.py +0 -0
  80. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/policy/resourcemapping/__init__.py +0 -0
  81. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/policy/resourcemapping/resource_mapping_pb2_grpc.py +0 -0
  82. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/policy/selectors_pb2_grpc.py +0 -0
  83. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/policy/subjectmapping/__init__.py +0 -0
  84. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/policy/subjectmapping/subject_mapping_pb2_grpc.py +0 -0
  85. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/policy/unsafe/__init__.py +0 -0
  86. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/policy/unsafe/unsafe_pb2_grpc.py +0 -0
  87. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/wellknownconfiguration/__init__.py +0 -0
  88. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/legacy_grpc/wellknownconfiguration/wellknown_configuration_pb2_grpc.py +0 -0
  89. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/logger/__init__.py +0 -0
  90. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/logger/audit/__init__.py +0 -0
  91. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/logger/audit/test_pb2.py +0 -0
  92. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/logger/audit/test_pb2.pyi +0 -0
  93. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/__init__.py +0 -0
  94. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/actions/__init__.py +0 -0
  95. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/actions/actions_connect.py +0 -0
  96. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/actions/actions_pb2.py +0 -0
  97. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/actions/actions_pb2.pyi +0 -0
  98. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/attributes/__init__.py +0 -0
  99. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/attributes/attributes_connect.py +0 -0
  100. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/attributes/attributes_pb2.py +0 -0
  101. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/attributes/attributes_pb2.pyi +0 -0
  102. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/kasregistry/__init__.py +0 -0
  103. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/kasregistry/key_access_server_registry_connect.py +0 -0
  104. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/kasregistry/key_access_server_registry_pb2.py +0 -0
  105. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/kasregistry/key_access_server_registry_pb2.pyi +0 -0
  106. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/keymanagement/__init__.py +0 -0
  107. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/keymanagement/key_management_connect.py +0 -0
  108. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/keymanagement/key_management_pb2.py +0 -0
  109. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/keymanagement/key_management_pb2.pyi +0 -0
  110. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/namespaces/__init__.py +0 -0
  111. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/namespaces/namespaces_connect.py +0 -0
  112. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/namespaces/namespaces_pb2.py +0 -0
  113. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/namespaces/namespaces_pb2.pyi +0 -0
  114. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/objects_pb2.py +0 -0
  115. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/objects_pb2.pyi +0 -0
  116. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/obligations/__init__.py +0 -0
  117. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/obligations/obligations_connect.py +0 -0
  118. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/obligations/obligations_pb2.py +0 -0
  119. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/obligations/obligations_pb2.pyi +0 -0
  120. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/registeredresources/__init__.py +0 -0
  121. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/registeredresources/registered_resources_connect.py +0 -0
  122. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/registeredresources/registered_resources_pb2.py +0 -0
  123. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/registeredresources/registered_resources_pb2.pyi +0 -0
  124. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/resourcemapping/__init__.py +0 -0
  125. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/resourcemapping/resource_mapping_connect.py +0 -0
  126. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/resourcemapping/resource_mapping_pb2.py +0 -0
  127. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/resourcemapping/resource_mapping_pb2.pyi +0 -0
  128. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/selectors_pb2.py +0 -0
  129. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/selectors_pb2.pyi +0 -0
  130. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/subjectmapping/__init__.py +0 -0
  131. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/subjectmapping/subject_mapping_connect.py +0 -0
  132. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/subjectmapping/subject_mapping_pb2.py +0 -0
  133. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/subjectmapping/subject_mapping_pb2.pyi +0 -0
  134. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/unsafe/__init__.py +0 -0
  135. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/unsafe/unsafe_connect.py +0 -0
  136. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/unsafe/unsafe_pb2.py +0 -0
  137. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/policy/unsafe/unsafe_pb2.pyi +0 -0
  138. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/wellknownconfiguration/__init__.py +0 -0
  139. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/wellknownconfiguration/wellknown_configuration_connect.py +0 -0
  140. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/wellknownconfiguration/wellknown_configuration_pb2.py +0 -0
  141. {otdf_python-0.11.0 → otdf_python-0.12.0}/_proto/otdf_python_proto/wellknownconfiguration/wellknown_configuration_pb2.pyi +0 -0
  142. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/__init__.py +0 -0
  143. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/__main__.py +0 -0
  144. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/address_normalizer.py +0 -0
  145. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/assertion_config.py +0 -0
  146. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/asym_crypto.py +0 -0
  147. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/auth_headers.py +0 -0
  148. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/autoconfigure_utils.py +0 -0
  149. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/collection_store_impl.py +0 -0
  150. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/config.py +0 -0
  151. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/connect_client.py +0 -0
  152. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/constants.py +0 -0
  153. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/crypto_utils.py +0 -0
  154. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/dpop.py +0 -0
  155. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/ecc_constants.py +0 -0
  156. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/ecc_mode.py +0 -0
  157. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/ecdh.py +0 -0
  158. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/eckeypair.py +0 -0
  159. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/header.py +0 -0
  160. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/invalid_zip_exception.py +0 -0
  161. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/kas_allowlist.py +0 -0
  162. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/kas_connect_rpc_client.py +0 -0
  163. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/kas_info.py +0 -0
  164. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/kas_key_cache.py +0 -0
  165. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/key_type.py +0 -0
  166. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/key_type_constants.py +0 -0
  167. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/nanotdf_ecdsa_struct.py +0 -0
  168. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/nanotdf_type.py +0 -0
  169. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/policy_object.py +0 -0
  170. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/policy_stub.py +0 -0
  171. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/resource_locator.py +0 -0
  172. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/sdk_exceptions.py +0 -0
  173. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/symmetric_and_payload_config.py +0 -0
  174. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/token_source.py +0 -0
  175. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/version.py +0 -0
  176. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/zip_reader.py +0 -0
  177. {otdf_python-0.11.0 → otdf_python-0.12.0}/src/otdf_python/zip_writer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: otdf-python
3
- Version: 0.11.0
3
+ Version: 0.12.0
4
4
  Summary: Unofficial OpenTDF SDK for Python
5
5
  Project-URL: Homepage, https://github.com/b-long/opentdf-python-sdk
6
6
  Project-URL: Repository, https://github.com/b-long/opentdf-python-sdk
@@ -25,9 +25,9 @@ Requires-Dist: connect-python>=0.9.0
25
25
  Requires-Dist: cryptography>=50.0.0
26
26
  Requires-Dist: grpcio-status>=1.74.0
27
27
  Requires-Dist: grpcio>=1.74.0
28
- Requires-Dist: httpx2>=2.3.0
28
+ Requires-Dist: httpx2>=2.12.0
29
29
  Requires-Dist: protobuf>=6.31.1
30
- Requires-Dist: pyjwt>=2.10.1
30
+ Requires-Dist: pyjwt>=2.15.0
31
31
  Requires-Dist: typing-extensions>=4.14.1
32
32
  Description-Content-Type: text/markdown
33
33
 
@@ -130,6 +130,12 @@ with open("decrypted.txt", "wb") as f:
130
130
 
131
131
  ```
132
132
 
133
+ ## TDF container format
134
+
135
+ TDF archives are written with the spec-mandated `manifest.json` zip entry (per opentdf/spec) instead of the legacy `0.manifest.json`. Readers accept both names, so archives written by older versions of this SDK remain readable forever. The payload zip entry is resolved from `manifest.payload.url` rather than assumed. `schemaVersion` is unchanged (still `4.3.0`); `tdf_spec_version` is now read at either its legacy or spec-conformant placement, but never written.
136
+
137
+ **Interoperability note:** archives written by this version cannot currently be opened by released `otdfctl`, or by the upstream `opentdf/platform` Go, Java, or JS SDKs, until they add a `manifest.json` read fallback (an upstream reader-fallback PR is planned). If you interoperate with those tools today, hold off upgrading until that fallback ships.
138
+
133
139
  ## Project Structure
134
140
 
135
141
  ```
@@ -97,6 +97,12 @@ with open("decrypted.txt", "wb") as f:
97
97
 
98
98
  ```
99
99
 
100
+ ## TDF container format
101
+
102
+ TDF archives are written with the spec-mandated `manifest.json` zip entry (per opentdf/spec) instead of the legacy `0.manifest.json`. Readers accept both names, so archives written by older versions of this SDK remain readable forever. The payload zip entry is resolved from `manifest.payload.url` rather than assumed. `schemaVersion` is unchanged (still `4.3.0`); `tdf_spec_version` is now read at either its legacy or spec-conformant placement, but never written.
103
+
104
+ **Interoperability note:** archives written by this version cannot currently be opened by released `otdfctl`, or by the upstream `opentdf/platform` Go, Java, or JS SDKs, until they add a `manifest.json` read fallback (an upstream reader-fallback PR is planned). If you interoperate with those tools today, hold off upgrading until that fallback ships.
105
+
100
106
  ## Project Structure
101
107
 
102
108
  ```
@@ -8,7 +8,7 @@ from hatchling.builders.hooks.plugin.interface import BuildHookInterface
8
8
  class CustomBuildHook(BuildHookInterface):
9
9
  """Dynamically resolves proto sources for wheel builds from source tree or sdist."""
10
10
 
11
- def initialize(self, version, build_data):
11
+ def initialize(self, _version, build_data):
12
12
  """Set force_include for otdf_python_proto based on build context."""
13
13
  if self.target_name != "wheel":
14
14
  return
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "otdf-python"
3
- version = "0.11.0"
3
+ version = "0.12.0"
4
4
  description = "Unofficial OpenTDF SDK for Python"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -27,9 +27,9 @@ classifiers = [
27
27
  dependencies = [
28
28
  "cryptography>=50.0.0",
29
29
  "connect-python>=0.9.0",
30
- "httpx2>=2.3.0",
30
+ "httpx2>=2.12.0",
31
31
  "protobuf>=6.31.1",
32
- "pyjwt>=2.10.1",
32
+ "pyjwt>=2.15.0",
33
33
  "typing-extensions>=4.14.1",
34
34
  # Legacy gRPC support (may be removed in future versions)
35
35
  "grpcio>=1.74.0",
@@ -44,7 +44,9 @@ class AesGcm:
44
44
  def encrypt_with_iv(
45
45
  self,
46
46
  iv: bytes,
47
- auth_tag_len: int,
47
+ # Kept for signature parity with the Java SDK; the cryptography lib
48
+ # derives the tag length itself.
49
+ auth_tag_len: int, # noqa: ARG002
48
50
  plaintext: bytes,
49
51
  offset: int = 0,
50
52
  length: int | None = None,
@@ -58,6 +60,11 @@ class AesGcm:
58
60
  return self.aesgcm.decrypt(encrypted.iv, encrypted.ciphertext, None)
59
61
 
60
62
  def decrypt_with_iv(
61
- self, iv: bytes, auth_tag_len: int, cipher_data: bytes
63
+ self,
64
+ iv: bytes,
65
+ # Kept for signature parity with the Java SDK; the cryptography lib
66
+ # derives the tag length itself.
67
+ auth_tag_len: int, # noqa: ARG002
68
+ cipher_data: bytes,
62
69
  ) -> bytes:
63
70
  return self.aesgcm.decrypt(iv, cipher_data, None)
@@ -205,12 +205,15 @@ def build_sdk(args) -> SDK:
205
205
 
206
206
  # Features the Python SDK currently exercises honestly in community xtest Stage-1.
207
207
  # Keep conservative: only advertise what encrypt/decrypt paths actually honor.
208
+ # "spec-container": writes manifest.json at the zip root and resolves the
209
+ # payload entry from manifest.payload.url (opentdf/spec container rules).
208
210
  _SUPPORTED_FEATURES: frozenset[str] = frozenset(
209
211
  {
210
212
  "autoconfigure",
211
213
  "connectrpc",
212
214
  "hexless",
213
215
  "kasallowlist",
216
+ "spec-container",
214
217
  }
215
218
  )
216
219
 
@@ -238,6 +241,7 @@ _KNOWN_FEATURES: frozenset[str] = frozenset(
238
241
  "mechanism-mlkem",
239
242
  "ns_grants",
240
243
  "obligations",
244
+ "spec-container",
241
245
  }
242
246
  )
243
247
 
@@ -286,7 +290,7 @@ def create_tdf_config(sdk: SDK, args) -> TDFConfig:
286
290
  return config
287
291
 
288
292
 
289
- def create_nano_tdf_config(sdk: SDK, args) -> NanoTDFConfig:
293
+ def create_nano_tdf_config(_sdk: SDK, args) -> NanoTDFConfig:
290
294
  """Create NanoTDF configuration from CLI arguments."""
291
295
  attributes = (
292
296
  parse_attributes(args.attributes)
@@ -29,7 +29,7 @@ class NoOpCollectionStore(CollectionStore):
29
29
  def store(self, header, key: CollectionKey):
30
30
  """Discard key operation (no-op)."""
31
31
 
32
- def get_key(self, header) -> CollectionKey:
32
+ def get_key(self, _header) -> CollectionKey:
33
33
  return self.NO_PRIVATE_KEY
34
34
 
35
35
 
@@ -553,7 +553,7 @@ class KASClient:
553
553
  return RSA_KEY_TYPE
554
554
  return session_key_type
555
555
 
556
- def _prepare_ec_keypair(self, session_key_type):
556
+ def _prepare_ec_keypair(self, _session_key_type):
557
557
  """Prepare EC key pair for unwrapping.
558
558
 
559
559
  Args:
@@ -86,6 +86,8 @@ class ManifestPayload:
86
86
  protocol: str
87
87
  mimeType: str
88
88
  isEncrypted: bool
89
+ # Read-only: some spec revisions place the version here. Never written.
90
+ tdf_spec_version: str | None = None
89
91
 
90
92
 
91
93
  @dataclass
@@ -116,6 +118,18 @@ class Manifest:
116
118
  encryptionInformation: ManifestEncryptionInformation | None = None
117
119
  payload: ManifestPayload | None = None
118
120
  assertions: list[ManifestAssertion] = field(default_factory=list)
121
+ # Read-only: spec prose places tdf_spec_version at the top level. Never written.
122
+ tdf_spec_version: str | None = None
123
+
124
+ def spec_version(self) -> str | None:
125
+ """Resolve the spec version: schemaVersion, then tdf_spec_version, then payload.tdf_spec_version."""
126
+ if self.schemaVersion:
127
+ return self.schemaVersion
128
+ if self.tdf_spec_version:
129
+ return self.tdf_spec_version
130
+ if self.payload and self.payload.tdf_spec_version:
131
+ return self.payload.tdf_spec_version
132
+ return None
119
133
 
120
134
  def _remove_none_values_and_empty_lists(self, obj):
121
135
  """Recursively remove None values and empty lists from dictionaries and lists."""
@@ -147,7 +161,9 @@ class Manifest:
147
161
  manifest_dict["encryptionInformation"] = asdict(self.encryptionInformation)
148
162
 
149
163
  if self.payload is not None:
150
- manifest_dict["payload"] = asdict(self.payload)
164
+ payload_dict = asdict(self.payload)
165
+ payload_dict.pop("tdf_spec_version", None)
166
+ manifest_dict["payload"] = payload_dict
151
167
 
152
168
  if self.schemaVersion is not None:
153
169
  manifest_dict["schemaVersion"] = self.schemaVersion
@@ -229,6 +245,7 @@ class Manifest:
229
245
 
230
246
  return Manifest(
231
247
  schemaVersion=d.get("schemaVersion", d.get("tdf_version")),
248
+ tdf_spec_version=d.get("tdf_spec_version"),
232
249
  encryptionInformation=_enc_info(
233
250
  d.get("encryptionInformation", d.get("encryption_information"))
234
251
  )
@@ -468,7 +468,7 @@ class NanoTDF:
468
468
  return len(header_bytes) + 3 + payload_length
469
469
 
470
470
  def _kas_unwrap(
471
- self, nano_tdf_data: bytes, header_len: int, wrapped_key: bytes
471
+ self, nano_tdf_data: bytes, header_len: int, _wrapped_key: bytes
472
472
  ) -> bytes | None:
473
473
  try:
474
474
  # For NanoTDF, send the entire header to KAS
@@ -622,7 +622,7 @@ class NanoTDF:
622
622
  # Try KAS unwrap first if services available
623
623
  if self.services:
624
624
  try:
625
- key = self._kas_unwrap(nano_tdf_data, header_len, wrapped_key=b"")
625
+ key = self._kas_unwrap(nano_tdf_data, header_len, b"")
626
626
  if key:
627
627
  logging.info(
628
628
  "Successfully unwrapped NanoTDF key via KAS (ECDH mode)"
@@ -25,7 +25,7 @@ class PolicyBindingSerializer:
25
25
 
26
26
  @staticmethod
27
27
  def deserialize(
28
- json_data: Any, typeofT: type | None = None, context: Any = None
28
+ json_data: Any, _typeof_t: type | None = None, _context: Any = None
29
29
  ) -> Any:
30
30
  if isinstance(json_data, dict):
31
31
  return PolicyBinding(**json_data)
@@ -35,7 +35,7 @@ class PolicyBindingSerializer:
35
35
 
36
36
  @staticmethod
37
37
  def serialize(
38
- src: Any, typeofSrc: type | None = None, context: Any = None
38
+ src: Any, _typeof_src: type | None = None, _context: Any = None
39
39
  ) -> dict | str:
40
40
  if isinstance(src, PolicyBinding):
41
41
  return vars(src)
@@ -43,7 +43,7 @@ class PolicyInfo:
43
43
  return offset - start
44
44
 
45
45
  @staticmethod
46
- def from_bytes_with_size(buffer: bytes, ecc_mode):
46
+ def from_bytes_with_size(buffer: bytes, _ecc_mode):
47
47
  # Parse policy_type (1 byte), body_len (2 bytes), body
48
48
  # Note: binding is NOT part of PolicyInfo - it's read separately in Header
49
49
  offset = 0
@@ -97,9 +97,9 @@ class KAS(AbstractContextManager):
97
97
 
98
98
  def unwrap_nanotdf(
99
99
  self,
100
- curve: Any,
101
- header: str,
102
- kas_url: str,
100
+ curve: Any, # noqa: ARG002
101
+ header: str, # noqa: ARG002
102
+ kas_url: str, # noqa: ARG002
103
103
  wrapped_key: bytes | None = None,
104
104
  kas_private_key: str | None = None,
105
105
  mock: bool = False,
@@ -368,13 +368,22 @@ class SDK(AbstractContextManager):
368
368
  import zipfile
369
369
  from io import BytesIO
370
370
 
371
+ from otdf_python.tdf_reader import (
372
+ payload_url_from_manifest_json,
373
+ resolve_manifest_name,
374
+ resolve_payload_name,
375
+ )
376
+
371
377
  try:
372
378
  file_like = BytesIO(data) if isinstance(data, bytes | bytearray) else data
373
379
  with zipfile.ZipFile(file_like) as zf:
374
- names = set(zf.namelist())
375
- return {"0.manifest.json", "0.payload"}.issubset(names) and len(
376
- names
377
- ) == 2
380
+ names = zf.namelist()
381
+ manifest_name = resolve_manifest_name(names)
382
+ url = payload_url_from_manifest_json(
383
+ zf.read(manifest_name).decode("utf-8")
384
+ )
385
+ resolve_payload_name(url, names)
386
+ return True
378
387
  except Exception:
379
388
  return False
380
389
 
@@ -275,7 +275,9 @@ class SDKBuilder:
275
275
  config_doc = response.json()
276
276
  # Newer platform versions return the fields directly at the top
277
277
  # level instead of nested under a "configuration" key.
278
- configuration = config_doc.get("configuration") or config_doc
278
+ configuration = config_doc.get("configuration")
279
+ if configuration is None:
280
+ configuration = config_doc
279
281
 
280
282
  # Try to get token endpoint from IDP configuration
281
283
  idp_config = configuration.get("idp", {})
@@ -28,6 +28,7 @@ from otdf_python.manifest import (
28
28
  ManifestSegment,
29
29
  )
30
30
  from otdf_python.policy_stub import NULL_POLICY_UUID
31
+ from otdf_python.tdf_reader import resolve_manifest_name, resolve_payload_name
31
32
  from otdf_python.tdf_writer import TDFWriter
32
33
 
33
34
 
@@ -399,7 +400,7 @@ class TDF:
399
400
  )
400
401
  payload_info = ManifestPayload(
401
402
  type="reference", # Changed from "file" to "reference" to match Java SDK
402
- url="0.payload",
403
+ url=TDFWriter.TDF_PAYLOAD_FILE_NAME,
403
404
  protocol="zip",
404
405
  mimeType=config.mime_type, # Use MIME type from config
405
406
  isEncrypted=True, # Changed from is_encrypted to isEncrypted
@@ -433,7 +434,8 @@ class TDF:
433
434
  tdf_bytes_io = io.BytesIO(tdf_data) if isinstance(tdf_data, bytes) else tdf_data
434
435
 
435
436
  with zipfile.ZipFile(tdf_bytes_io, "r") as z:
436
- manifest_json = z.read("0.manifest.json").decode()
437
+ names = z.namelist()
438
+ manifest_json = z.read(resolve_manifest_name(names)).decode()
437
439
  manifest = Manifest.from_json(manifest_json)
438
440
 
439
441
  if not manifest.encryptionInformation:
@@ -464,7 +466,8 @@ class TDF:
464
466
  segments = (
465
467
  manifest.encryptionInformation.integrityInformation.segments
466
468
  ) # Changed field name
467
- encrypted_payload = z.read("0.payload")
469
+ payload_url = manifest.payload.url if manifest.payload else None
470
+ encrypted_payload = z.read(resolve_payload_name(payload_url, names))
468
471
  payload = self._decrypt_segments(aesgcm, segments, encrypted_payload)
469
472
  return TDFReader(payload=payload, manifest=manifest)
470
473
 
@@ -487,7 +490,8 @@ class TDF:
487
490
  from .asym_crypto import AsymDecryption
488
491
 
489
492
  with zipfile.ZipFile(io.BytesIO(tdf_bytes), "r") as z:
490
- manifest_json = z.read("0.manifest.json").decode()
493
+ names = z.namelist()
494
+ manifest_json = z.read(resolve_manifest_name(names)).decode()
491
495
  manifest = Manifest.from_json(manifest_json)
492
496
 
493
497
  if not manifest.encryptionInformation:
@@ -510,7 +514,8 @@ class TDF:
510
514
  segments = (
511
515
  manifest.encryptionInformation.integrityInformation.segments
512
516
  ) # Changed field names
513
- encrypted_payload = z.read("0.payload")
517
+ payload_url = manifest.payload.url if manifest.payload else None
518
+ encrypted_payload = z.read(resolve_payload_name(payload_url, names))
514
519
  offset = 0
515
520
  for seg in segments:
516
521
  enc_len = seg.encryptedSegmentSize # Changed field name
@@ -1,15 +1,71 @@
1
1
  """TDFReader is responsible for reading and processing Trusted Data Format (TDF) files."""
2
2
 
3
+ import json
4
+ from collections.abc import Iterable
5
+
3
6
  from .manifest import Manifest
4
7
  from .policy_object import PolicyObject
5
8
  from .sdk_exceptions import SDKException
6
9
  from .zip_reader import ZipReader
7
10
 
8
- # Constants from TDFWriter
9
- TDF_MANIFEST_FILE_NAME = "0.manifest.json"
11
+ # Spec (opentdf/spec schema/OpenTDF/README.md): the manifest entry MUST be
12
+ # `manifest.json` at the archive root. `0.manifest.json` is what every SDK
13
+ # wrote before this change and is accepted forever on read.
14
+ TDF_MANIFEST_FILE_NAME = "manifest.json"
15
+ LEGACY_TDF_MANIFEST_FILE_NAME = "0.manifest.json"
16
+ # Payload entry name. Written by TDFWriter and into manifest.payload.url.
17
+ # On read this is only a fallback for manifests with an empty payload.url.
10
18
  TDF_PAYLOAD_FILE_NAME = "0.payload"
11
19
 
12
20
 
21
+ def resolve_manifest_name(names: Iterable[str]) -> str:
22
+ """Return the zip entry holding the manifest, spec name first."""
23
+ name_set = set(names)
24
+ for candidate in (TDF_MANIFEST_FILE_NAME, LEGACY_TDF_MANIFEST_FILE_NAME):
25
+ if candidate in name_set:
26
+ return candidate
27
+ raise ValueError("tdf doesn't contain a manifest")
28
+
29
+
30
+ def _is_safe_entry_name(name: str) -> bool:
31
+ if not name or name.startswith("/") or "\\" in name:
32
+ return False
33
+ return ".." not in name.split("/")
34
+
35
+
36
+ def resolve_payload_name(payload_url: str | None, names: Iterable[str]) -> str:
37
+ """Return the zip entry holding the payload.
38
+
39
+ Uses manifest.payload.url when present; falls back to `0.payload` only
40
+ when the url is empty or missing.
41
+ """
42
+ name_set = set(names)
43
+ if payload_url:
44
+ if not _is_safe_entry_name(payload_url):
45
+ raise ValueError(f"unsafe payload url in manifest: {payload_url!r}")
46
+ if payload_url in name_set:
47
+ return payload_url
48
+ raise ValueError(f"tdf doesn't contain payload entry {payload_url!r}")
49
+ if TDF_PAYLOAD_FILE_NAME in name_set:
50
+ return TDF_PAYLOAD_FILE_NAME
51
+ raise ValueError("tdf doesn't contain a payload")
52
+
53
+
54
+ def payload_url_from_manifest_json(manifest_text: str) -> str | None:
55
+ """Extract payload.url without requiring a fully valid manifest."""
56
+ try:
57
+ data = json.loads(manifest_text)
58
+ except (TypeError, ValueError):
59
+ return None
60
+ if not isinstance(data, dict):
61
+ return None
62
+ payload = data.get("payload")
63
+ if not isinstance(payload, dict):
64
+ return None
65
+ url = payload.get("url")
66
+ return url if isinstance(url, str) else None
67
+
68
+
13
69
  class TDFReader:
14
70
  """TDFReader is responsible for reading and processing Trusted Data Format (TDF) files.
15
71
 
@@ -31,15 +87,16 @@ class TDFReader:
31
87
  try:
32
88
  self._zip_reader = ZipReader(tdf)
33
89
  namelist = self._zip_reader.namelist()
34
-
35
- if TDF_MANIFEST_FILE_NAME not in namelist:
36
- raise ValueError("tdf doesn't contain a manifest")
37
- if TDF_PAYLOAD_FILE_NAME not in namelist:
38
- raise ValueError("tdf doesn't contain a payload")
39
-
40
- # Store the names for later use
41
- self._manifest_name = TDF_MANIFEST_FILE_NAME
42
- self._payload_name = TDF_PAYLOAD_FILE_NAME
90
+ self._manifest_name = resolve_manifest_name(namelist)
91
+ manifest_text = self._zip_reader.read(self._manifest_name).decode("utf-8")
92
+ payload_url = payload_url_from_manifest_json(manifest_text)
93
+ self._payload_name = resolve_payload_name(payload_url, namelist)
94
+ except UnicodeDecodeError as e:
95
+ # UnicodeDecodeError is a ValueError subclass, but it means the
96
+ # manifest entry is corrupt, not that the tdf is missing an
97
+ # entry (the resolvers' own ValueErrors). Wrap it like any
98
+ # other unexpected failure instead of letting it pass through.
99
+ raise SDKException("Error initializing TDFReader") from e
43
100
  except Exception as e:
44
101
  if isinstance(e, ValueError):
45
102
  raise
@@ -8,8 +8,10 @@ from otdf_python.zip_writer import ZipWriter
8
8
  class TDFWriter:
9
9
  """TDF file writer for creating encrypted TDF packages."""
10
10
 
11
+ # Spec: manifest entry MUST be `manifest.json` at the archive root.
12
+ TDF_MANIFEST_FILE_NAME = "manifest.json"
13
+ # Payload entry name; TDF.create_tdf writes this same value into manifest.payload.url.
11
14
  TDF_PAYLOAD_FILE_NAME = "0.payload"
12
- TDF_MANIFEST_FILE_NAME = "0.manifest.json"
13
15
 
14
16
  def __init__(self, out_stream: io.BytesIO | None = None):
15
17
  """Initialize TDF writer."""
File without changes