Refactoring masterpassword-algorithm.
This commit is contained in:
		@@ -18,43 +18,95 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.lyndir.masterpassword;
 | 
					package com.lyndir.masterpassword;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.fasterxml.jackson.annotation.JsonCreator;
 | 
				
			||||||
 | 
					import com.fasterxml.jackson.annotation.JsonValue;
 | 
				
			||||||
import com.google.common.primitives.UnsignedInteger;
 | 
					import com.google.common.primitives.UnsignedInteger;
 | 
				
			||||||
import com.lyndir.lhunath.opal.system.MessageAuthenticationDigests;
 | 
					import com.lyndir.lhunath.opal.system.MessageAuthenticationDigests;
 | 
				
			||||||
import com.lyndir.lhunath.opal.system.MessageDigests;
 | 
					import com.lyndir.lhunath.opal.system.MessageDigests;
 | 
				
			||||||
 | 
					import com.lyndir.masterpassword.impl.MPAlgorithmV0;
 | 
				
			||||||
 | 
					import com.lyndir.masterpassword.impl.MPAlgorithmV1;
 | 
				
			||||||
 | 
					import com.lyndir.masterpassword.impl.MPAlgorithmV2;
 | 
				
			||||||
 | 
					import com.lyndir.masterpassword.impl.MPAlgorithmV3;
 | 
				
			||||||
import java.nio.ByteOrder;
 | 
					import java.nio.ByteOrder;
 | 
				
			||||||
import java.nio.charset.Charset;
 | 
					import java.nio.charset.Charset;
 | 
				
			||||||
import javax.annotation.Nullable;
 | 
					import javax.annotation.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @see MPMasterKey.Version
 | 
					 * @see Version
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@SuppressWarnings({ "FieldMayBeStatic", "NewMethodNamingConvention", "MethodReturnAlwaysConstant" })
 | 
					@SuppressWarnings({ "FieldMayBeStatic", "NewMethodNamingConvention", "MethodReturnAlwaysConstant" })
 | 
				
			||||||
public abstract class MPAlgorithm {
 | 
					public abstract class MPAlgorithm {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Derive a master key that describes a user's identity.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param fullName       The name of the user whose identity is described by the key.
 | 
				
			||||||
 | 
					     * @param masterPassword The user's secret that authenticates his access to the identity.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    public abstract byte[] masterKey(String fullName, char[] masterPassword);
 | 
					    public abstract byte[] masterKey(String fullName, char[] masterPassword);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Derive a site key that describes a user's access to a specific entity.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param masterKey   The identity of the user trying to access the entity.
 | 
				
			||||||
 | 
					     * @param siteName    The name of the entity to access.
 | 
				
			||||||
 | 
					     * @param siteCounter The site key's generation.
 | 
				
			||||||
 | 
					     * @param keyPurpose  The action that the user aims to undertake with this key.
 | 
				
			||||||
 | 
					     * @param keyContext  An action-specific context within which to scope the key.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    public abstract byte[] siteKey(byte[] masterKey, String siteName, UnsignedInteger siteCounter,
 | 
					    public abstract byte[] siteKey(byte[] masterKey, String siteName, UnsignedInteger siteCounter,
 | 
				
			||||||
                                   MPKeyPurpose keyPurpose, @Nullable String keyContext);
 | 
					                                   MPKeyPurpose keyPurpose, @Nullable String keyContext);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Encode a templated result for a site key.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param resultType  The template to base the site key's encoding on.
 | 
				
			||||||
 | 
					     * @param resultParam A parameter that provides contextual data specific to the type template.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    public abstract String siteResult(byte[] masterKey, byte[] siteKey, String siteName, UnsignedInteger siteCounter,
 | 
					    public abstract String siteResult(byte[] masterKey, byte[] siteKey, String siteName, UnsignedInteger siteCounter,
 | 
				
			||||||
                                      MPKeyPurpose keyPurpose, @Nullable String keyContext,
 | 
					                                      MPKeyPurpose keyPurpose, @Nullable String keyContext,
 | 
				
			||||||
                                      MPResultType resultType, @Nullable String resultParam);
 | 
					                                      MPResultType resultType, @Nullable String resultParam);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public abstract String sitePasswordFromTemplate(byte[] masterKey, byte[] siteKey,
 | 
					    /**
 | 
				
			||||||
                                                    MPResultType resultType, @Nullable String resultParam);
 | 
					     * The result for {@link #siteResult(byte[], byte[], String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String)}
 | 
				
			||||||
 | 
					     * for the case where {@code resultType} is a {@link MPResultTypeClass#Template}.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract String siteResultFromTemplate(byte[] masterKey, byte[] siteKey,
 | 
				
			||||||
 | 
					                                                  MPResultType resultType, @Nullable String resultParam);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public abstract String sitePasswordFromCrypt(byte[] masterKey, byte[] siteKey, MPResultType resultType, @Nullable String resultParam);
 | 
					    /**
 | 
				
			||||||
 | 
					     * The result for {@link #siteResult(byte[], byte[], String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String)}
 | 
				
			||||||
 | 
					     * for the case where {@code resultType} is a {@link MPResultTypeClass#Stateful}.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract String siteResultFromState(byte[] masterKey, byte[] siteKey,
 | 
				
			||||||
 | 
					                                               MPResultType resultType, @Nullable String resultParam);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public abstract String sitePasswordFromDerive(byte[] masterKey, byte[] siteKey, MPResultType resultType, @Nullable String resultParam);
 | 
					    /**
 | 
				
			||||||
 | 
					     * The result for {@link #siteResult(byte[], byte[], String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String)}
 | 
				
			||||||
 | 
					     * for the case where {@code resultType} is a {@link MPResultTypeClass#Derive}.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract String siteResultFromDerive(byte[] masterKey, byte[] siteKey,
 | 
				
			||||||
 | 
					                                                MPResultType resultType, @Nullable String resultParam);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * For {@link MPResultTypeClass#Stateful} {@code resultType}s, generate the {@code resultParam} to use with the
 | 
				
			||||||
 | 
					     * {@link #siteResult(byte[], byte[], String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String)} call
 | 
				
			||||||
 | 
					     * in order to reconstruct this call's original {@code resultParam}.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param resultType  The template to base the site key's encoding on.
 | 
				
			||||||
 | 
					     * @param resultParam A parameter that provides contextual data specific to the type template.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    public abstract String siteState(byte[] masterKey, byte[] siteKey, String siteName, UnsignedInteger siteCounter,
 | 
					    public abstract String siteState(byte[] masterKey, byte[] siteKey, String siteName, UnsignedInteger siteCounter,
 | 
				
			||||||
                                     MPKeyPurpose keyPurpose, @Nullable String keyContext,
 | 
					                                     MPKeyPurpose keyPurpose, @Nullable String keyContext,
 | 
				
			||||||
                                     MPResultType resultType, String resultParam);
 | 
					                                     MPResultType resultType, String resultParam);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Configuration
 | 
					    // Configuration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public abstract MPMasterKey.Version version();
 | 
					    /**
 | 
				
			||||||
 | 
					     * The linear version identifier of this algorithm's implementation.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract Version version();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * mpw: defaults: password result type.
 | 
					     * mpw: defaults: password result type.
 | 
				
			||||||
@@ -133,11 +185,68 @@ public abstract class MPAlgorithm {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Utilities
 | 
					    // Utilities
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    abstract byte[] toBytes(int number);
 | 
					    protected abstract byte[] toBytes(int number);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    abstract byte[] toBytes(UnsignedInteger number);
 | 
					    protected abstract byte[] toBytes(UnsignedInteger number);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    abstract byte[] toBytes(char[] characters);
 | 
					    protected abstract byte[] toBytes(char[] characters);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    abstract byte[] toID(byte[] bytes);
 | 
					    protected abstract byte[] toID(byte[] bytes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * The algorithm iterations.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public enum Version {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * bugs:
 | 
				
			||||||
 | 
					         * - does math with chars whose signedness was platform-dependent.
 | 
				
			||||||
 | 
					         * - miscounted the byte-length for multi-byte site names.
 | 
				
			||||||
 | 
					         * - miscounted the byte-length for multi-byte user names.
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        V0( new MPAlgorithmV0() ),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * bugs:
 | 
				
			||||||
 | 
					         * - miscounted the byte-length for multi-byte site names.
 | 
				
			||||||
 | 
					         * - miscounted the byte-length for multi-byte user names.
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        V1( new MPAlgorithmV1() ),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * bugs:
 | 
				
			||||||
 | 
					         * - miscounted the byte-length for multi-byte user names.
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        V2( new MPAlgorithmV2() ),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * bugs:
 | 
				
			||||||
 | 
					         * - no known issues.
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        V3( new MPAlgorithmV3() );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static final Version CURRENT = V3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private final MPAlgorithm algorithm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Version(final MPAlgorithm algorithm) {
 | 
				
			||||||
 | 
					            this.algorithm = algorithm;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public MPAlgorithm getAlgorithm() {
 | 
				
			||||||
 | 
					            return algorithm;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @JsonCreator
 | 
				
			||||||
 | 
					        public static Version fromInt(final int algorithmVersion) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return values()[algorithmVersion];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @JsonValue
 | 
				
			||||||
 | 
					        public int toInt() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return ordinal();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,6 +18,8 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.lyndir.masterpassword;
 | 
					package com.lyndir.masterpassword;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.fasterxml.jackson.annotation.JsonCreator;
 | 
				
			||||||
 | 
					import com.fasterxml.jackson.annotation.JsonValue;
 | 
				
			||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
					import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
				
			||||||
import java.util.Locale;
 | 
					import java.util.Locale;
 | 
				
			||||||
import javax.annotation.Nullable;
 | 
					import javax.annotation.Nullable;
 | 
				
			||||||
@@ -33,10 +35,12 @@ public enum MPKeyPurpose {
 | 
				
			|||||||
     * Generate a key for authentication.
 | 
					     * Generate a key for authentication.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    Authentication( "authentication", "Generate a key for authentication.", "com.lyndir.masterpassword" ),
 | 
					    Authentication( "authentication", "Generate a key for authentication.", "com.lyndir.masterpassword" ),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Generate a name for identification.
 | 
					     * Generate a name for identification.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    Identification( "identification", "Generate a name for identification.", "com.lyndir.masterpassword.login" ),
 | 
					    Identification( "identification", "Generate a name for identification.", "com.lyndir.masterpassword.login" ),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Generate a recovery token.
 | 
					     * Generate a recovery token.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
@@ -85,11 +89,13 @@ public enum MPKeyPurpose {
 | 
				
			|||||||
        throw logger.bug( "No purpose for name: %s", shortNamePrefix );
 | 
					        throw logger.bug( "No purpose for name: %s", shortNamePrefix );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @JsonCreator
 | 
				
			||||||
    public static MPKeyPurpose forInt(final int keyPurpose) {
 | 
					    public static MPKeyPurpose forInt(final int keyPurpose) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return values()[keyPurpose];
 | 
					        return values()[keyPurpose];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @JsonValue
 | 
				
			||||||
    public int toInt() {
 | 
					    public int toInt() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return ordinal();
 | 
					        return ordinal();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,8 +18,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.lyndir.masterpassword;
 | 
					package com.lyndir.masterpassword;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.fasterxml.jackson.annotation.JsonCreator;
 | 
					 | 
				
			||||||
import com.fasterxml.jackson.annotation.JsonValue;
 | 
					 | 
				
			||||||
import com.google.common.base.Preconditions;
 | 
					import com.google.common.base.Preconditions;
 | 
				
			||||||
import com.google.common.primitives.UnsignedInteger;
 | 
					import com.google.common.primitives.UnsignedInteger;
 | 
				
			||||||
import com.lyndir.lhunath.opal.system.CodeUtils;
 | 
					import com.lyndir.lhunath.opal.system.CodeUtils;
 | 
				
			||||||
@@ -38,15 +36,15 @@ public class MPMasterKey {
 | 
				
			|||||||
    @SuppressWarnings("UnusedDeclaration")
 | 
					    @SuppressWarnings("UnusedDeclaration")
 | 
				
			||||||
    private static final Logger logger = Logger.get( MPMasterKey.class );
 | 
					    private static final Logger logger = Logger.get( MPMasterKey.class );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private final EnumMap<Version, byte[]> keyByVersion = new EnumMap<>( Version.class );
 | 
					    private final EnumMap<MPAlgorithm.Version, byte[]> keyByVersion = new EnumMap<>( MPAlgorithm.Version.class );
 | 
				
			||||||
    private final String                   fullName;
 | 
					    private final String                               fullName;
 | 
				
			||||||
    private final char[]                   masterPassword;
 | 
					    private final char[]                               masterPassword;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private boolean invalidated;
 | 
					    private boolean invalidated;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * @param masterPassword The characters of the user's master password.  Note: this array is held by reference and its contents
 | 
					     * @param masterPassword The characters of the user's master password.
 | 
				
			||||||
     *                       invalidated on {@link #invalidate()}.
 | 
					     *                       Note: this array is held by reference and its contents invalidated on {@link #invalidate()}.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    @SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter")
 | 
					    @SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter")
 | 
				
			||||||
    public MPMasterKey(final String fullName, final char[] masterPassword) {
 | 
					    public MPMasterKey(final String fullName, final char[] masterPassword) {
 | 
				
			||||||
@@ -55,113 +53,6 @@ public class MPMasterKey {
 | 
				
			|||||||
        this.masterPassword = masterPassword;
 | 
					        this.masterPassword = masterPassword;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Derive the master key for a user based on their name and master password.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @throws MPKeyUnavailableException {@link #invalidate()} has been called on this object.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private byte[] masterKey(final MPAlgorithm algorithm)
 | 
					 | 
				
			||||||
            throws MPKeyUnavailableException {
 | 
					 | 
				
			||||||
        Preconditions.checkArgument( masterPassword.length > 0 );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (invalidated)
 | 
					 | 
				
			||||||
            throw new MPKeyUnavailableException();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        byte[] key = keyByVersion.get( algorithm.version() );
 | 
					 | 
				
			||||||
        if (key == null) {
 | 
					 | 
				
			||||||
            logger.trc( "-- mpw_masterKey (algorithm: %s)", algorithm );
 | 
					 | 
				
			||||||
            logger.trc( "fullName: %s", fullName );
 | 
					 | 
				
			||||||
            logger.trc( "masterPassword.id: %s", CodeUtils.encodeHex(
 | 
					 | 
				
			||||||
                    algorithm.toID( algorithm.toBytes( masterPassword ) ) ) );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            keyByVersion.put( algorithm.version(), key = algorithm.masterKey( fullName, masterPassword ) );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return key;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Derive the master key for a user based on their name and master password.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @throws MPKeyUnavailableException {@link #invalidate()} has been called on this object.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private byte[] siteKey(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
 | 
					 | 
				
			||||||
                           @Nullable final String keyContext, final MPAlgorithm algorithm)
 | 
					 | 
				
			||||||
            throws MPKeyUnavailableException {
 | 
					 | 
				
			||||||
        Preconditions.checkArgument( !siteName.isEmpty() );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        byte[] masterKey = masterKey( algorithm );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        logger.trc( "-- mpw_siteKey (algorithm: %s)", algorithm );
 | 
					 | 
				
			||||||
        logger.trc( "siteName: %s", siteName );
 | 
					 | 
				
			||||||
        logger.trc( "siteCounter: %s", siteCounter );
 | 
					 | 
				
			||||||
        logger.trc( "keyPurpose: %d (%s)", keyPurpose.toInt(), keyPurpose.getShortName() );
 | 
					 | 
				
			||||||
        logger.trc( "keyContext: %s", keyContext );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return algorithm.siteKey( masterKey, siteName, siteCounter, keyPurpose, keyContext );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Generate a site result token.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param siteName    A site identifier.
 | 
					 | 
				
			||||||
     * @param siteCounter The result identifier.
 | 
					 | 
				
			||||||
     * @param keyPurpose  The intended purpose for this site result.
 | 
					 | 
				
			||||||
     * @param keyContext  A site-scoped result modifier.
 | 
					 | 
				
			||||||
     * @param resultType  The type of result to generate.
 | 
					 | 
				
			||||||
     * @param resultParam A parameter for the resultType.  For stateful result types, the output of
 | 
					 | 
				
			||||||
     *                    {@link #siteState(String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String, MPAlgorithm)}.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @throws MPKeyUnavailableException {@link #invalidate()} has been called on this object.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public String siteResult(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
 | 
					 | 
				
			||||||
                             @Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam,
 | 
					 | 
				
			||||||
                             final MPAlgorithm algorithm)
 | 
					 | 
				
			||||||
            throws MPKeyUnavailableException {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        byte[] masterKey = masterKey( algorithm );
 | 
					 | 
				
			||||||
        byte[] siteKey   = siteKey( siteName, siteCounter, keyPurpose, keyContext, algorithm );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        logger.trc( "-- mpw_siteResult (algorithm: %s)", algorithm );
 | 
					 | 
				
			||||||
        logger.trc( "resultType: %d (%s)", resultType.getType(), resultType.getShortName() );
 | 
					 | 
				
			||||||
        logger.trc( "resultParam: %s", resultParam );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return algorithm.siteResult(
 | 
					 | 
				
			||||||
                masterKey, siteKey, siteName, siteCounter, keyPurpose, keyContext, resultType, resultParam );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Encrypt a stateful site token for persistence.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param siteName    A site identifier.
 | 
					 | 
				
			||||||
     * @param siteCounter The result identifier.
 | 
					 | 
				
			||||||
     * @param keyPurpose  The intended purpose for the site token.
 | 
					 | 
				
			||||||
     * @param keyContext  A site-scoped key modifier.
 | 
					 | 
				
			||||||
     * @param resultType  The type of result token to encrypt.
 | 
					 | 
				
			||||||
     * @param resultParam The result token desired from
 | 
					 | 
				
			||||||
     *                    {@link #siteResult(String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String, MPAlgorithm)}.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @throws MPKeyUnavailableException {@link #invalidate()} has been called on this object.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public String siteState(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
 | 
					 | 
				
			||||||
                            @Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam,
 | 
					 | 
				
			||||||
                            final MPAlgorithm algorithm)
 | 
					 | 
				
			||||||
            throws MPKeyUnavailableException {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Preconditions.checkNotNull( resultParam );
 | 
					 | 
				
			||||||
        Preconditions.checkArgument( !resultParam.isEmpty() );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        byte[] masterKey = masterKey( algorithm );
 | 
					 | 
				
			||||||
        byte[] siteKey   = siteKey( siteName, siteCounter, keyPurpose, keyContext, algorithm );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        logger.trc( "-- mpw_siteState (algorithm: %s)", algorithm );
 | 
					 | 
				
			||||||
        logger.trc( "resultType: %d (%s)", resultType.getType(), resultType.getShortName() );
 | 
					 | 
				
			||||||
        logger.trc( "resultParam: %d bytes = %s", resultParam.getBytes( algorithm.mpw_charset() ).length, resultParam );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return algorithm.siteState(
 | 
					 | 
				
			||||||
                masterKey, siteKey, siteName, siteCounter, keyPurpose, keyContext, resultType, resultParam );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nonnull
 | 
					    @Nonnull
 | 
				
			||||||
    public String getFullName() {
 | 
					    public String getFullName() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -190,60 +81,101 @@ public class MPMasterKey {
 | 
				
			|||||||
        Arrays.fill( masterPassword, (char) 0 );
 | 
					        Arrays.fill( masterPassword, (char) 0 );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private byte[] masterKey(final MPAlgorithm algorithm)
 | 
				
			||||||
 | 
					            throws MPKeyUnavailableException {
 | 
				
			||||||
 | 
					        Preconditions.checkArgument( masterPassword.length > 0 );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (invalidated)
 | 
				
			||||||
 | 
					            throw new MPKeyUnavailableException();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        byte[] key = keyByVersion.get( algorithm.version() );
 | 
				
			||||||
 | 
					        if (key == null) {
 | 
				
			||||||
 | 
					            logger.trc( "-- mpw_masterKey (algorithm: %s)", algorithm );
 | 
				
			||||||
 | 
					            logger.trc( "fullName: %s", fullName );
 | 
				
			||||||
 | 
					            logger.trc( "masterPassword.id: %s", CodeUtils.encodeHex(
 | 
				
			||||||
 | 
					                    algorithm.toID( algorithm.toBytes( masterPassword ) ) ) );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            keyByVersion.put( algorithm.version(), key = algorithm.masterKey( fullName, masterPassword ) );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return key;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private byte[] siteKey(final String siteName, final MPAlgorithm algorithm, final UnsignedInteger siteCounter,
 | 
				
			||||||
 | 
					                           final MPKeyPurpose keyPurpose, @Nullable final String keyContext)
 | 
				
			||||||
 | 
					            throws MPKeyUnavailableException {
 | 
				
			||||||
 | 
					        Preconditions.checkArgument( !siteName.isEmpty() );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        byte[] masterKey = masterKey( algorithm );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        logger.trc( "-- mpw_siteKey (algorithm: %s)", algorithm );
 | 
				
			||||||
 | 
					        logger.trc( "siteName: %s", siteName );
 | 
				
			||||||
 | 
					        logger.trc( "siteCounter: %s", siteCounter );
 | 
				
			||||||
 | 
					        logger.trc( "keyPurpose: %d (%s)", keyPurpose.toInt(), keyPurpose.getShortName() );
 | 
				
			||||||
 | 
					        logger.trc( "keyContext: %s", keyContext );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return algorithm.siteKey( masterKey, siteName, siteCounter, keyPurpose, keyContext );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * The algorithm iterations.
 | 
					     * Generate a token for use with site.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param siteName    The site's identifier.
 | 
				
			||||||
 | 
					     * @param siteCounter The result's generation.
 | 
				
			||||||
 | 
					     * @param keyPurpose  The intended purpose for the site token.
 | 
				
			||||||
 | 
					     * @param keyContext  The purpose-specific context for this token.
 | 
				
			||||||
 | 
					     * @param resultType  The type of token we're deriving.
 | 
				
			||||||
 | 
					     * @param resultParam Type-specific contextual data for the derivation.
 | 
				
			||||||
 | 
					     *                    In the case of {@link MPResultTypeClass#Stateful} types, the result of
 | 
				
			||||||
 | 
					     *                    {@link #siteState(String, MPAlgorithm, UnsignedInteger, MPKeyPurpose, String, MPResultType, String)}.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws MPKeyUnavailableException {@link #invalidate()} has been called on this object.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public enum Version {
 | 
					    public String siteResult(final String siteName, final MPAlgorithm algorithm, final UnsignedInteger siteCounter,
 | 
				
			||||||
 | 
					                             final MPKeyPurpose keyPurpose, @Nullable final String keyContext,
 | 
				
			||||||
 | 
					                             final MPResultType resultType, @Nullable final String resultParam)
 | 
				
			||||||
 | 
					            throws MPKeyUnavailableException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /**
 | 
					        byte[] masterKey = masterKey( algorithm );
 | 
				
			||||||
         * bugs:
 | 
					        byte[] siteKey   = siteKey( siteName, algorithm, siteCounter, keyPurpose, keyContext );
 | 
				
			||||||
         * - does math with chars whose signedness was platform-dependent.
 | 
					 | 
				
			||||||
         * - miscounted the byte-length for multi-byte site names.
 | 
					 | 
				
			||||||
         * - miscounted the byte-length for multi-byte user names.
 | 
					 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        V0( new MPAlgorithmV0() ),
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /**
 | 
					        logger.trc( "-- mpw_siteResult (algorithm: %s)", algorithm );
 | 
				
			||||||
         * bugs:
 | 
					        logger.trc( "resultType: %d (%s)", resultType.getType(), resultType.getShortName() );
 | 
				
			||||||
         * - miscounted the byte-length for multi-byte site names.
 | 
					        logger.trc( "resultParam: %s", resultParam );
 | 
				
			||||||
         * - miscounted the byte-length for multi-byte user names.
 | 
					 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        V1( new MPAlgorithmV1() ),
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /**
 | 
					        return algorithm.siteResult(
 | 
				
			||||||
         * bugs:
 | 
					                masterKey, siteKey, siteName, siteCounter, keyPurpose, keyContext, resultType, resultParam );
 | 
				
			||||||
         * - miscounted the byte-length for multi-byte user names.
 | 
					    }
 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        V2( new MPAlgorithmV2() ),
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /**
 | 
					    /**
 | 
				
			||||||
         * bugs:
 | 
					     * Encrypt a stateful site token for persistence.
 | 
				
			||||||
         * - no known issues.
 | 
					     *
 | 
				
			||||||
         */
 | 
					     * @param siteName    The site's identifier.
 | 
				
			||||||
        V3( new MPAlgorithmV3() );
 | 
					     * @param siteCounter The result's generation.
 | 
				
			||||||
 | 
					     * @param keyPurpose  The intended purpose for the site token.
 | 
				
			||||||
 | 
					     * @param keyContext  The purpose-specific context for this token.
 | 
				
			||||||
 | 
					     * @param resultType  The type of token we're deriving.
 | 
				
			||||||
 | 
					     * @param resultParam The original token that this method's state should reconstruct when passed into
 | 
				
			||||||
 | 
					     *                    {@link #siteResult(String, MPAlgorithm, UnsignedInteger, MPKeyPurpose, String, MPResultType, String)}.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws MPKeyUnavailableException {@link #invalidate()} has been called on this object.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public String siteState(final String siteName, final MPAlgorithm algorithm, final UnsignedInteger siteCounter,
 | 
				
			||||||
 | 
					                            final MPKeyPurpose keyPurpose, @Nullable final String keyContext,
 | 
				
			||||||
 | 
					                            final MPResultType resultType, @Nullable final String resultParam)
 | 
				
			||||||
 | 
					            throws MPKeyUnavailableException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static final Version CURRENT = V3;
 | 
					        Preconditions.checkNotNull( resultParam );
 | 
				
			||||||
 | 
					        Preconditions.checkArgument( !resultParam.isEmpty() );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private final MPAlgorithm algorithm;
 | 
					        byte[] masterKey = masterKey( algorithm );
 | 
				
			||||||
 | 
					        byte[] siteKey   = siteKey( siteName, algorithm, siteCounter, keyPurpose, keyContext );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Version(final MPAlgorithm algorithm) {
 | 
					        logger.trc( "-- mpw_siteState (algorithm: %s)", algorithm );
 | 
				
			||||||
            this.algorithm = algorithm;
 | 
					        logger.trc( "resultType: %d (%s)", resultType.getType(), resultType.getShortName() );
 | 
				
			||||||
        }
 | 
					        logger.trc( "resultParam: %d bytes = %s", resultParam.getBytes( algorithm.mpw_charset() ).length, resultParam );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public MPAlgorithm getAlgorithm() {
 | 
					        return algorithm.siteState(
 | 
				
			||||||
            return algorithm;
 | 
					                masterKey, siteKey, siteName, siteCounter, keyPurpose, keyContext, resultType, resultParam );
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        @JsonCreator
 | 
					 | 
				
			||||||
        public static Version fromInt(final int algorithmVersion) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return values()[algorithmVersion];
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        @JsonValue
 | 
					 | 
				
			||||||
        public int toInt() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return ordinal();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,7 +16,7 @@
 | 
				
			|||||||
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
 | 
					// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
//==============================================================================
 | 
					//==============================================================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package com.lyndir.masterpassword;
 | 
					package com.lyndir.masterpassword.impl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.google.common.base.*;
 | 
					import com.google.common.base.*;
 | 
				
			||||||
import com.google.common.primitives.Bytes;
 | 
					import com.google.common.primitives.Bytes;
 | 
				
			||||||
@@ -26,6 +26,7 @@ import com.lyndir.lhunath.opal.crypto.CryptUtils;
 | 
				
			|||||||
import com.lyndir.lhunath.opal.system.*;
 | 
					import com.lyndir.lhunath.opal.system.*;
 | 
				
			||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
					import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
				
			||||||
import com.lyndir.lhunath.opal.system.util.ConversionUtils;
 | 
					import com.lyndir.lhunath.opal.system.util.ConversionUtils;
 | 
				
			||||||
 | 
					import com.lyndir.masterpassword.*;
 | 
				
			||||||
import java.nio.*;
 | 
					import java.nio.*;
 | 
				
			||||||
import java.nio.charset.Charset;
 | 
					import java.nio.charset.Charset;
 | 
				
			||||||
import java.security.GeneralSecurityException;
 | 
					import java.security.GeneralSecurityException;
 | 
				
			||||||
@@ -37,12 +38,12 @@ import javax.crypto.IllegalBlockSizeException;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @author lhunath, 2014-08-30
 | 
					 * @author lhunath, 2014-08-30
 | 
				
			||||||
 * @see MPMasterKey.Version#V0
 | 
					 * @see MPAlgorithm.Version#V0
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@SuppressWarnings("NewMethodNamingConvention")
 | 
					@SuppressWarnings("NewMethodNamingConvention")
 | 
				
			||||||
public class MPAlgorithmV0 extends MPAlgorithm {
 | 
					public class MPAlgorithmV0 extends MPAlgorithm {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public final MPMasterKey.Version version = MPMasterKey.Version.V0;
 | 
					    public final Version version = MPAlgorithm.Version.V0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected final Logger logger = Logger.get( getClass() );
 | 
					    protected final Logger logger = Logger.get( getClass() );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -122,19 +123,19 @@ public class MPAlgorithmV0 extends MPAlgorithm {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        switch (resultType.getTypeClass()) {
 | 
					        switch (resultType.getTypeClass()) {
 | 
				
			||||||
            case Template:
 | 
					            case Template:
 | 
				
			||||||
                return sitePasswordFromTemplate( masterKey, siteKey, resultType, resultParam );
 | 
					                return siteResultFromTemplate( masterKey, siteKey, resultType, resultParam );
 | 
				
			||||||
            case Stateful:
 | 
					            case Stateful:
 | 
				
			||||||
                return sitePasswordFromCrypt( masterKey, siteKey, resultType, resultParam );
 | 
					                return siteResultFromState( masterKey, siteKey, resultType, resultParam );
 | 
				
			||||||
            case Derive:
 | 
					            case Derive:
 | 
				
			||||||
                return sitePasswordFromDerive( masterKey, siteKey, resultType, resultParam );
 | 
					                return siteResultFromDerive( masterKey, siteKey, resultType, resultParam );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        throw logger.bug( "Unsupported result type class: %s", resultType.getTypeClass() );
 | 
					        throw logger.bug( "Unsupported result type class: %s", resultType.getTypeClass() );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public String sitePasswordFromTemplate(final byte[] masterKey, final byte[] siteKey,
 | 
					    public String siteResultFromTemplate(final byte[] masterKey, final byte[] siteKey,
 | 
				
			||||||
                                           final MPResultType resultType, @Nullable final String resultParam) {
 | 
					                                         final MPResultType resultType, @Nullable final String resultParam) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        int[] _siteKey = new int[siteKey.length];
 | 
					        int[] _siteKey = new int[siteKey.length];
 | 
				
			||||||
        for (int i = 0; i < siteKey.length; ++i) {
 | 
					        for (int i = 0; i < siteKey.length; ++i) {
 | 
				
			||||||
@@ -168,8 +169,8 @@ public class MPAlgorithmV0 extends MPAlgorithm {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public String sitePasswordFromCrypt(final byte[] masterKey, final byte[] siteKey,
 | 
					    public String siteResultFromState(final byte[] masterKey, final byte[] siteKey,
 | 
				
			||||||
                                        final MPResultType resultType, @Nullable final String resultParam) {
 | 
					                                      final MPResultType resultType, @Nullable final String resultParam) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Preconditions.checkNotNull( resultParam );
 | 
					        Preconditions.checkNotNull( resultParam );
 | 
				
			||||||
        Preconditions.checkArgument( !resultParam.isEmpty() );
 | 
					        Preconditions.checkArgument( !resultParam.isEmpty() );
 | 
				
			||||||
@@ -192,8 +193,8 @@ public class MPAlgorithmV0 extends MPAlgorithm {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public String sitePasswordFromDerive(final byte[] masterKey, final byte[] siteKey,
 | 
					    public String siteResultFromDerive(final byte[] masterKey, final byte[] siteKey,
 | 
				
			||||||
                                         final MPResultType resultType, @Nullable final String resultParam) {
 | 
					                                       final MPResultType resultType, @Nullable final String resultParam) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (resultType == MPResultType.DeriveKey) {
 | 
					        if (resultType == MPResultType.DeriveKey) {
 | 
				
			||||||
            int resultParamInt = ConversionUtils.toIntegerNN( resultParam );
 | 
					            int resultParamInt = ConversionUtils.toIntegerNN( resultParam );
 | 
				
			||||||
@@ -242,8 +243,8 @@ public class MPAlgorithmV0 extends MPAlgorithm {
 | 
				
			|||||||
    // Configuration
 | 
					    // Configuration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public MPMasterKey.Version version() {
 | 
					    public Version version() {
 | 
				
			||||||
        return MPMasterKey.Version.V0;
 | 
					        return MPAlgorithm.Version.V0;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
@@ -16,22 +16,23 @@
 | 
				
			|||||||
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
 | 
					// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
//==============================================================================
 | 
					//==============================================================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package com.lyndir.masterpassword;
 | 
					package com.lyndir.masterpassword.impl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.google.common.base.Preconditions;
 | 
					import com.google.common.base.Preconditions;
 | 
				
			||||||
import com.google.common.primitives.UnsignedBytes;
 | 
					import com.google.common.primitives.UnsignedBytes;
 | 
				
			||||||
 | 
					import com.lyndir.masterpassword.*;
 | 
				
			||||||
import javax.annotation.Nullable;
 | 
					import javax.annotation.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @author lhunath, 2014-08-30
 | 
					 * @author lhunath, 2014-08-30
 | 
				
			||||||
 * @see MPMasterKey.Version#V1
 | 
					 * @see Version#V1
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class MPAlgorithmV1 extends MPAlgorithmV0 {
 | 
					public class MPAlgorithmV1 extends MPAlgorithmV0 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public String sitePasswordFromTemplate(final byte[] masterKey, final byte[] siteKey, final MPResultType resultType,
 | 
					    public String siteResultFromTemplate(final byte[] masterKey, final byte[] siteKey,
 | 
				
			||||||
                                           @Nullable final String resultParam) {
 | 
					                                         final MPResultType resultType, @Nullable final String resultParam) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Determine the template.
 | 
					        // Determine the template.
 | 
				
			||||||
        Preconditions.checkState( siteKey.length > 0 );
 | 
					        Preconditions.checkState( siteKey.length > 0 );
 | 
				
			||||||
@@ -58,7 +59,7 @@ public class MPAlgorithmV1 extends MPAlgorithmV0 {
 | 
				
			|||||||
    // Configuration
 | 
					    // Configuration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public MPMasterKey.Version version() {
 | 
					    public Version version() {
 | 
				
			||||||
        return MPMasterKey.Version.V1;
 | 
					        return MPAlgorithm.Version.V1;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -16,23 +16,25 @@
 | 
				
			|||||||
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
 | 
					// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
//==============================================================================
 | 
					//==============================================================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package com.lyndir.masterpassword;
 | 
					package com.lyndir.masterpassword.impl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.google.common.primitives.Bytes;
 | 
					import com.google.common.primitives.Bytes;
 | 
				
			||||||
import com.google.common.primitives.UnsignedInteger;
 | 
					import com.google.common.primitives.UnsignedInteger;
 | 
				
			||||||
import com.lyndir.lhunath.opal.system.CodeUtils;
 | 
					import com.lyndir.lhunath.opal.system.CodeUtils;
 | 
				
			||||||
 | 
					import com.lyndir.masterpassword.MPAlgorithm;
 | 
				
			||||||
 | 
					import com.lyndir.masterpassword.MPKeyPurpose;
 | 
				
			||||||
import javax.annotation.Nullable;
 | 
					import javax.annotation.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @author lhunath, 2014-08-30
 | 
					 * @author lhunath, 2014-08-30
 | 
				
			||||||
 * @see MPMasterKey.Version#V2
 | 
					 * @see Version#V2
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class MPAlgorithmV2 extends MPAlgorithmV1 {
 | 
					public class MPAlgorithmV2 extends MPAlgorithmV1 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public byte[] siteKey(final byte[] masterKey, final String siteName, UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
 | 
					    public byte[] siteKey(final byte[] masterKey, final String siteName, UnsignedInteger siteCounter,
 | 
				
			||||||
                          @Nullable final String keyContext) {
 | 
					                          final MPKeyPurpose keyPurpose, @Nullable final String keyContext) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        String keyScope = keyPurpose.getScope();
 | 
					        String keyScope = keyPurpose.getScope();
 | 
				
			||||||
        logger.trc( "keyScope: %s", keyScope );
 | 
					        logger.trc( "keyScope: %s", keyScope );
 | 
				
			||||||
@@ -66,7 +68,7 @@ public class MPAlgorithmV2 extends MPAlgorithmV1 {
 | 
				
			|||||||
    // Configuration
 | 
					    // Configuration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public MPMasterKey.Version version() {
 | 
					    public Version version() {
 | 
				
			||||||
        return MPMasterKey.Version.V2;
 | 
					        return MPAlgorithm.Version.V2;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -16,16 +16,18 @@
 | 
				
			|||||||
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
 | 
					// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
//==============================================================================
 | 
					//==============================================================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package com.lyndir.masterpassword;
 | 
					package com.lyndir.masterpassword.impl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.google.common.primitives.Bytes;
 | 
					import com.google.common.primitives.Bytes;
 | 
				
			||||||
import com.lyndir.lhunath.opal.system.CodeUtils;
 | 
					import com.lyndir.lhunath.opal.system.CodeUtils;
 | 
				
			||||||
 | 
					import com.lyndir.masterpassword.MPAlgorithm;
 | 
				
			||||||
 | 
					import com.lyndir.masterpassword.MPKeyPurpose;
 | 
				
			||||||
import java.util.Arrays;
 | 
					import java.util.Arrays;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @author lhunath, 2014-08-30
 | 
					 * @author lhunath, 2014-08-30
 | 
				
			||||||
 * @see MPMasterKey.Version#V3
 | 
					 * @see Version#V3
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class MPAlgorithmV3 extends MPAlgorithmV2 {
 | 
					public class MPAlgorithmV3 extends MPAlgorithmV2 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -59,7 +61,7 @@ public class MPAlgorithmV3 extends MPAlgorithmV2 {
 | 
				
			|||||||
    // Configuration
 | 
					    // Configuration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public MPMasterKey.Version version() {
 | 
					    public Version version() {
 | 
				
			||||||
        return MPMasterKey.Version.V3;
 | 
					        return MPAlgorithm.Version.V3;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					//==============================================================================
 | 
				
			||||||
 | 
					// This file is part of Master Password.
 | 
				
			||||||
 | 
					// Copyright (c) 2011-2017, Maarten Billemont.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Master Password is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Master Password is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You can find a copy of the GNU General Public License in the
 | 
				
			||||||
 | 
					// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					//==============================================================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @author lhunath, 2018-05-15
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@ParametersAreNonnullByDefault
 | 
				
			||||||
 | 
					package com.lyndir.masterpassword.impl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import javax.annotation.ParametersAreNonnullByDefault;
 | 
				
			||||||
@@ -118,8 +118,8 @@ public abstract class MPBasicSite implements MPSite {
 | 
				
			|||||||
            throws MPKeyUnavailableException {
 | 
					            throws MPKeyUnavailableException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return getUser().getMasterKey().siteResult(
 | 
					        return getUser().getMasterKey().siteResult(
 | 
				
			||||||
                getName(), ifNotNullElse( counter, getAlgorithm().mpw_default_counter() ), keyPurpose, keyContext,
 | 
					                getName(), getAlgorithm(), ifNotNullElse( counter, getAlgorithm().mpw_default_counter() ),
 | 
				
			||||||
                type, state, getAlgorithm() );
 | 
					                keyPurpose, keyContext, type, state );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected String getState(final MPKeyPurpose keyPurpose, @Nullable final String keyContext,
 | 
					    protected String getState(final MPKeyPurpose keyPurpose, @Nullable final String keyContext,
 | 
				
			||||||
@@ -127,8 +127,8 @@ public abstract class MPBasicSite implements MPSite {
 | 
				
			|||||||
            throws MPKeyUnavailableException {
 | 
					            throws MPKeyUnavailableException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return getUser().getMasterKey().siteState(
 | 
					        return getUser().getMasterKey().siteState(
 | 
				
			||||||
                getName(), ifNotNullElse( counter, getAlgorithm().mpw_default_counter() ), keyPurpose, keyContext,
 | 
					                getName(), getAlgorithm(), ifNotNullElse( counter, getAlgorithm().mpw_default_counter() ),
 | 
				
			||||||
                type, state, getAlgorithm() );
 | 
					                keyPurpose, keyContext, type, state );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -49,7 +49,7 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
 | 
				
			|||||||
    private MPJSONFile json;
 | 
					    private MPJSONFile json;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public MPFileUser(final String fullName) {
 | 
					    public MPFileUser(final String fullName) {
 | 
				
			||||||
        this( fullName, null, MPMasterKey.Version.CURRENT.getAlgorithm() );
 | 
					        this( fullName, null, MPAlgorithm.Version.CURRENT.getAlgorithm() );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public MPFileUser(final String fullName, @Nullable final byte[] keyID, final MPAlgorithm algorithm) {
 | 
					    public MPFileUser(final String fullName, @Nullable final byte[] keyID, final MPAlgorithm algorithm) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -76,7 +76,7 @@ public class MPFlatUnmarshaller implements MPUnmarshaller {
 | 
				
			|||||||
                    headerStarted = true;
 | 
					                    headerStarted = true;
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
                    // Ends the header.
 | 
					                    // Ends the header.
 | 
				
			||||||
                    user = new MPFileUser( fullName, keyID, MPMasterKey.Version.fromInt( mpVersion ).getAlgorithm(),
 | 
					                    user = new MPFileUser( fullName, keyID, MPAlgorithm.Version.fromInt( mpVersion ).getAlgorithm(),
 | 
				
			||||||
                                           avatar, defaultType, new DateTime( 0 ), MPMarshalFormat.Flat,
 | 
					                                           avatar, defaultType, new DateTime( 0 ), MPMarshalFormat.Flat,
 | 
				
			||||||
                                           clearContent? MPMarshaller.ContentMode.VISIBLE : MPMarshaller.ContentMode.PROTECTED );
 | 
					                                           clearContent? MPMarshaller.ContentMode.VISIBLE : MPMarshaller.ContentMode.PROTECTED );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -117,7 +117,7 @@ public class MPFlatUnmarshaller implements MPUnmarshaller {
 | 
				
			|||||||
                switch (importFormat) {
 | 
					                switch (importFormat) {
 | 
				
			||||||
                    case 0:
 | 
					                    case 0:
 | 
				
			||||||
                        site = new MPFileSite( user, //
 | 
					                        site = new MPFileSite( user, //
 | 
				
			||||||
                                               siteMatcher.group( 5 ), MPMasterKey.Version.fromInt( ConversionUtils.toIntegerNN(
 | 
					                                               siteMatcher.group( 5 ), MPAlgorithm.Version.fromInt( ConversionUtils.toIntegerNN(
 | 
				
			||||||
                                                       colon.matcher( siteMatcher.group( 4 ) ).replaceAll( "" ) ) ).getAlgorithm(),
 | 
					                                                       colon.matcher( siteMatcher.group( 4 ) ).replaceAll( "" ) ) ).getAlgorithm(),
 | 
				
			||||||
                                               user.getAlgorithm().mpw_default_counter(),
 | 
					                                               user.getAlgorithm().mpw_default_counter(),
 | 
				
			||||||
                                               MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
 | 
					                                               MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
 | 
				
			||||||
@@ -130,7 +130,7 @@ public class MPFlatUnmarshaller implements MPUnmarshaller {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    case 1:
 | 
					                    case 1:
 | 
				
			||||||
                        site = new MPFileSite( user, //
 | 
					                        site = new MPFileSite( user, //
 | 
				
			||||||
                                               siteMatcher.group( 7 ), MPMasterKey.Version.fromInt( ConversionUtils.toIntegerNN(
 | 
					                                               siteMatcher.group( 7 ), MPAlgorithm.Version.fromInt( ConversionUtils.toIntegerNN(
 | 
				
			||||||
                                                       colon.matcher( siteMatcher.group( 4 ) ).replaceAll( "" ) ) ).getAlgorithm(),
 | 
					                                                       colon.matcher( siteMatcher.group( 4 ) ).replaceAll( "" ) ) ).getAlgorithm(),
 | 
				
			||||||
                                               UnsignedInteger.valueOf( colon.matcher( siteMatcher.group( 5 ) ).replaceAll( "" ) ),
 | 
					                                               UnsignedInteger.valueOf( colon.matcher( siteMatcher.group( 5 ) ).replaceAll( "" ) ),
 | 
				
			||||||
                                               MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
 | 
					                                               MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -72,8 +72,8 @@ public class MPJSONFile extends MPJSONAnyObject {
 | 
				
			|||||||
                // Clear Text
 | 
					                // Clear Text
 | 
				
			||||||
                content = modelSite.getResult();
 | 
					                content = modelSite.getResult();
 | 
				
			||||||
                loginContent = modelUser.getMasterKey().siteResult(
 | 
					                loginContent = modelUser.getMasterKey().siteResult(
 | 
				
			||||||
                        modelSite.getName(), modelSite.getAlgorithm().mpw_default_counter(),
 | 
					                        modelSite.getName(), modelSite.getAlgorithm(), modelSite.getAlgorithm().mpw_default_counter(),
 | 
				
			||||||
                        MPKeyPurpose.Identification, null, modelSite.getLoginType(), modelSite.getLoginState(), modelSite.getAlgorithm() );
 | 
					                        MPKeyPurpose.Identification, null, modelSite.getLoginType(), modelSite.getLoginState() );
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                // Redacted
 | 
					                // Redacted
 | 
				
			||||||
                if (modelSite.getResultType().supportsTypeFeature( MPSiteFeature.ExportContent ))
 | 
					                if (modelSite.getResultType().supportsTypeFeature( MPSiteFeature.ExportContent ))
 | 
				
			||||||
@@ -134,7 +134,7 @@ public class MPJSONFile extends MPJSONAnyObject {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public MPFileUser read(@Nullable final char[] masterPassword)
 | 
					    public MPFileUser read(@Nullable final char[] masterPassword)
 | 
				
			||||||
            throws MPIncorrectMasterPasswordException, MPKeyUnavailableException {
 | 
					            throws MPIncorrectMasterPasswordException, MPKeyUnavailableException {
 | 
				
			||||||
        MPAlgorithm algorithm = ifNotNullElse( user.algorithm, MPMasterKey.Version.CURRENT ).getAlgorithm();
 | 
					        MPAlgorithm algorithm = ifNotNullElse( user.algorithm, MPAlgorithm.Version.CURRENT ).getAlgorithm();
 | 
				
			||||||
        MPFileUser model = new MPFileUser(
 | 
					        MPFileUser model = new MPFileUser(
 | 
				
			||||||
                user.full_name, CodeUtils.decodeHex( user.key_id ), algorithm, user.avatar,
 | 
					                user.full_name, CodeUtils.decodeHex( user.key_id ), algorithm, user.avatar,
 | 
				
			||||||
                (user.default_type != null)? user.default_type: algorithm.mpw_default_result_type(),
 | 
					                (user.default_type != null)? user.default_type: algorithm.mpw_default_result_type(),
 | 
				
			||||||
@@ -192,7 +192,7 @@ public class MPJSONFile extends MPJSONAnyObject {
 | 
				
			|||||||
        @Nullable
 | 
					        @Nullable
 | 
				
			||||||
        String              key_id;
 | 
					        String              key_id;
 | 
				
			||||||
        @Nullable
 | 
					        @Nullable
 | 
				
			||||||
        MPMasterKey.Version algorithm;
 | 
					        MPAlgorithm.Version algorithm;
 | 
				
			||||||
        @Nullable
 | 
					        @Nullable
 | 
				
			||||||
        MPResultType        default_type;
 | 
					        MPResultType        default_type;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -203,7 +203,7 @@ public class MPJSONFile extends MPJSONAnyObject {
 | 
				
			|||||||
        @Nullable
 | 
					        @Nullable
 | 
				
			||||||
        MPResultType type;
 | 
					        MPResultType type;
 | 
				
			||||||
        long                counter;
 | 
					        long                counter;
 | 
				
			||||||
        MPMasterKey.Version algorithm;
 | 
					        MPAlgorithm.Version algorithm;
 | 
				
			||||||
        @Nullable
 | 
					        @Nullable
 | 
				
			||||||
        String       password;
 | 
					        String       password;
 | 
				
			||||||
        @Nullable
 | 
					        @Nullable
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -168,17 +168,13 @@ public class MPTestSuite implements Callable<Boolean> {
 | 
				
			|||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public Boolean call()
 | 
					    public Boolean call()
 | 
				
			||||||
            throws Exception {
 | 
					            throws Exception {
 | 
				
			||||||
        return forEach( "mpw", new TestCase() {
 | 
					        return forEach( "mpw", testCase -> {
 | 
				
			||||||
            @Override
 | 
					            MPMasterKey masterKey = new MPMasterKey( testCase.getFullName(), testCase.getMasterPassword().toCharArray() );
 | 
				
			||||||
            public boolean run(final MPTests.Case testCase)
 | 
					            String sitePassword = masterKey.siteResult( testCase.getSiteName(), testCase.getAlgorithm(), testCase.getSiteCounter(),
 | 
				
			||||||
                    throws Exception {
 | 
					                                                        testCase.getKeyPurpose(), testCase.getKeyContext(),
 | 
				
			||||||
                MPMasterKey masterKey = new MPMasterKey( testCase.getFullName(), testCase.getMasterPassword().toCharArray() );
 | 
					                                                        testCase.getResultType(), null );
 | 
				
			||||||
                String sitePassword = masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
 | 
					 | 
				
			||||||
                                                            testCase.getKeyContext(), testCase.getResultType(),
 | 
					 | 
				
			||||||
                                                            null, testCase.getAlgorithm() );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                return testCase.getResult().equals( sitePassword );
 | 
					            return testCase.getResult().equals( sitePassword );
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } );
 | 
					        } );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -192,12 +188,14 @@ public class MPTestSuite implements Callable<Boolean> {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @FunctionalInterface
 | 
				
			||||||
    public interface Listener {
 | 
					    public interface Listener {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        void progress(int current, int max, String messageFormat, Object... args);
 | 
					        void progress(int current, int max, String messageFormat, Object... args);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @FunctionalInterface
 | 
				
			||||||
    public interface TestCase {
 | 
					    public interface TestCase {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        boolean run(MPTests.Case testCase)
 | 
					        boolean run(MPTests.Case testCase)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -176,7 +176,7 @@ public class MPTests {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        @Nonnull
 | 
					        @Nonnull
 | 
				
			||||||
        public MPAlgorithm getAlgorithm() {
 | 
					        public MPAlgorithm getAlgorithm() {
 | 
				
			||||||
            return MPMasterKey.Version.fromInt( checkNotNull( algorithm ) ).getAlgorithm();
 | 
					            return MPAlgorithm.Version.fromInt( checkNotNull( algorithm ) ).getAlgorithm();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @Nonnull
 | 
					        @Nonnull
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -45,34 +45,30 @@ public class MPMasterKeyTest {
 | 
				
			|||||||
    public void testMasterKey()
 | 
					    public void testMasterKey()
 | 
				
			||||||
            throws Exception {
 | 
					            throws Exception {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        testSuite.forEach( "testMasterKey", new MPTestSuite.TestCase() {
 | 
					        testSuite.forEach( "testMasterKey", testCase -> {
 | 
				
			||||||
            @Override
 | 
					            char[]      masterPassword = testCase.getMasterPassword().toCharArray();
 | 
				
			||||||
            public boolean run(final MPTests.Case testCase)
 | 
					            MPMasterKey masterKey      = new MPMasterKey( testCase.getFullName(), masterPassword );
 | 
				
			||||||
                    throws Exception {
 | 
					 | 
				
			||||||
                char[]      masterPassword = testCase.getMasterPassword().toCharArray();
 | 
					 | 
				
			||||||
                MPMasterKey masterKey      = new MPMasterKey( testCase.getFullName(), masterPassword );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // Test key
 | 
					            // Test key
 | 
				
			||||||
                assertEquals(
 | 
					            assertEquals(
 | 
				
			||||||
                        CodeUtils.encodeHex( masterKey.getKeyID( testCase.getAlgorithm() ) ),
 | 
					                    CodeUtils.encodeHex( masterKey.getKeyID( testCase.getAlgorithm() ) ),
 | 
				
			||||||
                        testCase.getKeyID(),
 | 
					                    testCase.getKeyID(),
 | 
				
			||||||
                        "[testMasterKey] keyID mismatch: " + testCase );
 | 
					                    "[testMasterKey] keyID mismatch: " + testCase );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // Test invalidation
 | 
					            // Test invalidation
 | 
				
			||||||
                masterKey.invalidate();
 | 
					            masterKey.invalidate();
 | 
				
			||||||
                try {
 | 
					            try {
 | 
				
			||||||
                    masterKey.getKeyID( testCase.getAlgorithm() );
 | 
					                masterKey.getKeyID( testCase.getAlgorithm() );
 | 
				
			||||||
                    fail( "[testMasterKey] invalidate ineffective: " + testCase );
 | 
					                fail( "[testMasterKey] invalidate ineffective: " + testCase );
 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                catch (final MPKeyUnavailableException ignored) {
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                assertNotEquals(
 | 
					 | 
				
			||||||
                        masterPassword,
 | 
					 | 
				
			||||||
                        testCase.getMasterPassword().toCharArray(),
 | 
					 | 
				
			||||||
                        "[testMasterKey] masterPassword not wiped: " + testCase );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                return true;
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            catch (final MPKeyUnavailableException ignored) {
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            assertNotEquals(
 | 
				
			||||||
 | 
					                    masterPassword,
 | 
				
			||||||
 | 
					                    testCase.getMasterPassword().toCharArray(),
 | 
				
			||||||
 | 
					                    "[testMasterKey] masterPassword not wiped: " + testCase );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
        } );
 | 
					        } );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -80,23 +76,20 @@ public class MPMasterKeyTest {
 | 
				
			|||||||
    public void testSiteResult()
 | 
					    public void testSiteResult()
 | 
				
			||||||
            throws Exception {
 | 
					            throws Exception {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        testSuite.forEach( "testSiteResult", new MPTestSuite.TestCase() {
 | 
					        testSuite.forEach( "testSiteResult", testCase -> {
 | 
				
			||||||
            @Override
 | 
					            char[]      masterPassword = testCase.getMasterPassword().toCharArray();
 | 
				
			||||||
            public boolean run(final MPTests.Case testCase)
 | 
					            MPMasterKey masterKey      = new MPMasterKey( testCase.getFullName(), masterPassword );
 | 
				
			||||||
                    throws Exception {
 | 
					 | 
				
			||||||
                char[]      masterPassword = testCase.getMasterPassword().toCharArray();
 | 
					 | 
				
			||||||
                MPMasterKey masterKey      = new MPMasterKey( testCase.getFullName(), masterPassword );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // Test site result
 | 
					            // Test site result
 | 
				
			||||||
                assertEquals(
 | 
					            assertEquals(
 | 
				
			||||||
                        masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
 | 
					                    masterKey.siteResult( testCase.getSiteName(), testCase.getAlgorithm(), testCase.getSiteCounter(),
 | 
				
			||||||
                                              testCase.getKeyContext(), testCase.getResultType(),
 | 
					                                          testCase.getKeyPurpose(),
 | 
				
			||||||
                                              null, testCase.getAlgorithm() ),
 | 
					                                          testCase.getKeyContext(), testCase.getResultType(),
 | 
				
			||||||
                        testCase.getResult(),
 | 
					                                          null ),
 | 
				
			||||||
                        "[testSiteResult] result mismatch: " + testCase );
 | 
					                    testCase.getResult(),
 | 
				
			||||||
 | 
					                    "[testSiteResult] result mismatch: " + testCase );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                return true;
 | 
					            return true;
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } );
 | 
					        } );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -110,14 +103,14 @@ public class MPMasterKeyTest {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        String       password   = randomString( 8 );
 | 
					        String       password   = randomString( 8 );
 | 
				
			||||||
        MPResultType resultType = MPResultType.StoredPersonal;
 | 
					        MPResultType resultType = MPResultType.StoredPersonal;
 | 
				
			||||||
        for (final MPMasterKey.Version version : MPMasterKey.Version.values()) {
 | 
					        for (final MPAlgorithm.Version version : MPAlgorithm.Version.values()) {
 | 
				
			||||||
            MPAlgorithm algorithm = version.getAlgorithm();
 | 
					            MPAlgorithm algorithm = version.getAlgorithm();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Test site state
 | 
					            // Test site state
 | 
				
			||||||
            String state = masterKey.siteState( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
 | 
					            String state = masterKey.siteState( testCase.getSiteName(), algorithm, testCase.getSiteCounter(), testCase.getKeyPurpose(),
 | 
				
			||||||
                                                testCase.getKeyContext(), resultType, password, algorithm );
 | 
					                                                testCase.getKeyContext(), resultType, password );
 | 
				
			||||||
            String result = masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
 | 
					            String result = masterKey.siteResult( testCase.getSiteName(), algorithm, testCase.getSiteCounter(), testCase.getKeyPurpose(),
 | 
				
			||||||
                                                  testCase.getKeyContext(), resultType, state, algorithm );
 | 
					                                                  testCase.getKeyContext(), resultType, state );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            assertEquals(
 | 
					            assertEquals(
 | 
				
			||||||
                    result,
 | 
					                    result,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -58,7 +58,7 @@ public class EmergencyActivity extends Activity {
 | 
				
			|||||||
            Executors.newSingleThreadExecutor() );
 | 
					            Executors.newSingleThreadExecutor() );
 | 
				
			||||||
    private final ImmutableList<MPResultType>        allResultTypes = ImmutableList.copyOf(
 | 
					    private final ImmutableList<MPResultType>        allResultTypes = ImmutableList.copyOf(
 | 
				
			||||||
            MPResultType.forClass( MPResultTypeClass.Template ) );
 | 
					            MPResultType.forClass( MPResultTypeClass.Template ) );
 | 
				
			||||||
    private final ImmutableList<MPMasterKey.Version> allVersions    = ImmutableList.copyOf( MPMasterKey.Version.values() );
 | 
					    private final ImmutableList<MPAlgorithm.Version> allVersions    = ImmutableList.copyOf( MPAlgorithm.Version.values() );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Nullable
 | 
					    @Nullable
 | 
				
			||||||
    private MPMasterKey masterKey;
 | 
					    private MPMasterKey masterKey;
 | 
				
			||||||
@@ -160,7 +160,7 @@ public class EmergencyActivity extends Activity {
 | 
				
			|||||||
            @Override
 | 
					            @Override
 | 
				
			||||||
            public void onClick(final View v) {
 | 
					            public void onClick(final View v) {
 | 
				
			||||||
                @SuppressWarnings("SuspiciousMethodCalls")
 | 
					                @SuppressWarnings("SuspiciousMethodCalls")
 | 
				
			||||||
                MPMasterKey.Version siteVersion =
 | 
					                MPAlgorithm.Version siteVersion =
 | 
				
			||||||
                        allVersions.get( (allVersions.indexOf( siteVersionButton.getTag() ) + 1) % allVersions.size() );
 | 
					                        allVersions.get( (allVersions.indexOf( siteVersionButton.getTag() ) + 1) % allVersions.size() );
 | 
				
			||||||
                preferences.setDefaultVersion( siteVersion );
 | 
					                preferences.setDefaultVersion( siteVersion );
 | 
				
			||||||
                siteVersionButton.setTag( siteVersion );
 | 
					                siteVersionButton.setTag( siteVersion );
 | 
				
			||||||
@@ -227,7 +227,7 @@ public class EmergencyActivity extends Activity {
 | 
				
			|||||||
        MPResultType defaultResultType = preferences.getDefaultResultType();
 | 
					        MPResultType defaultResultType = preferences.getDefaultResultType();
 | 
				
			||||||
        resultTypeButton.setTag( defaultResultType );
 | 
					        resultTypeButton.setTag( defaultResultType );
 | 
				
			||||||
        resultTypeButton.setText( defaultResultType.getShortName() );
 | 
					        resultTypeButton.setText( defaultResultType.getShortName() );
 | 
				
			||||||
        MPMasterKey.Version defaultVersion = preferences.getDefaultVersion();
 | 
					        MPAlgorithm.Version defaultVersion = preferences.getDefaultVersion();
 | 
				
			||||||
        siteVersionButton.setTag( defaultVersion );
 | 
					        siteVersionButton.setTag( defaultVersion );
 | 
				
			||||||
        siteVersionButton.setText( defaultVersion.name() );
 | 
					        siteVersionButton.setText( defaultVersion.name() );
 | 
				
			||||||
        siteCounterButton.setText( MessageFormat.format( "{0}", 1 ) );
 | 
					        siteCounterButton.setText( MessageFormat.format( "{0}", 1 ) );
 | 
				
			||||||
@@ -289,7 +289,7 @@ public class EmergencyActivity extends Activity {
 | 
				
			|||||||
        final String              siteName = siteNameField.getText().toString();
 | 
					        final String              siteName = siteNameField.getText().toString();
 | 
				
			||||||
        final MPResultType        type     = (MPResultType) resultTypeButton.getTag();
 | 
					        final MPResultType        type     = (MPResultType) resultTypeButton.getTag();
 | 
				
			||||||
        final UnsignedInteger     counter  = UnsignedInteger.valueOf( siteCounterButton.getText().toString() );
 | 
					        final UnsignedInteger     counter  = UnsignedInteger.valueOf( siteCounterButton.getText().toString() );
 | 
				
			||||||
        final MPMasterKey.Version version  = (MPMasterKey.Version) siteVersionButton.getTag();
 | 
					        final MPAlgorithm.Version version  = (MPAlgorithm.Version) siteVersionButton.getTag();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ((masterKey == null) || siteName.isEmpty() || (type == null)) {
 | 
					        if ((masterKey == null) || siteName.isEmpty() || (type == null)) {
 | 
				
			||||||
            sitePasswordField.setText( "" );
 | 
					            sitePasswordField.setText( "" );
 | 
				
			||||||
@@ -306,8 +306,8 @@ public class EmergencyActivity extends Activity {
 | 
				
			|||||||
            @Override
 | 
					            @Override
 | 
				
			||||||
            public void run() {
 | 
					            public void run() {
 | 
				
			||||||
                try {
 | 
					                try {
 | 
				
			||||||
                    sitePassword = masterKey.siteResult( siteName, counter, MPKeyPurpose.Authentication, null, type, null,
 | 
					                    sitePassword = masterKey.siteResult( siteName, version.getAlgorithm(), counter, MPKeyPurpose.Authentication, null, type, null
 | 
				
			||||||
                                                         version.getAlgorithm() );
 | 
					                    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    runOnUiThread( new Runnable() {
 | 
					                    runOnUiThread( new Runnable() {
 | 
				
			||||||
                        @Override
 | 
					                        @Override
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -151,7 +151,7 @@ public final class Preferences {
 | 
				
			|||||||
        return MPResultType.values()[prefs().getInt( PREF_RESULT_TYPE, getDefaultVersion().getAlgorithm().mpw_default_result_type().ordinal() )];
 | 
					        return MPResultType.values()[prefs().getInt( PREF_RESULT_TYPE, getDefaultVersion().getAlgorithm().mpw_default_result_type().ordinal() )];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public boolean setDefaultVersion(final MPMasterKey.Version value) {
 | 
					    public boolean setDefaultVersion(final MPAlgorithm.Version value) {
 | 
				
			||||||
        if (getDefaultVersion() == value)
 | 
					        if (getDefaultVersion() == value)
 | 
				
			||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -160,7 +160,7 @@ public final class Preferences {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Nonnull
 | 
					    @Nonnull
 | 
				
			||||||
    public MPMasterKey.Version getDefaultVersion() {
 | 
					    public MPAlgorithm.Version getDefaultVersion() {
 | 
				
			||||||
        return MPMasterKey.Version.values()[prefs().getInt( PREF_ALGORITHM_VERSION, MPMasterKey.Version.CURRENT.ordinal() )];
 | 
					        return MPAlgorithm.Version.values()[prefs().getInt( PREF_ALGORITHM_VERSION, MPAlgorithm.Version.CURRENT.ordinal() )];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -112,14 +112,11 @@ public class GUI implements UnlockFrame.SignInCallback {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected void open() {
 | 
					    protected void open() {
 | 
				
			||||||
        SwingUtilities.invokeLater( new Runnable() {
 | 
					        SwingUtilities.invokeLater( () -> {
 | 
				
			||||||
            @Override
 | 
					            if (passwordFrame == null)
 | 
				
			||||||
            public void run() {
 | 
					                unlockFrame.setVisible( true );
 | 
				
			||||||
                if (passwordFrame == null)
 | 
					            else
 | 
				
			||||||
                    unlockFrame.setVisible( true );
 | 
					                passwordFrame.setVisible( true );
 | 
				
			||||||
                else
 | 
					 | 
				
			||||||
                    passwordFrame.setVisible( true );
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } );
 | 
					        } );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -60,15 +60,12 @@ public abstract class Res {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static Future<?> schedule(final Window host, final Runnable job, final long delay, final TimeUnit timeUnit) {
 | 
					    public static Future<?> schedule(final Window host, final Runnable job, final long delay, final TimeUnit timeUnit) {
 | 
				
			||||||
        return getExecutor( host ).schedule( new Runnable() {
 | 
					        return getExecutor( host ).schedule( () -> {
 | 
				
			||||||
            @Override
 | 
					            try {
 | 
				
			||||||
            public void run() {
 | 
					                job.run();
 | 
				
			||||||
                try {
 | 
					            }
 | 
				
			||||||
                    job.run();
 | 
					            catch (final Throwable t) {
 | 
				
			||||||
                }
 | 
					                logger.err( t, "Unexpected: %s", t.getLocalizedMessage() );
 | 
				
			||||||
                catch (final Throwable t) {
 | 
					 | 
				
			||||||
                    logger.err( t, "Unexpected: %s", t.getLocalizedMessage() );
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }, delay, timeUnit );
 | 
					        }, delay, timeUnit );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -79,17 +76,13 @@ public abstract class Res {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public static <V> ListenableFuture<V> schedule(final Window host, final Callable<V> job, final long delay, final TimeUnit timeUnit) {
 | 
					    public static <V> ListenableFuture<V> schedule(final Window host, final Callable<V> job, final long delay, final TimeUnit timeUnit) {
 | 
				
			||||||
        ScheduledExecutorService executor = getExecutor( host );
 | 
					        ScheduledExecutorService executor = getExecutor( host );
 | 
				
			||||||
        return JdkFutureAdapters.listenInPoolThread( executor.schedule( new Callable<V>() {
 | 
					        return JdkFutureAdapters.listenInPoolThread( executor.schedule( () -> {
 | 
				
			||||||
            @Override
 | 
					            try {
 | 
				
			||||||
            public V call()
 | 
					                return job.call();
 | 
				
			||||||
                    throws Exception {
 | 
					            }
 | 
				
			||||||
                try {
 | 
					            catch (final Throwable t) {
 | 
				
			||||||
                    return job.call();
 | 
					                logger.err( t, "Unexpected: %s", t.getLocalizedMessage() );
 | 
				
			||||||
                }
 | 
					                throw Throwables.propagate( t );
 | 
				
			||||||
                catch (final Throwable t) {
 | 
					 | 
				
			||||||
                    logger.err( t, "Unexpected: %s", t.getLocalizedMessage() );
 | 
					 | 
				
			||||||
                    throw Throwables.propagate( t );
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }, delay, timeUnit ), executor );
 | 
					        }, delay, timeUnit ), executor );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,17 +31,17 @@ import javax.annotation.Nullable;
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * @author lhunath, 14-12-16
 | 
					 * @author lhunath, 14-12-16
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class IncognitoSite extends MPBasicSite {
 | 
					public class MPIncognitoSite extends MPBasicSite {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private final IncognitoUser user;
 | 
					    private final MPIncognitoUser user;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public IncognitoSite(final IncognitoUser user, final String name) {
 | 
					    public MPIncognitoSite(final MPIncognitoUser user, final String name) {
 | 
				
			||||||
        this( user, name, null, null, null, null );
 | 
					        this( user, name, null, null, null, null );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public IncognitoSite(final IncognitoUser user, final String name,
 | 
					    public MPIncognitoSite(final MPIncognitoUser user, final String name,
 | 
				
			||||||
                         @Nullable final MPAlgorithm algorithm, @Nullable final UnsignedInteger counter,
 | 
					                           @Nullable final MPAlgorithm algorithm, @Nullable final UnsignedInteger counter,
 | 
				
			||||||
                         @Nullable final MPResultType resultType, @Nullable final MPResultType loginType) {
 | 
					                           @Nullable final MPResultType resultType, @Nullable final MPResultType loginType) {
 | 
				
			||||||
        super( name, (algorithm == null)? user.getAlgorithm(): algorithm, counter, resultType, loginType );
 | 
					        super( name, (algorithm == null)? user.getAlgorithm(): algorithm, counter, resultType, loginType );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.user = user;
 | 
					        this.user = user;
 | 
				
			||||||
@@ -18,7 +18,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.lyndir.masterpassword.gui.model;
 | 
					package com.lyndir.masterpassword.gui.model;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.lyndir.masterpassword.MPMasterKey;
 | 
					import com.lyndir.masterpassword.MPAlgorithm;
 | 
				
			||||||
import com.lyndir.masterpassword.model.impl.MPBasicUser;
 | 
					import com.lyndir.masterpassword.model.impl.MPBasicUser;
 | 
				
			||||||
import javax.annotation.Nullable;
 | 
					import javax.annotation.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -26,10 +26,10 @@ import javax.annotation.Nullable;
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * @author lhunath, 2014-06-08
 | 
					 * @author lhunath, 2014-06-08
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class IncognitoUser extends MPBasicUser<IncognitoSite> {
 | 
					public class MPIncognitoUser extends MPBasicUser<MPIncognitoSite> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public IncognitoUser(final String fullName) {
 | 
					    public MPIncognitoUser(final String fullName) {
 | 
				
			||||||
        super(fullName, MPMasterKey.Version.CURRENT.getAlgorithm());
 | 
					        super( fullName, MPAlgorithm.Version.CURRENT.getAlgorithm());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Nullable
 | 
					    @Nullable
 | 
				
			||||||
@@ -23,8 +23,8 @@ import com.google.common.primitives.UnsignedInteger;
 | 
				
			|||||||
import com.lyndir.masterpassword.MPAlgorithm;
 | 
					import com.lyndir.masterpassword.MPAlgorithm;
 | 
				
			||||||
import com.lyndir.masterpassword.MPResultType;
 | 
					import com.lyndir.masterpassword.MPResultType;
 | 
				
			||||||
import com.lyndir.masterpassword.gui.Res;
 | 
					import com.lyndir.masterpassword.gui.Res;
 | 
				
			||||||
import com.lyndir.masterpassword.gui.model.IncognitoSite;
 | 
					import com.lyndir.masterpassword.gui.model.MPIncognitoSite;
 | 
				
			||||||
import com.lyndir.masterpassword.gui.model.IncognitoUser;
 | 
					import com.lyndir.masterpassword.gui.model.MPIncognitoUser;
 | 
				
			||||||
import com.lyndir.masterpassword.gui.util.Components;
 | 
					import com.lyndir.masterpassword.gui.util.Components;
 | 
				
			||||||
import java.awt.*;
 | 
					import java.awt.*;
 | 
				
			||||||
import java.awt.event.ActionEvent;
 | 
					import java.awt.event.ActionEvent;
 | 
				
			||||||
@@ -40,7 +40,7 @@ import javax.swing.event.DocumentListener;
 | 
				
			|||||||
 * @author lhunath, 2014-06-11
 | 
					 * @author lhunath, 2014-06-11
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@SuppressWarnings({ "serial", "MagicNumber" })
 | 
					@SuppressWarnings({ "serial", "MagicNumber" })
 | 
				
			||||||
public class IncognitoAuthenticationPanel extends AuthenticationPanel<IncognitoUser> implements DocumentListener, ActionListener {
 | 
					public class IncognitoAuthenticationPanel extends AuthenticationPanel<MPIncognitoUser> implements DocumentListener, ActionListener {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private final JTextField     fullNameField;
 | 
					    private final JTextField     fullNameField;
 | 
				
			||||||
    private final JPasswordField masterPasswordField;
 | 
					    private final JPasswordField masterPasswordField;
 | 
				
			||||||
@@ -82,20 +82,20 @@ public class IncognitoAuthenticationPanel extends AuthenticationPanel<IncognitoU
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public PasswordFrame<IncognitoUser, ?> newPasswordFrame() {
 | 
					    public PasswordFrame<MPIncognitoUser, ?> newPasswordFrame() {
 | 
				
			||||||
        return new PasswordFrame<IncognitoUser, IncognitoSite>( Preconditions.checkNotNull( getSelectedUser() ) ) {
 | 
					        return new PasswordFrame<MPIncognitoUser, MPIncognitoSite>( Preconditions.checkNotNull( getSelectedUser() ) ) {
 | 
				
			||||||
            @Override
 | 
					            @Override
 | 
				
			||||||
            protected IncognitoSite createSite(final IncognitoUser user, final String siteName, final UnsignedInteger siteCounter,
 | 
					            protected MPIncognitoSite createSite(final MPIncognitoUser user, final String siteName, final UnsignedInteger siteCounter,
 | 
				
			||||||
                                               final MPResultType resultType, final MPAlgorithm algorithm) {
 | 
					                                                 final MPResultType resultType, final MPAlgorithm algorithm) {
 | 
				
			||||||
                return new IncognitoSite( user, siteName, algorithm, siteCounter, resultType, null );
 | 
					                return new MPIncognitoSite( user, siteName, algorithm, siteCounter, resultType, null );
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Nullable
 | 
					    @Nullable
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    protected IncognitoUser getSelectedUser() {
 | 
					    protected MPIncognitoUser getSelectedUser() {
 | 
				
			||||||
        return new IncognitoUser( fullNameField.getText() );
 | 
					        return new MPIncognitoUser( fullNameField.getText() );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Nonnull
 | 
					    @Nonnull
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,8 +21,6 @@ package com.lyndir.masterpassword.gui.view;
 | 
				
			|||||||
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
 | 
					import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
 | 
				
			||||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
 | 
					import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.google.common.base.Predicate;
 | 
					 | 
				
			||||||
import com.google.common.collect.FluentIterable;
 | 
					 | 
				
			||||||
import com.google.common.collect.Iterables;
 | 
					import com.google.common.collect.Iterables;
 | 
				
			||||||
import com.google.common.primitives.UnsignedInteger;
 | 
					import com.google.common.primitives.UnsignedInteger;
 | 
				
			||||||
import com.google.common.util.concurrent.*;
 | 
					import com.google.common.util.concurrent.*;
 | 
				
			||||||
@@ -30,19 +28,19 @@ import com.lyndir.masterpassword.*;
 | 
				
			|||||||
import com.lyndir.masterpassword.gui.Res;
 | 
					import com.lyndir.masterpassword.gui.Res;
 | 
				
			||||||
import com.lyndir.masterpassword.gui.util.Components;
 | 
					import com.lyndir.masterpassword.gui.util.Components;
 | 
				
			||||||
import com.lyndir.masterpassword.gui.util.UnsignedIntegerModel;
 | 
					import com.lyndir.masterpassword.gui.util.UnsignedIntegerModel;
 | 
				
			||||||
import com.lyndir.masterpassword.model.*;
 | 
					import com.lyndir.masterpassword.model.MPUser;
 | 
				
			||||||
import com.lyndir.masterpassword.model.impl.MPBasicSite;
 | 
					import com.lyndir.masterpassword.model.impl.*;
 | 
				
			||||||
import com.lyndir.masterpassword.model.impl.MPFileSite;
 | 
					 | 
				
			||||||
import com.lyndir.masterpassword.model.impl.MPFileUser;
 | 
					 | 
				
			||||||
import java.awt.*;
 | 
					import java.awt.*;
 | 
				
			||||||
import java.awt.datatransfer.StringSelection;
 | 
					import java.awt.datatransfer.StringSelection;
 | 
				
			||||||
import java.awt.datatransfer.Transferable;
 | 
					import java.awt.datatransfer.Transferable;
 | 
				
			||||||
import java.awt.event.*;
 | 
					import java.awt.event.*;
 | 
				
			||||||
import java.util.concurrent.Callable;
 | 
					import java.util.Collection;
 | 
				
			||||||
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
import javax.annotation.Nonnull;
 | 
					import javax.annotation.Nonnull;
 | 
				
			||||||
import javax.annotation.Nullable;
 | 
					import javax.annotation.Nullable;
 | 
				
			||||||
import javax.swing.*;
 | 
					import javax.swing.*;
 | 
				
			||||||
import javax.swing.event.*;
 | 
					import javax.swing.event.DocumentEvent;
 | 
				
			||||||
 | 
					import javax.swing.event.DocumentListener;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -54,7 +52,7 @@ public abstract class PasswordFrame<U extends MPUser<S>, S extends MPBasicSite>
 | 
				
			|||||||
    private final Components.GradientPanel       root;
 | 
					    private final Components.GradientPanel       root;
 | 
				
			||||||
    private final JTextField                     siteNameField;
 | 
					    private final JTextField                     siteNameField;
 | 
				
			||||||
    private final JButton                        siteActionButton;
 | 
					    private final JButton                        siteActionButton;
 | 
				
			||||||
    private final JComboBox<MPMasterKey.Version> siteVersionField;
 | 
					    private final JComboBox<MPAlgorithm.Version> siteVersionField;
 | 
				
			||||||
    private final JSpinner                       siteCounterField;
 | 
					    private final JSpinner                       siteCounterField;
 | 
				
			||||||
    private final UnsignedIntegerModel           siteCounterModel;
 | 
					    private final UnsignedIntegerModel           siteCounterModel;
 | 
				
			||||||
    private final JComboBox<MPResultType>        resultTypeField;
 | 
					    private final JComboBox<MPResultType>        resultTypeField;
 | 
				
			||||||
@@ -105,14 +103,11 @@ public abstract class PasswordFrame<U extends MPUser<S>, S extends MPBasicSite>
 | 
				
			|||||||
                        Transferable clipboardContents = new StringSelection( sitePassword );
 | 
					                        Transferable clipboardContents = new StringSelection( sitePassword );
 | 
				
			||||||
                        Toolkit.getDefaultToolkit().getSystemClipboard().setContents( clipboardContents, null );
 | 
					                        Toolkit.getDefaultToolkit().getSystemClipboard().setContents( clipboardContents, null );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        SwingUtilities.invokeLater( new Runnable() {
 | 
					                        SwingUtilities.invokeLater( () -> {
 | 
				
			||||||
                            @Override
 | 
					                            passwordField.setText( null );
 | 
				
			||||||
                            public void run() {
 | 
					                            siteNameField.setText( null );
 | 
				
			||||||
                                passwordField.setText( null );
 | 
					 | 
				
			||||||
                                siteNameField.setText( null );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                dispatchEvent( new WindowEvent( PasswordFrame.this, WindowEvent.WINDOW_CLOSING ) );
 | 
					                            dispatchEvent( new WindowEvent( PasswordFrame.this, WindowEvent.WINDOW_CLOSING ) );
 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        } );
 | 
					                        } );
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -122,19 +117,16 @@ public abstract class PasswordFrame<U extends MPUser<S>, S extends MPBasicSite>
 | 
				
			|||||||
                } );
 | 
					                } );
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } );
 | 
					        } );
 | 
				
			||||||
        siteActionButton.addActionListener( new ActionListener() {
 | 
					        siteActionButton.addActionListener( e -> {
 | 
				
			||||||
            @Override
 | 
					            if (currentSite == null)
 | 
				
			||||||
            public void actionPerformed(final ActionEvent e) {
 | 
					                return;
 | 
				
			||||||
                if (currentSite == null)
 | 
					            if (currentSite instanceof MPFileSite)
 | 
				
			||||||
                    return;
 | 
					                this.user.deleteSite( currentSite );
 | 
				
			||||||
                if (currentSite instanceof MPFileSite)
 | 
					            else
 | 
				
			||||||
                    PasswordFrame.this.user.deleteSite( currentSite );
 | 
					                this.user.addSite( currentSite );
 | 
				
			||||||
                else
 | 
					            siteNameField.requestFocus();
 | 
				
			||||||
                    PasswordFrame.this.user.addSite( currentSite );
 | 
					 | 
				
			||||||
                siteNameField.requestFocus();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                updatePassword( true );
 | 
					            updatePassword( true );
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } );
 | 
					        } );
 | 
				
			||||||
        sitePanel.add( siteControls );
 | 
					        sitePanel.add( siteControls );
 | 
				
			||||||
        sitePanel.add( Components.stud() );
 | 
					        sitePanel.add( Components.stud() );
 | 
				
			||||||
@@ -145,48 +137,28 @@ public abstract class PasswordFrame<U extends MPUser<S>, S extends MPBasicSite>
 | 
				
			|||||||
        JComponent siteSettings = Components.boxLayout( BoxLayout.LINE_AXIS,                                                  //
 | 
					        JComponent siteSettings = Components.boxLayout( BoxLayout.LINE_AXIS,                                                  //
 | 
				
			||||||
                                                        resultTypeField = Components.comboBox( types ),                         //
 | 
					                                                        resultTypeField = Components.comboBox( types ),                         //
 | 
				
			||||||
                                                        Components.stud(),                                                    //
 | 
					                                                        Components.stud(),                                                    //
 | 
				
			||||||
                                                        siteVersionField = Components.comboBox( MPMasterKey.Version.values() ), //
 | 
					                                                        siteVersionField = Components.comboBox( MPAlgorithm.Version.values() ), //
 | 
				
			||||||
                                                        Components.stud(),                                                    //
 | 
					                                                        Components.stud(),                                                    //
 | 
				
			||||||
                                                        siteCounterField = Components.spinner( siteCounterModel ) );
 | 
					                                                        siteCounterField = Components.spinner( siteCounterModel ) );
 | 
				
			||||||
        sitePanel.add( siteSettings );
 | 
					        sitePanel.add( siteSettings );
 | 
				
			||||||
        resultTypeField.setFont( Res.valueFont().deriveFont( resultTypeField.getFont().getSize2D() ) );
 | 
					        resultTypeField.setFont( Res.valueFont().deriveFont( resultTypeField.getFont().getSize2D() ) );
 | 
				
			||||||
        resultTypeField.setSelectedItem( user.getAlgorithm().mpw_default_result_type() );
 | 
					        resultTypeField.setSelectedItem( user.getAlgorithm().mpw_default_result_type() );
 | 
				
			||||||
        resultTypeField.addItemListener( new ItemListener() {
 | 
					        resultTypeField.addItemListener( e -> updatePassword( true ) );
 | 
				
			||||||
            @Override
 | 
					 | 
				
			||||||
            public void itemStateChanged(final ItemEvent e) {
 | 
					 | 
				
			||||||
                updatePassword( true );
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        siteVersionField.setFont( Res.valueFont().deriveFont( siteVersionField.getFont().getSize2D() ) );
 | 
					        siteVersionField.setFont( Res.valueFont().deriveFont( siteVersionField.getFont().getSize2D() ) );
 | 
				
			||||||
        siteVersionField.setAlignmentX( RIGHT_ALIGNMENT );
 | 
					        siteVersionField.setAlignmentX( RIGHT_ALIGNMENT );
 | 
				
			||||||
        siteVersionField.setSelectedItem( user.getAlgorithm() );
 | 
					        siteVersionField.setSelectedItem( user.getAlgorithm() );
 | 
				
			||||||
        siteVersionField.addItemListener( new ItemListener() {
 | 
					        siteVersionField.addItemListener( e -> updatePassword( true ) );
 | 
				
			||||||
            @Override
 | 
					 | 
				
			||||||
            public void itemStateChanged(final ItemEvent e) {
 | 
					 | 
				
			||||||
                updatePassword( true );
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        siteCounterField.setFont( Res.valueFont().deriveFont( siteCounterField.getFont().getSize2D() ) );
 | 
					        siteCounterField.setFont( Res.valueFont().deriveFont( siteCounterField.getFont().getSize2D() ) );
 | 
				
			||||||
        siteCounterField.setAlignmentX( RIGHT_ALIGNMENT );
 | 
					        siteCounterField.setAlignmentX( RIGHT_ALIGNMENT );
 | 
				
			||||||
        siteCounterField.addChangeListener( new ChangeListener() {
 | 
					        siteCounterField.addChangeListener( e -> updatePassword( true ) );
 | 
				
			||||||
            @Override
 | 
					 | 
				
			||||||
            public void stateChanged(final ChangeEvent e) {
 | 
					 | 
				
			||||||
                updatePassword( true );
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Mask
 | 
					        // Mask
 | 
				
			||||||
        maskPasswordField = Components.checkBox( "Hide Password" );
 | 
					        maskPasswordField = Components.checkBox( "Hide Password" );
 | 
				
			||||||
        maskPasswordField.setAlignmentX( Component.CENTER_ALIGNMENT );
 | 
					        maskPasswordField.setAlignmentX( Component.CENTER_ALIGNMENT );
 | 
				
			||||||
        maskPasswordField.setSelected( true );
 | 
					        maskPasswordField.setSelected( true );
 | 
				
			||||||
        maskPasswordField.addItemListener( new ItemListener() {
 | 
					        maskPasswordField.addItemListener( e -> updateMask() );
 | 
				
			||||||
            @Override
 | 
					 | 
				
			||||||
            public void itemStateChanged(final ItemEvent e) {
 | 
					 | 
				
			||||||
                updateMask();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Password
 | 
					        // Password
 | 
				
			||||||
        passwordField = Components.passwordField();
 | 
					        passwordField = Components.passwordField();
 | 
				
			||||||
@@ -229,7 +201,7 @@ public abstract class PasswordFrame<U extends MPUser<S>, S extends MPBasicSite>
 | 
				
			|||||||
    @Nonnull
 | 
					    @Nonnull
 | 
				
			||||||
    private ListenableFuture<String> updatePassword(final boolean allowNameCompletion) {
 | 
					    private ListenableFuture<String> updatePassword(final boolean allowNameCompletion) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        final String siteNameQuery = siteNameField.getText();
 | 
					        String siteNameQuery = siteNameField.getText();
 | 
				
			||||||
        if (updatingUI)
 | 
					        if (updatingUI)
 | 
				
			||||||
            return Futures.immediateCancelledFuture();
 | 
					            return Futures.immediateCancelledFuture();
 | 
				
			||||||
        if ((siteNameQuery == null) || siteNameQuery.isEmpty() || !user.isMasterKeyAvailable()) {
 | 
					        if ((siteNameQuery == null) || siteNameQuery.isEmpty() || !user.isMasterKeyAvailable()) {
 | 
				
			||||||
@@ -243,53 +215,40 @@ public abstract class PasswordFrame<U extends MPUser<S>, S extends MPBasicSite>
 | 
				
			|||||||
        MPAlgorithm     siteAlgorithm = siteVersionField.getItemAt( siteVersionField.getSelectedIndex() ).getAlgorithm();
 | 
					        MPAlgorithm     siteAlgorithm = siteVersionField.getItemAt( siteVersionField.getSelectedIndex() ).getAlgorithm();
 | 
				
			||||||
        UnsignedInteger siteCounter   = siteCounterModel.getNumber();
 | 
					        UnsignedInteger siteCounter   = siteCounterModel.getNumber();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Iterable<S> siteResults = user.findSites( siteNameQuery );
 | 
					        Collection<S> siteResults = user.findSites( siteNameQuery );
 | 
				
			||||||
        if (!allowNameCompletion)
 | 
					        if (!allowNameCompletion)
 | 
				
			||||||
            siteResults = FluentIterable.from( siteResults ).filter( new Predicate<S>() {
 | 
					            siteResults = siteResults.stream().filter(
 | 
				
			||||||
                @Override
 | 
					                    siteResult -> (siteResult != null) && siteNameQuery.equals( siteResult.getName() ) ).collect( Collectors.toList() );
 | 
				
			||||||
                public boolean apply(@Nullable final S siteResult) {
 | 
					        S site = ifNotNullElse( Iterables.getFirst( siteResults, null ),
 | 
				
			||||||
                    return (siteResult != null) && siteNameQuery.equals( siteResult.getName() );
 | 
					                                createSite( user, siteNameQuery, siteCounter, resultType, siteAlgorithm ) );
 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            } );
 | 
					 | 
				
			||||||
        final S site = ifNotNullElse( Iterables.getFirst( siteResults, null ),
 | 
					 | 
				
			||||||
                                      createSite( user, siteNameQuery, siteCounter, resultType, siteAlgorithm ) );
 | 
					 | 
				
			||||||
        if ((currentSite != null) && currentSite.getName().equals( site.getName() )) {
 | 
					        if ((currentSite != null) && currentSite.getName().equals( site.getName() )) {
 | 
				
			||||||
            site.setResultType( resultType );
 | 
					            site.setResultType( resultType );
 | 
				
			||||||
            site.setAlgorithm( siteAlgorithm );
 | 
					            site.setAlgorithm( siteAlgorithm );
 | 
				
			||||||
            site.setCounter( siteCounter );
 | 
					            site.setCounter( siteCounter );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ListenableFuture<String> passwordFuture = Res.execute( this, new Callable<String>() {
 | 
					        ListenableFuture<String> passwordFuture = Res.execute( this, () -> site.getResult( MPKeyPurpose.Authentication, null, null ) );
 | 
				
			||||||
            @Override
 | 
					 | 
				
			||||||
            public String call()
 | 
					 | 
				
			||||||
                    throws Exception {
 | 
					 | 
				
			||||||
                return site.getResult( MPKeyPurpose.Authentication, null, null );
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } );
 | 
					 | 
				
			||||||
        Futures.addCallback( passwordFuture, new FutureCallback<String>() {
 | 
					        Futures.addCallback( passwordFuture, new FutureCallback<String>() {
 | 
				
			||||||
            @Override
 | 
					            @Override
 | 
				
			||||||
            public void onSuccess(@Nullable final String sitePassword) {
 | 
					            public void onSuccess(@Nullable final String sitePassword) {
 | 
				
			||||||
                SwingUtilities.invokeLater( new Runnable() {
 | 
					                SwingUtilities.invokeLater( () -> {
 | 
				
			||||||
                    @Override
 | 
					                    updatingUI = true;
 | 
				
			||||||
                    public void run() {
 | 
					                    currentSite = site;
 | 
				
			||||||
                        updatingUI = true;
 | 
					                    siteActionButton.setVisible( user instanceof MPFileUser );
 | 
				
			||||||
                        currentSite = site;
 | 
					                    if (currentSite instanceof MPFileSite)
 | 
				
			||||||
                        siteActionButton.setVisible( user instanceof MPFileUser );
 | 
					                        siteActionButton.setText( "Delete Site" );
 | 
				
			||||||
                        if (currentSite instanceof MPFileSite)
 | 
					                    else
 | 
				
			||||||
                            siteActionButton.setText( "Delete Site" );
 | 
					                        siteActionButton.setText( "Add Site" );
 | 
				
			||||||
                        else
 | 
					                    resultTypeField.setSelectedItem( currentSite.getResultType() );
 | 
				
			||||||
                            siteActionButton.setText( "Add Site" );
 | 
					                    siteVersionField.setSelectedItem( currentSite.getAlgorithm() );
 | 
				
			||||||
                        resultTypeField.setSelectedItem( currentSite.getResultType() );
 | 
					                    siteCounterField.setValue( currentSite.getCounter() );
 | 
				
			||||||
                        siteVersionField.setSelectedItem( currentSite.getAlgorithm() );
 | 
					                    siteNameField.setText( currentSite.getName() );
 | 
				
			||||||
                        siteCounterField.setValue( currentSite.getCounter() );
 | 
					                    if (siteNameField.getText().startsWith( siteNameQuery ))
 | 
				
			||||||
                        siteNameField.setText( currentSite.getName() );
 | 
					                        siteNameField.select( siteNameQuery.length(), siteNameField.getText().length() );
 | 
				
			||||||
                        if (siteNameField.getText().startsWith( siteNameQuery ))
 | 
					 | 
				
			||||||
                            siteNameField.select( siteNameQuery.length(), siteNameField.getText().length() );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        passwordField.setText( sitePassword );
 | 
					                    passwordField.setText( sitePassword );
 | 
				
			||||||
                        tipLabel.setText( "Press [Enter] to copy the password.  Then paste it into the password field." );
 | 
					                    tipLabel.setText( "Press [Enter] to copy the password.  Then paste it into the password field." );
 | 
				
			||||||
                        updatingUI = false;
 | 
					                    updatingUI = false;
 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                } );
 | 
					                } );
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -119,20 +119,12 @@ public class UnlockFrame extends JFrame {
 | 
				
			|||||||
        authenticationContainer.add( authenticationPanel );
 | 
					        authenticationContainer.add( authenticationPanel );
 | 
				
			||||||
        authenticationContainer.add( Components.stud() );
 | 
					        authenticationContainer.add( Components.stud() );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        final JCheckBox incognitoCheckBox = Components.checkBox( "Incognito" );
 | 
					        JCheckBox incognitoCheckBox = Components.checkBox( "Incognito" );
 | 
				
			||||||
        incognitoCheckBox.setToolTipText( "Log in without saving any information." );
 | 
					        incognitoCheckBox.setToolTipText( "Log in without saving any information." );
 | 
				
			||||||
        incognitoCheckBox.setSelected( incognito );
 | 
					        incognitoCheckBox.setSelected( incognito );
 | 
				
			||||||
        incognitoCheckBox.addItemListener( new ItemListener() {
 | 
					        incognitoCheckBox.addItemListener( e -> {
 | 
				
			||||||
            @Override
 | 
					            incognito = incognitoCheckBox.isSelected();
 | 
				
			||||||
            public void itemStateChanged(final ItemEvent e) {
 | 
					            SwingUtilities.invokeLater( this::createAuthenticationPanel );
 | 
				
			||||||
                incognito = incognitoCheckBox.isSelected();
 | 
					 | 
				
			||||||
                SwingUtilities.invokeLater( new Runnable() {
 | 
					 | 
				
			||||||
                    @Override
 | 
					 | 
				
			||||||
                    public void run() {
 | 
					 | 
				
			||||||
                        createAuthenticationPanel();
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                } );
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } );
 | 
					        } );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        JComponent toolsPanel = Components.boxLayout( BoxLayout.LINE_AXIS, incognitoCheckBox, Box.createGlue() );
 | 
					        JComponent toolsPanel = Components.boxLayout( BoxLayout.LINE_AXIS, incognitoCheckBox, Box.createGlue() );
 | 
				
			||||||
@@ -149,12 +141,7 @@ public class UnlockFrame extends JFrame {
 | 
				
			|||||||
        validate();
 | 
					        validate();
 | 
				
			||||||
        repack();
 | 
					        repack();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        SwingUtilities.invokeLater( new Runnable() {
 | 
					        SwingUtilities.invokeLater( () -> ifNotNullElse( authenticationPanel.getFocusComponent(), signInButton ).requestFocusInWindow() );
 | 
				
			||||||
            @Override
 | 
					 | 
				
			||||||
            public void run() {
 | 
					 | 
				
			||||||
                ifNotNullElse( authenticationPanel.getFocusComponent(), signInButton ).requestFocusInWindow();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } );
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void updateUser(@Nullable final MPUser<? extends MPSite> user) {
 | 
					    void updateUser(@Nullable final MPUser<? extends MPSite> user) {
 | 
				
			||||||
@@ -165,28 +152,20 @@ public class UnlockFrame extends JFrame {
 | 
				
			|||||||
    boolean checkSignIn() {
 | 
					    boolean checkSignIn() {
 | 
				
			||||||
        if (identiconFuture != null)
 | 
					        if (identiconFuture != null)
 | 
				
			||||||
            identiconFuture.cancel( false );
 | 
					            identiconFuture.cancel( false );
 | 
				
			||||||
        identiconFuture = Res.schedule( this, new Runnable() {
 | 
					        identiconFuture = Res.schedule( this, () -> SwingUtilities.invokeLater( () -> {
 | 
				
			||||||
            @Override
 | 
					            String fullName       = (user == null)? "": user.getFullName();
 | 
				
			||||||
            public void run() {
 | 
					            char[] masterPassword = authenticationPanel.getMasterPassword();
 | 
				
			||||||
                SwingUtilities.invokeLater( new Runnable() {
 | 
					 | 
				
			||||||
                    @Override
 | 
					 | 
				
			||||||
                    public void run() {
 | 
					 | 
				
			||||||
                        String fullName       = (user == null)? "": user.getFullName();
 | 
					 | 
				
			||||||
                        char[] masterPassword = authenticationPanel.getMasterPassword();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        if (fullName.isEmpty() || (masterPassword.length == 0)) {
 | 
					            if (fullName.isEmpty() || (masterPassword.length == 0)) {
 | 
				
			||||||
                            identiconLabel.setText( " " );
 | 
					                identiconLabel.setText( " " );
 | 
				
			||||||
                            return;
 | 
					                return;
 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        MPIdenticon identicon = new MPIdenticon( fullName, masterPassword );
 | 
					 | 
				
			||||||
                        identiconLabel.setText( identicon.getText() );
 | 
					 | 
				
			||||||
                        identiconLabel.setForeground(
 | 
					 | 
				
			||||||
                                Res.colors().fromIdenticonColor( identicon.getColor(), Res.Colors.BackgroundMode.DARK ) );
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                } );
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }, 300, TimeUnit.MILLISECONDS );
 | 
					
 | 
				
			||||||
 | 
					            MPIdenticon identicon = new MPIdenticon( fullName, masterPassword );
 | 
				
			||||||
 | 
					            identiconLabel.setText( identicon.getText() );
 | 
				
			||||||
 | 
					            identiconLabel.setForeground(
 | 
				
			||||||
 | 
					                    Res.colors().fromIdenticonColor( identicon.getColor(), Res.Colors.BackgroundMode.DARK ) );
 | 
				
			||||||
 | 
					        } ), 300, TimeUnit.MILLISECONDS );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        String  fullName       = (user == null)? "": user.getFullName();
 | 
					        String  fullName       = (user == null)? "": user.getFullName();
 | 
				
			||||||
        char[]  masterPassword = authenticationPanel.getMasterPassword();
 | 
					        char[]  masterPassword = authenticationPanel.getMasterPassword();
 | 
				
			||||||
@@ -206,37 +185,29 @@ public class UnlockFrame extends JFrame {
 | 
				
			|||||||
        signInButton.setEnabled( false );
 | 
					        signInButton.setEnabled( false );
 | 
				
			||||||
        signInButton.setText( "Signing In..." );
 | 
					        signInButton.setText( "Signing In..." );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Res.execute( this, new Runnable() {
 | 
					        Res.execute( this, () -> {
 | 
				
			||||||
            @Override
 | 
					            try {
 | 
				
			||||||
            public void run() {
 | 
					                user.authenticate( authenticationPanel.getMasterPassword() );
 | 
				
			||||||
                try {
 | 
					 | 
				
			||||||
                    user.authenticate( authenticationPanel.getMasterPassword() );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    SwingUtilities.invokeLater( new Runnable() {
 | 
					                SwingUtilities.invokeLater( () -> {
 | 
				
			||||||
                        @Override
 | 
					                    signInCallback.signedIn( authenticationPanel.newPasswordFrame() );
 | 
				
			||||||
                        public void run() {
 | 
					                    dispose();
 | 
				
			||||||
                            signInCallback.signedIn( authenticationPanel.newPasswordFrame() );
 | 
					                } );
 | 
				
			||||||
                            dispose();
 | 
					            }
 | 
				
			||||||
                        }
 | 
					            catch (final MPIncorrectMasterPasswordException e) {
 | 
				
			||||||
                    } );
 | 
					                SwingUtilities.invokeLater( () -> {
 | 
				
			||||||
                }
 | 
					                    JOptionPane.showMessageDialog( null, e.getLocalizedMessage(), "Sign In Failed", JOptionPane.ERROR_MESSAGE );
 | 
				
			||||||
                catch (final MPIncorrectMasterPasswordException e) {
 | 
					                    authenticationPanel.reset();
 | 
				
			||||||
                    SwingUtilities.invokeLater( new Runnable() {
 | 
					                    signInButton.setText( "Sign In" );
 | 
				
			||||||
                        @Override
 | 
					                    for (final JComponent signInComponent : signInComponents)
 | 
				
			||||||
                        public void run() {
 | 
					                        signInComponent.setEnabled( true );
 | 
				
			||||||
                            JOptionPane.showMessageDialog( null, e.getLocalizedMessage(), "Sign In Failed", JOptionPane.ERROR_MESSAGE );
 | 
					                    checkSignIn();
 | 
				
			||||||
                            authenticationPanel.reset();
 | 
					                } );
 | 
				
			||||||
                            signInButton.setText( "Sign In" );
 | 
					 | 
				
			||||||
                            for (final JComponent signInComponent : signInComponents)
 | 
					 | 
				
			||||||
                                signInComponent.setEnabled( true );
 | 
					 | 
				
			||||||
                            checkSignIn();
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    } );
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } );
 | 
					        } );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @FunctionalInterface
 | 
				
			||||||
    public interface SignInCallback {
 | 
					    public interface SignInCallback {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        void signedIn(PasswordFrame<?, ?> passwordFrame);
 | 
					        void signedIn(PasswordFrame<?, ?> passwordFrame);
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user