Hey everyone, Alex here. Welcome back to another edition of Coding with Alex on sysseder.com.
If you've been scrolling through the tech news today, you probably caught a deeply unsettling headline: a police officer is currently under investigation for allegedly using generative AI to "create evidence" in multiple active cases. While the mainstream media is focusing on the sheer ethical horror of this (and rightly so), my developer brain immediately went somewhere else: How do we, as systems architects and software engineers, build systems that make this kind of manipulation impossible?
We are rapidly entering an era where data can no longer be trusted simply because it exists in a database. If a bad actor with access to an LLM or an image generator can forge logs, reports, or media, and inject them into a system, our traditional access control mechanisms are no longer sufficient. As developers, we are the gatekeepers of truth in digital systems. Whether you are building an enterprise document management system, a medical records portal, or a police evidence locker, you need to know how to design for data integrity and non-repudiation.
Today, we’re going to look at the architectural patterns, cryptographic primitives, and code you can use to build tamper-proof systems that stand up to the threat of AI-generated fraud.
The Threat Model: Why Traditional Databases Fall Short
In a standard web application, we rely on the classic CRUD (Create, Read, Update, Delete) paradigm. We secure this with Role-Based Access Control (RBAC). If User A has the "Admin" or "Officer" role, they can write to the database. We trust that whatever they write is accurate.
But generative AI breaks this trust model in two distinct ways:
- Internal Bad Actors: A user with legitimate write access uses an external AI tool to generate highly convincing, fake narrative text, synthetic images, or forged audio, and uploads it as authentic data.
- Data Injection/Mutation: Once the data is in our database, how do we prove to an external auditor (or a court of law) that the data wasn't altered post-upload by a database administrator or a rogue process?
To solve this, we must move away from simple database state storage and implement architectural designs rooted in cryptographic provenance. Specifically, we need to leverage digital signatures, cryptographic hashing, append-only ledgers, and write-once-read-many (WORM) storage.
Step 1: Establishing Cryptographic Provenance at the Edge
If we want to prove that a piece of data (like an incident report) hasn't been altered since the moment of its creation, we must sign it at the source. We cannot rely on the server generating a timestamp and saying "trust me."
The standard way to handle this is using public-key cryptography (asymmetric encryption). The client (e.g., a device, or a specific authenticated user session holding a hardware-backed key) signs the payload. Let's look at how we can implement a robust signing and verification pipeline in Node.js/TypeScript using the native crypto module.
Generating and Verifying Digital Signatures
First, let's write a utility to verify that a payload has been signed by a specific, trusted private key and has not been modified since that signature was generated.
const crypto = require('crypto');
// Generate a key pair for testing
function generateKeyPair() {
return crypto.generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: { type: 'spki', format: 'pem' },
privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
});
}
// Sign the data using the creator's private key
function signData(payload, privateKey) {
const sign = crypto.createSign('SHA256');
sign.update(JSON.stringify(payload));
sign.end();
return sign.sign(privateKey, 'hex');
}
// Verify that the payload matches the signature and the public key
function verifyData(payload, signature, publicKey) {
const verify = crypto.createVerify('SHA256');
verify.update(JSON.stringify(payload));
verify.end();
return verify.verify(publicKey, signature, 'hex');
}
// Let's run a test case
const { publicKey, privateKey } = generateKeyPair();
const evidencePayload = {
caseId: "CASE-90210",
officerId: "BADGE-404",
timestamp: new Date().toISOString(),
reportText: "Suspect was found in possession of unauthorized local LLM fine-tuning scripts."
};
const signature = signData(evidencePayload, privateKey);
console.log(`Generated Signature: ${signature.substring(0, 40)}...`);
const isValid = verifyData(evidencePayload, signature, publicKey);
console.log(`Is the data authentic? ${isValid}`); // Outputs: true
By enforcing client-side signing or hardware-token signing (such as YubiKeys or mobile Secure Enclaves), we ensure that even if our database is compromised, an attacker cannot forge signatures without access to the physical private keys.
Step 2: Designing an Append-Only Hash Chain (Merkle Trees)
Signing individual documents is great, but how do we prove that a series of events or reports has not been reordered, deleted, or had new entries covertly inserted? If an officer generates five fake reports and backdates them, how do we catch it?
This is where we take a page out of git's playbook (and blockchain, without the web3 hype). We link our database records together in a cryptographic hash chain. Every new record must contain the cryptographic hash of the previous record.
If any historical record is modified, deleted, or inserted, the hash chain breaks instantly.
A Simple Hash Chain Implementation
Let's look at a basic implementation of an append-only ledger in TypeScript. Every time we insert a "block" of evidence, we compute its hash based on its content and the previous block's hash.
const crypto = require('crypto');
interface LedgerBlock {
index: number;
timestamp: string;
data: any;
previousHash: string;
hash: string;
}
class EvidenceLedger {
private chain: LedgerBlock[] = [];
constructor() {
// Create the genesis block
this.chain.push(this.createBlock(0, "0", { info: "Genesis Block" }));
}
private calculateHash(index: number, previousHash: string, timestamp: string, data: any): string {
return crypto
.createHash('sha256')
.update(index + previousHash + timestamp + JSON.stringify(data))
.digest('hex');
}
private createBlock(index: number, previousHash: string, data: any): LedgerBlock {
const timestamp = new Date().toISOString();
const hash = this.calculateHash(index, previousHash, timestamp, data);
return { index, timestamp, data, previousHash, hash };
}
public addBlock(data: any): LedgerBlock {
const previousBlock = this.chain[this.chain.length - 1];
const newBlock = this.createBlock(previousBlock.index + 1, previousBlock.hash, data);
this.chain.push(newBlock);
return newBlock;
}
public validateChain(): boolean {
for (let i = 1; i < this.chain.length; i++) {
const current = this.chain[i];
const previous = this.chain[i - 1];
// 1. Recalculate hash of current block and verify
const reconstructedHash = this.calculateHash(current.index, current.previousHash, current.timestamp, current.data);
if (current.hash !== reconstructedHash) {
console.error(`Hash mismatch at index ${i}`);
return false;
}
// 2. Verify link to previous block
if (current.previousHash !== previous.hash) {
console.error(`Link broken between index ${i} and ${i - 1}`);
return false;
}
}
return true;
}
public getChain() { return this.chain; }
}
// Execution flow
const ledger = new EvidenceLedger();
ledger.addBlock({ report: "Initial assessment at crime scene." });
ledger.addBlock({ report: "AI-generated suspect statement uploaded." });
console.log("Is ledger valid?", ledger.validateChain()); // true
// Attempting to modify a past record
const chainData = ledger.getChain();
chainData[1].data.report = "Maliciously altered statement text.";
console.log("Is ledger valid after tamper attempt?", ledger.validateChain()); // false!
If this ledger is backed by a cloud database like AWS QLDB (Quantum Ledger Database) or stored on WORM (Write Once, Read Many) S3 buckets with Object Lock enabled, it becomes physically impossible for even a database admin to alter past records without leaving a flashing red light for compliance auditors.
Step 3: AI-Generated Content Attribution (Watermarking & Metadata)
While cryptographic hashing ensures that data isn't modified after it enters our system, how do we address the problem of AI-generated fakes being created before upload? This is where standardizing on image/media verification protocols comes in.
As developers, we should look into implementing the C2PA (Coalition for Content Provenance and Authenticity) standard. C2PA allows cameras, recording devices, and software (like Photoshop or even LLMs) to embed secure, cryptographically signed metadata directly into files.
When an image is uploaded to your system, instead of just accepting the raw bytes, your backend should parse and verify the C2PA manifest to check the asset’s "history of edits." If a photo was captured on an iPhone, it will have a signature from Apple’s hardware enclave. If it was generated or modified by Midjourney or Photoshop Generative Fill, the C2PA manifest will explicitly show that an AI tool touched the image.
What a C2PA verification workflow looks like:
- The user uploads an image/audio file.
- Your backend parses the file's metadata blocks looking for the JUMBF (Joint Universal Markup Framework) structure.
- The system verifies the cryptographic signature of the certifying authority (e.g., Leica, Apple, Adobe).
- If the signature is missing or indicates synthetic generation, the asset is flagged in the database with a
is_synthetic: trueorunverified_provenance: trueflag.
Conclusion: The New Era of Defensive Engineering
The headline about police officers using AI to forge evidence is a terrifying wake-up call. It proves that the line between physical reality and digital fabrication has completely dissolved. We can no longer rely on simple "authorized user" permissions to guarantee the authenticity of our data.
By designing our systems with edge digital signatures, append-only cryptographic ledgers, and standardizing on content provenance protocols like C2PA, we can build software that resists both external hackers and internal bad actors. This isn't just a security best practice anymore; it is a fundamental requirement for maintaining digital trust.
What are your thoughts? Have you ever implemented a cryptographic ledger or worked with C2PA metadata in your apps? Let’s chat in the comments below!
Until next time, keep your keys private and your ledgers append-only.
— Alex