@forklaunch/core 1.5.20 → 1.6.2
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/lib/persistence/fieldEncryptor.d.ts +137 -9
- package/lib/persistence/fieldEncryptor.d.ts.map +1 -1
- package/lib/persistence/index.d.ts +2 -1
- package/lib/persistence/index.d.ts.map +1 -1
- package/lib/persistence/index.js +413 -29
- package/lib/persistence/index.js.map +1 -1
- package/lib/persistence/index.mjs +395 -29
- package/lib/persistence/index.mjs.map +1 -1
- package/lib/persistence/keyRotation.d.ts +118 -0
- package/lib/persistence/keyRotation.d.ts.map +1 -0
- package/lib/persistence/tenantEm.d.ts +18 -5
- package/lib/persistence/tenantEm.d.ts.map +1 -1
- package/package.json +11 -11
|
@@ -10,17 +10,125 @@ export declare class EncryptionRequiredError extends Error {
|
|
|
10
10
|
readonly name: 'EncryptionRequiredError';
|
|
11
11
|
constructor(message?: string);
|
|
12
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Ciphertext envelopes the encryptor reads. Only `v3` names its key.
|
|
15
|
+
*
|
|
16
|
+
* v1:{iv}:{tag}:{data} random IV (legacy)
|
|
17
|
+
* v2:{iv}:{tag}:{data} deterministic IV
|
|
18
|
+
* v3:{keyId}:{iv}:{tag}:{data} deterministic IV, stamped with the key fingerprint
|
|
19
|
+
*/
|
|
20
|
+
export declare const ENCRYPTED_PREFIXES: readonly ['v1:', 'v2:', 'v3:'];
|
|
21
|
+
/** True when the string carries one of the encryptor's envelopes. */
|
|
22
|
+
export declare function isEncryptedCiphertext(value: unknown): value is string;
|
|
23
|
+
/**
|
|
24
|
+
* Which envelope new values are written in.
|
|
25
|
+
*
|
|
26
|
+
* 'v2' unstamped. Default. Byte-compatible with every existing row, so
|
|
27
|
+
* equality lookups keep matching rows written before the ring existed.
|
|
28
|
+
* 'v3' stamped with the writing key's fingerprint. Reads resolve the key
|
|
29
|
+
* directly, a missing key fails by name, and the rows still under a
|
|
30
|
+
* key can be counted with a LIKE. Switch to it only after a rotation
|
|
31
|
+
* sweep has rewritten every row: a v3 write does not byte-match a v2
|
|
32
|
+
* row of the same plaintext, so lookups miss until the sweep is done.
|
|
33
|
+
*/
|
|
34
|
+
export type CiphertextFormat = 'v2' | 'v3';
|
|
35
|
+
/** Environment variable holding the key new values are written with. */
|
|
36
|
+
export declare const ENCRYPTION_KEY_ENV = "ENCRYPTION_KEY";
|
|
37
|
+
/** Environment variable holding every previous key, comma separated. */
|
|
38
|
+
export declare const LEGACY_ENCRYPTION_KEYS_ENV = "LEGACY_ENCRYPTION_KEYS";
|
|
39
|
+
/** Older single-key spelling of {@link LEGACY_ENCRYPTION_KEYS_ENV}; still honoured. */
|
|
40
|
+
export declare const LEGACY_ENCRYPTION_KEY_ENV = "LEGACY_ENCRYPTION_KEY";
|
|
41
|
+
/** Environment variable selecting the write format, `v2` (default) or `v3`. */
|
|
42
|
+
export declare const ENCRYPTION_FORMAT_ENV = "ENCRYPTION_FORMAT";
|
|
43
|
+
/**
|
|
44
|
+
* Split a key list as it appears in an environment variable: comma,
|
|
45
|
+
* semicolon or whitespace separated, optionally quoted. Blanks and
|
|
46
|
+
* duplicates are dropped, order is kept.
|
|
47
|
+
*/
|
|
48
|
+
export declare function parseEncryptionKeyList(...sources: (string | undefined | null)[]): string[];
|
|
49
|
+
/**
|
|
50
|
+
* A short, non-reversible fingerprint of a master key, for logs, reports
|
|
51
|
+
* and the `v3` envelope. Two deployments with the same key get the same
|
|
52
|
+
* id; the id reveals nothing about the key.
|
|
53
|
+
*/
|
|
54
|
+
export declare function encryptionKeyId(masterKey: string): string;
|
|
55
|
+
/**
|
|
56
|
+
* The key fingerprint a stored value is stamped with, or null for `v1`/`v2`
|
|
57
|
+
* values, which name no key.
|
|
58
|
+
*/
|
|
59
|
+
export declare function stampedKeyId(ciphertext: string): string | null;
|
|
60
|
+
/** SQL LIKE pattern matching every value stamped with `keyId`. */
|
|
61
|
+
export declare function stampedPrefix(keyId: string): string;
|
|
62
|
+
export interface FieldEncryptorOptions {
|
|
63
|
+
/**
|
|
64
|
+
* Keys that data may still be encrypted under, newest first. Values are
|
|
65
|
+
* never written with these; they are only tried when the current key
|
|
66
|
+
* cannot open a ciphertext. Keep a key here until a rotation sweep
|
|
67
|
+
* (`reencryptEncryptedColumns`) reports nothing left under it.
|
|
68
|
+
*/
|
|
69
|
+
previousKeys?: readonly string[];
|
|
70
|
+
/** Envelope for new values. See {@link CiphertextFormat}. Default `v2`. */
|
|
71
|
+
format?: CiphertextFormat;
|
|
72
|
+
}
|
|
73
|
+
/** What {@link FieldEncryptor.open} learned about a ciphertext. */
|
|
74
|
+
export interface OpenedCiphertext {
|
|
75
|
+
plaintext: string;
|
|
76
|
+
/** Fingerprint of the key that opened it; see {@link encryptionKeyId}. */
|
|
77
|
+
keyId: string;
|
|
78
|
+
/** True when the current key opened it. */
|
|
79
|
+
current: boolean;
|
|
80
|
+
/** Envelope the value was stored in. */
|
|
81
|
+
version: 'v1' | 'v2' | 'v3';
|
|
82
|
+
/**
|
|
83
|
+
* True when a rewrite would change the stored bytes: the value is under
|
|
84
|
+
* a previous key, or it is unstamped while the encryptor writes `v3`.
|
|
85
|
+
*/
|
|
86
|
+
stale: boolean;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* AES-256-GCM field encryption with per-tenant keys derived by HKDF from a
|
|
90
|
+
* master key, and a key ring for rotation.
|
|
91
|
+
*
|
|
92
|
+
* The ring has one current key and any number of previous keys. Every
|
|
93
|
+
* write uses the current key. A `v3` value names its key, so a read looks
|
|
94
|
+
* it up directly and a missing key fails by name. A `v1`/`v2` value names
|
|
95
|
+
* nothing, so a read tries the current key and then each previous key in
|
|
96
|
+
* order; AES-GCM's authentication tag makes "this key opens it" a real
|
|
97
|
+
* check rather than a guess. Either way the master key can change without
|
|
98
|
+
* a downtime window: add the old key to `previousKeys`, deploy, run the
|
|
99
|
+
* rotation sweep, then drop the old key.
|
|
100
|
+
*/
|
|
13
101
|
export declare class FieldEncryptor {
|
|
14
|
-
private readonly
|
|
15
|
-
|
|
102
|
+
private readonly ring;
|
|
103
|
+
private readonly byId;
|
|
104
|
+
private readonly derived;
|
|
105
|
+
readonly format: CiphertextFormat;
|
|
106
|
+
constructor(masterKey: string, options?: FieldEncryptorOptions);
|
|
107
|
+
/**
|
|
108
|
+
* Build the ring from the environment: `ENCRYPTION_KEY` plus
|
|
109
|
+
* `LEGACY_ENCRYPTION_KEYS` (comma separated) and the older
|
|
110
|
+
* `LEGACY_ENCRYPTION_KEY`; `ENCRYPTION_FORMAT` selects the envelope.
|
|
111
|
+
*/
|
|
112
|
+
static fromEnv(env?: NodeJS.ProcessEnv): FieldEncryptor;
|
|
113
|
+
/** Fingerprints of the ring, current first. Safe to log. */
|
|
114
|
+
get keyIds(): {
|
|
115
|
+
current: string;
|
|
116
|
+
previous: string[];
|
|
117
|
+
};
|
|
118
|
+
/** A copy of this encryptor with a different set of previous keys. */
|
|
119
|
+
withPreviousKeys(previousKeys: readonly string[]): FieldEncryptor;
|
|
120
|
+
/** A copy of this encryptor writing a different envelope. */
|
|
121
|
+
withFormat(format: CiphertextFormat): FieldEncryptor;
|
|
16
122
|
/**
|
|
17
|
-
* Derive a per-tenant 32-byte key using HKDF-SHA256
|
|
18
|
-
* The master key is
|
|
123
|
+
* Derive a per-tenant 32-byte key using HKDF-SHA256 from the CURRENT
|
|
124
|
+
* master key. The master key is the input key material and the tenantId
|
|
125
|
+
* the info context.
|
|
19
126
|
*/
|
|
20
127
|
deriveKey(tenantId: string): Buffer;
|
|
128
|
+
private deriveKeyFor;
|
|
21
129
|
/**
|
|
22
130
|
* Derive a deterministic IV from the key and plaintext using HMAC-SHA256,
|
|
23
|
-
* truncated to IV_BYTES. Same plaintext + same key
|
|
131
|
+
* truncated to IV_BYTES. Same plaintext + same key -> same IV -> same
|
|
24
132
|
* ciphertext. This enables WHERE clause matching on encrypted columns
|
|
25
133
|
* while maintaining AES-256-GCM authenticated encryption.
|
|
26
134
|
*
|
|
@@ -28,21 +136,41 @@ export declare class FieldEncryptor {
|
|
|
28
136
|
*/
|
|
29
137
|
private deriveDeterministicIv;
|
|
30
138
|
/**
|
|
31
|
-
* Encrypt a plaintext string.
|
|
139
|
+
* Encrypt a plaintext string under the current key.
|
|
32
140
|
*
|
|
33
141
|
* Uses deterministic encryption (HMAC-derived IV) so the same plaintext
|
|
34
142
|
* always produces the same ciphertext. This enables database WHERE clause
|
|
35
143
|
* matching and UNIQUE constraints on encrypted columns.
|
|
36
144
|
*
|
|
37
|
-
* @returns
|
|
145
|
+
* @returns `v2:{iv}:{tag}:{data}`, or `v3:{keyId}:{iv}:{tag}:{data}` when
|
|
146
|
+
* the encryptor's format is `v3`. All parts base64.
|
|
38
147
|
*/
|
|
39
148
|
encrypt(plaintext: string | null): string | null;
|
|
40
149
|
encrypt(plaintext: string | null, tenantId: string): string | null;
|
|
41
150
|
/**
|
|
42
|
-
* Decrypt a ciphertext string produced by {@link encrypt}
|
|
43
|
-
*
|
|
151
|
+
* Decrypt a ciphertext string produced by {@link encrypt}, in any
|
|
152
|
+
* envelope: `v3` by its stamped key, `v1`/`v2` by trying every key in
|
|
153
|
+
* the ring.
|
|
44
154
|
*/
|
|
45
155
|
decrypt(ciphertext: string | null): string | null;
|
|
46
156
|
decrypt(ciphertext: string | null, tenantId: string): string | null;
|
|
157
|
+
/**
|
|
158
|
+
* Decrypt and report which key opened the value and whether a rewrite
|
|
159
|
+
* would change it. Throws {@link DecryptionError} when no key in the
|
|
160
|
+
* ring does.
|
|
161
|
+
*/
|
|
162
|
+
open(ciphertext: string, tenantId?: string): OpenedCiphertext;
|
|
163
|
+
/**
|
|
164
|
+
* True when a rewrite would change the stored bytes: the value is under
|
|
165
|
+
* a previous key, or unstamped while this encryptor writes `v3`. Throws
|
|
166
|
+
* when no key opens it.
|
|
167
|
+
*/
|
|
168
|
+
needsRotation(ciphertext: string, tenantId?: string): boolean;
|
|
169
|
+
/**
|
|
170
|
+
* Rewrite a value under the current key in the current format. Returns
|
|
171
|
+
* the input unchanged when nothing would change, so callers can compare
|
|
172
|
+
* to detect writes.
|
|
173
|
+
*/
|
|
174
|
+
rotate(ciphertext: string | null, tenantId?: string): string | null;
|
|
47
175
|
}
|
|
48
176
|
//# sourceMappingURL=fieldEncryptor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fieldEncryptor.d.ts","sourceRoot":"","sources":["../../src/persistence/fieldEncryptor.ts"],"names":[],"mappings":"AAMA,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,QAAQ,CAAC,IAAI,EAAG,2BAA2B,CAAU;IACrD,YAAY,OAAO,SAA2C,EAE7D;CACF;AAED,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,IAAI,EAAG,iBAAiB,CAAU;IAC3C,YACE,OAAO,SAAyE,EAGjF;CACF;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,QAAQ,CAAC,IAAI,EAAG,yBAAyB,CAAU;IACnD,YACE,OAAO,SAAmE,EAG3E;CACF;
|
|
1
|
+
{"version":3,"file":"fieldEncryptor.d.ts","sourceRoot":"","sources":["../../src/persistence/fieldEncryptor.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,7 +5,8 @@ export { fp } from './compliancePropertyBuilder';
|
|
|
5
5
|
export { defineComplianceEntity } from './defineComplianceEntity';
|
|
6
6
|
export { wrapEmWithNativeQueryBlocking } from './complianceEventSubscriber';
|
|
7
7
|
export { EncryptedType, registerEncryptor, setEncryptionTenantId, withEncryptionContext, getCurrentTenantId } from './encryptedType';
|
|
8
|
-
export { FieldEncryptor, MissingEncryptionKeyError, DecryptionError, EncryptionRequiredError } from './fieldEncryptor';
|
|
8
|
+
export { FieldEncryptor, MissingEncryptionKeyError, DecryptionError, EncryptionRequiredError, parseEncryptionKeyList, encryptionKeyId, stampedKeyId, stampedPrefix, isEncryptedCiphertext, ENCRYPTED_PREFIXES, ENCRYPTION_FORMAT_ENV, ENCRYPTION_KEY_ENV, LEGACY_ENCRYPTION_KEYS_ENV, LEGACY_ENCRYPTION_KEY_ENV, type FieldEncryptorOptions, type OpenedCiphertext, type CiphertextFormat } from './fieldEncryptor';
|
|
9
|
+
export { reencryptEncryptedColumns, classifyEncryptedValue, collectFallbackTenantIds, defaultTenantIdsFor, entityMetadataList, rotationTotals, countValuesByKeyId, isEncryptedValue, type ReencryptOptions, type RotationOutcome, type RotationTableReport, type SqlExecute, type EntityMetadataLike } from './keyRotation';
|
|
9
10
|
export { setupTenantFilter, getSuperAdminContext, createTenantFilterDef, TENANT_FILTER_NAME } from './tenantFilter';
|
|
10
11
|
export { setupRls, RlsEventSubscriber, type RlsConfig } from './rls';
|
|
11
12
|
export type { AnyMikroORM, ResolvedEntity } from './mikroOrm.types';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/persistence/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,eAAe,EACf,KAAK,eAAe,IAAI,mBAAmB,EAC3C,qBAAqB,EACrB,yBAAyB,EACzB,wBAAwB,EAExB,eAAe,EACf,KAAK,eAAe,IAAI,mBAAmB,EAC3C,KAAK,eAAe,EACpB,iBAAiB,EACjB,aAAa,EACb,gBAAgB,EAChB,KAAK,cAAc,EACnB,kBAAkB,EAClB,uBAAuB,EACvB,oBAAoB,EACpB,kBAAkB,EAClB,KAAK,wBAAwB,EAC7B,KAAK,eAAe,EACpB,KAAK,yBAAyB,EAC9B,KAAK,+BAA+B,EACrC,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,6BAA6B,EAC7B,KAAK,yBAAyB,EAC/B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,gBAAgB,EAChB,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,0BAA0B,EAC/B,KAAK,2BAA2B,EAChC,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,+BAA+B,EACrC,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,EAAE,EAAE,MAAM,6BAA6B,CAAC;AAGjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAGlE,OAAO,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AAG5E,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,kBAAkB,EACnB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,cAAc,EACd,yBAAyB,EACzB,eAAe,EACf,uBAAuB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/persistence/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,eAAe,EACf,KAAK,eAAe,IAAI,mBAAmB,EAC3C,qBAAqB,EACrB,yBAAyB,EACzB,wBAAwB,EAExB,eAAe,EACf,KAAK,eAAe,IAAI,mBAAmB,EAC3C,KAAK,eAAe,EACpB,iBAAiB,EACjB,aAAa,EACb,gBAAgB,EAChB,KAAK,cAAc,EACnB,kBAAkB,EAClB,uBAAuB,EACvB,oBAAoB,EACpB,kBAAkB,EAClB,KAAK,wBAAwB,EAC7B,KAAK,eAAe,EACpB,KAAK,yBAAyB,EAC9B,KAAK,+BAA+B,EACrC,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,6BAA6B,EAC7B,KAAK,yBAAyB,EAC/B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,gBAAgB,EAChB,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,0BAA0B,EAC/B,KAAK,2BAA2B,EAChC,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,+BAA+B,EACrC,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,EAAE,EAAE,MAAM,6BAA6B,CAAC;AAGjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAGlE,OAAO,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AAG5E,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,kBAAkB,EACnB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,cAAc,EACd,yBAAyB,EACzB,eAAe,EACf,uBAAuB,EACvB,sBAAsB,EACtB,eAAe,EACf,YAAY,EACZ,aAAa,EACb,qBAAqB,EACrB,kBAAkB,EAClB,qBAAqB,EACrB,kBAAkB,EAClB,0BAA0B,EAC1B,yBAAyB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACtB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,yBAAyB,EACzB,sBAAsB,EACtB,wBAAwB,EACxB,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAChB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,UAAU,EACf,KAAK,kBAAkB,EACxB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EACnB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAErE,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAIpE,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC"}
|