2
0

Key calculator and access to the full algorithm.

This commit is contained in:
Maarten Billemont 2018-08-24 13:48:53 -04:00
parent 9a40e52d53
commit d5551c8c8c
12 changed files with 253 additions and 51 deletions

View File

@ -24,6 +24,8 @@ shadowJar {
attributes 'Implementation-Version': version
}
doLast {
println("doLast: ${System.env.KEY_PW_DESKTOP}");
println("doLast: "+System.getenv( 'KEY_PW_DESKTOP' ));
if (System.getenv( 'KEY_PW_DESKTOP' ) != null)
ant.signjar( jar: archivePath,
alias: 'masterpassword-desktop',

View File

@ -1,6 +1,5 @@
package com.lyndir.masterpassword.gui.util;
import com.google.common.collect.ImmutableList;
import java.util.*;
import java.util.function.Consumer;
import javax.annotation.Nullable;
@ -18,25 +17,21 @@ public class CollectionListModel<E> extends AbstractListModel<E>
private final List<E> model = new LinkedList<>();
@Nullable
private E selectedItem;
private JList<E> list;
@Nullable
private E selectedItem;
@Nullable
private Consumer<E> selectionConsumer;
@SafeVarargs
public static <E> CollectionListModel<E> copy(final E... elements) {
return copy( Arrays.asList( elements ) );
public CollectionListModel(final E... elements) {
this( Arrays.asList( elements ) );
}
public static <E> CollectionListModel<E> copy(final Collection<? extends E> elements) {
CollectionListModel<E> model = new CollectionListModel<>();
synchronized (model) {
model.model.addAll( elements );
model.selectedItem = model.getElementAt( 0 );
model.fireIntervalAdded( model, 0, model.model.size() );
return model;
}
public CollectionListModel(final Collection<? extends E> elements) {
model.addAll( elements );
selectedItem = getElementAt( 0 );
fireIntervalAdded( this, 0, model.size() );
}
@Override
@ -44,8 +39,8 @@ public class CollectionListModel<E> extends AbstractListModel<E>
return model.size();
}
@Override
@Nullable
@Override
public synchronized E getElementAt(final int index) {
return (index < model.size())? model.get( index ): null;
}
@ -109,6 +104,11 @@ public class CollectionListModel<E> extends AbstractListModel<E>
return selectedItem;
}
public CollectionListModel<E> select(final E selectedItem) {
setSelectedItem( selectedItem );
return this;
}
public synchronized void registerList(final JList<E> list) {
// TODO: This class should probably implement ListSelectionModel instead.
if (this.list != null)

View File

@ -20,7 +20,6 @@ package com.lyndir.masterpassword.gui.util;
import com.google.common.base.Strings;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.masterpassword.util.Utilities;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
@ -34,8 +33,8 @@ import javax.annotation.Nullable;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.event.*;
import javax.swing.text.DefaultFormatterFactory;
import javax.swing.event.HyperlinkEvent;
import javax.swing.text.*;
import org.jetbrains.annotations.NonNls;
@ -114,9 +113,14 @@ public abstract class Components {
if (options == null)
return (selectedValue instanceof Integer)? (Integer) selectedValue: JOptionPane.CLOSED_OPTION;
try {
int option = Arrays.binarySearch( options, selectedValue );
return (option < 0)? JOptionPane.CLOSED_OPTION: option;
}
catch (final ClassCastException ignored) {
return JOptionPane.CLOSED_OPTION;
}
}
@Nullable
public static File showLoadDialog(@Nullable final Component owner, final String title) {
@ -164,7 +168,11 @@ public abstract class Components {
}
public static JTextField textField() {
return new JTextField() {
return textField( null );
}
public static JTextField textField(@Nullable final Document document) {
return new JTextField( document, null, 0 ) {
{
setBorder( BorderFactory.createCompoundBorder( BorderFactory.createLineBorder( Res.colors().controlBorder(), 1, true ),
BorderFactory.createEmptyBorder( 4, 4, 4, 4 ) ) );
@ -174,43 +182,30 @@ public abstract class Components {
@Override
public Dimension getMaximumSize() {
return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
return new Dimension( Integer.MAX_VALUE, Integer.MAX_VALUE );
}
};
}
public static JTextField textField(@Nullable final String text, @Nullable final Consumer<String> change) {
return new JTextField( text ) {
return textField( new DocumentModel( new PlainDocument() ).selection( text, change ).getDocument() );
}
public static JTextArea textArea() {
return new JTextArea() {
{
setBorder( BorderFactory.createCompoundBorder( BorderFactory.createLineBorder( Res.colors().controlBorder(), 1, true ),
BorderFactory.createEmptyBorder( 4, 4, 4, 4 ) ) );
setFont( Res.fonts().valueFont( TEXT_SIZE_CONTROL ) );
setAlignmentX( LEFT_ALIGNMENT );
if (change != null) {
getDocument().addDocumentListener( new DocumentListener() {
@Override
public void insertUpdate(final DocumentEvent e) {
change.accept( getText() );
}
@Override
public void removeUpdate(final DocumentEvent e) {
change.accept( getText() );
}
@Override
public void changedUpdate(final DocumentEvent e) {
change.accept( getText() );
}
} );
change.accept( getText() );
}
setLineWrap( true );
setRows( 3 );
}
@Override
public Dimension getMaximumSize() {
return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
return new Dimension( Integer.MAX_VALUE, Integer.MAX_VALUE );
}
};
}
@ -443,17 +438,17 @@ public abstract class Components {
public static <E> JComboBox<E> comboBox(final E[] values, final Function<E, String> valueTransformer,
@Nullable final E selectedItem, @Nullable final Consumer<E> selectionConsumer) {
return comboBox( CollectionListModel.copy( values ).selection( selectedItem, selectionConsumer ), valueTransformer );
return comboBox( new CollectionListModel<>( values ).selection( selectedItem, selectionConsumer ), valueTransformer );
}
public static <E> JComboBox<E> comboBox(final Collection<E> values, final Function<E, String> valueTransformer,
@Nullable final Consumer<E> selectionConsumer) {
return comboBox( CollectionListModel.copy( values ).selection( selectionConsumer ), valueTransformer );
return comboBox( new CollectionListModel<>( values ).selection( selectionConsumer ), valueTransformer );
}
public static <E> JComboBox<E> comboBox(final Collection<E> values, final Function<E, String> valueTransformer,
@Nullable final E selectedItem, @Nullable final Consumer<E> selectionConsumer) {
return comboBox( CollectionListModel.copy( values ).selection( selectedItem, selectionConsumer ), valueTransformer );
return comboBox( new CollectionListModel<>( values ).selection( selectedItem, selectionConsumer ), valueTransformer );
}
public static <E> JComboBox<E> comboBox(final ComboBoxModel<E> model, final Function<E, String> valueTransformer) {

View File

@ -0,0 +1,27 @@
package com.lyndir.masterpassword.gui.util;
import java.util.function.Consumer;
import javax.annotation.Nullable;
/**
* @author lhunath, 2018-08-23
*/
public class ConsumingTrigger<T> implements Consumer<T> {
private final Runnable trigger;
@Nullable
private T value;
public ConsumingTrigger(final Runnable trigger) {
this.trigger = trigger;
}
@Override
public void accept(final T t) {
value = t;
trigger.run();
}
}

View File

@ -0,0 +1,95 @@
package com.lyndir.masterpassword.gui.util;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.util.function.Consumer;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
/**
* @author lhunath, 2018-08-24
*/
public class DocumentModel implements Selectable<String, DocumentModel> {
private static final Logger logger = Logger.get( DocumentModel.class );
private final Document document;
@Nullable
private DocumentListener documentListener;
public DocumentModel(final Document document) {
this.document = document;
}
@Nonnull
public Document getDocument() {
return document;
}
@Nullable
public String getText() {
try {
return (document.getLength() > 0)? document.getText( 0, document.getLength() ): null;
}
catch (final BadLocationException e) {
logger.wrn( "While getting text for model", e );
return null;
}
}
public void setText(@Nullable final String text) {
try {
if (document.getLength() > 0)
document.remove( 0, document.getLength() );
if (text != null)
document.insertString( 0, text, null );
}
catch (final BadLocationException e) {
logger.err( "While setting text for model", e );
}
}
@Override
public DocumentModel selection(@Nullable final Consumer<String> selectionConsumer) {
if (documentListener != null)
document.removeDocumentListener( documentListener );
if (selectionConsumer != null)
document.addDocumentListener( documentListener = new DocumentListener() {
@Override
public void insertUpdate(final DocumentEvent e) {
trigger();
}
@Override
public void removeUpdate(final DocumentEvent e) {
trigger();
}
@Override
public void changedUpdate(final DocumentEvent e) {
trigger();
}
private void trigger() {
selectionConsumer.accept( getText() );
}
} );
return this;
}
@Override
public DocumentModel selection(@Nullable final String selectedItem, @Nullable final Consumer<String> selectionConsumer) {
selection( selectionConsumer );
setText( selectedItem );
return this;
}
}

View File

@ -164,6 +164,10 @@ public abstract class Res {
return icon( "media/icon_edit.png" );
}
public Icon key() {
return icon( "media/icon_key.png" );
}
public Icon avatar(final int index) {
return icon( strf( "media/avatar-%d.png", index % avatars() ) );
}

View File

@ -11,5 +11,5 @@ public interface Selectable<E, T> {
T selection(@Nullable Consumer<E> selectionConsumer);
T selection(E selectedItem, @Nullable Consumer<E> selectionConsumer);
T selection(@Nullable E selectedItem, @Nullable Consumer<E> selectionConsumer);
}

View File

@ -109,13 +109,14 @@ public class UnsignedIntegerModel extends SpinnerNumberModel implements Selectab
}
@Override
public UnsignedIntegerModel selection(final UnsignedInteger selectedItem, @Nullable final Consumer<UnsignedInteger> selectionConsumer) {
public UnsignedIntegerModel selection(@Nullable final UnsignedInteger selectedItem,
@Nullable final Consumer<UnsignedInteger> selectionConsumer) {
if (changeListener != null) {
removeChangeListener( changeListener );
changeListener = null;
}
setValue( selectedItem );
setValue( (selectedItem != null)? selectedItem: getMinimum() );
return selection( selectionConsumer );
}

View File

@ -23,7 +23,7 @@ public class FilesPanel extends JPanel implements MPFileUserManager.Listener, Ma
"Click to change the user's avatar." );
private final CollectionListModel<MPUser<?>> usersModel =
CollectionListModel.<MPUser<?>>copy( MPFileUserManager.get().getFiles() ).selection( MasterPassword.get()::activateUser );
new CollectionListModel<MPUser<?>>( MPFileUserManager.get().getFiles() ).selection( MasterPassword.get()::activateUser );
protected FilesPanel() {
setOpaque( false );

View File

@ -34,6 +34,7 @@ import javax.annotation.Nullable;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.PlainDocument;
/**
@ -470,8 +471,10 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
"Show site settings." );
private final JButton questionsButton = Components.button( Res.icons().question(), event -> showSiteQuestions(),
"Show site recovery questions." );
private final JButton editButton = Components.button( Res.icons().edit(), event -> showEditSite(),
private final JButton editButton = Components.button( Res.icons().edit(), event -> showSiteValues(),
"Set/save personal password/login." );
private final JButton keyButton = Components.button( Res.icons().key(), event -> showSiteKeys(),
"Cryptographic site keys." );
private final JButton deleteButton = Components.button( Res.icons().delete(), event -> deleteSite(),
"Delete the site from the user." );
@ -502,9 +505,12 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
siteToolbar.add( settingsButton );
siteToolbar.add( questionsButton );
siteToolbar.add( editButton );
siteToolbar.add( keyButton );
siteToolbar.add( deleteButton );
settingsButton.setEnabled( false );
questionsButton.setEnabled( false );
editButton.setEnabled( false );
keyButton.setEnabled( false );
deleteButton.setEnabled( false );
answerLabel = Components.label( "Answer:" );
@ -665,7 +671,7 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
} );
}
public void showEditSite() {
public void showSiteValues() {
MPSite<?> site = sitesModel.getSelectedItem();
if (site == null)
return;
@ -710,6 +716,76 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
}
}
public void showSiteKeys() {
MPSite<?> site = sitesModel.getSelectedItem();
if (site == null)
return;
JTextArea resultField = Components.textArea();
resultField.setEnabled( false );
CollectionListModel<MPKeyPurpose> purposeModel = new CollectionListModel<>( MPKeyPurpose.values() );
DocumentModel contextModel = new DocumentModel( new PlainDocument() );
UnsignedIntegerModel counterModel = new UnsignedIntegerModel( UnsignedInteger.ONE );
CollectionListModel<MPResultType> typeModel = new CollectionListModel<>( MPResultType.values() );
DocumentModel stateModel = new DocumentModel( new PlainDocument() );
Runnable trigger = () -> Res.job( () -> {
try {
MPKeyPurpose purpose = purposeModel.getSelectedItem();
MPResultType type = typeModel.getSelectedItem();
String result = ((purpose == null) || (type == null))? null:
site.getResult( purpose, contextModel.getText(), counterModel.getNumber(), type, stateModel.getText() );
Res.ui( () -> resultField.setText( result ) );
}
catch (final MPKeyUnavailableException | MPAlgorithmException e) {
logger.err( e, "While computing site edit results." );
}
} );
purposeModel.selection( MPKeyPurpose.Authentication, p -> trigger.run() );
contextModel.selection( c -> trigger.run() );
counterModel.selection( c -> trigger.run() );
typeModel.selection( MPResultType.DeriveKey, t -> {
switch (t) {
case DeriveKey:
stateModel.setText( Integer.toString( site.getAlgorithm().mpw_keySize_min() ) );
break;
default:
stateModel.setText( null );
}
trigger.run();
} );
stateModel.selection( c -> trigger.run() );
if (JOptionPane.OK_OPTION == Components.showDialog( this, site.getSiteName(), new JOptionPane( Components.panel(
BoxLayout.PAGE_AXIS,
Components.heading( "Key Calculator" ),
Components.label( "Purpose:" ),
Components.comboBox( purposeModel, MPKeyPurpose::getShortName ),
Components.strut(),
Components.label( "Context:" ),
Components.textField( contextModel.getDocument() ),
Components.label( "Counter:" ),
Components.spinner( counterModel ),
Components.label( "Type:" ),
Components.comboBox( typeModel, this::getTypeDescription ),
Components.label( "State:" ),
Components.scrollPane( Components.textField( stateModel.getDocument() ) ),
Components.strut(),
resultField ) ) {
{
setOptions( new Object[]{ "Copy", "Cancel" } );
setInitialValue( getOptions()[0] );
}
} ))
copyResult( resultField.getText() );
}
public void deleteSite() {
MPSite<?> site = sitesModel.getSelectedItem();
if (site == null)
@ -790,6 +866,8 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
passwordField.setText( (result != null)? result: " " );
settingsButton.setEnabled( result != null );
questionsButton.setEnabled( result != null );
editButton.setEnabled( result != null );
keyButton.setEnabled( result != null );
deleteButton.setEnabled( result != null );
} ) ) );
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB