mirror of
https://github.com/MaintainTeam/Hypatia.git
synced 2025-02-28 21:38:21 +03:00
GPG signature verification for databases
TODO: inform user if verification fails or key is unavailable Signed-off-by: Tad <tad@spotco.us>
This commit is contained in:
parent
76d06b504f
commit
44d2d1c905
17 changed files with 1326 additions and 628 deletions
2
.idea/compiler.xml
generated
2
.idea/compiler.xml
generated
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="11" />
|
||||
<bytecodeTargetLevel target="17" />
|
||||
</component>
|
||||
</project>
|
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
|
@ -54,7 +54,7 @@
|
|||
</value>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="JDK" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
|
|
|
@ -43,7 +43,6 @@ Geplante Updates
|
|||
- Automatische Datenbank-Updates
|
||||
- Automatische Datenbankerstellung
|
||||
- Client-seitige Datenbank-Generierung
|
||||
- Überprüfung der Datenbanksignatur
|
||||
- Datenbank-Sanity-Checks
|
||||
- Prüfung
|
||||
- Bessere GUI
|
||||
|
|
|
@ -45,7 +45,6 @@ Planned Updates
|
|||
- Automatic database updates
|
||||
- Automatic database generation
|
||||
- Client side database generation
|
||||
- Database signature verification
|
||||
- Database sanity checks
|
||||
- Testing
|
||||
- Better GUI
|
||||
|
@ -66,7 +65,8 @@ Credits
|
|||
- Nex (@botherder) for extra databases (CC BY-SA 4.0)
|
||||
- Amnesty International for extra databases (CC BY 2.0)
|
||||
- Echap for extra databases (CC BY 4.0)
|
||||
- RecursiveFileObserver.java (GPLv3): Daniel Gultsch, ownCloud Inc., Bartek Przybylski
|
||||
- RecursiveFileObserver.java (GPL-3.0-or-later): Daniel Gultsch, ownCloud Inc., Bartek Przybylski
|
||||
- GPGDetachedSignatureVerifier.java (GPL-2.0-or-later): Federico Fissore, Arduino LLC
|
||||
- Petra Mirelli for the German/Spanish/Italian translations, the app banner/feature graphic, and various tweaks.
|
||||
- Jean-Luc Tibaux and Petra Mirelli for the French translations.
|
||||
- @srccrow for the Italian translations.
|
||||
|
|
|
@ -43,7 +43,6 @@ Planowane aktualizacje
|
|||
- Automatyczne aktualizacje baz danych
|
||||
- Automatyczne generowanie baz danych
|
||||
- Generowanie baz danych przez klienta
|
||||
- Weryfikacja podpisów baz danych
|
||||
- Kontrola poprawności baz danych
|
||||
- Testy
|
||||
- Lepsze GUI
|
||||
|
|
|
@ -6,8 +6,8 @@ android {
|
|||
applicationId "us.spotco.malwarescanner"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 32
|
||||
versionCode 96
|
||||
versionName "2.30"
|
||||
versionCode 97
|
||||
versionName "2.31"
|
||||
resConfigs 'en', 'af', 'de', 'es', 'fi', 'fr', 'it', 'pl', 'pt', 'ru'
|
||||
}
|
||||
buildTypes {
|
||||
|
@ -29,5 +29,6 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation 'commons-io:commons-io:2.11.0'
|
||||
implementation 'org.bouncycastle:bcpg-jdk15to18:1.73'
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ package us.spotco.malwarescanner;
|
|||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
|
@ -72,12 +73,15 @@ class Database {
|
|||
public static void updateDatabase(Context context, ConcurrentLinkedQueue<SignatureDatabase> signatureDatabases) {
|
||||
initDatabase(context);
|
||||
log.append(context.getString(R.string.main_database_updating, signatureDatabases.size() + "") + "\n");
|
||||
if(!Utils.getDatabaseURL(context).equals(Utils.DATABASE_URL_DEFAULT)) {
|
||||
if (!Utils.getDatabaseURL(context).equals(Utils.DATABASE_URL_DEFAULT)) {
|
||||
log.append(context.getString(R.string.main_database_override, Utils.getDatabaseURL(context)) + "\n");
|
||||
}
|
||||
boolean onionRouting = prefs.getBoolean("ONION_ROUTING", false);
|
||||
new Downloader().executeOnExecutor(Utils.getThreadPoolExecutor(), onionRouting, Utils.getDatabaseURL(context) + "gpg.key", databasePath + "/gpg.key", Utils.getDatabaseURL(context));
|
||||
|
||||
for (SignatureDatabase signatureDatabase : signatureDatabases) {
|
||||
boolean onionRouting = prefs.getBoolean("ONION_ROUTING", false);
|
||||
new Downloader().executeOnExecutor(Utils.getThreadPoolExecutor(), onionRouting, signatureDatabase.getUrl(), databasePath + "/" + signatureDatabase.getName(), signatureDatabase.getBaseUrl());
|
||||
new Downloader().executeOnExecutor(Utils.getThreadPoolExecutor(), onionRouting, signatureDatabase.getUrl() + ".sig", databasePath + "/" + signatureDatabase.getName() + ".sig", signatureDatabase.getBaseUrl());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,39 +130,48 @@ class Database {
|
|||
signaturesSHA1.clear();
|
||||
signaturesSHA256.clear();
|
||||
System.gc();
|
||||
File publicKey = new File(databasePath + "/gpg.key");
|
||||
GPGDetachedSignatureVerifier verifier = new GPGDetachedSignatureVerifier(Utils.getSigningKey(context));
|
||||
for (SignatureDatabase database : signatureDatabases) {
|
||||
File databaseLocation = new File(databasePath + "/" + database.getName());
|
||||
if (databaseLocation.exists()) {
|
||||
File databaseSigLocation = new File(databasePath + "/" + database.getName() + ".sig");
|
||||
if (publicKey.exists() && databaseLocation.exists() && databaseSigLocation.exists()) {
|
||||
try {
|
||||
BufferedReader reader;
|
||||
if (databaseLocation.getName().endsWith(".gz")) {
|
||||
reader = new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(databaseLocation))));
|
||||
boolean validated = verifier.verify(databaseLocation, databaseSigLocation, publicKey);
|
||||
if (validated) {
|
||||
Log.d("Hypatia", "Successfully validated database");
|
||||
BufferedReader reader;
|
||||
if (databaseLocation.getName().endsWith(".gz")) {
|
||||
reader = new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(databaseLocation))));
|
||||
} else {
|
||||
reader = new BufferedReader(new FileReader(databaseLocation));
|
||||
}
|
||||
String line;
|
||||
if (database.getName().contains(".hdb")) {//.hdb format: md5, size, name
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.length() > 0) {
|
||||
String[] lineS = line.split(":");
|
||||
if (lineS[0].length() > 0) {
|
||||
signaturesMD5.put(lineS[0].substring(0, Utils.MAX_HASH_LENGTH), lineS[2].intern());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (database.getName().contains(".hsb")) {//.hsb format: sha256, size, name
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.length() > 0) {
|
||||
String[] lineS = line.split(":");
|
||||
if (lineS[0].length() == 32) {
|
||||
signaturesSHA1.put(lineS[0].substring(0, Utils.MAX_HASH_LENGTH), lineS[2].intern());
|
||||
} else if (lineS[0].length() > 0) {
|
||||
signaturesSHA256.put(lineS[0].substring(0, Utils.MAX_HASH_LENGTH), lineS[2].intern());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
reader.close();
|
||||
} else {
|
||||
reader = new BufferedReader(new FileReader(databaseLocation));
|
||||
Log.w("Hypatia", "Failed to validate database");
|
||||
}
|
||||
String line;
|
||||
if (database.getName().contains(".hdb")) {//.hdb format: md5, size, name
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.length() > 0) {
|
||||
String[] lineS = line.split(":");
|
||||
if (lineS[0].length() > 0) {
|
||||
signaturesMD5.put(lineS[0].substring(0, Utils.MAX_HASH_LENGTH), lineS[2].intern());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (database.getName().contains(".hsb")) {//.hsb format: sha256, size, name
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.length() > 0) {
|
||||
String[] lineS = line.split(":");
|
||||
if (lineS[0].length() == 32) {
|
||||
signaturesSHA1.put(lineS[0].substring(0, Utils.MAX_HASH_LENGTH), lineS[2].intern());
|
||||
} else if (lineS[0].length() > 0) {
|
||||
signaturesSHA256.put(lineS[0].substring(0, Utils.MAX_HASH_LENGTH), lineS[2].intern());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
reader.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* Source: https://github.com/arduino/Arduino/blob/master/arduino-core/src/cc/arduino/contributions/GPGDetachedSignatureVerifier.java
|
||||
* This file is part of Arduino.
|
||||
*
|
||||
* Copyright 2015 Arduino LLC (http://www.arduino.cc/)
|
||||
*
|
||||
* Arduino is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*/
|
||||
|
||||
package us.spotco.malwarescanner;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPObjectFactory;
|
||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
||||
import org.bouncycastle.openpgp.PGPSignature;
|
||||
import org.bouncycastle.openpgp.PGPSignatureList;
|
||||
import org.bouncycastle.openpgp.PGPUtil;
|
||||
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
|
||||
import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class GPGDetachedSignatureVerifier {
|
||||
|
||||
private final String keyId;
|
||||
|
||||
public GPGDetachedSignatureVerifier() {
|
||||
this(Utils.SIGNING_KEY_DEFAULT);
|
||||
}
|
||||
|
||||
public GPGDetachedSignatureVerifier(String keyId) {
|
||||
this.keyId = keyId;
|
||||
}
|
||||
|
||||
public boolean verify(File signedFile, File signature, File publicKey) throws IOException {
|
||||
FileInputStream signatureInputStream = null;
|
||||
FileInputStream signedFileInputStream = null;
|
||||
try {
|
||||
signatureInputStream = new FileInputStream(signature);
|
||||
PGPObjectFactory pgpObjectFactory = new PGPObjectFactory(signatureInputStream, new BcKeyFingerprintCalculator());
|
||||
|
||||
Object nextObject;
|
||||
try {
|
||||
nextObject = pgpObjectFactory.nextObject();
|
||||
if (!(nextObject instanceof PGPSignatureList)) {
|
||||
return false;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
PGPSignatureList pgpSignatureList = (PGPSignatureList) nextObject;
|
||||
assert pgpSignatureList.size() == 1;
|
||||
PGPSignature pgpSignature = pgpSignatureList.get(0);
|
||||
|
||||
PGPPublicKey pgpPublicKey = readPublicKey(publicKey, keyId);
|
||||
|
||||
pgpSignature.init(new BcPGPContentVerifierBuilderProvider(), pgpPublicKey);
|
||||
signedFileInputStream = new FileInputStream(signedFile);
|
||||
pgpSignature.update(IOUtils.toByteArray(signedFileInputStream));
|
||||
|
||||
return pgpSignature.verify();
|
||||
} catch (PGPException e) {
|
||||
throw new IOException(e);
|
||||
} finally {
|
||||
if (signatureInputStream != null) {
|
||||
signatureInputStream.close();
|
||||
}
|
||||
if (signedFileInputStream != null) {
|
||||
signedFileInputStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PGPPublicKey readPublicKey(File file, String id) throws IOException, PGPException {
|
||||
try (InputStream keyIn = new BufferedInputStream(new FileInputStream(file))) {
|
||||
return readPublicKey(keyIn, id);
|
||||
}
|
||||
}
|
||||
|
||||
private PGPPublicKey readPublicKey(InputStream input, String id) throws IOException, PGPException {
|
||||
PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(input), new BcKeyFingerprintCalculator());
|
||||
|
||||
Iterator<PGPPublicKeyRing> keyRingIter = pgpPub.getKeyRings();
|
||||
while (keyRingIter.hasNext()) {
|
||||
PGPPublicKeyRing keyRing = keyRingIter.next();
|
||||
|
||||
Iterator<PGPPublicKey> keyIter = keyRing.getPublicKeys();
|
||||
while (keyIter.hasNext()) {
|
||||
PGPPublicKey key = keyIter.next();
|
||||
|
||||
if (Long.toHexString(key.getKeyID()).toUpperCase().endsWith(id)) {
|
||||
return key;
|
||||
} else {
|
||||
Log.d("Hypatia", "readPublicKey: No match found, have key: " + Long.toHexString(key.getKeyID()).toUpperCase());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Can't find encryption key in key ring.");
|
||||
}
|
||||
|
||||
}
|
|
@ -24,7 +24,6 @@ import android.app.Activity;
|
|||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
|
@ -199,7 +198,7 @@ public class MainActivity extends Activity {
|
|||
}
|
||||
break;
|
||||
case R.id.mnuUpdateDatabase:
|
||||
if(malwareScanner.running) {
|
||||
if (malwareScanner.running) {
|
||||
logView.append(getString(R.string.lblScanRunning) + "\n");
|
||||
} else {
|
||||
if (prefs.getBoolean("ONION_ROUTING", false)) {
|
||||
|
@ -213,33 +212,41 @@ public class MainActivity extends Activity {
|
|||
selectDatabases();
|
||||
break;
|
||||
case R.id.mnuDatabaseServer:
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(getString(R.string.lblDatabaseServer));
|
||||
final EditText input = new EditText(this);
|
||||
input.setInputType(InputType.TYPE_CLASS_TEXT);
|
||||
input.setText(Utils.getDatabaseURL(this));
|
||||
builder.setView(input);
|
||||
builder.setPositiveButton(getString(R.string.lblOverride), new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
String newServer = input.getText().toString();
|
||||
if(!newServer.endsWith("/")) {
|
||||
newServer += "/";
|
||||
}
|
||||
prefs.edit().putString("DATABASE_SERVER", newServer).apply();
|
||||
AlertDialog.Builder builderServerOverride = new AlertDialog.Builder(this);
|
||||
builderServerOverride.setTitle(getString(R.string.lblDatabaseServer));
|
||||
final EditText inputServerOverride = new EditText(this);
|
||||
inputServerOverride.setInputType(InputType.TYPE_CLASS_TEXT);
|
||||
inputServerOverride.setText(Utils.getDatabaseURL(this));
|
||||
builderServerOverride.setView(inputServerOverride);
|
||||
builderServerOverride.setPositiveButton(getString(R.string.lblOverride), (dialog, which) -> {
|
||||
String newServer = inputServerOverride.getText().toString();
|
||||
if (!newServer.endsWith("/")) {
|
||||
newServer += "/";
|
||||
}
|
||||
prefs.edit().putString("DATABASE_SERVER", newServer).apply();
|
||||
});
|
||||
builder.setNegativeButton(getString(R.string.lblReset), new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
prefs.edit().putString("DATABASE_SERVER", Utils.DATABASE_URL_DEFAULT).apply();
|
||||
dialog.cancel();
|
||||
}
|
||||
builderServerOverride.setNegativeButton(getString(R.string.lblReset), (dialog, which) -> {
|
||||
prefs.edit().putString("DATABASE_SERVER", Utils.DATABASE_URL_DEFAULT).apply();
|
||||
dialog.cancel();
|
||||
});
|
||||
builder.show();
|
||||
builderServerOverride.show();
|
||||
break;
|
||||
case R.id.mnuSigningKey:
|
||||
AlertDialog.Builder builderKey = new AlertDialog.Builder(this);
|
||||
builderKey.setTitle(getString(R.string.lblSigningKey));
|
||||
final EditText inputKey = new EditText(this);
|
||||
inputKey.setInputType(InputType.TYPE_CLASS_TEXT);
|
||||
inputKey.setText(Utils.getSigningKey(this));
|
||||
builderKey.setView(inputKey);
|
||||
builderKey.setPositiveButton(getString(R.string.lblOverride), (dialog, which) -> prefs.edit().putString("SIGNING_KEY", inputKey.getText().toString()).apply());
|
||||
builderKey.setNegativeButton(getString(R.string.lblReset), (dialog, which) -> {
|
||||
prefs.edit().putString("SIGNING_KEY", Utils.SIGNING_KEY_DEFAULT).apply();
|
||||
dialog.cancel();
|
||||
});
|
||||
builderKey.show();
|
||||
break;
|
||||
case R.id.toggleRealtime:
|
||||
if(malwareScanner.running) {
|
||||
if (malwareScanner.running) {
|
||||
logView.append(getString(R.string.lblScanRunning) + "\n");
|
||||
} else {
|
||||
Intent realtimeScanner = new Intent(getApplicationContext(), MalwareScannerService.class);
|
||||
|
@ -308,7 +315,7 @@ public class MainActivity extends Activity {
|
|||
malwareScanner.executeOnExecutor(Utils.getThreadPoolExecutor(), filesToScan);
|
||||
new Thread(() -> {
|
||||
try {
|
||||
while(malwareScanner.running){
|
||||
while (malwareScanner.running) {
|
||||
Thread.sleep(500);
|
||||
}
|
||||
runOnUiThread(() -> updateScanButton(false));
|
||||
|
@ -319,7 +326,7 @@ public class MainActivity extends Activity {
|
|||
}
|
||||
|
||||
private void updateDatabase() {
|
||||
new Database((TextView) findViewById(R.id.txtLogOutput));
|
||||
new Database(findViewById(R.id.txtLogOutput));
|
||||
Database.updateDatabase(this, Database.signatureDatabases);
|
||||
if (Database.isDatabaseLoaded()) {
|
||||
Utils.getThreadPoolExecutor().execute(() -> Database.loadDatabase(getApplicationContext(), true, Database.signatureDatabases));
|
||||
|
@ -327,7 +334,7 @@ public class MainActivity extends Activity {
|
|||
}
|
||||
|
||||
private void updateScanButton(boolean running) {
|
||||
if(menu == null || menu.findItem(R.id.btnStartScan) == null) {
|
||||
if (menu == null || menu.findItem(R.id.btnStartScan) == null) {
|
||||
return;
|
||||
}
|
||||
if (running) {
|
||||
|
|
|
@ -35,6 +35,8 @@ class SignatureDatabase {
|
|||
return name;
|
||||
}
|
||||
|
||||
public final String getUrl() { return baseURL + name; }
|
||||
public final String getUrl() {
|
||||
return baseURL + name;
|
||||
}
|
||||
|
||||
}
|
|
@ -38,6 +38,7 @@ class Utils {
|
|||
public final static int MAX_SCAN_SIZE = (1000 * 1000) * 80; //80MB
|
||||
public final static int MAX_SCAN_SIZE_REALTIME = MAX_SCAN_SIZE / 2; //40MB
|
||||
public final static String DATABASE_URL_DEFAULT = "https://divested.dev/MalwareScannerSignatures/";
|
||||
public final static String SIGNING_KEY_DEFAULT = "BADFCABDDBF5B694";
|
||||
|
||||
public final static int MAX_HASH_LENGTH = 12;
|
||||
|
||||
|
@ -60,7 +61,7 @@ class Utils {
|
|||
if (maxThreads > 4) {
|
||||
maxThreads = 4;
|
||||
}
|
||||
if(maxThreads < 2) {
|
||||
if (maxThreads < 2) {
|
||||
maxThreads = 2;
|
||||
}
|
||||
return maxThreads;
|
||||
|
@ -104,6 +105,11 @@ class Utils {
|
|||
return prefs.getString("DATABASE_SERVER", DATABASE_URL_DEFAULT);
|
||||
}
|
||||
|
||||
public static String getSigningKey(Context context) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(BuildConfig.APPLICATION_ID, Context.MODE_PRIVATE);
|
||||
return prefs.getString("SIGNING_KEY", SIGNING_KEY_DEFAULT);
|
||||
}
|
||||
|
||||
public static void considerStartService(Context context) {
|
||||
if (!Utils.isServiceRunning(MalwareScannerService.class, context)) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(BuildConfig.APPLICATION_ID, Context.MODE_PRIVATE);
|
||||
|
|
|
@ -20,6 +20,9 @@
|
|||
<item
|
||||
android:id="@+id/mnuDatabaseServer"
|
||||
android:title="@string/lblDatabaseServer" />
|
||||
<item
|
||||
android:id="@+id/mnuSigningKey"
|
||||
android:title="@string/lblSigningKey" />
|
||||
<item
|
||||
android:id="@+id/toggleRealtime"
|
||||
android:title="@string/lblRealtimeScannerToggle"
|
||||
|
|
|
@ -61,4 +61,5 @@
|
|||
<string name="db_desc_size_large">Large</string>
|
||||
<string name="scan_control">Scan Control</string>
|
||||
<string name="lblScanRunning">Skipping action, a scan is running!</string>
|
||||
<string name="lblSigningKey">Database signing key</string>
|
||||
</resources>
|
||||
|
|
4
fastlane/metadata/android/en-US/changelogs/97.txt
Normal file
4
fastlane/metadata/android/en-US/changelogs/97.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
* Databases are now verified using GPG signatures
|
||||
* Users must use "Update databases" before use after installing this update
|
||||
* Databases that are not signed or fail to verify will be ignored
|
||||
* A custom database key is allowed to maintain support for third party database repos
|
Binary file not shown.
File diff suppressed because it is too large
Load diff
|
@ -60,6 +60,7 @@
|
|||
<trusted-key id="aa70c7c433d501636392ec02153e7a3c2b4e5118" group="org.eclipse.ee4j" name="project"/>
|
||||
<trusted-key id="afcc4c7594d09e2182c60e0f7a01b0f236e5430f" group="com.google.code.gson"/>
|
||||
<trusted-key id="b02335aa54ccf21e52bbf9abd9c565aa72ba2fdd" group="io.grpc"/>
|
||||
<trusted-key id="b6e73d84ea4fcc47166087253faad2cd5ecbb314" group="org.apache.commons" name="commons-parent"/>
|
||||
<trusted-key id="b801e2f8ef035068ec1139cc29579f18fa8fd93b" group="com.google.j2objc" name="j2objc-annotations" version="1.3"/>
|
||||
<trusted-key id="bcc135fc7ed8214f823d73e97fe9900f412d622e" group="com.google.flatbuffers" name="flatbuffers-java" version="1.12.0"/>
|
||||
<trusted-key id="bdb5fa4fe719d787fb3d3197f6d4a1d411e9d1ae" group="com.google.guava"/>
|
||||
|
@ -72,6 +73,7 @@
|
|||
<trusted-key id="ee0ca873074092f806f59b65d364abaa39a47320" group="com.google.errorprone"/>
|
||||
<trusted-key id="f254b35617dc255d9344bcfa873a8e86b4372146" group="org.codehaus.mojo"/>
|
||||
<trusted-key id="f3184bcd55f4d016e30d4c9bf42e87f9665015c9" group="org.jsoup" name="jsoup" version="1.13.1"/>
|
||||
<trusted-key id="fa77dcfef2ee6eb2debedd2c012579464d01c06a" group="org.apache" name="apache"/>
|
||||
<trusted-key id="fa7929f83ad44c4590f6cc6815c71c0a4e0b8edd" group="net.java.dev.jna"/>
|
||||
<trusted-key id="fc411cd3cb7dcb0abc9801058118b3bcdb1a5000" group="jakarta.xml.bind"/>
|
||||
</trusted-keys>
|
||||
|
@ -1155,6 +1157,11 @@
|
|||
<sha512 value="c675dc20d3d192a4193d651a6fa3ddac3bfe97844be536146ca6e78c29c1559b06fe9495be39d4dbf606b8a2cb0720391a6fe53f37d628949659c3224e4eaa8d" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.junit" name="junit-bom" version="5.7.2">
|
||||
<artifact name="junit-bom-5.7.2.pom">
|
||||
<pgp value="ff6e2c001948c5f2f38b0cc385911f425ec61b51"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.ow2" name="ow2" version="1.5">
|
||||
<artifact name="ow2-1.5.pom">
|
||||
<sha512 value="5445748e294cf9f23fe8f1e18e2ebb7108800d40f81a4566a73f9434fe21d2058d05acf3bc4d15f629151df47c42bcf948de3bba0b6a37982dfc3a8f1baf244d" origin="Generated by Gradle because artifact wasn't signed"/>
|
||||
|
|
Loading…
Add table
Reference in a new issue