diff --git a/.github/workflows/bot-assignment-check.yml b/.github/workflows/bot-assignment-check.yml index 46425fd58..010fcff70 100644 --- a/.github/workflows/bot-assignment-check.yml +++ b/.github/workflows/bot-assignment-check.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 with: egress-policy: audit diff --git a/.github/workflows/bot-inactivity-unassign-phase1.yml b/.github/workflows/bot-inactivity-unassign-phase1.yml index 3530400db..7e5fe01d3 100644 --- a/.github/workflows/bot-inactivity-unassign-phase1.yml +++ b/.github/workflows/bot-inactivity-unassign-phase1.yml @@ -19,7 +19,7 @@ jobs: uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 - name: Harden the runner - uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 with: egress-policy: audit diff --git a/.github/workflows/bot-office-hours.yml b/.github/workflows/bot-office-hours.yml index 58bfe45c5..2a8cda84a 100644 --- a/.github/workflows/bot-office-hours.yml +++ b/.github/workflows/bot-office-hours.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner - uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 with: egress-policy: audit diff --git a/.github/workflows/bot-verified-commits.yml b/.github/workflows/bot-verified-commits.yml index 03c87c3d6..5e032d27c 100644 --- a/.github/workflows/bot-verified-commits.yml +++ b/.github/workflows/bot-verified-commits.yml @@ -19,7 +19,7 @@ jobs: steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3 + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 with: egress-policy: audit diff --git a/.github/workflows/bot-workflows.yml b/.github/workflows/bot-workflows.yml index 740addd79..bdd528bc8 100644 --- a/.github/workflows/bot-workflows.yml +++ b/.github/workflows/bot-workflows.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3 + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 with: egress-policy: audit diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index a68569d27..11aa315a7 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3 + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 with: egress-policy: audit diff --git a/.github/workflows/merge-conflict-bot.yml b/.github/workflows/merge-conflict-bot.yml index af6d61426..3fead7a7d 100644 --- a/.github/workflows/merge-conflict-bot.yml +++ b/.github/workflows/merge-conflict-bot.yml @@ -18,7 +18,7 @@ jobs: steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3 + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 with: egress-policy: audit diff --git a/.github/workflows/pr-check-changelog.yml b/.github/workflows/pr-check-changelog.yml index e61d7dd69..748c5f27e 100644 --- a/.github/workflows/pr-check-changelog.yml +++ b/.github/workflows/pr-check-changelog.yml @@ -17,7 +17,7 @@ jobs: fetch-depth: 0 - name: Harden the runner - uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3 + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 with: egress-policy: audit diff --git a/.github/workflows/pr-check-title.yml b/.github/workflows/pr-check-title.yml index ee9a46987..b2be13c43 100644 --- a/.github/workflows/pr-check-title.yml +++ b/.github/workflows/pr-check-title.yml @@ -31,7 +31,7 @@ jobs: statuses: write steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3 + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 with: egress-policy: audit diff --git a/.github/workflows/pr-inactivity-reminder-bot.yml b/.github/workflows/pr-inactivity-reminder-bot.yml index 768ea0d4d..7de72d36f 100644 --- a/.github/workflows/pr-inactivity-reminder-bot.yml +++ b/.github/workflows/pr-inactivity-reminder-bot.yml @@ -26,7 +26,7 @@ jobs: steps: - name: Harden the runner - uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3 + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 with: egress-policy: audit - name: Checkout repository diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 525455c47..7ea81ee0e 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -18,7 +18,7 @@ jobs: id-token: write steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3 + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 with: egress-policy: audit diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 26b615afe..698302c1c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,7 +22,7 @@ jobs: steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3 + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 with: egress-policy: audit diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e1de1862..7d5c0f48a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,19 +7,21 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1. ## [Unreleased] ### Added + - Modularized `transfer_transaction_fungible` example by introducing `account_balance_query()` & `transfer_transaction()`.Renamed `transfer_tokens()` → `main()` - Phase 2 of the inactivity-unassign bot:Automatically detects stale open pull requests (no commit activity for 21+ days), comments with a helpful InactivityBot message, closes the stale PR, and unassigns the contributor from the linked issue. -- Added **str**() to CustomFixedFee and updated examples and tests accordingly. +- Added `__str__()` to CustomFixedFee and updated examples and tests accordingly. +- Added unit tests for `crypto_utils` (#993) - Added a github template for good first issues - Added `.github/workflows/bot-assignment-check.yml` to limit non-maintainers to 2 concurrent issue assignments. -- Added all missing fields to **str**() method and updated `test_tokem_info.py` +- Added all missing fields to `__str__()` method and updated `test_tokem_info.py` - Add examples/tokens/token_create_transaction_pause_key.py example demonstrating token pause/unpause behavior and pause key usage (#833) - Added `docs/sdk_developers/training/transaction_lifecycle.md` to explain the typical lifecycle of executing a transaction using the Hedera Python SDK. - Add inactivity bot workflow to unassign stale issue assignees (#952) - Made custom fraction fee end to end - feat: AccountCreateTransaction now supports both PrivateKey and PublicKey [#939](https://github.com/hiero-ledger/hiero-sdk-python/issues/939) - Added Acceptance Criteria section to Good First Issue template for better contributor guidance (#997) -- Added __str__() to CustomRoyaltyFee and updated examples and tests accordingly (#986) +- Added `__str__()` to CustomRoyaltyFee and updated examples and tests accordingly (#986) - Restore bug and feature request issue templates (#996)(https://github.com/hiero-ledger/hiero-sdk-python/issues/996) - Support selecting specific node account ID(s) for queries and transactions and added `Network._get_node()` with updated execution flow (#362) - Add TLS support with two-stage control (`set_transport_security()` and `set_verify_certificates()`) for encrypted connections to Hedera networks. TLS is enabled by default for hosted networks (mainnet, testnet, previewnet) and disabled for local networks (solo, localhost) (#855) diff --git a/tests/unit/test_crypto_utils.py b/tests/unit/test_crypto_utils.py new file mode 100644 index 000000000..efecde714 --- /dev/null +++ b/tests/unit/test_crypto_utils.py @@ -0,0 +1,88 @@ +"""Unit tests for crypto_utils module.""" +from cryptography.hazmat.primitives.asymmetric import ec +import pytest + +from hiero_sdk_python.utils.crypto_utils import ( + compress_point_unchecked, + compress_with_cryptography, + decompress_point, + keccak256, +) + +pytestmark = pytest.mark.unit + + +def test_keccak256(): + """Test keccak256 hashing.""" + # Known vector: empty string -> c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 + assert keccak256(b"").hex() == "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" + + # "hello" -> 1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8 + assert keccak256(b"hello").hex() == "1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8" + + # "Transfer" -> 461a29a8a7db848c0827103038dd4776114eb182e0717208d0a793574936353d + assert keccak256(b"Transfer").hex() == "f099cd8bde557814842a3121e8ddfd433a539b8c9f14bf31ebf108d12e6196e9" + + +def test_compress_point_unchecked(): + """Test point compression logic.""" + # Use cryptography to generate a valid point + priv = ec.generate_private_key(ec.SECP256K1()) + pub = priv.public_key() + nums = pub.public_numbers() + x = nums.x + y = nums.y + + compressed = compress_point_unchecked(x, y) + assert len(compressed) == 33 + + # Verify expected prefix + expected_prefix = 0x03 if y % 2 else 0x02 + assert compressed[0] == expected_prefix + assert int.from_bytes(compressed[1:], "big") == x + + +def test_decompress_point(): + """Test point decompression logic.""" + priv = ec.generate_private_key(ec.SECP256K1()) + pub = priv.public_key() + nums = pub.public_numbers() + + # Create valid compressed point + compressed = compress_point_unchecked(nums.x, nums.y) + + # Test decompression + x, y = decompress_point(compressed) + assert x == nums.x + assert y == nums.y + + # Test uncompressed 65-byte format (0x04 + x + y) + uncompressed = b'\x04' + nums.x.to_bytes(32, 'big') + nums.y.to_bytes(32, 'big') + x2, y2 = decompress_point(uncompressed) + assert x2 == nums.x + assert y2 == nums.y + + # Test invalid length + with pytest.raises(ValueError, match="Not recognized"): + decompress_point(b'\x04' * 10) + + # Test invalid prefix + with pytest.raises(ValueError, match="Not recognized"): + # 0x05 is invalid prefix for 33-byte point + invalid_point = b'\x05' + nums.x.to_bytes(32, 'big') + decompress_point(invalid_point) + + +def test_compress_with_cryptography(): + """Test compression using cryptography library.""" + priv = ec.generate_private_key(ec.SECP256K1()) + pub = priv.public_key() + nums = pub.public_numbers() + + # Create uncompressed + uncompressed = b'\x04' + nums.x.to_bytes(32, 'big') + nums.y.to_bytes(32, 'big') + + compressed_via_lib = compress_with_cryptography(uncompressed) + compressed_manual = compress_point_unchecked(nums.x, nums.y) + + assert compressed_via_lib == compressed_manual