Skip to content

Commit

Permalink
Migrate profile key into database.
Browse files Browse the repository at this point in the history
  • Loading branch information
alan-signal authored and greyson-signal committed Feb 14, 2020
1 parent b92c389 commit f10d1ea
Show file tree
Hide file tree
Showing 11 changed files with 87 additions and 57 deletions.
Original file line number Diff line number Diff line change
@@ -1,38 +1,25 @@
package org.thoughtcrime.securesms.crypto;


import android.content.Context;
import androidx.annotation.NonNull;

import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import androidx.annotation.NonNull;

import java.io.IOException;
import org.thoughtcrime.securesms.recipients.Recipient;

public class ProfileKeyUtil {
public final class ProfileKeyUtil {

public static synchronized boolean hasProfileKey(@NonNull Context context) {
return TextSecurePreferences.getProfileKey(context) != null;
private ProfileKeyUtil() {
}

public static synchronized @NonNull byte[] getProfileKey(@NonNull Context context) {
try {
String encodedProfileKey = TextSecurePreferences.getProfileKey(context);

if (encodedProfileKey == null) {
encodedProfileKey = Util.getSecret(32);
TextSecurePreferences.setProfileKey(context, encodedProfileKey);
}

return Base64.decode(encodedProfileKey);
} catch (IOException e) {
throw new AssertionError(e);
/**
* @deprecated Will inline later as part of Versioned profiles.
*/
@Deprecated
public static @NonNull byte[] getProfileKey(@NonNull Context context) {
byte[] profileKey = Recipient.self().getProfileKey();
if (profileKey == null) {
throw new AssertionError();
}
}

public static synchronized @NonNull byte[] rotateProfileKey(@NonNull Context context) {
TextSecurePreferences.setProfileKey(context, null);
return getProfileKey(context);
return profileKey;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public static Optional<UnidentifiedAccessPair> getAccessFor(@NonNull Context con
{
try {
byte[] theirUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipient);
byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context);
byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(ProfileKeyUtil.getProfileKey(context));
byte[] ourUnidentifiedAccessCertificate = recipient.resolve().isUuidSupported() && Recipient.self().isUuidSupported()
? TextSecurePreferences.getUnidentifiedAccessCertificate(context)
: TextSecurePreferences.getUnidentifiedAccessCertificateLegacy(context);
Expand Down Expand Up @@ -75,7 +75,7 @@ public static Optional<UnidentifiedAccessPair> getAccessFor(@NonNull Context con

public static Optional<UnidentifiedAccessPair> getAccessForSync(@NonNull Context context) {
try {
byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context);
byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(ProfileKeyUtil.getProfileKey(context));
byte[] ourUnidentifiedAccessCertificate = Recipient.self().isUuidSupported() ? TextSecurePreferences.getUnidentifiedAccessCertificate(context)
: TextSecurePreferences.getUnidentifiedAccessCertificateLegacy(context);

Expand All @@ -97,8 +97,8 @@ public static Optional<UnidentifiedAccessPair> getAccessForSync(@NonNull Context
}
}

public static @NonNull byte[] getSelfUnidentifiedAccessKey(@NonNull Context context) {
return UnidentifiedAccess.deriveAccessKeyFrom(ProfileKeyUtil.getProfileKey(context));
public static @NonNull byte[] getSelfUnidentifiedAccessKey(@NonNull byte[] selfProfileKey) {
return UnidentifiedAccess.deriveAccessKeyFrom(selfProfileKey);
}

private static @Nullable byte[] getTargetUnidentifiedAccessKey(@NonNull Recipient recipient) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@
import org.whispersystems.libsignal.util.Pair;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.util.UuidUtil;
import org.whispersystems.signalservice.api.storage.SignalContactRecord;
import org.whispersystems.signalservice.api.util.UuidUtil;

import java.io.Closeable;
import java.io.IOException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import android.net.Uri;
import android.os.Build;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.text.TextUtils;

import androidx.annotation.NonNull;
Expand Down Expand Up @@ -47,10 +48,12 @@
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.SqlUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;

import java.io.File;
import java.util.List;
Expand Down Expand Up @@ -105,8 +108,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
private static final int STICKER_PACK_ORDER = 44;
private static final int MEGAPHONES = 45;
private static final int MEGAPHONE_FIRST_APPEARANCE = 46;
private static final int PROFILE_KEY_TO_DB = 47;

private static final int DATABASE_VERSION = 46;
private static final int DATABASE_VERSION = 47;
private static final String DATABASE_NAME = "signal.db";

private final Context context;
Expand Down Expand Up @@ -724,6 +728,21 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("ALTER TABLE megaphone ADD COLUMN first_visible INTEGER DEFAULT 0");
}

if (oldVersion < PROFILE_KEY_TO_DB) {
String localNumber = TextSecurePreferences.getLocalNumber(context);
if (!TextUtils.isEmpty(localNumber)) {
String encodedProfileKey = PreferenceManager.getDefaultSharedPreferences(context).getString("pref_profile_key", null);
byte[] profileKey = encodedProfileKey != null ? Base64.decodeOrThrow(encodedProfileKey) : Util.getSecretBytes(32);
ContentValues values = new ContentValues(1);

values.put("profile_key", Base64.encodeBytes(profileKey));

if (db.update("recipient", values, "phone = ?", new String[]{localNumber}) == 0) {
throw new AssertionError("No rows updated!");
}
}
}

db.setTransactionSuccessful();
} finally {
db.endTransaction();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@
import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract;

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

import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
Expand Down Expand Up @@ -42,8 +41,6 @@
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
Expand Down Expand Up @@ -201,14 +198,17 @@ private void generateFullContactUpdate()
archived.contains(recipient.getId())));
}

if (ProfileKeyUtil.hasProfileKey(context)) {
Recipient self = Recipient.self();

Recipient self = Recipient.self();
byte[] profileKey = self.getProfileKey();

if (profileKey != null) {
out.write(new DeviceContact(RecipientUtil.toSignalServiceAddress(context, self),
Optional.absent(),
Optional.absent(),
Optional.of(self.getColor().serialize()),
Optional.absent(),
Optional.of(ProfileKeyUtil.getProfileKey(context)),
Optional.of(profileKey),
false,
self.getExpireMessages() > 0 ? Optional.of(self.getExpireMessages()) : Optional.absent(),
Optional.fromNullable(inboxPositions.get(self.getId())),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import androidx.annotation.NonNull;

import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.Data;
Expand Down Expand Up @@ -47,7 +48,7 @@ private RefreshAttributesJob(@NonNull Job.Parameters parameters) {
public void onRun() throws IOException {
int registrationId = TextSecurePreferences.getLocalRegistrationId(context);
boolean fetchesMessages = TextSecurePreferences.isFcmDisabled(context);
byte[] unidentifiedAccessKey = UnidentifiedAccessUtil.getSelfUnidentifiedAccessKey(context);
byte[] unidentifiedAccessKey = UnidentifiedAccessUtil.getSelfUnidentifiedAccessKey(ProfileKeyUtil.getProfileKey(context));
boolean universalUnidentifiedAccess = TextSecurePreferences.isUniversalUnidentifiedAccess(context);
String pin = null;
String registrationLockToken = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
Expand All @@ -14,6 +15,7 @@
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import org.whispersystems.signalservice.api.util.StreamDetails;
import org.whispersystems.signalservice.internal.util.Util;

import java.io.File;
import java.io.FileInputStream;
Expand Down Expand Up @@ -48,9 +50,12 @@ private RotateProfileKeyJob(@NonNull Job.Parameters parameters) {

@Override
public void onRun() throws Exception {
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
byte[] profileKey = ProfileKeyUtil.rotateProfileKey(context);
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
byte[] profileKey = Util.getSecretBytes(32);
Recipient self = Recipient.self();

recipientDatabase.setProfileKey(self.getId(), profileKey);
accountManager.setProfileName(profileKey, TextSecurePreferences.getProfileName(context).serialize());
accountManager.setProfileAvatar(profileKey, getProfileAvatar());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@
import androidx.annotation.Nullable;

import org.thoughtcrime.securesms.color.MaterialColor;
import org.thoughtcrime.securesms.database.RecipientDatabase.InsightsBannerTier;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
import org.thoughtcrime.securesms.database.RecipientDatabase.InsightsBannerTier;
import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings;
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode;
Expand Down Expand Up @@ -90,7 +89,7 @@ public class RecipientDetails {
this.profileName = isLocalNumber ? TextSecurePreferences.getProfileName(context) : settings.getProfileName();
this.defaultSubscriptionId = settings.getDefaultSubscriptionId();
this.registered = settings.getRegistered();
this.profileKey = isLocalNumber ? ProfileKeyUtil.getProfileKey(context) : settings.getProfileKey();
this.profileKey = settings.getProfileKey();
this.profileAvatar = settings.getProfileAvatar();
this.profileSharing = settings.isProfileSharing();
this.systemContact = systemContact;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@
import org.thoughtcrime.securesms.lock.PinHashing;
import org.thoughtcrime.securesms.lock.RegistrationLockReminders;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.migrations.RegistrationPinV2MigrationJob;
import org.thoughtcrime.securesms.push.AccountManagerFactory;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.service.DirectoryRefreshListener;
import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.IdentityKeyPair;
import org.whispersystems.libsignal.state.PreKeyRecord;
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
Expand Down Expand Up @@ -192,8 +193,15 @@ private static void verifyAccount(@NonNull Context context,
{
boolean isV2KbsPin = kbsTokenResponse != null;
int registrationId = KeyHelper.generateRegistrationId(false);
byte[] unidentifiedAccessKey = UnidentifiedAccessUtil.getSelfUnidentifiedAccessKey(context);
boolean universalUnidentifiedAccess = TextSecurePreferences.isUniversalUnidentifiedAccess(context);
byte[] profileKey = findExistingProfileKey(context, credentials.getE164number());

if (profileKey == null) {
profileKey = Util.getSecretBytes(32);
Log.i(TAG, "No profile key found, created a new one");
}

byte[] unidentifiedAccessKey = UnidentifiedAccessUtil.getSelfUnidentifiedAccessKey(profileKey);

TextSecurePreferences.setLocalRegistrationId(context, registrationId);
SessionUtil.archiveAllSessions(context);
Expand Down Expand Up @@ -227,6 +235,7 @@ private static void verifyAccount(@NonNull Context context,

TextSecurePreferences.setLocalNumber(context, credentials.getE164number());
TextSecurePreferences.setLocalUuid(context, uuid);
recipientDatabase.setProfileKey(selfId, profileKey);
ApplicationDependencies.getRecipientCache().clearSelf();

TextSecurePreferences.setFcmToken(context, fcmToken);
Expand Down Expand Up @@ -260,6 +269,17 @@ private static void verifyAccount(@NonNull Context context,
}
}

private static @Nullable byte[] findExistingProfileKey(@NonNull Context context, @NonNull String e164number) {
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
Optional<RecipientId> recipient = recipientDatabase.getByE164(e164number);

if (recipient.isPresent()) {
return Recipient.resolved(recipient.get()).getProfileKey();
}

return null;
}

private static void repostPinToResetTries(@NonNull Context context, @Nullable String pin, @NonNull RegistrationLockData kbsData) {
if (pin == null) return;

Expand Down
8 changes: 8 additions & 0 deletions app/src/main/java/org/thoughtcrime/securesms/util/Base64.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,12 @@ private Base64() {
public static @NonNull String encodeBytes(@NonNull byte[] source) {
return org.whispersystems.util.Base64.encodeBytes(source);
}

public static @NonNull byte[] decodeOrThrow(@NonNull String s) {
try {
return org.whispersystems.util.Base64.decode(s);
} catch (IOException e) {
throw new AssertionError();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,6 @@ public class TextSecurePreferences {
private static final String MULTI_DEVICE_PROVISIONED_PREF = "pref_multi_device";
public static final String DIRECT_CAPTURE_CAMERA_ID = "pref_direct_capture_camera_id";
private static final String ALWAYS_RELAY_CALLS_PREF = "pref_turn_only";
private static final String PROFILE_KEY_PREF = "pref_profile_key";
private static final String PROFILE_NAME_PREF = "pref_profile_name";
private static final String PROFILE_AVATAR_ID_PREF = "pref_profile_avatar_id";
public static final String READ_RECEIPTS_PREF = "pref_read_receipts";
Expand Down Expand Up @@ -435,14 +434,6 @@ public static void setIsGifSearchInGridLayout(Context context, boolean isGrid) {
setBooleanPreference(context, GIF_GRID_LAYOUT, isGrid);
}

public static @Nullable String getProfileKey(Context context) {
return getStringPreference(context, PROFILE_KEY_PREF, null);
}

public static void setProfileKey(Context context, String key) {
setStringPreference(context, PROFILE_KEY_PREF, key);
}

public static void setProfileName(Context context, ProfileName name) {
setStringPreference(context, PROFILE_NAME_PREF, name.serialize());
}
Expand Down

0 comments on commit f10d1ea

Please sign in to comment.