or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

data-management.mddata-storage.mdencryption.mdindex.mdinitialization.mdinstance-management.mdmulti-process.mdnamespace.md

encryption.mddocs/

0

# Encryption and Security

1

2

Built-in encryption capabilities with key management, supporting AES encryption for secure data storage and key transformation operations.

3

4

## Capabilities

5

6

### Encryption Key Management

7

8

Manage encryption keys for secure data storage with up to 16-byte keys.

9

10

```java { .api }

11

/**

12

* Get the current encryption key.

13

* @return The encryption key string, or null if not encrypted

14

*/

15

public String cryptKey();

16

17

/**

18

* Transform plain text into encrypted text, or vice versa.

19

* Can also change existing encryption key with a different key.

20

* @param cryptKey The new encryption key (no more than 16 bytes), null to remove encryption

21

* @return True if successful, false otherwise

22

*/

23

public boolean reKey(String cryptKey);

24

25

/**

26

* Reset the encryption key without encrypting or decrypting anything.

27

* Usually called after another process has reKey() the multi-process MMKV instance.

28

* @param cryptKey The new encryption key (no more than 16 bytes)

29

*/

30

public void checkReSetCryptKey(String cryptKey);

31

```

32

33

**Usage Example:**

34

35

```java

36

import com.tencent.mmkv.MMKV;

37

38

// Create encrypted MMKV instance

39

MMKV secureKv = MMKV.mmkvWithID("secure_data", MMKV.SINGLE_PROCESS_MODE, "my-secret-key");

40

41

// Check current encryption key

42

String currentKey = secureKv.cryptKey(); // "my-secret-key"

43

44

// Store sensitive data

45

secureKv.encode("password", "user-password");

46

secureKv.encode("api_key", "secret-api-key");

47

secureKv.encode("token", "access-token-12345");

48

49

// Change encryption key (re-encrypts all data)

50

boolean success = secureKv.reKey("new-secret-key");

51

if (success) {

52

Log.d("MMKV", "Successfully changed encryption key");

53

} else {

54

Log.e("MMKV", "Failed to change encryption key");

55

}

56

57

// Remove encryption (converts to plain text)

58

secureKv.reKey(null);

59

60

// Add encryption to existing plain text data

61

secureKv.reKey("another-secret-key");

62

```

63

64

### Multi-Process Encryption Synchronization

65

66

Handle encryption key changes across multiple processes safely.

67

68

```java { .api }

69

/**

70

* Reset the encryption key without encrypting/decrypting.

71

* Use this when another process has changed the encryption key.

72

* @param cryptKey The new encryption key that was set by another process

73

*/

74

public void checkReSetCryptKey(String cryptKey);

75

```

76

77

**Usage Example:**

78

79

```java

80

// In Process A - change encryption key

81

MMKV multiProcessKv = MMKV.mmkvWithID("shared_data", MMKV.MULTI_PROCESS_MODE, "old-key");

82

boolean changed = multiProcessKv.reKey("new-key");

83

84

// In Process B - synchronize the key change

85

MMKV sharedKv = MMKV.mmkvWithID("shared_data", MMKV.MULTI_PROCESS_MODE, "old-key");

86

if (changed) {

87

// Reset to the new key without re-encrypting

88

sharedKv.checkReSetCryptKey("new-key");

89

// Now Process B can access the data with the new key

90

}

91

```

92

93

### Encryption Status Check

94

95

Check if an MMKV instance has encryption enabled.

96

97

```java { .api }

98

/**

99

* Check if encryption is enabled for this instance.

100

* @return True if encryption is enabled, false otherwise

101

*/

102

private boolean isEncryptionEnabled();

103

```

104

105

**Usage Example:**

106

107

```java

108

MMKV plainKv = MMKV.mmkvWithID("plain_data");

109

MMKV encryptedKv = MMKV.mmkvWithID("encrypted_data", MMKV.SINGLE_PROCESS_MODE, "secret");

110

111

// Note: isEncryptionEnabled() is private, but you can check via cryptKey()

112

boolean plainHasEncryption = (plainKv.cryptKey() != null); // false

113

boolean encryptedHasEncryption = (encryptedKv.cryptKey() != null); // true

114

115

Log.d("MMKV", "Plain KV encrypted: " + plainHasEncryption);

116

Log.d("MMKV", "Encrypted KV encrypted: " + encryptedHasEncryption);

117

```

118

119

### Security Best Practices

120

121

Best practices for using MMKV encryption securely.

122

123

**Key Generation Example:**

124

125

```java

126

import java.security.SecureRandom;

127

import java.util.Base64;

128

129

public class MMKVSecurity {

130

131

/**

132

* Generate a secure random encryption key.

133

* @return A base64-encoded encryption key suitable for MMKV

134

*/

135

public static String generateSecureKey() {

136

SecureRandom random = new SecureRandom();

137

byte[] keyBytes = new byte[16]; // 16 bytes = 128 bits

138

random.nextBytes(keyBytes);

139

return Base64.getEncoder().encodeToString(keyBytes);

140

}

141

142

/**

143

* Create an encrypted MMKV instance with a secure key.

144

* @param instanceId The unique ID for the MMKV instance

145

* @param encryptionKey The encryption key (store securely!)

146

* @return The encrypted MMKV instance

147

*/

148

public static MMKV createSecureMMKV(String instanceId, String encryptionKey) {

149

return MMKV.mmkvWithID(instanceId, MMKV.SINGLE_PROCESS_MODE, encryptionKey);

150

}

151

}

152

153

// Usage

154

String secureKey = MMKVSecurity.generateSecureKey();

155

// Store secureKey in Android Keystore or other secure location

156

MMKV secureStorage = MMKVSecurity.createSecureMMKV("user_credentials", secureKey);

157

```

158

159

### Android Keystore Integration

160

161

Example of integrating MMKV encryption with Android Keystore for enhanced security.

162

163

**Usage Example:**

164

165

```java

166

import android.security.keystore.KeyGenParameterSpec;

167

import android.security.keystore.KeyProperties;

168

import java.security.KeyStore;

169

import javax.crypto.KeyGenerator;

170

import javax.crypto.SecretKey;

171

import javax.crypto.Cipher;

172

import android.util.Base64;

173

174

public class MMKVKeystoreHelper {

175

176

private static final String KEYSTORE_ALIAS = "MMKVMasterKey";

177

private static final String ANDROID_KEYSTORE = "AndroidKeyStore";

178

179

/**

180

* Generate or retrieve a master key from Android Keystore.

181

* @return The master key for encrypting MMKV keys

182

*/

183

public static SecretKey getMasterKey() throws Exception {

184

KeyStore keyStore = KeyStore.getInstance(ANDROID_KEYSTORE);

185

keyStore.load(null);

186

187

if (!keyStore.containsAlias(KEYSTORE_ALIAS)) {

188

// Generate new key

189

KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEYSTORE);

190

KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(

191

KEYSTORE_ALIAS,

192

KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)

193

.setBlockModes(KeyProperties.BLOCK_MODE_GCM)

194

.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)

195

.build();

196

keyGenerator.init(keyGenParameterSpec);

197

return keyGenerator.generateKey();

198

} else {

199

// Retrieve existing key

200

return (SecretKey) keyStore.getKey(KEYSTORE_ALIAS, null);

201

}

202

}

203

204

/**

205

* Create an MMKV instance with Keystore-protected encryption.

206

* @param instanceId The unique ID for the MMKV instance

207

* @return The encrypted MMKV instance

208

*/

209

public static MMKV createKeystoreProtectedMMKV(String instanceId) throws Exception {

210

// Generate MMKV encryption key

211

SecureRandom random = new SecureRandom();

212

byte[] mmkvKey = new byte[16];

213

random.nextBytes(mmkvKey);

214

215

// Encrypt the MMKV key with Keystore key

216

SecretKey masterKey = getMasterKey();

217

Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

218

cipher.init(Cipher.ENCRYPT_MODE, masterKey);

219

byte[] encryptedKey = cipher.doFinal(mmkvKey);

220

221

// Store encrypted key in SharedPreferences or other location

222

// (In real app, you'd save this encrypted key for later retrieval)

223

224

// Create MMKV with the raw key

225

String mmkvKeyString = Base64.encodeToString(mmkvKey, Base64.NO_WRAP);

226

return MMKV.mmkvWithID(instanceId, MMKV.SINGLE_PROCESS_MODE, mmkvKeyString);

227

}

228

}

229

230

// Usage

231

try {

232

MMKV keystoreProtectedKv = MMKVKeystoreHelper.createKeystoreProtectedMMKV("secure_user_data");

233

keystoreProtectedKv.encode("sensitive_info", "highly confidential data");

234

} catch (Exception e) {

235

Log.e("MMKV", "Failed to create keystore-protected MMKV", e);

236

}

237

```

238

239

### Performance Considerations

240

241

Encryption impact on performance and optimization strategies.

242

243

**Usage Example:**

244

245

```java

246

public class MMKVPerformanceExample {

247

248

public void demonstrateEncryptionPerformance() {

249

// Plain MMKV - fastest

250

MMKV plainKv = MMKV.mmkvWithID("plain_data");

251

252

// Encrypted MMKV - slower due to encryption overhead

253

MMKV encryptedKv = MMKV.mmkvWithID("encrypted_data", MMKV.SINGLE_PROCESS_MODE, "key");

254

255

// Disable compare-before-set for encrypted instances (it's inefficient)

256

// encryptedKv.enableCompareBeforeSet(); // Don't do this with encryption

257

258

long startTime, endTime;

259

String testData = "This is test data for performance measurement";

260

261

// Measure plain storage performance

262

startTime = System.nanoTime();

263

for (int i = 0; i < 1000; i++) {

264

plainKv.encode("key_" + i, testData);

265

}

266

endTime = System.nanoTime();

267

long plainTime = endTime - startTime;

268

269

// Measure encrypted storage performance

270

startTime = System.nanoTime();

271

for (int i = 0; i < 1000; i++) {

272

encryptedKv.encode("key_" + i, testData);

273

}

274

endTime = System.nanoTime();

275

long encryptedTime = endTime - startTime;

276

277

Log.d("MMKV", String.format("Plain: %d ns, Encrypted: %d ns, Overhead: %.2fx",

278

plainTime, encryptedTime, (double)encryptedTime / plainTime));

279

}

280

}

281

```

282

283

### Encryption Migration

284

285

Migrating existing plain text data to encrypted storage.

286

287

**Usage Example:**

288

289

```java

290

public class MMKVEncryptionMigration {

291

292

/**

293

* Migrate existing plain MMKV data to encrypted storage.

294

* @param instanceId The MMKV instance ID to migrate

295

* @param encryptionKey The encryption key to use

296

* @return True if migration successful

297

*/

298

public static boolean migrateToEncrypted(String instanceId, String encryptionKey) {

299

try {

300

// Get existing plain instance

301

MMKV plainKv = MMKV.mmkvWithID(instanceId);

302

303

// Get all existing keys and values

304

String[] allKeys = plainKv.allKeys();

305

if (allKeys == null || allKeys.length == 0) {

306

// No data to migrate, just enable encryption

307

return plainKv.reKey(encryptionKey);

308

}

309

310

// Create temporary encrypted instance

311

String tempId = instanceId + "_temp_encrypted";

312

MMKV tempEncryptedKv = MMKV.mmkvWithID(tempId, MMKV.SINGLE_PROCESS_MODE, encryptionKey);

313

314

// Copy all data to encrypted instance

315

for (String key : allKeys) {

316

// Try different data types (you might want to track types separately)

317

try {

318

String stringValue = plainKv.decodeString(key);

319

if (stringValue != null) {

320

tempEncryptedKv.encode(key, stringValue);

321

continue;

322

}

323

} catch (Exception ignored) {}

324

325

try {

326

int intValue = plainKv.decodeInt(key, Integer.MIN_VALUE);

327

if (intValue != Integer.MIN_VALUE) {

328

tempEncryptedKv.encode(key, intValue);

329

continue;

330

}

331

} catch (Exception ignored) {}

332

333

// Add other type checks as needed...

334

335

// Fallback to bytes

336

byte[] bytesValue = plainKv.decodeBytes(key);

337

if (bytesValue != null) {

338

tempEncryptedKv.encode(key, bytesValue);

339

}

340

}

341

342

// Replace original with encrypted version

343

plainKv.clearAll();

344

long importCount = plainKv.importFrom(tempEncryptedKv);

345

boolean reKeySuccess = plainKv.reKey(encryptionKey);

346

347

// Clean up temporary instance

348

MMKV.removeStorage(tempId);

349

350

return reKeySuccess && importCount > 0;

351

352

} catch (Exception e) {

353

Log.e("MMKV", "Failed to migrate to encrypted storage", e);

354

return false;

355

}

356

}

357

}

358

359

// Usage

360

boolean migrated = MMKVEncryptionMigration.migrateToEncrypted("user_data", "secret-key");

361

if (migrated) {

362

Log.d("MMKV", "Successfully migrated to encrypted storage");

363

} else {

364

Log.e("MMKV", "Failed to migrate to encrypted storage");

365

}

366

```

367

368

## Security Notes

369

370

1. **Key Length**: Encryption keys must be no more than 16 bytes (128-bit AES)

371

2. **Key Storage**: Store encryption keys securely using Android Keystore or other secure mechanisms

372

3. **Performance**: Encryption adds computational overhead; use only for sensitive data

373

4. **Multi-Process**: Use `checkReSetCryptKey()` to synchronize key changes across processes

374

5. **Compare-Before-Set**: Disable `enableCompareBeforeSet()` for encrypted instances as it's inefficient

375

6. **Key Rotation**: Use `reKey()` to change encryption keys and re-encrypt existing data

376

7. **Migration**: Plan migration strategy when adding encryption to existing plain text data