@northstartech/cipher 0.0.0-stage → 1.0.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.
Files changed (41) hide show
  1. package/README.md +541 -2
  2. package/dist/client.d.ts +237 -0
  3. package/dist/client.js +555 -0
  4. package/dist/client.js.map +1 -0
  5. package/dist/crypto.d.ts +72 -0
  6. package/dist/crypto.js +244 -0
  7. package/dist/crypto.js.map +1 -0
  8. package/dist/encoding.d.ts +17 -0
  9. package/dist/encoding.js +79 -0
  10. package/dist/encoding.js.map +1 -0
  11. package/dist/errors.d.ts +14 -0
  12. package/dist/errors.js +22 -0
  13. package/dist/errors.js.map +1 -0
  14. package/dist/index.d.ts +8 -0
  15. package/dist/index.js +6 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/integrations/drizzle.d.ts +96 -0
  18. package/dist/integrations/drizzle.js +285 -0
  19. package/dist/integrations/drizzle.js.map +1 -0
  20. package/dist/integrations/mongoose.d.ts +72 -0
  21. package/dist/integrations/mongoose.js +582 -0
  22. package/dist/integrations/mongoose.js.map +1 -0
  23. package/dist/integrations/prisma.d.ts +65 -0
  24. package/dist/integrations/prisma.js +588 -0
  25. package/dist/integrations/prisma.js.map +1 -0
  26. package/dist/integrations/sequelize.d.ts +62 -0
  27. package/dist/integrations/sequelize.js +677 -0
  28. package/dist/integrations/sequelize.js.map +1 -0
  29. package/dist/integrations/shared.d.ts +268 -0
  30. package/dist/integrations/shared.js +513 -0
  31. package/dist/integrations/shared.js.map +1 -0
  32. package/dist/integrations/typeorm.d.ts +109 -0
  33. package/dist/integrations/typeorm.js +457 -0
  34. package/dist/integrations/typeorm.js.map +1 -0
  35. package/dist/object.d.ts +53 -0
  36. package/dist/object.js +226 -0
  37. package/dist/object.js.map +1 -0
  38. package/dist/search.d.ts +250 -0
  39. package/dist/search.js +258 -0
  40. package/dist/search.js.map +1 -0
  41. package/package.json +101 -4
@@ -0,0 +1,285 @@
1
+ /**
2
+ * Drizzle ORM helpers. Drizzle has no async lifecycle hooks, so encryption is an
3
+ * explicit, typed step around your queries.
4
+ *
5
+ * ```ts
6
+ * import { encrypted, northstarDrizzle, cipherEq } from '@northstartech/cipher/drizzle'
7
+ *
8
+ * export const todos = encrypted(
9
+ * pgTable('todos', { id: serial('id').primaryKey(), title: text('title'), email: text('email'), emailBidx: varchar('email_bidx', { length: 64 }) }),
10
+ * { title: true, email: { searchable: 'email', indexField: 'emailBidx' } },
11
+ * )
12
+ * const nsc = northstarDrizzle(cipher, { db }) // `db` reserves serial ids on PostgreSQL
13
+ *
14
+ * await db.insert(todos).values(await nsc.encryptRows(todos, [{ title: 'Buy milk', email: 'ana@example.com' }]))
15
+ * const rows = await nsc.decryptRows(todos, await db.select().from(todos)) // one decrypt request
16
+ * const tokens = await nsc.searchTokens(todos.email, 'ANA@example.com')
17
+ * const mine = await nsc.decryptRows(todos, await db.select().from(todos).where(cipherEq(todos.email, tokens)))
18
+ *
19
+ * // Northstar Search: index stored rows as they are (the enclave decrypts the ciphertext)
20
+ * await cipher.index('todos').upsert(searchDocuments(todos, await db.select().from(todos), ['title', 'email', 'done']))
21
+ * ```
22
+ */
23
+ import { Column, Table, getTableColumns, getTableName, inArray, is, sql } from 'drizzle-orm';
24
+ import { CipherError } from '../errors.js';
25
+ import { INDEX_SUFFIX, PENDING_SENTINEL, configError, decryptValues, encryptValues, normalizeFieldSpec, plaintextOf, qualifiedTable, recordIdOf, requestError, reservedIdsFrom, searchDocumentOf, searchTokensFor, } from './shared.js';
26
+ export { CIPHERTEXT_PREFIX, PENDING_SENTINEL } from './shared.js';
27
+ const tablePlans = new WeakMap();
28
+ const columnPlans = new WeakMap();
29
+ const SERIAL_TYPES = new Set(['PgSerial', 'PgSmallSerial', 'PgBigSerial53', 'PgBigSerial64']);
30
+ const STRING_TYPES = new Set(['PgText', 'PgVarchar', 'PgChar', 'MySqlText', 'MySqlVarChar', 'MySqlChar', 'SQLiteText']);
31
+ /**
32
+ * Declare the encrypted columns of a Drizzle table (call once, next to the table
33
+ * definition). Returns the table unchanged so it can wrap the definition.
34
+ */
35
+ export function encrypted(table, fields, options = {}) {
36
+ if (!is(table, Table))
37
+ throw configError('Drizzle: encrypted(table, fields) expects a Drizzle table');
38
+ if (tablePlans.has(table))
39
+ throw configError(`Drizzle: ${getTableName(table)} is already configured`);
40
+ const columns = getTableColumns(table);
41
+ const name = getTableName(table);
42
+ const byDbName = new Map(Object.entries(columns).map(([k, c]) => [c.name, k]));
43
+ let pkKey = options.primaryKey;
44
+ if (pkKey === undefined) {
45
+ const pks = Object.entries(columns).filter(([, c]) => c.primary);
46
+ if (pks.length !== 1)
47
+ throw configError(`Drizzle: ${name} needs exactly one .primaryKey() column (or pass { primaryKey }) to bind ciphertext to records`);
48
+ pkKey = pks[0][0];
49
+ }
50
+ const pkColumn = columns[pkKey];
51
+ if (!pkColumn)
52
+ throw configError(`Drizzle: ${name}.${pkKey}: no such column`);
53
+ const plan = { table, name, schema: table[Symbol.for('drizzle:Schema')], pkKey, pkColumn, fields: [], byKey: new Map() };
54
+ for (const [key, spec] of Object.entries(fields)) {
55
+ if (spec === undefined)
56
+ continue;
57
+ const lb = `Drizzle: ${name}.${key}`;
58
+ const column = columns[key];
59
+ if (!column)
60
+ throw configError(`${lb}: no such column`);
61
+ if (key === pkKey)
62
+ throw configError(`${lb}: the primary key cannot be encrypted`);
63
+ if (!STRING_TYPES.has(column.columnType))
64
+ throw configError(`${lb}: encrypted columns must be text (got ${column.columnType})`);
65
+ const n = normalizeFieldSpec(spec, lb);
66
+ let indexKey;
67
+ if (n.searchable) {
68
+ indexKey = n.indexField ?? byDbName.get(`${column.name}${INDEX_SUFFIX}`) ?? (columns[`${key}${INDEX_SUFFIX}`] ? `${key}${INDEX_SUFFIX}` : columns[`${key}Bidx`] ? `${key}Bidx` : undefined);
69
+ if (!indexKey || !columns[indexKey])
70
+ throw configError(`${lb}: searchable columns need a blind-index column (${column.name}${INDEX_SUFFIX} varchar(64), indexed) or indexField`);
71
+ }
72
+ const f = {
73
+ key,
74
+ column,
75
+ name: column.name,
76
+ ...(n.normalization ? { normalization: n.normalization } : {}),
77
+ ...(indexKey ? { indexKey, indexColumn: columns[indexKey] } : {}),
78
+ };
79
+ plan.fields.push(f);
80
+ plan.byKey.set(key, f);
81
+ columnPlans.set(column, { plan, field: f, isIndex: false });
82
+ if (f.indexColumn)
83
+ columnPlans.set(f.indexColumn, { plan, field: f, isIndex: true });
84
+ }
85
+ if (!plan.fields.length)
86
+ throw configError(`Drizzle: ${name}: no encrypted columns listed`);
87
+ tablePlans.set(table, plan);
88
+ return table;
89
+ }
90
+ function planOf(table) {
91
+ const p = tablePlans.get(table);
92
+ if (!p)
93
+ throw configError(`Drizzle: ${is(table, Table) ? getTableName(table) : 'value'} is not configured; wrap the table with encrypted(table, { … })`);
94
+ return p;
95
+ }
96
+ /**
97
+ * Exact-match condition: `cipherEq(todos.email, tokens)` → `email_bidx IN (…tokens)`.
98
+ * Accepts the encrypted column (mapped to its blind index) or the blind-index column.
99
+ * Empty token lists match nothing.
100
+ */
101
+ export function cipherEq(column, tokens) {
102
+ const hit = columnPlans.get(column);
103
+ if (!hit || !hit.field.indexColumn)
104
+ throw configError(`Drizzle: ${column?.name ?? 'column'} is not a searchable encrypted column or its blind index`);
105
+ const flat = [...new Set(tokens.flat().map((t) => {
106
+ if (typeof t !== 'string')
107
+ throw requestError('Drizzle: cipherEq tokens must be strings (from searchTokens)');
108
+ return t;
109
+ }))];
110
+ if (!flat.length)
111
+ return sql `false`;
112
+ return inArray(hit.field.indexColumn, flat);
113
+ }
114
+ /**
115
+ * Two-phase write, step 1 (databases where the id is only known after INSERT):
116
+ * the row with every non-null encrypted value replaced by the `nsc:pending:v1`
117
+ * sentinel. Insert it, then `update(...).set(await nsc.encryptRow(table, row, { recordId }))`
118
+ * `.where(and(eq(table.id, id), eq(table.col, PENDING_SENTINEL)))`, ideally in one transaction.
119
+ */
120
+ export function pendingRow(table, row) {
121
+ const plan = planOf(table);
122
+ const out = { ...row };
123
+ for (const f of plan.fields) {
124
+ if (out[f.key] === undefined)
125
+ continue;
126
+ if (out[f.key] !== null)
127
+ out[f.key] = PENDING_SENTINEL;
128
+ if (f.indexKey)
129
+ out[f.indexKey] = null;
130
+ }
131
+ return out;
132
+ }
133
+ /**
134
+ * Northstar Search documents for stored rows (`db.select().from(table)`, not
135
+ * decrypted): encrypted columns are sent as their ciphertext with the context
136
+ * they are bound to and decrypted inside the enclave, so indexing needs no
137
+ * decrypt request. Rows already decrypted with `decryptRows` work too (their
138
+ * plaintext is sent). Document ids are the primary keys; field names are the
139
+ * table's JS keys.
140
+ */
141
+ export function searchDocuments(table, rows, fields) {
142
+ const plan = planOf(table);
143
+ const columns = getTableColumns(table);
144
+ const plans = fields.map((key) => {
145
+ if (!columns[key])
146
+ throw configError(`Drizzle: ${plan.name}.${key}: no such column`);
147
+ const f = plan.byKey.get(key);
148
+ return { attr: key, ...(f ? { encrypted: { collection: plan.name, field: f.name } } : {}) };
149
+ });
150
+ return rows.map((row) => searchDocumentOf(row, { id: row[plan.pkKey], fields: plans, label: plan.name }));
151
+ }
152
+ /** Bind the helpers to a Northstar Cipher client. */
153
+ export function northstarDrizzle(cipher, options = {}) {
154
+ if (!cipher || typeof cipher.encryptManySettled !== 'function')
155
+ throw configError('Drizzle: northstarDrizzle(cipher) requires a NorthstarCipher client');
156
+ const reserveIds = async (table, count, db = options.db) => {
157
+ const plan = planOf(table);
158
+ if (!db)
159
+ throw configError(`Drizzle: ${plan.name}.${plan.pkColumn.name}: pass { db } to reserve serial ids`);
160
+ if (!SERIAL_TYPES.has(plan.pkColumn.columnType) && !plan.pkColumn.generatedIdentity) {
161
+ throw configError(`Drizzle: ${plan.name}.${plan.pkColumn.name} is not a PostgreSQL serial/identity column`);
162
+ }
163
+ if (plan.pkColumn.generatedIdentity?.type === 'always') {
164
+ throw configError(`Drizzle: ${plan.name}.${plan.pkColumn.name} is GENERATED ALWAYS AS IDENTITY, which rejects explicit ids; use generatedByDefaultAsIdentity()`);
165
+ }
166
+ const res = await db.execute(sql `SELECT nextval(pg_get_serial_sequence(${qualifiedTable(plan.name, plan.schema)}, ${plan.pkColumn.name}))::text AS id FROM generate_series(1, ${count}::int)`);
167
+ const big = plan.pkColumn.columnType === 'PgBigSerial64' || plan.pkColumn.dataType === 'bigint';
168
+ return reservedIdsFrom(res, count, `${plan.name}.${plan.pkColumn.name}`).map((v) => (big ? BigInt(v) : Number(v)));
169
+ };
170
+ const fillIds = async (plan, rows, db) => {
171
+ const missing = rows.filter((r) => r[plan.pkKey] === undefined || r[plan.pkKey] === null);
172
+ if (!missing.length)
173
+ return;
174
+ const gen = plan.pkColumn.defaultFn;
175
+ if (gen) {
176
+ for (const r of missing) {
177
+ const v = gen();
178
+ if (v && typeof v === 'object')
179
+ throw configError(`Drizzle: ${plan.name}.${plan.pkColumn.name}: $defaultFn returns SQL; generate ids in JS to encrypt before insert`);
180
+ r[plan.pkKey] = v;
181
+ }
182
+ return;
183
+ }
184
+ if (SERIAL_TYPES.has(plan.pkColumn.columnType) || plan.pkColumn.generatedIdentity) {
185
+ const ids = await reserveIds(plan.table, missing.length, db);
186
+ missing.forEach((r, i) => (r[plan.pkKey] = ids[i]));
187
+ return;
188
+ }
189
+ throw requestError(`${plan.name}.${plan.pkColumn.name} is unknown before the insert: pass it, give the column a $defaultFn (e.g. crypto.randomUUID), ` +
190
+ `pass { db } for PostgreSQL serial keys, or use the two-phase write (pendingRow, then encryptRow with { recordId })`);
191
+ };
192
+ const encryptMany = async (plan, rows, ids) => {
193
+ const out = rows.map((r) => ({ ...r }));
194
+ const items = [];
195
+ const targets = [];
196
+ out.forEach((row, i) => {
197
+ for (const f of plan.fields) {
198
+ if (f.indexKey && row[f.indexKey] !== undefined && row[f.key] === undefined) {
199
+ throw requestError(`${plan.name}.${f.indexColumn.name} is the blind index of ${f.name} and is managed by Northstar Cipher`);
200
+ }
201
+ const v = row[f.key];
202
+ if (v === undefined)
203
+ continue;
204
+ if (v === null) {
205
+ if (f.indexKey)
206
+ row[f.indexKey] = null;
207
+ continue;
208
+ }
209
+ const lb = `${plan.name}.${f.name}`;
210
+ const recordId = recordIdOf(ids[i], lb);
211
+ items.push({ collection: plan.name, field: f.name, recordId, plaintext: plaintextOf(v, lb), normalization: f.normalization, label: `${lb} (${plan.pkColumn.name}=${recordId})` });
212
+ targets.push({ row, f });
213
+ }
214
+ });
215
+ const enc = await encryptValues(cipher, items, { keyId: options.keyId });
216
+ enc.forEach((e, i) => {
217
+ const { row, f } = targets[i];
218
+ row[f.key] = e.ciphertext;
219
+ if (f.indexKey)
220
+ row[f.indexKey] = e.index ?? null;
221
+ });
222
+ return out;
223
+ };
224
+ const decryptMany = async (plan, rows, purpose) => {
225
+ const out = rows.map((r) => ({ ...r }));
226
+ const items = [];
227
+ const targets = [];
228
+ for (const row of out) {
229
+ for (const f of plan.fields) {
230
+ if (!(f.key in row))
231
+ continue;
232
+ const stored = row[f.key];
233
+ const lb = `${plan.name}.${f.name}`;
234
+ if (stored !== null && stored !== undefined && (row[plan.pkKey] === undefined || row[plan.pkKey] === null)) {
235
+ throw requestError(`${lb}: select the primary key (${plan.pkKey}) together with encrypted columns`);
236
+ }
237
+ const recordId = stored === null || stored === undefined ? '' : recordIdOf(row[plan.pkKey], lb);
238
+ items.push({ collection: plan.name, field: f.name, recordId, stored, label: `${lb} (${plan.pkColumn.name}=${recordId})` });
239
+ targets.push({ row, key: f.key });
240
+ }
241
+ }
242
+ const plain = await decryptValues(cipher, items, { purpose: purpose ?? options.purpose, readLegacyPlaintext: options.readLegacyPlaintext === true });
243
+ plain.forEach((p, i) => (targets[i].row[targets[i].key] = p));
244
+ return out;
245
+ };
246
+ return {
247
+ async encryptRow(table, row, opts = {}) {
248
+ const plan = planOf(table);
249
+ if (!row || typeof row !== 'object')
250
+ throw requestError('Drizzle: encryptRow expects a row object');
251
+ const copy = { ...row };
252
+ let id = opts.recordId;
253
+ if (id === undefined) {
254
+ if (plan.fields.some((f) => copy[f.key] !== undefined && copy[f.key] !== null))
255
+ await fillIds(plan, [copy], opts.db);
256
+ id = copy[plan.pkKey];
257
+ }
258
+ return (await encryptMany(plan, [copy], [id]))[0];
259
+ },
260
+ async encryptRows(table, rows, opts = {}) {
261
+ const plan = planOf(table);
262
+ const copies = rows.map((r) => ({ ...r }));
263
+ await fillIds(plan, copies.filter((r) => plan.fields.some((f) => r[f.key] !== undefined && r[f.key] !== null)), opts.db);
264
+ return (await encryptMany(plan, copies, copies.map((r) => r[plan.pkKey])));
265
+ },
266
+ async decryptRows(table, rows, opts = {}) {
267
+ return decryptMany(planOf(table), rows, opts.purpose);
268
+ },
269
+ async decryptRow(table, row, opts = {}) {
270
+ if (row === undefined || row === null)
271
+ return row;
272
+ return (await decryptMany(planOf(table), [row], opts.purpose))[0];
273
+ },
274
+ async searchTokens(column, value) {
275
+ const hit = columnPlans.get(column);
276
+ if (!hit || !hit.field.indexColumn)
277
+ throw configError(`Drizzle: ${column?.name ?? 'column'} is not a searchable encrypted column`);
278
+ const f = hit.field;
279
+ return searchTokensFor(cipher, { collection: hit.plan.name, field: f.name, normalization: f.normalization, values: value, keyId: options.keyId, label: `${hit.plan.name}.${f.name}` });
280
+ },
281
+ reserveIds,
282
+ };
283
+ }
284
+ export { CipherError };
285
+ //# sourceMappingURL=drizzle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drizzle.js","sourceRoot":"","sources":["../../src/integrations/drizzle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Mongoose (v9) schema plugin: transparent field-level encryption.
3
+ *
4
+ * ```ts
5
+ * import { northstarCipherPlugin } from '@northstartech/cipher/mongoose'
6
+ *
7
+ * const todoSchema = new Schema({ title: String, email: String })
8
+ * todoSchema.plugin(northstarCipherPlugin, { cipher, fields: ['title', { path: 'email', searchable: 'email' }] })
9
+ * const Todo = model('Todo', todoSchema)
10
+ *
11
+ * await Todo.create({ title: 'Buy milk', email: 'ana@example.com' })
12
+ * const todos = await Todo.find() // one decrypt request per query
13
+ * const mine = await Todo.find().cipherWhere('email', 'ANA@example.com') // exact match on email_bidx
14
+ * ```
15
+ *
16
+ * ObjectIds exist before the insert, so `_id` is the record id. Validation always
17
+ * sees plaintext; setters (`lowercase`, `trim`, …) apply to plaintext and never
18
+ * to ciphertext. See README "Mongoose".
19
+ *
20
+ * Northstar Search: `search: { index: cipher.index('todos'), fields: ['title', 'email', 'done'] }`
21
+ * keeps an index in sync on `save()`, `insertMany()`, `doc.deleteOne()` and
22
+ * `findOneAndDelete()` (encrypted paths are indexed from their stored ciphertext,
23
+ * which the enclave decrypts); `Todo.cipherSearchDocuments(docs)` builds documents for a (re)index.
24
+ */
25
+ import { type Schema } from 'mongoose';
26
+ import type { NorthstarCipher } from '../client.js';
27
+ import { CipherError } from '../errors.js';
28
+ import { type CipherIntegrationOptions, type EncryptedFieldOptions, type EncryptedFieldSpec, type SearchSyncOptions } from './shared.js';
29
+ import type { SearchDocument } from '../search.js';
30
+ export { CIPHERTEXT_PREFIX, PENDING_SENTINEL } from './shared.js';
31
+ export type { EncryptedFieldOptions, EncryptedFieldSpec, CipherIntegrationOptions, SearchSyncOptions } from './shared.js';
32
+ export type MongooseFieldSpec = string | ({
33
+ path: string;
34
+ } & EncryptedFieldOptions);
35
+ export interface NorthstarMongooseOptions extends CipherIntegrationOptions {
36
+ cipher: NorthstarCipher;
37
+ /** Encrypted String paths: `['title', { path: 'email', searchable: 'email' }]` or `{ title: true, … }`. */
38
+ fields: MongooseFieldSpec[] | Record<string, EncryptedFieldSpec>;
39
+ /**
40
+ * Keep a Northstar Search index in sync (`save()`, `insertMany()`, `doc.deleteOne()`,
41
+ * `findOneAndDelete()`; changes are indexed when the write returns, also inside
42
+ * transactions). Query updates and `deleteMany()` are not tracked: reindex after those.
43
+ */
44
+ search?: SearchSyncOptions;
45
+ }
46
+ /** Query helper added by the plugin (add to your schema's `TQueryHelpers` type). */
47
+ export interface CipherQueryHelpers {
48
+ /** Exact match on a searchable encrypted path (resolved to `<path>_bidx: { $in: tokens }` when the query runs). */
49
+ cipherWhere(path: string, value: string | string[]): this;
50
+ }
51
+ /** Statics added by the plugin. */
52
+ export interface CipherModelStatics {
53
+ /** `{ <path>_bidx: { $in: tokens } }` for use in any filter. */
54
+ cipherFilter(path: string, value: string | string[]): Promise<Record<string, {
55
+ $in: string[];
56
+ }>>;
57
+ /** Decrypt documents or plain objects obtained outside `find` (aggregate, cursors, change streams). */
58
+ cipherDecrypt<T>(docs: T | T[]): Promise<T | T[]>;
59
+ /**
60
+ * Northstar Search documents for documents or plain objects (`fields` default to
61
+ * `search.fields`). Raw reads skip decryption, so
62
+ * `Todo.cipherSearchDocuments(await Todo.collection.find().toArray())` (or `aggregate()`
63
+ * results) index the stored ciphertext, which the enclave decrypts.
64
+ */
65
+ cipherSearchDocuments(docs: unknown[], fields?: string[]): SearchDocument[];
66
+ }
67
+ /**
68
+ * Mongoose schema plugin. `schema.plugin(northstarCipherPlugin, { cipher, fields })`.
69
+ * Apply before compiling the model.
70
+ */
71
+ export declare function northstarCipherPlugin(schema: Schema, options: NorthstarMongooseOptions): void;
72
+ export { CipherError };