Update Java to match C's internal changes.
This commit is contained in:
parent
70c784db83
commit
35c0431cec
@ -50,14 +50,11 @@ const MPResultType mpw_typeWithName(const char *typeName) {
|
||||
return MPResultTypeDeriveKey;
|
||||
}
|
||||
|
||||
// Lower-case and trim optionally leading "Generated" string from typeName to standardize it.
|
||||
size_t stdTypeNameOffset = 0;
|
||||
// Lower-case typeName to standardize it.
|
||||
size_t stdTypeNameSize = strlen( typeName );
|
||||
if (strstr( typeName, "Generated" ) == typeName)
|
||||
stdTypeNameSize -= (stdTypeNameOffset = strlen( "Generated" ));
|
||||
char stdTypeName[stdTypeNameSize + 1];
|
||||
for (size_t c = 0; c < stdTypeNameSize; ++c)
|
||||
stdTypeName[c] = (char)tolower( typeName[c + stdTypeNameOffset] );
|
||||
stdTypeName[c] = (char)tolower( typeName[c] );
|
||||
stdTypeName[stdTypeNameSize] = '\0';
|
||||
|
||||
// Find what password type is represented by the type name.
|
||||
|
@ -8,6 +8,7 @@ dependencies {
|
||||
compile (group: 'com.lyndir.lhunath.opal', name: 'opal-system', version: '1.6-p10') {
|
||||
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: 'org.jetbrains', name: 'annotations', version: '13.0'
|
||||
|
@ -31,6 +31,11 @@
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lyndir.lhunath.opal</groupId>
|
||||
<artifactId>opal-crypto</artifactId>
|
||||
<version>1.6-p9</version>
|
||||
</dependency>
|
||||
|
||||
<!-- EXTERNAL DEPENDENCIES -->
|
||||
<dependency>
|
||||
|
@ -32,68 +32,16 @@ public final class MPConstant {
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
public static final String env_rcDir = "MP_RCDIR";
|
||||
public static final String env_rcDir = "MPW_RCDIR";
|
||||
/**
|
||||
* mpw: permit automatic update checks.
|
||||
*/
|
||||
public static final String env_checkUpdates = "MP_CHECKUPDATES";
|
||||
public static final String env_checkUpdates = "MPW_CHECKUPDATES";
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -24,7 +24,6 @@ import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import java.util.*;
|
||||
import javax.annotation.Nullable;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
|
||||
|
||||
/**
|
||||
@ -32,15 +31,13 @@ import org.jetbrains.annotations.NonNls;
|
||||
*
|
||||
* @author lhunath
|
||||
*/
|
||||
public enum MPSiteType {
|
||||
public enum MPResultType {
|
||||
|
||||
GeneratedMaximum( "Max", "20 characters, contains symbols.", //
|
||||
ImmutableList.of( "x", "max", "maximum" ), // NON-NLS
|
||||
GeneratedMaximum( "Maximum", "20 characters, contains symbols.", //
|
||||
ImmutableList.of( new MPTemplate( "anoxxxxxxxxxxxxxxxxx" ), new MPTemplate( "axxxxxxxxxxxxxxxxxno" ) ), //
|
||||
MPSiteTypeClass.Generated, 0x0 ),
|
||||
MPResultTypeClass.Generated, 0x0 ),
|
||||
|
||||
GeneratedLong( "Long", "Copy-friendly, 14 characters, contains symbols.", //
|
||||
ImmutableList.of( "l", "long" ), // NON-NLS
|
||||
ImmutableList.of( new MPTemplate( "CvcvnoCvcvCvcv" ), new MPTemplate( "CvcvCvcvnoCvcv" ),
|
||||
new MPTemplate( "CvcvCvcvCvcvno" ), new MPTemplate( "CvccnoCvcvCvcv" ),
|
||||
new MPTemplate( "CvccCvcvnoCvcv" ), new MPTemplate( "CvccCvcvCvcvno" ),
|
||||
@ -52,65 +49,55 @@ public enum MPSiteType {
|
||||
new MPTemplate( "CvcvCvccnoCvcc" ), new MPTemplate( "CvcvCvccCvccno" ),
|
||||
new MPTemplate( "CvccnoCvcvCvcc" ), new MPTemplate( "CvccCvcvnoCvcc" ),
|
||||
new MPTemplate( "CvccCvcvCvccno" ) ), //
|
||||
MPSiteTypeClass.Generated, 0x1 ),
|
||||
MPResultTypeClass.Generated, 0x1 ),
|
||||
|
||||
GeneratedMedium( "Medium", "Copy-friendly, 8 characters, contains symbols.", //
|
||||
ImmutableList.of( "m", "med", "medium" ), // NON-NLS
|
||||
ImmutableList.of( new MPTemplate( "CvcnoCvc" ), new MPTemplate( "CvcCvcno" ) ), //
|
||||
MPSiteTypeClass.Generated, 0x2 ),
|
||||
MPResultTypeClass.Generated, 0x2 ),
|
||||
|
||||
GeneratedBasic( "Basic", "8 characters, no symbols.", //
|
||||
ImmutableList.of( "b", "basic" ), // NON-NLS
|
||||
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.", //
|
||||
ImmutableList.of( "s", "short" ), // NON-NLS
|
||||
ImmutableList.of( new MPTemplate( "Cvcn" ) ), //
|
||||
MPSiteTypeClass.Generated, 0x4 ),
|
||||
MPResultTypeClass.Generated, 0x4 ),
|
||||
|
||||
GeneratedPIN( "PIN", "4 numbers.", //
|
||||
ImmutableList.of( "i", "pin" ), // NON-NLS
|
||||
ImmutableList.of( new MPTemplate( "nnnn" ) ), //
|
||||
MPSiteTypeClass.Generated, 0x5 ),
|
||||
MPResultTypeClass.Generated, 0x5 ),
|
||||
|
||||
GeneratedName( "Name", "9 letter name.", //
|
||||
ImmutableList.of( "n", "name" ), // NON-NLS
|
||||
ImmutableList.of( new MPTemplate( "cvccvcvcv" ) ), //
|
||||
MPSiteTypeClass.Generated, 0xE ),
|
||||
MPResultTypeClass.Generated, 0xE ),
|
||||
|
||||
GeneratedPhrase( "Phrase", "20 character sentence.", //
|
||||
ImmutableList.of( "p", "phrase" ), // NON-NLS
|
||||
ImmutableList.of( new MPTemplate( "cvcc cvc cvccvcv cvc" ), new MPTemplate( "cvc cvccvcvcv cvcv" ),
|
||||
new MPTemplate( "cv cvccv cvc cvcvccv" ) ), //
|
||||
MPSiteTypeClass.Generated, 0xF ),
|
||||
MPResultTypeClass.Generated, 0xF ),
|
||||
|
||||
StoredPersonal( "Personal", "AES-encrypted, exportable.", //
|
||||
ImmutableList.of( "personal" ), // NON-NLS
|
||||
ImmutableList.<MPTemplate>of(), //
|
||||
MPSiteTypeClass.Stored, 0x0, MPSiteFeature.ExportContent ),
|
||||
MPResultTypeClass.Stored, 0x0, MPSiteFeature.ExportContent ),
|
||||
|
||||
StoredDevicePrivate( "Device", "AES-encrypted, not exported.", //
|
||||
ImmutableList.of( "device" ), // NON-NLS
|
||||
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 description;
|
||||
private final List<String> options;
|
||||
private final List<MPTemplate> templates;
|
||||
private final MPSiteTypeClass typeClass;
|
||||
private final MPResultTypeClass typeClass;
|
||||
private final int typeIndex;
|
||||
private final Set<MPSiteFeature> typeFeatures;
|
||||
|
||||
MPSiteType(final String shortName, final String description, final List<String> options, final List<MPTemplate> templates,
|
||||
final MPSiteTypeClass typeClass, final int typeIndex, final MPSiteFeature... typeFeatures) {
|
||||
MPResultType(final String shortName, final String description, final List<MPTemplate> templates,
|
||||
final MPResultTypeClass typeClass, final int typeIndex, final MPSiteFeature... typeFeatures) {
|
||||
|
||||
this.shortName = shortName;
|
||||
this.description = description;
|
||||
this.options = options;
|
||||
this.templates = templates;
|
||||
this.typeClass = typeClass;
|
||||
this.typeIndex = typeIndex;
|
||||
@ -131,11 +118,7 @@ public enum MPSiteType {
|
||||
return description;
|
||||
}
|
||||
|
||||
public List<String> getOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
public MPSiteTypeClass getTypeClass() {
|
||||
public MPResultTypeClass getTypeClass() {
|
||||
|
||||
return typeClass;
|
||||
}
|
||||
@ -154,35 +137,22 @@ public enum MPSiteType {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param option The option to select a type with. It is matched case insensitively.
|
||||
*
|
||||
* @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.
|
||||
* @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 with the given name.
|
||||
*/
|
||||
@Nullable
|
||||
@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;
|
||||
|
||||
for (final MPSiteType type : values())
|
||||
if (type.name().equalsIgnoreCase( name ))
|
||||
for (final MPResultType type : values())
|
||||
if (type.getShortName().toLowerCase( Locale.ROOT ).startsWith( shortNamePrefix.toLowerCase( Locale.ROOT ) ))
|
||||
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.
|
||||
*/
|
||||
public static ImmutableList<MPSiteType> forClass(final MPSiteTypeClass typeClass) {
|
||||
public static ImmutableList<MPResultType> forClass(final MPResultTypeClass typeClass) {
|
||||
|
||||
ImmutableList.Builder<MPSiteType> types = ImmutableList.builder();
|
||||
for (final MPSiteType type : values())
|
||||
ImmutableList.Builder<MPResultType> types = ImmutableList.builder();
|
||||
for (final MPResultType type : values())
|
||||
if (type.getTypeClass() == typeClass)
|
||||
types.add( type );
|
||||
|
||||
@ -205,11 +175,11 @@ public enum MPSiteType {
|
||||
*
|
||||
* @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())
|
||||
if (siteType.getType() == type)
|
||||
return siteType;
|
||||
for (final MPResultType resultType : values())
|
||||
if (resultType.getType() == type)
|
||||
return resultType;
|
||||
|
||||
throw logger.bug( "No type: %s", type );
|
||||
}
|
||||
@ -219,17 +189,27 @@ public enum MPSiteType {
|
||||
*
|
||||
* @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;
|
||||
ImmutableList.Builder<MPSiteType> types = ImmutableList.builder();
|
||||
for (final MPSiteType siteType : values())
|
||||
if (((siteType.getType() & ~0xF) & typeMask) != 0)
|
||||
types.add( siteType );
|
||||
ImmutableList.Builder<MPResultType> types = ImmutableList.builder();
|
||||
for (final MPResultType resultType : values())
|
||||
if (((resultType.getType() & ~0xF) & typeMask) != 0)
|
||||
types.add( resultType );
|
||||
|
||||
return types.build();
|
||||
}
|
||||
|
||||
public static MPResultType forInt(final int resultType) {
|
||||
|
||||
return values()[resultType];
|
||||
}
|
||||
|
||||
public int toInt() {
|
||||
|
||||
return ordinal();
|
||||
}
|
||||
|
||||
public MPTemplate getTemplateAtRollingIndex(final int templateIndex) {
|
||||
return templates.get( templateIndex % templates.size() );
|
||||
}
|
@ -23,13 +23,13 @@ package com.lyndir.masterpassword;
|
||||
*
|
||||
* @author lhunath
|
||||
*/
|
||||
public enum MPSiteTypeClass {
|
||||
public enum MPResultTypeClass {
|
||||
Generated( 1 << 4 ),
|
||||
Stored( 1 << 5 );
|
||||
|
||||
private final int mask;
|
||||
|
||||
MPSiteTypeClass(final int mask) {
|
||||
MPResultTypeClass(final int mask) {
|
||||
this.mask = mask;
|
||||
}
|
||||
|
@ -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 );
|
||||
}
|
||||
|
||||
}
|
@ -86,16 +86,61 @@ public abstract class MasterKey {
|
||||
allowNativeByDefault = allowNative;
|
||||
}
|
||||
|
||||
protected MasterKey(@Nonnull final String fullName) {
|
||||
protected MasterKey(final String fullName) {
|
||||
Preconditions.checkArgument( !fullName.isEmpty() );
|
||||
|
||||
this.fullName = 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
|
||||
@SuppressWarnings("MethodCanBeVariableArityMethod")
|
||||
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();
|
||||
|
||||
@Nonnull
|
||||
@ -125,9 +170,6 @@ public abstract class MasterKey {
|
||||
return idForBytes( getKey() );
|
||||
}
|
||||
|
||||
public abstract String encode(@Nonnull String siteName, MPSiteType siteType, @Nonnull UnsignedInteger siteCounter,
|
||||
MPSiteVariant siteVariant, @Nullable String siteContext);
|
||||
|
||||
public boolean isValid() {
|
||||
return masterKey != null;
|
||||
}
|
||||
@ -160,7 +202,7 @@ public abstract class MasterKey {
|
||||
|
||||
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);
|
||||
|
||||
@ -168,19 +210,19 @@ public abstract class MasterKey {
|
||||
/**
|
||||
* bugs:
|
||||
* - does math with chars whose signedness was platform-dependent.
|
||||
* - miscounted the byte-length fromInt multi-byte site names.
|
||||
* - miscounted the byte-length fromInt multi-byte full names.
|
||||
* - miscounted the byte-length for multi-byte site names.
|
||||
* - miscounted the byte-length for multi-byte full names.
|
||||
*/
|
||||
V0,
|
||||
/**
|
||||
* bugs:
|
||||
* - miscounted the byte-length fromInt multi-byte site names.
|
||||
* - miscounted the byte-length fromInt multi-byte full names.
|
||||
* - miscounted the byte-length for multi-byte site names.
|
||||
* - miscounted the byte-length for multi-byte full names.
|
||||
*/
|
||||
V1,
|
||||
/**
|
||||
* bugs:
|
||||
* - miscounted the byte-length fromInt multi-byte full names.
|
||||
* - miscounted the byte-length for multi-byte full names.
|
||||
*/
|
||||
V2,
|
||||
/**
|
||||
@ -200,20 +242,5 @@ public abstract class MasterKey {
|
||||
|
||||
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 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,29 +18,67 @@
|
||||
|
||||
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.UnsignedInteger;
|
||||
import com.lambdaworks.crypto.SCrypt;
|
||||
import com.lyndir.lhunath.opal.crypto.CryptUtils;
|
||||
import com.lyndir.lhunath.opal.system.*;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import java.nio.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Arrays;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
|
||||
|
||||
/**
|
||||
* bugs:
|
||||
* - V2: miscounted the byte-length fromInt multi-byte full names.
|
||||
* - V1: miscounted the byte-length fromInt multi-byte site names.
|
||||
* - V2: miscounted the byte-length for multi-byte full names.
|
||||
* - V1: miscounted the byte-length for multi-byte site names.
|
||||
* - V0: does math with chars whose signedness was platform-dependent.
|
||||
*
|
||||
* @author lhunath, 2014-08-30
|
||||
*/
|
||||
public class MasterKeyV0 extends MasterKey {
|
||||
|
||||
/**
|
||||
* 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")
|
||||
@ -59,112 +97,171 @@ public class MasterKeyV0 extends MasterKey {
|
||||
@Nullable
|
||||
@Override
|
||||
protected byte[] deriveKey(final char[] masterPassword) {
|
||||
Preconditions.checkArgument( masterPassword.length > 0 );
|
||||
|
||||
String fullName = getFullName();
|
||||
byte[] fullNameBytes = fullName.getBytes( MPConstant.mpw_charset );
|
||||
byte[] fullNameBytes = fullName.getBytes( mpw_charset );
|
||||
byte[] fullNameLengthBytes = bytesForInt( fullName.length() );
|
||||
ByteBuffer mpBytesBuf = mpw_charset.encode( CharBuffer.wrap( masterPassword ) );
|
||||
|
||||
String mpKeyScope = MPSiteVariant.Password.getScope();
|
||||
byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MPConstant.mpw_charset ), fullNameLengthBytes, fullNameBytes );
|
||||
logger.trc( "key scope: %s", mpKeyScope );
|
||||
logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
|
||||
logger.trc( "-- mpw_masterKey (algorithm: %u)", getAlgorithmVersion().toInt() );
|
||||
logger.trc( "fullName: %s", fullName );
|
||||
logger.trc( "masterPassword.id: %s", (Object) idForBytes( mpBytesBuf.array() ) );
|
||||
|
||||
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()];
|
||||
mpBytesBuf.get( mpBytes, 0, mpBytes.length );
|
||||
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
|
||||
protected byte[] scrypt(final byte[] masterKeySalt, final byte[] mpBytes) {
|
||||
try {
|
||||
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
|
||||
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) {
|
||||
logger.bug( e );
|
||||
return null;
|
||||
}
|
||||
finally {
|
||||
Arrays.fill( mpBytes, (byte) 0 );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encode(@Nonnull final String siteName, final MPSiteType siteType, @Nonnull UnsignedInteger siteCounter,
|
||||
final MPSiteVariant siteVariant, @Nullable final String siteContext) {
|
||||
Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
|
||||
protected byte[] siteKey(final String siteName, UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
|
||||
@Nullable final String keyContext) {
|
||||
Preconditions.checkArgument( !siteName.isEmpty() );
|
||||
|
||||
logger.trc( "-- mpw_siteKey (algorithm: %u)", getAlgorithmVersion().toInt() );
|
||||
logger.trc( "siteName: %s", siteName );
|
||||
logger.trc( "siteCounter: %d", siteCounter.longValue() );
|
||||
logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant );
|
||||
logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType );
|
||||
logger.trc( "siteCounter: %d", siteCounter );
|
||||
logger.trc( "keyPurpose: %d (%s)", keyPurpose.toInt(), keyPurpose.getShortName() );
|
||||
logger.trc( "keyContext: %s", keyContext );
|
||||
|
||||
String keyScope = keyPurpose.getScope();
|
||||
logger.trc( "keyScope: %s", keyScope );
|
||||
|
||||
// OTP counter value.
|
||||
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();
|
||||
byte[] siteNameBytes = siteName.getBytes( MPConstant.mpw_charset );
|
||||
// Calculate the site seed.
|
||||
byte[] siteNameBytes = siteName.getBytes( 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[] keyContextBytes = ((keyContext == null) || keyContext.isEmpty())? null: keyContext.getBytes( mpw_charset );
|
||||
byte[] keyContextLengthBytes = (keyContextBytes == null)? null: bytesForInt( keyContextBytes.length );
|
||||
logger.trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s",
|
||||
keyScope, CodeUtils.encodeHex( siteNameLengthBytes ), siteName, CodeUtils.encodeHex( siteCounterBytes ),
|
||||
(keyContextLengthBytes == null)? null: CodeUtils.encodeHex( keyContextLengthBytes ), keyContext );
|
||||
|
||||
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[] sitePasswordInfo = Bytes.concat( keyScope.getBytes( mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
|
||||
if (keyContextBytes != null)
|
||||
sitePasswordInfo = Bytes.concat( sitePasswordInfo, keyContextLengthBytes, keyContextBytes );
|
||||
logger.trc( " => siteSalt.id: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
|
||||
|
||||
byte[] sitePasswordSeedBytes = MPConstant.mpw_digest.of( getKey(), sitePasswordInfo );
|
||||
int[] sitePasswordSeed = new int[sitePasswordSeedBytes.length];
|
||||
for (int i = 0; i < sitePasswordSeedBytes.length; ++i) {
|
||||
byte[] masterKey = getKey();
|
||||
logger.trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )", (Object) idForBytes( masterKey ) );
|
||||
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 );
|
||||
Arrays.fill( buf.array(), (byte) ((sitePasswordSeedBytes[i] > 0)? 0x00: 0xFF) );
|
||||
Arrays.fill( buf.array(), (byte) ((siteKey[i] > 0)? 0x00: 0xFF) );
|
||||
buf.position( 2 );
|
||||
buf.put( sitePasswordSeedBytes[i] ).rewind();
|
||||
buf.put( siteKey[i] ).rewind();
|
||||
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 );
|
||||
int templateIndex = sitePasswordSeed[0];
|
||||
MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex );
|
||||
logger.trc( "type %s, template: %s", siteType, template.getTemplateString() );
|
||||
MPTemplate template = resultType.getTemplateAtRollingIndex( templateIndex );
|
||||
logger.trc( "template: %u => %s", templateIndex, template.getTemplateString() );
|
||||
|
||||
// Encode the password from the seed using the template.
|
||||
StringBuilder password = new StringBuilder( template.length() );
|
||||
for (int i = 0; i < template.length(); ++i) {
|
||||
int characterIndex = sitePasswordSeed[i + 1];
|
||||
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 );
|
||||
logger.trc( " - class: %c, index: %5u (0x%02hX) => character: %c",
|
||||
characterClass.getIdentifier(), characterIndex, sitePasswordSeed[i + 1], passwordCharacter );
|
||||
|
||||
password.append( passwordCharacter );
|
||||
}
|
||||
logger.trc( " => password: %s", password );
|
||||
|
||||
return password.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] bytesForInt(final int number) {
|
||||
return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MPConstant.mpw_byteOrder ).putInt( number ).array();
|
||||
public String siteState(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
|
||||
@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
|
||||
protected byte[] bytesForInt(@Nonnull final UnsignedInteger number) {
|
||||
return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MPConstant.mpw_byteOrder ).putInt( number.intValue() ).array();
|
||||
protected byte[] bytesForInt(final int number) {
|
||||
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
|
||||
protected byte[] idForBytes(final byte[] bytes) {
|
||||
return MPConstant.mpw_hash.of( bytes );
|
||||
return mpw_hash.of( bytes );
|
||||
}
|
||||
}
|
||||
|
@ -23,14 +23,13 @@ import com.google.common.primitives.Bytes;
|
||||
import com.google.common.primitives.UnsignedInteger;
|
||||
import com.lyndir.lhunath.opal.system.*;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
||||
/**
|
||||
* bugs:
|
||||
* - V2: miscounted the byte-length fromInt multi-byte full names.
|
||||
* - V1: miscounted the byte-length fromInt multi-byte site names.
|
||||
* - V2: miscounted the byte-length for multi-byte full names.
|
||||
* - V1: miscounted the byte-length for multi-byte site names.
|
||||
*
|
||||
* @author lhunath, 2014-08-30
|
||||
*/
|
||||
@ -50,53 +49,33 @@ public class MasterKeyV1 extends MasterKeyV0 {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encode(@Nonnull final String siteName, final MPSiteType siteType, @Nonnull UnsignedInteger siteCounter,
|
||||
final MPSiteVariant siteVariant, @Nullable final String siteContext) {
|
||||
Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
|
||||
Preconditions.checkArgument( !siteName.isEmpty() );
|
||||
public String siteResult(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
|
||||
@Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam) {
|
||||
|
||||
logger.trc( "siteName: %s", siteName );
|
||||
logger.trc( "siteCounter: %d", siteCounter.longValue() );
|
||||
logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant );
|
||||
logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType );
|
||||
byte[] sitePasswordSeed = siteKey( siteName, siteCounter, keyPurpose, keyContext );
|
||||
|
||||
if (siteCounter.longValue() == 0)
|
||||
siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (MPConstant.mpw_counter_timeout * 1000)) * MPConstant.mpw_counter_timeout );
|
||||
|
||||
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 ) ) );
|
||||
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 );
|
||||
int templateIndex = sitePasswordSeed[0] & 0xFF; // Mask the integer's sign.
|
||||
MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex );
|
||||
logger.trc( "type %s, template: %s", siteType, template.getTemplateString() );
|
||||
MPTemplate template = resultType.getTemplateAtRollingIndex( templateIndex );
|
||||
logger.trc( "template: %u => %s", templateIndex, template.getTemplateString() );
|
||||
|
||||
// Encode the password from the seed using the template.
|
||||
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 );
|
||||
logger.trc( " - class: %c, index: %3u (0x%02hhX) => character: %c",
|
||||
characterClass.getIdentifier(), characterIndex, sitePasswordSeed[i + 1], passwordCharacter );
|
||||
|
||||
password.append( passwordCharacter );
|
||||
}
|
||||
logger.trc( " => password: %s", password );
|
||||
|
||||
return password.toString();
|
||||
}
|
||||
|
@ -23,13 +23,12 @@ import com.google.common.primitives.Bytes;
|
||||
import com.google.common.primitives.UnsignedInteger;
|
||||
import com.lyndir.lhunath.opal.system.CodeUtils;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@ -49,54 +48,43 @@ public class MasterKeyV2 extends MasterKeyV1 {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encode(@Nonnull final String siteName, final MPSiteType siteType, @Nonnull UnsignedInteger siteCounter,
|
||||
final MPSiteVariant siteVariant, @Nullable final String siteContext) {
|
||||
Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
|
||||
protected byte[] siteKey(final String siteName, UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
|
||||
@Nullable final String keyContext) {
|
||||
Preconditions.checkArgument( !siteName.isEmpty() );
|
||||
|
||||
logger.trc( "-- mpw_siteKey (algorithm: %u)", getAlgorithmVersion().toInt() );
|
||||
logger.trc( "siteName: %s", siteName );
|
||||
logger.trc( "siteCounter: %d", siteCounter.longValue() );
|
||||
logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant );
|
||||
logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType );
|
||||
logger.trc( "siteCounter: %d", siteCounter );
|
||||
logger.trc( "keyPurpose: %d (%s)", keyPurpose.toInt(), keyPurpose.getShortName() );
|
||||
logger.trc( "keyContext: %s", keyContext );
|
||||
|
||||
String keyScope = keyPurpose.getScope();
|
||||
logger.trc( "keyScope: %s", keyScope );
|
||||
|
||||
// OTP counter value.
|
||||
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();
|
||||
byte[] siteNameBytes = siteName.getBytes( MPConstant.mpw_charset );
|
||||
// Calculate the site seed.
|
||||
byte[] siteNameBytes = siteName.getBytes( MasterKeyV0.mpw_charset );
|
||||
byte[] siteNameLengthBytes = bytesForInt( siteNameBytes.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[] keyContextBytes = ((keyContext == null) || keyContext.isEmpty())? null: keyContext.getBytes( MasterKeyV0.mpw_charset );
|
||||
byte[] keyContextLengthBytes = (keyContextBytes == null)? null: bytesForInt( keyContextBytes.length );
|
||||
logger.trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s",
|
||||
keyScope, CodeUtils.encodeHex( siteNameLengthBytes ), siteName, CodeUtils.encodeHex( siteCounterBytes ),
|
||||
(keyContextLengthBytes == null)? null: CodeUtils.encodeHex( keyContextLengthBytes ), keyContext );
|
||||
|
||||
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[] sitePasswordInfo = Bytes.concat( keyScope.getBytes( MasterKeyV0.mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
|
||||
if (keyContextBytes != null)
|
||||
sitePasswordInfo = Bytes.concat( sitePasswordInfo, keyContextLengthBytes, keyContextBytes );
|
||||
logger.trc( " => siteSalt.id: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
|
||||
|
||||
byte[] sitePasswordSeed = MPConstant.mpw_digest.of( getKey(), sitePasswordInfo );
|
||||
logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) );
|
||||
byte[] masterKey = getKey();
|
||||
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 );
|
||||
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();
|
||||
return sitePasswordSeedBytes;
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
package com.lyndir.masterpassword;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.primitives.Bytes;
|
||||
import com.lyndir.lhunath.opal.system.CodeUtils;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
@ -51,19 +52,37 @@ public class MasterKeyV3 extends MasterKeyV2 {
|
||||
@Nullable
|
||||
@Override
|
||||
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 );
|
||||
ByteBuffer mpBytesBuf = MasterKeyV0.mpw_charset.encode( CharBuffer.wrap( masterPassword ) );
|
||||
|
||||
String mpKeyScope = MPSiteVariant.Password.getScope();
|
||||
byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MPConstant.mpw_charset ), fullNameLengthBytes, fullNameBytes );
|
||||
logger.trc( "key scope: %s", mpKeyScope );
|
||||
logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
|
||||
logger.trc( "-- mpw_masterKey (algorithm: %u)", getAlgorithmVersion().toInt() );
|
||||
logger.trc( "fullName: %s", fullName );
|
||||
logger.trc( "masterPassword.id: %s", (Object) idForBytes( mpBytesBuf.array() ) );
|
||||
|
||||
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()];
|
||||
mpBytesBuf.get( mpBytes, 0, mpBytes.length );
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -14,50 +14,50 @@ import org.joda.time.Instant;
|
||||
*/
|
||||
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 );
|
||||
|
||||
private final MPUser user;
|
||||
private MasterKey.Version algorithmVersion;
|
||||
private Instant lastUsed;
|
||||
private String siteName;
|
||||
private MPSiteType siteType;
|
||||
private MPResultType resultType;
|
||||
private UnsignedInteger siteCounter;
|
||||
private int uses;
|
||||
private String loginName;
|
||||
|
||||
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.algorithmVersion = MasterKey.Version.CURRENT;
|
||||
this.lastUsed = new Instant();
|
||||
this.siteName = siteName;
|
||||
this.siteType = siteType;
|
||||
this.resultType = resultType;
|
||||
this.siteCounter = siteCounter;
|
||||
}
|
||||
|
||||
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) {
|
||||
this.user = user;
|
||||
this.algorithmVersion = algorithmVersion;
|
||||
this.lastUsed = lastUsed;
|
||||
this.siteName = siteName;
|
||||
this.siteType = siteType;
|
||||
this.resultType = resultType;
|
||||
this.siteCounter = siteCounter;
|
||||
this.uses = uses;
|
||||
this.loginName = loginName;
|
||||
}
|
||||
|
||||
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) {
|
||||
return masterKey.encode( siteName, siteType, siteCounter, variant, context );
|
||||
public String resultFor(final MasterKey masterKey, final MPKeyPurpose purpose, @Nullable final String context) {
|
||||
return masterKey.siteResult( siteName, siteCounter, purpose, context, resultType, null );
|
||||
}
|
||||
|
||||
public MPUser getUser() {
|
||||
@ -94,12 +94,12 @@ public class MPSite {
|
||||
this.siteName = siteName;
|
||||
}
|
||||
|
||||
public MPSiteType getSiteType() {
|
||||
return siteType;
|
||||
public MPResultType getResultType() {
|
||||
return resultType;
|
||||
}
|
||||
|
||||
public void setSiteType(final MPSiteType siteType) {
|
||||
this.siteType = siteType;
|
||||
public void setResultType(final MPResultType resultType) {
|
||||
this.resultType = resultType;
|
||||
}
|
||||
|
||||
public UnsignedInteger getSiteCounter() {
|
||||
|
@ -64,7 +64,7 @@ public class MPSiteMarshaller {
|
||||
header.append( "# Full Name: " ).append( user.getFullName() ).append( '\n' );
|
||||
header.append( "# Avatar: " ).append( user.getAvatar() ).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( "# Default Type: " ).append( user.getDefaultType().getType() ).append( '\n' );
|
||||
header.append( "# Passwords: " ).append( this.contentMode.name() ).append( '\n' );
|
||||
@ -82,7 +82,7 @@ public class MPSiteMarshaller {
|
||||
rfc3339.print( site.getLastUsed() ), // lastUsed
|
||||
site.getUses(), // uses
|
||||
strf( "%d:%d:%d", //
|
||||
site.getSiteType().getType(), // type
|
||||
site.getResultType().getType(), // type
|
||||
site.getAlgorithmVersion().toInt(), // algorithm
|
||||
site.getSiteCounter().intValue() ), // counter
|
||||
ifNotNullElse( site.getLoginName(), "" ), // loginName
|
||||
|
@ -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.util.ConversionUtils;
|
||||
import com.lyndir.lhunath.opal.system.util.NNOperation;
|
||||
import com.lyndir.masterpassword.MPSiteType;
|
||||
import com.lyndir.masterpassword.MPResultType;
|
||||
import com.lyndir.masterpassword.MasterKey;
|
||||
import java.io.*;
|
||||
import java.util.List;
|
||||
@ -58,7 +58,7 @@ public class MPSiteUnmarshaller {
|
||||
String fullName = null;
|
||||
int mpVersion = 0, importFormat = 0, avatar = 0;
|
||||
boolean clearContent = false, headerStarted = false;
|
||||
MPSiteType defaultType = MPSiteType.GeneratedLong;
|
||||
MPResultType defaultType = MPResultType.GeneratedLong;
|
||||
MPSiteUnmarshaller marshaller = null;
|
||||
final ImmutableList.Builder<MPSite> sites = ImmutableList.builder();
|
||||
|
||||
@ -92,7 +92,7 @@ public class MPSiteUnmarshaller {
|
||||
else if ("Passwords".equalsIgnoreCase( name ))
|
||||
clearContent = "visible".equalsIgnoreCase( value );
|
||||
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,
|
||||
final MPSiteType defaultType, final boolean clearContent) {
|
||||
final MPResultType defaultType, final boolean clearContent) {
|
||||
this.importFormat = importFormat;
|
||||
this.mpVersion = mpVersion;
|
||||
this.clearContent = clearContent;
|
||||
@ -131,7 +131,7 @@ public class MPSiteUnmarshaller {
|
||||
MasterKey.Version.fromInt( ConversionUtils.toIntegerNN( siteMatcher.group( 4 ).replace( ":", "" ) ) ), //
|
||||
rfc3339.parseDateTime( siteMatcher.group( 1 ) ).toInstant(), //
|
||||
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 ) ), //
|
||||
null, //
|
||||
siteMatcher.group( 6 ) );
|
||||
@ -142,7 +142,7 @@ public class MPSiteUnmarshaller {
|
||||
MasterKey.Version.fromInt( ConversionUtils.toIntegerNN( siteMatcher.group( 4 ).replace( ":", "" ) ) ), //
|
||||
rfc3339.parseDateTime( siteMatcher.group( 1 ) ).toInstant(), //
|
||||
siteMatcher.group( 7 ), //
|
||||
MPSiteType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
|
||||
MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
|
||||
UnsignedInteger.valueOf( siteMatcher.group( 5 ).replace( ":", "" ) ), //
|
||||
ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ), //
|
||||
siteMatcher.group( 6 ), //
|
||||
|
@ -5,7 +5,7 @@ import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.lyndir.lhunath.opal.system.CodeUtils;
|
||||
import com.lyndir.masterpassword.MPSiteType;
|
||||
import com.lyndir.masterpassword.MPResultType;
|
||||
import com.lyndir.masterpassword.MasterKey;
|
||||
import java.util.*;
|
||||
import javax.annotation.Nonnull;
|
||||
@ -25,7 +25,7 @@ public class MPUser implements Comparable<MPUser> {
|
||||
private byte[] keyID;
|
||||
private final MasterKey.Version algorithmVersion;
|
||||
private int avatar;
|
||||
private MPSiteType defaultType;
|
||||
private MPResultType defaultType;
|
||||
private ReadableInstant lastUsed;
|
||||
|
||||
public MPUser(final String fullName) {
|
||||
@ -33,11 +33,11 @@ public class MPUser implements Comparable<MPUser> {
|
||||
}
|
||||
|
||||
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,
|
||||
final MPSiteType defaultType, final ReadableInstant lastUsed) {
|
||||
final MPResultType defaultType, final ReadableInstant lastUsed) {
|
||||
this.fullName = fullName;
|
||||
this.keyID = (keyID == null)? null: keyID.clone();
|
||||
this.algorithmVersion = algorithmVersion;
|
||||
@ -107,11 +107,11 @@ public class MPUser implements Comparable<MPUser> {
|
||||
this.avatar = avatar;
|
||||
}
|
||||
|
||||
public MPSiteType getDefaultType() {
|
||||
public MPResultType getDefaultType() {
|
||||
return defaultType;
|
||||
}
|
||||
|
||||
public void setDefaultType(final MPSiteType defaultType) {
|
||||
public void setDefaultType(final MPResultType defaultType) {
|
||||
this.defaultType = defaultType;
|
||||
}
|
||||
|
||||
|
@ -99,12 +99,12 @@ public class MPTestSuite implements Callable<Boolean> {
|
||||
currentCase.siteName = text;
|
||||
if ("siteCounter".equals( qName ))
|
||||
currentCase.siteCounter = text.isEmpty()? null: UnsignedInteger.valueOf( text );
|
||||
if ("siteType".equals( qName ))
|
||||
currentCase.siteType = text;
|
||||
if ("siteVariant".equals( qName ))
|
||||
currentCase.siteVariant = text;
|
||||
if ("siteContext".equals( qName ))
|
||||
currentCase.siteContext = text;
|
||||
if ("resultType".equals( qName ))
|
||||
currentCase.resultType = text;
|
||||
if ("keyPurpose".equals( qName ))
|
||||
currentCase.keyPurpose = text;
|
||||
if ("keyContext".equals( qName ))
|
||||
currentCase.keyContext = text;
|
||||
if ("result".equals( qName ))
|
||||
currentCase.result = text;
|
||||
}
|
||||
@ -173,8 +173,9 @@ public class MPTestSuite implements Callable<Boolean> {
|
||||
@Override
|
||||
public Boolean apply(@Nonnull final MPTests.Case testCase) {
|
||||
MasterKey masterKey = MasterKey.create( testCase.getAlgorithm(), testCase.getFullName(), testCase.getMasterPassword() );
|
||||
String sitePassword = masterKey.encode( testCase.getSiteName(), testCase.getSiteType(), testCase.getSiteCounter(),
|
||||
testCase.getSiteVariant(), testCase.getSiteContext() );
|
||||
String sitePassword = masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
|
||||
testCase.getKeyContext(), testCase.getResultType(),
|
||||
null );
|
||||
|
||||
return testCase.getResult().equals( sitePassword );
|
||||
}
|
||||
|
@ -74,9 +74,9 @@ public class MPTests {
|
||||
String keyID;
|
||||
String siteName;
|
||||
UnsignedInteger siteCounter;
|
||||
String siteType;
|
||||
String siteVariant;
|
||||
String siteContext;
|
||||
String resultType;
|
||||
String keyPurpose;
|
||||
String keyContext;
|
||||
String result;
|
||||
|
||||
private transient Case parentCase;
|
||||
@ -130,25 +130,25 @@ public class MPTests {
|
||||
return checkNotNull( parentCase.siteCounter );
|
||||
}
|
||||
} );
|
||||
siteType = ifNotNullElse( siteType, new NNSupplier<String>() {
|
||||
resultType = ifNotNullElse( resultType, new NNSupplier<String>() {
|
||||
@Nonnull
|
||||
@Override
|
||||
public String get() {
|
||||
return checkNotNull( parentCase.siteType );
|
||||
return checkNotNull( parentCase.resultType );
|
||||
}
|
||||
} );
|
||||
siteVariant = ifNotNullElse( siteVariant, new NNSupplier<String>() {
|
||||
keyPurpose = ifNotNullElse( keyPurpose, new NNSupplier<String>() {
|
||||
@Nonnull
|
||||
@Override
|
||||
public String get() {
|
||||
return checkNotNull( parentCase.siteVariant );
|
||||
return checkNotNull( parentCase.keyPurpose );
|
||||
}
|
||||
} );
|
||||
siteContext = ifNotNullElse( siteContext, new NNSupplier<String>() {
|
||||
keyContext = ifNotNullElse( keyContext, new NNSupplier<String>() {
|
||||
@Nonnull
|
||||
@Override
|
||||
public String get() {
|
||||
return (parentCase == null)? "": checkNotNull( parentCase.siteContext );
|
||||
return (parentCase == null)? "": checkNotNull( parentCase.keyContext );
|
||||
}
|
||||
} );
|
||||
result = ifNotNullElse( result, new NNSupplier<String>() {
|
||||
@ -200,18 +200,18 @@ public class MPTests {
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public MPSiteType getSiteType() {
|
||||
return MPSiteType.forName( checkNotNull( siteType ) );
|
||||
public MPResultType getResultType() {
|
||||
return MPResultType.forName( checkNotNull( resultType ) );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public MPSiteVariant getSiteVariant() {
|
||||
return MPSiteVariant.forName( checkNotNull( siteVariant ) );
|
||||
public MPKeyPurpose getKeyPurpose() {
|
||||
return MPKeyPurpose.forName( checkNotNull( keyPurpose ) );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public String getSiteContext() {
|
||||
return checkNotNull( siteContext );
|
||||
public String getKeyContext() {
|
||||
return checkNotNull( keyContext );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
@ -7,7 +7,7 @@
|
||||
<keyID>98EEF4D1DF46D849574A82A03C3177056B15DFFCA29BB3899DE4628453675302</keyID>
|
||||
<siteName>masterpasswordapp.com</siteName>
|
||||
<siteCounter>1</siteCounter>
|
||||
<resultType>GeneratedLong</resultType>
|
||||
<resultType>Long</resultType>
|
||||
<keyPurpose>Authentication</keyPurpose>
|
||||
<result><!-- abstract --></result>
|
||||
</case>
|
||||
@ -33,12 +33,12 @@
|
||||
</case>
|
||||
<case id="v3_loginName" parent="v3">
|
||||
<keyPurpose>Identification</keyPurpose>
|
||||
<resultType>GeneratedName</resultType>
|
||||
<resultType>Name</resultType>
|
||||
<result>wohzaqage</result>
|
||||
</case>
|
||||
<case id="v3_securityAnswer" parent="v3">
|
||||
<keyPurpose>Recovery</keyPurpose>
|
||||
<resultType>GeneratedPhrase</resultType>
|
||||
<resultType>Phrase</resultType>
|
||||
<result>xin diyjiqoja hubu</result>
|
||||
</case>
|
||||
<case id="v3_securityAnswer_context" parent="v3_securityAnswer">
|
||||
@ -46,31 +46,31 @@
|
||||
<result>xogx tem cegyiva jab</result>
|
||||
</case>
|
||||
<case id="v3_type_maximum" parent="v3">
|
||||
<resultType>GeneratedMaximum</resultType>
|
||||
<resultType>Maximum</resultType>
|
||||
<result>W6@692^B1#&@gVdSdLZ@</result>
|
||||
</case>
|
||||
<case id="v3_type_medium" parent="v3">
|
||||
<resultType>GeneratedMedium</resultType>
|
||||
<resultType>Medium</resultType>
|
||||
<result>Jej2$Quv</result>
|
||||
</case>
|
||||
<case id="v3_type_basic" parent="v3">
|
||||
<resultType>GeneratedBasic</resultType>
|
||||
<resultType>Basic</resultType>
|
||||
<result>WAo2xIg6</result>
|
||||
</case>
|
||||
<case id="v3_type_short" parent="v3">
|
||||
<resultType>GeneratedShort</resultType>
|
||||
<resultType>Short</resultType>
|
||||
<result>Jej2</result>
|
||||
</case>
|
||||
<case id="v3_type_pin" parent="v3">
|
||||
<resultType>GeneratedPIN</resultType>
|
||||
<resultType>PIN</resultType>
|
||||
<result>7662</result>
|
||||
</case>
|
||||
<case id="v3_type_name" parent="v3">
|
||||
<resultType>GeneratedName</resultType>
|
||||
<resultType>Name</resultType>
|
||||
<result>jejraquvo</result>
|
||||
</case>
|
||||
<case id="v3_type_phrase" parent="v3">
|
||||
<resultType>GeneratedPhrase</resultType>
|
||||
<resultType>Phrase</resultType>
|
||||
<result>jejr quv cabsibu tam</result>
|
||||
</case>
|
||||
<case id="v3_counter_ceiling" parent="v3">
|
||||
@ -99,12 +99,12 @@
|
||||
</case>
|
||||
<case id="v2_loginName" parent="v2">
|
||||
<keyPurpose>Identification</keyPurpose>
|
||||
<resultType>GeneratedName</resultType>
|
||||
<resultType>Name</resultType>
|
||||
<result>wohzaqage</result>
|
||||
</case>
|
||||
<case id="v2_securityAnswer" parent="v2">
|
||||
<keyPurpose>Recovery</keyPurpose>
|
||||
<resultType>GeneratedPhrase</resultType>
|
||||
<resultType>Phrase</resultType>
|
||||
<result>xin diyjiqoja hubu</result>
|
||||
</case>
|
||||
<case id="v2_securityAnswer_context" parent="v2_securityAnswer">
|
||||
@ -112,31 +112,31 @@
|
||||
<result>xogx tem cegyiva jab</result>
|
||||
</case>
|
||||
<case id="v2_type_maximum" parent="v2">
|
||||
<resultType>GeneratedMaximum</resultType>
|
||||
<resultType>Maximum</resultType>
|
||||
<result>W6@692^B1#&@gVdSdLZ@</result>
|
||||
</case>
|
||||
<case id="v2_type_medium" parent="v2">
|
||||
<resultType>GeneratedMedium</resultType>
|
||||
<resultType>Medium</resultType>
|
||||
<result>Jej2$Quv</result>
|
||||
</case>
|
||||
<case id="v2_type_basic" parent="v2">
|
||||
<resultType>GeneratedBasic</resultType>
|
||||
<resultType>Basic</resultType>
|
||||
<result>WAo2xIg6</result>
|
||||
</case>
|
||||
<case id="v2_type_short" parent="v2">
|
||||
<resultType>GeneratedShort</resultType>
|
||||
<resultType>Short</resultType>
|
||||
<result>Jej2</result>
|
||||
</case>
|
||||
<case id="v2_type_pin" parent="v2">
|
||||
<resultType>GeneratedPIN</resultType>
|
||||
<resultType>PIN</resultType>
|
||||
<result>7662</result>
|
||||
</case>
|
||||
<case id="v2_type_name" parent="v2">
|
||||
<resultType>GeneratedName</resultType>
|
||||
<resultType>Name</resultType>
|
||||
<result>jejraquvo</result>
|
||||
</case>
|
||||
<case id="v2_type_phrase" parent="v2">
|
||||
<resultType>GeneratedPhrase</resultType>
|
||||
<resultType>Phrase</resultType>
|
||||
<result>jejr quv cabsibu tam</result>
|
||||
</case>
|
||||
<case id="v2_counter_ceiling" parent="v2">
|
||||
@ -165,12 +165,12 @@
|
||||
</case>
|
||||
<case id="v1_loginName" parent="v1">
|
||||
<keyPurpose>Identification</keyPurpose>
|
||||
<resultType>GeneratedName</resultType>
|
||||
<resultType>Name</resultType>
|
||||
<result>wohzaqage</result>
|
||||
</case>
|
||||
<case id="v1_securityAnswer" parent="v1">
|
||||
<keyPurpose>Recovery</keyPurpose>
|
||||
<resultType>GeneratedPhrase</resultType>
|
||||
<resultType>Phrase</resultType>
|
||||
<result>xin diyjiqoja hubu</result>
|
||||
</case>
|
||||
<case id="v1_securityAnswer_context" parent="v1_securityAnswer">
|
||||
@ -178,31 +178,31 @@
|
||||
<result>xogx tem cegyiva jab</result>
|
||||
</case>
|
||||
<case id="v1_type_maximum" parent="v1">
|
||||
<resultType>GeneratedMaximum</resultType>
|
||||
<resultType>Maximum</resultType>
|
||||
<result>W6@692^B1#&@gVdSdLZ@</result>
|
||||
</case>
|
||||
<case id="v1_type_medium" parent="v1">
|
||||
<resultType>GeneratedMedium</resultType>
|
||||
<resultType>Medium</resultType>
|
||||
<result>Jej2$Quv</result>
|
||||
</case>
|
||||
<case id="v1_type_basic" parent="v1">
|
||||
<resultType>GeneratedBasic</resultType>
|
||||
<resultType>Basic</resultType>
|
||||
<result>WAo2xIg6</result>
|
||||
</case>
|
||||
<case id="v1_type_short" parent="v1">
|
||||
<resultType>GeneratedShort</resultType>
|
||||
<resultType>Short</resultType>
|
||||
<result>Jej2</result>
|
||||
</case>
|
||||
<case id="v1_type_pin" parent="v1">
|
||||
<resultType>GeneratedPIN</resultType>
|
||||
<resultType>PIN</resultType>
|
||||
<result>7662</result>
|
||||
</case>
|
||||
<case id="v1_type_name" parent="v1">
|
||||
<resultType>GeneratedName</resultType>
|
||||
<resultType>Name</resultType>
|
||||
<result>jejraquvo</result>
|
||||
</case>
|
||||
<case id="v1_type_phrase" parent="v1">
|
||||
<resultType>GeneratedPhrase</resultType>
|
||||
<resultType>Phrase</resultType>
|
||||
<result>jejr quv cabsibu tam</result>
|
||||
</case>
|
||||
<case id="v1_counter_ceiling" parent="v1">
|
||||
@ -231,12 +231,12 @@
|
||||
</case>
|
||||
<case id="v0_loginName" parent="v0">
|
||||
<keyPurpose>Identification</keyPurpose>
|
||||
<resultType>GeneratedName</resultType>
|
||||
<resultType>Name</resultType>
|
||||
<result>lozwajave</result>
|
||||
</case>
|
||||
<case id="v0_securityAnswer" parent="v0">
|
||||
<keyPurpose>Recovery</keyPurpose>
|
||||
<resultType>GeneratedPhrase</resultType>
|
||||
<resultType>Phrase</resultType>
|
||||
<result>miy lirfijoja dubu</result>
|
||||
</case>
|
||||
<case id="v0_securityAnswer_context" parent="v0_securityAnswer">
|
||||
@ -244,31 +244,31 @@
|
||||
<result>movm bex gevrica jaf</result>
|
||||
</case>
|
||||
<case id="v0_type_maximum" parent="v0">
|
||||
<resultType>GeneratedMaximum</resultType>
|
||||
<resultType>Maximum</resultType>
|
||||
<result>w1!3bA3icmRAc)SS@lwl</result>
|
||||
</case>
|
||||
<case id="v0_type_medium" parent="v0">
|
||||
<resultType>GeneratedMedium</resultType>
|
||||
<resultType>Medium</resultType>
|
||||
<result>Fej7]Jug</result>
|
||||
</case>
|
||||
<case id="v0_type_basic" parent="v0">
|
||||
<resultType>GeneratedBasic</resultType>
|
||||
<resultType>Basic</resultType>
|
||||
<result>wvH7irC1</result>
|
||||
</case>
|
||||
<case id="v0_type_short" parent="v0">
|
||||
<resultType>GeneratedShort</resultType>
|
||||
<resultType>Short</resultType>
|
||||
<result>Fej7</result>
|
||||
</case>
|
||||
<case id="v0_type_pin" parent="v0">
|
||||
<resultType>GeneratedPIN</resultType>
|
||||
<resultType>PIN</resultType>
|
||||
<result>2117</result>
|
||||
</case>
|
||||
<case id="v0_type_name" parent="v0">
|
||||
<resultType>GeneratedName</resultType>
|
||||
<resultType>Name</resultType>
|
||||
<result>fejrajugo</result>
|
||||
</case>
|
||||
<case id="v0_type_phrase" parent="v0">
|
||||
<resultType>GeneratedPhrase</resultType>
|
||||
<resultType>Phrase</resultType>
|
||||
<result>fejr jug gabsibu bax</result>
|
||||
</case>
|
||||
<case id="v0_counter_ceiling" parent="v0">
|
||||
|
@ -55,8 +55,9 @@ public class MasterKeyTest {
|
||||
MasterKey masterKey = MasterKey.create( testCase.getAlgorithm(), testCase.getFullName(), testCase.getMasterPassword() );
|
||||
|
||||
assertEquals(
|
||||
masterKey.encode( testCase.getSiteName(), testCase.getSiteType(), testCase.getSiteCounter(),
|
||||
testCase.getSiteVariant(), testCase.getSiteContext() ),
|
||||
masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
|
||||
testCase.getKeyContext(), testCase.getResultType(),
|
||||
null ),
|
||||
testCase.getResult(), "[testEncode] Failed test case: " + testCase );
|
||||
|
||||
return true;
|
||||
@ -101,8 +102,9 @@ public class MasterKeyTest {
|
||||
|
||||
MasterKey masterKey = MasterKey.create( defaultCase.getFullName(), defaultCase.getMasterPassword() );
|
||||
masterKey.invalidate();
|
||||
masterKey.encode( defaultCase.getSiteName(), defaultCase.getSiteType(), defaultCase.getSiteCounter(),
|
||||
defaultCase.getSiteVariant(), defaultCase.getSiteContext() );
|
||||
masterKey.siteResult( defaultCase.getSiteName(), defaultCase.getSiteCounter(), defaultCase.getKeyPurpose(),
|
||||
defaultCase.getKeyContext(), defaultCase.getResultType(),
|
||||
null );
|
||||
|
||||
fail( "[testInvalidate] Master key should have been invalidated, but was still usable." );
|
||||
}
|
||||
|
@ -22,7 +22,6 @@
|
||||
<module>masterpassword-tests</module>
|
||||
<module>masterpassword-algorithm</module>
|
||||
<module>masterpassword-model</module>
|
||||
<module>masterpassword-cli</module>
|
||||
<module>masterpassword-gui</module>
|
||||
</modules>
|
||||
|
||||
|
@ -9,9 +9,6 @@ project(':masterpassword-model').projectDir = new File( '../core/java/model' )
|
||||
include 'masterpassword-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'
|
||||
project(':masterpassword-gui').projectDir = new File( '../platform-independent/gui-java' )
|
||||
|
||||
|
@ -54,7 +54,7 @@ public class EmergencyActivity extends Activity {
|
||||
|
||||
private final Preferences preferences = Preferences.get( this );
|
||||
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 ListenableFuture<MasterKey> masterKeyFuture;
|
||||
@ -71,8 +71,8 @@ public class EmergencyActivity extends Activity {
|
||||
@BindView(R.id.siteNameField)
|
||||
EditText siteNameField;
|
||||
|
||||
@BindView(R.id.siteTypeButton)
|
||||
Button siteTypeButton;
|
||||
@BindView(R.id.resultTypeButton)
|
||||
Button resultTypeButton;
|
||||
|
||||
@BindView(R.id.counterField)
|
||||
Button siteCounterButton;
|
||||
@ -131,15 +131,15 @@ public class EmergencyActivity extends Activity {
|
||||
updateSitePassword();
|
||||
}
|
||||
} );
|
||||
siteTypeButton.setOnClickListener( new View.OnClickListener() {
|
||||
resultTypeButton.setOnClickListener( new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final View v) {
|
||||
@SuppressWarnings("SuspiciousMethodCalls")
|
||||
MPSiteType siteType =
|
||||
allSiteTypes.get( (allSiteTypes.indexOf( siteTypeButton.getTag() ) + 1) % allSiteTypes.size() );
|
||||
preferences.setDefaultSiteType( siteType );
|
||||
siteTypeButton.setTag( siteType );
|
||||
siteTypeButton.setText( siteType.getShortName() );
|
||||
MPResultType resultType =
|
||||
allResultTypes.get( (allResultTypes.indexOf( resultTypeButton.getTag() ) + 1) % allResultTypes.size() );
|
||||
preferences.setDefaultResultType( resultType );
|
||||
resultTypeButton.setTag( resultType );
|
||||
resultTypeButton.setText( resultType.getShortName() );
|
||||
updateSitePassword();
|
||||
}
|
||||
} );
|
||||
@ -220,9 +220,9 @@ public class EmergencyActivity extends Activity {
|
||||
forgetPasswordField.setChecked( preferences.isForgetPassword() );
|
||||
maskPasswordField.setChecked( preferences.isMaskPassword() );
|
||||
sitePasswordField.setTransformationMethod( preferences.isMaskPassword()? new PasswordTransformationMethod(): null );
|
||||
MPSiteType defaultSiteType = preferences.getDefaultSiteType();
|
||||
siteTypeButton.setTag( defaultSiteType );
|
||||
siteTypeButton.setText( defaultSiteType.getShortName() );
|
||||
MPResultType defaultResultType = preferences.getDefaultResultType();
|
||||
resultTypeButton.setTag( defaultResultType );
|
||||
resultTypeButton.setText( defaultResultType.getShortName() );
|
||||
MasterKey.Version defaultVersion = preferences.getDefaultVersion();
|
||||
siteVersionButton.setTag( defaultVersion );
|
||||
siteVersionButton.setText( defaultVersion.name() );
|
||||
@ -314,7 +314,7 @@ public class EmergencyActivity extends Activity {
|
||||
|
||||
private void updateSitePassword() {
|
||||
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() );
|
||||
|
||||
if ((masterKeyFuture == null) || siteName.isEmpty() || (type == null)) {
|
||||
@ -332,7 +332,7 @@ public class EmergencyActivity extends Activity {
|
||||
@Override
|
||||
public void run() {
|
||||
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() {
|
||||
@Override
|
||||
|
@ -38,7 +38,7 @@ public final class Preferences {
|
||||
private static final String PREF_FORGET_PASSWORD = "forgetPassword";
|
||||
private static final String PREF_MASK_PASSWORD = "maskPassword";
|
||||
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 Preferences instance;
|
||||
|
||||
@ -138,20 +138,20 @@ public final class Preferences {
|
||||
return prefs().getString( PREF_FULL_NAME, "" );
|
||||
}
|
||||
|
||||
public boolean setDefaultSiteType(@Nonnull final MPSiteType value) {
|
||||
if (getDefaultSiteType() == value)
|
||||
public boolean setDefaultResultType(final MPResultType value) {
|
||||
if (getDefaultResultType() == value)
|
||||
return false;
|
||||
|
||||
prefs().edit().putInt( PREF_SITE_TYPE, value.ordinal() ).apply();
|
||||
prefs().edit().putInt( PREF_RESULT_TYPE, value.ordinal() ).apply();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public MPSiteType getDefaultSiteType() {
|
||||
return MPSiteType.values()[prefs().getInt( PREF_SITE_TYPE, MPSiteType.GeneratedLong.ordinal() )];
|
||||
public MPResultType getDefaultResultType() {
|
||||
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)
|
||||
return false;
|
||||
|
||||
|
@ -105,7 +105,7 @@
|
||||
android:id="@id/sitePasswordField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:nextFocusForward="@+id/siteTypeButton"
|
||||
android:nextFocusForward="@+id/resultTypeButton"
|
||||
android:gravity="center"
|
||||
android:background="@android:color/transparent"
|
||||
android:textColor="#FFFFFF"
|
||||
@ -157,7 +157,7 @@
|
||||
android:gravity="center">
|
||||
|
||||
<Button
|
||||
android:id="@id/siteTypeButton"
|
||||
android:id="@id/resultTypeButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
@ -175,12 +175,12 @@
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:labelFor="@id/siteTypeButton"
|
||||
android:labelFor="@id/resultTypeButton"
|
||||
android:gravity="center"
|
||||
android:background="@android:color/transparent"
|
||||
android:textSize="12sp"
|
||||
android:textColor="@android:color/tertiary_text_dark"
|
||||
android:text="@string/siteType_hint" />
|
||||
android:text="@string/resultType_hint" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
<string name="masterPassword_hint">Your master password</string>
|
||||
<string name="siteName_hint">eg. google.com</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="siteVersion_hint">Algorithm</string>
|
||||
<string name="empty" />
|
||||
|
@ -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>@&%?,=[]_:-+*$#!'^~;()/.</string>
|
||||
<key>x</key>
|
||||
<string>AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()</string>
|
||||
<key> </key>
|
||||
<string> </string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
@ -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'
|
||||
}
|
@ -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>
|
@ -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 ) );
|
||||
}
|
||||
}
|
@ -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;
|
@ -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
@ -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]"
|
@ -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 "$@"
|
@ -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
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package com.lyndir.masterpassword.gui.model;
|
||||
|
||||
import com.google.common.primitives.UnsignedInteger;
|
||||
import com.lyndir.masterpassword.MPSiteType;
|
||||
import com.lyndir.masterpassword.MPResultType;
|
||||
import com.lyndir.masterpassword.MasterKey;
|
||||
|
||||
|
||||
@ -11,15 +11,15 @@ import com.lyndir.masterpassword.MasterKey;
|
||||
public class IncognitoSite extends Site {
|
||||
|
||||
private String siteName;
|
||||
private MPSiteType siteType;
|
||||
private UnsignedInteger siteCounter;
|
||||
private MPResultType resultType;
|
||||
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) {
|
||||
this.siteName = siteName;
|
||||
this.siteType = siteType;
|
||||
this.siteCounter = siteCounter;
|
||||
this.resultType = resultType;
|
||||
this.algorithmVersion = algorithmVersion;
|
||||
}
|
||||
|
||||
@ -34,13 +34,13 @@ public class IncognitoSite extends Site {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MPSiteType getSiteType() {
|
||||
return siteType;
|
||||
public MPResultType getResultType() {
|
||||
return resultType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSiteType(final MPSiteType siteType) {
|
||||
this.siteType = siteType;
|
||||
public void setResultType(final MPResultType resultType) {
|
||||
this.resultType = resultType;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,7 +1,7 @@
|
||||
package com.lyndir.masterpassword.gui.model;
|
||||
|
||||
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.model.*;
|
||||
|
||||
@ -33,14 +33,14 @@ public class ModelSite extends Site {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MPSiteType getSiteType() {
|
||||
return model.getSiteType();
|
||||
public MPResultType getResultType() {
|
||||
return model.getResultType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSiteType(final MPSiteType siteType) {
|
||||
if (siteType != getSiteType()) {
|
||||
model.setSiteType( siteType );
|
||||
public void setResultType(final MPResultType resultType) {
|
||||
if (resultType != getResultType()) {
|
||||
model.setResultType( resultType );
|
||||
MPUserFileManager.get().save();
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ public class ModelUser extends User {
|
||||
|
||||
@Override
|
||||
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();
|
||||
MPUserFileManager.get().save();
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ package com.lyndir.masterpassword.gui.model;
|
||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
|
||||
|
||||
import com.google.common.primitives.UnsignedInteger;
|
||||
import com.lyndir.masterpassword.MPSiteType;
|
||||
import com.lyndir.masterpassword.MPResultType;
|
||||
import com.lyndir.masterpassword.MasterKey;
|
||||
|
||||
|
||||
@ -16,9 +16,9 @@ public abstract class Site {
|
||||
|
||||
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();
|
||||
|
||||
|
@ -32,10 +32,10 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
||||
private final Components.GradientPanel root;
|
||||
private final JTextField siteNameField;
|
||||
private final JButton siteActionButton;
|
||||
private final JComboBox<MPSiteType> siteTypeField;
|
||||
private final JComboBox<MasterKey.Version> siteVersionField;
|
||||
private final JSpinner siteCounterField;
|
||||
private final UnsignedIntegerModel siteCounterModel;
|
||||
private final JComboBox<MPResultType> resultTypeField;
|
||||
private final JPasswordField passwordField;
|
||||
private final JLabel tipLabel;
|
||||
private final JCheckBox maskPasswordField;
|
||||
@ -118,17 +118,17 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
||||
|
||||
// Site Type & Counter
|
||||
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, //
|
||||
siteTypeField = Components.comboBox( types ), //
|
||||
resultTypeField = Components.comboBox( types ), //
|
||||
Components.stud(), //
|
||||
siteVersionField = Components.comboBox( MasterKey.Version.values() ), //
|
||||
Components.stud(), //
|
||||
siteCounterField = Components.spinner( siteCounterModel ) );
|
||||
sitePanel.add( siteSettings );
|
||||
siteTypeField.setFont( Res.valueFont().deriveFont( 12f ) );
|
||||
siteTypeField.setSelectedItem( MPSiteType.GeneratedLong );
|
||||
siteTypeField.addItemListener( new ItemListener() {
|
||||
resultTypeField.setFont( Res.valueFont().deriveFont( 12f ) );
|
||||
resultTypeField.setSelectedItem( MPResultType.GeneratedLong );
|
||||
resultTypeField.addItemListener( new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(final ItemEvent e) {
|
||||
updatePassword( true );
|
||||
@ -215,7 +215,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
||||
return Futures.immediateCancelledFuture();
|
||||
}
|
||||
|
||||
MPSiteType siteType = siteTypeField.getModel().getElementAt( siteTypeField.getSelectedIndex() );
|
||||
MPResultType resultType = resultTypeField.getModel().getElementAt( resultTypeField.getSelectedIndex() );
|
||||
MasterKey.Version siteVersion = siteVersionField.getItemAt( siteVersionField.getSelectedIndex() );
|
||||
UnsignedInteger siteCounter = siteCounterModel.getNumber();
|
||||
|
||||
@ -228,9 +228,9 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
||||
}
|
||||
} );
|
||||
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() )) {
|
||||
site.setSiteType( siteType );
|
||||
site.setResultType( resultType );
|
||||
site.setAlgorithmVersion( siteVersion );
|
||||
site.setSiteCounter( siteCounter );
|
||||
}
|
||||
@ -240,7 +240,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
||||
public String call()
|
||||
throws Exception {
|
||||
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>() {
|
||||
@ -256,7 +256,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
||||
siteActionButton.setText( "Delete Site" );
|
||||
else
|
||||
siteActionButton.setText( "Add Site" );
|
||||
siteTypeField.setSelectedItem( currentSite.getSiteType() );
|
||||
resultTypeField.setSelectedItem( currentSite.getResultType() );
|
||||
siteVersionField.setSelectedItem( currentSite.getAlgorithmVersion() );
|
||||
siteCounterField.setValue( currentSite.getSiteCounter() );
|
||||
siteNameField.setText( currentSite.getSiteName() );
|
||||
|
Loading…
Reference in New Issue
Block a user