Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ repos:

- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.14.5
rev: v0.14.6
hooks:
# Run the linter.
- id: ruff-check
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ lint.select = [
# pyflakes checks.
"F",
# flake8-bugbear checks.
"B0",
"B",
# flake8-comprehensions checks.
"C4",
# McCabe complexity
Expand Down
10 changes: 5 additions & 5 deletions src/otdf_python/address_normalizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ def normalize_address(url_string: str, use_plaintext: bool) -> str:
port_str = host_port_pattern.group(2)
try:
port = int(port_str)
except ValueError:
raise SDKException(f"Invalid port in URL [{url_string}]")
except ValueError as err:
raise SDKException(f"Invalid port in URL [{url_string}]") from err

normalized_url = f"{scheme}://{host}:{port}"
logger.debug(f"normalized url [{url_string}] to [{normalized_url}]")
Expand Down Expand Up @@ -66,8 +66,8 @@ def normalize_address(url_string: str, use_plaintext: bool) -> str:
_, port_str = parsed_url.netloc.split(":", 1)
try:
port = int(port_str)
except ValueError:
raise SDKException(f"Invalid port in URL [{url_string}]")
except ValueError as err:
raise SDKException(f"Invalid port in URL [{url_string}]") from err

# If no port was found or extracted, use the default
if port is None:
Expand All @@ -81,4 +81,4 @@ def normalize_address(url_string: str, use_plaintext: bool) -> str:
except Exception as e:
if isinstance(e, SDKException):
raise e
raise SDKException(f"Error normalizing URL [{url_string}]", e)
raise SDKException(f"Error normalizing URL [{url_string}]: {e}") from e
10 changes: 5 additions & 5 deletions src/otdf_python/asym_crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def __init__(self, private_key_pem: str | None = None, private_key_obj=None):
decoded, password=None, backend=default_backend()
)
except Exception as e:
raise SDKException(f"Failed to load private key: {e}")
raise SDKException(f"Failed to load private key: {e}") from e
else:
self.private_key = None

Expand Down Expand Up @@ -89,7 +89,7 @@ def decrypt(self, data: bytes) -> bytes:
),
)
except Exception as e:
raise SDKException(f"Error performing decryption: {e}")
raise SDKException(f"Error performing decryption: {e}") from e


class AsymEncryption:
Expand Down Expand Up @@ -141,7 +141,7 @@ def __init__(self, public_key_pem: str | None = None, public_key_obj=None):
decoded, backend=default_backend()
)
except Exception as e:
raise SDKException(f"Failed to load public key: {e}")
raise SDKException(f"Failed to load public key: {e}") from e
else:
self.public_key = None

Expand Down Expand Up @@ -176,7 +176,7 @@ def encrypt(self, data: bytes) -> bytes:
),
)
except Exception as e:
raise SDKException(f"Error performing encryption: {e}")
raise SDKException(f"Error performing encryption: {e}") from e

def public_key_in_pem_format(self) -> str:
"""
Expand All @@ -195,4 +195,4 @@ def public_key_in_pem_format(self) -> str:
)
return pem.decode()
except Exception as e:
raise SDKException(f"Error exporting public key to PEM: {e}")
raise SDKException(f"Error exporting public key to PEM: {e}") from e
14 changes: 8 additions & 6 deletions src/otdf_python/autoconfigure_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ def __init__(self, url: str):
raise AutoConfigureException("invalid type: attribute regex fail")
try:
urllib.parse.unquote(matcher.group(2))
except Exception:
except Exception as err:
raise AutoConfigureException(
f"invalid type: error in attribute name [{matcher.group(2)}]"
)
) from err
self.url = url
self.key = url.lower()

Expand Down Expand Up @@ -76,8 +76,8 @@ def name(self):
raise AutoConfigureException("invalid attribute")
try:
return urllib.parse.unquote(matcher.group(1))
except Exception:
raise AutoConfigureException("invalid type")
except Exception as err:
raise AutoConfigureException("invalid type") from err


class AttributeValueFQN:
Expand All @@ -96,8 +96,10 @@ def __init__(self, url: str):
try:
urllib.parse.unquote(matcher.group(2))
urllib.parse.unquote(matcher.group(3))
except Exception:
raise AutoConfigureException("invalid type: error in attribute or value")
except Exception as err:
raise AutoConfigureException(
"invalid type: error in attribute or value"
) from err
self.url = url
self.key = url.lower()

Expand Down
4 changes: 2 additions & 2 deletions src/otdf_python/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,11 @@ def load_client_credentials(creds_file_path: str) -> tuple[str, str]:
except json.JSONDecodeError as e:
raise CLIError(
"CRITICAL", f"Invalid JSON in credentials file {creds_file_path}: {e}"
)
) from e
except Exception as e:
raise CLIError(
"CRITICAL", f"Error reading credentials file {creds_file_path}: {e}"
)
) from e


def build_sdk(args) -> SDK:
Expand Down
10 changes: 5 additions & 5 deletions src/otdf_python/ecdh.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ def decompress_public_key(

return ec.EllipticCurvePublicKey.from_encoded_point(curve, compressed_key)
except (ValueError, TypeError) as e:
raise InvalidKeyError(f"Failed to decompress public key: {e}")
raise InvalidKeyError(f"Failed to decompress public key: {e}") from e


def derive_shared_secret(
Expand All @@ -179,7 +179,7 @@ def derive_shared_secret(
shared_secret = private_key.exchange(ec.ECDH(), public_key)
return shared_secret
except Exception as e:
raise ECDHError(f"Failed to derive shared secret: {e}")
raise ECDHError(f"Failed to derive shared secret: {e}") from e


def derive_key_from_shared_secret(
Expand Down Expand Up @@ -216,7 +216,7 @@ def derive_key_from_shared_secret(
)
return hkdf.derive(shared_secret)
except Exception as e:
raise ECDHError(f"Failed to derive key from shared secret: {e}")
raise ECDHError(f"Failed to derive key from shared secret: {e}") from e


def encrypt_key_with_ecdh(
Expand Down Expand Up @@ -251,7 +251,7 @@ def encrypt_key_with_ecdh(
if not isinstance(recipient_public_key, ec.EllipticCurvePublicKey):
raise InvalidKeyError("Recipient's public key is not an EC key")
except Exception as e:
raise InvalidKeyError(f"Failed to load recipient's public key: {e}")
raise InvalidKeyError(f"Failed to load recipient's public key: {e}") from e

# Generate ephemeral keypair
ephemeral_private_key, ephemeral_public_key = generate_ephemeral_keypair(curve_name)
Expand Down Expand Up @@ -301,7 +301,7 @@ def decrypt_key_with_ecdh(
if not isinstance(recipient_private_key, ec.EllipticCurvePrivateKey):
raise InvalidKeyError("Recipient's private key is not an EC key")
except Exception as e:
raise InvalidKeyError(f"Failed to load recipient's private key: {e}")
raise InvalidKeyError(f"Failed to load recipient's private key: {e}") from e

# Decompress ephemeral public key
ephemeral_public_key = decompress_public_key(
Expand Down
16 changes: 8 additions & 8 deletions src/otdf_python/kas_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def _normalize_kas_url(self, url: str) -> str:
# Parse the URL
parsed = urlparse(url)
except Exception as e:
raise SDKException(f"error trying to parse URL [{url}]", e)
raise SDKException(f"error trying to parse URL [{url}]: {e}") from e

# Check if we have a host or if this is likely a hostname:port combination
if parsed.hostname is None:
Expand All @@ -100,10 +100,10 @@ def _handle_missing_scheme(self, url: str) -> str:
try:
port = int(port_str)
return f"{scheme}://{host}:{port}"
except ValueError:
except ValueError as err:
raise SDKException(
f"error trying to create URL for host and port [{url}]"
)
) from err
else:
# Hostname with or without path, add default port
if "/" in url:
Expand All @@ -116,7 +116,7 @@ def _handle_missing_scheme(self, url: str) -> str:
except Exception as e:
raise SDKException(
f"error trying to create URL for host and port [{url}]", e
)
) from e

def _handle_existing_scheme(self, parsed) -> str:
"""Handle URLs with existing scheme by normalizing protocol and port."""
Expand All @@ -138,7 +138,7 @@ def _handle_existing_scheme(self, parsed) -> str:
logging.debug(f"normalized url [{parsed.geturl()}] to [{normalized_url}]")
return normalized_url
except Exception as e:
raise SDKException("error creating KAS address", e)
raise SDKException(f"error creating KAS address: {e}") from e

def _get_wrapped_key_base64(self, key_access):
"""
Expand Down Expand Up @@ -483,7 +483,7 @@ def _get_public_key_with_connect_rpc(self, kas_info):
f"Connect RPC public key request failed: {type(e).__name__}: {e}"
)
logging.error(f"Full traceback: {error_details}")
raise SDKException(f"Connect RPC public key request failed: {e}")
raise SDKException(f"Connect RPC public key request failed: {e}") from e

def _normalize_session_key_type(self, session_key_type):
"""
Expand Down Expand Up @@ -608,7 +608,7 @@ def _parse_and_decrypt_response(self, response):
except Exception as e:
logging.error(f"Failed to parse JSON response: {e}")
logging.error(f"Raw response content: {response.content}")
raise SDKException(f"Invalid JSON response from KAS: {e}")
raise SDKException(f"Invalid JSON response from KAS: {e}") from e

entity_wrapped_key = response_data.get("entityWrappedKey")
if not entity_wrapped_key:
Expand Down Expand Up @@ -702,7 +702,7 @@ def _unwrap_with_connect_rpc(

except Exception as e:
logging.error(f"Connect RPC rewrap failed: {e}")
raise SDKException(f"Connect RPC rewrap failed: {e}")
raise SDKException(f"Connect RPC rewrap failed: {e}") from e

def get_key_cache(self) -> KASKeyCache:
"""Returns the KAS key cache used for storing and retrieving encryption keys."""
Expand Down
4 changes: 2 additions & 2 deletions src/otdf_python/kas_connect_rpc_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ def get_public_key(self, normalized_kas_url, kas_info, access_token=None):
f"Connect RPC public key request failed: {type(e).__name__}: {e}"
)
logging.error(f"Full traceback: {error_details}")
raise SDKException(f"Connect RPC public key request failed: {e}")
raise SDKException(f"Connect RPC public key request failed: {e}") from e

def unwrap_key(
self, normalized_kas_url, key_access, signed_token, access_token=None
Expand Down Expand Up @@ -210,4 +210,4 @@ def unwrap_key(

except Exception as e:
logging.error(f"Connect RPC rewrap failed: {e}")
raise SDKException(f"Connect RPC rewrap failed: {e}")
raise SDKException(f"Connect RPC rewrap failed: {e}") from e
4 changes: 2 additions & 2 deletions src/otdf_python/nanotdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ def _is_ec_key(self, key_pem: str) -> bool:
else:
raise SDKException("Invalid PEM format - no BEGIN header found")
except Exception as e:
raise SDKException(f"Failed to detect key type: {e}")
raise SDKException(f"Failed to detect key type: {e}") from e

def _derive_key_with_ecdh( # noqa: C901
self, config: NanoTDFConfig
Expand Down Expand Up @@ -582,7 +582,7 @@ def read_nano_tdf( # noqa: C901
header_len = Header.peek_length(nano_tdf_data)
header_obj = Header.from_bytes(nano_tdf_data[:header_len])
except Exception as e:
raise InvalidNanoTDFConfig(f"Failed to parse NanoTDF header: {e}")
raise InvalidNanoTDFConfig(f"Failed to parse NanoTDF header: {e}") from e

# Read payload section per NanoTDF spec:
# [3 bytes: length] [3 bytes: IV] [variable: ciphertext] [tag]
Expand Down
6 changes: 4 additions & 2 deletions src/otdf_python/sdk_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ def _discover_token_endpoint(self) -> None:
pass
raise AutoConfigureException(
f"Error during token endpoint discovery: {e!s}"
)
) from e

# Fall back to explicit issuer endpoint
if self.issuer_endpoint:
Expand Down Expand Up @@ -341,7 +341,9 @@ def _get_token_from_client_credentials(self) -> str:
)

except Exception as e:
raise AutoConfigureException(f"Error during token acquisition: {e!s}")
raise AutoConfigureException(
f"Error during token acquisition: {e!s}"
) from e

def _create_services(self) -> SDK.Services:
"""
Expand Down
2 changes: 1 addition & 1 deletion src/otdf_python/tdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def _validate_kas_infos(self, kas_infos):
except Exception as e:
raise ValueError(
f"Failed to fetch public key for KAS {kas.url}: {e}"
)
) from e
else:
raise ValueError(
"Each KAS info must have a public_key, or SDK services must be available to fetch it"
Expand Down
4 changes: 2 additions & 2 deletions src/otdf_python/zip_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def get_data(self) -> bytes:
try:
return self._zipfile.read(self._zipinfo)
except Exception as e:
raise InvalidZipException(f"Error reading entry data: {e}")
raise InvalidZipException(f"Error reading entry data: {e}") from e

def __init__(self, in_stream: io.BytesIO | bytes | None = None):
try:
Expand All @@ -29,7 +29,7 @@ def __init__(self, in_stream: io.BytesIO | bytes | None = None):
self.Entry(self.zipfile, zi) for zi in self.zipfile.infolist()
]
except zipfile.BadZipFile as e:
raise InvalidZipException(f"Invalid ZIP file: {e}")
raise InvalidZipException(f"Invalid ZIP file: {e}") from e

def get_entries(self) -> list:
return self.entries
Expand Down
6 changes: 3 additions & 3 deletions tests/integration/test_cli_tdf_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ def _validate_tdf_zip_structure(tdf_path: Path) -> None:
assert "uuid" in policy_obj, "policy missing 'uuid' field"
assert "body" in policy_obj, "policy missing 'body' field"
except Exception as e:
raise AssertionError(f"Failed to decode base64 policy: {e}")
raise AssertionError(f"Failed to decode base64 policy: {e}") from e

# Validate method structure
method = enc_info["method"]
Expand Down Expand Up @@ -216,9 +216,9 @@ def _validate_tdf_zip_structure(tdf_path: Path) -> None:
)

except json.JSONDecodeError as e:
raise AssertionError(f"Manifest is not valid JSON: {e}")
raise AssertionError(f"Manifest is not valid JSON: {e}") from e
except KeyError as e:
raise AssertionError(f"Manifest missing required field: {e}")
raise AssertionError(f"Manifest missing required field: {e}") from e

# Check for payload file (usually 0.payload)
payload_files = [f for f in file_list if f.endswith(".payload")]
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/test_pe_interaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def decrypt(input_path: Path, output_path: Path, sdk: SDK):
logger.error(f"Decryption failed: {e}")
# Clean up the output file if there was an error
output_path.unlink(missing_ok=True)
raise SDKException("Decryption failed")
raise SDKException("Decryption failed") from e


@pytest.mark.integration
Expand Down
4 changes: 2 additions & 2 deletions tests/support_otdfctl.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def check_for_otdfctl():
capture_output=True,
check=True,
)
except (subprocess.CalledProcessError, FileNotFoundError):
except (subprocess.CalledProcessError, FileNotFoundError) as err:
raise Exception(
"otdfctl command not found on system. Please install otdfctl to run this test."
)
) from err
Loading