aegis-lock 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +125 -31
- package/dist/adapters/mongodb.d.ts +19 -14
- package/dist/adapters/mongodb.d.ts.map +1 -1
- package/dist/adapters/mongodb.js +28 -14
- package/dist/adapters/mongodb.js.map +1 -1
- package/dist/adapters/supabase.d.ts +15 -2
- package/dist/adapters/supabase.d.ts.map +1 -1
- package/dist/adapters/supabase.js +22 -2
- package/dist/adapters/supabase.js.map +1 -1
- package/dist/client.d.ts +66 -6
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +143 -24
- package/dist/client.js.map +1 -1
- package/dist/crypto.d.ts +48 -2
- package/dist/crypto.d.ts.map +1 -1
- package/dist/crypto.js +118 -17
- package/dist/crypto.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +13 -3
- package/dist/types.d.ts.map +1 -1
- package/package.json +7 -2
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 API-Alchemist
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
# aegis-lock
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://badge.fury.io/js/aegis-lock)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
**Database-agnostic client-side AES-256-GCM field-level encryption with Contextual Binding and Blind Indexing.**
|
|
4
7
|
|
|
5
8
|
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.
|
|
6
9
|
|
|
10
|
+
> **⚠️ 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.
|
|
11
|
+
|
|
12
|
+
## Requirements
|
|
13
|
+
* Node.js 18+ (or any environment supporting the standard Web Crypto API, such as modern browsers, Cloudflare Workers, or Vercel Edge functions).
|
|
14
|
+
|
|
7
15
|
## Install
|
|
8
16
|
|
|
9
17
|
```bash
|
|
@@ -14,26 +22,54 @@ npm install aegis-lock
|
|
|
14
22
|
|
|
15
23
|
```typescript
|
|
16
24
|
import { createClient } from "@supabase/supabase-js";
|
|
17
|
-
import { AegisClient, SupabaseAdapter, generateKey, exportKey } from "aegis-lock";
|
|
25
|
+
import { AegisClient, SupabaseAdapter, generateKey, generateBidxKey, exportKey } from "aegis-lock";
|
|
18
26
|
|
|
19
27
|
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
|
|
20
28
|
const adapter = new SupabaseAdapter(supabase);
|
|
21
29
|
|
|
30
|
+
// Generate your master encryption key and searchable blind index key
|
|
22
31
|
const key = await generateKey();
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
32
|
+
const bidxKey = await generateBidxKey();
|
|
33
|
+
|
|
34
|
+
// Export and save these securely! e.g., await exportKey(key)
|
|
35
|
+
|
|
36
|
+
const aegis = new AegisClient({
|
|
37
|
+
adapter,
|
|
38
|
+
primaryKeyField: "record_id", // REQUIRED: Binds ciphertext to the row to prevent tampering
|
|
39
|
+
encryptedFields: {
|
|
40
|
+
secure_fields: ["encrypted_content", "ssn", "email"]
|
|
41
|
+
},
|
|
42
|
+
bidxFields: {
|
|
43
|
+
secure_fields: ["email"] // Optional: Creates an 'email_bidx' column for searching
|
|
44
|
+
}
|
|
45
|
+
}, key, bidxKey);
|
|
28
46
|
|
|
29
|
-
// Insert — fields are auto-encrypted
|
|
47
|
+
// 1. Insert — fields are auto-encrypted.
|
|
48
|
+
// Note: You MUST provide the primary key application-side!
|
|
30
49
|
await aegis.insert("secure_fields", {
|
|
31
|
-
record_id: "
|
|
50
|
+
record_id: "uuid-1234-5678",
|
|
51
|
+
email: "alice@example.com",
|
|
32
52
|
encrypted_content: "Top secret",
|
|
33
53
|
});
|
|
34
54
|
|
|
35
|
-
// Select —
|
|
36
|
-
const { data } = await aegis.select("secure_fields"
|
|
55
|
+
// 2. Select — Aegis automatically hashes the email to search the 'email_bidx' column securely
|
|
56
|
+
const { data } = await aegis.select("secure_fields", {
|
|
57
|
+
column: "email",
|
|
58
|
+
value: "alice@example.com"
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// 3. Update — automatically re-encrypts with a new IV and updates the blind index
|
|
62
|
+
await aegis.update("secure_fields", {
|
|
63
|
+
record_id: "uuid-1234-5678", // Must include the primary key!
|
|
64
|
+
email: "new_alice@example.com",
|
|
65
|
+
encrypted_content: "New secret"
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// 4. Delete — Aegis automatically hashes the email to find the correct row to delete
|
|
69
|
+
await aegis.delete("secure_fields", {
|
|
70
|
+
column: "email",
|
|
71
|
+
value: "new_alice@example.com"
|
|
72
|
+
});
|
|
37
73
|
```
|
|
38
74
|
|
|
39
75
|
## Quick Start — MongoDB
|
|
@@ -41,6 +77,7 @@ const { data } = await aegis.select("secure_fields");
|
|
|
41
77
|
```typescript
|
|
42
78
|
import { MongoClient } from "mongodb";
|
|
43
79
|
import { AegisClient, MongoDBAdapter, generateKey } from "aegis-lock";
|
|
80
|
+
import { v4 as uuidv4 } from "uuid";
|
|
44
81
|
|
|
45
82
|
const mongo = new MongoClient("mongodb://localhost:27017");
|
|
46
83
|
await mongo.connect();
|
|
@@ -48,13 +85,42 @@ const adapter = new MongoDBAdapter(mongo.db("myapp"));
|
|
|
48
85
|
|
|
49
86
|
const key = await generateKey();
|
|
50
87
|
|
|
51
|
-
const aegis = new AegisClient(
|
|
52
|
-
|
|
88
|
+
const aegis = new AegisClient({
|
|
89
|
+
adapter,
|
|
90
|
+
primaryKeyField: "_id",
|
|
91
|
+
encryptedFields: {
|
|
92
|
+
users: ["ssn", "credit_card"]
|
|
93
|
+
}
|
|
94
|
+
}, key);
|
|
95
|
+
|
|
96
|
+
const newUserId = uuidv4();
|
|
97
|
+
|
|
98
|
+
// 1. Insert with a client-generated ID
|
|
99
|
+
await aegis.insert("users", {
|
|
100
|
+
_id: newUserId,
|
|
101
|
+
name: "Alice",
|
|
102
|
+
ssn: "123-45-6789"
|
|
53
103
|
});
|
|
54
104
|
|
|
55
|
-
|
|
56
|
-
const { data } = await aegis.select("users", {
|
|
105
|
+
// 2. Select by an unencrypted field
|
|
106
|
+
const { data } = await aegis.select("users", {
|
|
107
|
+
column: "name",
|
|
108
|
+
value: "Alice"
|
|
109
|
+
});
|
|
57
110
|
// data[0].ssn → "123-45-6789" (decrypted)
|
|
111
|
+
|
|
112
|
+
// 3. Update — automatically re-encrypts the SSN with a new IV
|
|
113
|
+
await aegis.update("users", {
|
|
114
|
+
_id: newUserId, // Must include the primary key!
|
|
115
|
+
name: "Alice",
|
|
116
|
+
ssn: "999-99-9999"
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// 4. Delete — Removes the record from the database
|
|
120
|
+
await aegis.delete("users", {
|
|
121
|
+
column: "_id",
|
|
122
|
+
value: newUserId
|
|
123
|
+
});
|
|
58
124
|
```
|
|
59
125
|
|
|
60
126
|
## Custom Adapter
|
|
@@ -62,7 +128,7 @@ const { data } = await aegis.select("users", { column: "name", value: "Alice" })
|
|
|
62
128
|
Implement the `DatabaseAdapter` interface for any database:
|
|
63
129
|
|
|
64
130
|
```typescript
|
|
65
|
-
import { DatabaseAdapter
|
|
131
|
+
import { DatabaseAdapter } from "aegis-lock";
|
|
66
132
|
|
|
67
133
|
class MyAdapter implements DatabaseAdapter {
|
|
68
134
|
async insert(table: string, data: Record<string, unknown>) {
|
|
@@ -73,31 +139,59 @@ class MyAdapter implements DatabaseAdapter {
|
|
|
73
139
|
// your select logic
|
|
74
140
|
return { data: [], error: null };
|
|
75
141
|
}
|
|
142
|
+
async update(table: string, data: Record<string, unknown>) {
|
|
143
|
+
// your update logic
|
|
144
|
+
return { data: [data], error: null };
|
|
145
|
+
}
|
|
146
|
+
async delete(table: string, query: { column: string; value: unknown }) {
|
|
147
|
+
// your delete logic
|
|
148
|
+
return { data: [], error: null };
|
|
149
|
+
}
|
|
76
150
|
}
|
|
77
151
|
```
|
|
78
152
|
|
|
79
153
|
## API
|
|
80
154
|
|
|
81
|
-
### `new AegisClient(
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
155
|
+
### `new AegisClient(config, key, bidxKey?)`
|
|
156
|
+
|
|
157
|
+
* `config` — `AegisConfig` object:
|
|
158
|
+
* `adapter`: any `DatabaseAdapter` (`SupabaseAdapter`, `MongoDBAdapter`, or custom)
|
|
159
|
+
* `primaryKeyField`: `string` (The ID field used for cryptographic contextual binding)
|
|
160
|
+
* `encryptedFields`: `Record<tableName, fieldName[]>`
|
|
161
|
+
* `bidxFields`: *(Optional)* `Record<tableName, fieldName[]>`
|
|
162
|
+
* `key` — `CryptoKey` from `generateKey()` or `importKey()`
|
|
163
|
+
* `bidxKey` — *(Optional)* `CryptoKey` from `generateBidxKey()` or `importKey()`
|
|
164
|
+
|
|
165
|
+
### `AegisClient` Methods
|
|
166
|
+
|
|
167
|
+
* `insert(table, data)`: Encrypts data and creates blind indexes before saving.
|
|
168
|
+
* `select(table, query)`: Fetches and automatically decrypts data. Intercepts blind index queries.
|
|
169
|
+
* `update(table, data)`: Re-encrypts modified fields with new IVs and updates blind indexes.
|
|
170
|
+
* `delete(table, query)`: Intercepts queries to securely delete by blind-indexed fields.
|
|
171
|
+
* `selectRaw(table, query)`: Bypasses decryption to return raw database records (useful for auditing or migrations).
|
|
85
172
|
|
|
86
173
|
### Adapters
|
|
87
|
-
- `new SupabaseAdapter(supabaseClient)`
|
|
88
|
-
- `new MongoDBAdapter(mongoDb)`
|
|
89
174
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
175
|
+
* `new SupabaseAdapter(supabaseClient)`
|
|
176
|
+
* `new MongoDBAdapter(mongoDb)`
|
|
177
|
+
|
|
178
|
+
### Crypto Utilities
|
|
179
|
+
|
|
180
|
+
* `generateKey()` / `generateBidxKey()`
|
|
181
|
+
* `exportKey(key)` / `importKey(base64)`
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## How It Works (Security Architecture)
|
|
186
|
+
|
|
93
187
|
|
|
94
|
-
## How It Works
|
|
95
188
|
|
|
96
|
-
|
|
97
|
-
-
|
|
98
|
-
|
|
99
|
-
-
|
|
189
|
+
* **Web Crypto API (AES-256-GCM):** Runs natively in any modern browser or edge runtime.
|
|
190
|
+
* **Field-Level IVs:** Every single encrypted field gets a unique, mathematically random Initialization Vector (IV) stored as a composite string (`iv:ciphertext`) to prevent keystream reuse attacks.
|
|
191
|
+
* **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.
|
|
192
|
+
* **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.
|
|
193
|
+
* **Database-agnostic:** Swap adapters without changing your core application code.
|
|
100
194
|
|
|
101
195
|
## License
|
|
102
196
|
|
|
103
|
-
MIT
|
|
197
|
+
MIT
|
|
@@ -1,19 +1,8 @@
|
|
|
1
1
|
import type { DatabaseAdapter } from "../types";
|
|
2
2
|
/**
|
|
3
|
-
* MongoDB adapter for aegis-
|
|
3
|
+
* MongoDB adapter for aegis-lock.
|
|
4
4
|
*
|
|
5
|
-
* Works with any MongoDB client that exposes a `db()` method
|
|
6
|
-
* `mongodb` Node.js driver). Pass the Db instance directly.
|
|
7
|
-
*
|
|
8
|
-
* Usage:
|
|
9
|
-
* ```ts
|
|
10
|
-
* import { MongoClient } from "mongodb";
|
|
11
|
-
* import { MongoDBAdapter } from "aegis-mern/adapters/mongodb";
|
|
12
|
-
*
|
|
13
|
-
* const client = new MongoClient(uri);
|
|
14
|
-
* await client.connect();
|
|
15
|
-
* const adapter = new MongoDBAdapter(client.db("mydb"));
|
|
16
|
-
* ```
|
|
5
|
+
* Works with any MongoDB client that exposes a `db()` method.
|
|
17
6
|
*/
|
|
18
7
|
export declare class MongoDBAdapter implements DatabaseAdapter {
|
|
19
8
|
private db;
|
|
@@ -38,7 +27,23 @@ export declare class MongoDBAdapter implements DatabaseAdapter {
|
|
|
38
27
|
data: null;
|
|
39
28
|
error: unknown;
|
|
40
29
|
}>;
|
|
41
|
-
|
|
30
|
+
update(table: string, data: Record<string, unknown>): Promise<{
|
|
31
|
+
data: Record<string, unknown>[];
|
|
32
|
+
error: null;
|
|
33
|
+
} | {
|
|
34
|
+
data: null;
|
|
35
|
+
error: unknown;
|
|
36
|
+
}>;
|
|
37
|
+
delete(table: string, query: {
|
|
38
|
+
column: string;
|
|
39
|
+
value: unknown;
|
|
40
|
+
}): Promise<{
|
|
41
|
+
data: never[];
|
|
42
|
+
error: null;
|
|
43
|
+
} | {
|
|
44
|
+
data: null;
|
|
45
|
+
error: unknown;
|
|
46
|
+
}>;
|
|
42
47
|
get database(): any;
|
|
43
48
|
}
|
|
44
49
|
//# sourceMappingURL=mongodb.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mongodb.d.ts","sourceRoot":"","sources":["../../adapters/mongodb.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhD
|
|
1
|
+
{"version":3,"file":"mongodb.d.ts","sourceRoot":"","sources":["../../adapters/mongodb.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhD;;;;GAIG;AACH,qBAAa,cAAe,YAAW,eAAe;IACpD,OAAO,CAAC,EAAE,CAAM;gBAEJ,EAAE,EAAE,GAAG;IAIb,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;;;IAWnD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;cAY7D,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE;;;;;;IAM9C,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;IAqBnD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE;;;;;;;IAWrE,IAAI,QAAQ,QAEX;CACF"}
|
package/dist/adapters/mongodb.js
CHANGED
|
@@ -1,18 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* MongoDB adapter for aegis-
|
|
2
|
+
* MongoDB adapter for aegis-lock.
|
|
3
3
|
*
|
|
4
|
-
* Works with any MongoDB client that exposes a `db()` method
|
|
5
|
-
* `mongodb` Node.js driver). Pass the Db instance directly.
|
|
6
|
-
*
|
|
7
|
-
* Usage:
|
|
8
|
-
* ```ts
|
|
9
|
-
* import { MongoClient } from "mongodb";
|
|
10
|
-
* import { MongoDBAdapter } from "aegis-mern/adapters/mongodb";
|
|
11
|
-
*
|
|
12
|
-
* const client = new MongoClient(uri);
|
|
13
|
-
* await client.connect();
|
|
14
|
-
* const adapter = new MongoDBAdapter(client.db("mydb"));
|
|
15
|
-
* ```
|
|
4
|
+
* Works with any MongoDB client that exposes a `db()` method.
|
|
16
5
|
*/
|
|
17
6
|
export class MongoDBAdapter {
|
|
18
7
|
constructor(db) {
|
|
@@ -47,7 +36,32 @@ export class MongoDBAdapter {
|
|
|
47
36
|
return { data: null, error };
|
|
48
37
|
}
|
|
49
38
|
}
|
|
50
|
-
|
|
39
|
+
async update(table, data) {
|
|
40
|
+
try {
|
|
41
|
+
const collection = this.db.collection(table);
|
|
42
|
+
// Auto-detect the primary key field for MongoDB (usually _id or id)
|
|
43
|
+
const pkField = data._id ? '_id' : (data.id ? 'id' : null);
|
|
44
|
+
if (!pkField)
|
|
45
|
+
throw new Error("MongoDB Adapter: Missing '_id' or 'id' in data payload for update.");
|
|
46
|
+
const { [pkField]: pkValue, ...updateData } = data;
|
|
47
|
+
await collection.updateOne({ [pkField]: pkValue }, { $set: updateData });
|
|
48
|
+
return { data: [data], error: null };
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
return { data: null, error };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async delete(table, query) {
|
|
55
|
+
try {
|
|
56
|
+
const collection = this.db.collection(table);
|
|
57
|
+
const filter = { [query.column]: query.value };
|
|
58
|
+
await collection.deleteMany(filter);
|
|
59
|
+
return { data: [], error: null };
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
return { data: null, error };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
51
65
|
get database() {
|
|
52
66
|
return this.db;
|
|
53
67
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mongodb.js","sourceRoot":"","sources":["../../adapters/mongodb.ts"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"mongodb.js","sourceRoot":"","sources":["../../adapters/mongodb.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { SupabaseClient } from "@supabase/supabase-js";
|
|
2
2
|
import type { DatabaseAdapter } from "../types";
|
|
3
|
-
/** Supabase adapter for aegis-
|
|
3
|
+
/** Supabase adapter for aegis-lock. */
|
|
4
4
|
export declare class SupabaseAdapter implements DatabaseAdapter {
|
|
5
5
|
private supabase;
|
|
6
6
|
constructor(supabase: SupabaseClient);
|
|
@@ -16,7 +16,20 @@ export declare class SupabaseAdapter implements DatabaseAdapter {
|
|
|
16
16
|
data: Record<string, unknown>[] | null;
|
|
17
17
|
error: import("@supabase/postgrest-js").PostgrestError | null;
|
|
18
18
|
}>;
|
|
19
|
-
|
|
19
|
+
update(table: string, data: Record<string, unknown>): Promise<{
|
|
20
|
+
data: null;
|
|
21
|
+
error: Error;
|
|
22
|
+
} | {
|
|
23
|
+
data: Record<string, unknown>[] | null;
|
|
24
|
+
error: import("@supabase/postgrest-js").PostgrestError | null;
|
|
25
|
+
}>;
|
|
26
|
+
delete(table: string, query: {
|
|
27
|
+
column: string;
|
|
28
|
+
value: unknown;
|
|
29
|
+
}): Promise<{
|
|
30
|
+
data: Record<string, unknown>[] | null;
|
|
31
|
+
error: import("@supabase/postgrest-js").PostgrestError | null;
|
|
32
|
+
}>;
|
|
20
33
|
get client(): SupabaseClient<any, "public", "public", any, any>;
|
|
21
34
|
}
|
|
22
35
|
//# sourceMappingURL=supabase.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"supabase.d.ts","sourceRoot":"","sources":["../../adapters/supabase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhD,uCAAuC;AACvC,qBAAa,eAAgB,YAAW,eAAe;IACrD,OAAO,CAAC,QAAQ,CAAiB;gBAErB,QAAQ,EAAE,cAAc;IAI9B,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;cAE9B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI;;;IAGrD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;cAS/D,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI;;;
|
|
1
|
+
{"version":3,"file":"supabase.d.ts","sourceRoot":"","sources":["../../adapters/supabase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhD,uCAAuC;AACvC,qBAAa,eAAgB,YAAW,eAAe;IACrD,OAAO,CAAC,QAAQ,CAAiB;gBAErB,QAAQ,EAAE,cAAc;IAI9B,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;cAE9B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI;;;IAGrD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;cAS/D,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI;;;IAGnD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;cAc9B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI;;;IAGrD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE;cAO1C,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI;;;IAG3D,IAAI,MAAM,sDAET;CACF"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Supabase adapter for aegis-
|
|
1
|
+
/** Supabase adapter for aegis-lock. */
|
|
2
2
|
export class SupabaseAdapter {
|
|
3
3
|
constructor(supabase) {
|
|
4
4
|
this.supabase = supabase;
|
|
@@ -18,7 +18,27 @@ export class SupabaseAdapter {
|
|
|
18
18
|
const { data, error } = await q;
|
|
19
19
|
return { data: data, error };
|
|
20
20
|
}
|
|
21
|
-
|
|
21
|
+
async update(table, data) {
|
|
22
|
+
// Auto-detect primary key (Supabase usually uses 'id')
|
|
23
|
+
const pkField = data.id ? 'id' : (data._id ? '_id' : null);
|
|
24
|
+
if (!pkField) {
|
|
25
|
+
return { data: null, error: new Error("Supabase Adapter: Missing 'id' in data payload for update.") };
|
|
26
|
+
}
|
|
27
|
+
const { data: result, error } = await this.supabase
|
|
28
|
+
.from(table)
|
|
29
|
+
.update(data)
|
|
30
|
+
.eq(pkField, data[pkField])
|
|
31
|
+
.select();
|
|
32
|
+
return { data: result, error };
|
|
33
|
+
}
|
|
34
|
+
async delete(table, query) {
|
|
35
|
+
const { data: result, error } = await this.supabase
|
|
36
|
+
.from(table)
|
|
37
|
+
.delete()
|
|
38
|
+
.eq(query.column, query.value)
|
|
39
|
+
.select();
|
|
40
|
+
return { data: result, error };
|
|
41
|
+
}
|
|
22
42
|
get client() {
|
|
23
43
|
return this.supabase;
|
|
24
44
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"supabase.js","sourceRoot":"","sources":["../../adapters/supabase.ts"],"names":[],"mappings":"AAGA,uCAAuC;AACvC,MAAM,OAAO,eAAe;IAG1B,YAAY,QAAwB;QAClC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,IAA6B;QACvD,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;QACtF,OAAO,EAAE,IAAI,EAAE,MAA0C,EAAE,KAAK,EAAE,CAAC;IACrE,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,KAA4D;QACtF,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC9C,IAAI,KAAK,EAAE,MAAM,IAAI,KAAK,EAAE,KAAK,KAAK,SAAS,EAAE,CAAC;YAChD,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QACtC,CAAC;QACD,IAAI,KAAK,EAAE,KAAK,EAAE,CAAC;YACjB,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC3B,CAAC;QACD,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC,CAAC;QAChC,OAAO,EAAE,IAAI,EAAE,IAAwC,EAAE,KAAK,EAAE,CAAC;IACnE,CAAC;IAED,
|
|
1
|
+
{"version":3,"file":"supabase.js","sourceRoot":"","sources":["../../adapters/supabase.ts"],"names":[],"mappings":"AAGA,uCAAuC;AACvC,MAAM,OAAO,eAAe;IAG1B,YAAY,QAAwB;QAClC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,IAA6B;QACvD,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;QACtF,OAAO,EAAE,IAAI,EAAE,MAA0C,EAAE,KAAK,EAAE,CAAC;IACrE,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,KAA4D;QACtF,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC9C,IAAI,KAAK,EAAE,MAAM,IAAI,KAAK,EAAE,KAAK,KAAK,SAAS,EAAE,CAAC;YAChD,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QACtC,CAAC;QACD,IAAI,KAAK,EAAE,KAAK,EAAE,CAAC;YACjB,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC3B,CAAC;QACD,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC,CAAC;QAChC,OAAO,EAAE,IAAI,EAAE,IAAwC,EAAE,KAAK,EAAE,CAAC;IACnE,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,IAA6B;QACvD,uDAAuD;QACvD,MAAM,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAE3D,IAAI,CAAC,OAAO,EAAE,CAAC;YACZ,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,4DAA4D,CAAC,EAAE,CAAC;QACzG,CAAC;QAED,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ;aAChD,IAAI,CAAC,KAAK,CAAC;aACX,MAAM,CAAC,IAAI,CAAC;aACZ,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;aAC1B,MAAM,EAAE,CAAC;QAEZ,OAAO,EAAE,IAAI,EAAE,MAA0C,EAAE,KAAK,EAAE,CAAC;IACrE,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,KAAyC;QACnE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ;aAChD,IAAI,CAAC,KAAK,CAAC;aACX,MAAM,EAAE;aACR,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC;aAC7B,MAAM,EAAE,CAAC;QAEZ,OAAO,EAAE,IAAI,EAAE,MAA0C,EAAE,KAAK,EAAE,CAAC;IACrE,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;CACF"}
|
package/dist/client.d.ts
CHANGED
|
@@ -1,15 +1,41 @@
|
|
|
1
|
-
import type { DatabaseAdapter } from "./types";
|
|
1
|
+
import type { DatabaseAdapter, AegisConfig } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* The primary client for aegis-lock.
|
|
4
|
+
* Handles automatic encryption, decryption, contextual binding, and blind indexing
|
|
5
|
+
* before interacting with the underlying database adapter.
|
|
6
|
+
*/
|
|
2
7
|
export declare class AegisClient {
|
|
3
8
|
private adapter;
|
|
4
9
|
private key;
|
|
10
|
+
private bidxKey?;
|
|
5
11
|
private encryptedFields;
|
|
6
|
-
|
|
7
|
-
|
|
12
|
+
private bidxFields;
|
|
13
|
+
private primaryKeyField;
|
|
14
|
+
/**
|
|
15
|
+
* Initializes the AegisClient.
|
|
16
|
+
* * @param config - Configuration including the database adapter, primary key field, and field rules.
|
|
17
|
+
* @param key - The master CryptoKey used for AES-GCM encryption.
|
|
18
|
+
* @param bidxKey - (Optional) The CryptoKey used for generating HMAC-SHA256 blind indexes.
|
|
19
|
+
*/
|
|
20
|
+
constructor(config: AegisConfig, key: CryptoKey, bidxKey?: CryptoKey);
|
|
21
|
+
/**
|
|
22
|
+
* Securely inserts a new record into the database.
|
|
23
|
+
* Automatically generates blind indexes and encrypts configured fields.
|
|
24
|
+
* * @param table - The database table or collection name.
|
|
25
|
+
* @param data - The plaintext data object to insert. Must include the primary key.
|
|
26
|
+
* @returns The inserted data (as stored in the database) or an error.
|
|
27
|
+
*/
|
|
8
28
|
insert(table: string, data: Record<string, unknown>): Promise<{
|
|
9
29
|
data: Record<string, unknown>[] | null;
|
|
10
30
|
error: unknown;
|
|
11
31
|
}>;
|
|
12
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* Retrieves and automatically decrypts records from the database.
|
|
34
|
+
* If querying by a configured blind index field, the query is automatically intercepted and hashed.
|
|
35
|
+
* * @param table - The database table or collection name.
|
|
36
|
+
* @param query - The column and value to search for (e.g., { column: "email", value: "alice@example.com" }).
|
|
37
|
+
* @returns The decrypted plaintext data or an error.
|
|
38
|
+
*/
|
|
13
39
|
select(table: string, query?: {
|
|
14
40
|
column?: string;
|
|
15
41
|
value?: unknown;
|
|
@@ -18,7 +44,38 @@ export declare class AegisClient {
|
|
|
18
44
|
data: Record<string, unknown>[] | null;
|
|
19
45
|
error: unknown;
|
|
20
46
|
}>;
|
|
21
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* Securely modifies an existing record.
|
|
49
|
+
* Re-encrypts data with a new unique IV and updates blind indexes.
|
|
50
|
+
* * @param table - The database table or collection name.
|
|
51
|
+
* @param data - The updated plaintext data. Must include the primary key.
|
|
52
|
+
* @returns The updated data (as stored in the database) or an error.
|
|
53
|
+
*/
|
|
54
|
+
update(table: string, data: Record<string, unknown>): Promise<{
|
|
55
|
+
data: any[] | null;
|
|
56
|
+
error: any;
|
|
57
|
+
}>;
|
|
58
|
+
/**
|
|
59
|
+
* Securely removes records from the database.
|
|
60
|
+
* Automatically hashes the search query if deleting by a blind-indexed field.
|
|
61
|
+
* * @param table - The database table or collection name.
|
|
62
|
+
* @param query - The column and value to identify the record to delete.
|
|
63
|
+
* @returns The result of the deletion operation or an error.
|
|
64
|
+
*/
|
|
65
|
+
delete(table: string, query: {
|
|
66
|
+
column: string;
|
|
67
|
+
value: unknown;
|
|
68
|
+
}): Promise<{
|
|
69
|
+
data: any[] | null;
|
|
70
|
+
error: any;
|
|
71
|
+
}>;
|
|
72
|
+
/**
|
|
73
|
+
* Bypasses decryption and retrieves the raw, encrypted data directly from the database.
|
|
74
|
+
* Useful for database migrations, auditing, or debugging.
|
|
75
|
+
* * @param table - The database table or collection name.
|
|
76
|
+
* @param query - The raw query to execute against the database.
|
|
77
|
+
* @returns The raw, encrypted database rows.
|
|
78
|
+
*/
|
|
22
79
|
selectRaw(table: string, query?: {
|
|
23
80
|
column?: string;
|
|
24
81
|
value?: unknown;
|
|
@@ -27,7 +84,10 @@ export declare class AegisClient {
|
|
|
27
84
|
data: Record<string, unknown>[] | null;
|
|
28
85
|
error: unknown;
|
|
29
86
|
}>;
|
|
30
|
-
/**
|
|
87
|
+
/**
|
|
88
|
+
* Gets the underlying database adapter instance.
|
|
89
|
+
* Useful for executing custom database commands outside of Aegis's encryption flow.
|
|
90
|
+
*/
|
|
31
91
|
get db(): DatabaseAdapter;
|
|
32
92
|
}
|
|
33
93
|
//# sourceMappingURL=client.d.ts.map
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAG5D;;;;GAIG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,GAAG,CAAY;IACvB,OAAO,CAAC,OAAO,CAAC,CAAY;IAC5B,OAAO,CAAC,eAAe,CAA2B;IAClD,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,eAAe,CAAS;IAEhC;;;;;OAKG;gBACS,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,SAAS;IASpE;;;;;;OAMG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;IAyCzD;;;;;;OAMG;IACG,MAAM,CACV,KAAK,EAAE,MAAM,EACb,KAAK,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;;;;IA6D9D;;;;;;OAMG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;IAkCzD;;;;;;OAMG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE;;;;IAkBrE;;;;;;OAMG;IACG,SAAS,CACb,KAAK,EAAE,MAAM,EACb,KAAK,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;;;;IAK9D;;;OAGG;IACH,IAAI,EAAE,IAAI,eAAe,CAExB;CACF"}
|
package/dist/client.js
CHANGED
|
@@ -1,31 +1,83 @@
|
|
|
1
|
-
import { encrypt, decrypt } from "./crypto";
|
|
1
|
+
import { encrypt, decrypt, generateBlindIndex } from "./crypto";
|
|
2
|
+
/**
|
|
3
|
+
* The primary client for aegis-lock.
|
|
4
|
+
* Handles automatic encryption, decryption, contextual binding, and blind indexing
|
|
5
|
+
* before interacting with the underlying database adapter.
|
|
6
|
+
*/
|
|
2
7
|
export class AegisClient {
|
|
3
|
-
|
|
4
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Initializes the AegisClient.
|
|
10
|
+
* * @param config - Configuration including the database adapter, primary key field, and field rules.
|
|
11
|
+
* @param key - The master CryptoKey used for AES-GCM encryption.
|
|
12
|
+
* @param bidxKey - (Optional) The CryptoKey used for generating HMAC-SHA256 blind indexes.
|
|
13
|
+
*/
|
|
14
|
+
constructor(config, key, bidxKey) {
|
|
15
|
+
this.adapter = config.adapter;
|
|
5
16
|
this.key = key;
|
|
6
|
-
this.
|
|
17
|
+
this.bidxKey = bidxKey; // Optional Blind Index Key
|
|
18
|
+
this.encryptedFields = config.encryptedFields;
|
|
19
|
+
this.bidxFields = config.bidxFields || {};
|
|
20
|
+
this.primaryKeyField = config.primaryKeyField;
|
|
7
21
|
}
|
|
8
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Securely inserts a new record into the database.
|
|
24
|
+
* Automatically generates blind indexes and encrypts configured fields.
|
|
25
|
+
* * @param table - The database table or collection name.
|
|
26
|
+
* @param data - The plaintext data object to insert. Must include the primary key.
|
|
27
|
+
* @returns The inserted data (as stored in the database) or an error.
|
|
28
|
+
*/
|
|
9
29
|
async insert(table, data) {
|
|
10
30
|
const fields = this.encryptedFields[table] || [];
|
|
31
|
+
const bidxFields = this.bidxFields[table] || [];
|
|
11
32
|
const row = { ...data };
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
33
|
+
if (fields.length > 0 || bidxFields.length > 0) {
|
|
34
|
+
const rowId = row[this.primaryKeyField];
|
|
35
|
+
if (!rowId) {
|
|
36
|
+
throw new Error(`Aegis: Cannot encrypt. Missing primary key '${this.primaryKeyField}' in payload.`);
|
|
37
|
+
}
|
|
38
|
+
const aadContext = String(rowId);
|
|
39
|
+
// 1. Handle Blind Indexing FIRST (While row data is still plaintext)
|
|
40
|
+
for (const field of bidxFields) {
|
|
41
|
+
if (row[field] != null && this.bidxKey) {
|
|
42
|
+
row[`${field}_bidx`] = await generateBlindIndex(String(row[field]), // Safe: This is still "alice@example.com"
|
|
43
|
+
this.bidxKey);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// 2. Handle Encryption SECOND (This overwrites the plaintext)
|
|
47
|
+
for (const field of fields) {
|
|
48
|
+
if (row[field] != null) {
|
|
49
|
+
const { ciphertext, iv } = await encrypt(String(row[field]), this.key, aadContext);
|
|
50
|
+
row[field] = `${iv}:${ciphertext}`;
|
|
51
|
+
}
|
|
19
52
|
}
|
|
20
|
-
}
|
|
21
|
-
if (fields.length > 0) {
|
|
22
|
-
row.iv = sharedIv;
|
|
23
53
|
}
|
|
24
54
|
return this.adapter.insert(table, row);
|
|
25
55
|
}
|
|
26
|
-
/**
|
|
56
|
+
/**
|
|
57
|
+
* Retrieves and automatically decrypts records from the database.
|
|
58
|
+
* If querying by a configured blind index field, the query is automatically intercepted and hashed.
|
|
59
|
+
* * @param table - The database table or collection name.
|
|
60
|
+
* @param query - The column and value to search for (e.g., { column: "email", value: "alice@example.com" }).
|
|
61
|
+
* @returns The decrypted plaintext data or an error.
|
|
62
|
+
*/
|
|
27
63
|
async select(table, query) {
|
|
28
|
-
|
|
64
|
+
// Intercept and swap query for Blind Indexing (NEW)
|
|
65
|
+
const bidxFields = this.bidxFields[table] || [];
|
|
66
|
+
let activeQuery = query;
|
|
67
|
+
// Make sure this uses THIS.bidxKey
|
|
68
|
+
// Inside your select method in client.ts
|
|
69
|
+
if (query?.column &&
|
|
70
|
+
query?.value &&
|
|
71
|
+
bidxFields.includes(query.column) &&
|
|
72
|
+
this.bidxKey) {
|
|
73
|
+
const blindIndexValue = await generateBlindIndex(String(query.value), this.bidxKey);
|
|
74
|
+
activeQuery = {
|
|
75
|
+
...query,
|
|
76
|
+
column: `${query.column}_bidx`,
|
|
77
|
+
value: blindIndexValue, // Pass the resulting string
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const { data, error } = await this.adapter.select(table, activeQuery);
|
|
29
81
|
if (error || !data)
|
|
30
82
|
return { data, error };
|
|
31
83
|
const fields = this.encryptedFields[table] || [];
|
|
@@ -33,16 +85,19 @@ export class AegisClient {
|
|
|
33
85
|
return { data, error };
|
|
34
86
|
const decrypted = await Promise.all(data.map(async (row) => {
|
|
35
87
|
const decRow = { ...row };
|
|
36
|
-
const
|
|
37
|
-
if (!
|
|
88
|
+
const rowId = decRow[this.primaryKeyField];
|
|
89
|
+
if (!rowId)
|
|
38
90
|
return decRow;
|
|
91
|
+
const aadContext = String(rowId);
|
|
39
92
|
for (const field of fields) {
|
|
40
|
-
|
|
93
|
+
const payload = decRow[field];
|
|
94
|
+
if (typeof payload === "string" && payload.includes(":")) {
|
|
95
|
+
const [iv, ciphertext] = payload.split(":");
|
|
41
96
|
try {
|
|
42
|
-
decRow[field] = await decrypt({ ciphertext
|
|
97
|
+
decRow[field] = await decrypt({ ciphertext, iv }, this.key, aadContext);
|
|
43
98
|
}
|
|
44
99
|
catch {
|
|
45
|
-
//
|
|
100
|
+
// Tampering detected
|
|
46
101
|
}
|
|
47
102
|
}
|
|
48
103
|
}
|
|
@@ -50,11 +105,75 @@ export class AegisClient {
|
|
|
50
105
|
}));
|
|
51
106
|
return { data: decrypted, error };
|
|
52
107
|
}
|
|
53
|
-
/**
|
|
108
|
+
/**
|
|
109
|
+
* Securely modifies an existing record.
|
|
110
|
+
* Re-encrypts data with a new unique IV and updates blind indexes.
|
|
111
|
+
* * @param table - The database table or collection name.
|
|
112
|
+
* @param data - The updated plaintext data. Must include the primary key.
|
|
113
|
+
* @returns The updated data (as stored in the database) or an error.
|
|
114
|
+
*/
|
|
115
|
+
async update(table, data) {
|
|
116
|
+
const fields = this.encryptedFields[table] || [];
|
|
117
|
+
const bidxFields = this.bidxFields[table] || [];
|
|
118
|
+
const row = { ...data };
|
|
119
|
+
const rowId = row[this.primaryKeyField];
|
|
120
|
+
if (!rowId) {
|
|
121
|
+
throw new Error(`Aegis: Cannot update. Missing primary key '${this.primaryKeyField}' in payload.`);
|
|
122
|
+
}
|
|
123
|
+
const aadContext = String(rowId);
|
|
124
|
+
if (fields.length > 0 || bidxFields.length > 0) {
|
|
125
|
+
// 1. Update Blind Indexes
|
|
126
|
+
for (const field of bidxFields) {
|
|
127
|
+
if (row[field] != null && this.bidxKey) {
|
|
128
|
+
row[`${field}_bidx`] = await generateBlindIndex(String(row[field]), this.bidxKey);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// 2. Encrypt updated fields with a brand new IV
|
|
132
|
+
for (const field of fields) {
|
|
133
|
+
if (row[field] != null) {
|
|
134
|
+
const { ciphertext, iv } = await encrypt(String(row[field]), this.key, aadContext);
|
|
135
|
+
row[field] = `${iv}:${ciphertext}`;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return this.adapter.update(table, row);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Securely removes records from the database.
|
|
143
|
+
* Automatically hashes the search query if deleting by a blind-indexed field.
|
|
144
|
+
* * @param table - The database table or collection name.
|
|
145
|
+
* @param query - The column and value to identify the record to delete.
|
|
146
|
+
* @returns The result of the deletion operation or an error.
|
|
147
|
+
*/
|
|
148
|
+
async delete(table, query) {
|
|
149
|
+
const bidxFields = this.bidxFields[table] || [];
|
|
150
|
+
let activeQuery = query;
|
|
151
|
+
// Intercept the query! If they are deleting by an encrypted field (like email),
|
|
152
|
+
// we must hash the value so the database knows which row to delete.
|
|
153
|
+
if (bidxFields.includes(query.column) && this.bidxKey) {
|
|
154
|
+
const blindIndexValue = await generateBlindIndex(String(query.value), this.bidxKey);
|
|
155
|
+
activeQuery = {
|
|
156
|
+
...query,
|
|
157
|
+
column: `${query.column}_bidx`,
|
|
158
|
+
value: blindIndexValue,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
return this.adapter.delete(table, activeQuery);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Bypasses decryption and retrieves the raw, encrypted data directly from the database.
|
|
165
|
+
* Useful for database migrations, auditing, or debugging.
|
|
166
|
+
* * @param table - The database table or collection name.
|
|
167
|
+
* @param query - The raw query to execute against the database.
|
|
168
|
+
* @returns The raw, encrypted database rows.
|
|
169
|
+
*/
|
|
54
170
|
async selectRaw(table, query) {
|
|
55
171
|
return this.adapter.select(table, query);
|
|
56
172
|
}
|
|
57
|
-
/**
|
|
173
|
+
/**
|
|
174
|
+
* Gets the underlying database adapter instance.
|
|
175
|
+
* Useful for executing custom database commands outside of Aegis's encryption flow.
|
|
176
|
+
*/
|
|
58
177
|
get db() {
|
|
59
178
|
return this.adapter;
|
|
60
179
|
}
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../client.ts"],"names":[],"mappings":"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"}
|
package/dist/crypto.d.ts
CHANGED
|
@@ -1,7 +1,53 @@
|
|
|
1
1
|
import { EncryptedPayload } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Generates a strong, 256-bit master key for AES-GCM encryption and decryption.
|
|
4
|
+
* * @returns A Promise resolving to a native Web Crypto API CryptoKey.
|
|
5
|
+
*/
|
|
2
6
|
export declare function generateKey(): Promise<CryptoKey>;
|
|
7
|
+
/**
|
|
8
|
+
* Exports a CryptoKey into a raw Base64 string so it can be safely stored
|
|
9
|
+
* in an environment variable or Key Management Service (KMS).
|
|
10
|
+
* * @param key - The CryptoKey to export.
|
|
11
|
+
* @returns A Promise resolving to the Base64 representation of the key.
|
|
12
|
+
*/
|
|
3
13
|
export declare function exportKey(key: CryptoKey): Promise<string>;
|
|
14
|
+
/**
|
|
15
|
+
* Imports a Base64 string back into a usable CryptoKey for AES-GCM operations.
|
|
16
|
+
* * @param base64 - The Base64 encoded key string (usually from environment variables).
|
|
17
|
+
* @returns A Promise resolving to the imported CryptoKey.
|
|
18
|
+
*/
|
|
4
19
|
export declare function importKey(base64: string): Promise<CryptoKey>;
|
|
5
|
-
|
|
6
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Encrypts a plaintext string using AES-256-GCM.
|
|
22
|
+
* Automatically generates a unique 12-byte Initialization Vector (IV) and applies
|
|
23
|
+
* Contextual Binding (AAD) to tie the ciphertext to a specific database row.
|
|
24
|
+
* * @param plaintext - The sensitive data to encrypt.
|
|
25
|
+
* @param key - The master AES-GCM CryptoKey.
|
|
26
|
+
* @param aadContext - The row ID or primary key to bind this ciphertext to.
|
|
27
|
+
* @returns A Promise resolving to an object containing the Base64 `ciphertext` and `iv`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function encrypt(plaintext: string, key: CryptoKey, aadContext: string): Promise<EncryptedPayload>;
|
|
30
|
+
/**
|
|
31
|
+
* Decrypts an AES-256-GCM payload back into plaintext.
|
|
32
|
+
* Strictly verifies the Additional Authenticated Data (AAD) to ensure the data
|
|
33
|
+
* has not been tampered with or copied from another database row.
|
|
34
|
+
* * @param payload - The object containing the Base64 `ciphertext` and `iv`.
|
|
35
|
+
* @param key - The master AES-GCM CryptoKey.
|
|
36
|
+
* @param aadContext - The row ID or primary key that this ciphertext is bound to.
|
|
37
|
+
* @returns A Promise resolving to the decrypted plaintext string.
|
|
38
|
+
*/
|
|
39
|
+
export declare function decrypt(payload: EncryptedPayload, key: CryptoKey, aadContext: string): Promise<string>;
|
|
40
|
+
/** * Generates a dedicated HMAC-SHA256 key for deterministic Blind Indexing.
|
|
41
|
+
* This key should be kept separate from the master encryption key.
|
|
42
|
+
* * @returns A Promise resolving to an HMAC CryptoKey.
|
|
43
|
+
*/
|
|
44
|
+
export declare function generateBidxKey(): Promise<CryptoKey>;
|
|
45
|
+
/** * Generates a deterministic hash for database searching without exposing plaintext.
|
|
46
|
+
* Safely extracts the underlying memory buffer to guarantee identical hashes
|
|
47
|
+
* across different JavaScript runtimes (Node.js, Browsers, Edge).
|
|
48
|
+
* * @param plaintext - The data to hash (e.g., an email address).
|
|
49
|
+
* @param key - The HMAC Blind Index CryptoKey.
|
|
50
|
+
* @returns A Promise resolving to a deterministic Base64 hash string.
|
|
51
|
+
*/
|
|
52
|
+
export declare function generateBlindIndex(plaintext: string, key: CryptoKey): Promise<string>;
|
|
7
53
|
//# sourceMappingURL=crypto.d.ts.map
|
package/dist/crypto.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../crypto.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../crypto.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAqC3C;;;GAGG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,SAAS,CAAC,CAKtD;AAED;;;;;GAKG;AACH,wBAAsB,SAAS,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAG/D;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CASlE;AAED;;;;;;;;GAQG;AACH,wBAAsB,OAAO,CAC3B,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,SAAS,EACd,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,gBAAgB,CAAC,CAsB3B;AAED;;;;;;;;GAQG;AACH,wBAAsB,OAAO,CAC3B,OAAO,EAAE,gBAAgB,EACzB,GAAG,EAAE,SAAS,EACd,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAsBjB;AAID;;;GAGG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,SAAS,CAAC,CAK1D;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,SAAS,GACb,OAAO,CAAC,MAAM,CAAC,CAkBjB"}
|
package/dist/crypto.js
CHANGED
|
@@ -1,32 +1,133 @@
|
|
|
1
1
|
const ALGO = "AES-GCM";
|
|
2
2
|
const KEY_LENGTH = 256;
|
|
3
|
+
/**
|
|
4
|
+
* Safely converts an ArrayBuffer or Uint8Array to a Base64 string.
|
|
5
|
+
* Used internally to format ciphertexts, IVs, and exported keys for safe database storage.
|
|
6
|
+
* * @param buffer - The raw byte array to convert.
|
|
7
|
+
* @returns A Base64 encoded string.
|
|
8
|
+
*/
|
|
9
|
+
function bufferToBase64(buffer) {
|
|
10
|
+
let binary = "";
|
|
11
|
+
// Normalize to Uint8Array safely
|
|
12
|
+
const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
|
|
13
|
+
const len = bytes.byteLength;
|
|
14
|
+
for (let i = 0; i < len; i++) {
|
|
15
|
+
binary += String.fromCharCode(bytes[i]);
|
|
16
|
+
}
|
|
17
|
+
return btoa(binary);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Converts a Base64 string back into a Uint8Array.
|
|
21
|
+
* Used internally to reconstruct buffers for decryption or key importing.
|
|
22
|
+
* * @param base64 - The Base64 encoded string.
|
|
23
|
+
* @returns A Uint8Array containing the raw bytes.
|
|
24
|
+
*/
|
|
25
|
+
function base64ToBuffer(base64) {
|
|
26
|
+
const binaryString = atob(base64);
|
|
27
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
28
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
29
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
30
|
+
}
|
|
31
|
+
return bytes;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Generates a strong, 256-bit master key for AES-GCM encryption and decryption.
|
|
35
|
+
* * @returns A Promise resolving to a native Web Crypto API CryptoKey.
|
|
36
|
+
*/
|
|
3
37
|
export async function generateKey() {
|
|
4
|
-
return crypto.subtle.generateKey({ name: ALGO, length: KEY_LENGTH }, true, [
|
|
38
|
+
return crypto.subtle.generateKey({ name: ALGO, length: KEY_LENGTH }, true, [
|
|
39
|
+
"encrypt",
|
|
40
|
+
"decrypt",
|
|
41
|
+
]);
|
|
5
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Exports a CryptoKey into a raw Base64 string so it can be safely stored
|
|
45
|
+
* in an environment variable or Key Management Service (KMS).
|
|
46
|
+
* * @param key - The CryptoKey to export.
|
|
47
|
+
* @returns A Promise resolving to the Base64 representation of the key.
|
|
48
|
+
*/
|
|
6
49
|
export async function exportKey(key) {
|
|
7
50
|
const raw = await crypto.subtle.exportKey("raw", key);
|
|
8
|
-
return
|
|
51
|
+
return bufferToBase64(raw);
|
|
9
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Imports a Base64 string back into a usable CryptoKey for AES-GCM operations.
|
|
55
|
+
* * @param base64 - The Base64 encoded key string (usually from environment variables).
|
|
56
|
+
* @returns A Promise resolving to the imported CryptoKey.
|
|
57
|
+
*/
|
|
10
58
|
export async function importKey(base64) {
|
|
11
|
-
const raw =
|
|
12
|
-
return crypto.subtle.importKey("raw", raw, { name: ALGO, length: KEY_LENGTH }, true, [
|
|
13
|
-
"encrypt",
|
|
14
|
-
"decrypt",
|
|
15
|
-
]);
|
|
59
|
+
const raw = base64ToBuffer(base64);
|
|
60
|
+
return crypto.subtle.importKey("raw", raw, { name: ALGO, length: KEY_LENGTH }, true, ["encrypt", "decrypt"]);
|
|
16
61
|
}
|
|
17
|
-
|
|
18
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Encrypts a plaintext string using AES-256-GCM.
|
|
64
|
+
* Automatically generates a unique 12-byte Initialization Vector (IV) and applies
|
|
65
|
+
* Contextual Binding (AAD) to tie the ciphertext to a specific database row.
|
|
66
|
+
* * @param plaintext - The sensitive data to encrypt.
|
|
67
|
+
* @param key - The master AES-GCM CryptoKey.
|
|
68
|
+
* @param aadContext - The row ID or primary key to bind this ciphertext to.
|
|
69
|
+
* @returns A Promise resolving to an object containing the Base64 `ciphertext` and `iv`.
|
|
70
|
+
*/
|
|
71
|
+
export async function encrypt(plaintext, key, aadContext) {
|
|
72
|
+
const iv = crypto.getRandomValues(new Uint8Array(12)); // ALWAYS unique
|
|
19
73
|
const encoded = new TextEncoder().encode(plaintext);
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
iv: btoa(String.fromCharCode(...iv)),
|
|
74
|
+
const algorithm = {
|
|
75
|
+
name: ALGO,
|
|
76
|
+
iv: iv,
|
|
24
77
|
};
|
|
78
|
+
if (aadContext) {
|
|
79
|
+
algorithm.additionalData = new TextEncoder().encode(aadContext);
|
|
80
|
+
}
|
|
81
|
+
const cipherBuffer = await crypto.subtle.encrypt(algorithm, key, encoded);
|
|
82
|
+
return { ciphertext: bufferToBase64(cipherBuffer), iv: bufferToBase64(iv) };
|
|
25
83
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
84
|
+
/**
|
|
85
|
+
* Decrypts an AES-256-GCM payload back into plaintext.
|
|
86
|
+
* Strictly verifies the Additional Authenticated Data (AAD) to ensure the data
|
|
87
|
+
* has not been tampered with or copied from another database row.
|
|
88
|
+
* * @param payload - The object containing the Base64 `ciphertext` and `iv`.
|
|
89
|
+
* @param key - The master AES-GCM CryptoKey.
|
|
90
|
+
* @param aadContext - The row ID or primary key that this ciphertext is bound to.
|
|
91
|
+
* @returns A Promise resolving to the decrypted plaintext string.
|
|
92
|
+
*/
|
|
93
|
+
export async function decrypt(payload, key, aadContext) {
|
|
94
|
+
const cipherBytes = base64ToBuffer(payload.ciphertext);
|
|
95
|
+
const iv = base64ToBuffer(payload.iv);
|
|
96
|
+
const algorithm = {
|
|
97
|
+
name: ALGO,
|
|
98
|
+
iv: iv,
|
|
99
|
+
};
|
|
100
|
+
if (aadContext) {
|
|
101
|
+
algorithm.additionalData = new TextEncoder().encode(aadContext);
|
|
102
|
+
}
|
|
103
|
+
const decrypted = await crypto.subtle.decrypt(algorithm, key, cipherBytes);
|
|
30
104
|
return new TextDecoder().decode(decrypted);
|
|
31
105
|
}
|
|
106
|
+
const HMAC_ALGO = "HMAC";
|
|
107
|
+
/** * Generates a dedicated HMAC-SHA256 key for deterministic Blind Indexing.
|
|
108
|
+
* This key should be kept separate from the master encryption key.
|
|
109
|
+
* * @returns A Promise resolving to an HMAC CryptoKey.
|
|
110
|
+
*/
|
|
111
|
+
export async function generateBidxKey() {
|
|
112
|
+
return crypto.subtle.generateKey({ name: HMAC_ALGO, hash: "SHA-256" }, true, [
|
|
113
|
+
"sign",
|
|
114
|
+
"verify",
|
|
115
|
+
]);
|
|
116
|
+
}
|
|
117
|
+
/** * Generates a deterministic hash for database searching without exposing plaintext.
|
|
118
|
+
* Safely extracts the underlying memory buffer to guarantee identical hashes
|
|
119
|
+
* across different JavaScript runtimes (Node.js, Browsers, Edge).
|
|
120
|
+
* * @param plaintext - The data to hash (e.g., an email address).
|
|
121
|
+
* @param key - The HMAC Blind Index CryptoKey.
|
|
122
|
+
* @returns A Promise resolving to a deterministic Base64 hash string.
|
|
123
|
+
*/
|
|
124
|
+
export async function generateBlindIndex(plaintext, key) {
|
|
125
|
+
const encoder = new TextEncoder();
|
|
126
|
+
const encoded = encoder.encode(plaintext);
|
|
127
|
+
// PRODUCTION FIX: Isolated buffer extraction.
|
|
128
|
+
// This ensures the HMAC signature is deterministic regardless of memory alignment.
|
|
129
|
+
const cleanBuffer = encoded.buffer.slice(encoded.byteOffset, encoded.byteOffset + encoded.byteLength);
|
|
130
|
+
const signature = await crypto.subtle.sign("HMAC", key, cleanBuffer);
|
|
131
|
+
return bufferToBase64(signature);
|
|
132
|
+
}
|
|
32
133
|
//# sourceMappingURL=crypto.js.map
|
package/dist/crypto.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../crypto.ts"],"names":[],"mappings":"AAEA,MAAM,IAAI,GAAG,SAAS,CAAC;AACvB,MAAM,UAAU,GAAG,GAAG,CAAC;AAEvB,
|
|
1
|
+
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../crypto.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { AegisClient } from "./client";
|
|
2
|
-
export { generateKey, exportKey, importKey
|
|
2
|
+
export { generateKey, generateBidxKey, exportKey, importKey } from "./crypto";
|
|
3
3
|
export { SupabaseAdapter } from "./adapters/supabase";
|
|
4
4
|
export { MongoDBAdapter } from "./adapters/mongodb";
|
|
5
5
|
export type { AegisConfig, EncryptedPayload, AegisKeyPair, DatabaseAdapter } from "./types";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { AegisClient } from "./client";
|
|
2
|
-
export { generateKey, exportKey, importKey
|
|
2
|
+
export { generateKey, generateBidxKey, exportKey, importKey } from "./crypto";
|
|
3
3
|
export { SupabaseAdapter } from "./adapters/supabase";
|
|
4
4
|
export { MongoDBAdapter } from "./adapters/mongodb";
|
|
5
5
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
/** Generic database adapter interface — implement this for any database. */
|
|
2
1
|
export interface DatabaseAdapter {
|
|
3
|
-
/** Insert a row into a table/collection. Returns the inserted data. */
|
|
4
2
|
insert(table: string, data: Record<string, unknown>): Promise<{
|
|
5
3
|
data: Record<string, unknown>[] | null;
|
|
6
4
|
error: unknown;
|
|
7
5
|
}>;
|
|
8
|
-
/** Select rows from a table/collection with optional filters. */
|
|
9
6
|
select(table: string, query?: {
|
|
10
7
|
column?: string;
|
|
11
8
|
value?: unknown;
|
|
@@ -14,10 +11,23 @@ export interface DatabaseAdapter {
|
|
|
14
11
|
data: Record<string, unknown>[] | null;
|
|
15
12
|
error: unknown;
|
|
16
13
|
}>;
|
|
14
|
+
update(table: string, data: Record<string, unknown>): Promise<{
|
|
15
|
+
data: any[] | null;
|
|
16
|
+
error: any;
|
|
17
|
+
}>;
|
|
18
|
+
delete(table: string, query: {
|
|
19
|
+
column: string;
|
|
20
|
+
value: unknown;
|
|
21
|
+
}): Promise<{
|
|
22
|
+
data: any[] | null;
|
|
23
|
+
error: any;
|
|
24
|
+
}>;
|
|
17
25
|
}
|
|
18
26
|
export interface AegisConfig {
|
|
19
27
|
adapter: DatabaseAdapter;
|
|
20
28
|
encryptedFields: Record<string, string[]>;
|
|
29
|
+
primaryKeyField: string;
|
|
30
|
+
bidxFields?: Record<string, string[]>;
|
|
21
31
|
}
|
|
22
32
|
export interface EncryptedPayload {
|
|
23
33
|
ciphertext: string;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../types.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC1H,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACzJ,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;QAAC,KAAK,EAAE,GAAG,CAAA;KAAE,CAAC,CAAC;IAClG,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;QAAC,KAAK,EAAE,GAAG,CAAA;KAAE,CAAC,CAAC;CAC/G;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,eAAe,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC1C,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,SAAS,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aegis-lock",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Database-agnostic client-side AES-256-GCM field-level encryption. Works with Supabase, MongoDB, or any database via pluggable adapters.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -26,7 +26,8 @@
|
|
|
26
26
|
],
|
|
27
27
|
"scripts": {
|
|
28
28
|
"build": "tsc -p tsconfig.build.json",
|
|
29
|
-
"prepublishOnly": "npm run build"
|
|
29
|
+
"prepublishOnly": "npm run build",
|
|
30
|
+
"test": "jest"
|
|
30
31
|
},
|
|
31
32
|
"keywords": [
|
|
32
33
|
"encryption",
|
|
@@ -53,6 +54,10 @@
|
|
|
53
54
|
},
|
|
54
55
|
"devDependencies": {
|
|
55
56
|
"@supabase/supabase-js": "^2.0.0",
|
|
57
|
+
"@types/jest": "^30.0.0",
|
|
58
|
+
"@types/node": "^25.3.5",
|
|
59
|
+
"jest": "^30.2.0",
|
|
60
|
+
"ts-jest": "^29.4.6",
|
|
56
61
|
"typescript": "^5.9.3"
|
|
57
62
|
}
|
|
58
63
|
}
|