src/pqc_hypervisor_attestation/region.py
| 1 | """Memory region data structures.""" |
| 2 | |
| 3 | from __future__ import annotations |
| 4 | |
| 5 | import hashlib |
| 6 | from dataclasses import asdict, dataclass |
| 7 | from datetime import datetime, timezone |
| 8 | from typing import Any |
| 9 | |
| 10 | |
| 11 | @dataclass(frozen=True) |
| 12 | class MemoryRegion: |
| 13 | """An addressable region of memory the attester will cover. |
| 14 | |
| 15 | We use abstract addresses (strings or ints). In practice this is: |
| 16 | - guest-physical address + size for a VM |
| 17 | - process virtual address + size for a confidential container |
| 18 | - a model-weights object ID in a model-serving server |
| 19 | """ |
| 20 | |
| 21 | region_id: str # stable identifier (e.g. "model-weights-0") |
| 22 | description: str # human-readable description |
| 23 | address: int # base address (in abstract address space) |
| 24 | size: int # bytes |
| 25 | protection: str = "RO" # "R" | "RW" | "RO" | "RX" |
| 26 | |
| 27 | def to_dict(self) -> dict[str, Any]: |
| 28 | return asdict(self) |
| 29 | |
| 30 | |
| 31 | @dataclass(frozen=True) |
| 32 | class RegionSnapshot: |
| 33 | """A SHA3-256 fingerprint of a region at a point in time. |
| 34 | |
| 35 | Backends produce snapshots; attesters sign them. |
| 36 | """ |
| 37 | |
| 38 | region_id: str |
| 39 | content_hash: str # SHA3-256 of the exact bytes at `taken_at` |
| 40 | size: int |
| 41 | taken_at: str # ISO-8601 |
| 42 | |
| 43 | @staticmethod |
| 44 | def hash_bytes(data: bytes) -> str: |
| 45 | return hashlib.sha3_256(data).hexdigest() |
| 46 | |
| 47 | @classmethod |
| 48 | def create(cls, region_id: str, content: bytes) -> RegionSnapshot: |
| 49 | return cls( |
| 50 | region_id=region_id, |
| 51 | content_hash=cls.hash_bytes(content), |
| 52 | size=len(content), |
| 53 | taken_at=datetime.now(timezone.utc).isoformat(), |
| 54 | ) |
| 55 | |
| 56 | def to_dict(self) -> dict[str, Any]: |
| 57 | return asdict(self) |
| 58 | |