Skip to content

Commit

Permalink
Enable KBS.
Browse files Browse the repository at this point in the history
  • Loading branch information
alan-signal authored and greyson-signal committed Jan 27, 2020
1 parent bdb30eb commit 7d15c60
Show file tree
Hide file tree
Showing 11 changed files with 149 additions and 111 deletions.
4 changes: 2 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ dependencies {

implementation project(':libsignal-service')

implementation 'org.signal:argon2:13.0@aar'
implementation 'org.signal:argon2:13.1@aar'
implementation 'org.signal:ringrtc-android:0.3.1'

implementation "me.leolin:ShortcutBadger:1.1.16"
Expand Down Expand Up @@ -229,7 +229,7 @@ android {
buildConfigField "int", "CONTENT_PROXY_PORT", "443"
buildConfigField "String", "SIGNAL_AGENT", "\"OWA\""
buildConfigField "String", "MRENCLAVE", "\"cd6cfc342937b23b1bdd3bbf9721aa5615ac9ff50a75c5527d441cd3276826c9\""
buildConfigField "String", "KEY_BACKUP_ENCLAVE_NAME", "\"\""
buildConfigField "String", "KEY_BACKUP_ENCLAVE_NAME", "\"fe7c1bfae98f9b073d220366ea31163ee82f6d04bead774f71ca8e5c40847bfe\""
buildConfigField "String", "KEY_BACKUP_MRENCLAVE", "\"a3baab19ef6ce6f34ab9ebb25ba722725ae44a8872dc0ff08ad6d83a9489de87\""
buildConfigField "String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF\""
buildConfigField "String[]", "LANGUAGES", "new String[]{\"" + autoResConfig().collect { s -> s.replace('-r', '_') }.join('", "') + '"}'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,34 +10,84 @@

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

public final class PinHashing_hashPin_Test {

@Test
public void argon2_hashed_pin_password() throws IOException {
String pin = "password";
byte[] backupId = Hex.fromStringCondensed("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
MasterKey masterKey = new MasterKey(Hex.fromStringCondensed("202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f"));

HashedPin hashedPin = PinHashing.hashPin("password", () -> backupId);
HashedPin hashedPin = PinHashing.hashPin(pin, () -> backupId);
KbsData kbsData = hashedPin.createNewKbsData(masterKey);

assertArrayEquals(Hex.fromStringCondensed("ab7e8499d21f80a6600b3b9ee349ac6d72c07e3359fe885a934ba7aa844429f8"), hashedPin.getKbsAccessKey());
assertArrayEquals(hashedPin.getKbsAccessKey(), kbsData.getKbsAccessKey());
assertArrayEquals(Hex.fromStringCondensed("ab7e8499d21f80a6600b3b9ee349ac6d72c07e3359fe885a934ba7aa844429f8"), kbsData.getKbsAccessKey());
assertArrayEquals(Hex.fromStringCondensed("3f33ce58eb25b40436592a30eae2a8fabab1899095f4e2fba6e2d0dc43b4a2d9cac5a3931748522393951e0e54dec769"), kbsData.getCipherText());
assertEquals(masterKey, kbsData.getMasterKey());

String localPinHash = PinHashing.localPinHash(pin);
assertTrue(PinHashing.verifyLocalPinHash(localPinHash, pin));
}

@Test
public void argon2_hashed_pin_another_password() throws IOException {
String pin = "anotherpassword";
byte[] backupId = Hex.fromStringCondensed("202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f");
MasterKey masterKey = new MasterKey(Hex.fromStringCondensed("88a787415a2ecd79da0d1016a82a27c5c695c9a19b88b0aa1d35683280aa9a67"));

HashedPin hashedPin = PinHashing.hashPin("anotherpassword ", () -> backupId);
HashedPin hashedPin = PinHashing.hashPin(pin, () -> backupId);
KbsData kbsData = hashedPin.createNewKbsData(masterKey);

assertArrayEquals(Hex.fromStringCondensed("301d9dd1e96f20ce51083f67d3298fd37b97525de8324d5e12ed2d407d3d927b"), hashedPin.getKbsAccessKey());
assertArrayEquals(hashedPin.getKbsAccessKey(), kbsData.getKbsAccessKey());
assertArrayEquals(Hex.fromStringCondensed("301d9dd1e96f20ce51083f67d3298fd37b97525de8324d5e12ed2d407d3d927b"), kbsData.getKbsAccessKey());
assertArrayEquals(Hex.fromStringCondensed("9d9b05402ea39c17ff1c9298c8a0e86784a352aa02a74943bf8bcf07ec0f4b574a5b786ad0182c8d308d9eb06538b8c9"), kbsData.getCipherText());
assertEquals(masterKey, kbsData.getMasterKey());

String localPinHash = PinHashing.localPinHash(pin);
assertTrue(PinHashing.verifyLocalPinHash(localPinHash, pin));
}

@Test
public void argon2_hashed_pin_password_with_spaces_diacritics_and_non_arabic_numerals() throws IOException {
String pin = " Pass६örd ";
byte[] backupId = Hex.fromStringCondensed("cba811749042b303a6a7efa5ccd160aea5e3ea243c8d2692bd13d515732f51a8");
MasterKey masterKey = new MasterKey(Hex.fromStringCondensed("9571f3fde1e58588ba49bcf82be1b301ca3859a6f59076f79a8f47181ef952bf"));

HashedPin hashedPin = PinHashing.hashPin(pin, () -> backupId);
KbsData kbsData = hashedPin.createNewKbsData(masterKey);

assertArrayEquals(hashedPin.getKbsAccessKey(), kbsData.getKbsAccessKey());
assertArrayEquals(Hex.fromStringCondensed("ab645acdccc1652a48a34b2ac6926340ff35c03034013f68760f20013f028dd8"), kbsData.getKbsAccessKey());
assertArrayEquals(Hex.fromStringCondensed("11c0ba1834db15e47c172f6c987c64bd4cfc69c6047dd67a022afeec0165a10943f204d5b8f37b3cb7bab21c6dfc39c8"), kbsData.getCipherText());
assertEquals(masterKey, kbsData.getMasterKey());

assertEquals("577939bccb2b6638c39222d5a97998a867c5e154e30b82cc120f2dd07a3de987", kbsData.getMasterKey().deriveRegistrationLock());

String localPinHash = PinHashing.localPinHash(pin);
assertTrue(PinHashing.verifyLocalPinHash(localPinHash, pin));
}

@Test
public void argon2_hashed_pin_password_with_just_non_arabic_numerals() throws IOException {
String pin = " ६१८ ";
byte[] backupId = Hex.fromStringCondensed("717dc111a98423a57196512606822fca646c653facd037c10728f14ba0be2ab3");
MasterKey masterKey = new MasterKey(Hex.fromStringCondensed("0432d735b32f66d0e3a70d4f9cc821a8529521a4937d26b987715d8eff4e4c54"));

HashedPin hashedPin = PinHashing.hashPin(pin, () -> backupId);
KbsData kbsData = hashedPin.createNewKbsData(masterKey);


assertArrayEquals(hashedPin.getKbsAccessKey(), kbsData.getKbsAccessKey());
assertArrayEquals(Hex.fromStringCondensed("d2fedabd0d4c17a371491c9722578843a26be3b4923e28d452ab2fc5491e794b"), kbsData.getKbsAccessKey());
assertArrayEquals(Hex.fromStringCondensed("877ef871ef1fc668401c717ef21aa12e8523579fb1ff4474b76f28c2293537c80cc7569996c9e0229bea7f378e3a824e"), kbsData.getCipherText());
assertEquals(masterKey, kbsData.getMasterKey());

assertEquals("23a75cb1df1a87df45cc2ed167c2bdc85ab1220b847c88761b0005cac907fce5", kbsData.getMasterKey().deriveRegistrationLock());

String localPinHash = PinHashing.localPinHash(pin);
assertTrue(PinHashing.verifyLocalPinHash(localPinHash, pin));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
import org.thoughtcrime.securesms.recipients.LiveRecipientCache;
import org.thoughtcrime.securesms.service.IncomingMessageObserver;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.FrameRateTracker;
import org.thoughtcrime.securesms.util.IasKeyStore;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
Expand Down Expand Up @@ -69,7 +68,6 @@ public static synchronized void init(@NonNull Application application, @NonNull
}

public static synchronized @NonNull KeyBackupService getKeyBackupService() {
if (!FeatureFlags.kbs()) throw new AssertionError();
return getSignalServiceAccountManager().getKeyBackupService(IasKeyStore.getIasKeyStore(application),
BuildConfig.KEY_BACKUP_ENCLAVE_NAME,
BuildConfig.KEY_BACKUP_MRENCLAVE,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package org.thoughtcrime.securesms.lock;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import org.signal.argon2.Argon2;
import org.signal.argon2.Argon2Exception;
import org.signal.argon2.MemoryCost;
import org.signal.argon2.Type;
import org.signal.argon2.UnknownTypeException;
import org.signal.argon2.Version;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.signalservice.api.KeyBackupService;
Expand All @@ -15,17 +15,14 @@

public final class PinHashing {

private static final Type KBS_PIN_ARGON_TYPE = Type.Argon2id;
private static final Type LOCAL_PIN_ARGON_TYPE = Type.Argon2i;

private PinHashing() {
}

public static HashedPin hashPin(@NonNull String pin, @NonNull KeyBackupService.HashSession hashSession) {
return PinHasher.hashPin(PinHasher.normalize(pin), password -> {
try {
return new Argon2.Builder(Version.V13)
.type(KBS_PIN_ARGON_TYPE)
.type(Type.Argon2id)
.memoryCost(MemoryCost.MiB(16))
.parallelism(1)
.iterations(32)
Expand All @@ -43,7 +40,7 @@ public static String localPinHash(@NonNull String pin) {
byte[] normalized = PinHasher.normalize(pin);
try {
return new Argon2.Builder(Version.V13)
.type(LOCAL_PIN_ARGON_TYPE)
.type(Type.Argon2i)
.memoryCost(MemoryCost.KiB(256))
.parallelism(1)
.iterations(50)
Expand All @@ -58,6 +55,10 @@ public static String localPinHash(@NonNull String pin) {

public static boolean verifyLocalPinHash(@NonNull String localPinHash, @NonNull String pin) {
byte[] normalized = PinHasher.normalize(pin);
return Argon2.verify(localPinHash, normalized, LOCAL_PIN_ARGON_TYPE);
try {
return Argon2.verify(localPinHash, normalized);
} catch (UnknownTypeException e) {
throw new AssertionError(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.migrations.RegistrationPinV2MigrationJob;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ThemeUtil;
Expand Down Expand Up @@ -126,10 +125,8 @@ private static TextWatcher getV1PinWatcher(@NonNull Context context, AlertDialog
dialog.dismiss();
RegistrationLockReminders.scheduleReminder(context, true);

if (FeatureFlags.kbs()) {
Log.i(TAG, "Pin V1 successfully remembered, scheduling a migration to V2");
ApplicationDependencies.getJobManager().add(new RegistrationPinV2MigrationJob());
}
Log.i(TAG, "Pin V1 successfully remembered, scheduling a migration to V2");
ApplicationDependencies.getJobManager().add(new RegistrationPinV2MigrationJob());
}
});
}
Expand Down Expand Up @@ -201,34 +198,27 @@ protected void onPreExecute() {
@Override
protected Boolean doInBackground(Void... voids) {
try {
if (!FeatureFlags.kbs()) {
Log.i(TAG, "Setting V1 pin");
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
accountManager.setPin(pinValue);
TextSecurePreferences.setDeprecatedRegistrationLockPin(context, pinValue);
Log.i(TAG, "Setting pin on KBS");

KbsValues kbsValues = SignalStore.kbsValues();
MasterKey masterKey = kbsValues.getOrCreateMasterKey();
KeyBackupService keyBackupService = ApplicationDependencies.getKeyBackupService();
KeyBackupService.PinChangeSession pinChangeSession = keyBackupService.newPinChangeSession();
HashedPin hashedPin = PinHashing.hashPin(pinValue, pinChangeSession);
RegistrationLockData kbsData = pinChangeSession.setPin(hashedPin, masterKey);
RegistrationLockData restoredData = keyBackupService.newRestoreSession(kbsData.getTokenResponse())
.restorePin(hashedPin);

if (!restoredData.getMasterKey().equals(masterKey)) {
throw new AssertionError("Failed to set the pin correctly");
} else {
Log.i(TAG, "Setting pin on KBS");

KbsValues kbsValues = SignalStore.kbsValues();
MasterKey masterKey = kbsValues.getOrCreateMasterKey();
KeyBackupService keyBackupService = ApplicationDependencies.getKeyBackupService();
KeyBackupService.PinChangeSession pinChangeSession = keyBackupService.newPinChangeSession();
HashedPin hashedPin = PinHashing.hashPin(pinValue, pinChangeSession);
RegistrationLockData kbsData = pinChangeSession.setPin(hashedPin, masterKey);
RegistrationLockData restoredData = keyBackupService.newRestoreSession(kbsData.getTokenResponse())
.restorePin(hashedPin);

if (!restoredData.getMasterKey().equals(masterKey)) {
throw new AssertionError("Failed to set the pin correctly");
} else {
Log.i(TAG, "Set and retrieved pin on KBS successfully");
}

kbsValues.setRegistrationLockMasterKey(restoredData, PinHashing.localPinHash(pinValue));
TextSecurePreferences.clearOldRegistrationLockPin(context);
TextSecurePreferences.setRegistrationLockLastReminderTime(context, System.currentTimeMillis());
TextSecurePreferences.setRegistrationLockNextReminderInterval(context, RegistrationLockReminders.INITIAL_INTERVAL);
Log.i(TAG, "Set and retrieved pin on KBS successfully");
}

kbsValues.setRegistrationLockMasterKey(restoredData, PinHashing.localPinHash(pinValue));
TextSecurePreferences.clearOldRegistrationLockPin(context);
TextSecurePreferences.setRegistrationLockLastReminderTime(context, System.currentTimeMillis());
TextSecurePreferences.setRegistrationLockNextReminderInterval(context, RegistrationLockReminders.INITIAL_INTERVAL);
return true;
} catch (IOException | UnauthenticatedResponseException | KeyBackupServicePinException e) {
Log.w(TAG, e);
Expand Down Expand Up @@ -282,23 +272,18 @@ protected void onPreExecute() {
@Override
protected Boolean doInBackground(Void... voids) {
try {
if (!FeatureFlags.kbs()) {
Log.i(TAG, "Removing v2 registration lock pin from server");
KbsValues kbsValues = SignalStore.kbsValues();
TokenResponse currentToken = kbsValues.getRegistrationLockTokenResponse();

KeyBackupService keyBackupService = ApplicationDependencies.getKeyBackupService();
keyBackupService.newPinChangeSession(currentToken).removePin();
kbsValues.clearRegistrationLock();

// It is possible a migration has not occurred, in this case, we need to remove the old V1 Pin
if (TextSecurePreferences.isV1RegistrationLockEnabled(context)) {
Log.i(TAG, "Removing v1 registration lock pin from server");
ApplicationDependencies.getSignalServiceAccountManager().removeV1Pin();
} else {
Log.i(TAG, "Removing v2 registration lock pin from server");
KbsValues kbsValues = SignalStore.kbsValues();
TokenResponse currentToken = kbsValues.getRegistrationLockTokenResponse();

KeyBackupService keyBackupService = ApplicationDependencies.getKeyBackupService();
keyBackupService.newPinChangeSession(currentToken).removePin();
kbsValues.clearRegistrationLock();

// It is possible a migration has not occurred, in this case, we need to remove the old V1 Pin
if (TextSecurePreferences.isV1RegistrationLockEnabled(context)) {
Log.i(TAG, "Removing v1 registration lock pin from server");
ApplicationDependencies.getSignalServiceAccountManager().removeV1Pin();
}
}
TextSecurePreferences.clearOldRegistrationLockPin(context);
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.lock.PinHashing;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.KeyBackupService;
import org.whispersystems.signalservice.api.KeyBackupServicePinException;
Expand All @@ -23,6 +22,7 @@
import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

/**
* Deliberately not a {@link MigrationJob} because it is not something that needs to run at app start.
Expand All @@ -41,6 +41,7 @@ public RegistrationPinV2MigrationJob() {
.addConstraint(NetworkConstraint.KEY)
.setLifespan(Job.Parameters.IMMORTAL)
.setMaxAttempts(Job.Parameters.UNLIMITED)
.setMaxBackoff(TimeUnit.HOURS.toMillis(2))
.build());
}

Expand All @@ -55,11 +56,6 @@ private RegistrationPinV2MigrationJob(@NonNull Parameters parameters) {

@Override
protected void onRun() throws IOException, UnauthenticatedResponseException, KeyBackupServicePinException {
if (!FeatureFlags.kbs()) {
Log.i(TAG, "Not migrating pin to KBS");
return;
}

if (!TextSecurePreferences.isV1RegistrationLockEnabled(context)) {
Log.i(TAG, "Registration lock disabled");
return;
Expand Down

0 comments on commit 7d15c60

Please sign in to comment.