diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithm.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithm.java index ed042cd7..05eaf9dc 100644 --- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithm.java +++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithm.java @@ -18,43 +18,95 @@ package com.lyndir.masterpassword; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; import com.google.common.primitives.UnsignedInteger; import com.lyndir.lhunath.opal.system.MessageAuthenticationDigests; 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.charset.Charset; import javax.annotation.Nullable; /** - * @see MPMasterKey.Version + * @see Version */ @SuppressWarnings({ "FieldMayBeStatic", "NewMethodNamingConvention", "MethodReturnAlwaysConstant" }) 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); + /** + * 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, 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, MPKeyPurpose keyPurpose, @Nullable String keyContext, 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, MPKeyPurpose keyPurpose, @Nullable String keyContext, MPResultType resultType, String resultParam); // Configuration - public abstract MPMasterKey.Version version(); + /** + * The linear version identifier of this algorithm's implementation. + */ + public abstract Version version(); /** * mpw: defaults: password result type. @@ -133,11 +185,68 @@ public abstract class MPAlgorithm { // 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(); + } + } } diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPKeyPurpose.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPKeyPurpose.java index ac068bcd..c4a06d82 100644 --- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPKeyPurpose.java +++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPKeyPurpose.java @@ -18,6 +18,8 @@ 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 java.util.Locale; import javax.annotation.Nullable; @@ -33,10 +35,12 @@ public enum MPKeyPurpose { * Generate a key for authentication. */ Authentication( "authentication", "Generate a key for authentication.", "com.lyndir.masterpassword" ), + /** * Generate a name for identification. */ Identification( "identification", "Generate a name for identification.", "com.lyndir.masterpassword.login" ), + /** * Generate a recovery token. */ @@ -85,11 +89,13 @@ public enum MPKeyPurpose { throw logger.bug( "No purpose for name: %s", shortNamePrefix ); } + @JsonCreator public static MPKeyPurpose forInt(final int keyPurpose) { return values()[keyPurpose]; } + @JsonValue public int toInt() { return ordinal(); diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPMasterKey.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPMasterKey.java index 660d8a6c..e0468e0e 100644 --- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPMasterKey.java +++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPMasterKey.java @@ -18,8 +18,6 @@ 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.primitives.UnsignedInteger; import com.lyndir.lhunath.opal.system.CodeUtils; @@ -38,15 +36,15 @@ public class MPMasterKey { @SuppressWarnings("UnusedDeclaration") private static final Logger logger = Logger.get( MPMasterKey.class ); - private final EnumMap keyByVersion = new EnumMap<>( Version.class ); - private final String fullName; - private final char[] masterPassword; + private final EnumMap keyByVersion = new EnumMap<>( MPAlgorithm.Version.class ); + private final String fullName; + private final char[] masterPassword; private boolean invalidated; /** - * @param masterPassword The characters of the user's master password. Note: this array is held by reference and its contents - * invalidated on {@link #invalidate()}. + * @param masterPassword The characters of the user's master password. + * Note: this array is held by reference and its contents invalidated on {@link #invalidate()}. */ @SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter") public MPMasterKey(final String fullName, final char[] masterPassword) { @@ -55,113 +53,6 @@ public class MPMasterKey { 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 public String getFullName() { @@ -190,60 +81,101 @@ public class MPMasterKey { 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 { - /** - * 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() ), + byte[] masterKey = masterKey( algorithm ); + byte[] siteKey = siteKey( siteName, algorithm, siteCounter, keyPurpose, keyContext ); - /** - * bugs: - * - miscounted the byte-length for multi-byte site names. - * - miscounted the byte-length for multi-byte user names. - */ - V1( new MPAlgorithmV1() ), + logger.trc( "-- mpw_siteResult (algorithm: %s)", algorithm ); + logger.trc( "resultType: %d (%s)", resultType.getType(), resultType.getShortName() ); + logger.trc( "resultParam: %s", resultParam ); - /** - * bugs: - * - miscounted the byte-length for multi-byte user names. - */ - V2( new MPAlgorithmV2() ), + return algorithm.siteResult( + masterKey, siteKey, siteName, siteCounter, keyPurpose, keyContext, resultType, resultParam ); + } - /** - * bugs: - * - no known issues. - */ - V3( new MPAlgorithmV3() ); + /** + * Encrypt a stateful site token for persistence. + * + * @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 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) { - this.algorithm = 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 ); - public MPAlgorithm getAlgorithm() { - return algorithm; - } - - @JsonCreator - public static Version fromInt(final int algorithmVersion) { - - return values()[algorithmVersion]; - } - - @JsonValue - public int toInt() { - - return ordinal(); - } + return algorithm.siteState( + masterKey, siteKey, siteName, siteCounter, keyPurpose, keyContext, resultType, resultParam ); } } diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV0.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV0.java similarity index 93% rename from core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV0.java rename to core/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV0.java index 931b01f4..d4ab7a16 100644 --- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV0.java +++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV0.java @@ -16,7 +16,7 @@ // LICENSE file. Alternatively, see . //============================================================================== -package com.lyndir.masterpassword; +package com.lyndir.masterpassword.impl; import com.google.common.base.*; 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.logging.Logger; import com.lyndir.lhunath.opal.system.util.ConversionUtils; +import com.lyndir.masterpassword.*; import java.nio.*; import java.nio.charset.Charset; import java.security.GeneralSecurityException; @@ -37,12 +38,12 @@ import javax.crypto.IllegalBlockSizeException; /** * @author lhunath, 2014-08-30 - * @see MPMasterKey.Version#V0 + * @see MPAlgorithm.Version#V0 */ @SuppressWarnings("NewMethodNamingConvention") 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() ); @@ -122,19 +123,19 @@ public class MPAlgorithmV0 extends MPAlgorithm { switch (resultType.getTypeClass()) { case Template: - return sitePasswordFromTemplate( masterKey, siteKey, resultType, resultParam ); + return siteResultFromTemplate( masterKey, siteKey, resultType, resultParam ); case Stateful: - return sitePasswordFromCrypt( masterKey, siteKey, resultType, resultParam ); + return siteResultFromState( masterKey, siteKey, resultType, resultParam ); case Derive: - return sitePasswordFromDerive( masterKey, siteKey, resultType, resultParam ); + return siteResultFromDerive( masterKey, siteKey, resultType, resultParam ); } throw logger.bug( "Unsupported result type class: %s", resultType.getTypeClass() ); } @Override - public String sitePasswordFromTemplate(final byte[] masterKey, final byte[] siteKey, - final MPResultType resultType, @Nullable final String resultParam) { + public String siteResultFromTemplate(final byte[] masterKey, final byte[] siteKey, + final MPResultType resultType, @Nullable final String resultParam) { int[] _siteKey = new int[siteKey.length]; for (int i = 0; i < siteKey.length; ++i) { @@ -168,8 +169,8 @@ public class MPAlgorithmV0 extends MPAlgorithm { } @Override - public String sitePasswordFromCrypt(final byte[] masterKey, final byte[] siteKey, - final MPResultType resultType, @Nullable final String resultParam) { + public String siteResultFromState(final byte[] masterKey, final byte[] siteKey, + final MPResultType resultType, @Nullable final String resultParam) { Preconditions.checkNotNull( resultParam ); Preconditions.checkArgument( !resultParam.isEmpty() ); @@ -192,8 +193,8 @@ public class MPAlgorithmV0 extends MPAlgorithm { } @Override - public String sitePasswordFromDerive(final byte[] masterKey, final byte[] siteKey, - final MPResultType resultType, @Nullable final String resultParam) { + public String siteResultFromDerive(final byte[] masterKey, final byte[] siteKey, + final MPResultType resultType, @Nullable final String resultParam) { if (resultType == MPResultType.DeriveKey) { int resultParamInt = ConversionUtils.toIntegerNN( resultParam ); @@ -242,8 +243,8 @@ public class MPAlgorithmV0 extends MPAlgorithm { // Configuration @Override - public MPMasterKey.Version version() { - return MPMasterKey.Version.V0; + public Version version() { + return MPAlgorithm.Version.V0; } @Override diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV1.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV1.java similarity index 86% rename from core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV1.java rename to core/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV1.java index cfb98ebe..dc5a5f9c 100644 --- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV1.java +++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV1.java @@ -16,22 +16,23 @@ // LICENSE file. Alternatively, see . //============================================================================== -package com.lyndir.masterpassword; +package com.lyndir.masterpassword.impl; import com.google.common.base.Preconditions; import com.google.common.primitives.UnsignedBytes; +import com.lyndir.masterpassword.*; import javax.annotation.Nullable; /** * @author lhunath, 2014-08-30 - * @see MPMasterKey.Version#V1 + * @see Version#V1 */ public class MPAlgorithmV1 extends MPAlgorithmV0 { @Override - public String sitePasswordFromTemplate(final byte[] masterKey, final byte[] siteKey, final MPResultType resultType, - @Nullable final String resultParam) { + public String siteResultFromTemplate(final byte[] masterKey, final byte[] siteKey, + final MPResultType resultType, @Nullable final String resultParam) { // Determine the template. Preconditions.checkState( siteKey.length > 0 ); @@ -58,7 +59,7 @@ public class MPAlgorithmV1 extends MPAlgorithmV0 { // Configuration @Override - public MPMasterKey.Version version() { - return MPMasterKey.Version.V1; + public Version version() { + return MPAlgorithm.Version.V1; } } diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV2.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV2.java similarity index 89% rename from core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV2.java rename to core/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV2.java index 4ccdee7b..a71cc9ce 100644 --- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV2.java +++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV2.java @@ -16,23 +16,25 @@ // LICENSE file. Alternatively, see . //============================================================================== -package com.lyndir.masterpassword; +package com.lyndir.masterpassword.impl; import com.google.common.primitives.Bytes; import com.google.common.primitives.UnsignedInteger; import com.lyndir.lhunath.opal.system.CodeUtils; +import com.lyndir.masterpassword.MPAlgorithm; +import com.lyndir.masterpassword.MPKeyPurpose; import javax.annotation.Nullable; /** * @author lhunath, 2014-08-30 - * @see MPMasterKey.Version#V2 + * @see Version#V2 */ public class MPAlgorithmV2 extends MPAlgorithmV1 { @Override - public byte[] siteKey(final byte[] masterKey, final String siteName, UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose, - @Nullable final String keyContext) { + public byte[] siteKey(final byte[] masterKey, final String siteName, UnsignedInteger siteCounter, + final MPKeyPurpose keyPurpose, @Nullable final String keyContext) { String keyScope = keyPurpose.getScope(); logger.trc( "keyScope: %s", keyScope ); @@ -66,7 +68,7 @@ public class MPAlgorithmV2 extends MPAlgorithmV1 { // Configuration @Override - public MPMasterKey.Version version() { - return MPMasterKey.Version.V2; + public Version version() { + return MPAlgorithm.Version.V2; } } diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV3.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV3.java similarity index 91% rename from core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV3.java rename to core/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV3.java index 9254e277..845e9da8 100644 --- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV3.java +++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV3.java @@ -16,16 +16,18 @@ // LICENSE file. Alternatively, see . //============================================================================== -package com.lyndir.masterpassword; +package com.lyndir.masterpassword.impl; import com.google.common.primitives.Bytes; import com.lyndir.lhunath.opal.system.CodeUtils; +import com.lyndir.masterpassword.MPAlgorithm; +import com.lyndir.masterpassword.MPKeyPurpose; import java.util.Arrays; /** * @author lhunath, 2014-08-30 - * @see MPMasterKey.Version#V3 + * @see Version#V3 */ public class MPAlgorithmV3 extends MPAlgorithmV2 { @@ -59,7 +61,7 @@ public class MPAlgorithmV3 extends MPAlgorithmV2 { // Configuration @Override - public MPMasterKey.Version version() { - return MPMasterKey.Version.V3; + public Version version() { + return MPAlgorithm.Version.V3; } } diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/package-info.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/package-info.java new file mode 100644 index 00000000..33de6318 --- /dev/null +++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/package-info.java @@ -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 . +//============================================================================== + +/** + * @author lhunath, 2018-05-15 + */ +@ParametersAreNonnullByDefault +package com.lyndir.masterpassword.impl; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicSite.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicSite.java index 3ea52221..dcf0bf1b 100644 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicSite.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicSite.java @@ -118,8 +118,8 @@ public abstract class MPBasicSite implements MPSite { throws MPKeyUnavailableException { return getUser().getMasterKey().siteResult( - getName(), ifNotNullElse( counter, getAlgorithm().mpw_default_counter() ), keyPurpose, keyContext, - type, state, getAlgorithm() ); + getName(), getAlgorithm(), ifNotNullElse( counter, getAlgorithm().mpw_default_counter() ), + keyPurpose, keyContext, type, state ); } protected String getState(final MPKeyPurpose keyPurpose, @Nullable final String keyContext, @@ -127,8 +127,8 @@ public abstract class MPBasicSite implements MPSite { throws MPKeyUnavailableException { return getUser().getMasterKey().siteState( - getName(), ifNotNullElse( counter, getAlgorithm().mpw_default_counter() ), keyPurpose, keyContext, - type, state, getAlgorithm() ); + getName(), getAlgorithm(), ifNotNullElse( counter, getAlgorithm().mpw_default_counter() ), + keyPurpose, keyContext, type, state ); } @Override diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileUser.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileUser.java index 16706110..29751c35 100755 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileUser.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileUser.java @@ -49,7 +49,7 @@ public class MPFileUser extends MPBasicUser { private MPJSONFile json; 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) { diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFlatUnmarshaller.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFlatUnmarshaller.java index 126c1961..59314a10 100644 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFlatUnmarshaller.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFlatUnmarshaller.java @@ -76,7 +76,7 @@ public class MPFlatUnmarshaller implements MPUnmarshaller { headerStarted = true; else // 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, clearContent? MPMarshaller.ContentMode.VISIBLE : MPMarshaller.ContentMode.PROTECTED ); @@ -117,7 +117,7 @@ public class MPFlatUnmarshaller implements MPUnmarshaller { switch (importFormat) { case 0: 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(), user.getAlgorithm().mpw_default_counter(), MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ), @@ -130,7 +130,7 @@ public class MPFlatUnmarshaller implements MPUnmarshaller { case 1: 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(), UnsignedInteger.valueOf( colon.matcher( siteMatcher.group( 5 ) ).replaceAll( "" ) ), MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ), diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPJSONFile.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPJSONFile.java index 93189efb..39eade62 100644 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPJSONFile.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPJSONFile.java @@ -72,8 +72,8 @@ public class MPJSONFile extends MPJSONAnyObject { // Clear Text content = modelSite.getResult(); loginContent = modelUser.getMasterKey().siteResult( - modelSite.getName(), modelSite.getAlgorithm().mpw_default_counter(), - MPKeyPurpose.Identification, null, modelSite.getLoginType(), modelSite.getLoginState(), modelSite.getAlgorithm() ); + modelSite.getName(), modelSite.getAlgorithm(), modelSite.getAlgorithm().mpw_default_counter(), + MPKeyPurpose.Identification, null, modelSite.getLoginType(), modelSite.getLoginState() ); } else { // Redacted if (modelSite.getResultType().supportsTypeFeature( MPSiteFeature.ExportContent )) @@ -134,7 +134,7 @@ public class MPJSONFile extends MPJSONAnyObject { public MPFileUser read(@Nullable final char[] masterPassword) 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( user.full_name, CodeUtils.decodeHex( user.key_id ), algorithm, user.avatar, (user.default_type != null)? user.default_type: algorithm.mpw_default_result_type(), @@ -192,7 +192,7 @@ public class MPJSONFile extends MPJSONAnyObject { @Nullable String key_id; @Nullable - MPMasterKey.Version algorithm; + MPAlgorithm.Version algorithm; @Nullable MPResultType default_type; } @@ -203,7 +203,7 @@ public class MPJSONFile extends MPJSONAnyObject { @Nullable MPResultType type; long counter; - MPMasterKey.Version algorithm; + MPAlgorithm.Version algorithm; @Nullable String password; @Nullable diff --git a/core/java/tests/src/main/java/com/lyndir/masterpassword/MPTestSuite.java b/core/java/tests/src/main/java/com/lyndir/masterpassword/MPTestSuite.java index 23eb0b68..dc31fd64 100644 --- a/core/java/tests/src/main/java/com/lyndir/masterpassword/MPTestSuite.java +++ b/core/java/tests/src/main/java/com/lyndir/masterpassword/MPTestSuite.java @@ -168,17 +168,13 @@ public class MPTestSuite implements Callable { @Override public Boolean call() throws Exception { - return forEach( "mpw", new TestCase() { - @Override - public boolean run(final MPTests.Case testCase) - throws Exception { - MPMasterKey masterKey = new MPMasterKey( testCase.getFullName(), testCase.getMasterPassword().toCharArray() ); - String sitePassword = masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(), - testCase.getKeyContext(), testCase.getResultType(), - null, testCase.getAlgorithm() ); + return forEach( "mpw", testCase -> { + MPMasterKey masterKey = new MPMasterKey( testCase.getFullName(), testCase.getMasterPassword().toCharArray() ); + String sitePassword = masterKey.siteResult( testCase.getSiteName(), testCase.getAlgorithm(), testCase.getSiteCounter(), + testCase.getKeyPurpose(), testCase.getKeyContext(), + testCase.getResultType(), null ); - return testCase.getResult().equals( sitePassword ); - } + return testCase.getResult().equals( sitePassword ); } ); } @@ -192,12 +188,14 @@ public class MPTestSuite implements Callable { } + @FunctionalInterface public interface Listener { void progress(int current, int max, String messageFormat, Object... args); } + @FunctionalInterface public interface TestCase { boolean run(MPTests.Case testCase) diff --git a/core/java/tests/src/main/java/com/lyndir/masterpassword/MPTests.java b/core/java/tests/src/main/java/com/lyndir/masterpassword/MPTests.java index 081dffe3..c48b41f2 100644 --- a/core/java/tests/src/main/java/com/lyndir/masterpassword/MPTests.java +++ b/core/java/tests/src/main/java/com/lyndir/masterpassword/MPTests.java @@ -176,7 +176,7 @@ public class MPTests { @Nonnull public MPAlgorithm getAlgorithm() { - return MPMasterKey.Version.fromInt( checkNotNull( algorithm ) ).getAlgorithm(); + return MPAlgorithm.Version.fromInt( checkNotNull( algorithm ) ).getAlgorithm(); } @Nonnull diff --git a/core/java/tests/src/test/java/com/lyndir/masterpassword/MPMasterKeyTest.java b/core/java/tests/src/test/java/com/lyndir/masterpassword/MPMasterKeyTest.java index bfbc6840..dccd6ba1 100644 --- a/core/java/tests/src/test/java/com/lyndir/masterpassword/MPMasterKeyTest.java +++ b/core/java/tests/src/test/java/com/lyndir/masterpassword/MPMasterKeyTest.java @@ -45,34 +45,30 @@ public class MPMasterKeyTest { public void testMasterKey() throws Exception { - testSuite.forEach( "testMasterKey", new MPTestSuite.TestCase() { - @Override - public boolean run(final MPTests.Case testCase) - throws Exception { - char[] masterPassword = testCase.getMasterPassword().toCharArray(); - MPMasterKey masterKey = new MPMasterKey( testCase.getFullName(), masterPassword ); + testSuite.forEach( "testMasterKey", testCase -> { + char[] masterPassword = testCase.getMasterPassword().toCharArray(); + MPMasterKey masterKey = new MPMasterKey( testCase.getFullName(), masterPassword ); - // Test key - assertEquals( - CodeUtils.encodeHex( masterKey.getKeyID( testCase.getAlgorithm() ) ), - testCase.getKeyID(), - "[testMasterKey] keyID mismatch: " + testCase ); + // Test key + assertEquals( + CodeUtils.encodeHex( masterKey.getKeyID( testCase.getAlgorithm() ) ), + testCase.getKeyID(), + "[testMasterKey] keyID mismatch: " + testCase ); - // Test invalidation - masterKey.invalidate(); - try { - masterKey.getKeyID( testCase.getAlgorithm() ); - fail( "[testMasterKey] invalidate ineffective: " + testCase ); - } - catch (final MPKeyUnavailableException ignored) { - } - assertNotEquals( - masterPassword, - testCase.getMasterPassword().toCharArray(), - "[testMasterKey] masterPassword not wiped: " + testCase ); - - return true; + // Test invalidation + masterKey.invalidate(); + try { + masterKey.getKeyID( testCase.getAlgorithm() ); + fail( "[testMasterKey] invalidate ineffective: " + testCase ); } + 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() throws Exception { - testSuite.forEach( "testSiteResult", new MPTestSuite.TestCase() { - @Override - public boolean run(final MPTests.Case testCase) - throws Exception { - char[] masterPassword = testCase.getMasterPassword().toCharArray(); - MPMasterKey masterKey = new MPMasterKey( testCase.getFullName(), masterPassword ); + testSuite.forEach( "testSiteResult", testCase -> { + char[] masterPassword = testCase.getMasterPassword().toCharArray(); + MPMasterKey masterKey = new MPMasterKey( testCase.getFullName(), masterPassword ); - // Test site result - assertEquals( - masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(), - testCase.getKeyContext(), testCase.getResultType(), - null, testCase.getAlgorithm() ), - testCase.getResult(), - "[testSiteResult] result mismatch: " + testCase ); + // Test site result + assertEquals( + masterKey.siteResult( testCase.getSiteName(), testCase.getAlgorithm(), testCase.getSiteCounter(), + testCase.getKeyPurpose(), + testCase.getKeyContext(), testCase.getResultType(), + null ), + testCase.getResult(), + "[testSiteResult] result mismatch: " + testCase ); - return true; - } + return true; } ); } @@ -110,14 +103,14 @@ public class MPMasterKeyTest { String password = randomString( 8 ); MPResultType resultType = MPResultType.StoredPersonal; - for (final MPMasterKey.Version version : MPMasterKey.Version.values()) { + for (final MPAlgorithm.Version version : MPAlgorithm.Version.values()) { MPAlgorithm algorithm = version.getAlgorithm(); // Test site state - String state = masterKey.siteState( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(), - testCase.getKeyContext(), resultType, password, algorithm ); - String result = masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(), - testCase.getKeyContext(), resultType, state, algorithm ); + String state = masterKey.siteState( testCase.getSiteName(), algorithm, testCase.getSiteCounter(), testCase.getKeyPurpose(), + testCase.getKeyContext(), resultType, password ); + String result = masterKey.siteResult( testCase.getSiteName(), algorithm, testCase.getSiteCounter(), testCase.getKeyPurpose(), + testCase.getKeyContext(), resultType, state ); assertEquals( result, diff --git a/platform-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java b/platform-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java index a062785d..81a5dcb1 100644 --- a/platform-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java +++ b/platform-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java @@ -58,7 +58,7 @@ public class EmergencyActivity extends Activity { Executors.newSingleThreadExecutor() ); private final ImmutableList allResultTypes = ImmutableList.copyOf( MPResultType.forClass( MPResultTypeClass.Template ) ); - private final ImmutableList allVersions = ImmutableList.copyOf( MPMasterKey.Version.values() ); + private final ImmutableList allVersions = ImmutableList.copyOf( MPAlgorithm.Version.values() ); @Nullable private MPMasterKey masterKey; @@ -160,7 +160,7 @@ public class EmergencyActivity extends Activity { @Override public void onClick(final View v) { @SuppressWarnings("SuspiciousMethodCalls") - MPMasterKey.Version siteVersion = + MPAlgorithm.Version siteVersion = allVersions.get( (allVersions.indexOf( siteVersionButton.getTag() ) + 1) % allVersions.size() ); preferences.setDefaultVersion( siteVersion ); siteVersionButton.setTag( siteVersion ); @@ -227,7 +227,7 @@ public class EmergencyActivity extends Activity { MPResultType defaultResultType = preferences.getDefaultResultType(); resultTypeButton.setTag( defaultResultType ); resultTypeButton.setText( defaultResultType.getShortName() ); - MPMasterKey.Version defaultVersion = preferences.getDefaultVersion(); + MPAlgorithm.Version defaultVersion = preferences.getDefaultVersion(); siteVersionButton.setTag( defaultVersion ); siteVersionButton.setText( defaultVersion.name() ); siteCounterButton.setText( MessageFormat.format( "{0}", 1 ) ); @@ -289,7 +289,7 @@ public class EmergencyActivity extends Activity { final String siteName = siteNameField.getText().toString(); final MPResultType type = (MPResultType) resultTypeButton.getTag(); 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)) { sitePasswordField.setText( "" ); @@ -306,8 +306,8 @@ public class EmergencyActivity extends Activity { @Override public void run() { try { - sitePassword = masterKey.siteResult( siteName, counter, MPKeyPurpose.Authentication, null, type, null, - version.getAlgorithm() ); + sitePassword = masterKey.siteResult( siteName, version.getAlgorithm(), counter, MPKeyPurpose.Authentication, null, type, null + ); runOnUiThread( new Runnable() { @Override diff --git a/platform-android/src/main/java/com/lyndir/masterpassword/Preferences.java b/platform-android/src/main/java/com/lyndir/masterpassword/Preferences.java index 746bb0eb..cae9e7ba 100644 --- a/platform-android/src/main/java/com/lyndir/masterpassword/Preferences.java +++ b/platform-android/src/main/java/com/lyndir/masterpassword/Preferences.java @@ -151,7 +151,7 @@ public final class Preferences { 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) return false; @@ -160,7 +160,7 @@ public final class Preferences { } @Nonnull - public MPMasterKey.Version getDefaultVersion() { - return MPMasterKey.Version.values()[prefs().getInt( PREF_ALGORITHM_VERSION, MPMasterKey.Version.CURRENT.ordinal() )]; + public MPAlgorithm.Version getDefaultVersion() { + return MPAlgorithm.Version.values()[prefs().getInt( PREF_ALGORITHM_VERSION, MPAlgorithm.Version.CURRENT.ordinal() )]; } } diff --git a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/GUI.java b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/GUI.java index 17e11f13..23ab067a 100644 --- a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/GUI.java +++ b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/GUI.java @@ -112,14 +112,11 @@ public class GUI implements UnlockFrame.SignInCallback { } protected void open() { - SwingUtilities.invokeLater( new Runnable() { - @Override - public void run() { - if (passwordFrame == null) - unlockFrame.setVisible( true ); - else - passwordFrame.setVisible( true ); - } + SwingUtilities.invokeLater( () -> { + if (passwordFrame == null) + unlockFrame.setVisible( true ); + else + passwordFrame.setVisible( true ); } ); } diff --git a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/Res.java b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/Res.java index b5f1fe0a..7e8f9e7d 100644 --- a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/Res.java +++ b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/Res.java @@ -60,15 +60,12 @@ public abstract class Res { } public static Future schedule(final Window host, final Runnable job, final long delay, final TimeUnit timeUnit) { - return getExecutor( host ).schedule( new Runnable() { - @Override - public void run() { - try { - job.run(); - } - catch (final Throwable t) { - logger.err( t, "Unexpected: %s", t.getLocalizedMessage() ); - } + return getExecutor( host ).schedule( () -> { + try { + job.run(); + } + catch (final Throwable t) { + logger.err( t, "Unexpected: %s", t.getLocalizedMessage() ); } }, delay, timeUnit ); } @@ -79,17 +76,13 @@ public abstract class Res { public static ListenableFuture schedule(final Window host, final Callable job, final long delay, final TimeUnit timeUnit) { ScheduledExecutorService executor = getExecutor( host ); - return JdkFutureAdapters.listenInPoolThread( executor.schedule( new Callable() { - @Override - public V call() - throws Exception { - try { - return job.call(); - } - catch (final Throwable t) { - logger.err( t, "Unexpected: %s", t.getLocalizedMessage() ); - throw Throwables.propagate( t ); - } + return JdkFutureAdapters.listenInPoolThread( executor.schedule( () -> { + try { + return job.call(); + } + catch (final Throwable t) { + logger.err( t, "Unexpected: %s", t.getLocalizedMessage() ); + throw Throwables.propagate( t ); } }, delay, timeUnit ), executor ); } diff --git a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/IncognitoSite.java b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/MPIncognitoSite.java similarity index 79% rename from platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/IncognitoSite.java rename to platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/MPIncognitoSite.java index 3157128f..e560ab1e 100644 --- a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/IncognitoSite.java +++ b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/MPIncognitoSite.java @@ -31,17 +31,17 @@ import javax.annotation.Nullable; /** * @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 ); } - public IncognitoSite(final IncognitoUser user, final String name, - @Nullable final MPAlgorithm algorithm, @Nullable final UnsignedInteger counter, - @Nullable final MPResultType resultType, @Nullable final MPResultType loginType) { + public MPIncognitoSite(final MPIncognitoUser user, final String name, + @Nullable final MPAlgorithm algorithm, @Nullable final UnsignedInteger counter, + @Nullable final MPResultType resultType, @Nullable final MPResultType loginType) { super( name, (algorithm == null)? user.getAlgorithm(): algorithm, counter, resultType, loginType ); this.user = user; diff --git a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/IncognitoUser.java b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/MPIncognitoUser.java similarity index 83% rename from platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/IncognitoUser.java rename to platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/MPIncognitoUser.java index e6b52b3c..63e490bf 100755 --- a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/IncognitoUser.java +++ b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/MPIncognitoUser.java @@ -18,7 +18,7 @@ package com.lyndir.masterpassword.gui.model; -import com.lyndir.masterpassword.MPMasterKey; +import com.lyndir.masterpassword.MPAlgorithm; import com.lyndir.masterpassword.model.impl.MPBasicUser; import javax.annotation.Nullable; @@ -26,10 +26,10 @@ import javax.annotation.Nullable; /** * @author lhunath, 2014-06-08 */ -public class IncognitoUser extends MPBasicUser { +public class MPIncognitoUser extends MPBasicUser { - public IncognitoUser(final String fullName) { - super(fullName, MPMasterKey.Version.CURRENT.getAlgorithm()); + public MPIncognitoUser(final String fullName) { + super( fullName, MPAlgorithm.Version.CURRENT.getAlgorithm()); } @Nullable diff --git a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/IncognitoAuthenticationPanel.java b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/IncognitoAuthenticationPanel.java index 8f173b98..b88be32f 100644 --- a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/IncognitoAuthenticationPanel.java +++ b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/IncognitoAuthenticationPanel.java @@ -23,8 +23,8 @@ import com.google.common.primitives.UnsignedInteger; import com.lyndir.masterpassword.MPAlgorithm; import com.lyndir.masterpassword.MPResultType; import com.lyndir.masterpassword.gui.Res; -import com.lyndir.masterpassword.gui.model.IncognitoSite; -import com.lyndir.masterpassword.gui.model.IncognitoUser; +import com.lyndir.masterpassword.gui.model.MPIncognitoSite; +import com.lyndir.masterpassword.gui.model.MPIncognitoUser; import com.lyndir.masterpassword.gui.util.Components; import java.awt.*; import java.awt.event.ActionEvent; @@ -40,7 +40,7 @@ import javax.swing.event.DocumentListener; * @author lhunath, 2014-06-11 */ @SuppressWarnings({ "serial", "MagicNumber" }) -public class IncognitoAuthenticationPanel extends AuthenticationPanel implements DocumentListener, ActionListener { +public class IncognitoAuthenticationPanel extends AuthenticationPanel implements DocumentListener, ActionListener { private final JTextField fullNameField; private final JPasswordField masterPasswordField; @@ -82,20 +82,20 @@ public class IncognitoAuthenticationPanel extends AuthenticationPanel newPasswordFrame() { - return new PasswordFrame( Preconditions.checkNotNull( getSelectedUser() ) ) { + public PasswordFrame newPasswordFrame() { + return new PasswordFrame( Preconditions.checkNotNull( getSelectedUser() ) ) { @Override - protected IncognitoSite createSite(final IncognitoUser user, final String siteName, final UnsignedInteger siteCounter, - final MPResultType resultType, final MPAlgorithm algorithm) { - return new IncognitoSite( user, siteName, algorithm, siteCounter, resultType, null ); + protected MPIncognitoSite createSite(final MPIncognitoUser user, final String siteName, final UnsignedInteger siteCounter, + final MPResultType resultType, final MPAlgorithm algorithm) { + return new MPIncognitoSite( user, siteName, algorithm, siteCounter, resultType, null ); } }; } @Nullable @Override - protected IncognitoUser getSelectedUser() { - return new IncognitoUser( fullNameField.getText() ); + protected MPIncognitoUser getSelectedUser() { + return new MPIncognitoUser( fullNameField.getText() ); } @Nonnull diff --git a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/PasswordFrame.java b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/PasswordFrame.java index 63bea042..f4aa5614 100755 --- a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/PasswordFrame.java +++ b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/PasswordFrame.java @@ -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.StringUtils.*; -import com.google.common.base.Predicate; -import com.google.common.collect.FluentIterable; import com.google.common.collect.Iterables; import com.google.common.primitives.UnsignedInteger; 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.util.Components; import com.lyndir.masterpassword.gui.util.UnsignedIntegerModel; -import com.lyndir.masterpassword.model.*; -import com.lyndir.masterpassword.model.impl.MPBasicSite; -import com.lyndir.masterpassword.model.impl.MPFileSite; -import com.lyndir.masterpassword.model.impl.MPFileUser; +import com.lyndir.masterpassword.model.MPUser; +import com.lyndir.masterpassword.model.impl.*; import java.awt.*; import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.Transferable; 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.Nullable; 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, S extends MPBasicSite> private final Components.GradientPanel root; private final JTextField siteNameField; private final JButton siteActionButton; - private final JComboBox siteVersionField; + private final JComboBox siteVersionField; private final JSpinner siteCounterField; private final UnsignedIntegerModel siteCounterModel; private final JComboBox resultTypeField; @@ -105,14 +103,11 @@ public abstract class PasswordFrame, S extends MPBasicSite> Transferable clipboardContents = new StringSelection( sitePassword ); Toolkit.getDefaultToolkit().getSystemClipboard().setContents( clipboardContents, null ); - SwingUtilities.invokeLater( new Runnable() { - @Override - public void run() { - passwordField.setText( null ); - siteNameField.setText( null ); + SwingUtilities.invokeLater( () -> { + 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, S extends MPBasicSite> } ); } } ); - siteActionButton.addActionListener( new ActionListener() { - @Override - public void actionPerformed(final ActionEvent e) { - if (currentSite == null) - return; - if (currentSite instanceof MPFileSite) - PasswordFrame.this.user.deleteSite( currentSite ); - else - PasswordFrame.this.user.addSite( currentSite ); - siteNameField.requestFocus(); + siteActionButton.addActionListener( e -> { + if (currentSite == null) + return; + if (currentSite instanceof MPFileSite) + this.user.deleteSite( currentSite ); + else + this.user.addSite( currentSite ); + siteNameField.requestFocus(); - updatePassword( true ); - } + updatePassword( true ); } ); sitePanel.add( siteControls ); sitePanel.add( Components.stud() ); @@ -145,48 +137,28 @@ public abstract class PasswordFrame, S extends MPBasicSite> JComponent siteSettings = Components.boxLayout( BoxLayout.LINE_AXIS, // resultTypeField = Components.comboBox( types ), // Components.stud(), // - siteVersionField = Components.comboBox( MPMasterKey.Version.values() ), // + siteVersionField = Components.comboBox( MPAlgorithm.Version.values() ), // Components.stud(), // siteCounterField = Components.spinner( siteCounterModel ) ); sitePanel.add( siteSettings ); resultTypeField.setFont( Res.valueFont().deriveFont( resultTypeField.getFont().getSize2D() ) ); resultTypeField.setSelectedItem( user.getAlgorithm().mpw_default_result_type() ); - resultTypeField.addItemListener( new ItemListener() { - @Override - public void itemStateChanged(final ItemEvent e) { - updatePassword( true ); - } - } ); + resultTypeField.addItemListener( e -> updatePassword( true ) ); siteVersionField.setFont( Res.valueFont().deriveFont( siteVersionField.getFont().getSize2D() ) ); siteVersionField.setAlignmentX( RIGHT_ALIGNMENT ); siteVersionField.setSelectedItem( user.getAlgorithm() ); - siteVersionField.addItemListener( new ItemListener() { - @Override - public void itemStateChanged(final ItemEvent e) { - updatePassword( true ); - } - } ); + siteVersionField.addItemListener( e -> updatePassword( true ) ); siteCounterField.setFont( Res.valueFont().deriveFont( siteCounterField.getFont().getSize2D() ) ); siteCounterField.setAlignmentX( RIGHT_ALIGNMENT ); - siteCounterField.addChangeListener( new ChangeListener() { - @Override - public void stateChanged(final ChangeEvent e) { - updatePassword( true ); - } - } ); + siteCounterField.addChangeListener( e -> updatePassword( true ) ); // Mask maskPasswordField = Components.checkBox( "Hide Password" ); maskPasswordField.setAlignmentX( Component.CENTER_ALIGNMENT ); maskPasswordField.setSelected( true ); - maskPasswordField.addItemListener( new ItemListener() { - @Override - public void itemStateChanged(final ItemEvent e) { - updateMask(); - } - } ); + maskPasswordField.addItemListener( e -> updateMask() ); // Password passwordField = Components.passwordField(); @@ -229,7 +201,7 @@ public abstract class PasswordFrame, S extends MPBasicSite> @Nonnull private ListenableFuture updatePassword(final boolean allowNameCompletion) { - final String siteNameQuery = siteNameField.getText(); + String siteNameQuery = siteNameField.getText(); if (updatingUI) return Futures.immediateCancelledFuture(); if ((siteNameQuery == null) || siteNameQuery.isEmpty() || !user.isMasterKeyAvailable()) { @@ -243,53 +215,40 @@ public abstract class PasswordFrame, S extends MPBasicSite> MPAlgorithm siteAlgorithm = siteVersionField.getItemAt( siteVersionField.getSelectedIndex() ).getAlgorithm(); UnsignedInteger siteCounter = siteCounterModel.getNumber(); - Iterable siteResults = user.findSites( siteNameQuery ); + Collection siteResults = user.findSites( siteNameQuery ); if (!allowNameCompletion) - siteResults = FluentIterable.from( siteResults ).filter( new Predicate() { - @Override - public boolean apply(@Nullable final S siteResult) { - return (siteResult != null) && siteNameQuery.equals( siteResult.getName() ); - } - } ); - final S site = ifNotNullElse( Iterables.getFirst( siteResults, null ), - createSite( user, siteNameQuery, siteCounter, resultType, siteAlgorithm ) ); + siteResults = siteResults.stream().filter( + siteResult -> (siteResult != null) && siteNameQuery.equals( siteResult.getName() ) ).collect( Collectors.toList() ); + S site = ifNotNullElse( Iterables.getFirst( siteResults, null ), + createSite( user, siteNameQuery, siteCounter, resultType, siteAlgorithm ) ); if ((currentSite != null) && currentSite.getName().equals( site.getName() )) { site.setResultType( resultType ); site.setAlgorithm( siteAlgorithm ); site.setCounter( siteCounter ); } - ListenableFuture passwordFuture = Res.execute( this, new Callable() { - @Override - public String call() - throws Exception { - return site.getResult( MPKeyPurpose.Authentication, null, null ); - } - } ); + ListenableFuture passwordFuture = Res.execute( this, () -> site.getResult( MPKeyPurpose.Authentication, null, null ) ); Futures.addCallback( passwordFuture, new FutureCallback() { @Override public void onSuccess(@Nullable final String sitePassword) { - SwingUtilities.invokeLater( new Runnable() { - @Override - public void run() { - updatingUI = true; - currentSite = site; - siteActionButton.setVisible( user instanceof MPFileUser ); - if (currentSite instanceof MPFileSite) - siteActionButton.setText( "Delete Site" ); - else - siteActionButton.setText( "Add Site" ); - resultTypeField.setSelectedItem( currentSite.getResultType() ); - siteVersionField.setSelectedItem( currentSite.getAlgorithm() ); - siteCounterField.setValue( currentSite.getCounter() ); - siteNameField.setText( currentSite.getName() ); - if (siteNameField.getText().startsWith( siteNameQuery )) - siteNameField.select( siteNameQuery.length(), siteNameField.getText().length() ); + SwingUtilities.invokeLater( () -> { + updatingUI = true; + currentSite = site; + siteActionButton.setVisible( user instanceof MPFileUser ); + if (currentSite instanceof MPFileSite) + siteActionButton.setText( "Delete Site" ); + else + siteActionButton.setText( "Add Site" ); + resultTypeField.setSelectedItem( currentSite.getResultType() ); + siteVersionField.setSelectedItem( currentSite.getAlgorithm() ); + siteCounterField.setValue( currentSite.getCounter() ); + siteNameField.setText( currentSite.getName() ); + if (siteNameField.getText().startsWith( siteNameQuery )) + siteNameField.select( siteNameQuery.length(), siteNameField.getText().length() ); - passwordField.setText( sitePassword ); - tipLabel.setText( "Press [Enter] to copy the password. Then paste it into the password field." ); - updatingUI = false; - } + passwordField.setText( sitePassword ); + tipLabel.setText( "Press [Enter] to copy the password. Then paste it into the password field." ); + updatingUI = false; } ); } diff --git a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/UnlockFrame.java b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/UnlockFrame.java index b0175500..ff9dbb69 100644 --- a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/UnlockFrame.java +++ b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/UnlockFrame.java @@ -119,20 +119,12 @@ public class UnlockFrame extends JFrame { authenticationContainer.add( authenticationPanel ); authenticationContainer.add( Components.stud() ); - final JCheckBox incognitoCheckBox = Components.checkBox( "Incognito" ); + JCheckBox incognitoCheckBox = Components.checkBox( "Incognito" ); incognitoCheckBox.setToolTipText( "Log in without saving any information." ); incognitoCheckBox.setSelected( incognito ); - incognitoCheckBox.addItemListener( new ItemListener() { - @Override - public void itemStateChanged(final ItemEvent e) { - incognito = incognitoCheckBox.isSelected(); - SwingUtilities.invokeLater( new Runnable() { - @Override - public void run() { - createAuthenticationPanel(); - } - } ); - } + incognitoCheckBox.addItemListener( e -> { + incognito = incognitoCheckBox.isSelected(); + SwingUtilities.invokeLater( this::createAuthenticationPanel ); } ); JComponent toolsPanel = Components.boxLayout( BoxLayout.LINE_AXIS, incognitoCheckBox, Box.createGlue() ); @@ -149,12 +141,7 @@ public class UnlockFrame extends JFrame { validate(); repack(); - SwingUtilities.invokeLater( new Runnable() { - @Override - public void run() { - ifNotNullElse( authenticationPanel.getFocusComponent(), signInButton ).requestFocusInWindow(); - } - } ); + SwingUtilities.invokeLater( () -> ifNotNullElse( authenticationPanel.getFocusComponent(), signInButton ).requestFocusInWindow() ); } void updateUser(@Nullable final MPUser user) { @@ -165,28 +152,20 @@ public class UnlockFrame extends JFrame { boolean checkSignIn() { if (identiconFuture != null) identiconFuture.cancel( false ); - identiconFuture = Res.schedule( this, new Runnable() { - @Override - public void run() { - SwingUtilities.invokeLater( new Runnable() { - @Override - public void run() { - String fullName = (user == null)? "": user.getFullName(); - char[] masterPassword = authenticationPanel.getMasterPassword(); + identiconFuture = Res.schedule( this, () -> SwingUtilities.invokeLater( () -> { + String fullName = (user == null)? "": user.getFullName(); + char[] masterPassword = authenticationPanel.getMasterPassword(); - if (fullName.isEmpty() || (masterPassword.length == 0)) { - identiconLabel.setText( " " ); - return; - } - - MPIdenticon identicon = new MPIdenticon( fullName, masterPassword ); - identiconLabel.setText( identicon.getText() ); - identiconLabel.setForeground( - Res.colors().fromIdenticonColor( identicon.getColor(), Res.Colors.BackgroundMode.DARK ) ); - } - } ); + if (fullName.isEmpty() || (masterPassword.length == 0)) { + identiconLabel.setText( " " ); + return; } - }, 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(); char[] masterPassword = authenticationPanel.getMasterPassword(); @@ -206,37 +185,29 @@ public class UnlockFrame extends JFrame { signInButton.setEnabled( false ); signInButton.setText( "Signing In..." ); - Res.execute( this, new Runnable() { - @Override - public void run() { - try { - user.authenticate( authenticationPanel.getMasterPassword() ); + Res.execute( this, () -> { + try { + user.authenticate( authenticationPanel.getMasterPassword() ); - SwingUtilities.invokeLater( new Runnable() { - @Override - public void run() { - signInCallback.signedIn( authenticationPanel.newPasswordFrame() ); - dispose(); - } - } ); - } - catch (final MPIncorrectMasterPasswordException e) { - SwingUtilities.invokeLater( new Runnable() { - @Override - public void run() { - JOptionPane.showMessageDialog( null, e.getLocalizedMessage(), "Sign In Failed", JOptionPane.ERROR_MESSAGE ); - authenticationPanel.reset(); - signInButton.setText( "Sign In" ); - for (final JComponent signInComponent : signInComponents) - signInComponent.setEnabled( true ); - checkSignIn(); - } - } ); - } + SwingUtilities.invokeLater( () -> { + signInCallback.signedIn( authenticationPanel.newPasswordFrame() ); + dispose(); + } ); + } + catch (final MPIncorrectMasterPasswordException e) { + SwingUtilities.invokeLater( () -> { + JOptionPane.showMessageDialog( null, e.getLocalizedMessage(), "Sign In Failed", JOptionPane.ERROR_MESSAGE ); + authenticationPanel.reset(); + signInButton.setText( "Sign In" ); + for (final JComponent signInComponent : signInComponents) + signInComponent.setEnabled( true ); + checkSignIn(); + } ); } } ); } + @FunctionalInterface public interface SignInCallback { void signedIn(PasswordFrame passwordFrame);