Skip to main content
The PII map containing original values is encrypted using AES-256-GCM via the Web Crypto API, which works in both Node.js and browsers.

How It Works

When you anonymize text, Rehydra:
  1. Builds a mapping of placeholder IDs to original values
  2. Serializes the map to JSON
  3. Encrypts using AES-256-GCM with a random IV
  4. Returns the encrypted bundle
const result = await anonymizer.anonymize('Contact [email protected]');

// The encrypted PII map
result.piiMap = {
  ciphertext: "a3f2...",  // Base64-encoded encrypted data
  iv: "7d8c...",          // Base64-encoded initialization vector
  authTag: "9e1f..."      // Base64-encoded authentication tag
};

Key Providers

Rehydra uses a KeyProvider interface for flexible key management:

InMemoryKeyProvider (Development)

Generates a random key on instantiation. Key is lost on page refresh.
import { InMemoryKeyProvider } from 'rehydra';

const keyProvider = new InMemoryKeyProvider();

const anonymizer = createAnonymizer({
  keyProvider
});
Only use InMemoryKeyProvider for development/testing. The key is lost when the process ends.

ConfigKeyProvider (Production)

Uses a pre-configured key from your environment:
import { ConfigKeyProvider } from 'rehydra';

// Generate key: openssl rand -base64 32
const keyBase64 = process.env.PII_ENCRYPTION_KEY;
const keyProvider = new ConfigKeyProvider(keyBase64);

const anonymizer = createAnonymizer({
  keyProvider
});

Custom KeyProvider

Implement the interface for custom storage:
import { KeyProvider } from 'rehydra';

class SecureKeyProvider implements KeyProvider {
  async getKey(): Promise<Uint8Array> {
    // Retrieve from secure storage, HSM, keychain, etc.
    return await getKeyFromSecureStorage();
  }
}

Decryption and Rehydration

To restore original values, decrypt the PII map and rehydrate:
import { decryptPIIMap, rehydrate } from 'rehydra';

// Get the key (must be the same key used for encryption)
const key = await keyProvider.getKey();

// Decrypt the PII map
const piiMap = await decryptPIIMap(result.piiMap, key);
// Returns Map<string, string> where key is "EMAIL:1" and value is "[email protected]"

// Replace placeholders with original values
const original = rehydrate(translatedText, piiMap);

Key Generation

Generate a secure 256-bit key:
import { generateKey } from 'rehydra';

// Generate random key (returns Uint8Array)
const key = generateKey();
Or via command line:
# Generate base64-encoded key for ConfigKeyProvider
openssl rand -base64 32

Key Derivation

Derive a key from a password:
import { deriveKey, generateSalt } from 'rehydra';

const password = 'user-password';
const salt = generateSalt();  // Store this with the encrypted data

const key = await deriveKey(password, salt);

Security Best Practices

Always use the encrypted storage. Never log or expose the decrypted piiMap.
// ❌ Never do this
console.log(piiMap);

// ✅ Safe to log
console.log(result.piiMap);  // Only encrypted data
Use platform-specific secure storage:
  • iOS: Keychain
  • Android: Keystore
  • Desktop: OS keychain (via keytar or similar)
  • Server: Environment variables, secrets manager
For long-running applications, rotate keys periodically:
// 1. Decrypt with old key
const piiMap = await decryptPIIMap(oldEncrypted, oldKey);

// 2. Re-encrypt with new key
const newEncrypted = await encryptPIIMap(piiMap, newKey);

// 3. Store new encrypted data
await storage.save(sessionId, newEncrypted);
Catch any missed PII in output:
const anonymizer = createAnonymizer({
  defaultPolicy: {
    enableLeakScan: true
  }
});

// Check result
if (!result.stats.leakScanPassed) {
  console.warn('Potential PII leak detected');
}

Crypto Utilities

Additional cryptographic utilities:
import { 
  uint8ArrayToBase64,
  base64ToUint8Array,
  validateKey,
  secureCompare 
} from 'rehydra';

// Convert key to base64 for storage
const keyBase64 = uint8ArrayToBase64(key);

// Restore key from base64
const key = base64ToUint8Array(keyBase64);

// Validate key format and length
if (!validateKey(key)) {
  throw new Error('Invalid encryption key');
}

// Constant-time comparison (prevents timing attacks)
if (secureCompare(key1, key2)) {
  console.log('Keys match');
}

Next Steps