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