2
0

Big overhaul for proper site-specific algorithm support and big Android UI update.

This commit is contained in:
Maarten Billemont 2015-02-05 00:56:24 -05:00
parent a6ab9b9194
commit 145008406d
32 changed files with 485 additions and 267 deletions

View File

@ -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 );

View File

@ -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,

View File

@ -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 );
}
}
}

View File

@ -0,0 +1,9 @@
/**
*
* @author lhunath, 15-02-04
*/
@ParametersAreNonnullByDefault package com.lyndir.masterpassword;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@ -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() {

View File

@ -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" />

View File

@ -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>

View File

@ -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" />

View File

@ -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>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="MPTheme" parent="android:Theme.Holo.Dialog.MinWidth">
</style>
</resources>

View File

@ -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() ),

View File

@ -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();
}
}

View File

@ -0,0 +1,10 @@
/**
*
* @author lhunath, 15-02-04
*/
@ParametersAreNonnullByDefault
package com.lyndir.masterpassword;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@ -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;
}

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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 );
}
}

View File

@ -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

View File

@ -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 ) );
}
} );
}

View File

@ -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() {

View File

@ -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);
}
}

View File

@ -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)

View File

@ -0,0 +1,9 @@
/**
*
* @author lhunath, 15-02-04
*/
@ParametersAreNonnullByDefault
package com.lyndir.masterpassword.gui;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@ -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;
}

View File

@ -0,0 +1,9 @@
/**
*
* @author lhunath, 15-02-04
*/
@ParametersAreNonnullByDefault
package com.lyndir.masterpassword.gui.util;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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

View File

@ -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() {

View File

@ -0,0 +1,9 @@
/**
*
* @author lhunath, 15-02-04
*/
@ParametersAreNonnullByDefault
package com.lyndir.masterpassword.model;
import javax.annotation.ParametersAreNonnullByDefault;