3.3 KiB
seal parity (tai.py -> taicpp)
Namespace detz::seal, sources in src/seal/, one class per .h/.cpp pair, headers prefixed seal_. No dependency beyond the standard library: every primitive tai.py takes from hashlib/hmac/base64/os.urandom is written here.
Algorithm (must match byte for byte)
- master key:
PBKDF2-HMAC-SHA256(passphrase utf-8, salt, 200000 rounds, 32 bytes). enc_key = HMAC(master, "tai-enc-1"),mac_key = HMAC(master, "tai-mac-1"),check = hex(HMAC(master, "tai-check-1")).<home>/.seal:json.dumps({"salt": b64(16 random bytes), "check": check})(mode 0600), created on first use; later opens comparecheck, mismatch isSealError("wrong TAI_PASSPHRASE"), unreadable JSON or salt isSealError("seal file is corrupt"). An empty file counts as absent (pythonif raw).- keystream: concatenated
HMAC(enc_key, nonce || be32(counter)), counter from 0. - envelope:
tai1$+b64(nonce16)$b64(cipher)$b64(HMAC(mac_key, nonce||cipher)).lockof an already sealed text or with a disabled seal returns the text unchanged. unlockerrors:not sealed,sealed value is corrupt(not 4$pieces or bad base64),seal check failed(MAC mismatch, constant time compare).- Empty passphrase: seal disabled, no
.sealis read or written.
Parity table
| tai.py | taicpp | status |
|---|---|---|
SealError |
store::SealError (store/errors.h, shared) |
same messages |
Seal (lock, unlock, safe_unlock, stream, enabled, default_key, load_master) |
seal::Seal : store::SealPort |
ported, safe_unlock inherited from SealPort |
DEFAULT_PASSPHRASE |
Seal::kDefaultPassphrase |
|
resolve_passphrase |
repl::Boot::resolve_passphrase (foundation) |
not duplicated |
rotate_seal(config, old_seal, new) |
SealRotation::rotate(Paths, old, new) |
events, secrets value+meta (with or without profile column), schedules, settings, mcp_servers, *.sys.md, *.session.json, *.bots.json; logs vault/seal_rotated |
default-key upgrade in boot() |
SealFactory::make(home, passphrase, default_key) |
prints seal upgraded from default key to TAI_PASSPHRASE |
| hashlib sha256 / hmac / pbkdf2_hmac / base64 | SealSha256, SealHmac, SealPbkdf2, SealBase64 |
RFC vectors in seal_test |
Gaps (listed, not hidden): SealBase64::decode is strict about data after = where python silently discards it (never produced by either implementation). Invalid UTF-8 inside a sealed value is returned as bytes where python raises UnicodeDecodeError. Rotation paths are derived from the home (memory.db, profiles/) because the factory signature carries only the home. NULL values in events, secrets and schedules are not guarded in python either (python crashes, C++ re-seals an empty string).
Tests
make -f tests/seal.mk test from taicpp/:
tests/seal_test.cpp:test_seal.pyported 1:1 (all 16 cases) againstdetz::store::Store, plus RFC 4231/7914/FIPS vectors, factory upgrade and corrupt.sealcases.tests/seal_diff.pywithtests/seal_probe.cpp: master key and keystream equality, python seals / C++ opens and the reverse,.sealfile written by C++ and opened by python, wrong passphrase, tamper, whole vaults written by one side and decrypted by the other, rotation in both directions, boot upgrade.