Skip to content

hritesh-saha/Aegis-lock

Repository files navigation

aegis-lock

npm version License: MIT

Database-agnostic client-side AES-256-GCM field-level encryption with Contextual Binding, Blind Indexing, and Seamless Key Rotation.

Encrypt sensitive fields before they leave the browser or server. Decrypt after select. Zero plaintext hits the wire or your database. Works with Supabase, MongoDB, or any database via custom adapters.

⚠️ SECURITY WARNING: aegis-lock provides the cryptographic primitives, but you are responsible for your keys. Never hardcode CryptoKey exports in your source code. Always use a secure Key Management Service (KMS) or strict, vault-backed environment variables to inject your keys at runtime.

Requirements

  • Node.js 18+ (or any environment supporting the standard Web Crypto API, such as modern browsers, Cloudflare Workers, or Vercel Edge functions).

Install

npm install aegis-lock

Quick Start — Supabase

import { createClient } from "@supabase/supabase-js";
import { AegisClient, SupabaseAdapter, importKey, generateBidxKey } from "aegis-lock";

const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
const adapter = new SupabaseAdapter(supabase);

// 1. Setup your Key Ring for Key Rotation
// You should load these base64 strings securely from your environment variables
const keyRing = {
  "v1": await importKey(process.env.AEGIS_KEY_V1),
  "v2": await importKey(process.env.AEGIS_KEY_V2) // Your newest key
};
const activeVersion = "v2"; 

const bidxKey = await generateBidxKey(); // Or import your saved blind index key

// 2. Initialize the Client
const aegis = new AegisClient({
  adapter,
  primaryKeyField: "record_id", // REQUIRED: Binds ciphertext to the row to prevent tampering
  encryptedFields: {
    secure_fields: ["encrypted_content", "ssn", "email"]
  },
  bidxFields: {
    secure_fields: ["email"] // Optional: Creates an 'email_bidx' column for searching
  }
}, keyRing, activeVersion, bidxKey);

// 3. Insert — fields are auto-encrypted using the 'v2' key. 
// Note: You MUST provide the primary key application-side!
await aegis.insert("secure_fields", {
  record_id: "uuid-1234-5678", 
  email: "alice@example.com",
  encrypted_content: "Top secret",
});

// 4. Select — Aegis automatically hashes the email to search the 'email_bidx' column securely.
// It will dynamically select the correct key from the KeyRing to decrypt the results.
const { data } = await aegis.select("secure_fields", { 
  column: "email", 
  value: "alice@example.com" 
});

// 5. Update — automatically re-encrypts with a new IV and the active 'v2' key.
await aegis.update("secure_fields", {
  record_id: "uuid-1234-5678", // Must include the primary key!
  email: "new_alice@example.com",
  encrypted_content: "New secret"
});

// 6. Delete — Aegis automatically hashes the email to find the correct row to delete
await aegis.delete("secure_fields", {
  column: "email",
  value: "new_alice@example.com"
});

Quick Start — MongoDB

import { MongoClient } from "mongodb";
import { AegisClient, MongoDBAdapter, importKey } from "aegis-lock";
import { v4 as uuidv4 } from "uuid";

const mongo = new MongoClient("mongodb://localhost:27017");
await mongo.connect();
const adapter = new MongoDBAdapter(mongo.db("myapp"));

const keyRing = { "v1": await importKey(process.env.AEGIS_MASTER_KEY) };

const aegis = new AegisClient({
  adapter,
  primaryKeyField: "_id", 
  encryptedFields: {
    users: ["ssn", "credit_card"]
  }
}, keyRing, "v1");

const newUserId = uuidv4();

// Insert with a client-generated ID
await aegis.insert("users", { 
  _id: newUserId, 
  name: "Alice", 
  ssn: "123-45-6789" 
});

// Select by an unencrypted field
const { data } = await aegis.select("users", { 
  column: "name", 
  value: "Alice" 
});
// data[0].ssn → "123-45-6789" (decrypted)

Custom Adapter

Implement the DatabaseAdapter interface for any database:

import { DatabaseAdapter } from "aegis-lock";

class MyAdapter implements DatabaseAdapter {
  async insert(table: string, data: Record<string, unknown>) {
    // your insert logic
    return { data: [data], error: null };
  }
  async select(table: string, query?) {
    // your select logic
    return { data: [], error: null };
  }
  async update(table: string, data: Record<string, unknown>) {
    // your update logic
    return { data: [data], error: null };
  }
  async delete(table: string, query: { column: string; value: unknown }) {
    // your delete logic
    return { data: [], error: null };
  }
}

API

new AegisClient(config, keyRing, currentKeyVersion, bidxKey?)

  • configAegisConfig object:
    • adapter: any DatabaseAdapter (SupabaseAdapter, MongoDBAdapter, or custom)
    • primaryKeyField: string (The ID field used for cryptographic contextual binding)
    • encryptedFields: Record<tableName, fieldName[]>
    • bidxFields: (Optional) Record<tableName, fieldName[]>
  • keyRingRecord<string, CryptoKey> (A dictionary of all active and historical keys).
  • currentKeyVersionstring (The version tag of the key to use for new encryptions).
  • bidxKey(Optional) CryptoKey from generateBidxKey() or importKey()

AegisClient Methods

  • insert(table, data): Encrypts data and creates blind indexes before saving.
  • select(table, query): Fetches and automatically decrypts data. Intercepts blind index queries.
  • update(table, data): Re-encrypts modified fields with new IVs and updates blind indexes.
  • delete(table, query): Intercepts queries to securely delete by blind-indexed fields.
  • selectRaw(table, query): Bypasses decryption to return raw database records (useful for auditing or migrations).

Adapters

  • new SupabaseAdapter(supabaseClient)
  • new MongoDBAdapter(mongoDb)

Crypto Utilities

  • generateKey() / generateBidxKey()
  • exportKey(key) / importKey(base64)

How It Works (Security Architecture)

  • Web Crypto API (AES-256-GCM): Runs natively in any modern browser or edge runtime without bloated dependencies.
  • Seamless Key Rotation: Aegis-Lock accepts a dictionary of keys. It tags every database payload with the active key's version string. If a key is compromised, you simply introduce a v2 key. New data is secured with v2, while historical data seamlessly decrypts using v1 without a single second of database downtime.
  • Field-Level IVs: Every single encrypted field gets a unique, mathematically random Initialization Vector (IV). The final database payload is formatted as version:iv:ciphertext to prevent keystream reuse attacks.
  • Contextual Binding (AAD): Ciphertexts are cryptographically bound to the row's primaryKeyField. If an attacker copies an encrypted SSN from User A and pastes it into User B's row, the decryption will strictly fail.
  • Blind Indexing (HMAC-SHA256): Because AES-GCM is non-deterministic (the same text encrypts differently every time), you cannot query standard encrypted fields. If configured via bidxFields, Aegis-Lock automatically generates deterministic, memory-aligned HMAC hashes. This allows you to search for exact matches (like looking up an email address) without exposing the plaintext to the database.
  • Database-Agnostic: Swap adapters without changing your core application code.

License

MIT

About

A universal, zero-dependency cryptographic engine for client-side and server-side field-level encryption (AES-256-GCM) with Contextual Binding and Blind Indexing.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors