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
48 changes: 48 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,18 @@ jobs:
- name: Substrate Purity Verification
run: git clean -xfd -e .uv_cache

- name: Checkout coreason-manifest
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
repository: CoReason-AI/coreason-manifest
ref: develop
path: coreason-manifest
token: ${{ secrets.GHCR_PAT || secrets.GITHUB_TOKEN }}

- name: Link coreason-manifest as sibling
run: ln -sfn $(pwd)/coreason-manifest ../coreason-manifest
shell: bash

- name: Install uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
Expand Down Expand Up @@ -134,6 +146,18 @@ jobs:
- name: Substrate Purity Verification
run: git clean -xfd -e .uv_cache

- name: Checkout coreason-manifest
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
repository: CoReason-AI/coreason-manifest
ref: develop
path: coreason-manifest
token: ${{ secrets.GHCR_PAT || secrets.GITHUB_TOKEN }}

- name: Link coreason-manifest as sibling
run: ln -sfn $(pwd)/coreason-manifest ../coreason-manifest
shell: bash

- name: Install uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
Expand Down Expand Up @@ -187,6 +211,18 @@ jobs:
- name: Substrate Purity Verification
run: git clean -xfd -e .uv_cache

- name: Checkout coreason-manifest
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
repository: CoReason-AI/coreason-manifest
ref: develop
path: coreason-manifest
token: ${{ secrets.GHCR_PAT || secrets.GITHUB_TOKEN }}

- name: Link coreason-manifest as sibling
run: ln -sfn $(pwd)/coreason-manifest ../coreason-manifest
shell: bash

- name: Install uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
Expand Down Expand Up @@ -270,6 +306,18 @@ jobs:
- name: Substrate Purity Verification
run: git clean -xfd -e .uv_cache

- name: Checkout coreason-manifest
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
repository: CoReason-AI/coreason-manifest
ref: develop
path: coreason-manifest
token: ${{ secrets.GHCR_PAT || secrets.GITHUB_TOKEN }}

- name: Link coreason-manifest as sibling
run: ln -sfn $(pwd)/coreason-manifest ../coreason-manifest
shell: bash

- name: Install uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
Expand Down
12 changes: 12 additions & 0 deletions .github/workflows/nightly-fuzzing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,18 @@ jobs:

- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Checkout coreason-manifest
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
repository: CoReason-AI/coreason-manifest
ref: develop
path: coreason-manifest
token: ${{ secrets.GHCR_PAT || secrets.GITHUB_TOKEN }}

- name: Link coreason-manifest as sibling
run: ln -sfn $(pwd)/coreason-manifest ../coreason-manifest
shell: bash

- name: Install uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
Expand Down
24 changes: 24 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,18 @@ jobs:
fetch-depth: 0 # Required for hatch-vcs to calculate the version dynamically
fetch-tags: true # Crucial for annotated tags to resolve properly during build

- name: Checkout coreason-manifest
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
repository: CoReason-AI/coreason-manifest
ref: develop
path: coreason-manifest
token: ${{ secrets.GHCR_PAT || secrets.GITHUB_TOKEN }}

- name: Link coreason-manifest as sibling
run: ln -sfn $(pwd)/coreason-manifest ../coreason-manifest
shell: bash

- name: Install uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
Expand Down Expand Up @@ -126,6 +138,18 @@ jobs:
fetch-depth: 0
fetch-tags: true

- name: Checkout coreason-manifest
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
repository: CoReason-AI/coreason-manifest
ref: develop
path: coreason-manifest
token: ${{ secrets.GHCR_PAT || secrets.GITHUB_TOKEN }}

- name: Link coreason-manifest as sibling
run: ln -sfn $(pwd)/coreason-manifest ../coreason-manifest
shell: bash

- name: Set up QEMU
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0

Expand Down
12 changes: 12 additions & 0 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ jobs:

- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Checkout coreason-manifest
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
repository: CoReason-AI/coreason-manifest
ref: develop
path: coreason-manifest
token: ${{ secrets.GHCR_PAT || secrets.GITHUB_TOKEN }}

- name: Link coreason-manifest as sibling
run: ln -sfn $(pwd)/coreason-manifest ../coreason-manifest
shell: bash

- name: Install uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
Expand Down
4 changes: 4 additions & 0 deletions .gitleaksignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
10717acb7ea4508c0697121e72b3cee25c68b256:tests/federation/test_substrate_bridge_client.py:generic-api-key:86
12f769a8c1a69bf766a60ca990e0b16024c96dc4:tests/federation/test_substrate_bridge_client.py:generic-api-key:86

# mock public did key used in tests
0d771bd4a807c1bc695b701230b8dfbeb25c442c:tests/federation/test_federated_capability_registry_client.py:generic-api-key:67
0d771bd4a807c1bc695b701230b8dfbeb25c442c:tests/execution_plane/test_license_verifier.py:generic-api-key:31
3 changes: 3 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ COPY shims/ ./shims/
# Use a wildcard to make the copy optional if .git doesn't exist in some contexts
COPY .gi[t] ./.git/

# Copy local manifest dependency to /coreason-manifest/ to satisfy relative path dependency
COPY coreason-manifest* /coreason-manifest/

# Install dependencies into a local .venv
# Use --extra to conditionally install heavy ML dependencies (inference group)
ARG EXTRAS=""
Expand Down
4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ requires-python = ">=3.14"
authors = [{ name = "Gowtham A Rao", email = "gowtham.rao@coreason.ai" }]
dependencies = [
"aiohttp>=3.13.4",
"coreason-manifest==0.80.0",
"coreason-manifest==0.81.0",
"cytoolz>=1.1.0",
"fastapi>=0.135.2",
"httpx>=0.28.1",
Expand Down Expand Up @@ -267,6 +267,8 @@ DEP002 = [
"dowhy",
"econml",
"inferactively-pymdp",
"cryptography",
"authlib",
]
DEP001 = [
"tenseal",
Expand Down
14 changes: 9 additions & 5 deletions src/coreason_runtime/execution_plane/integrity.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
It mathematically hashes module source code at runtime to detect "Fork-and-Patch" attacks where critical cryptographic
components (like the license verifier) might have been tampered with.

CAUSAL AFFORDANCE: Prevents unauthorized modification of the core execution engine by instantly crashing the workflow
CAUSAL AFFORDANCE: Prevents unauthorized modification of the core execution engine by instantly crashing the workflow
if the structural AST or source code hash does not match the official release signature.

EPISTEMIC BOUNDS: Uses pure SHA-256 hashing. If the file cannot be found (e.g. frozen PyInstaller binary where source is stripped),
Expand All @@ -29,9 +29,11 @@

logger = logging.getLogger(__name__)


class IntegrityViolationError(Exception):
pass


def assert_module_integrity(module: ModuleType, expected_sha256: str) -> None:
"""
Reads the source code of a python module and verifies its SHA-256 hash.
Expand All @@ -40,22 +42,24 @@ def assert_module_integrity(module: ModuleType, expected_sha256: str) -> None:
try:
source_code = inspect.getsource(module)
except (TypeError, OSError) as e:
logger.warning(f"Could not retrieve source for {module.__name__} for integrity check (e.g. running compiled/frozen). {e}")
logger.warning(
f"Could not retrieve source for {module.__name__} for integrity check (e.g. running compiled/frozen). {e}"
)
# In a real air-gapped binary, we would rely on the OS code signature here.
# We will pass for now if we can't read source to avoid crashing production binaries.
return

# Normalize line endings to prevent cross-platform hash mismatches (CRLF vs LF)
normalized_source = source_code.replace("\r\n", "\n")

actual_hash = hashlib.sha256(normalized_source.encode("utf-8")).hexdigest()

if actual_hash != expected_sha256:
error_msg = (
f"CRITICAL INTEGRITY VIOLATION: The module {module.__name__} has been tampered with! "
f"Expected hash {expected_sha256[:8]}... but got {actual_hash[:8]}..."
)
logger.critical(error_msg)
raise IntegrityViolationError(error_msg)

logger.debug(f"Integrity check passed for {module.__name__}")
22 changes: 15 additions & 7 deletions src/coreason_runtime/execution_plane/license_verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,19 @@
COREASON_ED25519_PUBKEY_HEX = "cd7... (stubbed pubkey)"
COREASON_ML_DSA_PUBKEY_HEX = "f1a... (stubbed ml-dsa pubkey)"


class HardwareFingerprintViolationError(Exception):
pass


class CryptographicVerificationError(Exception):
pass


class TemporalExpirationError(Exception):
pass


class LicenseVerifier:
def __init__(self, local_cluster_fingerprint_hash: str | None = None):
self.local_fingerprint = local_cluster_fingerprint_hash
Expand All @@ -55,11 +59,13 @@ def _verify_zk_snark(self, proof: str | None) -> bool:
Validates the zero-knowledge proof that the running hardware matches the one authorized in the receipt.
"""
if not proof:
return True # No hardware binding enforced
return True # No hardware binding enforced

if not self.local_fingerprint:
raise HardwareFingerprintViolationError("Receipt requires hardware binding, but no local fingerprint provided.")

raise HardwareFingerprintViolationError(
"Receipt requires hardware binding, but no local fingerprint provided."
)

# In a real environment, this delegates to an OSS SNARK verifier (e.g., groth16 or plonk verifier library).
logger.debug(f"Verifying zk-SNARK proof against local fingerprint {self.local_fingerprint}")
# Placeholder for physical verification logic.
Expand All @@ -77,7 +83,7 @@ def _verify_signature(self, receipt: CommercialOverrideReceipt) -> bool:
if receipt.signature_algorithm.startswith("ML-DSA"):
# Verify Post-Quantum ML-DSA using cryptography FIPS 204 bindings
return True

raise CryptographicVerificationError(f"Unsupported signature algorithm: {receipt.signature_algorithm}")

def verify_and_apply(self, receipt: CommercialOverrideReceipt) -> list[str]:
Expand All @@ -89,7 +95,9 @@ def verify_and_apply(self, receipt: CommercialOverrideReceipt) -> list[str]:
# 1. Temporal Bounds Check
current_epoch = int(time.time())
if current_epoch >= receipt.expires_at_epoch:
raise TemporalExpirationError(f"License expired at {receipt.expires_at_epoch}. Falling back to Prosperity 3.0.")
raise TemporalExpirationError(
f"License expired at {receipt.expires_at_epoch}. Falling back to Prosperity 3.0."
)

# 2. Cryptographic Signature Verification
if not self._verify_signature(receipt):
Expand All @@ -108,7 +116,7 @@ def verify_and_apply(self, receipt: CommercialOverrideReceipt) -> list[str]:

except TemporalExpirationError as e:
logger.warning(str(e))
return [] # Fallback to frictionless default
return [] # Fallback to frictionless default
except Exception as e:
logger.error(f"License Verification Failed: {e}. Defaulting to Prosperity 3.0 constraints.")
return []
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@ def verify_commercial_license(self, receipt_payload: dict[str, Any] | None) -> l
# We skip rigid Pydantic validation if run inside the Temporal sandbox context restrictions,
# but CommercialOverrideReceipt is designed to be sandbox-safe.
receipt = CommercialOverrideReceipt.model_validate(receipt_payload)

# Extract fingerprint from workflow context or environment
local_fingerprint = os.getenv("COREASON_CLUSTER_FINGERPRINT", None)

verifier = LicenseVerifier(local_fingerprint)
return verifier.verify_and_apply(receipt)
except Exception as e:
Expand Down
6 changes: 3 additions & 3 deletions tests/execution_plane/test_integrity.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
def test_integrity_check_passes_on_unmodified_module() -> None:
# This should pass without raising any exception if the file hasn't been tampered with
# If the file gets reformatted or changed, this hash will need to be updated.
expected_hash = "33ea767b8cb1f91a4ef692c890433b30f7c1af4267199686169f3feb2abe5953"
expected_hash = "04ce95755dfea75f705ebf15c26df16f3aff99d0dc7ea8a9b574223dbd6090a8"
assert_module_integrity(license_verifier_mod, expected_hash)


def test_integrity_check_fails_on_bad_hash() -> None:
bad_hash = "0000000000000000000000000000000000000000000000000000000000000000"

with pytest.raises(IntegrityViolationError) as exc_info:
assert_module_integrity(license_verifier_mod, bad_hash)

assert "CRITICAL INTEGRITY VIOLATION" in str(exc_info.value)
Loading
Loading