pamoja-audit
A tamper-evident, hash-chained log; altering, reordering, or dropping a record breaks verification. One capability of pamoja, one memory-safe Rust core with bindings for TypeScript, Python, and C#.
Install
pip install pamoja-audit
from pamoja import audit
This pulls in pamoja-native, the compiled engine, and pamoja-security. pip install pamoja is the whole framework in one package.
Example
The script the test suite runs, spliced here as it ran.
From bindings/python/guides/audit.py:
from pamoja.audit import AuditEntry, AuditLog, verify_chain
from pamoja.core import PamojaError
from pamoja.security import DeviceIdentity
# The controller signs its own log with a provisioned seed and an auditor holds only the
# public half, so a log can be checked anywhere without the device present.
seed = bytes([7]) * 32
keeper = DeviceIdentity.from_seed(seed)
auditor = keeper.public_key
log = AuditLog(keeper)
lit = log.append(b"burner=on")
stopped = log.append(b"burner=off")
print(f"recorded burner=on as record {lit.index} and burner=off as record {stopped.index}")
# Each record hashes its own index, the digest of the record before it, and what it
# carries, so the chain fixes the order as well as the contents.
linked = "carries" if stopped.previous == lit.digest else "does not carry"
print(f"chained record {stopped.index} {linked} the digest of record {lit.index}")
try:
verify_chain(auditor, [lit, stopped])
print("verified the whole log is authentic and in order")
except PamojaError as error:
print(f"rejected {error}")
# Editing a stored record changes the digest its signature covers.
edited = bytearray(stopped.to_bytes())
edited[-1] ^= 0xFF
tampered = AuditEntry.from_bytes(bytes(edited))
try:
verify_chain(auditor, [lit, tampered])
print("an edited record verified, which should never happen")
except PamojaError as error:
print(f"edited caught: {error}")
# Dropping the first record, or swapping the two, leaves a record where its index says it
# cannot be, so a shortened or reordered log is caught as readily as an edited one.
try:
verify_chain(auditor, [stopped])
print("a shortened log verified, which should never happen")
except PamojaError as error:
print(f"shortened caught: {error}")
try:
verify_chain(auditor, [stopped, lit])
print("a reordered log verified, which should never happen")
except PamojaError as error:
print(f"reordered caught: {error}")
# A log checked against another device's key fails on the first signature.
stranger = DeviceIdentity.from_seed(bytes([8]) * 32).public_key
try:
verify_chain(stranger, [lit, stopped])
print("another device's key verified the log, which should never happen")
except PamojaError as error:
print(f"stranger caught: {error}")
# After a restart the controller loads its seed again and resumes from the last record in
# storage, so the log carries on as one chain rather than starting a second.
resumed = AuditLog.resume(DeviceIdentity.from_seed(seed), stopped)
relit = resumed.append(b"burner=on")
try:
verify_chain(auditor, [lit, stopped, relit])
print(f"resumed burner=on again as record {relit.index}, and the whole log still verifies")
except PamojaError as error:
print(f"rejected {error}")
# What a chain cannot show is a record cut from its end, because what is left is still a
# valid chain. The auditor catches it against the last index the device reported.
reported = relit.index
cut = [lit, stopped]
verify_chain(auditor, cut)
ends = cut[-1].index
verdict = "a record is missing" if ends < reported else "nothing is missing"
print(
f"cut the log verifies but ends at record {ends}, "
f"and the device reported record {reported}: {verdict}"
)
The same capability in every language
| Language | Package | Reference |
|---|---|---|
| Rust | pamoja-audit |
reference, docs.rs, install |
| TypeScript | @pamoja/audit |
reference, install |
| Python | pamoja-audit |
reference, install |
| C# | Pamoja.Audit |
reference, install |
Documentation
pamoja.auditreference, every class and function in this module.- The Audit log guide, with the same example in Rust, TypeScript, and C#.
- Every capability, and the install page.
License
MIT
Release files for pamoja-audit 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pamoja_audit-0.2.0.tar.gz | 4.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pamoja_audit-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 9.8 kB
Release files / pamoja_audit-0.2.0.tar.gz
| Download URL | pamoja_audit-0.2.0.tar.gz |
|---|---|
| Size | 4.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d24f088d22970f2cd870c28a7c22391832966e65f6aa79fa9790a146f069f638
|
|
BLAKE2b-256 checksum How to use checksums |
0d4ec9103a7fac070765fa254f4831d1aa1df3cadcadace13befc2f7547f64a1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.15
|
Release files / pamoja_audit-0.2.0-py3-none-any.whl
| Download URL | pamoja_audit-0.2.0-py3-none-any.whl |
|---|---|
| Size | 5.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
938b0152c5b9730ef22daff074946f0d311451fa33d0b1ebe21d652ae9018de3
|
|
BLAKE2b-256 checksum How to use checksums |
fbc7a7595681ec1a26dbd7094a4b5099fe07653e7f4dc43942b07223179fa3d6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.15
|