createSelectiveEncryptionEncryptHook function

PVCacheHook createSelectiveEncryptionEncryptHook({
  1. String? encryptionKey,
  2. String keyName = DEFAULT_ENCRYPTION_KEY_NAME,
  3. int priority = -50,
})

Selective Encryption Hook System

Encrypts specific fields rather than entire value. Each field gets unique nonce stored in metadata.

Usage:

final cache = PVCache(
  env: 'myCache',
  hooks: createSelectiveEncryptionHooks(),
  defaultMetadata: {},
);

await cache.put(
  'user:123',
  {'name': 'John', 'password': 'secret', 'profile': {'ssn': '123-45-6789'}},
  metadata: {'secure': ['password', 'profile.ssn']}
);

Features:

  • Dot notation paths
  • Unique nonce per field
  • Non-sensitive fields remain readable
  • Works with nested structures Creates a hook that selectively encrypts specified fields.

Implementation

/// Creates a hook that selectively encrypts specified fields.
PVCacheHook createSelectiveEncryptionEncryptHook({
  String? encryptionKey,
  String keyName = DEFAULT_ENCRYPTION_KEY_NAME,
  int priority = -50,
}) {
  return PVCacheHook(
    eventString: 'selective_encryption_encrypt',
    eventFlow: EventFlow.storageUpdate,
    priority: priority,
    actionTypes: [ActionType.put],
    hookFunction: (ctx) async {
      // Skip if no value
      if (ctx.entryValue == null) return;

      // Check if selective encryption is requested via initialMeta
      final securePaths = ctx.initialMeta['secure'];
      if (securePaths == null || securePaths is! List || securePaths.isEmpty) {
        return;
      }

      // Get or create encryption key
      final key = encryptionKey ?? await getOrCreateEncryptionKey(keyName);
      final cipher = AESCipher(key);

      // Store nonces for each encrypted field
      final nonces = <String, String>{};

      // Make a deep copy of the value to modify
      final valueJson = jsonEncode(ctx.entryValue);
      final modifiedValue = jsonDecode(valueJson);

      // Encrypt each specified field
      for (final path in securePaths) {
        if (path is! String) continue;

        // Get the value at this path
        final fieldValue = getNestedValue(modifiedValue, path);
        if (fieldValue == null) continue;

        // Generate unique nonce for this field
        final nonce = generateNonce();
        nonces[path] = nonce;

        // Convert field value to JSON string
        final fieldJson = jsonEncode(fieldValue);

        // Encrypt with nonce
        final encrypted = cipher.encryptStringWithNonce(fieldJson, nonce);

        // Replace field value with encrypted version
        setNestedValue(modifiedValue, path, encrypted);
      }

      // Update entry value with modified data
      ctx.entryValue = modifiedValue;

      // Store nonces and encryption flag in runtime metadata
      // This will be persisted by the cache system automatically
      ctx.runtimeMeta['_selective_encrypted'] = true;
      ctx.runtimeMeta['_encryption_nonces'] = nonces;
    },
  );
}