Test various Argon2 parameters.

master
Alan Evans 2020-01-15 16:11:41 -05:00 committed by GitHub
parent ddedf73939
commit ace1855797
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 321 additions and 1 deletions

View File

@ -117,6 +117,7 @@ dependencies {
implementation project(':libsignal-service')
implementation 'org.signal:argon2:13.0@aar'
implementation 'org.signal:ringrtc-android:0.3.1'
implementation "me.leolin:ShortcutBadger:1.1.16"

View File

@ -0,0 +1,246 @@
package org.thoughtcrime.securesms.jobs;
import android.app.ActivityManager;
import androidx.annotation.NonNull;
import org.signal.argon2.Argon2;
import org.signal.argon2.Argon2Exception;
import org.signal.argon2.MemoryCost;
import org.signal.argon2.Type;
import org.signal.argon2.Version;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.nio.charset.StandardCharsets;
import java.text.NumberFormat;
import java.util.Locale;
public final class Argon2TestJob extends BaseJob {
public static final String KEY = "Argon2TestJob";
private static final String TAG = Log.tag(Argon2TestJob.class);
private Argon2TestJob(@NonNull Parameters parameters) {
super(parameters);
}
public Argon2TestJob() {
this(new Parameters.Builder()
.setMaxAttempts(1)
.build());
}
@Override
protected void onRun() throws InterruptedException {
if (TextSecurePreferences.isArgon2Tested(context)) {
Log.w(TAG, "Skipping Argon2 test, it has been run before");
return;
}
TextSecurePreferences.setArgon2Tested(context,true);
Log.i(TAG, "Starting Argon2 test");
Argon2.Builder argon2Builder = new Argon2.Builder(Version.V13)
.type(Type.Argon2id)
.parallelism(1);
MemoryCost memoryCost = MemoryCost.MiB(4);
try {
checkRam(memoryCost);
} catch (Exception e) {
throw new Argon2NotEnoughRam4(e.getMessage());
}
test(argon2Builder.memoryCost(memoryCost)
.iterations(8)
.build(),
"c0286e8dfd91b633f41d9dc13dc99b3705b62e23349dd399ff68be5fc7720c41",
"$argon2id$v=19$m=4096,t=8,p=1$c29tZXNhbHQ$wChujf2RtjP0HZ3BPcmbNwW2LiM0ndOZ/2i+X8dyDEE");
Thread.sleep(3000);
memoryCost = MemoryCost.MiB(8);
try {
checkRam(memoryCost);
} catch (Exception e) {
throw new Argon2NotEnoughRam8(e.getMessage());
}
test(argon2Builder.memoryCost(memoryCost)
.iterations(3)
.build(),
"c52fdf6c6dc5e4e82b826b00d795781540d6d50458a43f0ccc44a7d701830318",
"$argon2id$v=19$m=8192,t=3,p=1$c29tZXNhbHQ$xS/fbG3F5OgrgmsA15V4FUDW1QRYpD8MzESn1wGDAxg");
Thread.sleep(3000);
memoryCost = MemoryCost.MiB(16);
try {
checkRam(memoryCost);
} catch (Exception e) {
throw new Argon2NotEnoughRam16(e.getMessage());
}
test(argon2Builder.memoryCost(memoryCost)
.iterations(3)
.build(),
"d41922d454814e3dbf2828108bb43a5b6319b22fc9f169528c20d1a2846e06c6",
"$argon2id$v=19$m=16384,t=3,p=1$c29tZXNhbHQ$1Bki1FSBTj2/KCgQi7Q6W2MZsi/J8WlSjCDRooRuBsY");
Thread.sleep(3000);
memoryCost = MemoryCost.MiB(32);
try {
checkRam(memoryCost);
} catch (Exception e) {
throw new Argon2NotEnoughRam32(e.getMessage());
}
test(argon2Builder.memoryCost(memoryCost)
.iterations(2)
.build(),
"8365c0271b505e7fa397982790802de7b62f71f4e11e05f7a4e6b2ad4f75fec0",
"$argon2id$v=19$m=32768,t=2,p=1$c29tZXNhbHQ$g2XAJxtQXn+jl5gnkIAt57YvcfThHgX3pOayrU91/sA");
Log.i(TAG, "Argon2 test complete");
}
private void test(Argon2 argon2, String expectedHashHex, String expectedEncoded) {
long startTime = System.currentTimeMillis();
Argon2.Result hash;
try {
hash = argon2.hash("signal".getBytes(StandardCharsets.UTF_8),
"somesalt".getBytes(StandardCharsets.UTF_8));
} catch (Argon2Exception e) {
throw new Argon2TestJobRuntimeException(e);
}
long endTime = System.currentTimeMillis();
Log.i(TAG, String.format(Locale.US, "Argon2 hash complete:%n%s%.3f seconds", hash, (endTime - startTime) / 1000f));
if (!hash.getHashHex().equals(expectedHashHex)) {
throw new Argon2HashIncorrectRuntimeException("Hash was not correct");
}
if (!hash.getEncoded().equals(expectedEncoded)) {
throw new Argon2EncodedHashIncorrectRuntimeException("Encoded Hash was not correct");
}
}
private void checkRam(MemoryCost memoryCost) throws Exception {
NumberFormat numberFormat = NumberFormat.getInstance(Locale.US);
ActivityManager activityManager = ServiceUtil.getActivityManager(context);
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
activityManager.getMemoryInfo(memoryInfo);
long remainingRam = memoryInfo.availMem - memoryInfo.threshold - memoryCost.toBytes();
if (remainingRam <= 0) {
throw new Exception(String.format("Not enough RAM available without taking the system into a low memory state.%n" +
"Available: %s%n" +
"Low memory threshold: %s%n" +
"Requested: %s%n" +
"Shortfall: %s",
numberFormat.format(memoryInfo.availMem),
numberFormat.format(memoryInfo.threshold),
numberFormat.format(memoryCost.toBytes()),
numberFormat.format(-remainingRam)
));
} else {
Log.i(TAG, String.format("Enough RAM available without taking the system into a low memory state.%n" +
"Available: %s%n" +
"Low memory threshold: %s%n" +
"Requested: %s%n" +
"Surplus: %s",
numberFormat.format(memoryInfo.availMem),
numberFormat.format(memoryInfo.threshold),
numberFormat.format(memoryCost.toBytes()),
numberFormat.format(remainingRam)
));
}
}
@Override
protected boolean onShouldRetry(@NonNull Exception e) {
return false;
}
@Override
public @NonNull Data serialize() {
return Data.EMPTY;
}
@Override
public @NonNull String getFactoryKey() {
return KEY;
}
@Override
public void onCanceled() {
}
private static abstract class Argon2RuntimeException extends RuntimeException {
private Argon2RuntimeException(String message) {
super(message);
}
private Argon2RuntimeException(Throwable inner) {
super(inner);
}
}
private static class Argon2HashIncorrectRuntimeException extends Argon2RuntimeException {
private Argon2HashIncorrectRuntimeException(String message) {
super(message);
}
}
private static class Argon2EncodedHashIncorrectRuntimeException extends Argon2RuntimeException {
private Argon2EncodedHashIncorrectRuntimeException(String message) {
super(message);
}
}
private static class Argon2TestJobRuntimeException extends Argon2RuntimeException {
private Argon2TestJobRuntimeException(Throwable t) {
super(t);
}
}
private static class Argon2NotEnoughRam4 extends Argon2RuntimeException {
private Argon2NotEnoughRam4(String message) {
super(message);
}
}
private static class Argon2NotEnoughRam8 extends Argon2RuntimeException {
private Argon2NotEnoughRam8(String message) {
super(message);
}
}
private static class Argon2NotEnoughRam16 extends Argon2RuntimeException {
private Argon2NotEnoughRam16(String message) {
super(message);
}
}
private static class Argon2NotEnoughRam32 extends Argon2RuntimeException {
private Argon2NotEnoughRam32(String message) {
super(message);
}
}
public static final class Factory implements Job.Factory<Argon2TestJob> {
@Override
public @NonNull Argon2TestJob create(@NonNull Parameters parameters, @NonNull Data data) {
return new Argon2TestJob(parameters);
}
}
}

View File

@ -18,6 +18,7 @@ import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraintOb
import org.thoughtcrime.securesms.jobmanager.migrations.RecipientIdFollowUpJobMigration;
import org.thoughtcrime.securesms.jobmanager.migrations.RecipientIdFollowUpJobMigration2;
import org.thoughtcrime.securesms.jobmanager.migrations.RecipientIdJobMigration;
import org.thoughtcrime.securesms.migrations.Argon2TestMigrationJob;
import org.thoughtcrime.securesms.migrations.AvatarMigrationJob;
import org.thoughtcrime.securesms.migrations.CachedAttachmentsMigrationJob;
import org.thoughtcrime.securesms.migrations.DatabaseMigrationJob;
@ -94,6 +95,7 @@ public final class JobManagerFactories {
put(TypingSendJob.KEY, new TypingSendJob.Factory());
put(UpdateApkJob.KEY, new UpdateApkJob.Factory());
put(MarkerJob.KEY, new MarkerJob.Factory());
put(Argon2TestJob.KEY, new Argon2TestJob.Factory());
// Migrations
put(AvatarMigrationJob.KEY, new AvatarMigrationJob.Factory());
@ -105,6 +107,7 @@ public final class JobManagerFactories {
put(StickerLaunchMigrationJob.KEY, new StickerLaunchMigrationJob.Factory());
put(UuidMigrationJob.KEY, new UuidMigrationJob.Factory());
put(RegistrationPinV2MigrationJob.KEY, new RegistrationPinV2MigrationJob.Factory());
put(Argon2TestMigrationJob.KEY, new Argon2TestMigrationJob.Factory());
// Dead jobs
put("PushContentReceiveJob", new FailingJob.Factory());

View File

@ -10,6 +10,7 @@ import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobs.Argon2TestJob;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
@ -38,7 +39,7 @@ public class ApplicationMigrations {
private static final int LEGACY_CANONICAL_VERSION = 455;
public static final int CURRENT_VERSION = 8;
public static final int CURRENT_VERSION = 9;
private static final class Version {
static final int LEGACY = 1;
@ -49,6 +50,7 @@ public class ApplicationMigrations {
static final int UUIDS = 6;
static final int CACHED_ATTACHMENTS = 7;
static final int STICKERS_LAUNCH = 8;
static final int TEST_ARGON2 = 9;
}
/**
@ -193,6 +195,10 @@ public class ApplicationMigrations {
jobs.put(Version.STICKERS_LAUNCH, new StickerLaunchMigrationJob());
}
if (lastSeenVersion < Version.TEST_ARGON2) {
jobs.put(Version.TEST_ARGON2, new Argon2TestMigrationJob());
}
return jobs;
}

View File

@ -0,0 +1,51 @@
package org.thoughtcrime.securesms.migrations;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobs.Argon2TestJob;
/**
* Triggers a Argon2 Test, just once.
*/
public final class Argon2TestMigrationJob extends MigrationJob {
public static final String KEY = "Argon2TestMigrationJob";
private Argon2TestMigrationJob(Parameters parameters) {
super(parameters);
}
public Argon2TestMigrationJob() {
this(new Parameters.Builder().build());
}
@Override
boolean isUiBlocking() {
return false;
}
@Override
void performMigration() {
ApplicationDependencies.getJobManager().add(new Argon2TestJob());
}
@Override
boolean shouldRetry(@NonNull Exception e) {
return false;
}
@Override
public @NonNull String getFactoryKey() {
return KEY;
}
public static final class Factory implements Job.Factory<Argon2TestMigrationJob> {
@Override
public @NonNull Argon2TestMigrationJob create(@NonNull Parameters parameters, @NonNull Data data) {
return new Argon2TestMigrationJob(parameters);
}
}
}

View File

@ -216,6 +216,8 @@ public class TextSecurePreferences {
private static final String STORAGE_MANIFEST_VERSION = "pref_storage_manifest_version";
private static final String ARGON2_TESTED = "argon2_tested";
public static boolean isScreenLockEnabled(@NonNull Context context) {
return getBooleanPreference(context, SCREEN_LOCK, false);
}
@ -1339,6 +1341,14 @@ public class TextSecurePreferences {
setLongPreference(context, STORAGE_MANIFEST_VERSION, version);
}
public static boolean isArgon2Tested(Context context) {
return getBooleanPreference(context, ARGON2_TESTED, false);
}
public static void setArgon2Tested(Context context, boolean tested) {
setBooleanPreference(context, ARGON2_TESTED, tested);
}
public static void setBooleanPreference(Context context, String key, boolean value) {
PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(key, value).apply();
}

View File

@ -351,6 +351,9 @@ dependencyVerification {
['org.signal:android-database-sqlcipher:3.5.9-S3',
'33d4063336893af00b9d68b418e7b290cace74c20ce8aacffddc0911010d3d73'],
['org.signal:argon2:13.0',
'34709776041840533a15e79d8abc4ebce6d0aa99bf8d39bd4202ef5791296f72'],
['org.signal:ringrtc-android:0.3.1',
'785c422a2322f810141d85f63eab3874961b19face886daaf140ab011d39e166'],