Refactoring masterpassword-algorithm.
This commit is contained in:
parent
8bdf1755b7
commit
0aa7baf59e
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user