2
0

A basic cross-platform GUI for the Master Password generation algorithm.

This commit is contained in:
Maarten Billemont 2014-06-08 16:39:26 -04:00
parent 585268eb0f
commit b74bc79699
10 changed files with 554 additions and 2 deletions

View File

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- PROJECT METADATA -->
<parent>
<groupId>com.lyndir.lhunath.masterpassword</groupId>
<artifactId>masterpassword</artifactId>
<version>GIT-SNAPSHOT</version>
</parent>
<name>Master Password GUI</name>
<description>A GUI interface to the Master Password algorithm</description>
<groupId>com.lyndir.lhunath.masterpassword</groupId>
<artifactId>masterpassword-gui</artifactId>
<packaging>jar</packaging>
<!-- BUILD CONFIGURATION -->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.2</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.lyndir.lhunath.masterpassword.GUI</mainClass>
</transformer>
</transformers>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<!-- DEPENDENCY MANAGEMENT -->
<dependencies>
<!-- PROJECT REFERENCES -->
<dependency>
<groupId>com.lyndir.lhunath.masterpassword</groupId>
<artifactId>masterpassword-algorithm</artifactId>
<version>GIT-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,87 @@
/*
* Copyright 2008, Maarten Billemont
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.lyndir.lhunath.masterpassword;
import com.apple.eawt.*;
import java.io.IOException;
import javax.swing.*;
/**
* <p> <i>Jun 10, 2008</i> </p>
*
* @author mbillemo
*/
public class GUI implements UnlockFrame.SignInCallback {
private UnlockFrame unlockFrame = new UnlockFrame( this );
private PasswordFrame passwordFrame;
public static void main(final String[] args)
throws IOException {
new GUI().open();
}
public GUI() {
try {
getClass().getClassLoader().loadClass( "com.apple.eawt.Application" );
Application application = Application.getApplication();
application.addAppEventListener( new AppForegroundListener() {
@Override
public void appMovedToBackground(AppEvent.AppForegroundEvent arg0) {
}
@Override
public void appRaisedToForeground(AppEvent.AppForegroundEvent arg0) {
open();
}
} );
application.addAppEventListener( new AppReOpenedListener() {
@Override
public void appReOpened(AppEvent.AppReOpenedEvent arg0) {
open();
}
} );
}
catch (ClassNotFoundException | NoClassDefFoundError ignored) {
}
}
private void open() {
SwingUtilities.invokeLater( new Runnable() {
@Override
public void run() {
if (passwordFrame == null) {
unlockFrame.setVisible( true );
} else {
passwordFrame.setVisible( true );
}
}
} );
}
@Override
public boolean signedIn(final String userName, final String masterPassword) {
final byte[] key = MasterPassword.keyForPassword( masterPassword, userName );
passwordFrame = new PasswordFrame( new User( userName, key ) );
open();
return true;
}
}

View File

@ -0,0 +1,152 @@
package com.lyndir.lhunath.masterpassword;
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
import com.lyndir.lhunath.masterpassword.util.Components;
import java.awt.*;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
/**
* @author lhunath, 2014-06-08
*/
public class PasswordFrame extends JFrame implements DocumentListener {
private static final ExecutorService executor = Executors.newSingleThreadExecutor();
private final User user;
private final JPanel root;
private final JTextField siteNameField;
private final JComboBox<MPElementType> siteTypeField;
private final JSpinner siteCounterField;
private final JLabel passwordLabel;
public PasswordFrame(User user)
throws HeadlessException {
super( "Master Password" );
this.user = user;
JLabel label;
setDefaultCloseOperation( DISPOSE_ON_CLOSE );
setContentPane( root = new JPanel( new BorderLayout( 20, 20 ) ) );
root.setBorder( new EmptyBorder( 20, 20, 20, 20 ) );
// User
add( new JLabel( strf( "Generating passwords for: %s", user.getName() ) ), BorderLayout.NORTH );
// Site
JPanel sitePanel = new JPanel();
sitePanel.setLayout( new BoxLayout( sitePanel, BoxLayout.PAGE_AXIS ) );
sitePanel.setBorder( new CompoundBorder( new EtchedBorder( EtchedBorder.RAISED ), new EmptyBorder( 8, 8, 8, 8 ) ) );
add( sitePanel, BorderLayout.CENTER );
// Site Name
sitePanel.add( new JLabel( "Site Name:", JLabel.LEADING ) );
sitePanel.add( siteNameField = new JTextField() {
@Override
public Dimension getMaximumSize() {
return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
}
} );
siteNameField.getDocument().addDocumentListener( this );
siteNameField.addActionListener( new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
updatePassword( new PasswordCallback() {
@Override
public void passwordGenerated(final String siteName, final String sitePassword) {
StringSelection clipboardContents = new StringSelection( sitePassword );
Toolkit.getDefaultToolkit().getSystemClipboard().setContents( clipboardContents, null );
SwingUtilities.invokeLater( new Runnable() {
@Override
public void run() {
dispose();
}
} );
}
} );
}
} );
// Site Type & Counter
sitePanel.add( Components.boxLayout( BoxLayout.LINE_AXIS, //
siteTypeField = new JComboBox<>( MPElementType.values() ),
siteCounterField = new JSpinner( new SpinnerNumberModel( 1, 1, Integer.MAX_VALUE, 1 ) ) {
@Override
public Dimension getMaximumSize() {
return new Dimension( 50, getPreferredSize().height );
}
} ) );
siteTypeField.setSelectedItem( MPElementType.GeneratedLong );
siteCounterField.addChangeListener( new ChangeListener() {
@Override
public void stateChanged(final ChangeEvent e) {
updatePassword( null );
}
} );
// Password
add( passwordLabel = new JLabel( " ", JLabel.CENTER ), BorderLayout.SOUTH );
passwordLabel.setFont( passwordLabel.getFont().deriveFont( 40f ) );
pack();
setMinimumSize( getSize() );
setPreferredSize( new Dimension( 600, getSize().height ) );
pack();
setLocationByPlatform( true );
setLocationRelativeTo( null );
}
private void updatePassword(final PasswordCallback callback) {
final MPElementType siteType = (MPElementType) siteTypeField.getSelectedItem();
final String siteName = siteNameField.getText();
final int siteCounter = (Integer) siteCounterField.getValue();
executor.submit( new Runnable() {
@Override
public void run() {
final String sitePassword = MasterPassword.generateContent( siteType, siteName, user.getKey(), siteCounter );
if (callback != null) {
callback.passwordGenerated( siteName, sitePassword );
}
SwingUtilities.invokeLater( new Runnable() {
@Override
public void run() {
passwordLabel.setText( sitePassword );
}
} );
}
} );
}
@Override
public void insertUpdate(final DocumentEvent e) {
updatePassword( null );
}
@Override
public void removeUpdate(final DocumentEvent e) {
updatePassword( null );
}
@Override
public void changedUpdate(final DocumentEvent e) {
updatePassword( null );
}
interface PasswordCallback {
void passwordGenerated(String siteName, String sitePassword);
}
}

View File

@ -0,0 +1,175 @@
package com.lyndir.lhunath.masterpassword;
import com.google.common.io.Resources;
import com.lyndir.lhunath.masterpassword.util.Components;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
/**
* @author lhunath, 2014-06-08
*/
public class UnlockFrame extends JFrame implements DocumentListener {
private static final ExecutorService executor = Executors.newSingleThreadExecutor();
private final SignInCallback signInCallback;
private final JPanel root;
private final JLabel avatarView;
private final JTextField userNameField;
private final JTextField masterPasswordField;
private final JButton signInButton;
public UnlockFrame(final SignInCallback signInCallback)
throws HeadlessException {
super( "Unlock Master Password" );
this.signInCallback = signInCallback;
JLabel label;
setDefaultCloseOperation( DISPOSE_ON_CLOSE );
setContentPane( root = new JPanel( new BorderLayout( 20, 20 ) ) );
root.setBorder( new EmptyBorder( 20, 20, 20, 20 ) );
JPanel userAndPassword = new JPanel();
userAndPassword.setLayout( new BoxLayout( userAndPassword, BoxLayout.PAGE_AXIS ) );
userAndPassword.setBorder( new CompoundBorder( new EtchedBorder( EtchedBorder.RAISED ), new EmptyBorder( 8, 8, 8, 8 ) ) );
add( userAndPassword, BorderLayout.CENTER );
// Avatar
userAndPassword.add( Box.createVerticalGlue() );
userAndPassword.add( avatarView = new JLabel( new ImageIcon( Resources.getResource( "media/Avatars/avatar-0.png" ) ) ) {
@Override
public Dimension getMaximumSize() {
return new Dimension( Integer.MAX_VALUE, Integer.MAX_VALUE );
}
} );
userAndPassword.add( Box.createVerticalGlue() );
// User Name
userAndPassword.add( label = new JLabel( "User Name:" ) );
label.setHorizontalAlignment( SwingConstants.CENTER );
label.setVerticalAlignment( SwingConstants.BOTTOM );
userAndPassword.add( userNameField = new JTextField() {
@Override
public Dimension getMaximumSize() {
return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
}
} );
userNameField.getDocument().addDocumentListener( this );
userNameField.addActionListener( new AbstractAction() {
@Override
public void actionPerformed(final ActionEvent e) {
trySignIn();
}
} );
// Master Password
userAndPassword.add( label = new JLabel( "Master Password:" ) );
label.setHorizontalAlignment( SwingConstants.CENTER );
label.setVerticalAlignment( SwingConstants.BOTTOM );
userAndPassword.add( masterPasswordField = new JPasswordField() {
@Override
public Dimension getMaximumSize() {
return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
}
} );
masterPasswordField.getDocument().addDocumentListener( this );
masterPasswordField.addActionListener( new AbstractAction() {
@Override
public void actionPerformed(final ActionEvent e) {
trySignIn();
}
} );
// Sign In
add( Components.boxLayout( BoxLayout.LINE_AXIS, Box.createGlue(), signInButton = new JButton( "Sign In" ), Box.createGlue() ),
BorderLayout.SOUTH );
signInButton.addActionListener( new AbstractAction() {
@Override
public void actionPerformed(final ActionEvent e) {
trySignIn();
}
} );
checkSignIn();
pack();
setMinimumSize( getSize() );
setPreferredSize( new Dimension( 300, 300 ) );
pack();
setLocationByPlatform( true );
setLocationRelativeTo( null );
}
private boolean checkSignIn() {
String userName = userNameField.getText();
String masterPassword = masterPasswordField.getText();
boolean enabled = !userName.isEmpty() && !masterPassword.isEmpty();
signInButton.setEnabled( enabled );
return enabled;
}
private void trySignIn() {
if (!checkSignIn())
return;
final String userName = userNameField.getText();
final String masterPassword = masterPasswordField.getText();
userNameField.setEnabled( false );
masterPasswordField.setEnabled( false );
signInButton.setEnabled( false );
signInButton.setText( "Signing In..." );
executor.submit( new Runnable() {
@Override
public void run() {
final boolean success = signInCallback.signedIn( userName, masterPassword );
SwingUtilities.invokeLater( new Runnable() {
@Override
public void run() {
if (success) {
dispose();
return;
}
userNameField.setEnabled( true );
masterPasswordField.setEnabled( true );
signInButton.setText( "Sign In" );
checkSignIn();
}
} );
}
} );
}
@Override
public void insertUpdate(final DocumentEvent e) {
checkSignIn();
}
@Override
public void removeUpdate(final DocumentEvent e) {
checkSignIn();
}
@Override
public void changedUpdate(final DocumentEvent e) {
checkSignIn();
}
interface SignInCallback {
boolean signedIn(String userName, String masterPassword);
}
}

View File

@ -0,0 +1,22 @@
package com.lyndir.lhunath.masterpassword;
/**
* @author lhunath, 2014-06-08
*/
public class User {
private final String name;
private final byte[] key;
public User(final String name, final byte[] key) {
this.name = name;
this.key = key;
}
public String getName() {
return name;
}
public byte[] getKey() {
return key;
}
}

View File

@ -0,0 +1,20 @@
package com.lyndir.lhunath.masterpassword.util;
import java.awt.*;
import javax.swing.*;
/**
* @author lhunath, 2014-06-08
*/
public abstract class Components {
public static JComponent boxLayout(int axis, Component... components) {
JPanel container = new JPanel();
container.setLayout( new BoxLayout( container, axis ) );
for (Component component : components)
container.add( component );
return container;
}
}

View File

@ -0,0 +1,15 @@
<configuration scan="true">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%-8relative %22c{0} [%-5level] %msg%n</Pattern>
</layout>
</appender>
<logger name="com.lyndir" level="TRACE" />
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>

View File

@ -0,0 +1 @@
../../../../../Resources/Media/

View File

@ -21,6 +21,7 @@
<modules> <modules>
<module>masterpassword-algorithm</module> <module>masterpassword-algorithm</module>
<module>masterpassword-cli</module> <module>masterpassword-cli</module>
<module>masterpassword-gui</module>
<!--module>masterpassword-android</module--> <!--module>masterpassword-android</module-->
</modules> </modules>

View File

@ -1876,7 +1876,7 @@
); );
GCC_PREFIX_HEADER = "MasterPassword-Prefix.pch"; GCC_PREFIX_HEADER = "MasterPassword-Prefix.pch";
INFOPLIST_FILE = "MasterPassword-Info.plist"; INFOPLIST_FILE = "MasterPassword-Info.plist";
PROVISIONING_PROFILE = "9FCC90BB-24CA-40E2-8565-860F95CCC039"; PROVISIONING_PROFILE = "9BA628A2-36F5-438B-9F08-A2527CECC9F8";
SKIP_INSTALL = NO; SKIP_INSTALL = NO;
WRAPPER_NAME = "Master Password.${WRAPPER_EXTENSION}"; WRAPPER_NAME = "Master Password.${WRAPPER_EXTENSION}";
}; };
@ -1896,7 +1896,7 @@
); );
GCC_PREFIX_HEADER = "MasterPassword-Prefix.pch"; GCC_PREFIX_HEADER = "MasterPassword-Prefix.pch";
INFOPLIST_FILE = "MasterPassword-Info.plist"; INFOPLIST_FILE = "MasterPassword-Info.plist";
PROVISIONING_PROFILE = "9FCC90BB-24CA-40E2-8565-860F95CCC039"; PROVISIONING_PROFILE = "9BA628A2-36F5-438B-9F08-A2527CECC9F8";
SKIP_INSTALL = NO; SKIP_INSTALL = NO;
WRAPPER_NAME = "Master Password.${WRAPPER_EXTENSION}"; WRAPPER_NAME = "Master Password.${WRAPPER_EXTENSION}";
}; };