2
0

Update Java to match C's internal changes.

This commit is contained in:
Maarten Billemont 2017-09-19 13:45:51 -04:00
parent 70c784db83
commit 35c0431cec
42 changed files with 589 additions and 3034 deletions

View File

@ -50,14 +50,11 @@ const MPResultType mpw_typeWithName(const char *typeName) {
return MPResultTypeDeriveKey; return MPResultTypeDeriveKey;
} }
// Lower-case and trim optionally leading "Generated" string from typeName to standardize it. // Lower-case typeName to standardize it.
size_t stdTypeNameOffset = 0;
size_t stdTypeNameSize = strlen( typeName ); size_t stdTypeNameSize = strlen( typeName );
if (strstr( typeName, "Generated" ) == typeName)
stdTypeNameSize -= (stdTypeNameOffset = strlen( "Generated" ));
char stdTypeName[stdTypeNameSize + 1]; char stdTypeName[stdTypeNameSize + 1];
for (size_t c = 0; c < stdTypeNameSize; ++c) for (size_t c = 0; c < stdTypeNameSize; ++c)
stdTypeName[c] = (char)tolower( typeName[c + stdTypeNameOffset] ); stdTypeName[c] = (char)tolower( typeName[c] );
stdTypeName[stdTypeNameSize] = '\0'; stdTypeName[stdTypeNameSize] = '\0';
// Find what password type is represented by the type name. // Find what password type is represented by the type name.

View File

@ -8,6 +8,7 @@ dependencies {
compile (group: 'com.lyndir.lhunath.opal', name: 'opal-system', version: '1.6-p10') { compile (group: 'com.lyndir.lhunath.opal', name: 'opal-system', version: '1.6-p10') {
exclude( module: 'joda-time' ) exclude( module: 'joda-time' )
} }
compile group: 'com.lyndir.lhunath.opal', name: 'opal-crypto', version: '1.6-p10'
compile group: 'com.lambdaworks', name: 'scrypt', version: '1.4.0' compile group: 'com.lambdaworks', name: 'scrypt', version: '1.4.0'
compile group: 'org.jetbrains', name: 'annotations', version: '13.0' compile group: 'org.jetbrains', name: 'annotations', version: '13.0'

View File

@ -31,6 +31,11 @@
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>com.lyndir.lhunath.opal</groupId>
<artifactId>opal-crypto</artifactId>
<version>1.6-p9</version>
</dependency>
<!-- EXTERNAL DEPENDENCIES --> <!-- EXTERNAL DEPENDENCIES -->
<dependency> <dependency>

View File

@ -32,68 +32,16 @@ public final class MPConstant {
/* Environment */ /* Environment */
/**
* mpw: default user name if one is not provided.
*/
public static final String env_userName = "MP_USERNAME";
/**
* mpw: default site type if one is not provided.
*
* @see MPSiteType#forOption(String)
*/
public static final String env_siteType = "MP_SITETYPE";
/**
* mpw: default site counter value if one is not provided.
*/
public static final String env_siteCounter = "MP_SITECOUNTER";
/** /**
* mpw: default path to look for run configuration files if the platform default is not desired. * mpw: default path to look for run configuration files if the platform default is not desired.
*/ */
public static final String env_rcDir = "MP_RCDIR"; public static final String env_rcDir = "MPW_RCDIR";
/** /**
* mpw: permit automatic update checks. * mpw: permit automatic update checks.
*/ */
public static final String env_checkUpdates = "MP_CHECKUPDATES"; public static final String env_checkUpdates = "MPW_CHECKUPDATES";
/* Algorithm */ /* Algorithm */
/**
* scrypt: CPU cost parameter.
*/
public static final int scrypt_N = 32768;
/**
* scrypt: Memory cost parameter.
*/
public static final int scrypt_r = 8;
/**
* scrypt: Parallelization parameter.
*/
public static final int scrypt_p = 2;
/**
* mpw: Master key size (byte).
*/
public static final int mpw_dkLen = 64;
/**
* mpw: Input character encoding.
*/
public static final Charset mpw_charset = Charsets.UTF_8;
/**
* mpw: Platform-agnostic byte order.
*/
public static final ByteOrder mpw_byteOrder = ByteOrder.BIG_ENDIAN;
/**
* mpw: Site digest.
*/
public static final MessageAuthenticationDigests mpw_digest = MessageAuthenticationDigests.HmacSHA256;
/**
* mpw: Key ID hash.
*/
public static final MessageDigests mpw_hash = MessageDigests.SHA256;
/**
* mpw: validity for the time-based rolling counter.
*/
public static final int mpw_counter_timeout = 5 * 60 /* s */;
public static final int MS_PER_S = 1000; public static final int MS_PER_S = 1000;
} }

View File

@ -0,0 +1,90 @@
//==============================================================================
// 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/>.
//==============================================================================
package com.lyndir.masterpassword;
import com.google.common.collect.ImmutableList;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.util.List;
import java.util.Locale;
import javax.annotation.Nullable;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NonNls;
/**
* @author lhunath, 14-12-02
*/
public enum MPKeyPurpose {
Password( "authentication", "Generate a key for authentication.", "com.lyndir.masterpassword" ),
Login( "identification", "Generate a name for identification.", "com.lyndir.masterpassword.login" ),
Answer( "recovery", "Generate an account recovery token.", "com.lyndir.masterpassword.answer" );
static final Logger logger = Logger.get( MPResultType.class );
private final String shortName;
private final String description;
private final String scope;
MPKeyPurpose(final String shortName, final String description, @NonNls final String scope) {
this.shortName = shortName;
this.description = description;
this.scope = scope;
}
public String getShortName() {
return shortName;
}
public String getDescription() {
return description;
}
public String getScope() {
return scope;
}
/**
* @param shortNamePrefix The name for the purpose to look up. It is a case insensitive prefix of the purpose's short name.
*
* @return The purpose registered with the given name.
*/
@Nullable
@Contract("!null -> !null")
public static MPKeyPurpose forName(@Nullable final String shortNamePrefix) {
if (shortNamePrefix == null)
return null;
for (final MPKeyPurpose type : values())
if (type.getShortName().toLowerCase( Locale.ROOT ).startsWith( shortNamePrefix.toLowerCase( Locale.ROOT ) ))
return type;
throw logger.bug( "No purpose for name: %s", shortNamePrefix );
}
public static MPKeyPurpose forInt(final int keyPurpose) {
return values()[keyPurpose];
}
public int toInt() {
return ordinal();
}
}

View File

@ -24,7 +24,6 @@ import com.lyndir.lhunath.opal.system.logging.Logger;
import java.util.*; import java.util.*;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NonNls;
/** /**
@ -32,15 +31,13 @@ import org.jetbrains.annotations.NonNls;
* *
* @author lhunath * @author lhunath
*/ */
public enum MPSiteType { public enum MPResultType {
GeneratedMaximum( "Max", "20 characters, contains symbols.", // GeneratedMaximum( "Maximum", "20 characters, contains symbols.", //
ImmutableList.of( "x", "max", "maximum" ), // NON-NLS
ImmutableList.of( new MPTemplate( "anoxxxxxxxxxxxxxxxxx" ), new MPTemplate( "axxxxxxxxxxxxxxxxxno" ) ), // ImmutableList.of( new MPTemplate( "anoxxxxxxxxxxxxxxxxx" ), new MPTemplate( "axxxxxxxxxxxxxxxxxno" ) ), //
MPSiteTypeClass.Generated, 0x0 ), MPResultTypeClass.Generated, 0x0 ),
GeneratedLong( "Long", "Copy-friendly, 14 characters, contains symbols.", // GeneratedLong( "Long", "Copy-friendly, 14 characters, contains symbols.", //
ImmutableList.of( "l", "long" ), // NON-NLS
ImmutableList.of( new MPTemplate( "CvcvnoCvcvCvcv" ), new MPTemplate( "CvcvCvcvnoCvcv" ), ImmutableList.of( new MPTemplate( "CvcvnoCvcvCvcv" ), new MPTemplate( "CvcvCvcvnoCvcv" ),
new MPTemplate( "CvcvCvcvCvcvno" ), new MPTemplate( "CvccnoCvcvCvcv" ), new MPTemplate( "CvcvCvcvCvcvno" ), new MPTemplate( "CvccnoCvcvCvcv" ),
new MPTemplate( "CvccCvcvnoCvcv" ), new MPTemplate( "CvccCvcvCvcvno" ), new MPTemplate( "CvccCvcvnoCvcv" ), new MPTemplate( "CvccCvcvCvcvno" ),
@ -52,65 +49,55 @@ public enum MPSiteType {
new MPTemplate( "CvcvCvccnoCvcc" ), new MPTemplate( "CvcvCvccCvccno" ), new MPTemplate( "CvcvCvccnoCvcc" ), new MPTemplate( "CvcvCvccCvccno" ),
new MPTemplate( "CvccnoCvcvCvcc" ), new MPTemplate( "CvccCvcvnoCvcc" ), new MPTemplate( "CvccnoCvcvCvcc" ), new MPTemplate( "CvccCvcvnoCvcc" ),
new MPTemplate( "CvccCvcvCvccno" ) ), // new MPTemplate( "CvccCvcvCvccno" ) ), //
MPSiteTypeClass.Generated, 0x1 ), MPResultTypeClass.Generated, 0x1 ),
GeneratedMedium( "Medium", "Copy-friendly, 8 characters, contains symbols.", // GeneratedMedium( "Medium", "Copy-friendly, 8 characters, contains symbols.", //
ImmutableList.of( "m", "med", "medium" ), // NON-NLS
ImmutableList.of( new MPTemplate( "CvcnoCvc" ), new MPTemplate( "CvcCvcno" ) ), // ImmutableList.of( new MPTemplate( "CvcnoCvc" ), new MPTemplate( "CvcCvcno" ) ), //
MPSiteTypeClass.Generated, 0x2 ), MPResultTypeClass.Generated, 0x2 ),
GeneratedBasic( "Basic", "8 characters, no symbols.", // GeneratedBasic( "Basic", "8 characters, no symbols.", //
ImmutableList.of( "b", "basic" ), // NON-NLS
ImmutableList.of( new MPTemplate( "aaanaaan" ), new MPTemplate( "aannaaan" ), new MPTemplate( "aaannaaa" ) ), // ImmutableList.of( new MPTemplate( "aaanaaan" ), new MPTemplate( "aannaaan" ), new MPTemplate( "aaannaaa" ) ), //
MPSiteTypeClass.Generated, 0x3 ), MPResultTypeClass.Generated, 0x3 ),
GeneratedShort( "Short", "Copy-friendly, 4 characters, no symbols.", // GeneratedShort( "Short", "Copy-friendly, 4 characters, no symbols.", //
ImmutableList.of( "s", "short" ), // NON-NLS
ImmutableList.of( new MPTemplate( "Cvcn" ) ), // ImmutableList.of( new MPTemplate( "Cvcn" ) ), //
MPSiteTypeClass.Generated, 0x4 ), MPResultTypeClass.Generated, 0x4 ),
GeneratedPIN( "PIN", "4 numbers.", // GeneratedPIN( "PIN", "4 numbers.", //
ImmutableList.of( "i", "pin" ), // NON-NLS
ImmutableList.of( new MPTemplate( "nnnn" ) ), // ImmutableList.of( new MPTemplate( "nnnn" ) ), //
MPSiteTypeClass.Generated, 0x5 ), MPResultTypeClass.Generated, 0x5 ),
GeneratedName( "Name", "9 letter name.", // GeneratedName( "Name", "9 letter name.", //
ImmutableList.of( "n", "name" ), // NON-NLS
ImmutableList.of( new MPTemplate( "cvccvcvcv" ) ), // ImmutableList.of( new MPTemplate( "cvccvcvcv" ) ), //
MPSiteTypeClass.Generated, 0xE ), MPResultTypeClass.Generated, 0xE ),
GeneratedPhrase( "Phrase", "20 character sentence.", // GeneratedPhrase( "Phrase", "20 character sentence.", //
ImmutableList.of( "p", "phrase" ), // NON-NLS
ImmutableList.of( new MPTemplate( "cvcc cvc cvccvcv cvc" ), new MPTemplate( "cvc cvccvcvcv cvcv" ), ImmutableList.of( new MPTemplate( "cvcc cvc cvccvcv cvc" ), new MPTemplate( "cvc cvccvcvcv cvcv" ),
new MPTemplate( "cv cvccv cvc cvcvccv" ) ), // new MPTemplate( "cv cvccv cvc cvcvccv" ) ), //
MPSiteTypeClass.Generated, 0xF ), MPResultTypeClass.Generated, 0xF ),
StoredPersonal( "Personal", "AES-encrypted, exportable.", // StoredPersonal( "Personal", "AES-encrypted, exportable.", //
ImmutableList.of( "personal" ), // NON-NLS
ImmutableList.<MPTemplate>of(), // ImmutableList.<MPTemplate>of(), //
MPSiteTypeClass.Stored, 0x0, MPSiteFeature.ExportContent ), MPResultTypeClass.Stored, 0x0, MPSiteFeature.ExportContent ),
StoredDevicePrivate( "Device", "AES-encrypted, not exported.", // StoredDevicePrivate( "Device", "AES-encrypted, not exported.", //
ImmutableList.of( "device" ), // NON-NLS
ImmutableList.<MPTemplate>of(), // ImmutableList.<MPTemplate>of(), //
MPSiteTypeClass.Stored, 0x1, MPSiteFeature.DevicePrivate ); MPResultTypeClass.Stored, 0x1, MPSiteFeature.DevicePrivate );
static final Logger logger = Logger.get( MPSiteType.class ); static final Logger logger = Logger.get( MPResultType.class );
private final String shortName; private final String shortName;
private final String description; private final String description;
private final List<String> options;
private final List<MPTemplate> templates; private final List<MPTemplate> templates;
private final MPSiteTypeClass typeClass; private final MPResultTypeClass typeClass;
private final int typeIndex; private final int typeIndex;
private final Set<MPSiteFeature> typeFeatures; private final Set<MPSiteFeature> typeFeatures;
MPSiteType(final String shortName, final String description, final List<String> options, final List<MPTemplate> templates, MPResultType(final String shortName, final String description, final List<MPTemplate> templates,
final MPSiteTypeClass typeClass, final int typeIndex, final MPSiteFeature... typeFeatures) { final MPResultTypeClass typeClass, final int typeIndex, final MPSiteFeature... typeFeatures) {
this.shortName = shortName; this.shortName = shortName;
this.description = description; this.description = description;
this.options = options;
this.templates = templates; this.templates = templates;
this.typeClass = typeClass; this.typeClass = typeClass;
this.typeIndex = typeIndex; this.typeIndex = typeIndex;
@ -131,11 +118,7 @@ public enum MPSiteType {
return description; return description;
} }
public List<String> getOptions() { public MPResultTypeClass getTypeClass() {
return options;
}
public MPSiteTypeClass getTypeClass() {
return typeClass; return typeClass;
} }
@ -154,35 +137,22 @@ public enum MPSiteType {
} }
/** /**
* @param option The option to select a type with. It is matched case insensitively. * @param shortNamePrefix The name for the type to look up. It is a case insensitive prefix of the type's short name.
*
* @return The type registered for the given option.
*/
public static MPSiteType forOption(final String option) {
for (final MPSiteType type : values())
if (type.getOptions().contains( option.toLowerCase( Locale.ROOT ) ))
return type;
throw logger.bug( "No type for option: %s", option );
}
/**
* @param name The name fromInt the type to look up. It is matched case insensitively.
* *
* @return The type registered with the given name. * @return The type registered with the given name.
*/ */
@Nullable
@Contract("!null -> !null") @Contract("!null -> !null")
public static MPSiteType forName(@Nullable final String name) { public static MPResultType forName(@Nullable final String shortNamePrefix) {
if (name == null) if (shortNamePrefix == null)
return null; return null;
for (final MPSiteType type : values()) for (final MPResultType type : values())
if (type.name().equalsIgnoreCase( name )) if (type.getShortName().toLowerCase( Locale.ROOT ).startsWith( shortNamePrefix.toLowerCase( Locale.ROOT ) ))
return type; return type;
throw logger.bug( "No type for name: %s", name ); throw logger.bug( "No type for name: %s", shortNamePrefix );
} }
/** /**
@ -190,10 +160,10 @@ public enum MPSiteType {
* *
* @return All types that support the given class. * @return All types that support the given class.
*/ */
public static ImmutableList<MPSiteType> forClass(final MPSiteTypeClass typeClass) { public static ImmutableList<MPResultType> forClass(final MPResultTypeClass typeClass) {
ImmutableList.Builder<MPSiteType> types = ImmutableList.builder(); ImmutableList.Builder<MPResultType> types = ImmutableList.builder();
for (final MPSiteType type : values()) for (final MPResultType type : values())
if (type.getTypeClass() == typeClass) if (type.getTypeClass() == typeClass)
types.add( type ); types.add( type );
@ -205,11 +175,11 @@ public enum MPSiteType {
* *
* @return The type registered with the given type. * @return The type registered with the given type.
*/ */
public static MPSiteType forType(final int type) { public static MPResultType forType(final int type) {
for (final MPSiteType siteType : values()) for (final MPResultType resultType : values())
if (siteType.getType() == type) if (resultType.getType() == type)
return siteType; return resultType;
throw logger.bug( "No type: %s", type ); throw logger.bug( "No type: %s", type );
} }
@ -219,17 +189,27 @@ public enum MPSiteType {
* *
* @return All types that support the given mask. * @return All types that support the given mask.
*/ */
public static ImmutableList<MPSiteType> forMask(final int mask) { public static ImmutableList<MPResultType> forMask(final int mask) {
int typeMask = mask & ~0xF; int typeMask = mask & ~0xF;
ImmutableList.Builder<MPSiteType> types = ImmutableList.builder(); ImmutableList.Builder<MPResultType> types = ImmutableList.builder();
for (final MPSiteType siteType : values()) for (final MPResultType resultType : values())
if (((siteType.getType() & ~0xF) & typeMask) != 0) if (((resultType.getType() & ~0xF) & typeMask) != 0)
types.add( siteType ); types.add( resultType );
return types.build(); return types.build();
} }
public static MPResultType forInt(final int resultType) {
return values()[resultType];
}
public int toInt() {
return ordinal();
}
public MPTemplate getTemplateAtRollingIndex(final int templateIndex) { public MPTemplate getTemplateAtRollingIndex(final int templateIndex) {
return templates.get( templateIndex % templates.size() ); return templates.get( templateIndex % templates.size() );
} }

View File

@ -23,13 +23,13 @@ package com.lyndir.masterpassword;
* *
* @author lhunath * @author lhunath
*/ */
public enum MPSiteTypeClass { public enum MPResultTypeClass {
Generated( 1 << 4 ), Generated( 1 << 4 ),
Stored( 1 << 5 ); Stored( 1 << 5 );
private final int mask; private final int mask;
MPSiteTypeClass(final int mask) { MPResultTypeClass(final int mask) {
this.mask = mask; this.mask = mask;
} }

View File

@ -1,103 +0,0 @@
//==============================================================================
// 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/>.
//==============================================================================
package com.lyndir.masterpassword;
import com.google.common.collect.ImmutableList;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.util.List;
import java.util.Locale;
import javax.annotation.Nullable;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NonNls;
/**
* @author lhunath, 14-12-02
*/
public enum MPSiteVariant {
Password( "Generate a key for authentication.", "Doesn't currently use a context.", //
ImmutableList.of( "p", "password" ), "com.lyndir.masterpassword" ), // NON-NLS
Login( "Generate a name for identification.", "Doesn't currently use a context.", //
ImmutableList.of( "l", "login" ), "com.lyndir.masterpassword.login" ), // NON-NLS
Answer( "Generate an answer to a security question.", "Empty for a universal site answer or\nthe most significant word(s) of the question.", //
ImmutableList.of( "a", "answer" ), "com.lyndir.masterpassword.answer" ); // NON-NLS
static final Logger logger = Logger.get( MPSiteType.class );
private final String description;
private final String contextDescription;
private final List<String> options;
private final String scope;
MPSiteVariant(final String description, final String contextDescription, final List<String> options, @NonNls final String scope) {
this.contextDescription = contextDescription;
this.options = options;
this.description = description;
this.scope = scope;
}
public String getDescription() {
return description;
}
public String getContextDescription() {
return contextDescription;
}
public List<String> getOptions() {
return options;
}
public String getScope() {
return scope;
}
/**
* @param option The option to select a variant with. It is matched case insensitively.
*
* @return The variant registered for the given option.
*/
public static MPSiteVariant forOption(final String option) {
for (final MPSiteVariant variant : values())
if (variant.getOptions().contains( option.toLowerCase( Locale.ROOT ) ))
return variant;
throw logger.bug( "No variant for option: %s", option );
}
/**
* @param name The name fromInt the variant to look up. It is matched case insensitively.
*
* @return The variant registered with the given name.
*/
@Contract("!null -> !null")
public static MPSiteVariant forName(@Nullable final String name) {
if (name == null)
return null;
for (final MPSiteVariant type : values())
if (type.name().equalsIgnoreCase( name ))
return type;
throw logger.bug( "No variant for name: %s", name );
}
}

View File

@ -86,16 +86,61 @@ public abstract class MasterKey {
allowNativeByDefault = allowNative; allowNativeByDefault = allowNative;
} }
protected MasterKey(@Nonnull final String fullName) { protected MasterKey(final String fullName) {
Preconditions.checkArgument( !fullName.isEmpty() );
this.fullName = fullName; this.fullName = fullName;
logger.trc( "fullName: %s", fullName ); logger.trc( "fullName: %s", fullName );
} }
/**
* Derive the master key for a user based on their name and master password.
*
* @param masterPassword The user's master password.
*/
@Nullable @Nullable
@SuppressWarnings("MethodCanBeVariableArityMethod") @SuppressWarnings("MethodCanBeVariableArityMethod")
protected abstract byte[] deriveKey(char[] masterPassword); protected abstract byte[] deriveKey(char[] masterPassword);
/**
* Derive the site key for a user's site from the given master key and site parameters.
*
* @param siteName A site identifier.
* @param siteCounter The result identifier.
* @param keyPurpose The intended purpose for this site key.
* @param keyContext A site-scoped key modifier.
*/
protected abstract byte[] siteKey(String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose,
@Nullable String 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)}.
*/
public abstract String siteResult(String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose,
@Nullable String keyContext, MPResultType resultType, @Nullable String 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)}.
*/
public abstract String siteState(String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose,
@Nullable String keyContext, MPResultType resultType, @Nullable String resultParam);
public abstract Version getAlgorithmVersion(); public abstract Version getAlgorithmVersion();
@Nonnull @Nonnull
@ -125,9 +170,6 @@ public abstract class MasterKey {
return idForBytes( getKey() ); return idForBytes( getKey() );
} }
public abstract String encode(@Nonnull String siteName, MPSiteType siteType, @Nonnull UnsignedInteger siteCounter,
MPSiteVariant siteVariant, @Nullable String siteContext);
public boolean isValid() { public boolean isValid() {
return masterKey != null; return masterKey != null;
} }
@ -150,17 +192,17 @@ public abstract class MasterKey {
masterKey = deriveKey( masterPassword ); masterKey = deriveKey( masterPassword );
if (masterKey == null) if (masterKey == null)
logger.dbg( "masterKey calculation failed after %.2fs.", (double)(System.currentTimeMillis() - start) / MPConstant.MS_PER_S ); logger.dbg( "masterKey calculation failed after %.2fs.", (double) (System.currentTimeMillis() - start) / MPConstant.MS_PER_S );
else else
logger.trc( "masterKey ID: %s (derived in %.2fs)", CodeUtils.encodeHex( idForBytes( masterKey ) ), logger.trc( "masterKey ID: %s (derived in %.2fs)", CodeUtils.encodeHex( idForBytes( masterKey ) ),
(double)(System.currentTimeMillis() - start) / MPConstant.MS_PER_S ); (double) (System.currentTimeMillis() - start) / MPConstant.MS_PER_S );
return this; return this;
} }
protected abstract byte[] bytesForInt(int number); protected abstract byte[] bytesForInt(int number);
protected abstract byte[] bytesForInt(@Nonnull UnsignedInteger number); protected abstract byte[] bytesForInt(UnsignedInteger number);
protected abstract byte[] idForBytes(byte[] bytes); protected abstract byte[] idForBytes(byte[] bytes);
@ -168,19 +210,19 @@ public abstract class MasterKey {
/** /**
* bugs: * bugs:
* - does math with chars whose signedness was platform-dependent. * - does math with chars whose signedness was platform-dependent.
* - miscounted the byte-length fromInt multi-byte site names. * - miscounted the byte-length for multi-byte site names.
* - miscounted the byte-length fromInt multi-byte full names. * - miscounted the byte-length for multi-byte full names.
*/ */
V0, V0,
/** /**
* bugs: * bugs:
* - miscounted the byte-length fromInt multi-byte site names. * - miscounted the byte-length for multi-byte site names.
* - miscounted the byte-length fromInt multi-byte full names. * - miscounted the byte-length for multi-byte full names.
*/ */
V1, V1,
/** /**
* bugs: * bugs:
* - miscounted the byte-length fromInt multi-byte full names. * - miscounted the byte-length for multi-byte full names.
*/ */
V2, V2,
/** /**
@ -200,20 +242,5 @@ public abstract class MasterKey {
return ordinal(); return ordinal();
} }
public String toBundleVersion() {
switch (this) {
case V0:
return "1.0";
case V1:
return "2.0";
case V2:
return "2.1";
case V3:
return "2.2";
}
throw new UnsupportedOperationException( strf( "Unsupported version: %s", this ) );
}
} }
} }

View File

@ -18,30 +18,68 @@
package com.lyndir.masterpassword; package com.lyndir.masterpassword;
import com.google.common.base.Preconditions; import com.google.common.base.*;
import com.google.common.primitives.Bytes; import com.google.common.primitives.Bytes;
import com.google.common.primitives.UnsignedInteger; import com.google.common.primitives.UnsignedInteger;
import com.lambdaworks.crypto.SCrypt; import com.lambdaworks.crypto.SCrypt;
import com.lyndir.lhunath.opal.crypto.CryptUtils;
import com.lyndir.lhunath.opal.system.*; import com.lyndir.lhunath.opal.system.*;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import java.nio.*; import java.nio.*;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.util.Arrays; import java.util.Arrays;
import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.crypto.IllegalBlockSizeException;
/** /**
* bugs: * bugs:
* - V2: miscounted the byte-length fromInt multi-byte full names. * - V2: miscounted the byte-length for multi-byte full names.
* - V1: miscounted the byte-length fromInt multi-byte site names. * - V1: miscounted the byte-length for multi-byte site names.
* - V0: does math with chars whose signedness was platform-dependent. * - V0: does math with chars whose signedness was platform-dependent.
* *
* @author lhunath, 2014-08-30 * @author lhunath, 2014-08-30
*/ */
public class MasterKeyV0 extends MasterKey { public class MasterKeyV0 extends MasterKey {
private static final int MP_intLen = 32; /**
* mpw: validity for the time-based rolling counter.
*/
protected static final int mpw_otp_window = 5 * 60 /* s */;
/**
* mpw: Key ID hash.
*/
protected static final MessageDigests mpw_hash = MessageDigests.SHA256;
/**
* mpw: Site digest.
*/
protected static final MessageAuthenticationDigests mpw_digest = MessageAuthenticationDigests.HmacSHA256;
/**
* mpw: Platform-agnostic byte order.
*/
protected static final ByteOrder mpw_byteOrder = ByteOrder.BIG_ENDIAN;
/**
* mpw: Input character encoding.
*/
protected static final Charset mpw_charset = Charsets.UTF_8;
/**
* mpw: Master key size (byte).
*/
protected static final int mpw_dkLen = 64;
/**
* scrypt: Parallelization parameter.
*/
protected static final int scrypt_p = 2;
/**
* scrypt: Memory cost parameter.
*/
protected static final int scrypt_r = 8;
/**
* scrypt: CPU cost parameter.
*/
protected static final int scrypt_N = 32768;
private static final int MP_intLen = 32;
@SuppressWarnings("UnusedDeclaration") @SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MasterKeyV0.class ); private static final Logger logger = Logger.get( MasterKeyV0.class );
@ -59,112 +97,171 @@ public class MasterKeyV0 extends MasterKey {
@Nullable @Nullable
@Override @Override
protected byte[] deriveKey(final char[] masterPassword) { protected byte[] deriveKey(final char[] masterPassword) {
Preconditions.checkArgument( masterPassword.length > 0 );
String fullName = getFullName(); String fullName = getFullName();
byte[] fullNameBytes = fullName.getBytes( MPConstant.mpw_charset ); byte[] fullNameBytes = fullName.getBytes( mpw_charset );
byte[] fullNameLengthBytes = bytesForInt( fullName.length() ); byte[] fullNameLengthBytes = bytesForInt( fullName.length() );
ByteBuffer mpBytesBuf = mpw_charset.encode( CharBuffer.wrap( masterPassword ) );
String mpKeyScope = MPSiteVariant.Password.getScope(); logger.trc( "-- mpw_masterKey (algorithm: %u)", getAlgorithmVersion().toInt() );
byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MPConstant.mpw_charset ), fullNameLengthBytes, fullNameBytes ); logger.trc( "fullName: %s", fullName );
logger.trc( "key scope: %s", mpKeyScope ); logger.trc( "masterPassword.id: %s", (Object) idForBytes( mpBytesBuf.array() ) );
logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
ByteBuffer mpBytesBuf = MPConstant.mpw_charset.encode( CharBuffer.wrap( masterPassword ) ); String keyScope = MPKeyPurpose.Password.getScope();
logger.trc( "keyScope: %s", keyScope );
// Calculate the master key salt.
logger.trc( "masterKeySalt: keyScope=%s | #fullName=%s | fullName=%s",
keyScope, CodeUtils.encodeHex( fullNameLengthBytes ), fullName );
byte[] masterKeySalt = Bytes.concat( keyScope.getBytes( mpw_charset ), fullNameLengthBytes, fullNameBytes );
logger.trc( " => masterKeySalt.id: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
// Calculate the master key.
logger.trc( "masterKey: scrypt( masterPassword, masterKeySalt, N=%lu, r=%u, p=%u )",
scrypt_N, scrypt_r, scrypt_p );
byte[] mpBytes = new byte[mpBytesBuf.remaining()]; byte[] mpBytes = new byte[mpBytesBuf.remaining()];
mpBytesBuf.get( mpBytes, 0, mpBytes.length ); mpBytesBuf.get( mpBytes, 0, mpBytes.length );
Arrays.fill( mpBytesBuf.array(), (byte) 0 ); Arrays.fill( mpBytesBuf.array(), (byte) 0 );
byte[] masterKey = scrypt( masterKeySalt, mpBytes ); // TODO: Why not mpBytesBuf.array()?
Arrays.fill( masterKeySalt, (byte) 0 );
Arrays.fill( mpBytes, (byte) 0 );
logger.trc( " => masterKey.id: %s", (masterKey == null)? null: (Object) idForBytes( masterKey ) );
return scrypt( masterKeySalt, mpBytes ); return masterKey;
} }
@Nullable @Nullable
protected byte[] scrypt(final byte[] masterKeySalt, final byte[] mpBytes) { protected byte[] scrypt(final byte[] masterKeySalt, final byte[] mpBytes) {
try { try {
if (isAllowNative()) if (isAllowNative())
return SCrypt.scrypt( mpBytes, masterKeySalt, MPConstant.scrypt_N, MPConstant.scrypt_r, MPConstant.scrypt_p, MPConstant.mpw_dkLen ); return SCrypt.scrypt( mpBytes, masterKeySalt, scrypt_N, scrypt_r, scrypt_p, mpw_dkLen );
else else
return SCrypt.scryptJ( mpBytes, masterKeySalt, MPConstant.scrypt_N, MPConstant.scrypt_r, MPConstant.scrypt_p, MPConstant.mpw_dkLen ); return SCrypt.scryptJ( mpBytes, masterKeySalt, scrypt_N, scrypt_r, scrypt_p, mpw_dkLen );
} }
catch (final GeneralSecurityException e) { catch (final GeneralSecurityException e) {
logger.bug( e ); logger.bug( e );
return null; return null;
} }
finally {
Arrays.fill( mpBytes, (byte) 0 );
}
} }
@Override @Override
public String encode(@Nonnull final String siteName, final MPSiteType siteType, @Nonnull UnsignedInteger siteCounter, protected byte[] siteKey(final String siteName, UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
final MPSiteVariant siteVariant, @Nullable final String siteContext) { @Nullable final String keyContext) {
Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
Preconditions.checkArgument( !siteName.isEmpty() ); Preconditions.checkArgument( !siteName.isEmpty() );
logger.trc( "-- mpw_siteKey (algorithm: %u)", getAlgorithmVersion().toInt() );
logger.trc( "siteName: %s", siteName ); logger.trc( "siteName: %s", siteName );
logger.trc( "siteCounter: %d", siteCounter.longValue() ); logger.trc( "siteCounter: %d", siteCounter );
logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant ); logger.trc( "keyPurpose: %d (%s)", keyPurpose.toInt(), keyPurpose.getShortName() );
logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType ); logger.trc( "keyContext: %s", keyContext );
String keyScope = keyPurpose.getScope();
logger.trc( "keyScope: %s", keyScope );
// OTP counter value.
if (siteCounter.longValue() == 0) if (siteCounter.longValue() == 0)
siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (MPConstant.mpw_counter_timeout * 1000)) * MPConstant.mpw_counter_timeout ); siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (mpw_otp_window * 1000)) * mpw_otp_window );
String siteScope = siteVariant.getScope(); // Calculate the site seed.
byte[] siteNameBytes = siteName.getBytes( MPConstant.mpw_charset ); byte[] siteNameBytes = siteName.getBytes( mpw_charset );
byte[] siteNameLengthBytes = bytesForInt( siteName.length() ); byte[] siteNameLengthBytes = bytesForInt( siteName.length() );
byte[] siteCounterBytes = bytesForInt( siteCounter ); byte[] siteCounterBytes = bytesForInt( siteCounter );
byte[] siteContextBytes = ((siteContext == null) || siteContext.isEmpty())? null: siteContext.getBytes( MPConstant.mpw_charset ); byte[] keyContextBytes = ((keyContext == null) || keyContext.isEmpty())? null: keyContext.getBytes( mpw_charset );
byte[] siteContextLengthBytes = bytesForInt( (siteContextBytes == null)? 0: siteContextBytes.length ); byte[] keyContextLengthBytes = (keyContextBytes == null)? null: bytesForInt( keyContextBytes.length );
logger.trc( "site scope: %s, context: %s", siteScope, (siteContextBytes == null)? "<empty>": siteContext ); logger.trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s",
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ), keyScope, CodeUtils.encodeHex( siteNameLengthBytes ), siteName, CodeUtils.encodeHex( siteCounterBytes ),
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ), (keyContextLengthBytes == null)? null: CodeUtils.encodeHex( keyContextLengthBytes ), keyContext );
(siteContextBytes == null)? "(null)": siteContext );
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MPConstant.mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes ); byte[] sitePasswordInfo = Bytes.concat( keyScope.getBytes( mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
if (siteContextBytes != null) if (keyContextBytes != null)
sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes ); sitePasswordInfo = Bytes.concat( sitePasswordInfo, keyContextLengthBytes, keyContextBytes );
logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) ); logger.trc( " => siteSalt.id: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
byte[] sitePasswordSeedBytes = MPConstant.mpw_digest.of( getKey(), sitePasswordInfo ); byte[] masterKey = getKey();
int[] sitePasswordSeed = new int[sitePasswordSeedBytes.length]; logger.trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )", (Object) idForBytes( masterKey ) );
for (int i = 0; i < sitePasswordSeedBytes.length; ++i) { byte[] sitePasswordSeedBytes = mpw_digest.of( masterKey, sitePasswordInfo );
logger.trc( " => siteKey.id: %s", (Object) idForBytes( sitePasswordSeedBytes ) );
return sitePasswordSeedBytes;
}
@Override
public String siteResult(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
@Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam) {
byte[] siteKey = siteKey( siteName, siteCounter, keyPurpose, keyContext );
int[] sitePasswordSeed = new int[siteKey.length];
for (int i = 0; i < siteKey.length; ++i) {
ByteBuffer buf = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( ByteOrder.BIG_ENDIAN ); ByteBuffer buf = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( ByteOrder.BIG_ENDIAN );
Arrays.fill( buf.array(), (byte) ((sitePasswordSeedBytes[i] > 0)? 0x00: 0xFF) ); Arrays.fill( buf.array(), (byte) ((siteKey[i] > 0)? 0x00: 0xFF) );
buf.position( 2 ); buf.position( 2 );
buf.put( sitePasswordSeedBytes[i] ).rewind(); buf.put( siteKey[i] ).rewind();
sitePasswordSeed[i] = buf.getInt() & 0xFFFF; sitePasswordSeed[i] = buf.getInt() & 0xFFFF;
} }
logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeedBytes ) ) );
logger.trc( "-- mpw_siteResult (algorithm: %u)", getAlgorithmVersion().toInt() );
logger.trc( "resultType: %d (%s)", resultType.toInt(), resultType.getShortName() );
logger.trc( "resultParam: %s", resultParam );
// Determine the template.
Preconditions.checkState( sitePasswordSeed.length > 0 ); Preconditions.checkState( sitePasswordSeed.length > 0 );
int templateIndex = sitePasswordSeed[0]; int templateIndex = sitePasswordSeed[0];
MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex ); MPTemplate template = resultType.getTemplateAtRollingIndex( templateIndex );
logger.trc( "type %s, template: %s", siteType, template.getTemplateString() ); logger.trc( "template: %u => %s", templateIndex, template.getTemplateString() );
// Encode the password from the seed using the template.
StringBuilder password = new StringBuilder( template.length() ); StringBuilder password = new StringBuilder( template.length() );
for (int i = 0; i < template.length(); ++i) { for (int i = 0; i < template.length(); ++i) {
int characterIndex = sitePasswordSeed[i + 1]; int characterIndex = sitePasswordSeed[i + 1];
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i ); MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex ); char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex, logger.trc( " - class: %c, index: %5u (0x%02hX) => character: %c",
sitePasswordSeed[i + 1], passwordCharacter ); characterClass.getIdentifier(), characterIndex, sitePasswordSeed[i + 1], passwordCharacter );
password.append( passwordCharacter ); password.append( passwordCharacter );
} }
logger.trc( " => password: %s", password );
return password.toString(); return password.toString();
} }
@Override @Override
protected byte[] bytesForInt(final int number) { public String siteState(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MPConstant.mpw_byteOrder ).putInt( number ).array(); @Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam) {
Preconditions.checkNotNull( resultParam );
Preconditions.checkArgument( !resultParam.isEmpty() );
try {
// Encrypt
ByteBuffer plainText = mpw_charset.encode( CharBuffer.wrap( resultParam ) );
byte[] cipherBuf = CryptUtils.encrypt( plainText.array(), getKey(), true );
logger.trc( "cipherBuf: %zu bytes = %s", cipherBuf.length, CodeUtils.encodeHex( cipherBuf ) );
// Base64-encode
String cipherText = Verify.verifyNotNull( CryptUtils.encodeBase64( cipherBuf ) );
logger.trc( "b64 encoded -> cipherText: %s", cipherText );
return cipherText;
}
catch (final IllegalBlockSizeException e) {
throw logger.bug( e );
}
} }
@Override @Override
protected byte[] bytesForInt(@Nonnull final UnsignedInteger number) { protected byte[] bytesForInt(final int number) {
return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MPConstant.mpw_byteOrder ).putInt( number.intValue() ).array(); return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( mpw_byteOrder ).putInt( number ).array();
}
@Override
protected byte[] bytesForInt(final UnsignedInteger number) {
return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( mpw_byteOrder ).putInt( number.intValue() ).array();
} }
@Override @Override
protected byte[] idForBytes(final byte[] bytes) { protected byte[] idForBytes(final byte[] bytes) {
return MPConstant.mpw_hash.of( bytes ); return mpw_hash.of( bytes );
} }
} }

View File

@ -23,14 +23,13 @@ import com.google.common.primitives.Bytes;
import com.google.common.primitives.UnsignedInteger; import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.*; import com.lyndir.lhunath.opal.system.*;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
/** /**
* bugs: * bugs:
* - V2: miscounted the byte-length fromInt multi-byte full names. * - V2: miscounted the byte-length for multi-byte full names.
* - V1: miscounted the byte-length fromInt multi-byte site names. * - V1: miscounted the byte-length for multi-byte site names.
* *
* @author lhunath, 2014-08-30 * @author lhunath, 2014-08-30
*/ */
@ -50,53 +49,33 @@ public class MasterKeyV1 extends MasterKeyV0 {
} }
@Override @Override
public String encode(@Nonnull final String siteName, final MPSiteType siteType, @Nonnull UnsignedInteger siteCounter, public String siteResult(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
final MPSiteVariant siteVariant, @Nullable final String siteContext) { @Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam) {
Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
Preconditions.checkArgument( !siteName.isEmpty() );
logger.trc( "siteName: %s", siteName ); byte[] sitePasswordSeed = siteKey( siteName, siteCounter, keyPurpose, keyContext );
logger.trc( "siteCounter: %d", siteCounter.longValue() );
logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant );
logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType );
if (siteCounter.longValue() == 0) logger.trc( "-- mpw_siteResult (algorithm: %u)", getAlgorithmVersion().toInt() );
siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (MPConstant.mpw_counter_timeout * 1000)) * MPConstant.mpw_counter_timeout ); logger.trc( "resultType: %d (%s)", resultType.toInt(), resultType.getShortName() );
logger.trc( "resultParam: %s", resultParam );
String siteScope = siteVariant.getScope();
byte[] siteNameBytes = siteName.getBytes( MPConstant.mpw_charset );
byte[] siteNameLengthBytes = bytesForInt( siteName.length() );
byte[] siteCounterBytes = bytesForInt( siteCounter );
byte[] siteContextBytes = ((siteContext == null) || siteContext.isEmpty())? null: siteContext.getBytes( MPConstant.mpw_charset );
byte[] siteContextLengthBytes = bytesForInt( (siteContextBytes == null)? 0: siteContextBytes.length );
logger.trc( "site scope: %s, context: %s", siteScope, (siteContextBytes == null)? "<empty>": siteContext );
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
(siteContextBytes == null)? "(null)": siteContext );
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MPConstant.mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
if (siteContextBytes != null)
sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
byte[] sitePasswordSeed = MPConstant.mpw_digest.of( getKey(), sitePasswordInfo );
logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) );
// Determine the template.
Preconditions.checkState( sitePasswordSeed.length > 0 ); Preconditions.checkState( sitePasswordSeed.length > 0 );
int templateIndex = sitePasswordSeed[0] & 0xFF; // Mask the integer's sign. int templateIndex = sitePasswordSeed[0] & 0xFF; // Mask the integer's sign.
MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex ); MPTemplate template = resultType.getTemplateAtRollingIndex( templateIndex );
logger.trc( "type %s, template: %s", siteType, template.getTemplateString() ); logger.trc( "template: %u => %s", templateIndex, template.getTemplateString() );
// Encode the password from the seed using the template.
StringBuilder password = new StringBuilder( template.length() ); StringBuilder password = new StringBuilder( template.length() );
for (int i = 0; i < template.length(); ++i) { for (int i = 0; i < template.length(); ++i) {
int characterIndex = sitePasswordSeed[i + 1] & 0xFF; // Mask the integer's sign. int characterIndex = sitePasswordSeed[i + 1] & 0xFF; // Mask the integer's sign.
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i ); MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex ); char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex, logger.trc( " - class: %c, index: %3u (0x%02hhX) => character: %c",
sitePasswordSeed[i + 1], passwordCharacter ); characterClass.getIdentifier(), characterIndex, sitePasswordSeed[i + 1], passwordCharacter );
password.append( passwordCharacter ); password.append( passwordCharacter );
} }
logger.trc( " => password: %s", password );
return password.toString(); return password.toString();
} }

View File

@ -23,13 +23,12 @@ import com.google.common.primitives.Bytes;
import com.google.common.primitives.UnsignedInteger; import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.CodeUtils; import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
/** /**
* bugs: * bugs:
* - V2: miscounted the byte-length fromInt multi-byte full names. * - V2: miscounted the byte-length for multi-byte full names.
* *
* @author lhunath, 2014-08-30 * @author lhunath, 2014-08-30
*/ */
@ -49,54 +48,43 @@ public class MasterKeyV2 extends MasterKeyV1 {
} }
@Override @Override
public String encode(@Nonnull final String siteName, final MPSiteType siteType, @Nonnull UnsignedInteger siteCounter, protected byte[] siteKey(final String siteName, UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
final MPSiteVariant siteVariant, @Nullable final String siteContext) { @Nullable final String keyContext) {
Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
Preconditions.checkArgument( !siteName.isEmpty() ); Preconditions.checkArgument( !siteName.isEmpty() );
logger.trc( "-- mpw_siteKey (algorithm: %u)", getAlgorithmVersion().toInt() );
logger.trc( "siteName: %s", siteName ); logger.trc( "siteName: %s", siteName );
logger.trc( "siteCounter: %d", siteCounter.longValue() ); logger.trc( "siteCounter: %d", siteCounter );
logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant ); logger.trc( "keyPurpose: %d (%s)", keyPurpose.toInt(), keyPurpose.getShortName() );
logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType ); logger.trc( "keyContext: %s", keyContext );
String keyScope = keyPurpose.getScope();
logger.trc( "keyScope: %s", keyScope );
// OTP counter value.
if (siteCounter.longValue() == 0) if (siteCounter.longValue() == 0)
siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (MPConstant.mpw_counter_timeout * 1000)) * MPConstant.mpw_counter_timeout ); siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (MasterKeyV0.mpw_otp_window * 1000)) * MasterKeyV0.mpw_otp_window );
String siteScope = siteVariant.getScope(); // Calculate the site seed.
byte[] siteNameBytes = siteName.getBytes( MPConstant.mpw_charset ); byte[] siteNameBytes = siteName.getBytes( MasterKeyV0.mpw_charset );
byte[] siteNameLengthBytes = bytesForInt( siteNameBytes.length ); byte[] siteNameLengthBytes = bytesForInt( siteNameBytes.length );
byte[] siteCounterBytes = bytesForInt( siteCounter ); byte[] siteCounterBytes = bytesForInt( siteCounter );
byte[] siteContextBytes = ((siteContext == null) || siteContext.isEmpty())? null: siteContext.getBytes( MPConstant.mpw_charset ); byte[] keyContextBytes = ((keyContext == null) || keyContext.isEmpty())? null: keyContext.getBytes( MasterKeyV0.mpw_charset );
byte[] siteContextLengthBytes = bytesForInt( (siteContextBytes == null)? 0: siteContextBytes.length ); byte[] keyContextLengthBytes = (keyContextBytes == null)? null: bytesForInt( keyContextBytes.length );
logger.trc( "site scope: %s, context: %s", siteScope, (siteContextBytes == null)? "<empty>": siteContext ); logger.trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s",
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ), keyScope, CodeUtils.encodeHex( siteNameLengthBytes ), siteName, CodeUtils.encodeHex( siteCounterBytes ),
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ), (keyContextLengthBytes == null)? null: CodeUtils.encodeHex( keyContextLengthBytes ), keyContext );
(siteContextBytes == null)? "(null)": siteContext );
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MPConstant.mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes ); byte[] sitePasswordInfo = Bytes.concat( keyScope.getBytes( MasterKeyV0.mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
if (siteContextBytes != null) if (keyContextBytes != null)
sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes ); sitePasswordInfo = Bytes.concat( sitePasswordInfo, keyContextLengthBytes, keyContextBytes );
logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) ); logger.trc( " => siteSalt.id: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
byte[] sitePasswordSeed = MPConstant.mpw_digest.of( getKey(), sitePasswordInfo ); byte[] masterKey = getKey();
logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) ); logger.trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )", (Object) idForBytes( masterKey ) );
byte[] sitePasswordSeedBytes = MasterKeyV0.mpw_digest.of( masterKey, sitePasswordInfo );
logger.trc( " => siteKey.id: %s", (Object) idForBytes( sitePasswordSeedBytes ) );
Preconditions.checkState( sitePasswordSeed.length > 0 ); return sitePasswordSeedBytes;
int templateIndex = sitePasswordSeed[0] & 0xFF; // Mask the integer's sign.
MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex );
logger.trc( "type %s, template: %s", siteType, template.getTemplateString() );
StringBuilder password = new StringBuilder( template.length() );
for (int i = 0; i < template.length(); ++i) {
int characterIndex = sitePasswordSeed[i + 1] & 0xFF; // Mask the integer's sign.
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex,
sitePasswordSeed[i + 1], passwordCharacter );
password.append( passwordCharacter );
}
return password.toString();
} }
} }

View File

@ -18,6 +18,7 @@
package com.lyndir.masterpassword; package com.lyndir.masterpassword;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Bytes; import com.google.common.primitives.Bytes;
import com.lyndir.lhunath.opal.system.CodeUtils; import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
@ -51,19 +52,37 @@ public class MasterKeyV3 extends MasterKeyV2 {
@Nullable @Nullable
@Override @Override
protected byte[] deriveKey(final char[] masterPassword) { protected byte[] deriveKey(final char[] masterPassword) {
byte[] fullNameBytes = getFullName().getBytes( MPConstant.mpw_charset ); Preconditions.checkArgument( masterPassword.length > 0 );
String fullName = getFullName();
byte[] fullNameBytes = fullName.getBytes( MasterKeyV0.mpw_charset );
byte[] fullNameLengthBytes = bytesForInt( fullNameBytes.length ); byte[] fullNameLengthBytes = bytesForInt( fullNameBytes.length );
ByteBuffer mpBytesBuf = MasterKeyV0.mpw_charset.encode( CharBuffer.wrap( masterPassword ) );
String mpKeyScope = MPSiteVariant.Password.getScope(); logger.trc( "-- mpw_masterKey (algorithm: %u)", getAlgorithmVersion().toInt() );
byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MPConstant.mpw_charset ), fullNameLengthBytes, fullNameBytes ); logger.trc( "fullName: %s", fullName );
logger.trc( "key scope: %s", mpKeyScope ); logger.trc( "masterPassword.id: %s", (Object) idForBytes( mpBytesBuf.array() ) );
logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
ByteBuffer mpBytesBuf = MPConstant.mpw_charset.encode( CharBuffer.wrap( masterPassword ) ); String keyScope = MPKeyPurpose.Password.getScope();
logger.trc( "keyScope: %s", keyScope );
// Calculate the master key salt.
logger.trc( "masterKeySalt: keyScope=%s | #fullName=%s | fullName=%s",
keyScope, CodeUtils.encodeHex( fullNameLengthBytes ), fullName );
byte[] masterKeySalt = Bytes.concat( keyScope.getBytes( MasterKeyV0.mpw_charset ), fullNameLengthBytes, fullNameBytes );
logger.trc( " => masterKeySalt.id: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
// Calculate the master key.
logger.trc( "masterKey: scrypt( masterPassword, masterKeySalt, N=%lu, r=%u, p=%u )",
MasterKeyV0.scrypt_N, MasterKeyV0.scrypt_r, MasterKeyV0.scrypt_p );
byte[] mpBytes = new byte[mpBytesBuf.remaining()]; byte[] mpBytes = new byte[mpBytesBuf.remaining()];
mpBytesBuf.get( mpBytes, 0, mpBytes.length ); mpBytesBuf.get( mpBytes, 0, mpBytes.length );
Arrays.fill( mpBytesBuf.array(), (byte) 0 ); Arrays.fill( mpBytesBuf.array(), (byte) 0 );
byte[] masterKey = scrypt( masterKeySalt, mpBytes ); // TODO: Why not mpBytesBuf.array()?
Arrays.fill( masterKeySalt, (byte) 0 );
Arrays.fill( mpBytes, (byte) 0 );
logger.trc( " => masterKey.id: %s", (masterKey == null)? null: (Object) idForBytes( masterKey ) );
return scrypt( masterKeySalt, mpBytes ); return masterKey;
} }
} }

View File

@ -14,50 +14,50 @@ import org.joda.time.Instant;
*/ */
public class MPSite { public class MPSite {
public static final MPSiteType DEFAULT_TYPE = MPSiteType.GeneratedLong; public static final MPResultType DEFAULT_TYPE = MPResultType.GeneratedLong;
public static final UnsignedInteger DEFAULT_COUNTER = UnsignedInteger.valueOf( 1 ); public static final UnsignedInteger DEFAULT_COUNTER = UnsignedInteger.valueOf( 1 );
private final MPUser user; private final MPUser user;
private MasterKey.Version algorithmVersion; private MasterKey.Version algorithmVersion;
private Instant lastUsed; private Instant lastUsed;
private String siteName; private String siteName;
private MPSiteType siteType; private MPResultType resultType;
private UnsignedInteger siteCounter; private UnsignedInteger siteCounter;
private int uses; private int uses;
private String loginName; private String loginName;
public MPSite(final MPUser user, final String siteName) { public MPSite(final MPUser user, final String siteName) {
this( user, siteName, DEFAULT_TYPE, DEFAULT_COUNTER ); this( user, siteName, DEFAULT_COUNTER, DEFAULT_TYPE );
} }
public MPSite(final MPUser user, final String siteName, final MPSiteType siteType, final UnsignedInteger siteCounter) { public MPSite(final MPUser user, final String siteName, final UnsignedInteger siteCounter, final MPResultType resultType) {
this.user = user; this.user = user;
this.algorithmVersion = MasterKey.Version.CURRENT; this.algorithmVersion = MasterKey.Version.CURRENT;
this.lastUsed = new Instant(); this.lastUsed = new Instant();
this.siteName = siteName; this.siteName = siteName;
this.siteType = siteType; this.resultType = resultType;
this.siteCounter = siteCounter; this.siteCounter = siteCounter;
} }
protected MPSite(final MPUser user, final MasterKey.Version algorithmVersion, final Instant lastUsed, final String siteName, protected MPSite(final MPUser user, final MasterKey.Version algorithmVersion, final Instant lastUsed, final String siteName,
final MPSiteType siteType, final UnsignedInteger siteCounter, final int uses, @Nullable final String loginName, final MPResultType resultType, final UnsignedInteger siteCounter, final int uses, @Nullable final String loginName,
@Nullable final String importContent) { @Nullable final String importContent) {
this.user = user; this.user = user;
this.algorithmVersion = algorithmVersion; this.algorithmVersion = algorithmVersion;
this.lastUsed = lastUsed; this.lastUsed = lastUsed;
this.siteName = siteName; this.siteName = siteName;
this.siteType = siteType; this.resultType = resultType;
this.siteCounter = siteCounter; this.siteCounter = siteCounter;
this.uses = uses; this.uses = uses;
this.loginName = loginName; this.loginName = loginName;
} }
public String resultFor(final MasterKey masterKey) { public String resultFor(final MasterKey masterKey) {
return resultFor( masterKey, MPSiteVariant.Password, null ); return resultFor( masterKey, MPKeyPurpose.Password, null );
} }
public String resultFor(final MasterKey masterKey, final MPSiteVariant variant, @Nullable final String context) { public String resultFor(final MasterKey masterKey, final MPKeyPurpose purpose, @Nullable final String context) {
return masterKey.encode( siteName, siteType, siteCounter, variant, context ); return masterKey.siteResult( siteName, siteCounter, purpose, context, resultType, null );
} }
public MPUser getUser() { public MPUser getUser() {
@ -94,12 +94,12 @@ public class MPSite {
this.siteName = siteName; this.siteName = siteName;
} }
public MPSiteType getSiteType() { public MPResultType getResultType() {
return siteType; return resultType;
} }
public void setSiteType(final MPSiteType siteType) { public void setResultType(final MPResultType resultType) {
this.siteType = siteType; this.resultType = resultType;
} }
public UnsignedInteger getSiteCounter() { public UnsignedInteger getSiteCounter() {

View File

@ -64,7 +64,7 @@ public class MPSiteMarshaller {
header.append( "# Full Name: " ).append( user.getFullName() ).append( '\n' ); header.append( "# Full Name: " ).append( user.getFullName() ).append( '\n' );
header.append( "# Avatar: " ).append( user.getAvatar() ).append( '\n' ); header.append( "# Avatar: " ).append( user.getAvatar() ).append( '\n' );
header.append( "# Key ID: " ).append( user.exportKeyID() ).append( '\n' ); header.append( "# Key ID: " ).append( user.exportKeyID() ).append( '\n' );
header.append( "# Version: " ).append( MasterKey.Version.CURRENT.toBundleVersion() ).append( '\n' ); // header.append( "# Version: " ).append( MasterKey.Version.CURRENT.toBundleVersion() ).append( '\n' );
header.append( "# Algorithm: " ).append( MasterKey.Version.CURRENT.toInt() ).append( '\n' ); header.append( "# Algorithm: " ).append( MasterKey.Version.CURRENT.toInt() ).append( '\n' );
header.append( "# Default Type: " ).append( user.getDefaultType().getType() ).append( '\n' ); header.append( "# Default Type: " ).append( user.getDefaultType().getType() ).append( '\n' );
header.append( "# Passwords: " ).append( this.contentMode.name() ).append( '\n' ); header.append( "# Passwords: " ).append( this.contentMode.name() ).append( '\n' );
@ -82,7 +82,7 @@ public class MPSiteMarshaller {
rfc3339.print( site.getLastUsed() ), // lastUsed rfc3339.print( site.getLastUsed() ), // lastUsed
site.getUses(), // uses site.getUses(), // uses
strf( "%d:%d:%d", // strf( "%d:%d:%d", //
site.getSiteType().getType(), // type site.getResultType().getType(), // type
site.getAlgorithmVersion().toInt(), // algorithm site.getAlgorithmVersion().toInt(), // algorithm
site.getSiteCounter().intValue() ), // counter site.getSiteCounter().intValue() ), // counter
ifNotNullElse( site.getLoginName(), "" ), // loginName ifNotNullElse( site.getLoginName(), "" ), // loginName

View File

@ -11,7 +11,7 @@ import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.lhunath.opal.system.util.ConversionUtils; import com.lyndir.lhunath.opal.system.util.ConversionUtils;
import com.lyndir.lhunath.opal.system.util.NNOperation; import com.lyndir.lhunath.opal.system.util.NNOperation;
import com.lyndir.masterpassword.MPSiteType; import com.lyndir.masterpassword.MPResultType;
import com.lyndir.masterpassword.MasterKey; import com.lyndir.masterpassword.MasterKey;
import java.io.*; import java.io.*;
import java.util.List; import java.util.List;
@ -54,13 +54,13 @@ public class MPSiteUnmarshaller {
@Nonnull @Nonnull
public static MPSiteUnmarshaller unmarshall(@Nonnull final List<String> lines) { public static MPSiteUnmarshaller unmarshall(@Nonnull final List<String> lines) {
byte[] keyID = null; byte[] keyID = null;
String fullName = null; String fullName = null;
int mpVersion = 0, importFormat = 0, avatar = 0; int mpVersion = 0, importFormat = 0, avatar = 0;
boolean clearContent = false, headerStarted = false; boolean clearContent = false, headerStarted = false;
MPSiteType defaultType = MPSiteType.GeneratedLong; MPResultType defaultType = MPResultType.GeneratedLong;
MPSiteUnmarshaller marshaller = null; MPSiteUnmarshaller marshaller = null;
final ImmutableList.Builder<MPSite> sites = ImmutableList.builder(); final ImmutableList.Builder<MPSite> sites = ImmutableList.builder();
for (final String line : lines) for (final String line : lines)
// Header delimitor. // Header delimitor.
@ -92,7 +92,7 @@ public class MPSiteUnmarshaller {
else if ("Passwords".equalsIgnoreCase( name )) else if ("Passwords".equalsIgnoreCase( name ))
clearContent = "visible".equalsIgnoreCase( value ); clearContent = "visible".equalsIgnoreCase( value );
else if ("Default Type".equalsIgnoreCase( name )) else if ("Default Type".equalsIgnoreCase( name ))
defaultType = MPSiteType.forType( ConversionUtils.toIntegerNN( value ) ); defaultType = MPResultType.forType( ConversionUtils.toIntegerNN( value ) );
} }
} }
} }
@ -110,7 +110,7 @@ public class MPSiteUnmarshaller {
} }
protected MPSiteUnmarshaller(final int importFormat, final int mpVersion, final String fullName, final byte[] keyID, final int avatar, protected MPSiteUnmarshaller(final int importFormat, final int mpVersion, final String fullName, final byte[] keyID, final int avatar,
final MPSiteType defaultType, final boolean clearContent) { final MPResultType defaultType, final boolean clearContent) {
this.importFormat = importFormat; this.importFormat = importFormat;
this.mpVersion = mpVersion; this.mpVersion = mpVersion;
this.clearContent = clearContent; this.clearContent = clearContent;
@ -131,7 +131,7 @@ public class MPSiteUnmarshaller {
MasterKey.Version.fromInt( ConversionUtils.toIntegerNN( siteMatcher.group( 4 ).replace( ":", "" ) ) ), // MasterKey.Version.fromInt( ConversionUtils.toIntegerNN( siteMatcher.group( 4 ).replace( ":", "" ) ) ), //
rfc3339.parseDateTime( siteMatcher.group( 1 ) ).toInstant(), // rfc3339.parseDateTime( siteMatcher.group( 1 ) ).toInstant(), //
siteMatcher.group( 5 ), // siteMatcher.group( 5 ), //
MPSiteType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ), MPSite.DEFAULT_COUNTER, // MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ), MPSite.DEFAULT_COUNTER, //
ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ), // ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ), //
null, // null, //
siteMatcher.group( 6 ) ); siteMatcher.group( 6 ) );
@ -142,7 +142,7 @@ public class MPSiteUnmarshaller {
MasterKey.Version.fromInt( ConversionUtils.toIntegerNN( siteMatcher.group( 4 ).replace( ":", "" ) ) ), // MasterKey.Version.fromInt( ConversionUtils.toIntegerNN( siteMatcher.group( 4 ).replace( ":", "" ) ) ), //
rfc3339.parseDateTime( siteMatcher.group( 1 ) ).toInstant(), // rfc3339.parseDateTime( siteMatcher.group( 1 ) ).toInstant(), //
siteMatcher.group( 7 ), // siteMatcher.group( 7 ), //
MPSiteType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ), MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
UnsignedInteger.valueOf( siteMatcher.group( 5 ).replace( ":", "" ) ), // UnsignedInteger.valueOf( siteMatcher.group( 5 ).replace( ":", "" ) ), //
ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ), // ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ), //
siteMatcher.group( 6 ), // siteMatcher.group( 6 ), //

View File

@ -5,7 +5,7 @@ import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.lyndir.lhunath.opal.system.CodeUtils; import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.masterpassword.MPSiteType; import com.lyndir.masterpassword.MPResultType;
import com.lyndir.masterpassword.MasterKey; import com.lyndir.masterpassword.MasterKey;
import java.util.*; import java.util.*;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -25,7 +25,7 @@ public class MPUser implements Comparable<MPUser> {
private byte[] keyID; private byte[] keyID;
private final MasterKey.Version algorithmVersion; private final MasterKey.Version algorithmVersion;
private int avatar; private int avatar;
private MPSiteType defaultType; private MPResultType defaultType;
private ReadableInstant lastUsed; private ReadableInstant lastUsed;
public MPUser(final String fullName) { public MPUser(final String fullName) {
@ -33,11 +33,11 @@ public class MPUser implements Comparable<MPUser> {
} }
public MPUser(final String fullName, @Nullable final byte[] keyID) { public MPUser(final String fullName, @Nullable final byte[] keyID) {
this( fullName, keyID, MasterKey.Version.CURRENT, 0, MPSiteType.GeneratedLong, new DateTime() ); this( fullName, keyID, MasterKey.Version.CURRENT, 0, MPResultType.GeneratedLong, new DateTime() );
} }
public MPUser(final String fullName, @Nullable final byte[] keyID, final MasterKey.Version algorithmVersion, final int avatar, public MPUser(final String fullName, @Nullable final byte[] keyID, final MasterKey.Version algorithmVersion, final int avatar,
final MPSiteType defaultType, final ReadableInstant lastUsed) { final MPResultType defaultType, final ReadableInstant lastUsed) {
this.fullName = fullName; this.fullName = fullName;
this.keyID = (keyID == null)? null: keyID.clone(); this.keyID = (keyID == null)? null: keyID.clone();
this.algorithmVersion = algorithmVersion; this.algorithmVersion = algorithmVersion;
@ -107,11 +107,11 @@ public class MPUser implements Comparable<MPUser> {
this.avatar = avatar; this.avatar = avatar;
} }
public MPSiteType getDefaultType() { public MPResultType getDefaultType() {
return defaultType; return defaultType;
} }
public void setDefaultType(final MPSiteType defaultType) { public void setDefaultType(final MPResultType defaultType) {
this.defaultType = defaultType; this.defaultType = defaultType;
} }

View File

@ -99,12 +99,12 @@ public class MPTestSuite implements Callable<Boolean> {
currentCase.siteName = text; currentCase.siteName = text;
if ("siteCounter".equals( qName )) if ("siteCounter".equals( qName ))
currentCase.siteCounter = text.isEmpty()? null: UnsignedInteger.valueOf( text ); currentCase.siteCounter = text.isEmpty()? null: UnsignedInteger.valueOf( text );
if ("siteType".equals( qName )) if ("resultType".equals( qName ))
currentCase.siteType = text; currentCase.resultType = text;
if ("siteVariant".equals( qName )) if ("keyPurpose".equals( qName ))
currentCase.siteVariant = text; currentCase.keyPurpose = text;
if ("siteContext".equals( qName )) if ("keyContext".equals( qName ))
currentCase.siteContext = text; currentCase.keyContext = text;
if ("result".equals( qName )) if ("result".equals( qName ))
currentCase.result = text; currentCase.result = text;
} }
@ -173,8 +173,9 @@ public class MPTestSuite implements Callable<Boolean> {
@Override @Override
public Boolean apply(@Nonnull final MPTests.Case testCase) { public Boolean apply(@Nonnull final MPTests.Case testCase) {
MasterKey masterKey = MasterKey.create( testCase.getAlgorithm(), testCase.getFullName(), testCase.getMasterPassword() ); MasterKey masterKey = MasterKey.create( testCase.getAlgorithm(), testCase.getFullName(), testCase.getMasterPassword() );
String sitePassword = masterKey.encode( testCase.getSiteName(), testCase.getSiteType(), testCase.getSiteCounter(), String sitePassword = masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
testCase.getSiteVariant(), testCase.getSiteContext() ); testCase.getKeyContext(), testCase.getResultType(),
null );
return testCase.getResult().equals( sitePassword ); return testCase.getResult().equals( sitePassword );
} }

View File

@ -66,18 +66,18 @@ public class MPTests {
public static class Case { public static class Case {
String identifier; String identifier;
String parent; String parent;
Integer algorithm; Integer algorithm;
String fullName; String fullName;
String masterPassword; String masterPassword;
String keyID; String keyID;
String siteName; String siteName;
UnsignedInteger siteCounter; UnsignedInteger siteCounter;
String siteType; String resultType;
String siteVariant; String keyPurpose;
String siteContext; String keyContext;
String result; String result;
private transient Case parentCase; private transient Case parentCase;
@ -130,25 +130,25 @@ public class MPTests {
return checkNotNull( parentCase.siteCounter ); return checkNotNull( parentCase.siteCounter );
} }
} ); } );
siteType = ifNotNullElse( siteType, new NNSupplier<String>() { resultType = ifNotNullElse( resultType, new NNSupplier<String>() {
@Nonnull @Nonnull
@Override @Override
public String get() { public String get() {
return checkNotNull( parentCase.siteType ); return checkNotNull( parentCase.resultType );
} }
} ); } );
siteVariant = ifNotNullElse( siteVariant, new NNSupplier<String>() { keyPurpose = ifNotNullElse( keyPurpose, new NNSupplier<String>() {
@Nonnull @Nonnull
@Override @Override
public String get() { public String get() {
return checkNotNull( parentCase.siteVariant ); return checkNotNull( parentCase.keyPurpose );
} }
} ); } );
siteContext = ifNotNullElse( siteContext, new NNSupplier<String>() { keyContext = ifNotNullElse( keyContext, new NNSupplier<String>() {
@Nonnull @Nonnull
@Override @Override
public String get() { public String get() {
return (parentCase == null)? "": checkNotNull( parentCase.siteContext ); return (parentCase == null)? "": checkNotNull( parentCase.keyContext );
} }
} ); } );
result = ifNotNullElse( result, new NNSupplier<String>() { result = ifNotNullElse( result, new NNSupplier<String>() {
@ -200,18 +200,18 @@ public class MPTests {
} }
@Nonnull @Nonnull
public MPSiteType getSiteType() { public MPResultType getResultType() {
return MPSiteType.forName( checkNotNull( siteType ) ); return MPResultType.forName( checkNotNull( resultType ) );
} }
@Nonnull @Nonnull
public MPSiteVariant getSiteVariant() { public MPKeyPurpose getKeyPurpose() {
return MPSiteVariant.forName( checkNotNull( siteVariant ) ); return MPKeyPurpose.forName( checkNotNull( keyPurpose ) );
} }
@Nonnull @Nonnull
public String getSiteContext() { public String getKeyContext() {
return checkNotNull( siteContext ); return checkNotNull( keyContext );
} }
@Nonnull @Nonnull

View File

@ -7,7 +7,7 @@
<keyID>98EEF4D1DF46D849574A82A03C3177056B15DFFCA29BB3899DE4628453675302</keyID> <keyID>98EEF4D1DF46D849574A82A03C3177056B15DFFCA29BB3899DE4628453675302</keyID>
<siteName>masterpasswordapp.com</siteName> <siteName>masterpasswordapp.com</siteName>
<siteCounter>1</siteCounter> <siteCounter>1</siteCounter>
<resultType>GeneratedLong</resultType> <resultType>Long</resultType>
<keyPurpose>Authentication</keyPurpose> <keyPurpose>Authentication</keyPurpose>
<result><!-- abstract --></result> <result><!-- abstract --></result>
</case> </case>
@ -33,12 +33,12 @@
</case> </case>
<case id="v3_loginName" parent="v3"> <case id="v3_loginName" parent="v3">
<keyPurpose>Identification</keyPurpose> <keyPurpose>Identification</keyPurpose>
<resultType>GeneratedName</resultType> <resultType>Name</resultType>
<result>wohzaqage</result> <result>wohzaqage</result>
</case> </case>
<case id="v3_securityAnswer" parent="v3"> <case id="v3_securityAnswer" parent="v3">
<keyPurpose>Recovery</keyPurpose> <keyPurpose>Recovery</keyPurpose>
<resultType>GeneratedPhrase</resultType> <resultType>Phrase</resultType>
<result>xin diyjiqoja hubu</result> <result>xin diyjiqoja hubu</result>
</case> </case>
<case id="v3_securityAnswer_context" parent="v3_securityAnswer"> <case id="v3_securityAnswer_context" parent="v3_securityAnswer">
@ -46,31 +46,31 @@
<result>xogx tem cegyiva jab</result> <result>xogx tem cegyiva jab</result>
</case> </case>
<case id="v3_type_maximum" parent="v3"> <case id="v3_type_maximum" parent="v3">
<resultType>GeneratedMaximum</resultType> <resultType>Maximum</resultType>
<result>W6@692^B1#&amp;@gVdSdLZ@</result> <result>W6@692^B1#&amp;@gVdSdLZ@</result>
</case> </case>
<case id="v3_type_medium" parent="v3"> <case id="v3_type_medium" parent="v3">
<resultType>GeneratedMedium</resultType> <resultType>Medium</resultType>
<result>Jej2$Quv</result> <result>Jej2$Quv</result>
</case> </case>
<case id="v3_type_basic" parent="v3"> <case id="v3_type_basic" parent="v3">
<resultType>GeneratedBasic</resultType> <resultType>Basic</resultType>
<result>WAo2xIg6</result> <result>WAo2xIg6</result>
</case> </case>
<case id="v3_type_short" parent="v3"> <case id="v3_type_short" parent="v3">
<resultType>GeneratedShort</resultType> <resultType>Short</resultType>
<result>Jej2</result> <result>Jej2</result>
</case> </case>
<case id="v3_type_pin" parent="v3"> <case id="v3_type_pin" parent="v3">
<resultType>GeneratedPIN</resultType> <resultType>PIN</resultType>
<result>7662</result> <result>7662</result>
</case> </case>
<case id="v3_type_name" parent="v3"> <case id="v3_type_name" parent="v3">
<resultType>GeneratedName</resultType> <resultType>Name</resultType>
<result>jejraquvo</result> <result>jejraquvo</result>
</case> </case>
<case id="v3_type_phrase" parent="v3"> <case id="v3_type_phrase" parent="v3">
<resultType>GeneratedPhrase</resultType> <resultType>Phrase</resultType>
<result>jejr quv cabsibu tam</result> <result>jejr quv cabsibu tam</result>
</case> </case>
<case id="v3_counter_ceiling" parent="v3"> <case id="v3_counter_ceiling" parent="v3">
@ -99,12 +99,12 @@
</case> </case>
<case id="v2_loginName" parent="v2"> <case id="v2_loginName" parent="v2">
<keyPurpose>Identification</keyPurpose> <keyPurpose>Identification</keyPurpose>
<resultType>GeneratedName</resultType> <resultType>Name</resultType>
<result>wohzaqage</result> <result>wohzaqage</result>
</case> </case>
<case id="v2_securityAnswer" parent="v2"> <case id="v2_securityAnswer" parent="v2">
<keyPurpose>Recovery</keyPurpose> <keyPurpose>Recovery</keyPurpose>
<resultType>GeneratedPhrase</resultType> <resultType>Phrase</resultType>
<result>xin diyjiqoja hubu</result> <result>xin diyjiqoja hubu</result>
</case> </case>
<case id="v2_securityAnswer_context" parent="v2_securityAnswer"> <case id="v2_securityAnswer_context" parent="v2_securityAnswer">
@ -112,31 +112,31 @@
<result>xogx tem cegyiva jab</result> <result>xogx tem cegyiva jab</result>
</case> </case>
<case id="v2_type_maximum" parent="v2"> <case id="v2_type_maximum" parent="v2">
<resultType>GeneratedMaximum</resultType> <resultType>Maximum</resultType>
<result>W6@692^B1#&amp;@gVdSdLZ@</result> <result>W6@692^B1#&amp;@gVdSdLZ@</result>
</case> </case>
<case id="v2_type_medium" parent="v2"> <case id="v2_type_medium" parent="v2">
<resultType>GeneratedMedium</resultType> <resultType>Medium</resultType>
<result>Jej2$Quv</result> <result>Jej2$Quv</result>
</case> </case>
<case id="v2_type_basic" parent="v2"> <case id="v2_type_basic" parent="v2">
<resultType>GeneratedBasic</resultType> <resultType>Basic</resultType>
<result>WAo2xIg6</result> <result>WAo2xIg6</result>
</case> </case>
<case id="v2_type_short" parent="v2"> <case id="v2_type_short" parent="v2">
<resultType>GeneratedShort</resultType> <resultType>Short</resultType>
<result>Jej2</result> <result>Jej2</result>
</case> </case>
<case id="v2_type_pin" parent="v2"> <case id="v2_type_pin" parent="v2">
<resultType>GeneratedPIN</resultType> <resultType>PIN</resultType>
<result>7662</result> <result>7662</result>
</case> </case>
<case id="v2_type_name" parent="v2"> <case id="v2_type_name" parent="v2">
<resultType>GeneratedName</resultType> <resultType>Name</resultType>
<result>jejraquvo</result> <result>jejraquvo</result>
</case> </case>
<case id="v2_type_phrase" parent="v2"> <case id="v2_type_phrase" parent="v2">
<resultType>GeneratedPhrase</resultType> <resultType>Phrase</resultType>
<result>jejr quv cabsibu tam</result> <result>jejr quv cabsibu tam</result>
</case> </case>
<case id="v2_counter_ceiling" parent="v2"> <case id="v2_counter_ceiling" parent="v2">
@ -165,12 +165,12 @@
</case> </case>
<case id="v1_loginName" parent="v1"> <case id="v1_loginName" parent="v1">
<keyPurpose>Identification</keyPurpose> <keyPurpose>Identification</keyPurpose>
<resultType>GeneratedName</resultType> <resultType>Name</resultType>
<result>wohzaqage</result> <result>wohzaqage</result>
</case> </case>
<case id="v1_securityAnswer" parent="v1"> <case id="v1_securityAnswer" parent="v1">
<keyPurpose>Recovery</keyPurpose> <keyPurpose>Recovery</keyPurpose>
<resultType>GeneratedPhrase</resultType> <resultType>Phrase</resultType>
<result>xin diyjiqoja hubu</result> <result>xin diyjiqoja hubu</result>
</case> </case>
<case id="v1_securityAnswer_context" parent="v1_securityAnswer"> <case id="v1_securityAnswer_context" parent="v1_securityAnswer">
@ -178,31 +178,31 @@
<result>xogx tem cegyiva jab</result> <result>xogx tem cegyiva jab</result>
</case> </case>
<case id="v1_type_maximum" parent="v1"> <case id="v1_type_maximum" parent="v1">
<resultType>GeneratedMaximum</resultType> <resultType>Maximum</resultType>
<result>W6@692^B1#&amp;@gVdSdLZ@</result> <result>W6@692^B1#&amp;@gVdSdLZ@</result>
</case> </case>
<case id="v1_type_medium" parent="v1"> <case id="v1_type_medium" parent="v1">
<resultType>GeneratedMedium</resultType> <resultType>Medium</resultType>
<result>Jej2$Quv</result> <result>Jej2$Quv</result>
</case> </case>
<case id="v1_type_basic" parent="v1"> <case id="v1_type_basic" parent="v1">
<resultType>GeneratedBasic</resultType> <resultType>Basic</resultType>
<result>WAo2xIg6</result> <result>WAo2xIg6</result>
</case> </case>
<case id="v1_type_short" parent="v1"> <case id="v1_type_short" parent="v1">
<resultType>GeneratedShort</resultType> <resultType>Short</resultType>
<result>Jej2</result> <result>Jej2</result>
</case> </case>
<case id="v1_type_pin" parent="v1"> <case id="v1_type_pin" parent="v1">
<resultType>GeneratedPIN</resultType> <resultType>PIN</resultType>
<result>7662</result> <result>7662</result>
</case> </case>
<case id="v1_type_name" parent="v1"> <case id="v1_type_name" parent="v1">
<resultType>GeneratedName</resultType> <resultType>Name</resultType>
<result>jejraquvo</result> <result>jejraquvo</result>
</case> </case>
<case id="v1_type_phrase" parent="v1"> <case id="v1_type_phrase" parent="v1">
<resultType>GeneratedPhrase</resultType> <resultType>Phrase</resultType>
<result>jejr quv cabsibu tam</result> <result>jejr quv cabsibu tam</result>
</case> </case>
<case id="v1_counter_ceiling" parent="v1"> <case id="v1_counter_ceiling" parent="v1">
@ -231,12 +231,12 @@
</case> </case>
<case id="v0_loginName" parent="v0"> <case id="v0_loginName" parent="v0">
<keyPurpose>Identification</keyPurpose> <keyPurpose>Identification</keyPurpose>
<resultType>GeneratedName</resultType> <resultType>Name</resultType>
<result>lozwajave</result> <result>lozwajave</result>
</case> </case>
<case id="v0_securityAnswer" parent="v0"> <case id="v0_securityAnswer" parent="v0">
<keyPurpose>Recovery</keyPurpose> <keyPurpose>Recovery</keyPurpose>
<resultType>GeneratedPhrase</resultType> <resultType>Phrase</resultType>
<result>miy lirfijoja dubu</result> <result>miy lirfijoja dubu</result>
</case> </case>
<case id="v0_securityAnswer_context" parent="v0_securityAnswer"> <case id="v0_securityAnswer_context" parent="v0_securityAnswer">
@ -244,31 +244,31 @@
<result>movm bex gevrica jaf</result> <result>movm bex gevrica jaf</result>
</case> </case>
<case id="v0_type_maximum" parent="v0"> <case id="v0_type_maximum" parent="v0">
<resultType>GeneratedMaximum</resultType> <resultType>Maximum</resultType>
<result>w1!3bA3icmRAc)SS@lwl</result> <result>w1!3bA3icmRAc)SS@lwl</result>
</case> </case>
<case id="v0_type_medium" parent="v0"> <case id="v0_type_medium" parent="v0">
<resultType>GeneratedMedium</resultType> <resultType>Medium</resultType>
<result>Fej7]Jug</result> <result>Fej7]Jug</result>
</case> </case>
<case id="v0_type_basic" parent="v0"> <case id="v0_type_basic" parent="v0">
<resultType>GeneratedBasic</resultType> <resultType>Basic</resultType>
<result>wvH7irC1</result> <result>wvH7irC1</result>
</case> </case>
<case id="v0_type_short" parent="v0"> <case id="v0_type_short" parent="v0">
<resultType>GeneratedShort</resultType> <resultType>Short</resultType>
<result>Fej7</result> <result>Fej7</result>
</case> </case>
<case id="v0_type_pin" parent="v0"> <case id="v0_type_pin" parent="v0">
<resultType>GeneratedPIN</resultType> <resultType>PIN</resultType>
<result>2117</result> <result>2117</result>
</case> </case>
<case id="v0_type_name" parent="v0"> <case id="v0_type_name" parent="v0">
<resultType>GeneratedName</resultType> <resultType>Name</resultType>
<result>fejrajugo</result> <result>fejrajugo</result>
</case> </case>
<case id="v0_type_phrase" parent="v0"> <case id="v0_type_phrase" parent="v0">
<resultType>GeneratedPhrase</resultType> <resultType>Phrase</resultType>
<result>fejr jug gabsibu bax</result> <result>fejr jug gabsibu bax</result>
</case> </case>
<case id="v0_counter_ceiling" parent="v0"> <case id="v0_counter_ceiling" parent="v0">

View File

@ -55,8 +55,9 @@ public class MasterKeyTest {
MasterKey masterKey = MasterKey.create( testCase.getAlgorithm(), testCase.getFullName(), testCase.getMasterPassword() ); MasterKey masterKey = MasterKey.create( testCase.getAlgorithm(), testCase.getFullName(), testCase.getMasterPassword() );
assertEquals( assertEquals(
masterKey.encode( testCase.getSiteName(), testCase.getSiteType(), testCase.getSiteCounter(), masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
testCase.getSiteVariant(), testCase.getSiteContext() ), testCase.getKeyContext(), testCase.getResultType(),
null ),
testCase.getResult(), "[testEncode] Failed test case: " + testCase ); testCase.getResult(), "[testEncode] Failed test case: " + testCase );
return true; return true;
@ -101,8 +102,9 @@ public class MasterKeyTest {
MasterKey masterKey = MasterKey.create( defaultCase.getFullName(), defaultCase.getMasterPassword() ); MasterKey masterKey = MasterKey.create( defaultCase.getFullName(), defaultCase.getMasterPassword() );
masterKey.invalidate(); masterKey.invalidate();
masterKey.encode( defaultCase.getSiteName(), defaultCase.getSiteType(), defaultCase.getSiteCounter(), masterKey.siteResult( defaultCase.getSiteName(), defaultCase.getSiteCounter(), defaultCase.getKeyPurpose(),
defaultCase.getSiteVariant(), defaultCase.getSiteContext() ); defaultCase.getKeyContext(), defaultCase.getResultType(),
null );
fail( "[testInvalidate] Master key should have been invalidated, but was still usable." ); fail( "[testInvalidate] Master key should have been invalidated, but was still usable." );
} }

View File

@ -22,7 +22,6 @@
<module>masterpassword-tests</module> <module>masterpassword-tests</module>
<module>masterpassword-algorithm</module> <module>masterpassword-algorithm</module>
<module>masterpassword-model</module> <module>masterpassword-model</module>
<module>masterpassword-cli</module>
<module>masterpassword-gui</module> <module>masterpassword-gui</module>
</modules> </modules>

View File

@ -9,9 +9,6 @@ project(':masterpassword-model').projectDir = new File( '../core/java/model' )
include 'masterpassword-tests' include 'masterpassword-tests'
project(':masterpassword-tests').projectDir = new File( '../core/java/tests' ) project(':masterpassword-tests').projectDir = new File( '../core/java/tests' )
include 'masterpassword-cli'
project(':masterpassword-cli').projectDir = new File( '../platform-independent/cli-java' )
include 'masterpassword-gui' include 'masterpassword-gui'
project(':masterpassword-gui').projectDir = new File( '../platform-independent/gui-java' ) project(':masterpassword-gui').projectDir = new File( '../platform-independent/gui-java' )

View File

@ -52,10 +52,10 @@ public class EmergencyActivity extends Activity {
private static final int PASSWORD_NOTIFICATION = 0; private static final int PASSWORD_NOTIFICATION = 0;
public static final int CLIPBOARD_CLEAR_DELAY = 20 /* s */ * MPConstant.MS_PER_S; public static final int CLIPBOARD_CLEAR_DELAY = 20 /* s */ * MPConstant.MS_PER_S;
private final Preferences preferences = Preferences.get( this ); private final Preferences preferences = Preferences.get( this );
private final ListeningExecutorService executor = MoreExecutors.listeningDecorator( Executors.newSingleThreadExecutor() ); private final ListeningExecutorService executor = MoreExecutors.listeningDecorator( Executors.newSingleThreadExecutor() );
private final ImmutableList<MPSiteType> allSiteTypes = ImmutableList.copyOf( MPSiteType.forClass( MPSiteTypeClass.Generated ) ); private final ImmutableList<MPResultType> allResultTypes = ImmutableList.copyOf( MPResultType.forClass( MPResultTypeClass.Generated ) );
private final ImmutableList<MasterKey.Version> allVersions = ImmutableList.copyOf( MasterKey.Version.values() ); private final ImmutableList<MasterKey.Version> allVersions = ImmutableList.copyOf( MasterKey.Version.values() );
private ListenableFuture<MasterKey> masterKeyFuture; private ListenableFuture<MasterKey> masterKeyFuture;
@ -71,8 +71,8 @@ public class EmergencyActivity extends Activity {
@BindView(R.id.siteNameField) @BindView(R.id.siteNameField)
EditText siteNameField; EditText siteNameField;
@BindView(R.id.siteTypeButton) @BindView(R.id.resultTypeButton)
Button siteTypeButton; Button resultTypeButton;
@BindView(R.id.counterField) @BindView(R.id.counterField)
Button siteCounterButton; Button siteCounterButton;
@ -131,15 +131,15 @@ public class EmergencyActivity extends Activity {
updateSitePassword(); updateSitePassword();
} }
} ); } );
siteTypeButton.setOnClickListener( new View.OnClickListener() { resultTypeButton.setOnClickListener( new View.OnClickListener() {
@Override @Override
public void onClick(final View v) { public void onClick(final View v) {
@SuppressWarnings("SuspiciousMethodCalls") @SuppressWarnings("SuspiciousMethodCalls")
MPSiteType siteType = MPResultType resultType =
allSiteTypes.get( (allSiteTypes.indexOf( siteTypeButton.getTag() ) + 1) % allSiteTypes.size() ); allResultTypes.get( (allResultTypes.indexOf( resultTypeButton.getTag() ) + 1) % allResultTypes.size() );
preferences.setDefaultSiteType( siteType ); preferences.setDefaultResultType( resultType );
siteTypeButton.setTag( siteType ); resultTypeButton.setTag( resultType );
siteTypeButton.setText( siteType.getShortName() ); resultTypeButton.setText( resultType.getShortName() );
updateSitePassword(); updateSitePassword();
} }
} ); } );
@ -220,9 +220,9 @@ public class EmergencyActivity extends Activity {
forgetPasswordField.setChecked( preferences.isForgetPassword() ); forgetPasswordField.setChecked( preferences.isForgetPassword() );
maskPasswordField.setChecked( preferences.isMaskPassword() ); maskPasswordField.setChecked( preferences.isMaskPassword() );
sitePasswordField.setTransformationMethod( preferences.isMaskPassword()? new PasswordTransformationMethod(): null ); sitePasswordField.setTransformationMethod( preferences.isMaskPassword()? new PasswordTransformationMethod(): null );
MPSiteType defaultSiteType = preferences.getDefaultSiteType(); MPResultType defaultResultType = preferences.getDefaultResultType();
siteTypeButton.setTag( defaultSiteType ); resultTypeButton.setTag( defaultResultType );
siteTypeButton.setText( defaultSiteType.getShortName() ); resultTypeButton.setText( defaultResultType.getShortName() );
MasterKey.Version defaultVersion = preferences.getDefaultVersion(); MasterKey.Version defaultVersion = preferences.getDefaultVersion();
siteVersionButton.setTag( defaultVersion ); siteVersionButton.setTag( defaultVersion );
siteVersionButton.setText( defaultVersion.name() ); siteVersionButton.setText( defaultVersion.name() );
@ -313,9 +313,9 @@ public class EmergencyActivity extends Activity {
} }
private void updateSitePassword() { private void updateSitePassword() {
final String siteName = siteNameField.getText().toString(); final String siteName = siteNameField.getText().toString();
final MPSiteType type = (MPSiteType) siteTypeButton.getTag(); final MPResultType type = (MPResultType) resultTypeButton.getTag();
final UnsignedInteger counter = UnsignedInteger.valueOf( siteCounterButton.getText().toString() ); final UnsignedInteger counter = UnsignedInteger.valueOf( siteCounterButton.getText().toString() );
if ((masterKeyFuture == null) || siteName.isEmpty() || (type == null)) { if ((masterKeyFuture == null) || siteName.isEmpty() || (type == null)) {
sitePasswordField.setText( "" ); sitePasswordField.setText( "" );
@ -332,7 +332,7 @@ public class EmergencyActivity extends Activity {
@Override @Override
public void run() { public void run() {
try { try {
sitePassword = masterKeyFuture.get().encode( siteName, type, counter, MPSiteVariant.Password, null ); sitePassword = masterKeyFuture.get().siteResult( siteName, counter, MPKeyPurpose.Password, null, type, null );
runOnUiThread( new Runnable() { runOnUiThread( new Runnable() {
@Override @Override

View File

@ -38,7 +38,7 @@ public final class Preferences {
private static final String PREF_FORGET_PASSWORD = "forgetPassword"; private static final String PREF_FORGET_PASSWORD = "forgetPassword";
private static final String PREF_MASK_PASSWORD = "maskPassword"; private static final String PREF_MASK_PASSWORD = "maskPassword";
private static final String PREF_FULL_NAME = "fullName"; private static final String PREF_FULL_NAME = "fullName";
private static final String PREF_SITE_TYPE = "siteType"; private static final String PREF_RESULT_TYPE = "resultType";
private static final String PREF_ALGORITHM_VERSION = "algorithmVersion"; private static final String PREF_ALGORITHM_VERSION = "algorithmVersion";
private static Preferences instance; private static Preferences instance;
@ -138,20 +138,20 @@ public final class Preferences {
return prefs().getString( PREF_FULL_NAME, "" ); return prefs().getString( PREF_FULL_NAME, "" );
} }
public boolean setDefaultSiteType(@Nonnull final MPSiteType value) { public boolean setDefaultResultType(final MPResultType value) {
if (getDefaultSiteType() == value) if (getDefaultResultType() == value)
return false; return false;
prefs().edit().putInt( PREF_SITE_TYPE, value.ordinal() ).apply(); prefs().edit().putInt( PREF_RESULT_TYPE, value.ordinal() ).apply();
return true; return true;
} }
@Nonnull @Nonnull
public MPSiteType getDefaultSiteType() { public MPResultType getDefaultResultType() {
return MPSiteType.values()[prefs().getInt( PREF_SITE_TYPE, MPSiteType.GeneratedLong.ordinal() )]; return MPResultType.values()[prefs().getInt( PREF_RESULT_TYPE, MPResultType.GeneratedLong.ordinal() )];
} }
public boolean setDefaultVersion(@Nonnull final MasterKey.Version value) { public boolean setDefaultVersion(final MasterKey.Version value) {
if (getDefaultVersion() == value) if (getDefaultVersion() == value)
return false; return false;

View File

@ -105,7 +105,7 @@
android:id="@id/sitePasswordField" android:id="@id/sitePasswordField"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:nextFocusForward="@+id/siteTypeButton" android:nextFocusForward="@+id/resultTypeButton"
android:gravity="center" android:gravity="center"
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:textColor="#FFFFFF" android:textColor="#FFFFFF"
@ -157,7 +157,7 @@
android:gravity="center"> android:gravity="center">
<Button <Button
android:id="@id/siteTypeButton" android:id="@id/resultTypeButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
@ -175,12 +175,12 @@
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:labelFor="@id/siteTypeButton" android:labelFor="@id/resultTypeButton"
android:gravity="center" android:gravity="center"
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:textSize="12sp" android:textSize="12sp"
android:textColor="@android:color/tertiary_text_dark" android:textColor="@android:color/tertiary_text_dark"
android:text="@string/siteType_hint" /> android:text="@string/resultType_hint" />
</LinearLayout> </LinearLayout>

View File

@ -8,7 +8,7 @@
<string name="masterPassword_hint">Your master password</string> <string name="masterPassword_hint">Your master password</string>
<string name="siteName_hint">eg. google.com</string> <string name="siteName_hint">eg. google.com</string>
<string name="sitePassword_hint">Tap to copy</string> <string name="sitePassword_hint">Tap to copy</string>
<string name="siteType_hint">Type</string> <string name="resultType_hint">Type</string>
<string name="siteCounter_hint">Counter</string> <string name="siteCounter_hint">Counter</string>
<string name="siteVersion_hint">Algorithm</string> <string name="siteVersion_hint">Algorithm</string>
<string name="empty" /> <string name="empty" />

View File

@ -1,90 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>MPGeneratedSiteEntity</key>
<dict>
<key>Login Name</key>
<array>
<string>cvccvcvcv</string>
</array>
<key>Phrase</key>
<array>
<string>cvcc cvc cvccvcv cvc</string>
<string>cvc cvccvcvcv cvcv</string>
<string>cv cvccv cvc cvcvccv</string>
</array>
<key>Maximum Security Password</key>
<array>
<string>anoxxxxxxxxxxxxxxxxx</string>
<string>axxxxxxxxxxxxxxxxxno</string>
</array>
<key>Long Password</key>
<array>
<string>CvcvnoCvcvCvcv</string>
<string>CvcvCvcvnoCvcv</string>
<string>CvcvCvcvCvcvno</string>
<string>CvccnoCvcvCvcv</string>
<string>CvccCvcvnoCvcv</string>
<string>CvccCvcvCvcvno</string>
<string>CvcvnoCvccCvcv</string>
<string>CvcvCvccnoCvcv</string>
<string>CvcvCvccCvcvno</string>
<string>CvcvnoCvcvCvcc</string>
<string>CvcvCvcvnoCvcc</string>
<string>CvcvCvcvCvccno</string>
<string>CvccnoCvccCvcv</string>
<string>CvccCvccnoCvcv</string>
<string>CvccCvccCvcvno</string>
<string>CvcvnoCvccCvcc</string>
<string>CvcvCvccnoCvcc</string>
<string>CvcvCvccCvccno</string>
<string>CvccnoCvcvCvcc</string>
<string>CvccCvcvnoCvcc</string>
<string>CvccCvcvCvccno</string>
</array>
<key>Medium Password</key>
<array>
<string>CvcnoCvc</string>
<string>CvcCvcno</string>
</array>
<key>Basic Password</key>
<array>
<string>aaanaaan</string>
<string>aannaaan</string>
<string>aaannaaa</string>
</array>
<key>Short Password</key>
<array>
<string>Cvcn</string>
</array>
<key>PIN</key>
<array>
<string>nnnn</string>
</array>
</dict>
<key>MPCharacterClasses</key>
<dict>
<key>V</key>
<string>AEIOU</string>
<key>C</key>
<string>BCDFGHJKLMNPQRSTVWXYZ</string>
<key>v</key>
<string>aeiou</string>
<key>c</key>
<string>bcdfghjklmnpqrstvwxyz</string>
<key>A</key>
<string>AEIOUBCDFGHJKLMNPQRSTVWXYZ</string>
<key>a</key>
<string>AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz</string>
<key>n</key>
<string>0123456789</string>
<key>o</key>
<string>@&amp;%?,=[]_:-+*$#!'^~;()/.</string>
<key>x</key>
<string>AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&amp;*()</string>
<key> </key>
<string> </string>
</dict>
</dict>
</plist>

View File

@ -1,14 +0,0 @@
plugins {
id 'java'
id 'application'
id 'com.github.johnrengelman.shadow' version '1.2.4'
}
description = 'Master Password CLI'
mainClassName = 'com.lyndir.masterpassword.CLI'
dependencies {
compile project(':masterpassword-algorithm')
compile group: 'ch.qos.logback', name: 'logback-classic', version:'1.1.2'
}

View File

@ -1,98 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- PROJECT METADATA -->
<parent>
<groupId>com.lyndir.masterpassword</groupId>
<artifactId>masterpassword</artifactId>
<version>GIT-SNAPSHOT</version>
</parent>
<name>Master Password CLI</name>
<description>A CLI interface to the Master Password algorithm</description>
<artifactId>masterpassword-cli</artifactId>
<packaging>jar</packaging>
<!-- BUILD CONFIGURATION -->
<build>
<resources>
<resource>
<directory>src/main/scripts</directory>
<filtering>true</filtering>
<targetPath>${project.build.directory}</targetPath>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<id>prepare-package</id>
<phase>prepare-package</phase>
<configuration>
<target>
<chmod file="${project.build.directory}/install" perm="755" />
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.2</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.lyndir.masterpassword.CLI</mainClass>
</transformer>
</transformers>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<!-- DEPENDENCY MANAGEMENT -->
<dependencies>
<!-- PROJECT REFERENCES -->
<dependency>
<groupId>com.lyndir.masterpassword</groupId>
<artifactId>masterpassword-algorithm</artifactId>
<version>GIT-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -1,189 +0,0 @@
//==============================================================================
// 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/>.
//==============================================================================
package com.lyndir.masterpassword;
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.ifNotNullElse;
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
import com.google.common.base.Charsets;
import com.google.common.base.Joiner;
import com.google.common.collect.Maps;
import com.google.common.io.LineReader;
import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.util.ConversionUtils;
import com.lyndir.lhunath.opal.system.util.StringUtils;
import java.io.*;
import java.util.Arrays;
import java.util.Map;
/**
* <p> <i>Jun 10, 2008</i> </p>
*
* @author mbillemo
*/
@SuppressWarnings({ "UseOfSystemOutOrSystemErr", "HardCodedStringLiteral" })
public final class CLI {
public static void main(final String... args)
throws IOException {
// Read information from the environment.
char[] masterPassword;
String siteName = null, context = null;
String userName = System.getenv( MPConstant.env_userName );
String siteTypeName = ifNotNullElse( System.getenv( MPConstant.env_siteType ), "" );
MPSiteType siteType = siteTypeName.isEmpty()? MPSiteType.GeneratedLong: MPSiteType.forOption( siteTypeName );
MPSiteVariant variant = MPSiteVariant.Password;
String siteCounterName = ifNotNullElse( System.getenv( MPConstant.env_siteCounter ), "" );
UnsignedInteger siteCounter = siteCounterName.isEmpty()? UnsignedInteger.valueOf( 1 ): UnsignedInteger.valueOf( siteCounterName );
// Parse information from option arguments.
boolean userNameArg = false, typeArg = false, counterArg = false, variantArg = false, contextArg = false;
for (final String arg : Arrays.asList( args ))
// Full Name
if ("-u".equals( arg ) || "--username".equals( arg ))
userNameArg = true;
else if (userNameArg) {
userName = arg;
userNameArg = false;
}
// Type
else if ("-t".equals( arg ) || "--type".equals( arg ))
typeArg = true;
else if (typeArg) {
siteType = MPSiteType.forOption( arg );
typeArg = false;
}
// Counter
else if ("-c".equals( arg ) || "--counter".equals( arg ))
counterArg = true;
else if (counterArg) {
siteCounter = UnsignedInteger.valueOf( arg );
counterArg = false;
}
// Variant
else if ("-v".equals( arg ) || "--variant".equals( arg ))
variantArg = true;
else if (variantArg) {
variant = MPSiteVariant.forOption( arg );
variantArg = false;
}
// Context
else if ("-C".equals( arg ) || "--context".equals( arg ))
contextArg = true;
else if (contextArg) {
context = arg;
contextArg = false;
}
// Help
else if ("-h".equals( arg ) || "--help".equals( arg )) {
System.out.println();
System.out.format( "Usage: mpw [-u name] [-t type] [-c counter] site%n%n" );
System.out.format( " -u name Specify the full name of the user.%n" );
System.out.format( " Defaults to %s in env.%n%n", MPConstant.env_userName );
System.out.format( " -t type Specify the password's template.%n" );
System.out.format( " Defaults to %s in env or 'long' for password, 'name' for login.%n", MPConstant.env_siteType );
int optionsLength = 0;
Map<String, MPSiteType> typeMap = Maps.newLinkedHashMap();
for (final MPSiteType elementType : MPSiteType.values()) {
String options = Joiner.on( ", " ).join( elementType.getOptions() );
typeMap.put( options, elementType );
optionsLength = Math.max( optionsLength, options.length() );
}
for (final Map.Entry<String, MPSiteType> entry : typeMap.entrySet()) {
String infoString = strf( " -v %" + optionsLength + "s | ", entry.getKey() );
String infoNewline = strf( "%n%s | ", StringUtils.repeat( " ", infoString.length() - 3 ) );
infoString += entry.getValue().getDescription().replaceAll( strf( "%n" ), infoNewline );
System.out.println( infoString );
}
System.out.println();
System.out.format( " -c counter The value of the counter.%n" );
System.out.format( " Defaults to %s in env or '1'.%n%n", MPConstant.env_siteCounter );
System.out.format( " -v variant The kind of content to generate.%n" );
System.out.format( " Defaults to 'password'.%n" );
optionsLength = 0;
Map<String, MPSiteVariant> variantMap = Maps.newLinkedHashMap();
for (final MPSiteVariant elementVariant : MPSiteVariant.values()) {
String options = Joiner.on( ", " ).join( elementVariant.getOptions() );
variantMap.put( options, elementVariant );
optionsLength = Math.max( optionsLength, options.length() );
}
for (final Map.Entry<String, MPSiteVariant> entry : variantMap.entrySet()) {
String infoString = strf( " -v %" + optionsLength + "s | ", entry.getKey() );
String infoNewline = strf( "%n%s | ", StringUtils.repeat( " ", infoString.length() - 3 ) );
infoString += entry.getValue().getDescription().replaceAll( strf( "%n" ), infoNewline );
System.out.println( infoString );
}
System.out.println();
System.out.format( " -C context A variant-specific context.%n" );
System.out.format( " Defaults to empty.%n" );
for (final Map.Entry<String, MPSiteVariant> entry : variantMap.entrySet()) {
String infoString = strf( " -v %" + optionsLength + "s | ", entry.getKey() );
String infoNewline = strf( "%n%s | ", StringUtils.repeat( " ", infoString.length() - 3 ) );
infoString += entry.getValue().getContextDescription().replaceAll( strf( "%n" ), infoNewline );
System.out.println( infoString );
}
System.out.println();
System.out.format( " ENVIRONMENT%n%n" );
System.out.format( " MP_USERNAME | The full name of the user.%n" );
System.out.format( " MP_SITETYPE | The default password template.%n" );
System.out.format( " MP_SITECOUNTER | The default counter value.%n%n" );
return;
} else
siteName = arg;
// Read missing information from the console.
Console console = System.console();
try (InputStreamReader inReader = new InputStreamReader( System.in, Charsets.UTF_8 )) {
LineReader lineReader = new LineReader( inReader );
if (siteName == null) {
System.err.format( "Site name: " );
siteName = lineReader.readLine();
}
if (userName == null) {
System.err.format( "User's name: " );
userName = lineReader.readLine();
}
if (console != null)
masterPassword = console.readPassword( "%s's master password: ", userName );
else {
System.err.format( "%s's master password: ", userName );
masterPassword = lineReader.readLine().toCharArray();
}
}
// Encode and write out the site password.
System.out.println( MasterKey.create( userName, masterPassword ).encode( siteName, siteType, siteCounter, variant, context ) );
}
}

View File

@ -1,28 +0,0 @@
//==============================================================================
// 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, 15-02-04
*/
@ParametersAreNonnullByDefault
package com.lyndir.masterpassword;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@ -1,15 +0,0 @@
<configuration scan="false">
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%-8relative %22c{0} [%-5level] %msg%n</pattern>
</encoder>
</appender>
<logger name="com.lyndir" level="${mp.log.level:-INFO}" />
<root level="INFO">
<appender-ref ref="stdout" />
</root>
</configuration>

File diff suppressed because it is too large Load Diff

View File

@ -1,56 +0,0 @@
#!/usr/bin/env bash
#
# Install the Master Password CLI tool.
set -e
cd "${BASH_SOURCE%/*}"
source bashlib
inf "This will install the mpw tool."
# Try to guess then ask for the bin dir to install to.
IFS=: read -a paths <<< "$PATH"
if inArray ~/bin "${paths[@]}"; then
bindir=~/bin
elif inArray ~/.bin "${paths[@]}"; then
bindir=~/.bin
elif inArray /usr/local/bin "${paths[@]}"; then
bindir=/usr/local/bin
else
bindir=~/bin
fi
bindir=$(ask -d "$bindir" "What bin directory should I install to?")
[[ -d "$bindir" ]] || mkdir "$bindir" || ftl 'Cannot create missing bin directory: %s' "$bindir" || exit
[[ -w "$bindir" ]] || ftl 'Cannot write to bin directory: %s' "$bindir" || exit
# Try to guess then ask for the share dir to install to.
sharedir=$(cd -P "$bindir/.."; [[ $bindir = */.bin ]] && printf '%s/.share' "$PWD" || printf '%s/share' "$PWD")
sharedir=$(ask -d "$sharedir" "What share directory should I install to?")
[[ -d "$sharedir" ]] || mkdir "$sharedir" || ftl 'Cannot create missing share directory: %s' "$sharedir" || exit
[[ -w "$sharedir" ]] || ftl 'Cannot write to share directory: %s' "$sharedir" || exit
# Install Master Password.
sharepath=$sharedir/masterpassword
mkdir -p "$sharepath"
cp -a "masterpassword-cli-"*".jar" bashlib mpw "$sharepath"
ex -c "%s~%SHAREPATH%~$sharepath~g|x" "$sharepath/mpw"
ln -sf "$sharepath/mpw" "$bindir/mpw"
chmod +x "$sharepath/mpw"
[[ ! -e "$bindir/bashlib" ]] && ln -s "$sharepath/bashlib" "$bindir/bashlib" ||:
# Convenience bash function.
inf "Installation successful!"
echo
inf "To improve usability, you can install an mpw function in your bash shell."
inf "This function adds the following features:"
inf " - Ask the Master Password once, remember in the shell."
inf " - Automatically put the password in the clipboard (some platforms)."
echo
inf "To do this you need the following function in ~/.bashrc:\n%s" "$(<mpw.bashrc)"
echo
inf "We can do this for you automatically now."
if ask -c Y!n "Append the mpw function to your .bashrc?"; then
cat mpw.bashrc >> ~/.bashrc
inf "Done! Don't forget to run '%s' to apply the changes!" "source ~/.bashrc"
fi
echo
inf "To begin using Master Password, type: mpw [site name]"

View File

@ -1,9 +0,0 @@
#!/usr/bin/env bash
# Uncomment this to set your user name. Master Password will no longer ask you for a user name.
# export MP_USERNAME="Robert Lee Mitchell"
# Uncomment this to hardcode your master password. Make sure this file's permissions are tight. Master Password will not ask you for your master password anymore. This is probably not a good idea.
# export MP_PASSWORD="banana colored duckling"
cd "%SHAREPATH%"
exec java -jar masterpassword-cli-GIT-SNAPSHOT.jar "$@"

View File

@ -1,15 +0,0 @@
source bashlib
mpw() {
_nocopy() { echo >&2 "$(cat)"; }
_copy() { "$(type -P pbcopy || type -P xclip || echo _nocopy)"; }
# Empty the clipboard
:| _copy 2>/dev/null
# Ask for the user's name and password if not yet known.
MP_USERNAME=${MP_USERNAME:-$(ask -s 'Your Full Name:')}
MP_PASSWORD=${MP_PASSWORD:-$(ask -s 'Master Password:')}
# Start Master Password and copy the output.
printf %s "$(MP_USERNAME=$MP_USERNAME MP_PASSWORD=$MP_PASSWORD command mpw "$@")" | _copy
}

View File

@ -1,7 +1,7 @@
package com.lyndir.masterpassword.gui.model; package com.lyndir.masterpassword.gui.model;
import com.google.common.primitives.UnsignedInteger; import com.google.common.primitives.UnsignedInteger;
import com.lyndir.masterpassword.MPSiteType; import com.lyndir.masterpassword.MPResultType;
import com.lyndir.masterpassword.MasterKey; import com.lyndir.masterpassword.MasterKey;
@ -11,15 +11,15 @@ import com.lyndir.masterpassword.MasterKey;
public class IncognitoSite extends Site { public class IncognitoSite extends Site {
private String siteName; private String siteName;
private MPSiteType siteType;
private UnsignedInteger siteCounter; private UnsignedInteger siteCounter;
private MPResultType resultType;
private MasterKey.Version algorithmVersion; private MasterKey.Version algorithmVersion;
public IncognitoSite(final String siteName, final MPSiteType siteType, final UnsignedInteger siteCounter, public IncognitoSite(final String siteName, final UnsignedInteger siteCounter, final MPResultType resultType,
final MasterKey.Version algorithmVersion) { final MasterKey.Version algorithmVersion) {
this.siteName = siteName; this.siteName = siteName;
this.siteType = siteType;
this.siteCounter = siteCounter; this.siteCounter = siteCounter;
this.resultType = resultType;
this.algorithmVersion = algorithmVersion; this.algorithmVersion = algorithmVersion;
} }
@ -34,13 +34,13 @@ public class IncognitoSite extends Site {
} }
@Override @Override
public MPSiteType getSiteType() { public MPResultType getResultType() {
return siteType; return resultType;
} }
@Override @Override
public void setSiteType(final MPSiteType siteType) { public void setResultType(final MPResultType resultType) {
this.siteType = siteType; this.resultType = resultType;
} }
@Override @Override

View File

@ -1,7 +1,7 @@
package com.lyndir.masterpassword.gui.model; package com.lyndir.masterpassword.gui.model;
import com.google.common.primitives.UnsignedInteger; import com.google.common.primitives.UnsignedInteger;
import com.lyndir.masterpassword.MPSiteType; import com.lyndir.masterpassword.MPResultType;
import com.lyndir.masterpassword.MasterKey; import com.lyndir.masterpassword.MasterKey;
import com.lyndir.masterpassword.model.*; import com.lyndir.masterpassword.model.*;
@ -33,14 +33,14 @@ public class ModelSite extends Site {
} }
@Override @Override
public MPSiteType getSiteType() { public MPResultType getResultType() {
return model.getSiteType(); return model.getResultType();
} }
@Override @Override
public void setSiteType(final MPSiteType siteType) { public void setResultType(final MPResultType resultType) {
if (siteType != getSiteType()) { if (resultType != getResultType()) {
model.setSiteType( siteType ); model.setResultType( resultType );
MPUserFileManager.get().save(); MPUserFileManager.get().save();
} }
} }

View File

@ -79,7 +79,7 @@ public class ModelUser extends User {
@Override @Override
public void addSite(final Site site) { public void addSite(final Site site) {
model.addSite( new MPSite( model, site.getSiteName(), site.getSiteType(), site.getSiteCounter() ) ); model.addSite( new MPSite( model, site.getSiteName(), site.getSiteCounter(), site.getResultType() ) );
model.updateLastUsed(); model.updateLastUsed();
MPUserFileManager.get().save(); MPUserFileManager.get().save();
} }

View File

@ -3,7 +3,7 @@ package com.lyndir.masterpassword.gui.model;
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf; import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
import com.google.common.primitives.UnsignedInteger; import com.google.common.primitives.UnsignedInteger;
import com.lyndir.masterpassword.MPSiteType; import com.lyndir.masterpassword.MPResultType;
import com.lyndir.masterpassword.MasterKey; import com.lyndir.masterpassword.MasterKey;
@ -16,9 +16,9 @@ public abstract class Site {
public abstract void setSiteName(String siteName); public abstract void setSiteName(String siteName);
public abstract MPSiteType getSiteType(); public abstract MPResultType getResultType();
public abstract void setSiteType(MPSiteType siteType); public abstract void setResultType(MPResultType resultType);
public abstract MasterKey.Version getAlgorithmVersion(); public abstract MasterKey.Version getAlgorithmVersion();

View File

@ -32,10 +32,10 @@ public class PasswordFrame extends JFrame implements DocumentListener {
private final Components.GradientPanel root; private final Components.GradientPanel root;
private final JTextField siteNameField; private final JTextField siteNameField;
private final JButton siteActionButton; private final JButton siteActionButton;
private final JComboBox<MPSiteType> siteTypeField;
private final JComboBox<MasterKey.Version> siteVersionField; private final JComboBox<MasterKey.Version> siteVersionField;
private final JSpinner siteCounterField; private final JSpinner siteCounterField;
private final UnsignedIntegerModel siteCounterModel; private final UnsignedIntegerModel siteCounterModel;
private final JComboBox<MPResultType> resultTypeField;
private final JPasswordField passwordField; private final JPasswordField passwordField;
private final JLabel tipLabel; private final JLabel tipLabel;
private final JCheckBox maskPasswordField; private final JCheckBox maskPasswordField;
@ -118,17 +118,17 @@ public class PasswordFrame extends JFrame implements DocumentListener {
// Site Type & Counter // Site Type & Counter
siteCounterModel = new UnsignedIntegerModel( UnsignedInteger.ONE, UnsignedInteger.ONE ); siteCounterModel = new UnsignedIntegerModel( UnsignedInteger.ONE, UnsignedInteger.ONE );
MPSiteType[] types = Iterables.toArray( MPSiteType.forClass( MPSiteTypeClass.Generated ), MPSiteType.class ); MPResultType[] types = Iterables.toArray( MPResultType.forClass( MPResultTypeClass.Generated ), MPResultType.class );
JComponent siteSettings = Components.boxLayout( BoxLayout.LINE_AXIS, // JComponent siteSettings = Components.boxLayout( BoxLayout.LINE_AXIS, //
siteTypeField = Components.comboBox( types ), // resultTypeField = Components.comboBox( types ), //
Components.stud(), // Components.stud(), //
siteVersionField = Components.comboBox( MasterKey.Version.values() ), // siteVersionField = Components.comboBox( MasterKey.Version.values() ), //
Components.stud(), // Components.stud(), //
siteCounterField = Components.spinner( siteCounterModel ) ); siteCounterField = Components.spinner( siteCounterModel ) );
sitePanel.add( siteSettings ); sitePanel.add( siteSettings );
siteTypeField.setFont( Res.valueFont().deriveFont( 12f ) ); resultTypeField.setFont( Res.valueFont().deriveFont( 12f ) );
siteTypeField.setSelectedItem( MPSiteType.GeneratedLong ); resultTypeField.setSelectedItem( MPResultType.GeneratedLong );
siteTypeField.addItemListener( new ItemListener() { resultTypeField.addItemListener( new ItemListener() {
@Override @Override
public void itemStateChanged(final ItemEvent e) { public void itemStateChanged(final ItemEvent e) {
updatePassword( true ); updatePassword( true );
@ -215,7 +215,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
return Futures.immediateCancelledFuture(); return Futures.immediateCancelledFuture();
} }
MPSiteType siteType = siteTypeField.getModel().getElementAt( siteTypeField.getSelectedIndex() ); MPResultType resultType = resultTypeField.getModel().getElementAt( resultTypeField.getSelectedIndex() );
MasterKey.Version siteVersion = siteVersionField.getItemAt( siteVersionField.getSelectedIndex() ); MasterKey.Version siteVersion = siteVersionField.getItemAt( siteVersionField.getSelectedIndex() );
UnsignedInteger siteCounter = siteCounterModel.getNumber(); UnsignedInteger siteCounter = siteCounterModel.getNumber();
@ -228,9 +228,9 @@ public class PasswordFrame extends JFrame implements DocumentListener {
} }
} ); } );
final Site site = ifNotNullElse( Iterables.getFirst( siteResults, null ), final Site site = ifNotNullElse( Iterables.getFirst( siteResults, null ),
new IncognitoSite( siteNameQuery, siteType, siteCounter, siteVersion ) ); new IncognitoSite( siteNameQuery, siteCounter, resultType, siteVersion ) );
if ((currentSite != null) && currentSite.getSiteName().equals( site.getSiteName() )) { if ((currentSite != null) && currentSite.getSiteName().equals( site.getSiteName() )) {
site.setSiteType( siteType ); site.setResultType( resultType );
site.setAlgorithmVersion( siteVersion ); site.setAlgorithmVersion( siteVersion );
site.setSiteCounter( siteCounter ); site.setSiteCounter( siteCounter );
} }
@ -240,7 +240,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
public String call() public String call()
throws Exception { throws Exception {
return user.getKey( site.getAlgorithmVersion() ) return user.getKey( site.getAlgorithmVersion() )
.encode( site.getSiteName(), site.getSiteType(), site.getSiteCounter(), MPSiteVariant.Password, null ); .siteResult( site.getSiteName(), site.getSiteCounter(), MPKeyPurpose.Password, null, site.getResultType(), null );
} }
} ); } );
Futures.addCallback( passwordFuture, new FutureCallback<String>() { Futures.addCallback( passwordFuture, new FutureCallback<String>() {
@ -256,7 +256,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
siteActionButton.setText( "Delete Site" ); siteActionButton.setText( "Delete Site" );
else else
siteActionButton.setText( "Add Site" ); siteActionButton.setText( "Add Site" );
siteTypeField.setSelectedItem( currentSite.getSiteType() ); resultTypeField.setSelectedItem( currentSite.getResultType() );
siteVersionField.setSelectedItem( currentSite.getAlgorithmVersion() ); siteVersionField.setSelectedItem( currentSite.getAlgorithmVersion() );
siteCounterField.setValue( currentSite.getSiteCounter() ); siteCounterField.setValue( currentSite.getSiteCounter() );
siteNameField.setText( currentSite.getSiteName() ); siteNameField.setText( currentSite.getSiteName() );