Big overhaul for proper site-specific algorithm support and big Android UI update.
This commit is contained in:
parent
a6ab9b9194
commit
145008406d
@ -23,13 +23,13 @@ public abstract class MasterKey {
|
||||
@Nullable
|
||||
private byte[] masterKey;
|
||||
|
||||
public static MasterKey create(final String fullName, final String masterPassword) {
|
||||
public static MasterKey create(final String fullName, final char[] masterPassword) {
|
||||
|
||||
return create( Version.CURRENT, fullName, masterPassword );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static MasterKey create(Version version, final String fullName, final String masterPassword) {
|
||||
public static MasterKey create(Version version, final String fullName, final char[] masterPassword) {
|
||||
|
||||
switch (version) {
|
||||
case V0:
|
||||
@ -52,7 +52,7 @@ public abstract class MasterKey {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected abstract byte[] deriveKey(final String masterPassword);
|
||||
protected abstract byte[] deriveKey(final char[] masterPassword);
|
||||
|
||||
protected abstract Version getAlgorithm();
|
||||
|
||||
@ -73,7 +73,7 @@ public abstract class MasterKey {
|
||||
return idForBytes( getMasterKey() );
|
||||
}
|
||||
|
||||
public abstract String encode(final String siteName, final MPSiteType siteType, int siteCounter, final MPSiteVariant siteVariant,
|
||||
public abstract String encode(@Nonnull final String siteName, final MPSiteType siteType, int siteCounter, final MPSiteVariant siteVariant,
|
||||
@Nullable final String siteContext);
|
||||
|
||||
public boolean isValid() {
|
||||
@ -88,10 +88,10 @@ public abstract class MasterKey {
|
||||
}
|
||||
}
|
||||
|
||||
public MasterKey revalidate(final String masterPassword) {
|
||||
public MasterKey revalidate(final char[] masterPassword) {
|
||||
invalidate();
|
||||
|
||||
logger.trc( "masterPassword: %s", masterPassword );
|
||||
logger.trc( "masterPassword: %s", new String( masterPassword ) );
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
masterKey = deriveKey( masterPassword );
|
||||
|
@ -6,10 +6,10 @@ import com.google.common.primitives.Bytes;
|
||||
import com.lambdaworks.crypto.SCrypt;
|
||||
import com.lyndir.lhunath.opal.system.*;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Arrays;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
||||
@ -48,7 +48,7 @@ public class MasterKeyV0 extends MasterKey {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected byte[] deriveKey(final String masterPassword) {
|
||||
protected byte[] deriveKey(final char[] masterPassword) {
|
||||
String fullName = getFullName();
|
||||
byte[] fullNameBytes = fullName.getBytes( MP_charset );
|
||||
byte[] fullNameLengthBytes = bytesForInt( fullName.length() );
|
||||
@ -58,13 +58,18 @@ public class MasterKeyV0 extends MasterKey {
|
||||
logger.trc( "key scope: %s", mpKeyScope );
|
||||
logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
|
||||
|
||||
CharBuffer mpChars = CharBuffer.wrap( masterPassword );
|
||||
byte[] mpBytes = MP_charset.encode( mpChars ).array();
|
||||
try {
|
||||
return SCrypt.scrypt( masterPassword.getBytes( MP_charset ), masterKeySalt, MP_N, MP_r, MP_p, MP_dkLen );
|
||||
return SCrypt.scrypt( mpBytes, masterKeySalt, MP_N, MP_r, MP_p, MP_dkLen );
|
||||
}
|
||||
catch (GeneralSecurityException e) {
|
||||
logger.bug( e );
|
||||
return null;
|
||||
}
|
||||
finally {
|
||||
Arrays.fill( mpBytes, (byte) 0 );
|
||||
}
|
||||
}
|
||||
|
||||
public String encode(final String siteName, final MPSiteType siteType, int siteCounter, final MPSiteVariant siteVariant,
|
||||
|
@ -4,7 +4,9 @@ import com.google.common.primitives.Bytes;
|
||||
import com.lambdaworks.crypto.SCrypt;
|
||||
import com.lyndir.lhunath.opal.system.CodeUtils;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import java.nio.CharBuffer;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Arrays;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
||||
@ -31,7 +33,7 @@ public class MasterKeyV3 extends MasterKeyV2 {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected byte[] deriveKey(final String masterPassword) {
|
||||
protected byte[] deriveKey(final char[] masterPassword) {
|
||||
byte[] fullNameBytes = getFullName().getBytes( MP_charset );
|
||||
byte[] fullNameLengthBytes = bytesForInt( fullNameBytes.length );
|
||||
|
||||
@ -40,12 +42,17 @@ public class MasterKeyV3 extends MasterKeyV2 {
|
||||
logger.trc( "key scope: %s", mpKeyScope );
|
||||
logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
|
||||
|
||||
CharBuffer mpChars = CharBuffer.wrap( masterPassword );
|
||||
byte[] mpBytes = MP_charset.encode( mpChars ).array();
|
||||
try {
|
||||
return SCrypt.scrypt( masterPassword.getBytes( MP_charset ), masterKeySalt, MP_N, MP_r, MP_p, MP_dkLen );
|
||||
return SCrypt.scrypt( mpBytes, masterKeySalt, MP_N, MP_r, MP_p, MP_dkLen );
|
||||
}
|
||||
catch (GeneralSecurityException e) {
|
||||
logger.bug( e );
|
||||
return null;
|
||||
}
|
||||
finally {
|
||||
Arrays.fill( mpBytes, (byte) 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
/**
|
||||
*
|
||||
* @author lhunath, 15-02-04
|
||||
*/
|
||||
|
||||
|
||||
@ParametersAreNonnullByDefault package com.lyndir.masterpassword;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
@ -80,7 +80,7 @@ public class MPWTests {
|
||||
@Nonnull
|
||||
@Override
|
||||
public String get() {
|
||||
return parentCase.getMasterPassword();
|
||||
return new String( parentCase.getMasterPassword() );
|
||||
}
|
||||
} );
|
||||
keyID = ifNotNullElse( keyID, new NNSupplier<String>() {
|
||||
@ -148,8 +148,8 @@ public class MPWTests {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
public String getMasterPassword() {
|
||||
return masterPassword;
|
||||
public char[] getMasterPassword() {
|
||||
return masterPassword.toCharArray();
|
||||
}
|
||||
|
||||
public String getKeyID() {
|
||||
|
@ -5,14 +5,14 @@
|
||||
android:versionName="GIT-SNAPSHOT">
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="14"
|
||||
android:targetSdkVersion="19" />
|
||||
android:minSdkVersion="19"
|
||||
android:targetSdkVersion="21" />
|
||||
|
||||
<application
|
||||
android:icon="@drawable/icon"
|
||||
android:label="@string/app_name"
|
||||
android:allowBackup="true">
|
||||
<activity android:name=".EmergencyActivity">
|
||||
<activity android:name=".EmergencyActivity" android:theme="@style/MPTheme">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
|
@ -8,6 +8,7 @@
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="20dp"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center">
|
||||
|
||||
@ -16,55 +17,79 @@
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressView"
|
||||
android:layout_width="300dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="20dp"
|
||||
android:indeterminate="true" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/userNameField"
|
||||
android:layout_width="300dp"
|
||||
android:id="@+id/fullNameField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:nextFocusForward="@+id/masterPasswordField"
|
||||
android:inputType="text|textCapWords|textPersonName"
|
||||
android:hint="@string/userName_hint"
|
||||
android:hint="@string/fullName_hint"
|
||||
android:gravity="center"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="26sp" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/masterPasswordField"
|
||||
android:layout_width="300dp"
|
||||
<CheckBox
|
||||
android:id="@+id/rememberFullNameField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:nextFocusForward="@+id/rememberPasswordField"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@android:color/tertiary_text_dark"
|
||||
android:text="@string/remember" />
|
||||
|
||||
<EditText
|
||||
android:id="@id/masterPasswordField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:nextFocusForward="@+id/siteNameField"
|
||||
android:inputType="text|textPassword"
|
||||
android:hint="@string/masterPassword_hint"
|
||||
android:gravity="center"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/siteNameField"
|
||||
android:layout_width="300dp"
|
||||
<CheckBox
|
||||
android:id="@id/rememberPasswordField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@android:color/tertiary_text_dark"
|
||||
android:text="@string/forgetOnClose" />
|
||||
|
||||
<EditText
|
||||
android:id="@id/siteNameField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:nextFocusForward="@+id/sitePasswordField"
|
||||
android:inputType="text|textNoSuggestions|textUri"
|
||||
android:hint="@string/siteName_hint"
|
||||
android:gravity="center"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="26sp" />
|
||||
android:textSize="18sp" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="300dp"
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginBottom="30dp"
|
||||
android:src="@drawable/double_"
|
||||
android:contentDescription="@string/empty" />
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="20dp"
|
||||
android:indeterminate="true" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sitePasswordField"
|
||||
android:layout_width="300dp"
|
||||
android:id="@id/sitePasswordField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:nextFocusForward="@+id/siteTypeField"
|
||||
android:gravity="center"
|
||||
android:background="@android:color/transparent"
|
||||
android:textColor="#FFFFFF"
|
||||
@ -72,33 +97,78 @@
|
||||
android:text="LuxdZozvDuma4["
|
||||
android:onClick="copySitePassword" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/typeField"
|
||||
android:layout_width="300dp"
|
||||
<TextView
|
||||
android:id="@+id/sitePasswordTip"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:labelFor="@id/sitePasswordField"
|
||||
android:gravity="center"
|
||||
android:background="@android:color/transparent"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@android:color/tertiary_text_dark"
|
||||
android:text="@string/sitePassword_hint" />
|
||||
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/maskPasswordField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@android:color/tertiary_text_dark"
|
||||
android:text="@string/maskPassword" />
|
||||
|
||||
<Spinner
|
||||
android:id="@id/siteTypeField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:nextFocusForward="@+id/counterField"
|
||||
android:gravity="center" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/counterField"
|
||||
android:layout_width="300dp"
|
||||
android:id="@id/counterField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:nextFocusForward="@+id/siteVersionField"
|
||||
android:gravity="center"
|
||||
android:inputType="text|textNoSuggestions"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="26sp"
|
||||
android:textSize="18sp"
|
||||
android:text="1" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:labelFor="@id/counterField"
|
||||
android:gravity="center"
|
||||
android:background="@android:color/transparent"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@android:color/tertiary_text_dark"
|
||||
android:text="@string/siteCounter_hint" />
|
||||
|
||||
<Spinner
|
||||
android:id="@id/siteVersionField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:nextFocusForward="@id/rememberFullNameField"
|
||||
android:gravity="center" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:labelFor="@id/siteVersionField"
|
||||
android:gravity="center"
|
||||
android:background="@android:color/transparent"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@android:color/tertiary_text_dark"
|
||||
android:text="@string/siteVersion_hint" />
|
||||
|
||||
<View
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/rememberPasswordField"
|
||||
android:layout_width="300dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/remember" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
@ -6,4 +6,4 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/avatar0"
|
||||
android:drawablePadding="8dp"
|
||||
android:text="Maarten Billemont" />
|
||||
android:text="Robert Lee Mitchell" />
|
||||
|
@ -2,9 +2,14 @@
|
||||
<resources>
|
||||
<string name="app_name">Master Password</string>
|
||||
<string name="avatar">User Avatar</string>
|
||||
<string name="remember">Remember Password</string>
|
||||
<string name="siteName_hint">Site Name</string>
|
||||
<string name="userName_hint">Your Name</string>
|
||||
<string name="masterPassword_hint">Your Master Password</string>
|
||||
<string name="remember">Remember</string>
|
||||
<string name="forgetOnClose">Forget on close</string>
|
||||
<string name="maskPassword">Hide password</string>
|
||||
<string name="fullName_hint">Your full name</string>
|
||||
<string name="masterPassword_hint">Your master password</string>
|
||||
<string name="siteName_hint">eg. google.com</string>
|
||||
<string name="sitePassword_hint">Tap to copy</string>
|
||||
<string name="siteCounter_hint">Password #</string>
|
||||
<string name="siteVersion_hint">Algorithm</string>
|
||||
<string name="empty" />
|
||||
</resources>
|
||||
|
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="MPTheme" parent="android:Theme.Holo.Dialog.MinWidth">
|
||||
</style>
|
||||
</resources>
|
@ -4,10 +4,11 @@ import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.*;
|
||||
import android.content.ClipboardManager;
|
||||
import android.graphics.Paint;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.text.*;
|
||||
import android.text.method.PasswordTransformationMethod;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.*;
|
||||
@ -17,7 +18,9 @@ import com.google.common.base.Throwables;
|
||||
import com.google.common.util.concurrent.*;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import com.lyndir.lhunath.opal.system.util.ConversionUtils;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.*;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
||||
public class EmergencyActivity extends Activity {
|
||||
@ -44,8 +47,8 @@ public class EmergencyActivity extends Activity {
|
||||
@InjectView(R.id.progressView)
|
||||
ProgressBar progressView;
|
||||
|
||||
@InjectView(R.id.userNameField)
|
||||
EditText userNameField;
|
||||
@InjectView(R.id.fullNameField)
|
||||
EditText fullNameField;
|
||||
|
||||
@InjectView(R.id.masterPasswordField)
|
||||
EditText masterPasswordField;
|
||||
@ -53,23 +56,36 @@ public class EmergencyActivity extends Activity {
|
||||
@InjectView(R.id.siteNameField)
|
||||
EditText siteNameField;
|
||||
|
||||
@InjectView(R.id.typeField)
|
||||
Spinner typeField;
|
||||
@InjectView(R.id.siteTypeField)
|
||||
Spinner siteTypeField;
|
||||
|
||||
@InjectView(R.id.counterField)
|
||||
EditText counterField;
|
||||
|
||||
@InjectView(R.id.siteVersionField)
|
||||
Spinner siteVersionField;
|
||||
|
||||
@InjectView(R.id.sitePasswordField)
|
||||
TextView sitePasswordField;
|
||||
|
||||
@InjectView(R.id.sitePasswordTip)
|
||||
TextView sitePasswordTip;
|
||||
|
||||
@InjectView(R.id.rememberFullNameField)
|
||||
CheckBox rememberFullNameField;
|
||||
|
||||
@InjectView(R.id.rememberPasswordField)
|
||||
CheckBox rememberPasswordField;
|
||||
CheckBox forgetPasswordField;
|
||||
|
||||
@InjectView(R.id.maskPasswordField)
|
||||
CheckBox maskPasswordField;
|
||||
|
||||
private int hc_userName;
|
||||
private int hc_masterPassword;
|
||||
private String sitePassword;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate( savedInstanceState );
|
||||
Res.init( getResources() );
|
||||
|
||||
@ -77,14 +93,25 @@ public class EmergencyActivity extends Activity {
|
||||
setContentView( R.layout.activity_emergency );
|
||||
ButterKnife.inject( this );
|
||||
|
||||
userNameField.setOnFocusChangeListener( updateMasterKey );
|
||||
fullNameField.setOnFocusChangeListener( updateMasterKey );
|
||||
masterPasswordField.setOnFocusChangeListener( updateMasterKey );
|
||||
siteNameField.addTextChangedListener( updateSitePassword );
|
||||
typeField.setOnItemSelectedListener( updateSitePassword );
|
||||
siteTypeField.setOnItemSelectedListener( updateSitePassword );
|
||||
counterField.addTextChangedListener( updateSitePassword );
|
||||
siteVersionField.setOnItemSelectedListener( updateMasterKey );
|
||||
sitePasswordField.addTextChangedListener( new ValueChangedListener() {
|
||||
@Override
|
||||
void update() {
|
||||
boolean noPassword = TextUtils.isEmpty( sitePasswordField.getText() );
|
||||
sitePasswordTip.setVisibility( noPassword? View.INVISIBLE: View.VISIBLE );
|
||||
|
||||
userNameField.setTypeface( Res.exo_Thin );
|
||||
userNameField.setPaintFlags( userNameField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG );
|
||||
if (noPassword)
|
||||
sitePassword = null;
|
||||
}
|
||||
} );
|
||||
|
||||
fullNameField.setTypeface( Res.exo_Thin );
|
||||
fullNameField.setPaintFlags( fullNameField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG );
|
||||
masterPasswordField.setTypeface( Res.sourceCodePro_ExtraLight );
|
||||
masterPasswordField.setPaintFlags( masterPasswordField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG );
|
||||
siteNameField.setTypeface( Res.exo_Regular );
|
||||
@ -92,13 +119,33 @@ public class EmergencyActivity extends Activity {
|
||||
sitePasswordField.setTypeface( Res.sourceCodePro_Black );
|
||||
sitePasswordField.setPaintFlags( sitePasswordField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG );
|
||||
|
||||
typeField.setAdapter( new ArrayAdapter<>( this, R.layout.type_item, MPSiteType.forClass( MPSiteTypeClass.Generated ) ) );
|
||||
typeField.setSelection( MPSiteType.GeneratedLong.ordinal() );
|
||||
siteTypeField.setAdapter( new ArrayAdapter<>( this, R.layout.spinner_item, MPSiteType.forClass( MPSiteTypeClass.Generated ) ) );
|
||||
siteTypeField.setSelection( MPSiteType.GeneratedLong.ordinal() );
|
||||
|
||||
rememberPasswordField.setOnCheckedChangeListener( new CompoundButton.OnCheckedChangeListener() {
|
||||
siteVersionField.setAdapter( new ArrayAdapter<>( this, R.layout.spinner_item, MasterKey.Version.values() ) );
|
||||
siteVersionField.setSelection( MasterKey.Version.CURRENT.ordinal() );
|
||||
|
||||
rememberFullNameField.setOnCheckedChangeListener( new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) {
|
||||
getPreferences( MODE_PRIVATE ).edit().putBoolean( "rememberPassword", isChecked ).apply();
|
||||
getPreferences( MODE_PRIVATE ).edit().putBoolean( "rememberFullName", isChecked ).apply();
|
||||
if (isChecked)
|
||||
getPreferences( MODE_PRIVATE ).edit().putString( "fullName", fullNameField.getText().toString() ).apply();
|
||||
else
|
||||
getPreferences( MODE_PRIVATE ).edit().putString( "fullName", "" ).apply();
|
||||
}
|
||||
} );
|
||||
forgetPasswordField.setOnCheckedChangeListener( new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) {
|
||||
getPreferences( MODE_PRIVATE ).edit().putBoolean( "forgetPassword", isChecked ).apply();
|
||||
}
|
||||
} );
|
||||
maskPasswordField.setOnCheckedChangeListener( new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) {
|
||||
getPreferences( MODE_PRIVATE ).edit().putBoolean( "maskPassword", isChecked ).apply();
|
||||
sitePasswordField.setTransformationMethod( isChecked? new PasswordTransformationMethod(): null );
|
||||
}
|
||||
} );
|
||||
}
|
||||
@ -107,14 +154,21 @@ public class EmergencyActivity extends Activity {
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
userNameField.setText( getPreferences( MODE_PRIVATE ).getString( "userName", "" ) );
|
||||
rememberPasswordField.setSelected( isRememberPasswordEnabled() );
|
||||
fullNameField.setText( getPreferences( MODE_PRIVATE ).getString( "fullName", "" ) );
|
||||
rememberFullNameField.setChecked( isRememberFullNameEnabled() );
|
||||
forgetPasswordField.setChecked( isForgetPasswordEnabled() );
|
||||
maskPasswordField.setChecked( isMaskPasswordEnabled() );
|
||||
sitePasswordField.setTransformationMethod( isMaskPasswordEnabled()? new PasswordTransformationMethod(): null );
|
||||
|
||||
if (TextUtils.isEmpty( masterPasswordField.getText() ))
|
||||
masterPasswordField.requestFocus();
|
||||
else
|
||||
siteNameField.requestFocus();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
if (!isRememberPasswordEnabled()) {
|
||||
if (isForgetPasswordEnabled()) {
|
||||
synchronized (this) {
|
||||
hc_userName = hc_masterPassword = 0;
|
||||
if (masterKeyFuture != null) {
|
||||
@ -122,44 +176,64 @@ public class EmergencyActivity extends Activity {
|
||||
masterKeyFuture = null;
|
||||
}
|
||||
|
||||
masterPasswordField.setText( "" );
|
||||
}
|
||||
}
|
||||
|
||||
siteNameField.setText( "" );
|
||||
sitePasswordField.setText( "" );
|
||||
progressView.setVisibility( View.INVISIBLE );
|
||||
}
|
||||
}
|
||||
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
private boolean isRememberPasswordEnabled() {
|
||||
return getPreferences( MODE_PRIVATE ).getBoolean( "rememberPassword", false );
|
||||
private boolean isRememberFullNameEnabled() {
|
||||
return getPreferences( MODE_PRIVATE ).getBoolean( "rememberFullName", false );
|
||||
}
|
||||
|
||||
private boolean isForgetPasswordEnabled() {
|
||||
return getPreferences( MODE_PRIVATE ).getBoolean( "forgetPassword", false );
|
||||
}
|
||||
|
||||
private boolean isMaskPasswordEnabled() {
|
||||
return getPreferences( MODE_PRIVATE ).getBoolean( "maskPassword", false );
|
||||
}
|
||||
|
||||
private synchronized void updateMasterKey() {
|
||||
final String userName = userNameField.getText().toString();
|
||||
final String masterPassword = masterPasswordField.getText().toString();
|
||||
if (userName.hashCode() == hc_userName && masterPassword.hashCode() == hc_masterPassword)
|
||||
final String fullName = fullNameField.getText().toString();
|
||||
final char[] masterPassword = masterPasswordField.getText().toString().toCharArray();
|
||||
final MasterKey.Version version = (MasterKey.Version) siteVersionField.getSelectedItem();
|
||||
try {
|
||||
if (fullName.hashCode() == hc_userName && Arrays.hashCode( masterPassword ) == hc_masterPassword &&
|
||||
masterKeyFuture != null && masterKeyFuture.get().getAlgorithm() == version)
|
||||
return;
|
||||
hc_userName = userName.hashCode();
|
||||
hc_masterPassword = masterPassword.hashCode();
|
||||
}
|
||||
catch (InterruptedException | ExecutionException e) {
|
||||
return;
|
||||
}
|
||||
hc_userName = fullName.hashCode();
|
||||
hc_masterPassword = Arrays.hashCode( masterPassword );
|
||||
|
||||
getPreferences( MODE_PRIVATE ).edit().putString( "userName", userName ).apply();
|
||||
if (isRememberFullNameEnabled())
|
||||
getPreferences( MODE_PRIVATE ).edit().putString( "fullName", fullName ).apply();
|
||||
|
||||
if (masterKeyFuture != null)
|
||||
masterKeyFuture.cancel( true );
|
||||
|
||||
if (userName.isEmpty() || masterPassword.isEmpty()) {
|
||||
if (fullName.isEmpty() || masterPassword.length == 0) {
|
||||
sitePasswordField.setText( "" );
|
||||
progressView.setVisibility( View.INVISIBLE );
|
||||
return;
|
||||
}
|
||||
|
||||
sitePasswordField.setText( "" );
|
||||
progressView.setVisibility( View.VISIBLE );
|
||||
(masterKeyFuture = executor.submit( new Callable<MasterKey>() {
|
||||
@Override
|
||||
public MasterKey call()
|
||||
throws Exception {
|
||||
try {
|
||||
return MasterKey.create( userName, masterPassword );
|
||||
return MasterKey.create( version, fullName, masterPassword );
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
sitePasswordField.setText( "" );
|
||||
@ -183,21 +257,25 @@ public class EmergencyActivity extends Activity {
|
||||
|
||||
private void updateSitePassword() {
|
||||
final String siteName = siteNameField.getText().toString();
|
||||
final MPSiteType type = (MPSiteType) typeField.getSelectedItem();
|
||||
final MPSiteType type = (MPSiteType) siteTypeField.getSelectedItem();
|
||||
final int counter = ConversionUtils.toIntegerNN( counterField.getText() );
|
||||
|
||||
if (masterKeyFuture == null || siteName.isEmpty() || type == null) {
|
||||
sitePasswordField.setText( "" );
|
||||
progressView.setVisibility( View.INVISIBLE );
|
||||
|
||||
if (masterKeyFuture == null)
|
||||
updateMasterKey();
|
||||
return;
|
||||
}
|
||||
|
||||
sitePasswordField.setText( "" );
|
||||
progressView.setVisibility( View.VISIBLE );
|
||||
executor.submit( new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
final String sitePassword = masterKeyFuture.get().encode( siteName, type, counter, MPSiteVariant.Password, null );
|
||||
sitePassword = masterKeyFuture.get().encode( siteName, type, counter, MPSiteVariant.Password, null );
|
||||
|
||||
runOnUiThread( new Runnable() {
|
||||
@Override
|
||||
@ -228,8 +306,7 @@ public class EmergencyActivity extends Activity {
|
||||
}
|
||||
|
||||
public void copySitePassword(View view) {
|
||||
String sitePassword = sitePasswordField.getText().toString();
|
||||
if (sitePassword.isEmpty())
|
||||
if (TextUtils.isEmpty( sitePassword ))
|
||||
return;
|
||||
|
||||
ClipDescription description = new ClipDescription( strf( "Password for %s", siteNameField.getText() ),
|
||||
|
@ -45,7 +45,8 @@ public class CLI {
|
||||
throws IOException {
|
||||
|
||||
// Read information from the environment.
|
||||
String siteName = null, masterPassword, context = null;
|
||||
char[] masterPassword = null;
|
||||
String siteName = null, context = null;
|
||||
String userName = System.getenv( ENV_USERNAME );
|
||||
String siteTypeName = ifNotNullElse( System.getenv( ENV_SITETYPE ), "" );
|
||||
MPSiteType siteType = siteTypeName.isEmpty()? MPSiteType.GeneratedLong: MPSiteType.forOption( siteTypeName );
|
||||
@ -174,11 +175,11 @@ public class CLI {
|
||||
}
|
||||
|
||||
if (console != null)
|
||||
masterPassword = new String( console.readPassword( "%s's master password: ", userName ) );
|
||||
masterPassword = console.readPassword( "%s's master password: ", userName );
|
||||
|
||||
else {
|
||||
System.err.format( "%s's master password: ", userName );
|
||||
masterPassword = lineReader.readLine();
|
||||
masterPassword = lineReader.readLine().toCharArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,10 @@
|
||||
/**
|
||||
*
|
||||
* @author lhunath, 15-02-04
|
||||
*/
|
||||
|
||||
|
||||
@ParametersAreNonnullByDefault
|
||||
package com.lyndir.masterpassword;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
@ -1,7 +1,7 @@
|
||||
package com.lyndir.masterpassword.gui;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.lyndir.masterpassword.util.Components;
|
||||
import com.lyndir.masterpassword.gui.util.Components;
|
||||
import java.awt.*;
|
||||
import javax.swing.*;
|
||||
|
||||
@ -32,7 +32,7 @@ public abstract class AuthenticationPanel extends Components.GradientPanel {
|
||||
}
|
||||
|
||||
protected void updateUser(boolean repack) {
|
||||
unlockFrame.setUser( getSelectedUser() );
|
||||
unlockFrame.updateUser( getSelectedUser() );
|
||||
validate();
|
||||
|
||||
if (repack)
|
||||
@ -41,6 +41,8 @@ public abstract class AuthenticationPanel extends Components.GradientPanel {
|
||||
|
||||
protected abstract User getSelectedUser();
|
||||
|
||||
public abstract char[] getMasterPassword();
|
||||
|
||||
public Component getFocusComponent() {
|
||||
return null;
|
||||
}
|
||||
|
@ -21,6 +21,8 @@ import com.google.common.base.Charsets;
|
||||
import com.google.common.io.*;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import com.lyndir.lhunath.opal.system.util.TypeUtils;
|
||||
import com.lyndir.masterpassword.MasterKey;
|
||||
import com.lyndir.masterpassword.model.IncorrectMasterPasswordException;
|
||||
import java.io.*;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
@ -93,19 +95,9 @@ public class GUI implements UnlockFrame.SignInCallback {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean signedIn(final User user) {
|
||||
if (!user.isKeyAvailable())
|
||||
return false;
|
||||
try {
|
||||
user.getKey();
|
||||
public void signedIn(final User user) {
|
||||
passwordFrame = newPasswordFrame( user );
|
||||
|
||||
open();
|
||||
return true;
|
||||
} catch (MasterKeyException e) {
|
||||
JOptionPane.showMessageDialog( null, e.getLocalizedMessage(), "Sign In Failed", JOptionPane.ERROR_MESSAGE );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected PasswordFrame newPasswordFrame(final User user) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.lyndir.masterpassword.gui;
|
||||
|
||||
import com.lyndir.masterpassword.util.Components;
|
||||
import com.lyndir.masterpassword.gui.util.Components;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
@ -55,7 +55,12 @@ public class IncognitoAuthenticationPanel extends AuthenticationPanel implements
|
||||
|
||||
@Override
|
||||
protected User getSelectedUser() {
|
||||
return new IncognitoUser( fullNameField.getText(), new String( masterPasswordField.getPassword() ) );
|
||||
return new IncognitoUser( fullNameField.getText() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] getMasterPassword() {
|
||||
return masterPasswordField.getPassword();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,7 +1,8 @@
|
||||
package com.lyndir.masterpassword.gui;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.lyndir.masterpassword.MasterKey;
|
||||
import com.lyndir.masterpassword.model.IncorrectMasterPasswordException;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
||||
/**
|
||||
@ -10,31 +11,26 @@ import com.lyndir.masterpassword.MasterKey;
|
||||
public class IncognitoUser extends User {
|
||||
|
||||
private final String fullName;
|
||||
private final String masterPassword;
|
||||
private MasterKey.Version algorithmVersion;
|
||||
private char[] masterPassword;
|
||||
|
||||
public IncognitoUser(final String fullName, final String masterPassword) {
|
||||
public IncognitoUser(final String fullName) {
|
||||
this.fullName = fullName;
|
||||
this.masterPassword = masterPassword;
|
||||
}
|
||||
|
||||
public String getFullName() {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected String getMasterPassword() {
|
||||
protected char[] getMasterPassword() {
|
||||
return masterPassword;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MasterKey.Version getAlgorithmVersion() {
|
||||
return algorithmVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlgorithmVersion(final MasterKey.Version algorithmVersion) {
|
||||
this.algorithmVersion = algorithmVersion;
|
||||
public void authenticate(final char[] masterPassword)
|
||||
throws IncorrectMasterPasswordException {
|
||||
this.masterPassword = masterPassword;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,11 +0,0 @@
|
||||
package com.lyndir.masterpassword.gui;
|
||||
|
||||
/**
|
||||
* @author lhunath, 14-12-17
|
||||
*/
|
||||
public class MasterKeyException extends Exception {
|
||||
|
||||
public MasterKeyException(final String message) {
|
||||
super( message );
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ import com.google.common.collect.*;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import com.lyndir.masterpassword.model.MPUser;
|
||||
import com.lyndir.masterpassword.model.MPUserFileManager;
|
||||
import com.lyndir.masterpassword.util.Components;
|
||||
import com.lyndir.masterpassword.gui.util.Components;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import javax.annotation.Nullable;
|
||||
@ -90,11 +90,12 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
|
||||
if (selectedIndex < 0)
|
||||
return null;
|
||||
|
||||
ModelUser selectedUser = userField.getModel().getElementAt( selectedIndex );
|
||||
if (selectedUser != null)
|
||||
selectedUser.setMasterPassword( new String( masterPasswordField.getPassword() ) );
|
||||
return userField.getModel().getElementAt( selectedIndex );
|
||||
}
|
||||
|
||||
return selectedUser;
|
||||
@Override
|
||||
public char[] getMasterPassword() {
|
||||
return masterPasswordField.getPassword();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,14 +1,10 @@
|
||||
package com.lyndir.masterpassword.gui;
|
||||
|
||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.*;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.lyndir.lhunath.opal.system.util.ObjectUtils;
|
||||
import com.lyndir.masterpassword.MasterKey;
|
||||
import com.lyndir.masterpassword.model.*;
|
||||
import javax.annotation.Nullable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import java.util.Arrays;
|
||||
import javax.annotation.*;
|
||||
|
||||
|
||||
/**
|
||||
@ -17,7 +13,9 @@ import org.jetbrains.annotations.NotNull;
|
||||
public class ModelUser extends User {
|
||||
|
||||
private final MPUser model;
|
||||
private String masterPassword;
|
||||
|
||||
@Nullable
|
||||
private char[] masterPassword;
|
||||
|
||||
public ModelUser(MPUser model) {
|
||||
this.model = model;
|
||||
@ -32,22 +30,12 @@ public class ModelUser extends User {
|
||||
return model.getFullName();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected String getMasterPassword() {
|
||||
protected char[] getMasterPassword() {
|
||||
return masterPassword;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MasterKey.Version getAlgorithmVersion() {
|
||||
return model.getAlgorithmVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlgorithmVersion(final MasterKey.Version algorithmVersion) {
|
||||
model.setAlgorithmVersion( algorithmVersion );
|
||||
MPUserFileManager.get().save();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvatar() {
|
||||
return model.getAvatar();
|
||||
@ -58,39 +46,29 @@ public class ModelUser extends User {
|
||||
MPUserFileManager.get().save();
|
||||
}
|
||||
|
||||
public void setMasterPassword(final String masterPassword) {
|
||||
public void authenticate(final char[] masterPassword)
|
||||
throws IncorrectMasterPasswordException {
|
||||
model.authenticate( masterPassword );
|
||||
this.masterPassword = masterPassword;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public MasterKey getKey() throws MasterKeyException {
|
||||
MasterKey key = super.getKey();
|
||||
if (!model.hasKeyID()) {
|
||||
model.setKeyID( key.getKeyID() );
|
||||
MPUserFileManager.get().save();
|
||||
} else if (!model.hasKeyID( key.getKeyID() )) {
|
||||
reset();
|
||||
throw new MasterKeyException( strf( "Incorrect master password for user: %s", getFullName() ) );
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
super.reset();
|
||||
|
||||
if (masterPassword != null) {
|
||||
Arrays.fill( masterPassword, (char) 0 );
|
||||
masterPassword = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Site> findSitesByName(final String query) {
|
||||
return FluentIterable.from( model.findSitesByName( query ) ).transform( new Function<MPSiteResult, Site>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public Site apply(final MPSiteResult result) {
|
||||
return new ModelSite( result );
|
||||
public Site apply(@Nullable final MPSiteResult result) {
|
||||
return new ModelSite( Preconditions.checkNotNull( result ) );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
@ -5,12 +5,13 @@ import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.util.concurrent.*;
|
||||
import com.lyndir.masterpassword.*;
|
||||
import com.lyndir.masterpassword.util.Components;
|
||||
import com.lyndir.masterpassword.gui.util.Components;
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.awt.event.*;
|
||||
import java.util.concurrent.Callable;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.*;
|
||||
|
||||
@ -49,7 +50,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
||||
sitePanel.setOpaque( true );
|
||||
sitePanel.setBackground( Res.colors().controlBg() );
|
||||
sitePanel.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) );
|
||||
add( Components.bordered( sitePanel, BorderFactory.createRaisedBevelBorder(), Res.colors().frameBg() ), BorderLayout.CENTER );
|
||||
add( Components.borderPanel( sitePanel, BorderFactory.createRaisedBevelBorder(), Res.colors().frameBg() ), BorderLayout.CENTER );
|
||||
|
||||
// User
|
||||
sitePanel.add( Components.label( strf( "Generating passwords for: %s", user.getFullName() ), JLabel.CENTER ) );
|
||||
@ -66,7 +67,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
Futures.addCallback( updatePassword(), new FutureCallback<String>() {
|
||||
@Override
|
||||
public void onSuccess(final String sitePassword) {
|
||||
public void onSuccess(@Nullable final String sitePassword) {
|
||||
StringSelection clipboardContents = new StringSelection( sitePassword );
|
||||
Toolkit.getDefaultToolkit().getSystemClipboard().setContents( clipboardContents, null );
|
||||
|
||||
@ -119,7 +120,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
||||
|
||||
siteVersionField.setFont( Res.valueFont().deriveFont( 12f ) );
|
||||
siteVersionField.setAlignmentX( RIGHT_ALIGNMENT );
|
||||
siteVersionField.setSelectedItem( user.getAlgorithmVersion() );
|
||||
siteVersionField.setSelectedItem( MasterKey.Version.CURRENT );
|
||||
siteVersionField.addItemListener( new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(final ItemEvent e) {
|
||||
@ -165,7 +166,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
||||
passwordContainer.setOpaque( true );
|
||||
passwordContainer.setBackground( Color.white );
|
||||
passwordContainer.setBorder( BorderFactory.createEmptyBorder( 8, 8, 8, 8 ) );
|
||||
add( Components.bordered( passwordContainer, BorderFactory.createLoweredSoftBevelBorder(), Res.colors().frameBg() ),
|
||||
add( Components.borderPanel( passwordContainer, BorderFactory.createLoweredSoftBevelBorder(), Res.colors().frameBg() ),
|
||||
BorderLayout.SOUTH );
|
||||
|
||||
pack();
|
||||
@ -197,7 +198,8 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
||||
final MasterKey.Version siteVersion = siteVersionField.getItemAt( siteVersionField.getSelectedIndex() );
|
||||
final int siteCounter = (Integer) siteCounterField.getValue();
|
||||
final Site site = currentSite != null && currentSite.getSiteName().equals( siteNameQuery )? currentSite
|
||||
: Iterables.getFirst( user.findSitesByName( siteNameQuery ), new IncognitoSite( siteNameQuery, siteType, siteCounter, user.getAlgorithmVersion() ) );
|
||||
: Iterables.getFirst( user.findSitesByName( siteNameQuery ),
|
||||
new IncognitoSite( siteNameQuery, siteType, siteCounter, siteVersion ) );
|
||||
assert site != null;
|
||||
if (site == currentSite) {
|
||||
site.setSiteType( siteType );
|
||||
@ -215,7 +217,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
||||
} );
|
||||
Futures.addCallback( passwordFuture, new FutureCallback<String>() {
|
||||
@Override
|
||||
public void onSuccess(final String sitePassword) {
|
||||
public void onSuccess(@Nullable final String sitePassword) {
|
||||
SwingUtilities.invokeLater( new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
@ -2,7 +2,8 @@ package com.lyndir.masterpassword.gui;
|
||||
|
||||
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
|
||||
|
||||
import com.lyndir.masterpassword.util.Components;
|
||||
import com.lyndir.masterpassword.gui.util.Components;
|
||||
import com.lyndir.masterpassword.model.IncorrectMasterPasswordException;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import javax.swing.*;
|
||||
@ -34,7 +35,7 @@ public class UnlockFrame extends JFrame {
|
||||
authenticationContainer.setOpaque( true );
|
||||
authenticationContainer.setBackground( Res.colors().controlBg() );
|
||||
authenticationContainer.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) );
|
||||
add( Components.bordered( authenticationContainer, BorderFactory.createRaisedBevelBorder(), Res.colors().frameBg() ) );
|
||||
add( Components.borderPanel( authenticationContainer, BorderFactory.createRaisedBevelBorder(), Res.colors().frameBg() ) );
|
||||
|
||||
// Sign In
|
||||
JPanel signInBox = Components.boxLayout( BoxLayout.LINE_AXIS, Box.createGlue(), signInButton = Components.button( "Sign In" ),
|
||||
@ -108,13 +109,13 @@ public class UnlockFrame extends JFrame {
|
||||
} );
|
||||
}
|
||||
|
||||
void setUser(User user) {
|
||||
void updateUser(User user) {
|
||||
this.user = user;
|
||||
checkSignIn();
|
||||
}
|
||||
|
||||
boolean checkSignIn() {
|
||||
boolean enabled = user != null && !user.getFullName().isEmpty() && user.isKeyAvailable();
|
||||
boolean enabled = user != null && !user.getFullName().isEmpty() && authenticationPanel.getMasterPassword().length > 0;
|
||||
signInButton.setEnabled( enabled );
|
||||
|
||||
return enabled;
|
||||
@ -133,16 +134,22 @@ public class UnlockFrame extends JFrame {
|
||||
Res.execute( this, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final boolean success = signInCallback.signedIn( user );
|
||||
try {
|
||||
user.authenticate( authenticationPanel.getMasterPassword() );
|
||||
|
||||
SwingUtilities.invokeLater( new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (success) {
|
||||
signInCallback.signedIn( user );
|
||||
dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
} );
|
||||
}
|
||||
catch (final IncorrectMasterPasswordException e) {
|
||||
SwingUtilities.invokeLater( new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
JOptionPane.showMessageDialog( null, e.getLocalizedMessage(), "Sign In Failed", JOptionPane.ERROR_MESSAGE );
|
||||
authenticationPanel.reset();
|
||||
signInButton.setText( "Sign In" );
|
||||
for (JComponent signInComponent : signInComponents)
|
||||
@ -151,11 +158,13 @@ public class UnlockFrame extends JFrame {
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
interface SignInCallback {
|
||||
|
||||
boolean signedIn(User user);
|
||||
void signedIn(User user);
|
||||
}
|
||||
}
|
||||
|
@ -4,9 +4,11 @@ import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.lyndir.masterpassword.MasterKey;
|
||||
import com.lyndir.masterpassword.model.IncorrectMasterPasswordException;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Objects;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
||||
/**
|
||||
@ -19,33 +21,23 @@ public abstract class User {
|
||||
|
||||
public abstract String getFullName();
|
||||
|
||||
protected abstract String getMasterPassword();
|
||||
@Nullable
|
||||
protected abstract char[] getMasterPassword();
|
||||
|
||||
public abstract MasterKey.Version getAlgorithmVersion();
|
||||
|
||||
public abstract void setAlgorithmVersion(final MasterKey.Version algorithmVersion);
|
||||
public abstract void authenticate(final char[] masterPassword)
|
||||
throws IncorrectMasterPasswordException;
|
||||
|
||||
public int getAvatar() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public boolean isKeyAvailable() {
|
||||
String masterPassword = getMasterPassword();
|
||||
return masterPassword != null && !masterPassword.isEmpty();
|
||||
return getMasterPassword() != null;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public MasterKey getKey() throws MasterKeyException {
|
||||
return getKey( getAlgorithmVersion() );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public MasterKey getKey(MasterKey.Version algorithmVersion) throws MasterKeyException {
|
||||
String masterPassword = getMasterPassword();
|
||||
if (masterPassword == null || masterPassword.isEmpty()) {
|
||||
reset();
|
||||
throw new MasterKeyException( strf( "Master password unknown for user: %s", getFullName() ) );
|
||||
}
|
||||
public MasterKey getKey(MasterKey.Version algorithmVersion) {
|
||||
char[] masterPassword = getMasterPassword();
|
||||
|
||||
MasterKey key = keyByVersion.get( algorithmVersion );
|
||||
if (key == null)
|
||||
|
@ -0,0 +1,9 @@
|
||||
/**
|
||||
*
|
||||
* @author lhunath, 15-02-04
|
||||
*/
|
||||
|
||||
@ParametersAreNonnullByDefault
|
||||
package com.lyndir.masterpassword.gui;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
@ -1,8 +1,8 @@
|
||||
package com.lyndir.masterpassword.util;
|
||||
package com.lyndir.masterpassword.gui.util;
|
||||
|
||||
import com.lyndir.masterpassword.gui.ModelUser;
|
||||
import com.lyndir.masterpassword.gui.Res;
|
||||
import java.awt.*;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.border.CompoundBorder;
|
||||
@ -23,11 +23,11 @@ public abstract class Components {
|
||||
return container;
|
||||
}
|
||||
|
||||
public static GradientPanel bordered(final JComponent component, final Border border) {
|
||||
return bordered( component, border, null );
|
||||
public static GradientPanel borderPanel(final JComponent component, @Nullable final Border border) {
|
||||
return borderPanel( component, border, null );
|
||||
}
|
||||
|
||||
public static GradientPanel bordered(final JComponent component, final Border border, Color background) {
|
||||
public static GradientPanel borderPanel(final JComponent component, @Nullable final Border border, @Nullable Color background) {
|
||||
GradientPanel box = boxLayout( BoxLayout.LINE_AXIS, component );
|
||||
|
||||
if (border != null)
|
||||
@ -39,7 +39,7 @@ public abstract class Components {
|
||||
return box;
|
||||
}
|
||||
|
||||
public static GradientPanel gradientPanel(final LayoutManager layout, final Color color) {
|
||||
public static GradientPanel gradientPanel(@Nullable final LayoutManager layout, @Nullable final Color color) {
|
||||
return new GradientPanel( layout, color ) {
|
||||
{
|
||||
setOpaque( color != null );
|
||||
@ -176,20 +176,24 @@ public abstract class Components {
|
||||
|
||||
public static class GradientPanel extends JPanel {
|
||||
|
||||
@Nullable
|
||||
private Color gradientColor;
|
||||
|
||||
@Nullable
|
||||
private GradientPaint paint;
|
||||
|
||||
protected GradientPanel(final LayoutManager layout, final Color gradientColor) {
|
||||
protected GradientPanel(@Nullable final LayoutManager layout, @Nullable final Color gradientColor) {
|
||||
super( layout );
|
||||
this.gradientColor = gradientColor;
|
||||
setBackground( null );
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Color getGradientColor() {
|
||||
return gradientColor;
|
||||
}
|
||||
|
||||
public void setGradientColor(final Color gradientColor) {
|
||||
public void setGradientColor(@Nullable final Color gradientColor) {
|
||||
this.gradientColor = gradientColor;
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
/**
|
||||
*
|
||||
* @author lhunath, 15-02-04
|
||||
*/
|
||||
|
||||
@ParametersAreNonnullByDefault
|
||||
package com.lyndir.masterpassword.gui.util;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
@ -0,0 +1,19 @@
|
||||
package com.lyndir.masterpassword.model;
|
||||
|
||||
/**
|
||||
* @author lhunath, 14-12-17
|
||||
*/
|
||||
public class IncorrectMasterPasswordException extends Exception {
|
||||
|
||||
private final MPUser user;
|
||||
|
||||
public IncorrectMasterPasswordException(final MPUser user) {
|
||||
super( "Incorrect master password for user: " + user.getFullName() );
|
||||
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public MPUser getUser() {
|
||||
return user;
|
||||
}
|
||||
}
|
@ -39,8 +39,9 @@ public class MPSite {
|
||||
this.siteCounter = siteCounter;
|
||||
}
|
||||
|
||||
protected MPSite(final MPUser user, final MasterKey.Version algorithmVersion, final Instant lastUsed, final String siteName, final MPSiteType siteType, final int siteCounter,
|
||||
final int uses, final String loginName, final String importContent) {
|
||||
protected MPSite(final MPUser user, final MasterKey.Version algorithmVersion, final Instant lastUsed, final String siteName,
|
||||
final MPSiteType siteType, final int siteCounter, final int uses, @Nullable final String loginName,
|
||||
@Nullable final String importContent) {
|
||||
this.user = user;
|
||||
this.algorithmVersion = algorithmVersion;
|
||||
this.lastUsed = lastUsed;
|
||||
|
@ -4,6 +4,7 @@ import static com.lyndir.lhunath.opal.system.util.ObjectUtils.ifNotNullElse;
|
||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.lyndir.lhunath.opal.system.CodeUtils;
|
||||
import com.lyndir.masterpassword.MasterKey;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
@ -63,8 +64,8 @@ public class MPSiteMarshaller {
|
||||
header.append( "# Full Name: " ).append( user.getFullName() ).append( '\n' );
|
||||
header.append( "# Avatar: " ).append( user.getAvatar() ).append( '\n' );
|
||||
header.append( "# Key ID: " ).append( user.exportKeyID() ).append( '\n' );
|
||||
header.append( "# Version: " ).append( user.getAlgorithmVersion().toBundleVersion() ).append( '\n' );
|
||||
header.append( "# Algorithm: " ).append( user.getAlgorithmVersion().toInt() ).append( '\n' );
|
||||
header.append( "# Version: " ).append( MasterKey.Version.CURRENT.toBundleVersion() ).append( '\n' );
|
||||
header.append( "# Algorithm: " ).append( MasterKey.Version.CURRENT.toInt() ).append( '\n' );
|
||||
header.append( "# Default Type: " ).append( user.getDefaultType().getType() ).append( '\n' );
|
||||
header.append( "# Passwords: " ).append( contentMode.name() ).append( '\n' );
|
||||
header.append( "##\n" );
|
||||
@ -82,7 +83,7 @@ public class MPSiteMarshaller {
|
||||
site.getUses(), // uses
|
||||
strf( "%d:%d:%d", //
|
||||
site.getSiteType().getType(), // type
|
||||
site.getAlgorithmVersion(), // algorithm
|
||||
site.getAlgorithmVersion().toInt(), // algorithm
|
||||
site.getSiteCounter() ), // counter
|
||||
ifNotNullElse( site.getLoginName(), "" ), // loginName
|
||||
site.getSiteName(), // siteName
|
||||
|
@ -8,6 +8,8 @@ import com.lyndir.lhunath.opal.system.CodeUtils;
|
||||
import com.lyndir.masterpassword.MPSiteType;
|
||||
import com.lyndir.masterpassword.MasterKey;
|
||||
import java.util.*;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.*;
|
||||
|
||||
|
||||
@ -19,6 +21,7 @@ public class MPUser implements Comparable<MPUser> {
|
||||
private final String fullName;
|
||||
private final Collection<MPSite> sites = Sets.newHashSet();
|
||||
|
||||
@Nullable
|
||||
private byte[] keyID;
|
||||
private MasterKey.Version algorithmVersion;
|
||||
private int avatar;
|
||||
@ -29,12 +32,12 @@ public class MPUser implements Comparable<MPUser> {
|
||||
this( fullName, null );
|
||||
}
|
||||
|
||||
public MPUser(final String fullName, final byte[] keyID) {
|
||||
public MPUser(final String fullName, @Nullable final byte[] keyID) {
|
||||
this( fullName, keyID, MasterKey.Version.CURRENT, 0, MPSiteType.GeneratedLong, new DateTime() );
|
||||
}
|
||||
|
||||
public MPUser(final String fullName, final byte[] keyID, final MasterKey.Version algorithmVersion, final int avatar, final MPSiteType defaultType,
|
||||
final ReadableInstant lastUsed) {
|
||||
public MPUser(final String fullName, @Nullable final byte[] keyID, final MasterKey.Version algorithmVersion, final int avatar,
|
||||
final MPSiteType defaultType, final ReadableInstant lastUsed) {
|
||||
this.fullName = fullName;
|
||||
this.keyID = keyID;
|
||||
this.algorithmVersion = algorithmVersion;
|
||||
@ -64,24 +67,31 @@ public class MPUser implements Comparable<MPUser> {
|
||||
return keyID != null;
|
||||
}
|
||||
|
||||
public boolean hasKeyID(final byte[] keyID) {
|
||||
return Arrays.equals( this.keyID, keyID );
|
||||
}
|
||||
|
||||
public String exportKeyID() {
|
||||
return CodeUtils.encodeHex( keyID );
|
||||
}
|
||||
|
||||
public void setKeyID(final byte[] keyID) {
|
||||
this.keyID = keyID;
|
||||
}
|
||||
/**
|
||||
* Performs an authentication attempt against the keyID for this user.
|
||||
*
|
||||
* Note: If this user doesn't have a keyID set yet, authentication will always succeed and the key ID will be set as a result.
|
||||
*
|
||||
* @param masterPassword The password to authenticate with.
|
||||
*
|
||||
* @return The master key for the user if authentication was successful.
|
||||
*
|
||||
* @throws IncorrectMasterPasswordException If authentication fails due to the given master password not matching the user's keyID.
|
||||
*/
|
||||
@Nonnull
|
||||
public MasterKey authenticate(final char[] masterPassword)
|
||||
throws IncorrectMasterPasswordException {
|
||||
MasterKey masterKey = MasterKey.create( algorithmVersion, getFullName(), masterPassword );
|
||||
if (keyID == null)
|
||||
keyID = masterKey.getKeyID();
|
||||
else if (!Arrays.equals( masterKey.getKeyID(), keyID ))
|
||||
throw new IncorrectMasterPasswordException( this );
|
||||
|
||||
public MasterKey.Version getAlgorithmVersion() {
|
||||
return algorithmVersion;
|
||||
}
|
||||
|
||||
public void setAlgorithmVersion(final MasterKey.Version algorithmVersion) {
|
||||
this.algorithmVersion = algorithmVersion;
|
||||
return masterKey;
|
||||
}
|
||||
|
||||
public int getAvatar() {
|
||||
|
@ -0,0 +1,9 @@
|
||||
/**
|
||||
*
|
||||
* @author lhunath, 15-02-04
|
||||
*/
|
||||
|
||||
@ParametersAreNonnullByDefault
|
||||
package com.lyndir.masterpassword.model;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
Loading…
Reference in New Issue
Block a user