Refactor Android-specific code out of bramble-core.

This commit is contained in:
akwizgran
2020-01-10 17:31:16 +00:00
parent f650b2236e
commit c61c9bbc02
13 changed files with 210 additions and 230 deletions

View File

@@ -1,6 +1,6 @@
package org.briarproject.briar.android;
import org.briarproject.bramble.api.crypto.KeyStoreConfig;
import org.briarproject.bramble.api.crypto.KeyStrengthener;
import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -8,19 +8,18 @@ import java.io.File;
import javax.annotation.Nullable;
import static android.os.Build.VERSION.SDK_INT;
@NotNullByDefault
class AndroidDatabaseConfig implements DatabaseConfig {
private final File dbDir, keyDir;
@Nullable
private final KeyStoreConfig keyStoreConfig;
private final KeyStrengthener keyStrengthener;
AndroidDatabaseConfig(File dbDir, File keyDir) {
AndroidDatabaseConfig(File dbDir, File keyDir,
@Nullable KeyStrengthener keyStrengthener) {
this.dbDir = dbDir;
this.keyDir = keyDir;
keyStoreConfig = SDK_INT >= 23 ? new AndroidKeyStoreConfig() : null;
this.keyStrengthener = keyStrengthener;
}
@Override
@@ -35,7 +34,7 @@ class AndroidDatabaseConfig implements DatabaseConfig {
@Nullable
@Override
public KeyStoreConfig getKeyStoreConfig() {
return keyStoreConfig;
public KeyStrengthener getKeyStrengthener() {
return keyStrengthener;
}
}

View File

@@ -1,66 +0,0 @@
package org.briarproject.briar.android;
import android.security.keystore.KeyGenParameterSpec;
import org.briarproject.bramble.api.crypto.KeyStoreConfig;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.security.spec.AlgorithmParameterSpec;
import java.util.List;
import androidx.annotation.RequiresApi;
import static android.os.Build.VERSION.SDK_INT;
import static android.security.keystore.KeyProperties.PURPOSE_SIGN;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
@RequiresApi(23)
@NotNullByDefault
class AndroidKeyStoreConfig implements KeyStoreConfig {
private final List<AlgorithmParameterSpec> specs;
AndroidKeyStoreConfig() {
KeyGenParameterSpec noStrongBox =
new KeyGenParameterSpec.Builder("db", PURPOSE_SIGN)
.setKeySize(256)
.build();
if (SDK_INT >= 28) {
// Prefer StrongBox if available
KeyGenParameterSpec strongBox =
new KeyGenParameterSpec.Builder("db", PURPOSE_SIGN)
.setIsStrongBoxBacked(true)
.setKeySize(256)
.build();
specs = asList(strongBox, noStrongBox);
} else {
specs = singletonList(noStrongBox);
}
}
@Override
public String getKeyStoreType() {
return "AndroidKeyStore";
}
@Override
public String getKeyAlias() {
return "db";
}
@Override
public String getProviderName() {
return "AndroidKeyStore";
}
@Override
public String getMacAlgorithmName() {
return "HmacSHA256";
}
@Override
public List<AlgorithmParameterSpec> getParameterSpecs() {
return specs;
}
}

View File

@@ -0,0 +1,118 @@
package org.briarproject.briar.android;
import android.security.keystore.KeyGenParameterSpec;
import org.briarproject.bramble.api.crypto.KeyStrengthener;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStore.Entry;
import java.security.KeyStore.SecretKeyEntry;
import java.security.spec.AlgorithmParameterSpec;
import java.util.List;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import androidx.annotation.RequiresApi;
import static android.os.Build.VERSION.SDK_INT;
import static android.security.keystore.KeyProperties.KEY_ALGORITHM_HMAC_SHA256;
import static android.security.keystore.KeyProperties.PURPOSE_SIGN;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static java.util.logging.Level.INFO;
import static java.util.logging.Logger.getLogger;
@RequiresApi(23)
@NotNullByDefault
class AndroidKeyStrengthener implements KeyStrengthener {
private static final Logger LOG =
getLogger(AndroidKeyStrengthener.class.getName());
private static final String KEY_STORE_TYPE = "AndroidKeyStore";
private static final String PROVIDER_NAME = "AndroidKeyStore";
private static final String KEY_ALIAS = "db";
private static final int KEY_BITS = 256;
private final List<AlgorithmParameterSpec> specs;
AndroidKeyStrengthener() {
KeyGenParameterSpec noStrongBox =
new KeyGenParameterSpec.Builder(KEY_ALIAS, PURPOSE_SIGN)
.setKeySize(KEY_BITS)
.build();
if (SDK_INT >= 28) {
// Prefer StrongBox if available
KeyGenParameterSpec strongBox =
new KeyGenParameterSpec.Builder(KEY_ALIAS, PURPOSE_SIGN)
.setIsStrongBoxBacked(true)
.setKeySize(KEY_BITS)
.build();
specs = asList(strongBox, noStrongBox);
} else {
specs = singletonList(noStrongBox);
}
}
@GuardedBy("this")
@Nullable
private javax.crypto.SecretKey storedKey = null;
@Override
public synchronized boolean isInitialised() {
if (storedKey != null) return true;
try {
KeyStore ks = KeyStore.getInstance(KEY_STORE_TYPE);
ks.load(null);
Entry entry = ks.getEntry(KEY_ALIAS, null);
if (entry instanceof SecretKeyEntry) {
storedKey = ((SecretKeyEntry) entry).getSecretKey();
LOG.info("Loaded key from keystore");
return true;
}
return false;
} catch (GeneralSecurityException | IOException e) {
throw new RuntimeException(e);
}
}
@Override
public synchronized SecretKey strengthenKey(SecretKey k) {
try {
if (!isInitialised()) initialise();
// Use the input key and the stored key to derive the output key
Mac mac = Mac.getInstance(KEY_ALGORITHM_HMAC_SHA256);
mac.init(storedKey);
return new SecretKey(mac.doFinal(k.getBytes()));
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
private synchronized void initialise() throws GeneralSecurityException {
// Try the parameter specs in order of preference
for (AlgorithmParameterSpec spec : specs) {
try {
KeyGenerator kg = KeyGenerator.getInstance(
KEY_ALGORITHM_HMAC_SHA256, PROVIDER_NAME);
kg.init(spec);
storedKey = kg.generateKey();
LOG.info("Stored key in keystore");
return;
} catch (Exception e) {
if (LOG.isLoggable(INFO))
LOG.info("Could not generate key: " + e);
// Fall back to next spec
}
}
throw new GeneralSecurityException("Could not generate key");
}
}

View File

@@ -10,6 +10,7 @@ import com.vanniktech.emoji.RecentEmoji;
import org.briarproject.bramble.api.FeatureFlags;
import org.briarproject.bramble.api.battery.BatteryManager;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyStrengthener;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.event.EventBus;
@@ -56,6 +57,7 @@ import dagger.Module;
import dagger.Provides;
import static android.content.Context.MODE_PRIVATE;
import static android.os.Build.VERSION.SDK_INT;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static org.briarproject.bramble.api.reporting.ReportingConstants.DEV_ONION_ADDRESS;
@@ -101,7 +103,9 @@ public class AppModule {
File dbDir = app.getApplicationContext().getDir("db", MODE_PRIVATE);
File keyDir = app.getApplicationContext().getDir("key", MODE_PRIVATE);
StrictMode.setThreadPolicy(tp);
return new AndroidDatabaseConfig(dbDir, keyDir);
KeyStrengthener keyStrengthener = SDK_INT >= 23
? new AndroidKeyStrengthener() : null;
return new AndroidDatabaseConfig(dbDir, keyDir, keyStrengthener);
}
@Provides