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