Moving null checks from CipherStorageFacebookConceal and PrefsStorage to KeychainModule and introducing checks for empty service in CipherStorageKeystoreAESCBC.

This commit is contained in:
Pelle Stenild Coltau 2017-06-29 09:49:35 +02:00
parent 71f567d65a
commit ef9b5256ef
5 changed files with 39 additions and 28 deletions

View File

@ -30,6 +30,7 @@ public class KeychainModule extends ReactContextBaseJavaModule {
public static final String E_CRYPTO_FAILED = "E_CRYPTO_FAILED"; public static final String E_CRYPTO_FAILED = "E_CRYPTO_FAILED";
public static final String E_KEYSTORE_ACCESS_ERROR = "E_KEYSTORE_ACCESS_ERROR"; public static final String E_KEYSTORE_ACCESS_ERROR = "E_KEYSTORE_ACCESS_ERROR";
public static final String KEYCHAIN_MODULE = "RNKeychainManager"; public static final String KEYCHAIN_MODULE = "RNKeychainManager";
public static final String EMPTY_STRING = "";
private final Map<String, CipherStorage> cipherStorageMap = new HashMap<>(); private final Map<String, CipherStorage> cipherStorageMap = new HashMap<>();
private final PrefsStorage prefsStorage; private final PrefsStorage prefsStorage;
@ -57,6 +58,8 @@ public class KeychainModule extends ReactContextBaseJavaModule {
if (username == null || username.isEmpty() || password == null || password.isEmpty()) { if (username == null || username.isEmpty() || password == null || password.isEmpty()) {
throw new EmptyParameterException("you passed empty or null username/password"); throw new EmptyParameterException("you passed empty or null username/password");
} }
service = getDefaultServiceIfNull(service);
CipherStorage currentCipherStorage = getCipherStorageForCurrentAPILevel(); CipherStorage currentCipherStorage = getCipherStorageForCurrentAPILevel();
EncryptionResult result = currentCipherStorage.encrypt(service, username, password); EncryptionResult result = currentCipherStorage.encrypt(service, username, password);
@ -75,6 +78,8 @@ public class KeychainModule extends ReactContextBaseJavaModule {
@ReactMethod @ReactMethod
public void getGenericPasswordForOptions(String service, Promise promise) { public void getGenericPasswordForOptions(String service, Promise promise) {
try { try {
service = getDefaultServiceIfNull(service);
CipherStorage currentCipherStorage = getCipherStorageForCurrentAPILevel(); CipherStorage currentCipherStorage = getCipherStorageForCurrentAPILevel();
final DecryptionResult decryptionResult; final DecryptionResult decryptionResult;
@ -121,6 +126,8 @@ public class KeychainModule extends ReactContextBaseJavaModule {
@ReactMethod @ReactMethod
public void resetGenericPasswordForOptions(String service, Promise promise) { public void resetGenericPasswordForOptions(String service, Promise promise) {
try { try {
service = getDefaultServiceIfNull(service);
// First we clean up the cipher storage (using the cipher storage that was used to store the entry) // First we clean up the cipher storage (using the cipher storage that was used to store the entry)
ResultSet resultSet = prefsStorage.getEncryptedEntry(service); ResultSet resultSet = prefsStorage.getEncryptedEntry(service);
if (resultSet != null) { if (resultSet != null) {
@ -176,4 +183,9 @@ public class KeychainModule extends ReactContextBaseJavaModule {
private CipherStorage getCipherStorageByName(String cipherStorageName) { private CipherStorage getCipherStorageByName(String cipherStorageName) {
return cipherStorageMap.get(cipherStorageName); return cipherStorageMap.get(cipherStorageName);
} }
@NonNull
private String getDefaultServiceIfNull(String service) {
return service == null ? EMPTY_STRING : service;
}
} }

View File

@ -2,6 +2,7 @@ package com.oblador.keychain;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.support.annotation.NonNull;
import android.util.Base64; import android.util.Base64;
import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactApplicationContext;
@ -10,7 +11,6 @@ import com.oblador.keychain.cipherStorage.CipherStorageFacebookConceal;
public class PrefsStorage { public class PrefsStorage {
public static final String KEYCHAIN_DATA = "RN_KEYCHAIN"; public static final String KEYCHAIN_DATA = "RN_KEYCHAIN";
public static final String EMPTY_STRING = "";
static public class ResultSet { static public class ResultSet {
public final String cipherStorageName; public final String cipherStorageName;
@ -30,9 +30,7 @@ public class PrefsStorage {
this.prefs = reactContext.getSharedPreferences(KEYCHAIN_DATA, Context.MODE_PRIVATE); this.prefs = reactContext.getSharedPreferences(KEYCHAIN_DATA, Context.MODE_PRIVATE);
} }
public ResultSet getEncryptedEntry(String service) { public ResultSet getEncryptedEntry(@NonNull String service) {
service = service == null ? EMPTY_STRING : service;
byte[] bytesForUsername = getBytesForUsername(service); byte[] bytesForUsername = getBytesForUsername(service);
byte[] bytesForPassword = getBytesForPassword(service); byte[] bytesForPassword = getBytesForPassword(service);
String cipherStorageName = getCipherStorageName(service); String cipherStorageName = getCipherStorageName(service);
@ -46,9 +44,7 @@ public class PrefsStorage {
return null; return null;
} }
public void removeEntry(String service) { public void removeEntry(@NonNull String service) {
service = service == null ? EMPTY_STRING : service;
String keyForUsername = getKeyForUsername(service); String keyForUsername = getKeyForUsername(service);
String keyForPassword = getKeyForPassword(service); String keyForPassword = getKeyForPassword(service);
String keyForCipherStorage = getKeyForCipherStorage(service); String keyForCipherStorage = getKeyForCipherStorage(service);
@ -59,9 +55,7 @@ public class PrefsStorage {
.remove(keyForCipherStorage).apply(); .remove(keyForCipherStorage).apply();
} }
public void storeEncryptedEntry(String service, EncryptionResult encryptionResult) { public void storeEncryptedEntry(@NonNull String service, @NonNull EncryptionResult encryptionResult) {
service = service == null ? EMPTY_STRING : service;
String keyForUsername = getKeyForUsername(service); String keyForUsername = getKeyForUsername(service);
String keyForPassword = getKeyForPassword(service); String keyForPassword = getKeyForPassword(service);
String keyForCipherStorage = getKeyForCipherStorage(service); String keyForCipherStorage = getKeyForCipherStorage(service);

View File

@ -1,5 +1,7 @@
package com.oblador.keychain.cipherStorage; package com.oblador.keychain.cipherStorage;
import android.support.annotation.NonNull;
import com.oblador.keychain.exceptions.CryptoFailedException; import com.oblador.keychain.exceptions.CryptoFailedException;
import com.oblador.keychain.exceptions.KeyStoreAccessException; import com.oblador.keychain.exceptions.KeyStoreAccessException;
@ -29,11 +31,11 @@ public interface CipherStorage {
} }
} }
EncryptionResult encrypt(String service, String username, String password) throws CryptoFailedException; EncryptionResult encrypt(@NonNull String service, @NonNull String username, @NonNull String password) throws CryptoFailedException;
DecryptionResult decrypt(String service, byte[] username, byte[] password) throws CryptoFailedException; DecryptionResult decrypt(@NonNull String service, @NonNull byte[] username, @NonNull byte[] password) throws CryptoFailedException;
void removeKey(String service) throws KeyStoreAccessException; void removeKey(@NonNull String service) throws KeyStoreAccessException;
String getCipherStorageName(); String getCipherStorageName();

View File

@ -1,6 +1,7 @@
package com.oblador.keychain.cipherStorage; package com.oblador.keychain.cipherStorage;
import android.os.Build; import android.os.Build;
import android.support.annotation.NonNull;
import com.facebook.android.crypto.keychain.AndroidConceal; import com.facebook.android.crypto.keychain.AndroidConceal;
import com.facebook.android.crypto.keychain.SharedPrefsBackedKeyChain; import com.facebook.android.crypto.keychain.SharedPrefsBackedKeyChain;
@ -16,7 +17,6 @@ import java.nio.charset.Charset;
public class CipherStorageFacebookConceal implements CipherStorage { public class CipherStorageFacebookConceal implements CipherStorage {
public static final String CIPHER_STORAGE_NAME = "FacebookConceal"; public static final String CIPHER_STORAGE_NAME = "FacebookConceal";
public static final String KEYCHAIN_DATA = "RN_KEYCHAIN"; public static final String KEYCHAIN_DATA = "RN_KEYCHAIN";
public static final String EMPTY_STRING = "";
private final Crypto crypto; private final Crypto crypto;
public CipherStorageFacebookConceal(ReactApplicationContext reactContext) { public CipherStorageFacebookConceal(ReactApplicationContext reactContext) {
@ -35,12 +35,10 @@ public class CipherStorageFacebookConceal implements CipherStorage {
} }
@Override @Override
public EncryptionResult encrypt(String service, String username, String password) throws CryptoFailedException { public EncryptionResult encrypt(@NonNull String service, @NonNull String username, @NonNull String password) throws CryptoFailedException {
if (!crypto.isAvailable()) { if (!crypto.isAvailable()) {
throw new CryptoFailedException("Crypto is missing"); throw new CryptoFailedException("Crypto is missing");
} }
service = service == null ? EMPTY_STRING : service;
Entity usernameEntity = createUsernameEntity(service); Entity usernameEntity = createUsernameEntity(service);
Entity passwordEntity = createPasswordEntity(service); Entity passwordEntity = createPasswordEntity(service);
@ -55,12 +53,10 @@ public class CipherStorageFacebookConceal implements CipherStorage {
} }
@Override @Override
public DecryptionResult decrypt(String service, byte[] username, byte[] password) throws CryptoFailedException { public DecryptionResult decrypt(@NonNull String service, @NonNull byte[] username, @NonNull byte[] password) throws CryptoFailedException {
if (!crypto.isAvailable()) { if (!crypto.isAvailable()) {
throw new CryptoFailedException("Crypto is missing"); throw new CryptoFailedException("Crypto is missing");
} }
service = service == null ? EMPTY_STRING : service;
Entity usernameEntity = createUsernameEntity(service); Entity usernameEntity = createUsernameEntity(service);
Entity passwordEntity = createPasswordEntity(service); Entity passwordEntity = createPasswordEntity(service);
@ -77,9 +73,10 @@ public class CipherStorageFacebookConceal implements CipherStorage {
} }
@Override @Override
public void removeKey(String service) { public void removeKey(@NonNull String service) {
// Facebook Conceal stores only one key across all services, so we cannot delete the key (otherwise decryption will fail for encrypted data of other services). // Facebook Conceal stores only one key across all services, so we cannot delete the key (otherwise decryption will fail for encrypted data of other services).
} }
private Entity createUsernameEntity(String service) { private Entity createUsernameEntity(String service) {
String prefix = getEntityPrefix(service); String prefix = getEntityPrefix(service);
return Entity.create(prefix + "user"); return Entity.create(prefix + "user");

View File

@ -4,6 +4,7 @@ import android.annotation.TargetApi;
import android.os.Build; import android.os.Build;
import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties; import android.security.keystore.KeyProperties;
import android.support.annotation.NonNull;
import com.oblador.keychain.exceptions.CryptoFailedException; import com.oblador.keychain.exceptions.CryptoFailedException;
import com.oblador.keychain.exceptions.KeyStoreAccessException; import com.oblador.keychain.exceptions.KeyStoreAccessException;
@ -30,7 +31,7 @@ import javax.crypto.spec.IvParameterSpec;
public class CipherStorageKeystoreAESCBC implements CipherStorage { public class CipherStorageKeystoreAESCBC implements CipherStorage {
public static final String CIPHER_STORAGE_NAME = "KeystoreAESCBC"; public static final String CIPHER_STORAGE_NAME = "KeystoreAESCBC";
public static final String DEFAULT_ALIAS = "RN_KEYCHAIN_DEFAULT_ALIAS"; public static final String DEFAULT_SERVICE = "RN_KEYCHAIN_DEFAULT_ALIAS";
public static final String KEYSTORE_TYPE = "AndroidKeyStore"; public static final String KEYSTORE_TYPE = "AndroidKeyStore";
public static final String ENCRYPTION_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES; public static final String ENCRYPTION_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES;
public static final String ENCRYPTION_BLOCK_MODE = KeyProperties.BLOCK_MODE_CBC; public static final String ENCRYPTION_BLOCK_MODE = KeyProperties.BLOCK_MODE_CBC;
@ -53,8 +54,8 @@ public class CipherStorageKeystoreAESCBC implements CipherStorage {
@TargetApi(Build.VERSION_CODES.M) @TargetApi(Build.VERSION_CODES.M)
@Override @Override
public EncryptionResult encrypt(String service, String username, String password) throws CryptoFailedException { public EncryptionResult encrypt(@NonNull String service, @NonNull String username, @NonNull String password) throws CryptoFailedException {
service = service == null ? DEFAULT_ALIAS : service; service = getDefaultServiceIfEmpty(service);
try { try {
KeyStore keyStore = getKeyStoreAndLoad(); KeyStore keyStore = getKeyStoreAndLoad();
@ -91,8 +92,8 @@ public class CipherStorageKeystoreAESCBC implements CipherStorage {
} }
@Override @Override
public DecryptionResult decrypt(String service, byte[] username, byte[] password) throws CryptoFailedException { public DecryptionResult decrypt(@NonNull String service, @NonNull byte[] username, @NonNull byte[] password) throws CryptoFailedException {
service = service == null ? DEFAULT_ALIAS : service; service = getDefaultServiceIfEmpty(service);
try { try {
KeyStore keyStore = getKeyStoreAndLoad(); KeyStore keyStore = getKeyStoreAndLoad();
@ -111,8 +112,8 @@ public class CipherStorageKeystoreAESCBC implements CipherStorage {
} }
@Override @Override
public void removeKey(String service) throws KeyStoreAccessException { public void removeKey(@NonNull String service) throws KeyStoreAccessException {
service = service == null ? DEFAULT_ALIAS : service; service = getDefaultServiceIfEmpty(service);
try { try {
KeyStore keyStore = getKeyStoreAndLoad(); KeyStore keyStore = getKeyStoreAndLoad();
@ -183,4 +184,9 @@ public class CipherStorageKeystoreAESCBC implements CipherStorage {
throw new KeyStoreAccessException("Could not access Keystore", e); throw new KeyStoreAccessException("Could not access Keystore", e);
} }
} }
@NonNull
private String getDefaultServiceIfEmpty(@NonNull String service) {
return service.isEmpty() ? DEFAULT_SERVICE : service;
}
} }