Improved Master Password algorithm API & GUI improvements.
[IMPROVED] Read the master password using Console, not stdin. [IMPROVED] Clear the site password when the dialog closes. [IMPROVED] Make the site password selectable.
This commit is contained in:
parent
2adb74c971
commit
9d7799c814
@ -80,11 +80,9 @@ public enum MPElementType {
|
|||||||
*/
|
*/
|
||||||
public static MPElementType forName(final String name) {
|
public static MPElementType forName(final String name) {
|
||||||
|
|
||||||
for (final MPElementType type : values()) {
|
for (final MPElementType type : values())
|
||||||
if (type.getName().equalsIgnoreCase( name ) || type.getShortName().equalsIgnoreCase( name )) {
|
if (type.getName().equalsIgnoreCase( name ) || type.getShortName().equalsIgnoreCase( name ))
|
||||||
return type;
|
return type;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw logger.bug( "Element type not known: %s", name );
|
throw logger.bug( "Element type not known: %s", name );
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,8 @@ package com.lyndir.masterpassword;
|
|||||||
|
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.google.common.io.CharSource;
|
||||||
|
import com.google.common.io.CharStreams;
|
||||||
import com.google.common.primitives.Bytes;
|
import com.google.common.primitives.Bytes;
|
||||||
import com.lambdaworks.crypto.SCrypt;
|
import com.lambdaworks.crypto.SCrypt;
|
||||||
import com.lyndir.lhunath.opal.crypto.CryptUtils;
|
import com.lyndir.lhunath.opal.crypto.CryptUtils;
|
||||||
@ -11,18 +13,17 @@ import java.nio.ByteBuffer;
|
|||||||
import java.nio.ByteOrder;
|
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.xml.stream.events.Characters;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of the Master Password algorithm.
|
* @author lhunath, 2014-08-30
|
||||||
*
|
|
||||||
* <i>07 04, 2012</i>
|
|
||||||
*
|
|
||||||
* @author lhunath
|
|
||||||
*/
|
*/
|
||||||
public abstract class MasterPassword {
|
public class MasterKey {
|
||||||
|
|
||||||
static final Logger logger = Logger.get( MasterPassword.class );
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
|
private static final Logger logger = Logger.get( MasterKey.class );
|
||||||
private static final int MP_N = 32768;
|
private static final int MP_N = 32768;
|
||||||
private static final int MP_r = 8;
|
private static final int MP_r = 8;
|
||||||
private static final int MP_p = 2;
|
private static final int MP_p = 2;
|
||||||
@ -33,52 +34,60 @@ public abstract class MasterPassword {
|
|||||||
private static final MessageAuthenticationDigests MP_mac = MessageAuthenticationDigests.HmacSHA256;
|
private static final MessageAuthenticationDigests MP_mac = MessageAuthenticationDigests.HmacSHA256;
|
||||||
private static final MPTemplates templates = MPTemplates.load();
|
private static final MPTemplates templates = MPTemplates.load();
|
||||||
|
|
||||||
public static byte[] keyForPassword(final String password, final String username) {
|
private final String userName;
|
||||||
|
private final byte[] key;
|
||||||
|
|
||||||
|
private boolean valid;
|
||||||
|
|
||||||
|
public MasterKey(final String userName, final String masterPassword) {
|
||||||
|
|
||||||
|
this.userName = userName;
|
||||||
|
|
||||||
long start = System.currentTimeMillis();
|
long start = System.currentTimeMillis();
|
||||||
byte[] nusernameLengthBytes = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE )
|
byte[] userNameLengthBytes = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE )
|
||||||
.order( MP_byteOrder )
|
.order( MP_byteOrder )
|
||||||
.putInt( username.length() )
|
.putInt( userName.length() )
|
||||||
.array();
|
.array();
|
||||||
byte[] salt = Bytes.concat( "com.lyndir.masterpassword".getBytes( MP_charset ), //
|
byte[] salt = Bytes.concat( "com.lyndir.masterpassword".getBytes( MP_charset ), //
|
||||||
nusernameLengthBytes, //
|
userNameLengthBytes, userName.getBytes( MP_charset ) );
|
||||||
username.getBytes( MP_charset ) );
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
byte[] key = SCrypt.scrypt( password.getBytes( MP_charset ), salt, MP_N, MP_r, MP_p, MP_dkLen );
|
key = SCrypt.scrypt( masterPassword.getBytes( MP_charset ), salt, MP_N, MP_r, MP_p, MP_dkLen );
|
||||||
logger.trc( "User: %s, password: %s derives to key ID: %s (took %.2fs)", username, password,
|
valid = true;
|
||||||
CodeUtils.encodeHex( keyIDForKey( key ) ), (double) (System.currentTimeMillis() - start) / 1000 );
|
|
||||||
|
|
||||||
return key;
|
logger.trc( "User: %s, master password derives to key ID: %s (took %.2fs)", //
|
||||||
|
userName, getKeyID(), (double) (System.currentTimeMillis() - start) / 1000 );
|
||||||
}
|
}
|
||||||
catch (GeneralSecurityException e) {
|
catch (GeneralSecurityException e) {
|
||||||
throw logger.bug( e );
|
throw logger.bug( e );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] subkeyForKey(final byte[] key, final int subkeyLength) {
|
public String getUserName() {
|
||||||
|
|
||||||
|
return userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKeyID() {
|
||||||
|
|
||||||
|
Preconditions.checkState( valid );
|
||||||
|
return CodeUtils.encodeHex( MP_hash.of( key ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] getSubkey(final int subkeyLength) {
|
||||||
|
|
||||||
|
Preconditions.checkState( valid );
|
||||||
byte[] subkey = new byte[Math.min( subkeyLength, key.length )];
|
byte[] subkey = new byte[Math.min( subkeyLength, key.length )];
|
||||||
System.arraycopy( key, 0, subkey, 0, subkey.length );
|
System.arraycopy( key, 0, subkey, 0, subkey.length );
|
||||||
|
|
||||||
return subkey;
|
return subkey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] keyIDForPassword(final String password, final String username) {
|
public String encode(final String name, final MPElementType type, int counter) {
|
||||||
|
|
||||||
return keyIDForKey( keyForPassword( password, username ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[] keyIDForKey(final byte[] key) {
|
|
||||||
|
|
||||||
return MP_hash.of( key );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String generateContent(final MPElementType type, final String name, final byte[] key, int counter) {
|
|
||||||
|
|
||||||
|
Preconditions.checkState( valid );
|
||||||
Preconditions.checkArgument( type.getTypeClass() == MPElementTypeClass.Generated );
|
Preconditions.checkArgument( type.getTypeClass() == MPElementTypeClass.Generated );
|
||||||
Preconditions.checkArgument( !name.isEmpty() );
|
Preconditions.checkArgument( !name.isEmpty() );
|
||||||
Preconditions.checkArgument( key.length > 0 );
|
|
||||||
|
|
||||||
if (counter == 0)
|
if (counter == 0)
|
||||||
counter = (int) (System.currentTimeMillis() / (300 * 1000)) * 300;
|
counter = (int) (System.currentTimeMillis() / (300 * 1000)) * 300;
|
||||||
@ -112,17 +121,9 @@ public abstract class MasterPassword {
|
|||||||
return password.toString();
|
return password.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(final String... arguments) {
|
public void invalidate() {
|
||||||
|
|
||||||
String masterPassword = "test-mp";
|
valid = false;
|
||||||
String username = "test-user";
|
Arrays.fill( key, (byte) 0 );
|
||||||
String siteName = "test-site";
|
|
||||||
MPElementType siteType = MPElementType.GeneratedLong;
|
|
||||||
int siteCounter = 42;
|
|
||||||
|
|
||||||
String sitePassword = generateContent( siteType, siteName, keyForPassword( masterPassword, username ), siteCounter );
|
|
||||||
|
|
||||||
logger.inf( "master password: %s, username: %s\nsite name: %s, site type: %s, site counter: %d\n => site password: %s",
|
|
||||||
masterPassword, username, siteName, siteType, siteCounter, sitePassword );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,7 +16,6 @@ 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 java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.prefs.Preferences;
|
|
||||||
|
|
||||||
|
|
||||||
public class EmergencyActivity extends Activity {
|
public class EmergencyActivity extends Activity {
|
||||||
@ -38,7 +37,7 @@ public class EmergencyActivity extends Activity {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private ListenableFuture<byte[]> masterKeyFuture;
|
private ListenableFuture<MasterKey> masterKeyFuture;
|
||||||
|
|
||||||
@InjectView(R.id.progressView)
|
@InjectView(R.id.progressView)
|
||||||
ProgressBar progressView;
|
ProgressBar progressView;
|
||||||
@ -142,16 +141,12 @@ public class EmergencyActivity extends Activity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
progressView.setVisibility( View.VISIBLE );
|
progressView.setVisibility( View.VISIBLE );
|
||||||
(masterKeyFuture = executor.submit( new Callable<byte[]>() {
|
(masterKeyFuture = executor.submit( new Callable<MasterKey>() {
|
||||||
@Override
|
@Override
|
||||||
public byte[] call()
|
public MasterKey call()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
try {
|
try {
|
||||||
long start = System.currentTimeMillis();
|
return new MasterKey( userName, masterPassword );
|
||||||
byte[] masterKey = MasterPassword.keyForPassword( masterPassword, userName );
|
|
||||||
logger.inf( "masterKey time: %d", System.currentTimeMillis() - start );
|
|
||||||
|
|
||||||
return masterKey;
|
|
||||||
}
|
}
|
||||||
catch (RuntimeException e) {
|
catch (RuntimeException e) {
|
||||||
sitePasswordField.setText( "" );
|
sitePasswordField.setText( "" );
|
||||||
@ -189,9 +184,7 @@ public class EmergencyActivity extends Activity {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
long start = System.currentTimeMillis();
|
final String sitePassword = masterKeyFuture.get().encode( siteName, type, counter );
|
||||||
final String sitePassword = MasterPassword.generateContent( type, siteName, masterKeyFuture.get(), counter );
|
|
||||||
logger.inf( "sitePassword time: %d", System.currentTimeMillis() - start );
|
|
||||||
|
|
||||||
runOnUiThread( new Runnable() {
|
runOnUiThread( new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -13,13 +13,16 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
package com.lyndir.masterpassword;
|
package com.lyndir.masterpassword;
|
||||||
|
|
||||||
|
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.ifNotNullElse;
|
||||||
|
|
||||||
import com.google.common.io.LineReader;
|
import com.google.common.io.LineReader;
|
||||||
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.io.IOException;
|
import java.io.*;
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
|
||||||
@ -30,22 +33,24 @@ import java.util.Arrays;
|
|||||||
*/
|
*/
|
||||||
public class CLI {
|
public class CLI {
|
||||||
|
|
||||||
static final Logger logger = Logger.get( CLI.class );
|
|
||||||
private static final String ENV_USERNAME = "MP_USERNAME";
|
private static final String ENV_USERNAME = "MP_USERNAME";
|
||||||
private static final String ENV_PASSWORD = "MP_PASSWORD";
|
private static final String ENV_PASSWORD = "MP_PASSWORD";
|
||||||
|
private static final String ENV_SITETYPE = "MP_SITETYPE";
|
||||||
|
private static final String ENV_SITECOUNTER = "MP_SITECOUNTER";
|
||||||
|
|
||||||
public static void main(final String[] args)
|
public static void main(final String[] args)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
|
||||||
String userName, masterPassword, siteName = null;
|
// Read information from the environment.
|
||||||
|
String siteName = null;
|
||||||
|
String userName = System.getenv().get( ENV_USERNAME );
|
||||||
|
String masterPassword = System.getenv().get( ENV_PASSWORD );
|
||||||
|
String siteTypeName = ifNotNullElse( System.getenv().get( ENV_SITETYPE ), "" );
|
||||||
|
MPElementType siteType = siteTypeName.isEmpty()? MPElementType.GeneratedLong: MPElementType.forName( siteTypeName );
|
||||||
|
String siteCounterName = ifNotNullElse( System.getenv().get( ENV_SITECOUNTER ), "" );
|
||||||
|
int siteCounter = siteCounterName.isEmpty()? 1: Integer.parseInt( siteCounterName );
|
||||||
|
|
||||||
/* Environment. */
|
// Parse information from option arguments.
|
||||||
userName = System.getenv().get( ENV_USERNAME );
|
|
||||||
masterPassword = System.getenv().get( ENV_PASSWORD );
|
|
||||||
|
|
||||||
/* Arguments. */
|
|
||||||
int counter = 1;
|
|
||||||
MPElementType type = MPElementType.GeneratedLong;
|
|
||||||
boolean typeArg = false, counterArg = false, userNameArg = false;
|
boolean typeArg = false, counterArg = false, userNameArg = false;
|
||||||
for (final String arg : Arrays.asList( args ))
|
for (final String arg : Arrays.asList( args ))
|
||||||
if ("-t".equals( arg ) || "--type".equals( arg ))
|
if ("-t".equals( arg ) || "--type".equals( arg ))
|
||||||
@ -58,12 +63,12 @@ public class CLI {
|
|||||||
System.exit( 0 );
|
System.exit( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
type = MPElementType.forName( arg );
|
siteType = MPElementType.forName( arg );
|
||||||
typeArg = false;
|
typeArg = false;
|
||||||
} else if ("-c".equals( arg ) || "--counter".equals( arg ))
|
} else if ("-c".equals( arg ) || "--counter".equals( arg ))
|
||||||
counterArg = true;
|
counterArg = true;
|
||||||
else if (counterArg) {
|
else if (counterArg) {
|
||||||
counter = ConversionUtils.toIntegerNN( arg );
|
siteCounter = ConversionUtils.toIntegerNN( arg );
|
||||||
counterArg = false;
|
counterArg = false;
|
||||||
} else if ("-u".equals( arg ) || "--username".equals( arg ))
|
} else if ("-u".equals( arg ) || "--username".equals( arg ))
|
||||||
userNameArg = true;
|
userNameArg = true;
|
||||||
@ -80,12 +85,12 @@ public class CLI {
|
|||||||
System.out.println( "Available options:" );
|
System.out.println( "Available options:" );
|
||||||
|
|
||||||
System.out.println( "\t-t | --type [site password type]" );
|
System.out.println( "\t-t | --type [site password type]" );
|
||||||
System.out.format( "\t\tDefault: %s. The password type to use for this site.\n", type.getName() );
|
System.out.format( "\t\tDefault: %s. The password type to use for this site.\n", siteType.getName() );
|
||||||
System.out.println( "\t\tUse 'list' to see the available types." );
|
System.out.println( "\t\tUse 'list' to see the available types." );
|
||||||
|
|
||||||
System.out.println();
|
System.out.println();
|
||||||
System.out.println( "\t-c | --counter [site counter]" );
|
System.out.println( "\t-c | --counter [site counter]" );
|
||||||
System.out.format( "\t\tDefault: %d. The counter to use for this site.\n", counter );
|
System.out.format( "\t\tDefault: %d. The counter to use for this site.\n", siteCounter );
|
||||||
System.out.println( "\t\tIncrement the counter if you need a new password." );
|
System.out.println( "\t\tIncrement the counter if you need a new password." );
|
||||||
|
|
||||||
System.out.println();
|
System.out.println();
|
||||||
@ -106,28 +111,33 @@ public class CLI {
|
|||||||
} else
|
} else
|
||||||
siteName = arg;
|
siteName = arg;
|
||||||
|
|
||||||
InputStreamReader inReader = new InputStreamReader( System.in );
|
// Read missing information from the console.
|
||||||
try {
|
Console console = System.console();
|
||||||
|
try (InputStreamReader inReader = new InputStreamReader( System.in )) {
|
||||||
LineReader lineReader = new LineReader( inReader );
|
LineReader lineReader = new LineReader( inReader );
|
||||||
|
|
||||||
if (siteName == null) {
|
if (siteName == null) {
|
||||||
System.err.format( "Site name: " );
|
System.err.format( "Site name: " );
|
||||||
siteName = lineReader.readLine();
|
siteName = lineReader.readLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userName == null) {
|
if (userName == null) {
|
||||||
System.err.format( "User's name: " );
|
System.err.format( "User's name: " );
|
||||||
userName = lineReader.readLine();
|
userName = lineReader.readLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (masterPassword == null) {
|
if (masterPassword == null) {
|
||||||
|
if (console != null)
|
||||||
|
masterPassword = new String( console.readPassword( "%s's master password: ", userName ) );
|
||||||
|
|
||||||
|
else {
|
||||||
System.err.format( "%s's master password: ", userName );
|
System.err.format( "%s's master password: ", userName );
|
||||||
masterPassword = lineReader.readLine();
|
masterPassword = lineReader.readLine();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
byte[] masterKey = MasterPassword.keyForPassword( masterPassword, userName );
|
// Encode and write out the site password.
|
||||||
String sitePassword = MasterPassword.generateContent( type, siteName, masterKey, counter );
|
System.out.println( new MasterKey( userName, masterPassword ).encode( siteName, siteType, siteCounter ) );
|
||||||
System.out.println( sitePassword );
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
inReader.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ public class ConfigAuthenticationPanel extends AuthenticationPanel implements It
|
|||||||
return selectedUser;
|
return selectedUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new User( selectedUser.getName(), new String( masterPasswordField.getPassword() ) );
|
return new User( selectedUser.getUserName(), new String( masterPasswordField.getPassword() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getHelpText() {
|
public String getHelpText() {
|
||||||
|
@ -21,7 +21,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
|||||||
private final JTextField siteNameField;
|
private final JTextField siteNameField;
|
||||||
private final JComboBox<MPElementType> siteTypeField;
|
private final JComboBox<MPElementType> siteTypeField;
|
||||||
private final JSpinner siteCounterField;
|
private final JSpinner siteCounterField;
|
||||||
private final JLabel passwordLabel;
|
private final JTextField passwordField;
|
||||||
private final JLabel tipLabel;
|
private final JLabel tipLabel;
|
||||||
|
|
||||||
public PasswordFrame(User user)
|
public PasswordFrame(User user)
|
||||||
@ -38,7 +38,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
|||||||
} );
|
} );
|
||||||
|
|
||||||
// User
|
// User
|
||||||
add( label = new JLabel( strf( "Generating passwords for: %s", user.getName() ) ), BorderLayout.NORTH );
|
add( label = new JLabel( strf( "Generating passwords for: %s", user.getUserName() ) ), BorderLayout.NORTH );
|
||||||
label.setFont( Res.exoRegular().deriveFont( 12f ) );
|
label.setFont( Res.exoRegular().deriveFont( 12f ) );
|
||||||
label.setAlignmentX( LEFT_ALIGNMENT );
|
label.setAlignmentX( LEFT_ALIGNMENT );
|
||||||
|
|
||||||
@ -74,6 +74,9 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
|||||||
SwingUtilities.invokeLater( new Runnable() {
|
SwingUtilities.invokeLater( new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
passwordField.setText( null );
|
||||||
|
siteNameField.setText( null );
|
||||||
|
|
||||||
if (getDefaultCloseOperation() == WindowConstants.EXIT_ON_CLOSE)
|
if (getDefaultCloseOperation() == WindowConstants.EXIT_ON_CLOSE)
|
||||||
System.exit( 0 );
|
System.exit( 0 );
|
||||||
else
|
else
|
||||||
@ -120,16 +123,18 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
|||||||
} );
|
} );
|
||||||
|
|
||||||
// Password
|
// Password
|
||||||
passwordLabel = new JLabel( " ", JLabel.CENTER );
|
passwordField = new JTextField( " " );
|
||||||
passwordLabel.setFont( Res.sourceCodeProBlack().deriveFont( 40f ) );
|
passwordField.setFont( Res.sourceCodeProBlack().deriveFont( 40f ) );
|
||||||
passwordLabel.setAlignmentX( Component.CENTER_ALIGNMENT );
|
passwordField.setHorizontalAlignment( JTextField.CENTER );
|
||||||
|
passwordField.setAlignmentX( Component.CENTER_ALIGNMENT );
|
||||||
|
passwordField.setEditable( false );
|
||||||
|
|
||||||
// Tip
|
// Tip
|
||||||
tipLabel = new JLabel( " ", JLabel.CENTER );
|
tipLabel = new JLabel( " ", JLabel.CENTER );
|
||||||
tipLabel.setFont( Res.exoThin().deriveFont( 9f ) );
|
tipLabel.setFont( Res.exoThin().deriveFont( 9f ) );
|
||||||
tipLabel.setAlignmentX( Component.CENTER_ALIGNMENT );
|
tipLabel.setAlignmentX( Component.CENTER_ALIGNMENT );
|
||||||
|
|
||||||
add( Components.boxLayout( BoxLayout.PAGE_AXIS, passwordLabel, tipLabel ), BorderLayout.SOUTH );
|
add( Components.boxLayout( BoxLayout.PAGE_AXIS, passwordField, tipLabel ), BorderLayout.SOUTH );
|
||||||
|
|
||||||
pack();
|
pack();
|
||||||
setMinimumSize( getSize() );
|
setMinimumSize( getSize() );
|
||||||
@ -146,7 +151,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
|||||||
final int siteCounter = (Integer) siteCounterField.getValue();
|
final int siteCounter = (Integer) siteCounterField.getValue();
|
||||||
|
|
||||||
if (siteType.getTypeClass() != MPElementTypeClass.Generated || siteName == null || siteName.isEmpty() || !user.hasKey()) {
|
if (siteType.getTypeClass() != MPElementTypeClass.Generated || siteName == null || siteName.isEmpty() || !user.hasKey()) {
|
||||||
passwordLabel.setText( null );
|
passwordField.setText( null );
|
||||||
tipLabel.setText( null );
|
tipLabel.setText( null );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -154,14 +159,17 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
|||||||
Res.execute( new Runnable() {
|
Res.execute( new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
final String sitePassword = MasterPassword.generateContent( siteType, siteName, user.getKey(), siteCounter );
|
final String sitePassword = user.getKey().encode( siteName, siteType, siteCounter );
|
||||||
if (callback != null)
|
if (callback != null)
|
||||||
callback.passwordGenerated( siteName, sitePassword );
|
callback.passwordGenerated( siteName, sitePassword );
|
||||||
|
|
||||||
SwingUtilities.invokeLater( new Runnable() {
|
SwingUtilities.invokeLater( new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
passwordLabel.setText( sitePassword );
|
if (!siteName.equals( siteNameField.getText() ))
|
||||||
|
return;
|
||||||
|
|
||||||
|
passwordField.setText( sitePassword );
|
||||||
tipLabel.setText( "Press [Enter] to copy the password." );
|
tipLabel.setText( "Press [Enter] to copy the password." );
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
@ -126,7 +126,7 @@ public class UnlockFrame extends JFrame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
boolean checkSignIn() {
|
boolean checkSignIn() {
|
||||||
boolean enabled = user != null && !user.getName().isEmpty() && user.hasKey();
|
boolean enabled = user != null && !user.getUserName().isEmpty() && user.hasKey();
|
||||||
signInButton.setEnabled( enabled );
|
signInButton.setEnabled( enabled );
|
||||||
|
|
||||||
return enabled;
|
return enabled;
|
||||||
|
@ -8,29 +8,29 @@ import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
|
|||||||
*/
|
*/
|
||||||
public class User {
|
public class User {
|
||||||
|
|
||||||
private final String name;
|
private final String userName;
|
||||||
private final String masterPassword;
|
private final String masterPassword;
|
||||||
private byte[] key;
|
private MasterKey key;
|
||||||
|
|
||||||
public User(final String name, final String masterPassword) {
|
public User(final String userName, final String masterPassword) {
|
||||||
this.name = name;
|
this.userName = userName;
|
||||||
this.masterPassword = masterPassword;
|
this.masterPassword = masterPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getUserName() {
|
||||||
return name;
|
return userName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasKey() {
|
public boolean hasKey() {
|
||||||
return key != null || (masterPassword != null && !masterPassword.isEmpty());
|
return key != null || (masterPassword != null && !masterPassword.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getKey() {
|
public MasterKey getKey() {
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
if (!hasKey()) {
|
if (!hasKey()) {
|
||||||
throw new IllegalStateException( strf( "Master password unknown for user: %s", name ) );
|
throw new IllegalStateException( strf( "Master password unknown for user: %s", userName ) );
|
||||||
} else {
|
} else {
|
||||||
key = MasterPassword.keyForPassword( masterPassword, name );
|
key = new MasterKey( userName, masterPassword );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,11 +39,11 @@ public class User {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return name.hashCode();
|
return userName.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return name;
|
return userName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user