Update for seed changes

This commit is contained in:
Niels Andriesse 2019-08-19 15:34:21 +10:00
parent 24a03cd5bc
commit ae54c641a9
7 changed files with 61 additions and 55 deletions

View File

@ -465,7 +465,7 @@
android:windowSoftInputMode="stateUnchanged" android:windowSoftInputMode="stateUnchanged"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/> android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name="org.thoughtcrime.securesms.loki.KeyPairActivity" <activity android:name="org.thoughtcrime.securesms.loki.SeedActivity"
android:launchMode="singleTask" android:launchMode="singleTask"
android:theme="@style/TextSecure.DarkRegistrationTheme" android:theme="@style/TextSecure.DarkRegistrationTheme"
android:windowSoftInputMode="stateUnchanged" android:windowSoftInputMode="stateUnchanged"

View File

@ -5,7 +5,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".loki.KeyPairActivity"> tools:context=".loki.SeedActivity">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -311,19 +311,26 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
break; break;
case PREFERENCE_CATEGORY_SEED: case PREFERENCE_CATEGORY_SEED:
File languageFileDirectory = new File(getContext().getApplicationInfo().dataDir); File languageFileDirectory = new File(getContext().getApplicationInfo().dataDir);
String hexEncodedPrivateKey = SerializationKt.getHexEncodedPrivateKey(IdentityKeyUtil.getIdentityKeyPair(getContext())); try {
String seed = new MnemonicCodec(languageFileDirectory).encode(hexEncodedPrivateKey, MnemonicCodec.Language.Configuration.Companion.getEnglish()); String hexEncodedSeed = IdentityKeyUtil.retrieve(getContext(), IdentityKeyUtil.lokiSeedKey);
new AlertDialog.Builder(getContext()) if (hexEncodedSeed == null) {
.setTitle(R.string.activity_settings_seed_dialog_title) hexEncodedSeed = SerializationKt.getHexEncodedPrivateKey(IdentityKeyUtil.getIdentityKeyPair(getContext())); // Legacy account
.setMessage(seed) }
.setPositiveButton(R.string.activity_settings_seed_dialog_copy_button_title, (DialogInterface.OnClickListener) (dialog, which) -> { String seed = new MnemonicCodec(languageFileDirectory).encode(hexEncodedSeed, MnemonicCodec.Language.Configuration.Companion.getEnglish());
ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE); new AlertDialog.Builder(getContext())
ClipData clip = ClipData.newPlainText("seed", seed); .setTitle(R.string.activity_settings_seed_dialog_title)
clipboard.setPrimaryClip(clip); .setMessage(seed)
Toast.makeText(getContext(), R.string.activity_settings_seed_copied_message, Toast.LENGTH_SHORT).show(); .setPositiveButton(R.string.activity_settings_seed_dialog_copy_button_title, (DialogInterface.OnClickListener) (dialog, which) -> {
}) ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
.setNeutralButton(R.string.activity_settings_seed_dialog_ok_button_title, null) ClipData clip = ClipData.newPlainText("seed", seed);
.show(); clipboard.setPrimaryClip(clip);
Toast.makeText(getContext(), R.string.activity_settings_seed_copied_message, Toast.LENGTH_SHORT).show();
})
.setNeutralButton(R.string.activity_settings_seed_dialog_ok_button_title, null)
.show();
} catch (Exception e) {
// Do nothing
}
break; break;
default: default:
throw new AssertionError(); throw new AssertionError();

View File

@ -65,7 +65,7 @@ public class PassphraseCreateActivity extends PassphraseActivity {
passphrase); passphrase);
MasterSecretUtil.generateAsymmetricMasterSecret(PassphraseCreateActivity.this, masterSecret); MasterSecretUtil.generateAsymmetricMasterSecret(PassphraseCreateActivity.this, masterSecret);
IdentityKeyUtil.generateIdentityKeys(PassphraseCreateActivity.this); IdentityKeyUtil.generateIdentityKeyPair(PassphraseCreateActivity.this);
VersionTracker.updateLastSeenVersion(PassphraseCreateActivity.this); VersionTracker.updateLastSeenVersion(PassphraseCreateActivity.this);
TextSecurePreferences.setLastExperienceVersionCode(PassphraseCreateActivity.this, Util.getCanonicalVersionCode()); TextSecurePreferences.setLastExperienceVersionCode(PassphraseCreateActivity.this, Util.getCanonicalVersionCode());

View File

@ -24,14 +24,12 @@ import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.backup.BackupProtos; import org.thoughtcrime.securesms.backup.BackupProtos;
import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.Hex;
import org.whispersystems.libsignal.IdentityKey; import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.IdentityKeyPair; import org.whispersystems.libsignal.IdentityKeyPair;
import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.libsignal.ecc.Curve; import org.whispersystems.libsignal.ecc.Curve;
import org.whispersystems.libsignal.ecc.ECKeyPair; import org.whispersystems.libsignal.ecc.ECKeyPair;
import org.whispersystems.libsignal.ecc.ECPrivateKey; import org.whispersystems.libsignal.ecc.ECPrivateKey;
import org.whispersystems.libsignal.logging.Log;
import java.io.IOException; import java.io.IOException;
import java.util.LinkedList; import java.util.LinkedList;
@ -54,6 +52,8 @@ public class IdentityKeyUtil {
private static final String IDENTITY_PUBLIC_KEY_PREF = "pref_identity_public_v3"; private static final String IDENTITY_PUBLIC_KEY_PREF = "pref_identity_public_v3";
private static final String IDENTITY_PRIVATE_KEY_PREF = "pref_identity_private_v3"; private static final String IDENTITY_PRIVATE_KEY_PREF = "pref_identity_private_v3";
public static final String lokiSeedKey = "loki_seed";
public static boolean hasIdentityKey(Context context) { public static boolean hasIdentityKey(Context context) {
SharedPreferences preferences = context.getSharedPreferences(MasterSecretUtil.PREFERENCES_NAME, 0); SharedPreferences preferences = context.getSharedPreferences(MasterSecretUtil.PREFERENCES_NAME, 0);
@ -86,25 +86,21 @@ public class IdentityKeyUtil {
} }
} }
public static void generateIdentityKeyPair(Context context, String hexEncodedPrivateKey) { public static void generateIdentityKeyPair(Context context, byte[] seed) {
try { ECKeyPair keyPair;
byte[] privateKey = Hex.fromStringCondensed(hexEncodedPrivateKey); if (seed != null) {
ECKeyPair keyPair = Curve.generateKeyPair(privateKey); keyPair = Curve.generateKeyPair(seed);
IdentityKey publicKey = new IdentityKey(keyPair.getPublicKey()); } else {
save(context, IDENTITY_PUBLIC_KEY_PREF, Base64.encodeBytes(publicKey.serialize())); keyPair = Curve.generateKeyPair();
save(context, IDENTITY_PRIVATE_KEY_PREF, Base64.encodeBytes(keyPair.getPrivateKey().serialize()));
} catch (Exception e) {
Log.d("Loki", "Couldn't restore key pair from seed due to error: " + e.getMessage() + ".");
} }
IdentityKey publicKey = new IdentityKey(keyPair.getPublicKey());
ECPrivateKey privateKey = keyPair.getPrivateKey();
save(context, IDENTITY_PUBLIC_KEY_PREF, Base64.encodeBytes(publicKey.serialize()));
save(context, IDENTITY_PRIVATE_KEY_PREF, Base64.encodeBytes(privateKey.serialize()));
} }
public static void generateIdentityKeys(Context context) { public static void generateIdentityKeyPair(Context context) {
ECKeyPair djbKeyPair = Curve.generateKeyPair(); generateIdentityKeyPair(context, null);
IdentityKey djbIdentityKey = new IdentityKey(djbKeyPair.getPublicKey());
ECPrivateKey djbPrivateKey = djbKeyPair.getPrivateKey();
save(context, IDENTITY_PUBLIC_KEY_PREF, Base64.encodeBytes(djbIdentityKey.serialize()));
save(context, IDENTITY_PRIVATE_KEY_PREF, Base64.encodeBytes(djbPrivateKey.serialize()));
} }
public static void migrateIdentityKeys(@NonNull Context context, public static void migrateIdentityKeys(@NonNull Context context,
@ -120,7 +116,7 @@ public class IdentityKeyUtil {
delete(context, IDENTITY_PUBLIC_KEY_CIPHERTEXT_LEGACY_PREF); delete(context, IDENTITY_PUBLIC_KEY_CIPHERTEXT_LEGACY_PREF);
delete(context, IDENTITY_PRIVATE_KEY_CIPHERTEXT_LEGACY_PREF); delete(context, IDENTITY_PRIVATE_KEY_CIPHERTEXT_LEGACY_PREF);
} else { } else {
generateIdentityKeys(context); generateIdentityKeyPair(context);
} }
} }
} }
@ -163,12 +159,12 @@ public class IdentityKeyUtil {
} }
} }
private static String retrieve(Context context, String key) { public static String retrieve(Context context, String key) {
SharedPreferences preferences = context.getSharedPreferences(MasterSecretUtil.PREFERENCES_NAME, 0); SharedPreferences preferences = context.getSharedPreferences(MasterSecretUtil.PREFERENCES_NAME, 0);
return preferences.getString(key, null); return preferences.getString(key, null);
} }
private static void save(Context context, String key, String value) { public static void save(Context context, String key, String value) {
SharedPreferences preferences = context.getSharedPreferences(MasterSecretUtil.PREFERENCES_NAME, 0); SharedPreferences preferences = context.getSharedPreferences(MasterSecretUtil.PREFERENCES_NAME, 0);
Editor preferencesEditor = preferences.edit(); Editor preferencesEditor = preferences.edit();

View File

@ -29,7 +29,7 @@ class AccountDetailsActivity : BaseActionBarActivity() {
} }
val inputMethodManager = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager val inputMethodManager = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(nameEditText.windowToken, 0) inputMethodManager.hideSoftInputFromWindow(nameEditText.windowToken, 0)
startActivity(Intent(this, KeyPairActivity::class.java)) startActivity(Intent(this, SeedActivity::class.java))
finish() finish()
} }
} }

View File

@ -8,8 +8,8 @@ import android.os.Bundle
import android.view.View import android.view.View
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.Toast import android.widget.Toast
import kotlinx.android.synthetic.main.activity_key_pair.* import kotlinx.android.synthetic.main.activity_seed.*
import network.loki.messenger.R; import network.loki.messenger.R
import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.BaseActionBarActivity import org.thoughtcrime.securesms.BaseActionBarActivity
import org.thoughtcrime.securesms.ConversationListActivity import org.thoughtcrime.securesms.ConversationListActivity
@ -17,20 +17,20 @@ import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
import org.thoughtcrime.securesms.database.Address import org.thoughtcrime.securesms.database.Address
import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.IdentityDatabase import org.thoughtcrime.securesms.database.IdentityDatabase
import org.thoughtcrime.securesms.util.Hex
import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.libsignal.IdentityKeyPair import org.whispersystems.curve25519.Curve25519
import org.whispersystems.libsignal.util.KeyHelper import org.whispersystems.libsignal.util.KeyHelper
import org.whispersystems.signalservice.loki.crypto.MnemonicCodec import org.whispersystems.signalservice.loki.crypto.MnemonicCodec
import org.whispersystems.signalservice.loki.utilities.hexEncodedPrivateKey
import org.whispersystems.signalservice.loki.utilities.hexEncodedPublicKey import org.whispersystems.signalservice.loki.utilities.hexEncodedPublicKey
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
class KeyPairActivity : BaseActionBarActivity() { class SeedActivity : BaseActionBarActivity() {
private lateinit var languageFileDirectory: File private lateinit var languageFileDirectory: File
private var mode = Mode.Register private var mode = Mode.Register
set(newValue) { field = newValue; updateUI() } set(newValue) { field = newValue; updateUI() }
private var keyPair: IdentityKeyPair? = null private var seed: ByteArray? = null
set(newValue) { field = newValue; updateMnemonic() } set(newValue) { field = newValue; updateMnemonic() }
private var mnemonic: String? = null private var mnemonic: String? = null
set(newValue) { field = newValue; updateMnemonicTextView() } set(newValue) { field = newValue; updateMnemonicTextView() }
@ -42,9 +42,9 @@ class KeyPairActivity : BaseActionBarActivity() {
// region Lifecycle // region Lifecycle
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_key_pair) setContentView(R.layout.activity_seed)
setUpLanguageFileDirectory() setUpLanguageFileDirectory()
updateKeyPair() updateSeed()
copyButton.setOnClickListener { copy() } copyButton.setOnClickListener { copy() }
toggleModeButton.setOnClickListener { toggleMode() } toggleModeButton.setOnClickListener { toggleMode() }
registerOrRestoreButton.setOnClickListener { registerOrRestore() } registerOrRestoreButton.setOnClickListener { registerOrRestore() }
@ -75,9 +75,8 @@ class KeyPairActivity : BaseActionBarActivity() {
// endregion // endregion
// region Updating // region Updating
private fun updateKeyPair() { private fun updateSeed() {
IdentityKeyUtil.generateIdentityKeys(this) seed = Curve25519.getInstance("best").generateSeed(16)
keyPair = IdentityKeyUtil.getIdentityKeyPair(this)
} }
private fun updateUI() { private fun updateUI() {
@ -100,7 +99,8 @@ class KeyPairActivity : BaseActionBarActivity() {
} }
private fun updateMnemonic() { private fun updateMnemonic() {
mnemonic = MnemonicCodec(languageFileDirectory).encode(keyPair!!.hexEncodedPrivateKey) val hexEncodedSeed = Hex.toStringCondensed(seed)
mnemonic = MnemonicCodec(languageFileDirectory).encode(hexEncodedSeed)
} }
private fun updateMnemonicTextView() { private fun updateMnemonicTextView() {
@ -124,21 +124,24 @@ class KeyPairActivity : BaseActionBarActivity() {
} }
private fun registerOrRestore() { private fun registerOrRestore() {
val keyPair: IdentityKeyPair var seed: ByteArray
when (mode) { when (mode) {
Mode.Register -> keyPair = this.keyPair!! Mode.Register -> seed = this.seed!!
Mode.Restore -> { Mode.Restore -> {
val mnemonic = mnemonicEditText.text.toString() val mnemonic = mnemonicEditText.text.toString()
try { try {
val hexEncodedPrivateKey = MnemonicCodec(languageFileDirectory).decode(mnemonic) val hexEncodedSeed = MnemonicCodec(languageFileDirectory).decode(mnemonic)
IdentityKeyUtil.generateIdentityKeyPair(this, hexEncodedPrivateKey) seed = Hex.fromStringCondensed(hexEncodedSeed)
keyPair = IdentityKeyUtil.getIdentityKeyPair(this)
} catch (e: Exception) { } catch (e: Exception) {
val message = if (e is MnemonicCodec.DecodingError) e.description else MnemonicCodec.DecodingError.Generic.description val message = if (e is MnemonicCodec.DecodingError) e.description else MnemonicCodec.DecodingError.Generic.description
return Toast.makeText(this, message, Toast.LENGTH_SHORT).show() return Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
} }
} }
} }
IdentityKeyUtil.save(this, IdentityKeyUtil.lokiSeedKey, Hex.toStringCondensed(seed))
if (seed.count() == 16) seed = seed + seed
IdentityKeyUtil.generateIdentityKeyPair(this, seed)
val keyPair = IdentityKeyUtil.getIdentityKeyPair(this)
val publicKey = keyPair.publicKey val publicKey = keyPair.publicKey
val hexEncodedPublicKey = keyPair.hexEncodedPublicKey val hexEncodedPublicKey = keyPair.hexEncodedPublicKey
val registrationID = KeyHelper.generateRegistrationId(false) val registrationID = KeyHelper.generateRegistrationId(false)