Skip to content

Commit

Permalink
Merge pull request #635 from Iterable/MOB-6865-Keychain-null-crash-fix
Browse files Browse the repository at this point in the history
[MOB - 6865] - Keychain null crash fix
  • Loading branch information
Ayyanchira committed Oct 3, 2023
2 parents d628203 + 8d5b427 commit eddcc65
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 47 deletions.
2 changes: 1 addition & 1 deletion iterableapi/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ dependencies {
api 'androidx.appcompat:appcompat:1.0.0'
api 'androidx.annotation:annotation:1.0.0'
api 'com.google.firebase:firebase-messaging:20.3.0'
implementation "androidx.security:security-crypto:1.1.0-alpha04"
implementation "androidx.security:security-crypto:1.1.0-alpha06"

testImplementation 'junit:junit:4.12'
testImplementation 'androidx.test:runner:1.3.0'
Expand Down
30 changes: 17 additions & 13 deletions iterableapi/src/main/java/com/iterable/iterableapi/IterableApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ IterableAuthManager getAuthManager() {
return authManager;
}

@NonNull
@Nullable
IterableKeychain getKeychain() {
if (keychain == null) {
try {
Expand Down Expand Up @@ -350,15 +350,25 @@ private String getDeviceId() {
}

private void storeAuthData() {
getKeychain().saveEmail(_email);
getKeychain().saveUserId(_userId);
getKeychain().saveAuthToken(_authToken);
IterableKeychain iterableKeychain = getKeychain();
if (iterableKeychain != null) {
iterableKeychain.saveEmail(_email);
iterableKeychain.saveUserId(_userId);
iterableKeychain.saveAuthToken(_authToken);
} else {
IterableLogger.e(TAG, "Shared preference creation failed. ");
}
}

private void retrieveEmailAndUserId() {
_email = getKeychain().getEmail();
_userId = getKeychain().getUserId();
_authToken = getKeychain().getAuthToken();
IterableKeychain iterableKeychain = getKeychain();
if (iterableKeychain != null) {
_email = iterableKeychain.getEmail();
_userId = iterableKeychain.getUserId();
_authToken = iterableKeychain.getAuthToken();
} else {
IterableLogger.e(TAG, "retrieveEmailAndUserId: Shared preference creation failed. Could not retrieve email/userId");
}

if (config.authHandler != null) {
if (_authToken != null) {
Expand All @@ -370,10 +380,6 @@ private void retrieveEmailAndUserId() {
}
}

private void updateSDKVersion() {

}

private class IterableApiAuthProvider implements IterableApiClient.AuthProvider {
@Nullable
@Override
Expand Down Expand Up @@ -507,8 +513,6 @@ public static void initialize(@NonNull Context context, @NonNull String apiKey,
sharedInstance.config = new IterableConfig.Builder().build();
}

sharedInstance.updateSDKVersion();

sharedInstance.retrieveEmailAndUserId();

IterableActivityMonitor.getInstance().registerLifecycleCallbacks(context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,12 @@ public Builder setAllowedProtocols(@NonNull String[] allowedProtocols) {
return this;
}

/**
* Set whether the SDK should enforce encryption. If set to `true`, the SDK will not use fallback mechanism
* of storing data in un-encrypted shared preferences if encrypted database is not available. Set this to `true`
* if PII confidentiality is a concern for your app.
* @param encryptionEnforced `true` will have the SDK enforce encryption.
*/
public Builder setEncryptionEnforced(boolean encryptionEnforced) {
this.encryptionEnforced = encryptionEnforced;
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ class IterableKeychain {
encryptionEnabled = false
sharedPrefs = context.getSharedPreferences(
IterableConstants.SHARED_PREFS_FILE,
Context.MODE_PRIVATE)
Context.MODE_PRIVATE
)
IterableLogger.v(TAG, "SharedPreferences being used")
} else {
// See if EncryptedSharedPreferences can be created successfully
Expand All @@ -39,45 +40,42 @@ class IterableKeychain {
encryptedSharedPrefsFileName,
masterKeyAlias,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM)
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
encryptionEnabled = true
} catch (e: Exception) {
} catch (e: Throwable) {
if (e is Error) {
IterableLogger.e(
TAG,
"EncryptionSharedPreference creation failed with Error. Attempting to continue"
)
}

if (encryptionEnforced) {
//TODO: In memory Or similar solution needs to be implemented in future.
IterableLogger.e(TAG, "Error creating EncryptedSharedPreferences", e)
//TODO: In-memory or similar solution needs to be implemented in the future.
IterableLogger.w(
TAG,
"Encryption is enforced. PII will not be persisted due to EncryptionSharedPreference failure. Email/UserId and Auth token will have to be passed for every app session.",
e
)
throw e.fillInStackTrace()
} else {
sharedPrefs = context.getSharedPreferences(
IterableConstants.SHARED_PREFS_FILE,
Context.MODE_PRIVATE)
Context.MODE_PRIVATE
)
IterableLogger.w(
TAG,
"Using SharedPreference as EncryptionSharedPreference creation failed."
)
encryptionEnabled = false
}
}

//Try to migrate data from SharedPreferences to EncryptedSharedPreferences
if(encryptionEnabled) {
if (encryptionEnabled) {
migrateAuthDataFromSharedPrefsToKeychain(context)
}

}
val masterKeyAlias = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
sharedPrefs = try {
EncryptedSharedPreferences.create(
context,
encryptedSharedPrefsFileName,
masterKeyAlias,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
} catch (e: Exception) {
if (encryptionEnforced) {
IterableLogger.e(TAG, "Error creating EncryptedSharedPreferences", e)
throw e.fillInStackTrace()
} else {
context.getSharedPreferences(encryptedSharedPrefsFileName, Context.MODE_PRIVATE)
}
}
}

Expand Down Expand Up @@ -113,17 +111,20 @@ class IterableKeychain {

@RequiresApi(api = Build.VERSION_CODES.M)
private fun migrateAuthDataFromSharedPrefsToKeychain(context: Context) {
val oldPrefs: SharedPreferences = context.getSharedPreferences(
val oldPrefs: SharedPreferences = context.getSharedPreferences(
IterableConstants.SHARED_PREFS_FILE,
Context.MODE_PRIVATE)
Context.MODE_PRIVATE
)
val sharedPrefsEmail = oldPrefs.getString(IterableConstants.SHARED_PREFS_EMAIL_KEY, null)
val sharedPrefsUserId = oldPrefs.getString(IterableConstants.SHARED_PREFS_USERID_KEY, null)
val sharedPrefsAuthToken = oldPrefs.getString(IterableConstants.SHARED_PREFS_AUTH_TOKEN_KEY, null)
val sharedPrefsAuthToken =
oldPrefs.getString(IterableConstants.SHARED_PREFS_AUTH_TOKEN_KEY, null)
val editor: SharedPreferences.Editor = oldPrefs.edit()
if (getEmail() == null && sharedPrefsEmail != null) {
saveEmail(sharedPrefsEmail)
editor.remove(IterableConstants.SHARED_PREFS_EMAIL_KEY)
IterableLogger.v(TAG,
IterableLogger.v(
TAG,
"UPDATED: migrated email from SharedPreferences to IterableKeychain"
)
} else if (sharedPrefsEmail != null) {
Expand All @@ -132,7 +133,8 @@ class IterableKeychain {
if (getUserId() == null && sharedPrefsUserId != null) {
saveUserId(sharedPrefsUserId)
editor.remove(IterableConstants.SHARED_PREFS_USERID_KEY)
IterableLogger.v(TAG,
IterableLogger.v(
TAG,
"UPDATED: migrated userId from SharedPreferences to IterableKeychain"
)
} else if (sharedPrefsUserId != null) {
Expand All @@ -141,7 +143,8 @@ class IterableKeychain {
if (getAuthToken() == null && sharedPrefsAuthToken != null) {
saveAuthToken(sharedPrefsAuthToken)
editor.remove(IterableConstants.SHARED_PREFS_AUTH_TOKEN_KEY)
IterableLogger.v(TAG,
IterableLogger.v(
TAG,
"UPDATED: migrated authToken from SharedPreferences to IterableKeychain"
)
} else if (sharedPrefsAuthToken != null) {
Expand Down

0 comments on commit eddcc65

Please sign in to comment.