CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-tencent--mmkv-shared

MMKV is an efficient, small, easy-to-use mobile key-value storage framework used in the WeChat application.

Pending
Overview
Eval results
Files

encryption.mddocs/

Encryption and Security

Encryption capabilities for protecting sensitive data with AES encryption, key management, and secure key rotation. MMKV provides transparent encryption/decryption for all stored data when an encryption key is provided.

Capabilities

Key Management

Manage encryption keys for MMKV instances with secure key operations.

/**
 * Get the encryption key (no more than 16 bytes).
 * @return The encryption key, or null if no encryption is used
 */
public String cryptKey();

/**
 * Transform plain text into encrypted text, or vice versa by passing a null encryption key.
 * You can also change existing crypt key with a different cryptKey.
 * @param cryptKey The new encryption key (no more than 16 bytes)
 * @return True if success, otherwise False
 */
public boolean reKey(String cryptKey);

/**
 * Just reset the encryption key (will not encrypt or decrypt anything).
 * Usually you should call this method after another process has reKey() the multi-process MMKV instance.
 * @param cryptKey The new encryption key (no more than 16 bytes)
 */
public void checkReSetCryptKey(String cryptKey);

Usage Example:

// Create encrypted MMKV instance
MMKV encryptedKv = MMKV.mmkvWithID("secure_data", MMKV.SINGLE_PROCESS_MODE, "my_secret_key");

// Store sensitive data (automatically encrypted)
encryptedKv.encode("credit_card", "4111-1111-1111-1111");
encryptedKv.encode("api_token", "secret_api_token_12345");

// Check current encryption key
String currentKey = encryptedKv.cryptKey();
Log.d("MMKV", "Current encryption key: " + (currentKey != null ? "[ENCRYPTED]" : "None"));

// Change encryption key (re-encrypts all data)
boolean success = encryptedKv.reKey("new_secret_key");
if (success) {
    Log.d("MMKV", "Successfully changed encryption key");
} else {
    Log.e("MMKV", "Failed to change encryption key");
}

// Remove encryption (converts to plain text)
boolean removed = encryptedKv.reKey(null);
if (removed) {
    Log.d("MMKV", "Encryption removed, data is now plain text");
}

Multi-Process Key Synchronization

Manage encryption keys across multiple processes safely.

/**
 * Just reset the encryption key (will not encrypt or decrypt anything).
 * Usually you should call this method after another process has reKey() the multi-process MMKV instance.
 * @param cryptKey The new encryption key (no more than 16 bytes)
 */
public void checkReSetCryptKey(String cryptKey);

Usage Example:

// In main process: Create encrypted multi-process instance
MMKV mainKv = MMKV.mmkvWithID("shared_secure", MMKV.MULTI_PROCESS_MODE, "shared_key");
mainKv.encode("secure_config", "sensitive_value");

// Change encryption key in main process
boolean keyChanged = mainKv.reKey("new_shared_key");

// In other processes: Update to new key without re-encryption
MMKV otherKv = MMKV.mmkvWithID("shared_secure", MMKV.MULTI_PROCESS_MODE, "shared_key");
if (keyChanged) {
    // Synchronize key change from main process
    otherKv.checkReSetCryptKey("new_shared_key");
}

// Now both processes can access the data with the new key
String config = otherKv.decodeString("secure_config", "");

Encryption Best Practices

// 1. Use strong, randomly generated keys
SecureRandom random = new SecureRandom();
byte[] keyBytes = new byte[16]; // 16 bytes for AES-128
random.nextBytes(keyBytes);
String encryptionKey = Base64.encodeToString(keyBytes, Base64.NO_WRAP);

MMKV secureKv = MMKV.mmkvWithID("user_secrets", MMKV.SINGLE_PROCESS_MODE, encryptionKey);

// 2. Store encryption keys securely (Android Keystore)
private String getOrCreateEncryptionKey() {
    // Use Android Keystore for key management
    return KeystoreManager.getOrCreateKey("mmkv_encryption_key");
}

// 3. Separate encrypted and unencrypted data
MMKV publicKv = MMKV.mmkvWithID("public_data"); // No encryption for non-sensitive data
MMKV privateKv = MMKV.mmkvWithID("private_data", MMKV.SINGLE_PROCESS_MODE, getEncryptionKey());

publicKv.encode("app_version", "1.0.0");
publicKv.encode("user_preferences", "theme:dark");

privateKv.encode("auth_token", userToken);
privateKv.encode("biometric_data", biometricInfo);

// 4. Handle key rotation securely  
private void rotateEncryptionKey(MMKV kv) {
    String oldKey = kv.cryptKey();
    String newKey = generateNewKey();
    
    // Create backup before key rotation
    boolean backupSuccess = MMKV.backupOneToDirectory(
        kv.mmapID(), 
        getBackupDirectory(), 
        null
    );
    
    if (backupSuccess) {
        boolean rotationSuccess = kv.reKey(newKey);
        if (rotationSuccess) {
            Log.d("MMKV", "Key rotation successful");
            // Update stored key reference
            updateStoredEncryptionKey(newKey);
        } else {
            Log.e("MMKV", "Key rotation failed, keeping old key");
        }
    }
}

// 5. Verify encryption status
private boolean isEncrypted(MMKV kv) {
    return kv.cryptKey() != null;
}

// 6. Handle encryption errors gracefully
private void safeEncryptedOperation(MMKV kv, String key, String value) {
    try {
        if (isEncrypted(kv)) {
            boolean success = kv.encode(key, value);
            if (!success) {
                Log.e("MMKV", "Failed to encrypt and store data");
                // Handle encryption failure
            }
        } else {
            Log.w("MMKV", "Storing sensitive data without encryption");
            kv.encode(key, value);
        }
    } catch (Exception e) {
        Log.e("MMKV", "Encryption operation failed", e);
        // Handle error appropriately
    }
}

Encrypted Anonymous Shared Memory

Use encryption with Anonymous Shared Memory for secure inter-process communication.

/**
 * Create an MMKV instance base on Anonymous Shared Memory with encryption.
 * @param context The context of Android App
 * @param mmapID The unique ID of the MMKV instance
 * @param size The maximum size of the underlying Anonymous Shared Memory
 * @param mode The process mode of the MMKV instance
 * @param cryptKey The encryption key (no more than 16 bytes)
 * @return MMKV instance
 * @throws RuntimeException if there's a runtime error
 */
public static MMKV mmkvWithAshmemID(Context context, String mmapID, int size, int mode, String cryptKey);

Usage Example:

// Create encrypted ashmem instance for secure IPC
String sharedKey = getSharedEncryptionKey(); // Securely shared between processes
MMKV secureIPC = MMKV.mmkvWithAshmemID(
    this,
    "secure_ipc_channel",
    1024 * 1024, // 1MB
    MMKV.MULTI_PROCESS_MODE,
    sharedKey
);

// Share encrypted data between processes
secureIPC.encode("sensitive_message", "confidential_data");
secureIPC.encode("auth_credentials", userCredentials);

// Data is automatically encrypted in memory and during IPC
ParcelableMMKV parcelable = new ParcelableMMKV(secureIPC);
// Pass parcelable to other process...

Encryption Performance Considerations

// Encryption impacts performance - use judiciously
public class EncryptionStrategy {
    private MMKV publicKv;
    private MMKV encryptedKv;
    
    public EncryptionStrategy() {
        // Fast access for non-sensitive data
        publicKv = MMKV.mmkvWithID("public_data");
        
        // Encrypted storage for sensitive data
        encryptedKv = MMKV.mmkvWithID("sensitive_data", MMKV.SINGLE_PROCESS_MODE, getEncryptionKey());
    }
    
    public void storeUserPreference(String key, String value) {
        // Non-sensitive preferences - use fast unencrypted storage
        publicKv.encode(key, value);
    }
    
    public void storeAuthToken(String token) {
        // Sensitive data - use encrypted storage
        encryptedKv.encode("auth_token", token);
    }
    
    public void storeBiometricTemplate(byte[] template) {
        // Highly sensitive data - use encrypted storage
        encryptedKv.encode("biometric_template", template);
    }
}

Integration with Android Keystore

// Secure key management using Android Keystore
public class SecureMMKVManager {
    private static final String KEYSTORE_ALIAS = "mmkv_encryption_key";
    
    public MMKV createSecureInstance(String instanceId) {
        String encryptionKey = getOrCreateKeystoreKey();
        return MMKV.mmkvWithID(instanceId, MMKV.SINGLE_PROCESS_MODE, encryptionKey);
    }
    
    private String getOrCreateKeystoreKey() {
        try {
            KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
            keyStore.load(null);
            
            if (!keyStore.containsAlias(KEYSTORE_ALIAS)) {
                // Generate new key in Android Keystore
                generateKeystoreKey();
            }
            
            // Retrieve and use key for MMKV encryption
            return retrieveKeystoreKey();
        } catch (Exception e) {
            Log.e("MMKV", "Keystore operation failed", e);
            return null;
        }
    }
    
    private void generateKeystoreKey() {
        // Implementation depends on Android version and requirements
        // Use KeyGenerator with AndroidKeyStore provider
    }
    
    private String retrieveKeystoreKey() {
        // Retrieve key from Android Keystore
        // Convert to format suitable for MMKV
        return "keystore_derived_key";
    }
}

Encryption Limitations and Considerations

// Important encryption considerations:

// 1. Key length limit (16 bytes maximum)
String validKey = "1234567890123456"; // 16 bytes - OK
String invalidKey = "12345678901234567"; // 17 bytes - Will be truncated

// 2. Performance impact
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
    encryptedKv.encode("key" + i, "value" + i);
}
long encryptedTime = System.currentTimeMillis() - startTime;

startTime = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
    plainKv.encode("key" + i, "value" + i);
}
long plainTime = System.currentTimeMillis() - startTime;

Log.d("MMKV", "Encrypted: " + encryptedTime + "ms, Plain: " + plainTime + "ms");

// 3. Incompatibility with compare-before-set optimization
MMKV encryptedKv = MMKV.mmkvWithID("test", MMKV.SINGLE_PROCESS_MODE, "key");
try {
    encryptedKv.enableCompareBeforeSet(); // Will throw exception
} catch (RuntimeException e) {
    Log.w("MMKV", "Compare-before-set not available with encryption");
}

// 4. Multi-process key synchronization complexity
// Ensure all processes use the same key and synchronize key changes

Install with Tessl CLI

npx tessl i tessl/maven-com-tencent--mmkv-shared

docs

advanced-features.md

data-management.md

encryption.md

index.md

initialization.md

instance-management.md

multi-process.md

storage-operations.md

tile.json