idea195-genomeloom-verifiab.../src/genomeloom/signature.py

95 lines
3.1 KiB
Python

"""Behavioral signature generator.
This module implements a compact, deterministic behavioral fingerprint for a
compiled genome bundle. It expects two inputs:
- syscall_sequence: an iterable of syscall/call names observed or statically
extracted from the artifact (e.g. ["open","read","write","socket"]).
- static_imports: an iterable of import or feature strings (e.g. ["wasi::fd_read"]).
The generator computes n-gram sketches over the syscall sequence and a
feature-bit vector for imports, then produces a keyed HMAC-SHA256 over a
canonical JSON payload. The result is a compact hex string representing the
fingerprint.
The implementation aims to be small, deterministic, and fast so it can be used
in constrained admission controllers.
"""
from __future__ import annotations
import hmac
import hashlib
import json
from collections import Counter, deque
from typing import Iterable, List, Tuple
DEFAULT_KEY = b"GenomeLoom::BehavioralSig::v1" # stable keyed hash
def _ngrams(seq: List[str], n: int) -> Iterable[Tuple[str, ...]]:
if n <= 0:
return []
dq = deque(maxlen=n)
out = []
for item in seq:
dq.append(item)
if len(dq) == n:
out.append(tuple(dq))
return out
def generate_behavioral_signature(
syscall_sequence: Iterable[str],
static_imports: Iterable[str] = (),
ngram_ns: Tuple[int, ...] = (1, 2, 3),
top_k: int = 32,
key: bytes = DEFAULT_KEY,
) -> str:
"""Generate a deterministic behavioral signature.
Returns a hex string (64 chars) representing HMAC-SHA256 over a canonical
payload. The payload contains:
- top n-gram counts (sorted)
- sorted list of imports
Parameters:
- syscall_sequence: iterable of syscall/call names
- static_imports: iterable of import feature strings
- ngram_ns: tuple sizes for n-grams
- top_k: how many n-grams to keep
- key: keyed hash secret (should be globally stable for the operator)
"""
seq = list(syscall_sequence)
# collect n-gram counts
counts = Counter()
for n in ngram_ns:
for ng in _ngrams(seq, n):
counts["+".join(ng)] += 1
# pick top-k by count then lexicographic for determinism
top = sorted(counts.items(), key=lambda kv: (-kv[1], kv[0]))[:top_k]
top_list = [(k, v) for k, v in top]
imports_sorted = sorted(set(static_imports))
payload = {"ngrams": top_list, "imports": imports_sorted}
canon = json.dumps(payload, separators=(",", ":"), sort_keys=True)
mac = hmac.new(key, canon.encode("utf-8"), hashlib.sha256).hexdigest()
return mac
def signature_summary(syscall_sequence: Iterable[str], static_imports: Iterable[str] = ()) -> dict:
"""Return a structured summary used in certificates (not the HMAC).
This includes top n-grams and feature import bits. Useful for human
inspection and sparse indexing.
"""
seq = list(syscall_sequence)
counts = Counter()
for n in (1, 2, 3):
for ng in _ngrams(seq, n):
counts["+".join(ng)] += 1
top = sorted(counts.items(), key=lambda kv: (-kv[1], kv[0]))[:32]
return {"ngrams": top, "imports": sorted(set(static_imports))}