"""Identifiers for the FablePool wire format. All identifiers are lowercase-hex SHA-256 digests with typed prefixes: * operation id: ``fp:op:<64 hex>`` = SHA-256 of the canonical bytes of the full signed envelope * actor id: ``fp:actor:<64 hex>`` = SHA-256 of the 32 raw Ed25519 public-key bytes * content hash: ``sha256:<64 hex>`` = SHA-256 of raw evidence bytes * public key: ``ed25519:<64 hex>`` (raw 32-byte key, hex) * signature: ``ed25519:<128 hex>`` (raw 64-byte signature, hex) See spec/02-wire-format/01-encoding-and-identifiers.md. """ import hashlib import re from .canonical import canonicalize OP_ID_PATTERN = r"^fp:op:[0-9a-f]{64}$" ACTOR_ID_PATTERN = r"^fp:actor:[0-9a-f]{64}$" PUBKEY_PATTERN = r"^ed25519:[0-9a-f]{64}$" SIG_PATTERN = r"^ed25519:[0-9a-f]{128}$" CONTENT_HASH_PATTERN = r"^sha256:[0-9a-f]{64}$" _OP_ID_RE = re.compile(OP_ID_PATTERN) _PUBKEY_RE = re.compile(PUBKEY_PATTERN) _CONTENT_HASH_RE = re.compile(CONTENT_HASH_PATTERN) OP_ID_PREFIX = "fp:op:" ACTOR_ID_PREFIX = "fp:actor:" PUBKEY_PREFIX = "ed25519:" CONTENT_HASH_PREFIX = "sha256:" def sha256_hex(data: bytes) -> str: return hashlib.sha256(data).hexdigest() def op_id_for(envelope: dict) -> str: """Operation id: hash of the canonical bytes of the *signed* envelope.""" return OP_ID_PREFIX + sha256_hex(canonicalize(envelope)) def actor_id(public_key: str) -> str: """Actor id derived from an ``ed25519:`` public-key string.""" if not _PUBKEY_RE.match(public_key): raise ValueError("not a valid public key string: %r" % public_key) raw = bytes.fromhex(public_key[len(PUBKEY_PREFIX):]) return ACTOR_ID_PREFIX + sha256_hex(raw) def content_hash(data: bytes) -> str: """Content hash for raw evidence bytes.""" return CONTENT_HASH_PREFIX + sha256_hex(data) def is_op_id(s: str) -> bool: return isinstance(s, str) and bool(_OP_ID_RE.match(s)) def is_content_hash(s: str) -> bool: return isinstance(s, str) and bool(_CONTENT_HASH_RE.match(s))