Pamoja.Audit
0.2.0
dotnet add package Pamoja.Audit --version 0.2.0
NuGet\Install-Package Pamoja.Audit -Version 0.2.0
<PackageReference Include="Pamoja.Audit" Version="0.2.0" />
<PackageVersion Include="Pamoja.Audit" Version="0.2.0" />
<PackageReference Include="Pamoja.Audit" />
paket add Pamoja.Audit --version 0.2.0
#r "nuget: Pamoja.Audit, 0.2.0"
#:package Pamoja.Audit@0.2.0
#addin nuget:?package=Pamoja.Audit&version=0.2.0
#tool nuget:?package=Pamoja.Audit&version=0.2.0
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
dotnet add package Pamoja.Audit
using Pamoja.Audit;
This pulls in Pamoja.Native, the compiled engine, and Pamoja.Codec and Pamoja.Security. dotnet add package Pamoja is the whole framework in one package.
Example
The guide project's example, spliced here as it ran in CI.
From bindings/dotnet/samples/Pamoja.Guides/AuditGuide.cs:
// 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.
byte[] seed = new byte[32];
Array.Fill(seed, (byte)7);
using var keeper = new DeviceIdentity(seed);
byte[] auditor = keeper.PublicKey;
using var log = new AuditLog(keeper);
using AuditEntry lit = log.Append("burner=on"u8);
using AuditEntry stopped = log.Append("burner=off"u8);
Console.WriteLine($"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.
string linked = stopped.Previous.SequenceEqual(lit.Digest) ? "carries" : "does not carry";
Console.WriteLine($"chained record {stopped.Index} {linked} the digest of record {lit.Index}");
try
{
Audit.VerifyChain(auditor, [lit, stopped]);
Console.WriteLine("verified the whole log is authentic and in order");
}
catch (PamojaException error)
{
Console.WriteLine($"rejected {error.Message}");
}
// Editing a stored record changes the digest its signature covers.
byte[] edited = stopped.ToBytes();
edited[^1] ^= 0xFF;
using AuditEntry tampered = AuditEntry.FromBytes(edited);
try
{
Audit.VerifyChain(auditor, [lit, tampered]);
Console.WriteLine("an edited record verified, which should never happen");
}
catch (PamojaException error)
{
Console.WriteLine($"edited caught: {error.Message}");
}
// 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
{
Audit.VerifyChain(auditor, [stopped]);
Console.WriteLine("a shortened log verified, which should never happen");
}
catch (PamojaException error)
{
Console.WriteLine($"shortened caught: {error.Message}");
}
try
{
Audit.VerifyChain(auditor, [stopped, lit]);
Console.WriteLine("a reordered log verified, which should never happen");
}
catch (PamojaException error)
{
Console.WriteLine($"reordered caught: {error.Message}");
}
// A log checked against another device's key fails on the first signature.
byte[] strangerSeed = new byte[32];
Array.Fill(strangerSeed, (byte)8);
using var strangerDevice = new DeviceIdentity(strangerSeed);
byte[] stranger = strangerDevice.PublicKey;
try
{
Audit.VerifyChain(stranger, [lit, stopped]);
Console.WriteLine("another device's key verified the log, which should never happen");
}
catch (PamojaException error)
{
Console.WriteLine($"stranger caught: {error.Message}");
}
// 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.
using var restarted = new DeviceIdentity(seed);
using var resumed = AuditLog.Resume(restarted, stopped);
using AuditEntry relit = resumed.Append("burner=on"u8);
try
{
Audit.VerifyChain(auditor, [lit, stopped, relit]);
Console.WriteLine($"resumed burner=on again as record {relit.Index}, and the whole log still verifies");
}
catch (PamojaException error)
{
Console.WriteLine($"rejected {error.Message}");
}
// 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.
ulong reported = relit.Index;
AuditEntry[] cut = [lit, stopped];
Audit.VerifyChain(auditor, cut);
ulong ends = cut[^1].Index;
string verdict = ends < reported ? "a record is missing" : "nothing is missing";
Console.WriteLine(
$"cut the log verifies but ends at record {ends}, 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 type in this namespace.- The Audit log guide, with the same example in Rust, TypeScript, and Python.
- Every capability, and the install page.
License
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Pamoja.Codec (>= 0.2.0)
- Pamoja.Native (>= 0.2.0)
- Pamoja.Security (>= 0.2.0)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Pamoja.Audit:
| Package | Downloads |
|---|---|
|
Pamoja
The whole pamoja framework in one package: every capability of one memory-safe Rust core, behind an idiomatic C# facade, for IoT, robotics, and drones. |
|
|
Pamoja.Trust
Trust and operation: Proving what a node did, saying it in confidence, fixing it in the field, and deciding how often it can afford to do any of that. |
GitHub repositories
This package is not used by any popular GitHub repositories.