@elanlanguages/bridge-anonymization 0.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.
Files changed (126) hide show
  1. package/README.md +382 -0
  2. package/dist/crypto/index.d.ts +6 -0
  3. package/dist/crypto/index.d.ts.map +1 -0
  4. package/dist/crypto/index.js +6 -0
  5. package/dist/crypto/index.js.map +1 -0
  6. package/dist/crypto/pii-map-crypto.d.ts +100 -0
  7. package/dist/crypto/pii-map-crypto.d.ts.map +1 -0
  8. package/dist/crypto/pii-map-crypto.js +163 -0
  9. package/dist/crypto/pii-map-crypto.js.map +1 -0
  10. package/dist/index.d.ts +173 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +294 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/ner/bio-decoder.d.ts +64 -0
  15. package/dist/ner/bio-decoder.d.ts.map +1 -0
  16. package/dist/ner/bio-decoder.js +216 -0
  17. package/dist/ner/bio-decoder.js.map +1 -0
  18. package/dist/ner/index.d.ts +10 -0
  19. package/dist/ner/index.d.ts.map +1 -0
  20. package/dist/ner/index.js +10 -0
  21. package/dist/ner/index.js.map +1 -0
  22. package/dist/ner/model-manager.d.ts +102 -0
  23. package/dist/ner/model-manager.d.ts.map +1 -0
  24. package/dist/ner/model-manager.js +253 -0
  25. package/dist/ner/model-manager.js.map +1 -0
  26. package/dist/ner/ner-model.d.ts +114 -0
  27. package/dist/ner/ner-model.d.ts.map +1 -0
  28. package/dist/ner/ner-model.js +240 -0
  29. package/dist/ner/ner-model.js.map +1 -0
  30. package/dist/ner/onnx-runtime.d.ts +45 -0
  31. package/dist/ner/onnx-runtime.d.ts.map +1 -0
  32. package/dist/ner/onnx-runtime.js +99 -0
  33. package/dist/ner/onnx-runtime.js.map +1 -0
  34. package/dist/ner/tokenizer.d.ts +140 -0
  35. package/dist/ner/tokenizer.d.ts.map +1 -0
  36. package/dist/ner/tokenizer.js +341 -0
  37. package/dist/ner/tokenizer.js.map +1 -0
  38. package/dist/pipeline/index.d.ts +9 -0
  39. package/dist/pipeline/index.d.ts.map +1 -0
  40. package/dist/pipeline/index.js +9 -0
  41. package/dist/pipeline/index.js.map +1 -0
  42. package/dist/pipeline/prenormalize.d.ts +48 -0
  43. package/dist/pipeline/prenormalize.d.ts.map +1 -0
  44. package/dist/pipeline/prenormalize.js +94 -0
  45. package/dist/pipeline/prenormalize.js.map +1 -0
  46. package/dist/pipeline/resolver.d.ts +56 -0
  47. package/dist/pipeline/resolver.d.ts.map +1 -0
  48. package/dist/pipeline/resolver.js +238 -0
  49. package/dist/pipeline/resolver.js.map +1 -0
  50. package/dist/pipeline/tagger.d.ts +74 -0
  51. package/dist/pipeline/tagger.d.ts.map +1 -0
  52. package/dist/pipeline/tagger.js +169 -0
  53. package/dist/pipeline/tagger.js.map +1 -0
  54. package/dist/pipeline/validator.d.ts +65 -0
  55. package/dist/pipeline/validator.d.ts.map +1 -0
  56. package/dist/pipeline/validator.js +264 -0
  57. package/dist/pipeline/validator.js.map +1 -0
  58. package/dist/recognizers/base.d.ts +78 -0
  59. package/dist/recognizers/base.d.ts.map +1 -0
  60. package/dist/recognizers/base.js +100 -0
  61. package/dist/recognizers/base.js.map +1 -0
  62. package/dist/recognizers/bic-swift.d.ts +10 -0
  63. package/dist/recognizers/bic-swift.d.ts.map +1 -0
  64. package/dist/recognizers/bic-swift.js +107 -0
  65. package/dist/recognizers/bic-swift.js.map +1 -0
  66. package/dist/recognizers/credit-card.d.ts +32 -0
  67. package/dist/recognizers/credit-card.d.ts.map +1 -0
  68. package/dist/recognizers/credit-card.js +160 -0
  69. package/dist/recognizers/credit-card.js.map +1 -0
  70. package/dist/recognizers/custom-id.d.ts +28 -0
  71. package/dist/recognizers/custom-id.d.ts.map +1 -0
  72. package/dist/recognizers/custom-id.js +116 -0
  73. package/dist/recognizers/custom-id.js.map +1 -0
  74. package/dist/recognizers/email.d.ts +10 -0
  75. package/dist/recognizers/email.d.ts.map +1 -0
  76. package/dist/recognizers/email.js +75 -0
  77. package/dist/recognizers/email.js.map +1 -0
  78. package/dist/recognizers/iban.d.ts +14 -0
  79. package/dist/recognizers/iban.d.ts.map +1 -0
  80. package/dist/recognizers/iban.js +67 -0
  81. package/dist/recognizers/iban.js.map +1 -0
  82. package/dist/recognizers/index.d.ts +20 -0
  83. package/dist/recognizers/index.d.ts.map +1 -0
  84. package/dist/recognizers/index.js +42 -0
  85. package/dist/recognizers/index.js.map +1 -0
  86. package/dist/recognizers/ip-address.d.ts +14 -0
  87. package/dist/recognizers/ip-address.d.ts.map +1 -0
  88. package/dist/recognizers/ip-address.js +183 -0
  89. package/dist/recognizers/ip-address.js.map +1 -0
  90. package/dist/recognizers/phone.d.ts +10 -0
  91. package/dist/recognizers/phone.d.ts.map +1 -0
  92. package/dist/recognizers/phone.js +145 -0
  93. package/dist/recognizers/phone.js.map +1 -0
  94. package/dist/recognizers/registry.d.ts +59 -0
  95. package/dist/recognizers/registry.d.ts.map +1 -0
  96. package/dist/recognizers/registry.js +113 -0
  97. package/dist/recognizers/registry.js.map +1 -0
  98. package/dist/recognizers/url.d.ts +14 -0
  99. package/dist/recognizers/url.d.ts.map +1 -0
  100. package/dist/recognizers/url.js +121 -0
  101. package/dist/recognizers/url.js.map +1 -0
  102. package/dist/types/index.d.ts +134 -0
  103. package/dist/types/index.d.ts.map +1 -0
  104. package/dist/types/index.js +69 -0
  105. package/dist/types/index.js.map +1 -0
  106. package/dist/types/pii-types.d.ts +50 -0
  107. package/dist/types/pii-types.d.ts.map +1 -0
  108. package/dist/types/pii-types.js +114 -0
  109. package/dist/types/pii-types.js.map +1 -0
  110. package/dist/utils/iban-checksum.d.ts +23 -0
  111. package/dist/utils/iban-checksum.d.ts.map +1 -0
  112. package/dist/utils/iban-checksum.js +106 -0
  113. package/dist/utils/iban-checksum.js.map +1 -0
  114. package/dist/utils/index.d.ts +8 -0
  115. package/dist/utils/index.d.ts.map +1 -0
  116. package/dist/utils/index.js +8 -0
  117. package/dist/utils/index.js.map +1 -0
  118. package/dist/utils/luhn.d.ts +17 -0
  119. package/dist/utils/luhn.d.ts.map +1 -0
  120. package/dist/utils/luhn.js +55 -0
  121. package/dist/utils/luhn.js.map +1 -0
  122. package/dist/utils/offsets.d.ts +86 -0
  123. package/dist/utils/offsets.d.ts.map +1 -0
  124. package/dist/utils/offsets.js +124 -0
  125. package/dist/utils/offsets.js.map +1 -0
  126. package/package.json +62 -0
package/README.md ADDED
@@ -0,0 +1,382 @@
1
+ # Bridge Anonymization
2
+
3
+ On-device PII anonymization module for high-privacy translation workflows. Detects and replaces Personally Identifiable Information (PII) with placeholder tags while maintaining an encrypted mapping for later rehydration.
4
+
5
+ ## Features
6
+
7
+ - **Structured PII Detection**: Regex-based detection for emails, phones, IBANs, credit cards, IPs, URLs
8
+ - **Soft PII Detection**: ONNX-powered NER model for names, organizations, locations (auto-downloads on first use)
9
+ - **Secure PII Mapping**: AES-256-GCM encrypted storage of original PII values
10
+ - **Configurable Policies**: Customizable detection rules, thresholds, and allowlists
11
+ - **Validation & Leak Scanning**: Built-in validation and optional leak detection
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install bridge-anonymization
17
+ ```
18
+
19
+ > **Bun users**: Install `onnxruntime-web` additionally: `bun add bridge-anonymization onnxruntime-web`
20
+
21
+ ## Quick Start
22
+
23
+ ### Regex-Only Mode (No Downloads Required)
24
+
25
+ For structured PII like emails, phones, IBANs, credit cards:
26
+
27
+ ```typescript
28
+ import { anonymizeRegexOnly } from 'bridge-anonymization';
29
+
30
+ const result = await anonymizeRegexOnly(
31
+ 'Contact john@example.com or call +49 30 123456. IBAN: DE89370400440532013000'
32
+ );
33
+
34
+ console.log(result.anonymizedText);
35
+ // "Contact <PII type="EMAIL" id="1"/> or call <PII type="PHONE" id="2"/>. IBAN: <PII type="IBAN" id="3"/>"
36
+ ```
37
+
38
+ ### Full Mode with NER (Detects Names, Organizations, Locations)
39
+
40
+ The NER model is automatically downloaded on first use (~280 MB for quantized):
41
+
42
+ ```typescript
43
+ import { createAnonymizer } from 'bridge-anonymization';
44
+
45
+ const anonymizer = createAnonymizer({
46
+ ner: {
47
+ mode: 'quantized', // or 'standard' for full model (~1.1 GB)
48
+ onStatus: (status) => console.log(status), // Optional progress
49
+ }
50
+ });
51
+
52
+ await anonymizer.initialize(); // Downloads model if needed
53
+
54
+ const result = await anonymizer.anonymize(
55
+ 'Hello John Smith from Acme Corp in Berlin!'
56
+ );
57
+
58
+ console.log(result.anonymizedText);
59
+ // "Hello <PII type="PERSON" id="1"/> from <PII type="ORG" id="2"/> in <PII type="LOCATION" id="3"/>!"
60
+ ```
61
+
62
+ ### One-liner with NER
63
+
64
+ ```typescript
65
+ import { anonymizeWithNER } from 'bridge-anonymization';
66
+
67
+ const result = await anonymizeWithNER(
68
+ 'Contact John Smith at john@example.com',
69
+ { mode: 'quantized', onStatus: console.log }
70
+ );
71
+ ```
72
+
73
+ ## API Reference
74
+
75
+ ### Configuration Options
76
+
77
+ ```typescript
78
+ import { createAnonymizer, InMemoryKeyProvider } from 'bridge-anonymization';
79
+
80
+ const anonymizer = createAnonymizer({
81
+ // NER configuration
82
+ ner: {
83
+ mode: 'quantized', // 'standard' | 'quantized' | 'disabled' | 'custom'
84
+ autoDownload: true, // Auto-download model if not present
85
+ onStatus: (s) => {}, // Status messages callback
86
+ onDownloadProgress: (p) => {}, // Download progress callback
87
+
88
+ // For 'custom' mode only:
89
+ modelPath: './my-model.onnx',
90
+ vocabPath: './vocab.txt',
91
+ },
92
+
93
+ // Encryption key provider
94
+ keyProvider: new InMemoryKeyProvider(),
95
+
96
+ // Custom policy
97
+ defaultPolicy: { /* ... */ },
98
+ });
99
+
100
+ await anonymizer.initialize();
101
+ ```
102
+
103
+ ### NER Modes
104
+
105
+ | Mode | Description | Size | Auto-Download |
106
+ |------|-------------|------|---------------|
107
+ | `'disabled'` | No NER, regex only | 0 | N/A |
108
+ | `'quantized'` | Smaller model, ~95% accuracy | ~280 MB | Yes |
109
+ | `'standard'` | Full model, best accuracy | ~1.1 GB | Yes |
110
+ | `'custom'` | Your own ONNX model | Varies | No |
111
+
112
+ ### Main Functions
113
+
114
+ #### `createAnonymizer(config?)`
115
+
116
+ Creates an anonymizer instance:
117
+
118
+ ```typescript
119
+ const anonymizer = createAnonymizer({
120
+ ner: { mode: 'quantized' }
121
+ });
122
+
123
+ await anonymizer.initialize();
124
+ const result = await anonymizer.anonymize('text');
125
+ await anonymizer.dispose();
126
+ ```
127
+
128
+ #### `anonymize(text, locale?, policy?)`
129
+
130
+ One-off anonymization (regex-only by default):
131
+
132
+ ```typescript
133
+ const result = await anonymize('Contact test@example.com');
134
+ ```
135
+
136
+ #### `anonymizeWithNER(text, nerConfig, policy?)`
137
+
138
+ One-off anonymization with NER:
139
+
140
+ ```typescript
141
+ const result = await anonymizeWithNER(
142
+ 'Hello John Smith',
143
+ { mode: 'quantized' }
144
+ );
145
+ ```
146
+
147
+ #### `anonymizeRegexOnly(text, policy?)`
148
+
149
+ Fast regex-only anonymization:
150
+
151
+ ```typescript
152
+ const result = await anonymizeRegexOnly('Card: 4111111111111111');
153
+ ```
154
+
155
+ ### Result Structure
156
+
157
+ ```typescript
158
+ interface AnonymizationResult {
159
+ // Text with PII replaced by placeholder tags
160
+ anonymizedText: string;
161
+
162
+ // Detected entities (without original text for safety)
163
+ entities: Array<{
164
+ type: PIIType;
165
+ id: number;
166
+ start: number;
167
+ end: number;
168
+ confidence: number;
169
+ source: 'REGEX' | 'NER';
170
+ }>;
171
+
172
+ // Encrypted PII mapping (for later rehydration)
173
+ piiMap: {
174
+ ciphertext: string; // Base64
175
+ iv: string; // Base64
176
+ authTag: string; // Base64
177
+ };
178
+
179
+ // Processing statistics
180
+ stats: {
181
+ countsByType: Record<PIIType, number>;
182
+ totalEntities: number;
183
+ processingTimeMs: number;
184
+ modelVersion: string;
185
+ leakScanPassed?: boolean;
186
+ };
187
+ }
188
+ ```
189
+
190
+ ## Supported PII Types
191
+
192
+ | Type | Description | Detection Method |
193
+ |------|-------------|------------------|
194
+ | `EMAIL` | Email addresses | Regex |
195
+ | `PHONE` | Phone numbers (international) | Regex |
196
+ | `IBAN` | International Bank Account Numbers | Regex + Checksum |
197
+ | `BIC_SWIFT` | Bank Identifier Codes | Regex |
198
+ | `CREDIT_CARD` | Credit card numbers | Regex + Luhn |
199
+ | `IP_ADDRESS` | IPv4 and IPv6 addresses | Regex |
200
+ | `URL` | Web URLs | Regex |
201
+ | `CASE_ID` | Case/ticket numbers | Regex (configurable) |
202
+ | `CUSTOMER_ID` | Customer identifiers | Regex (configurable) |
203
+ | `PERSON` | Person names | NER |
204
+ | `ORG` | Organization names | NER |
205
+ | `LOCATION` | Location/place names | NER |
206
+ | `ADDRESS` | Physical addresses | NER |
207
+ | `DATE_OF_BIRTH` | Dates of birth | NER |
208
+
209
+ ## Configuration
210
+
211
+ ### Anonymization Policy
212
+
213
+ ```typescript
214
+ import { createAnonymizer, PIIType } from 'bridge-anonymization';
215
+
216
+ const anonymizer = createAnonymizer({
217
+ ner: { mode: 'quantized' },
218
+ defaultPolicy: {
219
+ // Which PII types to detect
220
+ enabledTypes: new Set([PIIType.EMAIL, PIIType.PHONE, PIIType.PERSON]),
221
+
222
+ // Confidence thresholds per type (0.0 - 1.0)
223
+ confidenceThresholds: new Map([
224
+ [PIIType.PERSON, 0.8],
225
+ [PIIType.EMAIL, 0.5],
226
+ ]),
227
+
228
+ // Terms to never treat as PII
229
+ allowlistTerms: new Set(['Customer Service', 'Help Desk']),
230
+
231
+ // Enable leak scanning on output
232
+ enableLeakScan: true,
233
+ },
234
+ });
235
+ ```
236
+
237
+ ### Custom Recognizers
238
+
239
+ Add domain-specific patterns:
240
+
241
+ ```typescript
242
+ import { createCustomIdRecognizer, PIIType, createAnonymizer } from 'bridge-anonymization';
243
+
244
+ const customRecognizer = createCustomIdRecognizer([
245
+ {
246
+ name: 'Order Number',
247
+ pattern: /\bORD-[A-Z0-9]{8}\b/g,
248
+ type: PIIType.CASE_ID,
249
+ },
250
+ ]);
251
+
252
+ const anonymizer = createAnonymizer();
253
+ anonymizer.getRegistry().register(customRecognizer);
254
+ ```
255
+
256
+ ## Model Management
257
+
258
+ Models are hosted on [Hugging Face Hub](https://huggingface.co/Xenova/xlm-roberta-base-ner-hrl) and automatically downloaded on first use.
259
+
260
+ **Cache locations:**
261
+ - **macOS**: `~/Library/Caches/bridge-anonymization/models/`
262
+ - **Linux**: `~/.cache/bridge-anonymization/models/`
263
+ - **Windows**: `%LOCALAPPDATA%/bridge-anonymization/models/`
264
+
265
+ ### Manual Model Management
266
+
267
+ ```typescript
268
+ import {
269
+ isModelDownloaded,
270
+ downloadModel,
271
+ clearModelCache,
272
+ listDownloadedModels,
273
+ getModelCacheDir
274
+ } from 'bridge-anonymization';
275
+
276
+ // Check if model is downloaded
277
+ const hasModel = await isModelDownloaded('quantized');
278
+
279
+ // Manually download
280
+ await downloadModel('quantized', (progress) => {
281
+ console.log(`${progress.file}: ${progress.percent}%`);
282
+ });
283
+
284
+ // List downloaded models
285
+ const models = await listDownloadedModels();
286
+
287
+ // Clear cache
288
+ await clearModelCache('quantized'); // or clearModelCache() for all
289
+ ```
290
+
291
+ ## Encryption & Security
292
+
293
+ The PII map is encrypted using AES-256-GCM:
294
+
295
+ ```typescript
296
+ import { createAnonymizer, KeyProvider, generateKey } from 'bridge-anonymization';
297
+
298
+ class SecureKeyProvider implements KeyProvider {
299
+ async getKey(): Promise<Buffer> {
300
+ // Retrieve from OS keychain, HSM, or secure storage
301
+ return await getKeyFromSecureStorage();
302
+ }
303
+ }
304
+
305
+ const anonymizer = createAnonymizer({
306
+ keyProvider: new SecureKeyProvider(),
307
+ ner: { mode: 'quantized' },
308
+ });
309
+ ```
310
+
311
+ ### Security Best Practices
312
+
313
+ - **Never log the raw PII map** - Always use encrypted storage
314
+ - **Rotate keys** - Implement key rotation for long-running applications
315
+ - **Use platform keystores** - iOS Keychain, Android Keystore, or OS credential managers
316
+ - **Enable leak scanning** - Catch any missed PII in output
317
+
318
+ ## Bun Support
319
+
320
+ This library works with [Bun](https://bun.sh). Since `onnxruntime-node` is a native Node.js addon, Bun users need `onnxruntime-web`:
321
+
322
+ ```bash
323
+ bun add bridge-anonymization onnxruntime-web
324
+ ```
325
+
326
+ Usage is identical - the library auto-detects the runtime:
327
+
328
+ ```typescript
329
+ import { createAnonymizer } from 'bridge-anonymization';
330
+
331
+ const anonymizer = createAnonymizer({
332
+ ner: { mode: 'quantized' }
333
+ });
334
+
335
+ await anonymizer.initialize();
336
+ const result = await anonymizer.anonymize('Hello John Smith');
337
+ ```
338
+
339
+ ## Performance
340
+
341
+ | Component | Time (2K chars) | Notes |
342
+ |-----------|-----------------|-------|
343
+ | Regex pass | ~5 ms | All regex recognizers |
344
+ | NER inference | ~100-150 ms | Quantized model |
345
+ | Total pipeline | ~150-200 ms | Full anonymization |
346
+
347
+ | Model | Size | First-Use Download |
348
+ |-------|------|-------------------|
349
+ | Quantized | ~280 MB | ~30s on fast connection |
350
+ | Standard | ~1.1 GB | ~2min on fast connection |
351
+
352
+ ## Development
353
+
354
+ ```bash
355
+ # Install dependencies
356
+ npm install
357
+
358
+ # Run tests
359
+ npm test
360
+
361
+ # Build
362
+ npm run build
363
+ ```
364
+
365
+ ### Building Custom Models
366
+
367
+ For development or custom models, you can use the setup script:
368
+
369
+ ```bash
370
+ # Requires Python 3.8+
371
+ npm run setup:ner # Standard model
372
+ npm run setup:ner:quantized # Quantized model
373
+ ```
374
+
375
+ ## Requirements
376
+
377
+ - Node.js >= 18.0.0 (ONNX runtime included automatically)
378
+ - Bun >= 1.0.0 (requires `onnxruntime-web`)
379
+
380
+ ## License
381
+
382
+ MIT
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Crypto Module
3
+ * Exports encryption utilities for PII map
4
+ */
5
+ export * from './pii-map-crypto.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/crypto/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,qBAAqB,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Crypto Module
3
+ * Exports encryption utilities for PII map
4
+ */
5
+ export * from './pii-map-crypto.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/crypto/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,qBAAqB,CAAC"}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * PII Map Encryption
3
+ * AES-256-GCM encryption for the PII mapping
4
+ */
5
+ import { EncryptedPIIMap } from '../types/index.js';
6
+ import type { RawPIIMap } from '../pipeline/tagger.js';
7
+ /**
8
+ * Encryption configuration
9
+ */
10
+ export interface EncryptionConfig {
11
+ /** Algorithm (default: aes-256-gcm) */
12
+ algorithm: string;
13
+ /** IV length in bytes (default: 12 for GCM) */
14
+ ivLength: number;
15
+ /** Auth tag length in bytes (default: 16) */
16
+ authTagLength: number;
17
+ }
18
+ /**
19
+ * Default encryption configuration
20
+ */
21
+ export declare const DEFAULT_ENCRYPTION_CONFIG: EncryptionConfig;
22
+ /**
23
+ * Key generation options
24
+ */
25
+ export interface KeyGenOptions {
26
+ /** Key length in bytes (default: 32 for AES-256) */
27
+ length: number;
28
+ }
29
+ /**
30
+ * Generates a random encryption key
31
+ * @returns Buffer containing the key
32
+ */
33
+ export declare function generateKey(options?: Partial<KeyGenOptions>): Buffer;
34
+ /**
35
+ * Derives a key from a password using PBKDF2
36
+ * @param password - Password string
37
+ * @param salt - Salt buffer (should be randomly generated and stored)
38
+ * @param iterations - Number of iterations (default: 100000)
39
+ * @returns Buffer containing the derived key
40
+ */
41
+ export declare function deriveKey(password: string, salt: Buffer, iterations?: number): Buffer;
42
+ /**
43
+ * Generates a random salt for key derivation
44
+ * @param length - Salt length in bytes (default: 16)
45
+ * @returns Buffer containing the salt
46
+ */
47
+ export declare function generateSalt(length?: number): Buffer;
48
+ /**
49
+ * Encrypts a PII map using AES-256-GCM
50
+ * @param piiMap - Raw PII map to encrypt
51
+ * @param key - 32-byte encryption key
52
+ * @param config - Encryption configuration
53
+ * @returns Encrypted PII map
54
+ */
55
+ export declare function encryptPIIMap(piiMap: RawPIIMap, key: Buffer, config?: Partial<EncryptionConfig>): EncryptedPIIMap;
56
+ /**
57
+ * Decrypts an encrypted PII map
58
+ * @param encrypted - Encrypted PII map
59
+ * @param key - 32-byte encryption key
60
+ * @param config - Encryption configuration
61
+ * @returns Decrypted PII map
62
+ */
63
+ export declare function decryptPIIMap(encrypted: EncryptedPIIMap, key: Buffer, config?: Partial<EncryptionConfig>): RawPIIMap;
64
+ /**
65
+ * Key provider interface for external key management
66
+ */
67
+ export interface KeyProvider {
68
+ /** Gets the current encryption key */
69
+ getKey(): Promise<Buffer>;
70
+ /** Rotates to a new key (optional) */
71
+ rotateKey?(): Promise<Buffer>;
72
+ }
73
+ /**
74
+ * Simple in-memory key provider (for testing/development)
75
+ * WARNING: Not secure for production use
76
+ */
77
+ export declare class InMemoryKeyProvider implements KeyProvider {
78
+ private key;
79
+ constructor(key?: Buffer);
80
+ getKey(): Promise<Buffer>;
81
+ rotateKey(): Promise<Buffer>;
82
+ }
83
+ /**
84
+ * Environment variable key provider
85
+ * Reads key from environment variable (base64 encoded)
86
+ */
87
+ export declare class EnvKeyProvider implements KeyProvider {
88
+ private envVarName;
89
+ constructor(envVarName?: string);
90
+ getKey(): Promise<Buffer>;
91
+ }
92
+ /**
93
+ * Validates that a key is suitable for AES-256
94
+ */
95
+ export declare function validateKey(key: Buffer): boolean;
96
+ /**
97
+ * Securely compares two buffers (timing-safe)
98
+ */
99
+ export declare function secureCompare(a: Buffer, b: Buffer): boolean;
100
+ //# sourceMappingURL=pii-map-crypto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pii-map-crypto.d.ts","sourceRoot":"","sources":["../../src/crypto/pii-map-crypto.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,eAAO,MAAM,yBAAyB,EAAE,gBAIvC,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,oDAAoD;IACpD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,GAAE,OAAO,CAAC,aAAa,CAAM,GAAG,MAAM,CAGxE;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,UAAU,GAAE,MAAe,GAC1B,MAAM,CAER;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,GAAE,MAAW,GAAG,MAAM,CAExD;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,SAAS,EACjB,GAAG,EAAE,MAAM,EACX,MAAM,GAAE,OAAO,CAAC,gBAAgB,CAAM,GACrC,eAAe,CAwCjB;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,eAAe,EAC1B,GAAG,EAAE,MAAM,EACX,MAAM,GAAE,OAAO,CAAC,gBAAgB,CAAM,GACrC,SAAS,CAuCX;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,sCAAsC;IACtC,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1B,sCAAsC;IACtC,SAAS,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CAC/B;AAED;;;GAGG;AACH,qBAAa,mBAAoB,YAAW,WAAW;IACrD,OAAO,CAAC,GAAG,CAAS;gBAER,GAAG,CAAC,EAAE,MAAM;IAIlB,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;IAIzB,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC;CAInC;AAED;;;GAGG;AACH,qBAAa,cAAe,YAAW,WAAW;IAChD,OAAO,CAAC,UAAU,CAAS;gBAEf,UAAU,GAAE,MAA6B;IAI/C,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;CAahC;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEhD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAK3D"}
@@ -0,0 +1,163 @@
1
+ /**
2
+ * PII Map Encryption
3
+ * AES-256-GCM encryption for the PII mapping
4
+ */
5
+ import * as crypto from 'crypto';
6
+ /**
7
+ * Default encryption configuration
8
+ */
9
+ export const DEFAULT_ENCRYPTION_CONFIG = {
10
+ algorithm: 'aes-256-gcm',
11
+ ivLength: 12,
12
+ authTagLength: 16,
13
+ };
14
+ /**
15
+ * Generates a random encryption key
16
+ * @returns Buffer containing the key
17
+ */
18
+ export function generateKey(options = {}) {
19
+ const length = options.length ?? 32;
20
+ return crypto.randomBytes(length);
21
+ }
22
+ /**
23
+ * Derives a key from a password using PBKDF2
24
+ * @param password - Password string
25
+ * @param salt - Salt buffer (should be randomly generated and stored)
26
+ * @param iterations - Number of iterations (default: 100000)
27
+ * @returns Buffer containing the derived key
28
+ */
29
+ export function deriveKey(password, salt, iterations = 100000) {
30
+ return crypto.pbkdf2Sync(password, salt, iterations, 32, 'sha256');
31
+ }
32
+ /**
33
+ * Generates a random salt for key derivation
34
+ * @param length - Salt length in bytes (default: 16)
35
+ * @returns Buffer containing the salt
36
+ */
37
+ export function generateSalt(length = 16) {
38
+ return crypto.randomBytes(length);
39
+ }
40
+ /**
41
+ * Encrypts a PII map using AES-256-GCM
42
+ * @param piiMap - Raw PII map to encrypt
43
+ * @param key - 32-byte encryption key
44
+ * @param config - Encryption configuration
45
+ * @returns Encrypted PII map
46
+ */
47
+ export function encryptPIIMap(piiMap, key, config = {}) {
48
+ const encConfig = { ...DEFAULT_ENCRYPTION_CONFIG, ...config };
49
+ // Validate key length
50
+ if (key.length !== 32) {
51
+ throw new Error(`Invalid key length: expected 32 bytes, got ${key.length}`);
52
+ }
53
+ // Convert map to JSON
54
+ const mapObject = {};
55
+ for (const [k, v] of piiMap) {
56
+ mapObject[k] = v;
57
+ }
58
+ const plaintext = JSON.stringify(mapObject);
59
+ // Generate random IV
60
+ const iv = crypto.randomBytes(encConfig.ivLength);
61
+ // Create cipher
62
+ const cipher = crypto.createCipheriv(encConfig.algorithm, key, iv, { authTagLength: encConfig.authTagLength });
63
+ // Encrypt
64
+ const encrypted = Buffer.concat([
65
+ cipher.update(plaintext, 'utf8'),
66
+ cipher.final(),
67
+ ]);
68
+ // Get auth tag
69
+ const authTag = cipher.getAuthTag();
70
+ return {
71
+ ciphertext: encrypted.toString('base64'),
72
+ iv: iv.toString('base64'),
73
+ authTag: authTag.toString('base64'),
74
+ };
75
+ }
76
+ /**
77
+ * Decrypts an encrypted PII map
78
+ * @param encrypted - Encrypted PII map
79
+ * @param key - 32-byte encryption key
80
+ * @param config - Encryption configuration
81
+ * @returns Decrypted PII map
82
+ */
83
+ export function decryptPIIMap(encrypted, key, config = {}) {
84
+ const encConfig = { ...DEFAULT_ENCRYPTION_CONFIG, ...config };
85
+ // Validate key length
86
+ if (key.length !== 32) {
87
+ throw new Error(`Invalid key length: expected 32 bytes, got ${key.length}`);
88
+ }
89
+ // Decode base64
90
+ const ciphertext = Buffer.from(encrypted.ciphertext, 'base64');
91
+ const iv = Buffer.from(encrypted.iv, 'base64');
92
+ const authTag = Buffer.from(encrypted.authTag, 'base64');
93
+ // Create decipher
94
+ const decipher = crypto.createDecipheriv(encConfig.algorithm, key, iv, { authTagLength: encConfig.authTagLength });
95
+ // Set auth tag
96
+ decipher.setAuthTag(authTag);
97
+ // Decrypt
98
+ const decrypted = Buffer.concat([
99
+ decipher.update(ciphertext),
100
+ decipher.final(),
101
+ ]);
102
+ // Parse JSON back to map
103
+ const mapObject = JSON.parse(decrypted.toString('utf8'));
104
+ const piiMap = new Map();
105
+ for (const [k, v] of Object.entries(mapObject)) {
106
+ piiMap.set(k, v);
107
+ }
108
+ return piiMap;
109
+ }
110
+ /**
111
+ * Simple in-memory key provider (for testing/development)
112
+ * WARNING: Not secure for production use
113
+ */
114
+ export class InMemoryKeyProvider {
115
+ key;
116
+ constructor(key) {
117
+ this.key = key ?? generateKey();
118
+ }
119
+ async getKey() {
120
+ return this.key;
121
+ }
122
+ async rotateKey() {
123
+ this.key = generateKey();
124
+ return this.key;
125
+ }
126
+ }
127
+ /**
128
+ * Environment variable key provider
129
+ * Reads key from environment variable (base64 encoded)
130
+ */
131
+ export class EnvKeyProvider {
132
+ envVarName;
133
+ constructor(envVarName = 'PII_ENCRYPTION_KEY') {
134
+ this.envVarName = envVarName;
135
+ }
136
+ async getKey() {
137
+ const keyBase64 = process.env[this.envVarName];
138
+ if (keyBase64 === undefined || keyBase64.length === 0) {
139
+ throw new Error(`Encryption key not found in environment variable: ${this.envVarName}`);
140
+ }
141
+ const key = Buffer.from(keyBase64, 'base64');
142
+ if (key.length !== 32) {
143
+ throw new Error(`Invalid key length from ${this.envVarName}: expected 32 bytes`);
144
+ }
145
+ return key;
146
+ }
147
+ }
148
+ /**
149
+ * Validates that a key is suitable for AES-256
150
+ */
151
+ export function validateKey(key) {
152
+ return key.length === 32;
153
+ }
154
+ /**
155
+ * Securely compares two buffers (timing-safe)
156
+ */
157
+ export function secureCompare(a, b) {
158
+ if (a.length !== b.length) {
159
+ return false;
160
+ }
161
+ return crypto.timingSafeEqual(a, b);
162
+ }
163
+ //# sourceMappingURL=pii-map-crypto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pii-map-crypto.js","sourceRoot":"","sources":["../../src/crypto/pii-map-crypto.ts"],"names":[],"mappings":"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"}