Refactoring masterpassword-algorithm.
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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();
|
||||
|
@@ -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<Version, byte[]> keyByVersion = new EnumMap<>( Version.class );
|
||||
private final String fullName;
|
||||
private final char[] masterPassword;
|
||||
private final EnumMap<MPAlgorithm.Version, byte[]> 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 );
|
||||
}
|
||||
}
|
||||
|
@@ -16,7 +16,7 @@
|
||||
// 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.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
|
@@ -16,22 +16,23 @@
|
||||
// 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.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;
|
||||
}
|
||||
}
|
@@ -16,23 +16,25 @@
|
||||
// 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.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;
|
||||
}
|
||||
}
|
@@ -16,16 +16,18 @@
|
||||
// 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.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;
|
||||
}
|
||||
}
|
@@ -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 {
|
||||
|
||||
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
|
||||
|
@@ -49,7 +49,7 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
|
||||
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) {
|
||||
|
@@ -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 ) ) ),
|
||||
|
@@ -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
|
||||
|
@@ -168,17 +168,13 @@ public class MPTestSuite implements Callable<Boolean> {
|
||||
@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<Boolean> {
|
||||
}
|
||||
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Listener {
|
||||
|
||||
void progress(int current, int max, String messageFormat, Object... args);
|
||||
}
|
||||
|
||||
|
||||
@FunctionalInterface
|
||||
public interface TestCase {
|
||||
|
||||
boolean run(MPTests.Case testCase)
|
||||
|
@@ -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
|
||||
|
@@ -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,
|
||||
|
Reference in New Issue
Block a user