Update Java to match C's internal changes.
This commit is contained in:
		@@ -50,14 +50,11 @@ const MPResultType mpw_typeWithName(const char *typeName) {
 | 
			
		||||
            return MPResultTypeDeriveKey;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Lower-case and trim optionally leading "Generated" string from typeName to standardize it.
 | 
			
		||||
    size_t stdTypeNameOffset = 0;
 | 
			
		||||
    // Lower-case typeName to standardize it.
 | 
			
		||||
    size_t stdTypeNameSize = strlen( typeName );
 | 
			
		||||
    if (strstr( typeName, "Generated" ) == typeName)
 | 
			
		||||
        stdTypeNameSize -= (stdTypeNameOffset = strlen( "Generated" ));
 | 
			
		||||
    char stdTypeName[stdTypeNameSize + 1];
 | 
			
		||||
    for (size_t c = 0; c < stdTypeNameSize; ++c)
 | 
			
		||||
        stdTypeName[c] = (char)tolower( typeName[c + stdTypeNameOffset] );
 | 
			
		||||
        stdTypeName[c] = (char)tolower( typeName[c] );
 | 
			
		||||
    stdTypeName[stdTypeNameSize] = '\0';
 | 
			
		||||
 | 
			
		||||
    // Find what password type is represented by the type name.
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ dependencies {
 | 
			
		||||
    compile (group: 'com.lyndir.lhunath.opal', name: 'opal-system', version: '1.6-p10') {
 | 
			
		||||
        exclude( module: 'joda-time' )
 | 
			
		||||
    }
 | 
			
		||||
    compile group: 'com.lyndir.lhunath.opal', name: 'opal-crypto', version: '1.6-p10'
 | 
			
		||||
 | 
			
		||||
    compile group: 'com.lambdaworks', name: 'scrypt', version: '1.4.0'
 | 
			
		||||
    compile group: 'org.jetbrains', name: 'annotations', version: '13.0'
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,11 @@
 | 
			
		||||
                </exclusion>
 | 
			
		||||
            </exclusions>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>com.lyndir.lhunath.opal</groupId>
 | 
			
		||||
            <artifactId>opal-crypto</artifactId>
 | 
			
		||||
            <version>1.6-p9</version>
 | 
			
		||||
        </dependency>
 | 
			
		||||
 | 
			
		||||
        <!-- EXTERNAL DEPENDENCIES -->
 | 
			
		||||
        <dependency>
 | 
			
		||||
 
 | 
			
		||||
@@ -32,68 +32,16 @@ public final class MPConstant {
 | 
			
		||||
 | 
			
		||||
    /* Environment */
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * mpw: default user name if one is not provided.
 | 
			
		||||
     */
 | 
			
		||||
    public static final String env_userName     = "MP_USERNAME";
 | 
			
		||||
    /**
 | 
			
		||||
     * mpw: default site type if one is not provided.
 | 
			
		||||
     *
 | 
			
		||||
     * @see MPSiteType#forOption(String)
 | 
			
		||||
     */
 | 
			
		||||
    public static final String env_siteType     = "MP_SITETYPE";
 | 
			
		||||
    /**
 | 
			
		||||
     * mpw: default site counter value if one is not provided.
 | 
			
		||||
     */
 | 
			
		||||
    public static final String env_siteCounter  = "MP_SITECOUNTER";
 | 
			
		||||
    /**
 | 
			
		||||
     * mpw: default path to look for run configuration files if the platform default is not desired.
 | 
			
		||||
     */
 | 
			
		||||
    public static final String env_rcDir        = "MP_RCDIR";
 | 
			
		||||
    public static final String env_rcDir        = "MPW_RCDIR";
 | 
			
		||||
    /**
 | 
			
		||||
     * mpw: permit automatic update checks.
 | 
			
		||||
     */
 | 
			
		||||
    public static final String env_checkUpdates = "MP_CHECKUPDATES";
 | 
			
		||||
    public static final String env_checkUpdates = "MPW_CHECKUPDATES";
 | 
			
		||||
 | 
			
		||||
    /* Algorithm */
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * scrypt: CPU cost parameter.
 | 
			
		||||
     */
 | 
			
		||||
    public static final int                          scrypt_N      = 32768;
 | 
			
		||||
    /**
 | 
			
		||||
     * scrypt: Memory cost parameter.
 | 
			
		||||
     */
 | 
			
		||||
    public static final int                          scrypt_r      = 8;
 | 
			
		||||
    /**
 | 
			
		||||
     * scrypt: Parallelization parameter.
 | 
			
		||||
     */
 | 
			
		||||
    public static final int                          scrypt_p      = 2;
 | 
			
		||||
    /**
 | 
			
		||||
     * mpw: Master key size (byte).
 | 
			
		||||
     */
 | 
			
		||||
    public static final int                          mpw_dkLen     = 64;
 | 
			
		||||
    /**
 | 
			
		||||
     * mpw: Input character encoding.
 | 
			
		||||
     */
 | 
			
		||||
    public static final Charset                      mpw_charset         = Charsets.UTF_8;
 | 
			
		||||
    /**
 | 
			
		||||
     * mpw: Platform-agnostic byte order.
 | 
			
		||||
     */
 | 
			
		||||
    public static final ByteOrder                    mpw_byteOrder       = ByteOrder.BIG_ENDIAN;
 | 
			
		||||
    /**
 | 
			
		||||
     * mpw: Site digest.
 | 
			
		||||
     */
 | 
			
		||||
    public static final MessageAuthenticationDigests mpw_digest          = MessageAuthenticationDigests.HmacSHA256;
 | 
			
		||||
    /**
 | 
			
		||||
     * mpw: Key ID hash.
 | 
			
		||||
     */
 | 
			
		||||
    public static final MessageDigests               mpw_hash            = MessageDigests.SHA256;
 | 
			
		||||
    /**
 | 
			
		||||
     * mpw: validity for the time-based rolling counter.
 | 
			
		||||
     */
 | 
			
		||||
    public static final int                          mpw_counter_timeout = 5 * 60 /* s */;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public static final int MS_PER_S = 1000;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,90 @@
 | 
			
		||||
//==============================================================================
 | 
			
		||||
// This file is part of Master Password.
 | 
			
		||||
// Copyright (c) 2011-2017, Maarten Billemont.
 | 
			
		||||
//
 | 
			
		||||
// Master Password is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// (at your option) any later version.
 | 
			
		||||
//
 | 
			
		||||
// Master Password is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You can find a copy of the GNU General Public License in the
 | 
			
		||||
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
package com.lyndir.masterpassword;
 | 
			
		||||
 | 
			
		||||
import com.google.common.collect.ImmutableList;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Locale;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import org.jetbrains.annotations.Contract;
 | 
			
		||||
import org.jetbrains.annotations.NonNls;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author lhunath, 14-12-02
 | 
			
		||||
 */
 | 
			
		||||
public enum MPKeyPurpose {
 | 
			
		||||
    Password( "authentication", "Generate a key for authentication.", "com.lyndir.masterpassword" ),
 | 
			
		||||
    Login( "identification", "Generate a name for identification.", "com.lyndir.masterpassword.login" ),
 | 
			
		||||
    Answer( "recovery", "Generate an account recovery token.", "com.lyndir.masterpassword.answer" );
 | 
			
		||||
 | 
			
		||||
    static final Logger logger = Logger.get( MPResultType.class );
 | 
			
		||||
 | 
			
		||||
    private final String shortName;
 | 
			
		||||
    private final String description;
 | 
			
		||||
    private final String scope;
 | 
			
		||||
 | 
			
		||||
    MPKeyPurpose(final String shortName, final String description, @NonNls final String scope) {
 | 
			
		||||
        this.shortName = shortName;
 | 
			
		||||
        this.description = description;
 | 
			
		||||
        this.scope = scope;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getShortName() {
 | 
			
		||||
        return shortName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getDescription() {
 | 
			
		||||
        return description;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getScope() {
 | 
			
		||||
        return scope;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param shortNamePrefix The name for the purpose to look up.  It is a case insensitive prefix of the purpose's short name.
 | 
			
		||||
     *
 | 
			
		||||
     * @return The purpose registered with the given name.
 | 
			
		||||
     */
 | 
			
		||||
    @Nullable
 | 
			
		||||
    @Contract("!null -> !null")
 | 
			
		||||
    public static MPKeyPurpose forName(@Nullable final String shortNamePrefix) {
 | 
			
		||||
 | 
			
		||||
        if (shortNamePrefix == null)
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
        for (final MPKeyPurpose type : values())
 | 
			
		||||
            if (type.getShortName().toLowerCase( Locale.ROOT ).startsWith( shortNamePrefix.toLowerCase( Locale.ROOT ) ))
 | 
			
		||||
                return type;
 | 
			
		||||
 | 
			
		||||
        throw logger.bug( "No purpose for name: %s", shortNamePrefix );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static MPKeyPurpose forInt(final int keyPurpose) {
 | 
			
		||||
 | 
			
		||||
        return values()[keyPurpose];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int toInt() {
 | 
			
		||||
 | 
			
		||||
        return ordinal();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -24,7 +24,6 @@ import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import org.jetbrains.annotations.Contract;
 | 
			
		||||
import org.jetbrains.annotations.NonNls;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -32,15 +31,13 @@ import org.jetbrains.annotations.NonNls;
 | 
			
		||||
 *
 | 
			
		||||
 * @author lhunath
 | 
			
		||||
 */
 | 
			
		||||
public enum MPSiteType {
 | 
			
		||||
public enum MPResultType {
 | 
			
		||||
 | 
			
		||||
    GeneratedMaximum( "Max", "20 characters, contains symbols.", //
 | 
			
		||||
                      ImmutableList.of( "x", "max", "maximum" ), // NON-NLS
 | 
			
		||||
    GeneratedMaximum( "Maximum", "20 characters, contains symbols.", //
 | 
			
		||||
                      ImmutableList.of( new MPTemplate( "anoxxxxxxxxxxxxxxxxx" ), new MPTemplate( "axxxxxxxxxxxxxxxxxno" ) ), //
 | 
			
		||||
                      MPSiteTypeClass.Generated, 0x0 ),
 | 
			
		||||
                      MPResultTypeClass.Generated, 0x0 ),
 | 
			
		||||
 | 
			
		||||
    GeneratedLong( "Long", "Copy-friendly, 14 characters, contains symbols.", //
 | 
			
		||||
                   ImmutableList.of( "l", "long" ), // NON-NLS
 | 
			
		||||
                   ImmutableList.of( new MPTemplate( "CvcvnoCvcvCvcv" ), new MPTemplate( "CvcvCvcvnoCvcv" ),
 | 
			
		||||
                                     new MPTemplate( "CvcvCvcvCvcvno" ), new MPTemplate( "CvccnoCvcvCvcv" ),
 | 
			
		||||
                                     new MPTemplate( "CvccCvcvnoCvcv" ), new MPTemplate( "CvccCvcvCvcvno" ),
 | 
			
		||||
@@ -52,65 +49,55 @@ public enum MPSiteType {
 | 
			
		||||
                                     new MPTemplate( "CvcvCvccnoCvcc" ), new MPTemplate( "CvcvCvccCvccno" ),
 | 
			
		||||
                                     new MPTemplate( "CvccnoCvcvCvcc" ), new MPTemplate( "CvccCvcvnoCvcc" ),
 | 
			
		||||
                                     new MPTemplate( "CvccCvcvCvccno" ) ), //
 | 
			
		||||
                   MPSiteTypeClass.Generated, 0x1 ),
 | 
			
		||||
                   MPResultTypeClass.Generated, 0x1 ),
 | 
			
		||||
 | 
			
		||||
    GeneratedMedium( "Medium", "Copy-friendly, 8 characters, contains symbols.", //
 | 
			
		||||
                     ImmutableList.of( "m", "med", "medium" ), // NON-NLS
 | 
			
		||||
                     ImmutableList.of( new MPTemplate( "CvcnoCvc" ), new MPTemplate( "CvcCvcno" ) ), //
 | 
			
		||||
                     MPSiteTypeClass.Generated, 0x2 ),
 | 
			
		||||
                     MPResultTypeClass.Generated, 0x2 ),
 | 
			
		||||
 | 
			
		||||
    GeneratedBasic( "Basic", "8 characters, no symbols.", //
 | 
			
		||||
                    ImmutableList.of( "b", "basic" ), // NON-NLS
 | 
			
		||||
                    ImmutableList.of( new MPTemplate( "aaanaaan" ), new MPTemplate( "aannaaan" ), new MPTemplate( "aaannaaa" ) ), //
 | 
			
		||||
                    MPSiteTypeClass.Generated, 0x3 ),
 | 
			
		||||
                    MPResultTypeClass.Generated, 0x3 ),
 | 
			
		||||
 | 
			
		||||
    GeneratedShort( "Short", "Copy-friendly, 4 characters, no symbols.", //
 | 
			
		||||
                    ImmutableList.of( "s", "short" ), // NON-NLS
 | 
			
		||||
                    ImmutableList.of( new MPTemplate( "Cvcn" ) ), //
 | 
			
		||||
                    MPSiteTypeClass.Generated, 0x4 ),
 | 
			
		||||
                    MPResultTypeClass.Generated, 0x4 ),
 | 
			
		||||
 | 
			
		||||
    GeneratedPIN( "PIN", "4 numbers.", //
 | 
			
		||||
                  ImmutableList.of( "i", "pin" ), // NON-NLS
 | 
			
		||||
                  ImmutableList.of( new MPTemplate( "nnnn" ) ), //
 | 
			
		||||
                  MPSiteTypeClass.Generated, 0x5 ),
 | 
			
		||||
                  MPResultTypeClass.Generated, 0x5 ),
 | 
			
		||||
 | 
			
		||||
    GeneratedName( "Name", "9 letter name.", //
 | 
			
		||||
                   ImmutableList.of( "n", "name" ), // NON-NLS
 | 
			
		||||
                   ImmutableList.of( new MPTemplate( "cvccvcvcv" ) ), //
 | 
			
		||||
                   MPSiteTypeClass.Generated, 0xE ),
 | 
			
		||||
                   MPResultTypeClass.Generated, 0xE ),
 | 
			
		||||
 | 
			
		||||
    GeneratedPhrase( "Phrase", "20 character sentence.", //
 | 
			
		||||
                     ImmutableList.of( "p", "phrase" ), // NON-NLS
 | 
			
		||||
                     ImmutableList.of( new MPTemplate( "cvcc cvc cvccvcv cvc" ), new MPTemplate( "cvc cvccvcvcv cvcv" ),
 | 
			
		||||
                                       new MPTemplate( "cv cvccv cvc cvcvccv" ) ), //
 | 
			
		||||
                     MPSiteTypeClass.Generated, 0xF ),
 | 
			
		||||
                     MPResultTypeClass.Generated, 0xF ),
 | 
			
		||||
 | 
			
		||||
    StoredPersonal( "Personal", "AES-encrypted, exportable.", //
 | 
			
		||||
                    ImmutableList.of( "personal" ), // NON-NLS
 | 
			
		||||
                    ImmutableList.<MPTemplate>of(), //
 | 
			
		||||
                    MPSiteTypeClass.Stored, 0x0, MPSiteFeature.ExportContent ),
 | 
			
		||||
                    MPResultTypeClass.Stored, 0x0, MPSiteFeature.ExportContent ),
 | 
			
		||||
 | 
			
		||||
    StoredDevicePrivate( "Device", "AES-encrypted, not exported.", //
 | 
			
		||||
                         ImmutableList.of( "device" ), // NON-NLS
 | 
			
		||||
                         ImmutableList.<MPTemplate>of(), //
 | 
			
		||||
                         MPSiteTypeClass.Stored, 0x1, MPSiteFeature.DevicePrivate );
 | 
			
		||||
                         MPResultTypeClass.Stored, 0x1, MPSiteFeature.DevicePrivate );
 | 
			
		||||
 | 
			
		||||
    static final Logger logger = Logger.get( MPSiteType.class );
 | 
			
		||||
    static final Logger logger = Logger.get( MPResultType.class );
 | 
			
		||||
 | 
			
		||||
    private final String             shortName;
 | 
			
		||||
    private final String             description;
 | 
			
		||||
    private final List<String>       options;
 | 
			
		||||
    private final List<MPTemplate>   templates;
 | 
			
		||||
    private final MPSiteTypeClass    typeClass;
 | 
			
		||||
    private final MPResultTypeClass  typeClass;
 | 
			
		||||
    private final int                typeIndex;
 | 
			
		||||
    private final Set<MPSiteFeature> typeFeatures;
 | 
			
		||||
 | 
			
		||||
    MPSiteType(final String shortName, final String description, final List<String> options, final List<MPTemplate> templates,
 | 
			
		||||
               final MPSiteTypeClass typeClass, final int typeIndex, final MPSiteFeature... typeFeatures) {
 | 
			
		||||
    MPResultType(final String shortName, final String description, final List<MPTemplate> templates,
 | 
			
		||||
                 final MPResultTypeClass typeClass, final int typeIndex, final MPSiteFeature... typeFeatures) {
 | 
			
		||||
 | 
			
		||||
        this.shortName = shortName;
 | 
			
		||||
        this.description = description;
 | 
			
		||||
        this.options = options;
 | 
			
		||||
        this.templates = templates;
 | 
			
		||||
        this.typeClass = typeClass;
 | 
			
		||||
        this.typeIndex = typeIndex;
 | 
			
		||||
@@ -131,11 +118,7 @@ public enum MPSiteType {
 | 
			
		||||
        return description;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public List<String> getOptions() {
 | 
			
		||||
        return options;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MPSiteTypeClass getTypeClass() {
 | 
			
		||||
    public MPResultTypeClass getTypeClass() {
 | 
			
		||||
 | 
			
		||||
        return typeClass;
 | 
			
		||||
    }
 | 
			
		||||
@@ -154,35 +137,22 @@ public enum MPSiteType {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param option The option to select a type with.  It is matched case insensitively.
 | 
			
		||||
     *
 | 
			
		||||
     * @return The type registered for the given option.
 | 
			
		||||
     */
 | 
			
		||||
    public static MPSiteType forOption(final String option) {
 | 
			
		||||
 | 
			
		||||
        for (final MPSiteType type : values())
 | 
			
		||||
            if (type.getOptions().contains( option.toLowerCase( Locale.ROOT ) ))
 | 
			
		||||
                return type;
 | 
			
		||||
 | 
			
		||||
        throw logger.bug( "No type for option: %s", option );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param name The name fromInt the type to look up.  It is matched case insensitively.
 | 
			
		||||
     * @param shortNamePrefix The name for the type to look up.  It is a case insensitive prefix of the type's short name.
 | 
			
		||||
     *
 | 
			
		||||
     * @return The type registered with the given name.
 | 
			
		||||
     */
 | 
			
		||||
    @Nullable
 | 
			
		||||
    @Contract("!null -> !null")
 | 
			
		||||
    public static MPSiteType forName(@Nullable final String name) {
 | 
			
		||||
    public static MPResultType forName(@Nullable final String shortNamePrefix) {
 | 
			
		||||
 | 
			
		||||
        if (name == null)
 | 
			
		||||
        if (shortNamePrefix == null)
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
        for (final MPSiteType type : values())
 | 
			
		||||
            if (type.name().equalsIgnoreCase( name ))
 | 
			
		||||
        for (final MPResultType type : values())
 | 
			
		||||
            if (type.getShortName().toLowerCase( Locale.ROOT ).startsWith( shortNamePrefix.toLowerCase( Locale.ROOT ) ))
 | 
			
		||||
                return type;
 | 
			
		||||
 | 
			
		||||
        throw logger.bug( "No type for name: %s", name );
 | 
			
		||||
        throw logger.bug( "No type for name: %s", shortNamePrefix );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -190,10 +160,10 @@ public enum MPSiteType {
 | 
			
		||||
     *
 | 
			
		||||
     * @return All types that support the given class.
 | 
			
		||||
     */
 | 
			
		||||
    public static ImmutableList<MPSiteType> forClass(final MPSiteTypeClass typeClass) {
 | 
			
		||||
    public static ImmutableList<MPResultType> forClass(final MPResultTypeClass typeClass) {
 | 
			
		||||
 | 
			
		||||
        ImmutableList.Builder<MPSiteType> types = ImmutableList.builder();
 | 
			
		||||
        for (final MPSiteType type : values())
 | 
			
		||||
        ImmutableList.Builder<MPResultType> types = ImmutableList.builder();
 | 
			
		||||
        for (final MPResultType type : values())
 | 
			
		||||
            if (type.getTypeClass() == typeClass)
 | 
			
		||||
                types.add( type );
 | 
			
		||||
 | 
			
		||||
@@ -205,11 +175,11 @@ public enum MPSiteType {
 | 
			
		||||
     *
 | 
			
		||||
     * @return The type registered with the given type.
 | 
			
		||||
     */
 | 
			
		||||
    public static MPSiteType forType(final int type) {
 | 
			
		||||
    public static MPResultType forType(final int type) {
 | 
			
		||||
 | 
			
		||||
        for (final MPSiteType siteType : values())
 | 
			
		||||
            if (siteType.getType() == type)
 | 
			
		||||
                return siteType;
 | 
			
		||||
        for (final MPResultType resultType : values())
 | 
			
		||||
            if (resultType.getType() == type)
 | 
			
		||||
                return resultType;
 | 
			
		||||
 | 
			
		||||
        throw logger.bug( "No type: %s", type );
 | 
			
		||||
    }
 | 
			
		||||
@@ -219,17 +189,27 @@ public enum MPSiteType {
 | 
			
		||||
     *
 | 
			
		||||
     * @return All types that support the given mask.
 | 
			
		||||
     */
 | 
			
		||||
    public static ImmutableList<MPSiteType> forMask(final int mask) {
 | 
			
		||||
    public static ImmutableList<MPResultType> forMask(final int mask) {
 | 
			
		||||
 | 
			
		||||
        int                                 typeMask = mask & ~0xF;
 | 
			
		||||
        ImmutableList.Builder<MPSiteType> types = ImmutableList.builder();
 | 
			
		||||
        for (final MPSiteType siteType : values())
 | 
			
		||||
            if (((siteType.getType() & ~0xF) & typeMask) != 0)
 | 
			
		||||
                types.add( siteType );
 | 
			
		||||
        ImmutableList.Builder<MPResultType> types    = ImmutableList.builder();
 | 
			
		||||
        for (final MPResultType resultType : values())
 | 
			
		||||
            if (((resultType.getType() & ~0xF) & typeMask) != 0)
 | 
			
		||||
                types.add( resultType );
 | 
			
		||||
 | 
			
		||||
        return types.build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static MPResultType forInt(final int resultType) {
 | 
			
		||||
 | 
			
		||||
        return values()[resultType];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int toInt() {
 | 
			
		||||
 | 
			
		||||
        return ordinal();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MPTemplate getTemplateAtRollingIndex(final int templateIndex) {
 | 
			
		||||
        return templates.get( templateIndex % templates.size() );
 | 
			
		||||
    }
 | 
			
		||||
@@ -23,13 +23,13 @@ package com.lyndir.masterpassword;
 | 
			
		||||
 *
 | 
			
		||||
 * @author lhunath
 | 
			
		||||
 */
 | 
			
		||||
public enum MPSiteTypeClass {
 | 
			
		||||
public enum MPResultTypeClass {
 | 
			
		||||
    Generated( 1 << 4 ),
 | 
			
		||||
    Stored( 1 << 5 );
 | 
			
		||||
 | 
			
		||||
    private final int mask;
 | 
			
		||||
 | 
			
		||||
    MPSiteTypeClass(final int mask) {
 | 
			
		||||
    MPResultTypeClass(final int mask) {
 | 
			
		||||
        this.mask = mask;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1,103 +0,0 @@
 | 
			
		||||
//==============================================================================
 | 
			
		||||
// This file is part of Master Password.
 | 
			
		||||
// Copyright (c) 2011-2017, Maarten Billemont.
 | 
			
		||||
//
 | 
			
		||||
// Master Password is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// (at your option) any later version.
 | 
			
		||||
//
 | 
			
		||||
// Master Password is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You can find a copy of the GNU General Public License in the
 | 
			
		||||
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
package com.lyndir.masterpassword;
 | 
			
		||||
 | 
			
		||||
import com.google.common.collect.ImmutableList;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Locale;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import org.jetbrains.annotations.Contract;
 | 
			
		||||
import org.jetbrains.annotations.NonNls;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author lhunath, 14-12-02
 | 
			
		||||
 */
 | 
			
		||||
public enum MPSiteVariant {
 | 
			
		||||
    Password( "Generate a key for authentication.", "Doesn't currently use a context.", //
 | 
			
		||||
              ImmutableList.of( "p", "password" ), "com.lyndir.masterpassword" ), // NON-NLS
 | 
			
		||||
    Login( "Generate a name for identification.", "Doesn't currently use a context.", //
 | 
			
		||||
           ImmutableList.of( "l", "login" ), "com.lyndir.masterpassword.login" ), // NON-NLS
 | 
			
		||||
    Answer( "Generate an answer to a security question.", "Empty for a universal site answer or\nthe most significant word(s) of the question.", //
 | 
			
		||||
            ImmutableList.of( "a", "answer" ), "com.lyndir.masterpassword.answer" ); // NON-NLS
 | 
			
		||||
 | 
			
		||||
    static final Logger logger = Logger.get( MPSiteType.class );
 | 
			
		||||
 | 
			
		||||
    private final String       description;
 | 
			
		||||
    private final String       contextDescription;
 | 
			
		||||
    private final List<String> options;
 | 
			
		||||
    private final String       scope;
 | 
			
		||||
 | 
			
		||||
    MPSiteVariant(final String description, final String contextDescription, final List<String> options, @NonNls final String scope) {
 | 
			
		||||
        this.contextDescription = contextDescription;
 | 
			
		||||
 | 
			
		||||
        this.options = options;
 | 
			
		||||
        this.description = description;
 | 
			
		||||
        this.scope = scope;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getDescription() {
 | 
			
		||||
        return description;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getContextDescription() {
 | 
			
		||||
        return contextDescription;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public List<String> getOptions() {
 | 
			
		||||
        return options;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getScope() {
 | 
			
		||||
        return scope;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param option The option to select a variant with.  It is matched case insensitively.
 | 
			
		||||
     *
 | 
			
		||||
     * @return The variant registered for the given option.
 | 
			
		||||
     */
 | 
			
		||||
    public static MPSiteVariant forOption(final String option) {
 | 
			
		||||
 | 
			
		||||
        for (final MPSiteVariant variant : values())
 | 
			
		||||
            if (variant.getOptions().contains( option.toLowerCase( Locale.ROOT ) ))
 | 
			
		||||
                return variant;
 | 
			
		||||
 | 
			
		||||
        throw logger.bug( "No variant for option: %s", option );
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * @param name The name fromInt the variant to look up.  It is matched case insensitively.
 | 
			
		||||
     *
 | 
			
		||||
     * @return The variant registered with the given name.
 | 
			
		||||
     */
 | 
			
		||||
    @Contract("!null -> !null")
 | 
			
		||||
    public static MPSiteVariant forName(@Nullable final String name) {
 | 
			
		||||
 | 
			
		||||
        if (name == null)
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
        for (final MPSiteVariant type : values())
 | 
			
		||||
            if (type.name().equalsIgnoreCase( name ))
 | 
			
		||||
                return type;
 | 
			
		||||
 | 
			
		||||
        throw logger.bug( "No variant for name: %s", name );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -86,16 +86,61 @@ public abstract class MasterKey {
 | 
			
		||||
        allowNativeByDefault = allowNative;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected MasterKey(@Nonnull final String fullName) {
 | 
			
		||||
    protected MasterKey(final String fullName) {
 | 
			
		||||
        Preconditions.checkArgument( !fullName.isEmpty() );
 | 
			
		||||
 | 
			
		||||
        this.fullName = fullName;
 | 
			
		||||
        logger.trc( "fullName: %s", fullName );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Derive the master key for a user based on their name and master password.
 | 
			
		||||
     *
 | 
			
		||||
     * @param masterPassword The user's master password.
 | 
			
		||||
     */
 | 
			
		||||
    @Nullable
 | 
			
		||||
    @SuppressWarnings("MethodCanBeVariableArityMethod")
 | 
			
		||||
    protected abstract byte[] deriveKey(char[] masterPassword);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Derive the site key for a user's site from the given master key and site parameters.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteName    A site identifier.
 | 
			
		||||
     * @param siteCounter The result identifier.
 | 
			
		||||
     * @param keyPurpose  The intended purpose for this site key.
 | 
			
		||||
     * @param keyContext  A site-scoped key modifier.
 | 
			
		||||
     */
 | 
			
		||||
    protected abstract byte[] siteKey(String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose,
 | 
			
		||||
                                      @Nullable String keyContext);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Generate a site result token.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteName    A site identifier.
 | 
			
		||||
     * @param siteCounter The result identifier.
 | 
			
		||||
     * @param keyPurpose  The intended purpose for this site result.
 | 
			
		||||
     * @param keyContext  A site-scoped result modifier.
 | 
			
		||||
     * @param resultType  The type of result to generate.
 | 
			
		||||
     * @param resultParam A parameter for the resultType.  For stateful result types, the output of
 | 
			
		||||
     *                    {@link #siteState(String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String)}.
 | 
			
		||||
     */
 | 
			
		||||
    public abstract String siteResult(String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose,
 | 
			
		||||
                                      @Nullable String keyContext, MPResultType resultType, @Nullable String resultParam);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Encrypt a stateful site token for persistence.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteName    A site identifier.
 | 
			
		||||
     * @param siteCounter The result identifier.
 | 
			
		||||
     * @param keyPurpose  The intended purpose for the site token.
 | 
			
		||||
     * @param keyContext  A site-scoped key modifier.
 | 
			
		||||
     * @param resultType  The type of result token to encrypt.
 | 
			
		||||
     * @param resultParam The result token desired from
 | 
			
		||||
     *                    {@link #siteResult(String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String)}.
 | 
			
		||||
     */
 | 
			
		||||
    public abstract String siteState(String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose,
 | 
			
		||||
                                     @Nullable String keyContext, MPResultType resultType, @Nullable String resultParam);
 | 
			
		||||
 | 
			
		||||
    public abstract Version getAlgorithmVersion();
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
@@ -125,9 +170,6 @@ public abstract class MasterKey {
 | 
			
		||||
        return idForBytes( getKey() );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public abstract String encode(@Nonnull String siteName, MPSiteType siteType, @Nonnull UnsignedInteger siteCounter,
 | 
			
		||||
                                  MPSiteVariant siteVariant, @Nullable String siteContext);
 | 
			
		||||
 | 
			
		||||
    public boolean isValid() {
 | 
			
		||||
        return masterKey != null;
 | 
			
		||||
    }
 | 
			
		||||
@@ -160,7 +202,7 @@ public abstract class MasterKey {
 | 
			
		||||
 | 
			
		||||
    protected abstract byte[] bytesForInt(int number);
 | 
			
		||||
 | 
			
		||||
    protected abstract byte[] bytesForInt(@Nonnull UnsignedInteger number);
 | 
			
		||||
    protected abstract byte[] bytesForInt(UnsignedInteger number);
 | 
			
		||||
 | 
			
		||||
    protected abstract byte[] idForBytes(byte[] bytes);
 | 
			
		||||
 | 
			
		||||
@@ -168,19 +210,19 @@ public abstract class MasterKey {
 | 
			
		||||
        /**
 | 
			
		||||
         * bugs:
 | 
			
		||||
         * - does math with chars whose signedness was platform-dependent.
 | 
			
		||||
         * - miscounted the byte-length fromInt multi-byte site names.
 | 
			
		||||
         * - miscounted the byte-length fromInt multi-byte full names.
 | 
			
		||||
         * - miscounted the byte-length for multi-byte site names.
 | 
			
		||||
         * - miscounted the byte-length for multi-byte full names.
 | 
			
		||||
         */
 | 
			
		||||
        V0,
 | 
			
		||||
        /**
 | 
			
		||||
         * bugs:
 | 
			
		||||
         * - miscounted the byte-length fromInt multi-byte site names.
 | 
			
		||||
         * - miscounted the byte-length fromInt multi-byte full names.
 | 
			
		||||
         * - miscounted the byte-length for multi-byte site names.
 | 
			
		||||
         * - miscounted the byte-length for multi-byte full names.
 | 
			
		||||
         */
 | 
			
		||||
        V1,
 | 
			
		||||
        /**
 | 
			
		||||
         * bugs:
 | 
			
		||||
         * - miscounted the byte-length fromInt multi-byte full names.
 | 
			
		||||
         * - miscounted the byte-length for multi-byte full names.
 | 
			
		||||
         */
 | 
			
		||||
        V2,
 | 
			
		||||
        /**
 | 
			
		||||
@@ -200,20 +242,5 @@ public abstract class MasterKey {
 | 
			
		||||
 | 
			
		||||
            return ordinal();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public String toBundleVersion() {
 | 
			
		||||
            switch (this) {
 | 
			
		||||
                case V0:
 | 
			
		||||
                    return "1.0";
 | 
			
		||||
                case V1:
 | 
			
		||||
                    return "2.0";
 | 
			
		||||
                case V2:
 | 
			
		||||
                    return "2.1";
 | 
			
		||||
                case V3:
 | 
			
		||||
                    return "2.2";
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            throw new UnsupportedOperationException( strf( "Unsupported version: %s", this ) );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -18,29 +18,67 @@
 | 
			
		||||
 | 
			
		||||
package com.lyndir.masterpassword;
 | 
			
		||||
 | 
			
		||||
import com.google.common.base.Preconditions;
 | 
			
		||||
import com.google.common.base.*;
 | 
			
		||||
import com.google.common.primitives.Bytes;
 | 
			
		||||
import com.google.common.primitives.UnsignedInteger;
 | 
			
		||||
import com.lambdaworks.crypto.SCrypt;
 | 
			
		||||
import com.lyndir.lhunath.opal.crypto.CryptUtils;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.*;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
			
		||||
import java.nio.*;
 | 
			
		||||
import java.nio.charset.Charset;
 | 
			
		||||
import java.security.GeneralSecurityException;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import javax.crypto.IllegalBlockSizeException;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * bugs:
 | 
			
		||||
 * - V2: miscounted the byte-length fromInt multi-byte full names.
 | 
			
		||||
 * - V1: miscounted the byte-length fromInt multi-byte site names.
 | 
			
		||||
 * - V2: miscounted the byte-length for multi-byte full names.
 | 
			
		||||
 * - V1: miscounted the byte-length for multi-byte site names.
 | 
			
		||||
 * - V0: does math with chars whose signedness was platform-dependent.
 | 
			
		||||
 *
 | 
			
		||||
 * @author lhunath, 2014-08-30
 | 
			
		||||
 */
 | 
			
		||||
public class MasterKeyV0 extends MasterKey {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * mpw: validity for the time-based rolling counter.
 | 
			
		||||
     */
 | 
			
		||||
    protected static final int                          mpw_otp_window = 5 * 60 /* s */;
 | 
			
		||||
    /**
 | 
			
		||||
     * mpw: Key ID hash.
 | 
			
		||||
     */
 | 
			
		||||
    protected static final MessageDigests               mpw_hash       = MessageDigests.SHA256;
 | 
			
		||||
    /**
 | 
			
		||||
     * mpw: Site digest.
 | 
			
		||||
     */
 | 
			
		||||
    protected static final MessageAuthenticationDigests mpw_digest     = MessageAuthenticationDigests.HmacSHA256;
 | 
			
		||||
    /**
 | 
			
		||||
     * mpw: Platform-agnostic byte order.
 | 
			
		||||
     */
 | 
			
		||||
    protected static final ByteOrder                    mpw_byteOrder  = ByteOrder.BIG_ENDIAN;
 | 
			
		||||
    /**
 | 
			
		||||
     * mpw: Input character encoding.
 | 
			
		||||
     */
 | 
			
		||||
    protected static final Charset                      mpw_charset    = Charsets.UTF_8;
 | 
			
		||||
    /**
 | 
			
		||||
     * mpw: Master key size (byte).
 | 
			
		||||
     */
 | 
			
		||||
    protected static final int                          mpw_dkLen      = 64;
 | 
			
		||||
    /**
 | 
			
		||||
     * scrypt: Parallelization parameter.
 | 
			
		||||
     */
 | 
			
		||||
    protected static final int                          scrypt_p       = 2;
 | 
			
		||||
    /**
 | 
			
		||||
     * scrypt: Memory cost parameter.
 | 
			
		||||
     */
 | 
			
		||||
    protected static final int                          scrypt_r       = 8;
 | 
			
		||||
    /**
 | 
			
		||||
     * scrypt: CPU cost parameter.
 | 
			
		||||
     */
 | 
			
		||||
    protected static final int                          scrypt_N       = 32768;
 | 
			
		||||
    private static final   int                          MP_intLen      = 32;
 | 
			
		||||
 | 
			
		||||
    @SuppressWarnings("UnusedDeclaration")
 | 
			
		||||
@@ -59,112 +97,171 @@ public class MasterKeyV0 extends MasterKey {
 | 
			
		||||
    @Nullable
 | 
			
		||||
    @Override
 | 
			
		||||
    protected byte[] deriveKey(final char[] masterPassword) {
 | 
			
		||||
        Preconditions.checkArgument( masterPassword.length > 0 );
 | 
			
		||||
 | 
			
		||||
        String fullName = getFullName();
 | 
			
		||||
        byte[] fullNameBytes = fullName.getBytes( MPConstant.mpw_charset );
 | 
			
		||||
        byte[] fullNameBytes = fullName.getBytes( mpw_charset );
 | 
			
		||||
        byte[] fullNameLengthBytes = bytesForInt( fullName.length() );
 | 
			
		||||
        ByteBuffer mpBytesBuf = mpw_charset.encode( CharBuffer.wrap( masterPassword ) );
 | 
			
		||||
 | 
			
		||||
        String mpKeyScope = MPSiteVariant.Password.getScope();
 | 
			
		||||
        byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MPConstant.mpw_charset ), fullNameLengthBytes, fullNameBytes );
 | 
			
		||||
        logger.trc( "key scope: %s", mpKeyScope );
 | 
			
		||||
        logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
 | 
			
		||||
        logger.trc( "-- mpw_masterKey (algorithm: %u)", getAlgorithmVersion().toInt() );
 | 
			
		||||
        logger.trc( "fullName: %s", fullName );
 | 
			
		||||
        logger.trc( "masterPassword.id: %s", (Object) idForBytes( mpBytesBuf.array() ) );
 | 
			
		||||
 | 
			
		||||
        ByteBuffer mpBytesBuf = MPConstant.mpw_charset.encode( CharBuffer.wrap( masterPassword ) );
 | 
			
		||||
        String keyScope = MPKeyPurpose.Password.getScope();
 | 
			
		||||
        logger.trc( "keyScope: %s", keyScope );
 | 
			
		||||
 | 
			
		||||
        // Calculate the master key salt.
 | 
			
		||||
        logger.trc( "masterKeySalt: keyScope=%s | #fullName=%s | fullName=%s",
 | 
			
		||||
             keyScope, CodeUtils.encodeHex( fullNameLengthBytes ), fullName );
 | 
			
		||||
        byte[] masterKeySalt = Bytes.concat( keyScope.getBytes( mpw_charset ), fullNameLengthBytes, fullNameBytes );
 | 
			
		||||
        logger.trc( "  => masterKeySalt.id: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
 | 
			
		||||
 | 
			
		||||
        // Calculate the master key.
 | 
			
		||||
        logger.trc( "masterKey: scrypt( masterPassword, masterKeySalt, N=%lu, r=%u, p=%u )",
 | 
			
		||||
                    scrypt_N, scrypt_r, scrypt_p );
 | 
			
		||||
        byte[] mpBytes = new byte[mpBytesBuf.remaining()];
 | 
			
		||||
        mpBytesBuf.get( mpBytes, 0, mpBytes.length );
 | 
			
		||||
        Arrays.fill( mpBytesBuf.array(), (byte) 0 );
 | 
			
		||||
        byte[] masterKey = scrypt( masterKeySalt, mpBytes ); // TODO: Why not mpBytesBuf.array()?
 | 
			
		||||
        Arrays.fill( masterKeySalt, (byte) 0 );
 | 
			
		||||
        Arrays.fill( mpBytes, (byte) 0 );
 | 
			
		||||
        logger.trc( "  => masterKey.id: %s", (masterKey == null)? null: (Object) idForBytes( masterKey ) );
 | 
			
		||||
 | 
			
		||||
        return scrypt( masterKeySalt, mpBytes );
 | 
			
		||||
        return masterKey;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    protected byte[] scrypt(final byte[] masterKeySalt, final byte[] mpBytes) {
 | 
			
		||||
        try {
 | 
			
		||||
            if (isAllowNative())
 | 
			
		||||
                return SCrypt.scrypt( mpBytes, masterKeySalt, MPConstant.scrypt_N, MPConstant.scrypt_r, MPConstant.scrypt_p, MPConstant.mpw_dkLen );
 | 
			
		||||
                return SCrypt.scrypt( mpBytes, masterKeySalt, scrypt_N, scrypt_r, scrypt_p, mpw_dkLen );
 | 
			
		||||
            else
 | 
			
		||||
                return SCrypt.scryptJ( mpBytes, masterKeySalt, MPConstant.scrypt_N, MPConstant.scrypt_r, MPConstant.scrypt_p, MPConstant.mpw_dkLen );
 | 
			
		||||
                return SCrypt.scryptJ( mpBytes, masterKeySalt, scrypt_N, scrypt_r, scrypt_p, mpw_dkLen );
 | 
			
		||||
        }
 | 
			
		||||
        catch (final GeneralSecurityException e) {
 | 
			
		||||
            logger.bug( e );
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        finally {
 | 
			
		||||
            Arrays.fill( mpBytes, (byte) 0 );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String encode(@Nonnull final String siteName, final MPSiteType siteType, @Nonnull UnsignedInteger siteCounter,
 | 
			
		||||
                         final MPSiteVariant siteVariant, @Nullable final String siteContext) {
 | 
			
		||||
        Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
 | 
			
		||||
    protected byte[] siteKey(final String siteName, UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
 | 
			
		||||
                             @Nullable final String keyContext) {
 | 
			
		||||
        Preconditions.checkArgument( !siteName.isEmpty() );
 | 
			
		||||
 | 
			
		||||
        logger.trc( "-- mpw_siteKey (algorithm: %u)", getAlgorithmVersion().toInt() );
 | 
			
		||||
        logger.trc( "siteName: %s", siteName );
 | 
			
		||||
        logger.trc( "siteCounter: %d", siteCounter.longValue() );
 | 
			
		||||
        logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant );
 | 
			
		||||
        logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType );
 | 
			
		||||
        logger.trc( "siteCounter: %d", siteCounter );
 | 
			
		||||
        logger.trc( "keyPurpose: %d (%s)", keyPurpose.toInt(), keyPurpose.getShortName() );
 | 
			
		||||
        logger.trc( "keyContext: %s", keyContext );
 | 
			
		||||
 | 
			
		||||
        String keyScope = keyPurpose.getScope();
 | 
			
		||||
        logger.trc( "keyScope: %s", keyScope );
 | 
			
		||||
 | 
			
		||||
        // OTP counter value.
 | 
			
		||||
        if (siteCounter.longValue() == 0)
 | 
			
		||||
            siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (MPConstant.mpw_counter_timeout * 1000)) * MPConstant.mpw_counter_timeout );
 | 
			
		||||
            siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (mpw_otp_window * 1000)) * mpw_otp_window );
 | 
			
		||||
 | 
			
		||||
        String siteScope = siteVariant.getScope();
 | 
			
		||||
        byte[] siteNameBytes = siteName.getBytes( MPConstant.mpw_charset );
 | 
			
		||||
        // Calculate the site seed.
 | 
			
		||||
        byte[] siteNameBytes = siteName.getBytes( mpw_charset );
 | 
			
		||||
        byte[] siteNameLengthBytes = bytesForInt( siteName.length() );
 | 
			
		||||
        byte[] siteCounterBytes = bytesForInt( siteCounter );
 | 
			
		||||
        byte[] siteContextBytes = ((siteContext == null) || siteContext.isEmpty())? null: siteContext.getBytes( MPConstant.mpw_charset );
 | 
			
		||||
        byte[] siteContextLengthBytes = bytesForInt( (siteContextBytes == null)? 0: siteContextBytes.length );
 | 
			
		||||
        logger.trc( "site scope: %s, context: %s", siteScope, (siteContextBytes == null)? "<empty>": siteContext );
 | 
			
		||||
        logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
 | 
			
		||||
                    siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
 | 
			
		||||
                    (siteContextBytes == null)? "(null)": siteContext );
 | 
			
		||||
        byte[] keyContextBytes = ((keyContext == null) || keyContext.isEmpty())? null: keyContext.getBytes( mpw_charset );
 | 
			
		||||
        byte[] keyContextLengthBytes = (keyContextBytes == null)? null: bytesForInt( keyContextBytes.length );
 | 
			
		||||
        logger.trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s",
 | 
			
		||||
                    keyScope, CodeUtils.encodeHex( siteNameLengthBytes ), siteName, CodeUtils.encodeHex( siteCounterBytes ),
 | 
			
		||||
                    (keyContextLengthBytes == null)? null: CodeUtils.encodeHex( keyContextLengthBytes ), keyContext );
 | 
			
		||||
 | 
			
		||||
        byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MPConstant.mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
 | 
			
		||||
        if (siteContextBytes != null)
 | 
			
		||||
            sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
 | 
			
		||||
        logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
 | 
			
		||||
        byte[] sitePasswordInfo = Bytes.concat( keyScope.getBytes( mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
 | 
			
		||||
        if (keyContextBytes != null)
 | 
			
		||||
            sitePasswordInfo = Bytes.concat( sitePasswordInfo, keyContextLengthBytes, keyContextBytes );
 | 
			
		||||
        logger.trc( "  => siteSalt.id: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
 | 
			
		||||
 | 
			
		||||
        byte[] sitePasswordSeedBytes = MPConstant.mpw_digest.of( getKey(), sitePasswordInfo );
 | 
			
		||||
        int[] sitePasswordSeed = new int[sitePasswordSeedBytes.length];
 | 
			
		||||
        for (int i = 0; i < sitePasswordSeedBytes.length; ++i) {
 | 
			
		||||
        byte[] masterKey = getKey();
 | 
			
		||||
        logger.trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )", (Object) idForBytes( masterKey ) );
 | 
			
		||||
        byte[] sitePasswordSeedBytes = mpw_digest.of( masterKey, sitePasswordInfo );
 | 
			
		||||
        logger.trc( "  => siteKey.id: %s", (Object) idForBytes( sitePasswordSeedBytes ) );
 | 
			
		||||
 | 
			
		||||
        return sitePasswordSeedBytes;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String siteResult(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
 | 
			
		||||
                             @Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam) {
 | 
			
		||||
 | 
			
		||||
        byte[] siteKey = siteKey( siteName, siteCounter, keyPurpose, keyContext );
 | 
			
		||||
        int[] sitePasswordSeed = new int[siteKey.length];
 | 
			
		||||
        for (int i = 0; i < siteKey.length; ++i) {
 | 
			
		||||
            ByteBuffer buf = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( ByteOrder.BIG_ENDIAN );
 | 
			
		||||
            Arrays.fill( buf.array(), (byte) ((sitePasswordSeedBytes[i] > 0)? 0x00: 0xFF) );
 | 
			
		||||
            Arrays.fill( buf.array(), (byte) ((siteKey[i] > 0)? 0x00: 0xFF) );
 | 
			
		||||
            buf.position( 2 );
 | 
			
		||||
            buf.put( sitePasswordSeedBytes[i] ).rewind();
 | 
			
		||||
            buf.put( siteKey[i] ).rewind();
 | 
			
		||||
            sitePasswordSeed[i] = buf.getInt() & 0xFFFF;
 | 
			
		||||
        }
 | 
			
		||||
        logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeedBytes ) ) );
 | 
			
		||||
 | 
			
		||||
        logger.trc( "-- mpw_siteResult (algorithm: %u)", getAlgorithmVersion().toInt() );
 | 
			
		||||
        logger.trc( "resultType: %d (%s)", resultType.toInt(), resultType.getShortName() );
 | 
			
		||||
        logger.trc( "resultParam: %s", resultParam );
 | 
			
		||||
 | 
			
		||||
        // Determine the template.
 | 
			
		||||
        Preconditions.checkState( sitePasswordSeed.length > 0 );
 | 
			
		||||
        int templateIndex = sitePasswordSeed[0];
 | 
			
		||||
        MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex );
 | 
			
		||||
        logger.trc( "type %s, template: %s", siteType, template.getTemplateString() );
 | 
			
		||||
        MPTemplate template = resultType.getTemplateAtRollingIndex( templateIndex );
 | 
			
		||||
        logger.trc( "template: %u => %s", templateIndex, template.getTemplateString() );
 | 
			
		||||
 | 
			
		||||
        // Encode the password from the seed using the template.
 | 
			
		||||
        StringBuilder password = new StringBuilder( template.length() );
 | 
			
		||||
        for (int i = 0; i < template.length(); ++i) {
 | 
			
		||||
            int characterIndex = sitePasswordSeed[i + 1];
 | 
			
		||||
            MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
 | 
			
		||||
            char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
 | 
			
		||||
            logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex,
 | 
			
		||||
                        sitePasswordSeed[i + 1], passwordCharacter );
 | 
			
		||||
            logger.trc( "  - class: %c, index: %5u (0x%02hX) => character: %c",
 | 
			
		||||
                        characterClass.getIdentifier(), characterIndex, sitePasswordSeed[i + 1], passwordCharacter );
 | 
			
		||||
 | 
			
		||||
            password.append( passwordCharacter );
 | 
			
		||||
        }
 | 
			
		||||
        logger.trc( "  => password: %s", password );
 | 
			
		||||
 | 
			
		||||
        return password.toString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected byte[] bytesForInt(final int number) {
 | 
			
		||||
        return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MPConstant.mpw_byteOrder ).putInt( number ).array();
 | 
			
		||||
    public String siteState(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
 | 
			
		||||
                            @Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam) {
 | 
			
		||||
 | 
			
		||||
        Preconditions.checkNotNull( resultParam );
 | 
			
		||||
        Preconditions.checkArgument( !resultParam.isEmpty() );
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            // Encrypt
 | 
			
		||||
            ByteBuffer plainText = mpw_charset.encode( CharBuffer.wrap( resultParam ) );
 | 
			
		||||
            byte[] cipherBuf = CryptUtils.encrypt( plainText.array(), getKey(), true );
 | 
			
		||||
            logger.trc( "cipherBuf: %zu bytes = %s", cipherBuf.length, CodeUtils.encodeHex( cipherBuf ) );
 | 
			
		||||
 | 
			
		||||
            // Base64-encode
 | 
			
		||||
            String cipherText = Verify.verifyNotNull( CryptUtils.encodeBase64( cipherBuf ) );
 | 
			
		||||
            logger.trc( "b64 encoded -> cipherText: %s", cipherText );
 | 
			
		||||
 | 
			
		||||
            return cipherText;
 | 
			
		||||
        }
 | 
			
		||||
        catch (final IllegalBlockSizeException e) {
 | 
			
		||||
            throw logger.bug( e );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected byte[] bytesForInt(@Nonnull final UnsignedInteger number) {
 | 
			
		||||
        return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MPConstant.mpw_byteOrder ).putInt( number.intValue() ).array();
 | 
			
		||||
    protected byte[] bytesForInt(final int number) {
 | 
			
		||||
        return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( mpw_byteOrder ).putInt( number ).array();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected byte[] bytesForInt(final UnsignedInteger number) {
 | 
			
		||||
        return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( mpw_byteOrder ).putInt( number.intValue() ).array();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected byte[] idForBytes(final byte[] bytes) {
 | 
			
		||||
        return MPConstant.mpw_hash.of( bytes );
 | 
			
		||||
        return mpw_hash.of( bytes );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -23,14 +23,13 @@ import com.google.common.primitives.Bytes;
 | 
			
		||||
import com.google.common.primitives.UnsignedInteger;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.*;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * bugs:
 | 
			
		||||
 * - V2: miscounted the byte-length fromInt multi-byte full names.
 | 
			
		||||
 * - V1: miscounted the byte-length fromInt multi-byte site names.
 | 
			
		||||
 * - V2: miscounted the byte-length for multi-byte full names.
 | 
			
		||||
 * - V1: miscounted the byte-length for multi-byte site names.
 | 
			
		||||
 *
 | 
			
		||||
 * @author lhunath, 2014-08-30
 | 
			
		||||
 */
 | 
			
		||||
@@ -50,53 +49,33 @@ public class MasterKeyV1 extends MasterKeyV0 {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String encode(@Nonnull final String siteName, final MPSiteType siteType, @Nonnull UnsignedInteger siteCounter,
 | 
			
		||||
                         final MPSiteVariant siteVariant, @Nullable final String siteContext) {
 | 
			
		||||
        Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
 | 
			
		||||
        Preconditions.checkArgument( !siteName.isEmpty() );
 | 
			
		||||
    public String siteResult(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
 | 
			
		||||
                             @Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam) {
 | 
			
		||||
 | 
			
		||||
        logger.trc( "siteName: %s", siteName );
 | 
			
		||||
        logger.trc( "siteCounter: %d", siteCounter.longValue() );
 | 
			
		||||
        logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant );
 | 
			
		||||
        logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType );
 | 
			
		||||
        byte[] sitePasswordSeed = siteKey( siteName, siteCounter, keyPurpose, keyContext );
 | 
			
		||||
 | 
			
		||||
        if (siteCounter.longValue() == 0)
 | 
			
		||||
            siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (MPConstant.mpw_counter_timeout * 1000)) * MPConstant.mpw_counter_timeout );
 | 
			
		||||
 | 
			
		||||
        String siteScope = siteVariant.getScope();
 | 
			
		||||
        byte[] siteNameBytes = siteName.getBytes( MPConstant.mpw_charset );
 | 
			
		||||
        byte[] siteNameLengthBytes = bytesForInt( siteName.length() );
 | 
			
		||||
        byte[] siteCounterBytes = bytesForInt( siteCounter );
 | 
			
		||||
        byte[] siteContextBytes = ((siteContext == null) || siteContext.isEmpty())? null: siteContext.getBytes( MPConstant.mpw_charset );
 | 
			
		||||
        byte[] siteContextLengthBytes = bytesForInt( (siteContextBytes == null)? 0: siteContextBytes.length );
 | 
			
		||||
        logger.trc( "site scope: %s, context: %s", siteScope, (siteContextBytes == null)? "<empty>": siteContext );
 | 
			
		||||
        logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
 | 
			
		||||
                    siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
 | 
			
		||||
                    (siteContextBytes == null)? "(null)": siteContext );
 | 
			
		||||
 | 
			
		||||
        byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MPConstant.mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
 | 
			
		||||
        if (siteContextBytes != null)
 | 
			
		||||
            sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
 | 
			
		||||
        logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
 | 
			
		||||
 | 
			
		||||
        byte[] sitePasswordSeed = MPConstant.mpw_digest.of( getKey(), sitePasswordInfo );
 | 
			
		||||
        logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) );
 | 
			
		||||
        logger.trc( "-- mpw_siteResult (algorithm: %u)", getAlgorithmVersion().toInt() );
 | 
			
		||||
        logger.trc( "resultType: %d (%s)", resultType.toInt(), resultType.getShortName() );
 | 
			
		||||
        logger.trc( "resultParam: %s", resultParam );
 | 
			
		||||
 | 
			
		||||
        // Determine the template.
 | 
			
		||||
        Preconditions.checkState( sitePasswordSeed.length > 0 );
 | 
			
		||||
        int templateIndex = sitePasswordSeed[0] & 0xFF; // Mask the integer's sign.
 | 
			
		||||
        MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex );
 | 
			
		||||
        logger.trc( "type %s, template: %s", siteType, template.getTemplateString() );
 | 
			
		||||
        MPTemplate template = resultType.getTemplateAtRollingIndex( templateIndex );
 | 
			
		||||
        logger.trc( "template: %u => %s", templateIndex, template.getTemplateString() );
 | 
			
		||||
 | 
			
		||||
        // Encode the password from the seed using the template.
 | 
			
		||||
        StringBuilder password = new StringBuilder( template.length() );
 | 
			
		||||
        for (int i = 0; i < template.length(); ++i) {
 | 
			
		||||
            int characterIndex = sitePasswordSeed[i + 1] & 0xFF; // Mask the integer's sign.
 | 
			
		||||
            MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
 | 
			
		||||
            char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
 | 
			
		||||
            logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex,
 | 
			
		||||
                        sitePasswordSeed[i + 1], passwordCharacter );
 | 
			
		||||
            logger.trc( "  - class: %c, index: %3u (0x%02hhX) => character: %c",
 | 
			
		||||
                        characterClass.getIdentifier(), characterIndex, sitePasswordSeed[i + 1], passwordCharacter );
 | 
			
		||||
 | 
			
		||||
            password.append( passwordCharacter );
 | 
			
		||||
        }
 | 
			
		||||
        logger.trc( "  => password: %s", password );
 | 
			
		||||
 | 
			
		||||
        return password.toString();
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -23,13 +23,12 @@ import com.google.common.primitives.Bytes;
 | 
			
		||||
import com.google.common.primitives.UnsignedInteger;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.CodeUtils;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * bugs:
 | 
			
		||||
 * - V2: miscounted the byte-length fromInt multi-byte full names.
 | 
			
		||||
 * - V2: miscounted the byte-length for multi-byte full names.
 | 
			
		||||
 * 
 | 
			
		||||
 * @author lhunath, 2014-08-30
 | 
			
		||||
 */
 | 
			
		||||
@@ -49,54 +48,43 @@ public class MasterKeyV2 extends MasterKeyV1 {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String encode(@Nonnull final String siteName, final MPSiteType siteType, @Nonnull UnsignedInteger siteCounter,
 | 
			
		||||
                         final MPSiteVariant siteVariant, @Nullable final String siteContext) {
 | 
			
		||||
        Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
 | 
			
		||||
    protected byte[] siteKey(final String siteName, UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
 | 
			
		||||
                             @Nullable final String keyContext) {
 | 
			
		||||
        Preconditions.checkArgument( !siteName.isEmpty() );
 | 
			
		||||
 | 
			
		||||
        logger.trc( "-- mpw_siteKey (algorithm: %u)", getAlgorithmVersion().toInt() );
 | 
			
		||||
        logger.trc( "siteName: %s", siteName );
 | 
			
		||||
        logger.trc( "siteCounter: %d", siteCounter.longValue() );
 | 
			
		||||
        logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant );
 | 
			
		||||
        logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType );
 | 
			
		||||
        logger.trc( "siteCounter: %d", siteCounter );
 | 
			
		||||
        logger.trc( "keyPurpose: %d (%s)", keyPurpose.toInt(), keyPurpose.getShortName() );
 | 
			
		||||
        logger.trc( "keyContext: %s", keyContext );
 | 
			
		||||
 | 
			
		||||
        String keyScope = keyPurpose.getScope();
 | 
			
		||||
        logger.trc( "keyScope: %s", keyScope );
 | 
			
		||||
 | 
			
		||||
        // OTP counter value.
 | 
			
		||||
        if (siteCounter.longValue() == 0)
 | 
			
		||||
            siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (MPConstant.mpw_counter_timeout * 1000)) * MPConstant.mpw_counter_timeout );
 | 
			
		||||
            siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (MasterKeyV0.mpw_otp_window * 1000)) * MasterKeyV0.mpw_otp_window );
 | 
			
		||||
 | 
			
		||||
        String siteScope = siteVariant.getScope();
 | 
			
		||||
        byte[] siteNameBytes = siteName.getBytes( MPConstant.mpw_charset );
 | 
			
		||||
        // Calculate the site seed.
 | 
			
		||||
        byte[] siteNameBytes = siteName.getBytes( MasterKeyV0.mpw_charset );
 | 
			
		||||
        byte[] siteNameLengthBytes = bytesForInt( siteNameBytes.length );
 | 
			
		||||
        byte[] siteCounterBytes = bytesForInt( siteCounter );
 | 
			
		||||
        byte[] siteContextBytes = ((siteContext == null) || siteContext.isEmpty())? null: siteContext.getBytes( MPConstant.mpw_charset );
 | 
			
		||||
        byte[] siteContextLengthBytes = bytesForInt( (siteContextBytes == null)? 0: siteContextBytes.length );
 | 
			
		||||
        logger.trc( "site scope: %s, context: %s", siteScope, (siteContextBytes == null)? "<empty>": siteContext );
 | 
			
		||||
        logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
 | 
			
		||||
                    siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
 | 
			
		||||
                    (siteContextBytes == null)? "(null)": siteContext );
 | 
			
		||||
        byte[] keyContextBytes = ((keyContext == null) || keyContext.isEmpty())? null: keyContext.getBytes( MasterKeyV0.mpw_charset );
 | 
			
		||||
        byte[] keyContextLengthBytes = (keyContextBytes == null)? null: bytesForInt( keyContextBytes.length );
 | 
			
		||||
        logger.trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s",
 | 
			
		||||
                    keyScope, CodeUtils.encodeHex( siteNameLengthBytes ), siteName, CodeUtils.encodeHex( siteCounterBytes ),
 | 
			
		||||
                    (keyContextLengthBytes == null)? null: CodeUtils.encodeHex( keyContextLengthBytes ), keyContext );
 | 
			
		||||
 | 
			
		||||
        byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MPConstant.mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
 | 
			
		||||
        if (siteContextBytes != null)
 | 
			
		||||
            sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
 | 
			
		||||
        logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
 | 
			
		||||
        byte[] sitePasswordInfo = Bytes.concat( keyScope.getBytes( MasterKeyV0.mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
 | 
			
		||||
        if (keyContextBytes != null)
 | 
			
		||||
            sitePasswordInfo = Bytes.concat( sitePasswordInfo, keyContextLengthBytes, keyContextBytes );
 | 
			
		||||
        logger.trc( "  => siteSalt.id: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
 | 
			
		||||
 | 
			
		||||
        byte[] sitePasswordSeed = MPConstant.mpw_digest.of( getKey(), sitePasswordInfo );
 | 
			
		||||
        logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) );
 | 
			
		||||
        byte[] masterKey = getKey();
 | 
			
		||||
        logger.trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )", (Object) idForBytes( masterKey ) );
 | 
			
		||||
        byte[] sitePasswordSeedBytes = MasterKeyV0.mpw_digest.of( masterKey, sitePasswordInfo );
 | 
			
		||||
        logger.trc( "  => siteKey.id: %s", (Object) idForBytes( sitePasswordSeedBytes ) );
 | 
			
		||||
 | 
			
		||||
        Preconditions.checkState( sitePasswordSeed.length > 0 );
 | 
			
		||||
        int templateIndex = sitePasswordSeed[0] & 0xFF; // Mask the integer's sign.
 | 
			
		||||
        MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex );
 | 
			
		||||
        logger.trc( "type %s, template: %s", siteType, template.getTemplateString() );
 | 
			
		||||
 | 
			
		||||
        StringBuilder password = new StringBuilder( template.length() );
 | 
			
		||||
        for (int i = 0; i < template.length(); ++i) {
 | 
			
		||||
            int characterIndex = sitePasswordSeed[i + 1] & 0xFF; // Mask the integer's sign.
 | 
			
		||||
            MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
 | 
			
		||||
            char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
 | 
			
		||||
            logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex,
 | 
			
		||||
                        sitePasswordSeed[i + 1], passwordCharacter );
 | 
			
		||||
 | 
			
		||||
            password.append( passwordCharacter );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return password.toString();
 | 
			
		||||
        return sitePasswordSeedBytes;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,7 @@
 | 
			
		||||
 | 
			
		||||
package com.lyndir.masterpassword;
 | 
			
		||||
 | 
			
		||||
import com.google.common.base.Preconditions;
 | 
			
		||||
import com.google.common.primitives.Bytes;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.CodeUtils;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
			
		||||
@@ -51,19 +52,37 @@ public class MasterKeyV3 extends MasterKeyV2 {
 | 
			
		||||
    @Nullable
 | 
			
		||||
    @Override
 | 
			
		||||
    protected byte[] deriveKey(final char[] masterPassword) {
 | 
			
		||||
        byte[] fullNameBytes = getFullName().getBytes( MPConstant.mpw_charset );
 | 
			
		||||
        Preconditions.checkArgument( masterPassword.length > 0 );
 | 
			
		||||
 | 
			
		||||
        String fullName = getFullName();
 | 
			
		||||
        byte[] fullNameBytes = fullName.getBytes( MasterKeyV0.mpw_charset );
 | 
			
		||||
        byte[] fullNameLengthBytes = bytesForInt( fullNameBytes.length );
 | 
			
		||||
        ByteBuffer mpBytesBuf = MasterKeyV0.mpw_charset.encode( CharBuffer.wrap( masterPassword ) );
 | 
			
		||||
 | 
			
		||||
        String mpKeyScope = MPSiteVariant.Password.getScope();
 | 
			
		||||
        byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MPConstant.mpw_charset ), fullNameLengthBytes, fullNameBytes );
 | 
			
		||||
        logger.trc( "key scope: %s", mpKeyScope );
 | 
			
		||||
        logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
 | 
			
		||||
        logger.trc( "-- mpw_masterKey (algorithm: %u)", getAlgorithmVersion().toInt() );
 | 
			
		||||
        logger.trc( "fullName: %s", fullName );
 | 
			
		||||
        logger.trc( "masterPassword.id: %s", (Object) idForBytes( mpBytesBuf.array() ) );
 | 
			
		||||
 | 
			
		||||
        ByteBuffer mpBytesBuf = MPConstant.mpw_charset.encode( CharBuffer.wrap( masterPassword ) );
 | 
			
		||||
        String keyScope = MPKeyPurpose.Password.getScope();
 | 
			
		||||
        logger.trc( "keyScope: %s", keyScope );
 | 
			
		||||
 | 
			
		||||
        // Calculate the master key salt.
 | 
			
		||||
        logger.trc( "masterKeySalt: keyScope=%s | #fullName=%s | fullName=%s",
 | 
			
		||||
                    keyScope, CodeUtils.encodeHex( fullNameLengthBytes ), fullName );
 | 
			
		||||
        byte[] masterKeySalt = Bytes.concat( keyScope.getBytes( MasterKeyV0.mpw_charset ), fullNameLengthBytes, fullNameBytes );
 | 
			
		||||
        logger.trc( "  => masterKeySalt.id: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
 | 
			
		||||
 | 
			
		||||
        // Calculate the master key.
 | 
			
		||||
        logger.trc( "masterKey: scrypt( masterPassword, masterKeySalt, N=%lu, r=%u, p=%u )",
 | 
			
		||||
                    MasterKeyV0.scrypt_N, MasterKeyV0.scrypt_r, MasterKeyV0.scrypt_p );
 | 
			
		||||
        byte[] mpBytes = new byte[mpBytesBuf.remaining()];
 | 
			
		||||
        mpBytesBuf.get( mpBytes, 0, mpBytes.length );
 | 
			
		||||
        Arrays.fill( mpBytesBuf.array(), (byte) 0 );
 | 
			
		||||
        byte[] masterKey = scrypt( masterKeySalt, mpBytes ); // TODO: Why not mpBytesBuf.array()?
 | 
			
		||||
        Arrays.fill( masterKeySalt, (byte) 0 );
 | 
			
		||||
        Arrays.fill( mpBytes, (byte) 0 );
 | 
			
		||||
        logger.trc( "  => masterKey.id: %s", (masterKey == null)? null: (Object) idForBytes( masterKey ) );
 | 
			
		||||
 | 
			
		||||
        return scrypt( masterKeySalt, mpBytes );
 | 
			
		||||
        return masterKey;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -14,50 +14,50 @@ import org.joda.time.Instant;
 | 
			
		||||
 */
 | 
			
		||||
public class MPSite {
 | 
			
		||||
 | 
			
		||||
    public static final MPSiteType      DEFAULT_TYPE    = MPSiteType.GeneratedLong;
 | 
			
		||||
    public static final MPResultType    DEFAULT_TYPE    = MPResultType.GeneratedLong;
 | 
			
		||||
    public static final UnsignedInteger DEFAULT_COUNTER = UnsignedInteger.valueOf( 1 );
 | 
			
		||||
 | 
			
		||||
    private final MPUser            user;
 | 
			
		||||
    private       MasterKey.Version algorithmVersion;
 | 
			
		||||
    private       Instant           lastUsed;
 | 
			
		||||
    private       String            siteName;
 | 
			
		||||
    private       MPSiteType        siteType;
 | 
			
		||||
    private       MPResultType      resultType;
 | 
			
		||||
    private       UnsignedInteger   siteCounter;
 | 
			
		||||
    private       int               uses;
 | 
			
		||||
    private       String            loginName;
 | 
			
		||||
 | 
			
		||||
    public MPSite(final MPUser user, final String siteName) {
 | 
			
		||||
        this( user, siteName, DEFAULT_TYPE, DEFAULT_COUNTER );
 | 
			
		||||
        this( user, siteName, DEFAULT_COUNTER, DEFAULT_TYPE );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MPSite(final MPUser user, final String siteName, final MPSiteType siteType, final UnsignedInteger siteCounter) {
 | 
			
		||||
    public MPSite(final MPUser user, final String siteName, final UnsignedInteger siteCounter, final MPResultType resultType) {
 | 
			
		||||
        this.user = user;
 | 
			
		||||
        this.algorithmVersion = MasterKey.Version.CURRENT;
 | 
			
		||||
        this.lastUsed = new Instant();
 | 
			
		||||
        this.siteName = siteName;
 | 
			
		||||
        this.siteType = siteType;
 | 
			
		||||
        this.resultType = resultType;
 | 
			
		||||
        this.siteCounter = siteCounter;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected MPSite(final MPUser user, final MasterKey.Version algorithmVersion, final Instant lastUsed, final String siteName,
 | 
			
		||||
                     final MPSiteType siteType, final UnsignedInteger siteCounter, final int uses, @Nullable final String loginName,
 | 
			
		||||
                     final MPResultType resultType, final UnsignedInteger siteCounter, final int uses, @Nullable final String loginName,
 | 
			
		||||
                     @Nullable final String importContent) {
 | 
			
		||||
        this.user = user;
 | 
			
		||||
        this.algorithmVersion = algorithmVersion;
 | 
			
		||||
        this.lastUsed = lastUsed;
 | 
			
		||||
        this.siteName = siteName;
 | 
			
		||||
        this.siteType = siteType;
 | 
			
		||||
        this.resultType = resultType;
 | 
			
		||||
        this.siteCounter = siteCounter;
 | 
			
		||||
        this.uses = uses;
 | 
			
		||||
        this.loginName = loginName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String resultFor(final MasterKey masterKey) {
 | 
			
		||||
        return resultFor( masterKey, MPSiteVariant.Password, null );
 | 
			
		||||
        return resultFor( masterKey, MPKeyPurpose.Password, null );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String resultFor(final MasterKey masterKey, final MPSiteVariant variant, @Nullable final String context) {
 | 
			
		||||
        return masterKey.encode( siteName, siteType, siteCounter, variant, context );
 | 
			
		||||
    public String resultFor(final MasterKey masterKey, final MPKeyPurpose purpose, @Nullable final String context) {
 | 
			
		||||
        return masterKey.siteResult( siteName, siteCounter, purpose, context, resultType, null );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MPUser getUser() {
 | 
			
		||||
@@ -94,12 +94,12 @@ public class MPSite {
 | 
			
		||||
        this.siteName = siteName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MPSiteType getSiteType() {
 | 
			
		||||
        return siteType;
 | 
			
		||||
    public MPResultType getResultType() {
 | 
			
		||||
        return resultType;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setSiteType(final MPSiteType siteType) {
 | 
			
		||||
        this.siteType = siteType;
 | 
			
		||||
    public void setResultType(final MPResultType resultType) {
 | 
			
		||||
        this.resultType = resultType;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public UnsignedInteger getSiteCounter() {
 | 
			
		||||
 
 | 
			
		||||
@@ -64,7 +64,7 @@ public class MPSiteMarshaller {
 | 
			
		||||
        header.append( "# Full Name: " ).append( user.getFullName() ).append( '\n' );
 | 
			
		||||
        header.append( "# Avatar: " ).append( user.getAvatar() ).append( '\n' );
 | 
			
		||||
        header.append( "# Key ID: " ).append( user.exportKeyID() ).append( '\n' );
 | 
			
		||||
        header.append( "# Version: " ).append( MasterKey.Version.CURRENT.toBundleVersion() ).append( '\n' );
 | 
			
		||||
//        header.append( "# Version: " ).append( MasterKey.Version.CURRENT.toBundleVersion() ).append( '\n' );
 | 
			
		||||
        header.append( "# Algorithm: " ).append( MasterKey.Version.CURRENT.toInt() ).append( '\n' );
 | 
			
		||||
        header.append( "# Default Type: " ).append( user.getDefaultType().getType() ).append( '\n' );
 | 
			
		||||
        header.append( "# Passwords: " ).append( this.contentMode.name() ).append( '\n' );
 | 
			
		||||
@@ -82,7 +82,7 @@ public class MPSiteMarshaller {
 | 
			
		||||
                                  rfc3339.print( site.getLastUsed() ), // lastUsed
 | 
			
		||||
                                  site.getUses(), // uses
 | 
			
		||||
                                  strf( "%d:%d:%d", //
 | 
			
		||||
                                        site.getSiteType().getType(), // type
 | 
			
		||||
                                        site.getResultType().getType(), // type
 | 
			
		||||
                                        site.getAlgorithmVersion().toInt(), // algorithm
 | 
			
		||||
                                        site.getSiteCounter().intValue() ), // counter
 | 
			
		||||
                                  ifNotNullElse( site.getLoginName(), "" ), // loginName
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ import com.lyndir.lhunath.opal.system.CodeUtils;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.util.ConversionUtils;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.util.NNOperation;
 | 
			
		||||
import com.lyndir.masterpassword.MPSiteType;
 | 
			
		||||
import com.lyndir.masterpassword.MPResultType;
 | 
			
		||||
import com.lyndir.masterpassword.MasterKey;
 | 
			
		||||
import java.io.*;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
@@ -58,7 +58,7 @@ public class MPSiteUnmarshaller {
 | 
			
		||||
        String                              fullName     = null;
 | 
			
		||||
        int                                 mpVersion    = 0, importFormat = 0, avatar = 0;
 | 
			
		||||
        boolean                             clearContent = false, headerStarted = false;
 | 
			
		||||
        MPSiteType defaultType = MPSiteType.GeneratedLong;
 | 
			
		||||
        MPResultType                        defaultType  = MPResultType.GeneratedLong;
 | 
			
		||||
        MPSiteUnmarshaller                  marshaller   = null;
 | 
			
		||||
        final ImmutableList.Builder<MPSite> sites        = ImmutableList.builder();
 | 
			
		||||
 | 
			
		||||
@@ -92,7 +92,7 @@ public class MPSiteUnmarshaller {
 | 
			
		||||
                        else if ("Passwords".equalsIgnoreCase( name ))
 | 
			
		||||
                            clearContent = "visible".equalsIgnoreCase( value );
 | 
			
		||||
                        else if ("Default Type".equalsIgnoreCase( name ))
 | 
			
		||||
                            defaultType = MPSiteType.forType( ConversionUtils.toIntegerNN( value ) );
 | 
			
		||||
                            defaultType = MPResultType.forType( ConversionUtils.toIntegerNN( value ) );
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -110,7 +110,7 @@ public class MPSiteUnmarshaller {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected MPSiteUnmarshaller(final int importFormat, final int mpVersion, final String fullName, final byte[] keyID, final int avatar,
 | 
			
		||||
                                 final MPSiteType defaultType, final boolean clearContent) {
 | 
			
		||||
                                 final MPResultType defaultType, final boolean clearContent) {
 | 
			
		||||
        this.importFormat = importFormat;
 | 
			
		||||
        this.mpVersion = mpVersion;
 | 
			
		||||
        this.clearContent = clearContent;
 | 
			
		||||
@@ -131,7 +131,7 @@ public class MPSiteUnmarshaller {
 | 
			
		||||
                                   MasterKey.Version.fromInt( ConversionUtils.toIntegerNN( siteMatcher.group( 4 ).replace( ":", "" ) ) ), //
 | 
			
		||||
                                   rfc3339.parseDateTime( siteMatcher.group( 1 ) ).toInstant(), //
 | 
			
		||||
                                   siteMatcher.group( 5 ), //
 | 
			
		||||
                                   MPSiteType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ), MPSite.DEFAULT_COUNTER, //
 | 
			
		||||
                                   MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ), MPSite.DEFAULT_COUNTER, //
 | 
			
		||||
                                   ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ), //
 | 
			
		||||
                                   null, //
 | 
			
		||||
                                   siteMatcher.group( 6 ) );
 | 
			
		||||
@@ -142,7 +142,7 @@ public class MPSiteUnmarshaller {
 | 
			
		||||
                                   MasterKey.Version.fromInt( ConversionUtils.toIntegerNN( siteMatcher.group( 4 ).replace( ":", "" ) ) ), //
 | 
			
		||||
                                   rfc3339.parseDateTime( siteMatcher.group( 1 ) ).toInstant(), //
 | 
			
		||||
                                   siteMatcher.group( 7 ), //
 | 
			
		||||
                                   MPSiteType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
 | 
			
		||||
                                   MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
 | 
			
		||||
                                   UnsignedInteger.valueOf( siteMatcher.group( 5 ).replace( ":", "" ) ), //
 | 
			
		||||
                                   ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ), //
 | 
			
		||||
                                   siteMatcher.group( 6 ), //
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
 | 
			
		||||
import com.google.common.collect.ImmutableList;
 | 
			
		||||
import com.google.common.collect.Sets;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.CodeUtils;
 | 
			
		||||
import com.lyndir.masterpassword.MPSiteType;
 | 
			
		||||
import com.lyndir.masterpassword.MPResultType;
 | 
			
		||||
import com.lyndir.masterpassword.MasterKey;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
@@ -25,7 +25,7 @@ public class MPUser implements Comparable<MPUser> {
 | 
			
		||||
    private       byte[]            keyID;
 | 
			
		||||
    private final MasterKey.Version algorithmVersion;
 | 
			
		||||
    private       int               avatar;
 | 
			
		||||
    private       MPSiteType        defaultType;
 | 
			
		||||
    private       MPResultType      defaultType;
 | 
			
		||||
    private       ReadableInstant   lastUsed;
 | 
			
		||||
 | 
			
		||||
    public MPUser(final String fullName) {
 | 
			
		||||
@@ -33,11 +33,11 @@ public class MPUser implements Comparable<MPUser> {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MPUser(final String fullName, @Nullable final byte[] keyID) {
 | 
			
		||||
        this( fullName, keyID, MasterKey.Version.CURRENT, 0, MPSiteType.GeneratedLong, new DateTime() );
 | 
			
		||||
        this( fullName, keyID, MasterKey.Version.CURRENT, 0, MPResultType.GeneratedLong, new DateTime() );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MPUser(final String fullName, @Nullable final byte[] keyID, final MasterKey.Version algorithmVersion, final int avatar,
 | 
			
		||||
                  final MPSiteType defaultType, final ReadableInstant lastUsed) {
 | 
			
		||||
                  final MPResultType defaultType, final ReadableInstant lastUsed) {
 | 
			
		||||
        this.fullName = fullName;
 | 
			
		||||
        this.keyID = (keyID == null)? null: keyID.clone();
 | 
			
		||||
        this.algorithmVersion = algorithmVersion;
 | 
			
		||||
@@ -107,11 +107,11 @@ public class MPUser implements Comparable<MPUser> {
 | 
			
		||||
        this.avatar = avatar;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MPSiteType getDefaultType() {
 | 
			
		||||
    public MPResultType getDefaultType() {
 | 
			
		||||
        return defaultType;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setDefaultType(final MPSiteType defaultType) {
 | 
			
		||||
    public void setDefaultType(final MPResultType defaultType) {
 | 
			
		||||
        this.defaultType = defaultType;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -99,12 +99,12 @@ public class MPTestSuite implements Callable<Boolean> {
 | 
			
		||||
                        currentCase.siteName = text;
 | 
			
		||||
                    if ("siteCounter".equals( qName ))
 | 
			
		||||
                        currentCase.siteCounter = text.isEmpty()? null: UnsignedInteger.valueOf( text );
 | 
			
		||||
                    if ("siteType".equals( qName ))
 | 
			
		||||
                        currentCase.siteType = text;
 | 
			
		||||
                    if ("siteVariant".equals( qName ))
 | 
			
		||||
                        currentCase.siteVariant = text;
 | 
			
		||||
                    if ("siteContext".equals( qName ))
 | 
			
		||||
                        currentCase.siteContext = text;
 | 
			
		||||
                    if ("resultType".equals( qName ))
 | 
			
		||||
                        currentCase.resultType = text;
 | 
			
		||||
                    if ("keyPurpose".equals( qName ))
 | 
			
		||||
                        currentCase.keyPurpose = text;
 | 
			
		||||
                    if ("keyContext".equals( qName ))
 | 
			
		||||
                        currentCase.keyContext = text;
 | 
			
		||||
                    if ("result".equals( qName ))
 | 
			
		||||
                        currentCase.result = text;
 | 
			
		||||
                }
 | 
			
		||||
@@ -173,8 +173,9 @@ public class MPTestSuite implements Callable<Boolean> {
 | 
			
		||||
            @Override
 | 
			
		||||
            public Boolean apply(@Nonnull final MPTests.Case testCase) {
 | 
			
		||||
                MasterKey masterKey = MasterKey.create( testCase.getAlgorithm(), testCase.getFullName(), testCase.getMasterPassword() );
 | 
			
		||||
                String sitePassword = masterKey.encode( testCase.getSiteName(), testCase.getSiteType(), testCase.getSiteCounter(),
 | 
			
		||||
                                                        testCase.getSiteVariant(), testCase.getSiteContext() );
 | 
			
		||||
                String sitePassword = masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
 | 
			
		||||
                                                            testCase.getKeyContext(), testCase.getResultType(),
 | 
			
		||||
                                                            null );
 | 
			
		||||
 | 
			
		||||
                return testCase.getResult().equals( sitePassword );
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -74,9 +74,9 @@ public class MPTests {
 | 
			
		||||
        String          keyID;
 | 
			
		||||
        String          siteName;
 | 
			
		||||
        UnsignedInteger siteCounter;
 | 
			
		||||
        String  siteType;
 | 
			
		||||
        String  siteVariant;
 | 
			
		||||
        String  siteContext;
 | 
			
		||||
        String          resultType;
 | 
			
		||||
        String          keyPurpose;
 | 
			
		||||
        String          keyContext;
 | 
			
		||||
        String          result;
 | 
			
		||||
 | 
			
		||||
        private transient Case parentCase;
 | 
			
		||||
@@ -130,25 +130,25 @@ public class MPTests {
 | 
			
		||||
                    return checkNotNull( parentCase.siteCounter );
 | 
			
		||||
                }
 | 
			
		||||
            } );
 | 
			
		||||
            siteType = ifNotNullElse( siteType, new NNSupplier<String>() {
 | 
			
		||||
            resultType = ifNotNullElse( resultType, new NNSupplier<String>() {
 | 
			
		||||
                @Nonnull
 | 
			
		||||
                @Override
 | 
			
		||||
                public String get() {
 | 
			
		||||
                    return checkNotNull( parentCase.siteType );
 | 
			
		||||
                    return checkNotNull( parentCase.resultType );
 | 
			
		||||
                }
 | 
			
		||||
            } );
 | 
			
		||||
            siteVariant = ifNotNullElse( siteVariant, new NNSupplier<String>() {
 | 
			
		||||
            keyPurpose = ifNotNullElse( keyPurpose, new NNSupplier<String>() {
 | 
			
		||||
                @Nonnull
 | 
			
		||||
                @Override
 | 
			
		||||
                public String get() {
 | 
			
		||||
                    return checkNotNull( parentCase.siteVariant );
 | 
			
		||||
                    return checkNotNull( parentCase.keyPurpose );
 | 
			
		||||
                }
 | 
			
		||||
            } );
 | 
			
		||||
            siteContext = ifNotNullElse( siteContext, new NNSupplier<String>() {
 | 
			
		||||
            keyContext = ifNotNullElse( keyContext, new NNSupplier<String>() {
 | 
			
		||||
                @Nonnull
 | 
			
		||||
                @Override
 | 
			
		||||
                public String get() {
 | 
			
		||||
                    return (parentCase == null)? "": checkNotNull( parentCase.siteContext );
 | 
			
		||||
                    return (parentCase == null)? "": checkNotNull( parentCase.keyContext );
 | 
			
		||||
                }
 | 
			
		||||
            } );
 | 
			
		||||
            result = ifNotNullElse( result, new NNSupplier<String>() {
 | 
			
		||||
@@ -200,18 +200,18 @@ public class MPTests {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Nonnull
 | 
			
		||||
        public MPSiteType getSiteType() {
 | 
			
		||||
            return MPSiteType.forName( checkNotNull( siteType ) );
 | 
			
		||||
        public MPResultType getResultType() {
 | 
			
		||||
            return MPResultType.forName( checkNotNull( resultType ) );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Nonnull
 | 
			
		||||
        public MPSiteVariant getSiteVariant() {
 | 
			
		||||
            return MPSiteVariant.forName( checkNotNull( siteVariant ) );
 | 
			
		||||
        public MPKeyPurpose getKeyPurpose() {
 | 
			
		||||
            return MPKeyPurpose.forName( checkNotNull( keyPurpose ) );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Nonnull
 | 
			
		||||
        public String getSiteContext() {
 | 
			
		||||
            return checkNotNull( siteContext );
 | 
			
		||||
        public String getKeyContext() {
 | 
			
		||||
            return checkNotNull( keyContext );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Nonnull
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
        <keyID>98EEF4D1DF46D849574A82A03C3177056B15DFFCA29BB3899DE4628453675302</keyID>
 | 
			
		||||
        <siteName>masterpasswordapp.com</siteName>
 | 
			
		||||
        <siteCounter>1</siteCounter>
 | 
			
		||||
        <resultType>GeneratedLong</resultType>
 | 
			
		||||
        <resultType>Long</resultType>
 | 
			
		||||
        <keyPurpose>Authentication</keyPurpose>
 | 
			
		||||
        <result><!-- abstract --></result>
 | 
			
		||||
    </case>
 | 
			
		||||
@@ -33,12 +33,12 @@
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v3_loginName" parent="v3">
 | 
			
		||||
        <keyPurpose>Identification</keyPurpose>
 | 
			
		||||
        <resultType>GeneratedName</resultType>
 | 
			
		||||
        <resultType>Name</resultType>
 | 
			
		||||
        <result>wohzaqage</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v3_securityAnswer" parent="v3">
 | 
			
		||||
        <keyPurpose>Recovery</keyPurpose>
 | 
			
		||||
        <resultType>GeneratedPhrase</resultType>
 | 
			
		||||
        <resultType>Phrase</resultType>
 | 
			
		||||
        <result>xin diyjiqoja hubu</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v3_securityAnswer_context" parent="v3_securityAnswer">
 | 
			
		||||
@@ -46,31 +46,31 @@
 | 
			
		||||
        <result>xogx tem cegyiva jab</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v3_type_maximum" parent="v3">
 | 
			
		||||
        <resultType>GeneratedMaximum</resultType>
 | 
			
		||||
        <resultType>Maximum</resultType>
 | 
			
		||||
        <result>W6@692^B1#&@gVdSdLZ@</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v3_type_medium" parent="v3">
 | 
			
		||||
        <resultType>GeneratedMedium</resultType>
 | 
			
		||||
        <resultType>Medium</resultType>
 | 
			
		||||
        <result>Jej2$Quv</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v3_type_basic" parent="v3">
 | 
			
		||||
        <resultType>GeneratedBasic</resultType>
 | 
			
		||||
        <resultType>Basic</resultType>
 | 
			
		||||
        <result>WAo2xIg6</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v3_type_short" parent="v3">
 | 
			
		||||
        <resultType>GeneratedShort</resultType>
 | 
			
		||||
        <resultType>Short</resultType>
 | 
			
		||||
        <result>Jej2</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v3_type_pin" parent="v3">
 | 
			
		||||
        <resultType>GeneratedPIN</resultType>
 | 
			
		||||
        <resultType>PIN</resultType>
 | 
			
		||||
        <result>7662</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v3_type_name" parent="v3">
 | 
			
		||||
        <resultType>GeneratedName</resultType>
 | 
			
		||||
        <resultType>Name</resultType>
 | 
			
		||||
        <result>jejraquvo</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v3_type_phrase" parent="v3">
 | 
			
		||||
        <resultType>GeneratedPhrase</resultType>
 | 
			
		||||
        <resultType>Phrase</resultType>
 | 
			
		||||
        <result>jejr quv cabsibu tam</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v3_counter_ceiling" parent="v3">
 | 
			
		||||
@@ -99,12 +99,12 @@
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v2_loginName" parent="v2">
 | 
			
		||||
        <keyPurpose>Identification</keyPurpose>
 | 
			
		||||
        <resultType>GeneratedName</resultType>
 | 
			
		||||
        <resultType>Name</resultType>
 | 
			
		||||
        <result>wohzaqage</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v2_securityAnswer" parent="v2">
 | 
			
		||||
        <keyPurpose>Recovery</keyPurpose>
 | 
			
		||||
        <resultType>GeneratedPhrase</resultType>
 | 
			
		||||
        <resultType>Phrase</resultType>
 | 
			
		||||
        <result>xin diyjiqoja hubu</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v2_securityAnswer_context" parent="v2_securityAnswer">
 | 
			
		||||
@@ -112,31 +112,31 @@
 | 
			
		||||
        <result>xogx tem cegyiva jab</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v2_type_maximum" parent="v2">
 | 
			
		||||
        <resultType>GeneratedMaximum</resultType>
 | 
			
		||||
        <resultType>Maximum</resultType>
 | 
			
		||||
        <result>W6@692^B1#&@gVdSdLZ@</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v2_type_medium" parent="v2">
 | 
			
		||||
        <resultType>GeneratedMedium</resultType>
 | 
			
		||||
        <resultType>Medium</resultType>
 | 
			
		||||
        <result>Jej2$Quv</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v2_type_basic" parent="v2">
 | 
			
		||||
        <resultType>GeneratedBasic</resultType>
 | 
			
		||||
        <resultType>Basic</resultType>
 | 
			
		||||
        <result>WAo2xIg6</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v2_type_short" parent="v2">
 | 
			
		||||
        <resultType>GeneratedShort</resultType>
 | 
			
		||||
        <resultType>Short</resultType>
 | 
			
		||||
        <result>Jej2</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v2_type_pin" parent="v2">
 | 
			
		||||
        <resultType>GeneratedPIN</resultType>
 | 
			
		||||
        <resultType>PIN</resultType>
 | 
			
		||||
        <result>7662</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v2_type_name" parent="v2">
 | 
			
		||||
        <resultType>GeneratedName</resultType>
 | 
			
		||||
        <resultType>Name</resultType>
 | 
			
		||||
        <result>jejraquvo</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v2_type_phrase" parent="v2">
 | 
			
		||||
        <resultType>GeneratedPhrase</resultType>
 | 
			
		||||
        <resultType>Phrase</resultType>
 | 
			
		||||
        <result>jejr quv cabsibu tam</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v2_counter_ceiling" parent="v2">
 | 
			
		||||
@@ -165,12 +165,12 @@
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v1_loginName" parent="v1">
 | 
			
		||||
        <keyPurpose>Identification</keyPurpose>
 | 
			
		||||
        <resultType>GeneratedName</resultType>
 | 
			
		||||
        <resultType>Name</resultType>
 | 
			
		||||
        <result>wohzaqage</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v1_securityAnswer" parent="v1">
 | 
			
		||||
        <keyPurpose>Recovery</keyPurpose>
 | 
			
		||||
        <resultType>GeneratedPhrase</resultType>
 | 
			
		||||
        <resultType>Phrase</resultType>
 | 
			
		||||
        <result>xin diyjiqoja hubu</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v1_securityAnswer_context" parent="v1_securityAnswer">
 | 
			
		||||
@@ -178,31 +178,31 @@
 | 
			
		||||
        <result>xogx tem cegyiva jab</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v1_type_maximum" parent="v1">
 | 
			
		||||
        <resultType>GeneratedMaximum</resultType>
 | 
			
		||||
        <resultType>Maximum</resultType>
 | 
			
		||||
        <result>W6@692^B1#&@gVdSdLZ@</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v1_type_medium" parent="v1">
 | 
			
		||||
        <resultType>GeneratedMedium</resultType>
 | 
			
		||||
        <resultType>Medium</resultType>
 | 
			
		||||
        <result>Jej2$Quv</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v1_type_basic" parent="v1">
 | 
			
		||||
        <resultType>GeneratedBasic</resultType>
 | 
			
		||||
        <resultType>Basic</resultType>
 | 
			
		||||
        <result>WAo2xIg6</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v1_type_short" parent="v1">
 | 
			
		||||
        <resultType>GeneratedShort</resultType>
 | 
			
		||||
        <resultType>Short</resultType>
 | 
			
		||||
        <result>Jej2</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v1_type_pin" parent="v1">
 | 
			
		||||
        <resultType>GeneratedPIN</resultType>
 | 
			
		||||
        <resultType>PIN</resultType>
 | 
			
		||||
        <result>7662</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v1_type_name" parent="v1">
 | 
			
		||||
        <resultType>GeneratedName</resultType>
 | 
			
		||||
        <resultType>Name</resultType>
 | 
			
		||||
        <result>jejraquvo</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v1_type_phrase" parent="v1">
 | 
			
		||||
        <resultType>GeneratedPhrase</resultType>
 | 
			
		||||
        <resultType>Phrase</resultType>
 | 
			
		||||
        <result>jejr quv cabsibu tam</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v1_counter_ceiling" parent="v1">
 | 
			
		||||
@@ -231,12 +231,12 @@
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v0_loginName" parent="v0">
 | 
			
		||||
        <keyPurpose>Identification</keyPurpose>
 | 
			
		||||
        <resultType>GeneratedName</resultType>
 | 
			
		||||
        <resultType>Name</resultType>
 | 
			
		||||
        <result>lozwajave</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v0_securityAnswer" parent="v0">
 | 
			
		||||
        <keyPurpose>Recovery</keyPurpose>
 | 
			
		||||
        <resultType>GeneratedPhrase</resultType>
 | 
			
		||||
        <resultType>Phrase</resultType>
 | 
			
		||||
        <result>miy lirfijoja dubu</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v0_securityAnswer_context" parent="v0_securityAnswer">
 | 
			
		||||
@@ -244,31 +244,31 @@
 | 
			
		||||
        <result>movm bex gevrica jaf</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v0_type_maximum" parent="v0">
 | 
			
		||||
        <resultType>GeneratedMaximum</resultType>
 | 
			
		||||
        <resultType>Maximum</resultType>
 | 
			
		||||
        <result>w1!3bA3icmRAc)SS@lwl</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v0_type_medium" parent="v0">
 | 
			
		||||
        <resultType>GeneratedMedium</resultType>
 | 
			
		||||
        <resultType>Medium</resultType>
 | 
			
		||||
        <result>Fej7]Jug</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v0_type_basic" parent="v0">
 | 
			
		||||
        <resultType>GeneratedBasic</resultType>
 | 
			
		||||
        <resultType>Basic</resultType>
 | 
			
		||||
        <result>wvH7irC1</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v0_type_short" parent="v0">
 | 
			
		||||
        <resultType>GeneratedShort</resultType>
 | 
			
		||||
        <resultType>Short</resultType>
 | 
			
		||||
        <result>Fej7</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v0_type_pin" parent="v0">
 | 
			
		||||
        <resultType>GeneratedPIN</resultType>
 | 
			
		||||
        <resultType>PIN</resultType>
 | 
			
		||||
        <result>2117</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v0_type_name" parent="v0">
 | 
			
		||||
        <resultType>GeneratedName</resultType>
 | 
			
		||||
        <resultType>Name</resultType>
 | 
			
		||||
        <result>fejrajugo</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v0_type_phrase" parent="v0">
 | 
			
		||||
        <resultType>GeneratedPhrase</resultType>
 | 
			
		||||
        <resultType>Phrase</resultType>
 | 
			
		||||
        <result>fejr jug gabsibu bax</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v0_counter_ceiling" parent="v0">
 | 
			
		||||
 
 | 
			
		||||
@@ -55,8 +55,9 @@ public class MasterKeyTest {
 | 
			
		||||
                MasterKey masterKey = MasterKey.create( testCase.getAlgorithm(), testCase.getFullName(), testCase.getMasterPassword() );
 | 
			
		||||
 | 
			
		||||
                assertEquals(
 | 
			
		||||
                        masterKey.encode( testCase.getSiteName(), testCase.getSiteType(), testCase.getSiteCounter(),
 | 
			
		||||
                                          testCase.getSiteVariant(), testCase.getSiteContext() ),
 | 
			
		||||
                        masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
 | 
			
		||||
                                              testCase.getKeyContext(), testCase.getResultType(),
 | 
			
		||||
                                              null ),
 | 
			
		||||
                        testCase.getResult(), "[testEncode] Failed test case: " + testCase );
 | 
			
		||||
 | 
			
		||||
                return true;
 | 
			
		||||
@@ -101,8 +102,9 @@ public class MasterKeyTest {
 | 
			
		||||
 | 
			
		||||
            MasterKey masterKey = MasterKey.create( defaultCase.getFullName(), defaultCase.getMasterPassword() );
 | 
			
		||||
            masterKey.invalidate();
 | 
			
		||||
            masterKey.encode( defaultCase.getSiteName(), defaultCase.getSiteType(), defaultCase.getSiteCounter(),
 | 
			
		||||
                              defaultCase.getSiteVariant(), defaultCase.getSiteContext() );
 | 
			
		||||
            masterKey.siteResult( defaultCase.getSiteName(), defaultCase.getSiteCounter(), defaultCase.getKeyPurpose(),
 | 
			
		||||
                                  defaultCase.getKeyContext(), defaultCase.getResultType(),
 | 
			
		||||
                                  null );
 | 
			
		||||
 | 
			
		||||
            fail( "[testInvalidate] Master key should have been invalidated, but was still usable." );
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,6 @@
 | 
			
		||||
        <module>masterpassword-tests</module>
 | 
			
		||||
        <module>masterpassword-algorithm</module>
 | 
			
		||||
        <module>masterpassword-model</module>
 | 
			
		||||
        <module>masterpassword-cli</module>
 | 
			
		||||
        <module>masterpassword-gui</module>
 | 
			
		||||
    </modules>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,9 +9,6 @@ project(':masterpassword-model').projectDir = new File( '../core/java/model' )
 | 
			
		||||
include 'masterpassword-tests'
 | 
			
		||||
project(':masterpassword-tests').projectDir = new File( '../core/java/tests' )
 | 
			
		||||
 | 
			
		||||
include 'masterpassword-cli'
 | 
			
		||||
project(':masterpassword-cli').projectDir = new File( '../platform-independent/cli-java' )
 | 
			
		||||
 | 
			
		||||
include 'masterpassword-gui'
 | 
			
		||||
project(':masterpassword-gui').projectDir = new File( '../platform-independent/gui-java' )
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -54,7 +54,7 @@ public class EmergencyActivity extends Activity {
 | 
			
		||||
 | 
			
		||||
    private final Preferences                      preferences    = Preferences.get( this );
 | 
			
		||||
    private final ListeningExecutorService         executor       = MoreExecutors.listeningDecorator( Executors.newSingleThreadExecutor() );
 | 
			
		||||
    private final ImmutableList<MPSiteType>        allSiteTypes = ImmutableList.copyOf( MPSiteType.forClass( MPSiteTypeClass.Generated ) );
 | 
			
		||||
    private final ImmutableList<MPResultType>      allResultTypes = ImmutableList.copyOf( MPResultType.forClass( MPResultTypeClass.Generated ) );
 | 
			
		||||
    private final ImmutableList<MasterKey.Version> allVersions    = ImmutableList.copyOf( MasterKey.Version.values() );
 | 
			
		||||
 | 
			
		||||
    private ListenableFuture<MasterKey> masterKeyFuture;
 | 
			
		||||
@@ -71,8 +71,8 @@ public class EmergencyActivity extends Activity {
 | 
			
		||||
    @BindView(R.id.siteNameField)
 | 
			
		||||
    EditText siteNameField;
 | 
			
		||||
 | 
			
		||||
    @BindView(R.id.siteTypeButton)
 | 
			
		||||
    Button siteTypeButton;
 | 
			
		||||
    @BindView(R.id.resultTypeButton)
 | 
			
		||||
    Button resultTypeButton;
 | 
			
		||||
 | 
			
		||||
    @BindView(R.id.counterField)
 | 
			
		||||
    Button siteCounterButton;
 | 
			
		||||
@@ -131,15 +131,15 @@ public class EmergencyActivity extends Activity {
 | 
			
		||||
                updateSitePassword();
 | 
			
		||||
            }
 | 
			
		||||
        } );
 | 
			
		||||
        siteTypeButton.setOnClickListener( new View.OnClickListener() {
 | 
			
		||||
        resultTypeButton.setOnClickListener( new View.OnClickListener() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onClick(final View v) {
 | 
			
		||||
                @SuppressWarnings("SuspiciousMethodCalls")
 | 
			
		||||
                MPSiteType siteType =
 | 
			
		||||
                        allSiteTypes.get( (allSiteTypes.indexOf( siteTypeButton.getTag() ) + 1) % allSiteTypes.size() );
 | 
			
		||||
                preferences.setDefaultSiteType( siteType );
 | 
			
		||||
                siteTypeButton.setTag( siteType );
 | 
			
		||||
                siteTypeButton.setText( siteType.getShortName() );
 | 
			
		||||
                MPResultType resultType =
 | 
			
		||||
                        allResultTypes.get( (allResultTypes.indexOf( resultTypeButton.getTag() ) + 1) % allResultTypes.size() );
 | 
			
		||||
                preferences.setDefaultResultType( resultType );
 | 
			
		||||
                resultTypeButton.setTag( resultType );
 | 
			
		||||
                resultTypeButton.setText( resultType.getShortName() );
 | 
			
		||||
                updateSitePassword();
 | 
			
		||||
            }
 | 
			
		||||
        } );
 | 
			
		||||
@@ -220,9 +220,9 @@ public class EmergencyActivity extends Activity {
 | 
			
		||||
        forgetPasswordField.setChecked( preferences.isForgetPassword() );
 | 
			
		||||
        maskPasswordField.setChecked( preferences.isMaskPassword() );
 | 
			
		||||
        sitePasswordField.setTransformationMethod( preferences.isMaskPassword()? new PasswordTransformationMethod(): null );
 | 
			
		||||
        MPSiteType defaultSiteType = preferences.getDefaultSiteType();
 | 
			
		||||
        siteTypeButton.setTag( defaultSiteType );
 | 
			
		||||
        siteTypeButton.setText( defaultSiteType.getShortName() );
 | 
			
		||||
        MPResultType defaultResultType = preferences.getDefaultResultType();
 | 
			
		||||
        resultTypeButton.setTag( defaultResultType );
 | 
			
		||||
        resultTypeButton.setText( defaultResultType.getShortName() );
 | 
			
		||||
        MasterKey.Version defaultVersion = preferences.getDefaultVersion();
 | 
			
		||||
        siteVersionButton.setTag( defaultVersion );
 | 
			
		||||
        siteVersionButton.setText( defaultVersion.name() );
 | 
			
		||||
@@ -314,7 +314,7 @@ public class EmergencyActivity extends Activity {
 | 
			
		||||
 | 
			
		||||
    private void updateSitePassword() {
 | 
			
		||||
        final String          siteName = siteNameField.getText().toString();
 | 
			
		||||
        final MPSiteType type = (MPSiteType) siteTypeButton.getTag();
 | 
			
		||||
        final MPResultType    type     = (MPResultType) resultTypeButton.getTag();
 | 
			
		||||
        final UnsignedInteger counter  = UnsignedInteger.valueOf( siteCounterButton.getText().toString() );
 | 
			
		||||
 | 
			
		||||
        if ((masterKeyFuture == null) || siteName.isEmpty() || (type == null)) {
 | 
			
		||||
@@ -332,7 +332,7 @@ public class EmergencyActivity extends Activity {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void run() {
 | 
			
		||||
                try {
 | 
			
		||||
                    sitePassword = masterKeyFuture.get().encode( siteName, type, counter, MPSiteVariant.Password, null );
 | 
			
		||||
                    sitePassword = masterKeyFuture.get().siteResult( siteName, counter, MPKeyPurpose.Password, null, type, null );
 | 
			
		||||
 | 
			
		||||
                    runOnUiThread( new Runnable() {
 | 
			
		||||
                        @Override
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,7 @@ public final class Preferences {
 | 
			
		||||
    private static final String PREF_FORGET_PASSWORD    = "forgetPassword";
 | 
			
		||||
    private static final String PREF_MASK_PASSWORD      = "maskPassword";
 | 
			
		||||
    private static final String PREF_FULL_NAME          = "fullName";
 | 
			
		||||
    private static final String PREF_SITE_TYPE          = "siteType";
 | 
			
		||||
    private static final String PREF_RESULT_TYPE        = "resultType";
 | 
			
		||||
    private static final String PREF_ALGORITHM_VERSION  = "algorithmVersion";
 | 
			
		||||
    private static Preferences instance;
 | 
			
		||||
 | 
			
		||||
@@ -138,20 +138,20 @@ public final class Preferences {
 | 
			
		||||
        return prefs().getString( PREF_FULL_NAME, "" );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean setDefaultSiteType(@Nonnull final MPSiteType value) {
 | 
			
		||||
        if (getDefaultSiteType() == value)
 | 
			
		||||
    public boolean setDefaultResultType(final MPResultType value) {
 | 
			
		||||
        if (getDefaultResultType() == value)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        prefs().edit().putInt( PREF_SITE_TYPE, value.ordinal() ).apply();
 | 
			
		||||
        prefs().edit().putInt( PREF_RESULT_TYPE, value.ordinal() ).apply();
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    public MPSiteType getDefaultSiteType() {
 | 
			
		||||
        return MPSiteType.values()[prefs().getInt( PREF_SITE_TYPE, MPSiteType.GeneratedLong.ordinal() )];
 | 
			
		||||
    public MPResultType getDefaultResultType() {
 | 
			
		||||
        return MPResultType.values()[prefs().getInt( PREF_RESULT_TYPE, MPResultType.GeneratedLong.ordinal() )];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean setDefaultVersion(@Nonnull final MasterKey.Version value) {
 | 
			
		||||
    public boolean setDefaultVersion(final MasterKey.Version value) {
 | 
			
		||||
        if (getDefaultVersion() == value)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -105,7 +105,7 @@
 | 
			
		||||
                        android:id="@id/sitePasswordField"
 | 
			
		||||
                        android:layout_width="match_parent"
 | 
			
		||||
                        android:layout_height="wrap_content"
 | 
			
		||||
                        android:nextFocusForward="@+id/siteTypeButton"
 | 
			
		||||
                        android:nextFocusForward="@+id/resultTypeButton"
 | 
			
		||||
                        android:gravity="center"
 | 
			
		||||
                        android:background="@android:color/transparent"
 | 
			
		||||
                        android:textColor="#FFFFFF"
 | 
			
		||||
@@ -157,7 +157,7 @@
 | 
			
		||||
                    android:gravity="center">
 | 
			
		||||
 | 
			
		||||
                <Button
 | 
			
		||||
                        android:id="@id/siteTypeButton"
 | 
			
		||||
                        android:id="@id/resultTypeButton"
 | 
			
		||||
                        android:layout_width="wrap_content"
 | 
			
		||||
                        android:layout_height="wrap_content"
 | 
			
		||||
                        android:layout_marginStart="8dp"
 | 
			
		||||
@@ -175,12 +175,12 @@
 | 
			
		||||
                <TextView
 | 
			
		||||
                        android:layout_width="wrap_content"
 | 
			
		||||
                        android:layout_height="wrap_content"
 | 
			
		||||
                        android:labelFor="@id/siteTypeButton"
 | 
			
		||||
                        android:labelFor="@id/resultTypeButton"
 | 
			
		||||
                        android:gravity="center"
 | 
			
		||||
                        android:background="@android:color/transparent"
 | 
			
		||||
                        android:textSize="12sp"
 | 
			
		||||
                        android:textColor="@android:color/tertiary_text_dark"
 | 
			
		||||
                        android:text="@string/siteType_hint" />
 | 
			
		||||
                        android:text="@string/resultType_hint" />
 | 
			
		||||
 | 
			
		||||
            </LinearLayout>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@
 | 
			
		||||
    <string name="masterPassword_hint">Your master password</string>
 | 
			
		||||
    <string name="siteName_hint">eg. google.com</string>
 | 
			
		||||
    <string name="sitePassword_hint">Tap to copy</string>
 | 
			
		||||
    <string name="siteType_hint">Type</string>
 | 
			
		||||
    <string name="resultType_hint">Type</string>
 | 
			
		||||
    <string name="siteCounter_hint">Counter</string>
 | 
			
		||||
    <string name="siteVersion_hint">Algorithm</string>
 | 
			
		||||
    <string name="empty" />
 | 
			
		||||
 
 | 
			
		||||
@@ -1,90 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 | 
			
		||||
<plist version="1.0">
 | 
			
		||||
<dict>
 | 
			
		||||
	<key>MPGeneratedSiteEntity</key>
 | 
			
		||||
	<dict>
 | 
			
		||||
		<key>Login Name</key>
 | 
			
		||||
		<array>
 | 
			
		||||
			<string>cvccvcvcv</string>
 | 
			
		||||
		</array>
 | 
			
		||||
		<key>Phrase</key>
 | 
			
		||||
		<array>
 | 
			
		||||
			<string>cvcc cvc cvccvcv cvc</string>
 | 
			
		||||
			<string>cvc cvccvcvcv cvcv</string>
 | 
			
		||||
			<string>cv cvccv cvc cvcvccv</string>
 | 
			
		||||
		</array>
 | 
			
		||||
		<key>Maximum Security Password</key>
 | 
			
		||||
		<array>
 | 
			
		||||
			<string>anoxxxxxxxxxxxxxxxxx</string>
 | 
			
		||||
			<string>axxxxxxxxxxxxxxxxxno</string>
 | 
			
		||||
		</array>
 | 
			
		||||
		<key>Long Password</key>
 | 
			
		||||
		<array>
 | 
			
		||||
			<string>CvcvnoCvcvCvcv</string>
 | 
			
		||||
			<string>CvcvCvcvnoCvcv</string>
 | 
			
		||||
			<string>CvcvCvcvCvcvno</string>
 | 
			
		||||
			<string>CvccnoCvcvCvcv</string>
 | 
			
		||||
			<string>CvccCvcvnoCvcv</string>
 | 
			
		||||
			<string>CvccCvcvCvcvno</string>
 | 
			
		||||
			<string>CvcvnoCvccCvcv</string>
 | 
			
		||||
			<string>CvcvCvccnoCvcv</string>
 | 
			
		||||
			<string>CvcvCvccCvcvno</string>
 | 
			
		||||
			<string>CvcvnoCvcvCvcc</string>
 | 
			
		||||
			<string>CvcvCvcvnoCvcc</string>
 | 
			
		||||
			<string>CvcvCvcvCvccno</string>
 | 
			
		||||
			<string>CvccnoCvccCvcv</string>
 | 
			
		||||
			<string>CvccCvccnoCvcv</string>
 | 
			
		||||
			<string>CvccCvccCvcvno</string>
 | 
			
		||||
			<string>CvcvnoCvccCvcc</string>
 | 
			
		||||
			<string>CvcvCvccnoCvcc</string>
 | 
			
		||||
			<string>CvcvCvccCvccno</string>
 | 
			
		||||
			<string>CvccnoCvcvCvcc</string>
 | 
			
		||||
			<string>CvccCvcvnoCvcc</string>
 | 
			
		||||
			<string>CvccCvcvCvccno</string>
 | 
			
		||||
		</array>
 | 
			
		||||
		<key>Medium Password</key>
 | 
			
		||||
		<array>
 | 
			
		||||
			<string>CvcnoCvc</string>
 | 
			
		||||
			<string>CvcCvcno</string>
 | 
			
		||||
		</array>
 | 
			
		||||
		<key>Basic Password</key>
 | 
			
		||||
		<array>
 | 
			
		||||
			<string>aaanaaan</string>
 | 
			
		||||
			<string>aannaaan</string>
 | 
			
		||||
			<string>aaannaaa</string>
 | 
			
		||||
		</array>
 | 
			
		||||
		<key>Short Password</key>
 | 
			
		||||
		<array>
 | 
			
		||||
			<string>Cvcn</string>
 | 
			
		||||
		</array>
 | 
			
		||||
		<key>PIN</key>
 | 
			
		||||
		<array>
 | 
			
		||||
			<string>nnnn</string>
 | 
			
		||||
		</array>
 | 
			
		||||
	</dict>
 | 
			
		||||
	<key>MPCharacterClasses</key>
 | 
			
		||||
	<dict>
 | 
			
		||||
		<key>V</key>
 | 
			
		||||
		<string>AEIOU</string>
 | 
			
		||||
		<key>C</key>
 | 
			
		||||
		<string>BCDFGHJKLMNPQRSTVWXYZ</string>
 | 
			
		||||
		<key>v</key>
 | 
			
		||||
		<string>aeiou</string>
 | 
			
		||||
		<key>c</key>
 | 
			
		||||
		<string>bcdfghjklmnpqrstvwxyz</string>
 | 
			
		||||
		<key>A</key>
 | 
			
		||||
		<string>AEIOUBCDFGHJKLMNPQRSTVWXYZ</string>
 | 
			
		||||
		<key>a</key>
 | 
			
		||||
		<string>AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz</string>
 | 
			
		||||
		<key>n</key>
 | 
			
		||||
		<string>0123456789</string>
 | 
			
		||||
		<key>o</key>
 | 
			
		||||
		<string>@&%?,=[]_:-+*$#!'^~;()/.</string>
 | 
			
		||||
		<key>x</key>
 | 
			
		||||
		<string>AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()</string>
 | 
			
		||||
		<key> </key>
 | 
			
		||||
		<string> </string>
 | 
			
		||||
	</dict>
 | 
			
		||||
</dict>
 | 
			
		||||
</plist>
 | 
			
		||||
@@ -1,14 +0,0 @@
 | 
			
		||||
plugins {
 | 
			
		||||
    id 'java'
 | 
			
		||||
    id 'application'
 | 
			
		||||
    id 'com.github.johnrengelman.shadow' version '1.2.4'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
description = 'Master Password CLI'
 | 
			
		||||
mainClassName = 'com.lyndir.masterpassword.CLI'
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    compile project(':masterpassword-algorithm')
 | 
			
		||||
 | 
			
		||||
    compile group: 'ch.qos.logback', name: 'logback-classic', version:'1.1.2'
 | 
			
		||||
}
 | 
			
		||||
@@ -1,98 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
 | 
			
		||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 | 
			
		||||
    <modelVersion>4.0.0</modelVersion>
 | 
			
		||||
 | 
			
		||||
    <!-- PROJECT METADATA -->
 | 
			
		||||
    <parent>
 | 
			
		||||
        <groupId>com.lyndir.masterpassword</groupId>
 | 
			
		||||
        <artifactId>masterpassword</artifactId>
 | 
			
		||||
        <version>GIT-SNAPSHOT</version>
 | 
			
		||||
    </parent>
 | 
			
		||||
 | 
			
		||||
    <name>Master Password CLI</name>
 | 
			
		||||
    <description>A CLI interface to the Master Password algorithm</description>
 | 
			
		||||
 | 
			
		||||
    <artifactId>masterpassword-cli</artifactId>
 | 
			
		||||
    <packaging>jar</packaging>
 | 
			
		||||
 | 
			
		||||
    <!-- BUILD CONFIGURATION -->
 | 
			
		||||
    <build>
 | 
			
		||||
        <resources>
 | 
			
		||||
            <resource>
 | 
			
		||||
                <directory>src/main/scripts</directory>
 | 
			
		||||
                <filtering>true</filtering>
 | 
			
		||||
                <targetPath>${project.build.directory}</targetPath>
 | 
			
		||||
            </resource>
 | 
			
		||||
        </resources>
 | 
			
		||||
        <plugins>
 | 
			
		||||
            <plugin>
 | 
			
		||||
                <groupId>org.apache.maven.plugins</groupId>
 | 
			
		||||
                <artifactId>maven-antrun-plugin</artifactId>
 | 
			
		||||
                <version>1.7</version>
 | 
			
		||||
                <executions>
 | 
			
		||||
                    <execution>
 | 
			
		||||
                        <id>prepare-package</id>
 | 
			
		||||
                        <phase>prepare-package</phase>
 | 
			
		||||
                        <configuration>
 | 
			
		||||
                            <target>
 | 
			
		||||
                                <chmod file="${project.build.directory}/install" perm="755" />
 | 
			
		||||
                            </target>
 | 
			
		||||
                        </configuration>
 | 
			
		||||
                        <goals>
 | 
			
		||||
                            <goal>run</goal>
 | 
			
		||||
                        </goals>
 | 
			
		||||
                    </execution>
 | 
			
		||||
                </executions>
 | 
			
		||||
            </plugin>
 | 
			
		||||
            <plugin>
 | 
			
		||||
                <groupId>org.apache.maven.plugins</groupId>
 | 
			
		||||
                <artifactId>maven-shade-plugin</artifactId>
 | 
			
		||||
                <version>2.2</version>
 | 
			
		||||
                <executions>
 | 
			
		||||
                    <execution>
 | 
			
		||||
                        <phase>package</phase>
 | 
			
		||||
                        <goals>
 | 
			
		||||
                            <goal>shade</goal>
 | 
			
		||||
                        </goals>
 | 
			
		||||
                        <configuration>
 | 
			
		||||
                            <transformers>
 | 
			
		||||
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
 | 
			
		||||
                                    <mainClass>com.lyndir.masterpassword.CLI</mainClass>
 | 
			
		||||
                                </transformer>
 | 
			
		||||
                            </transformers>
 | 
			
		||||
                            <filters>
 | 
			
		||||
                                <filter>
 | 
			
		||||
                                    <artifact>*:*</artifact>
 | 
			
		||||
                                    <excludes>
 | 
			
		||||
                                        <exclude>META-INF/*.SF</exclude>
 | 
			
		||||
                                        <exclude>META-INF/*.DSA</exclude>
 | 
			
		||||
                                        <exclude>META-INF/*.RSA</exclude>
 | 
			
		||||
                                    </excludes>
 | 
			
		||||
                                </filter>
 | 
			
		||||
                            </filters>
 | 
			
		||||
                        </configuration>
 | 
			
		||||
                    </execution>
 | 
			
		||||
                </executions>
 | 
			
		||||
            </plugin>
 | 
			
		||||
        </plugins>
 | 
			
		||||
    </build>
 | 
			
		||||
 | 
			
		||||
    <!-- DEPENDENCY MANAGEMENT -->
 | 
			
		||||
    <dependencies>
 | 
			
		||||
 | 
			
		||||
        <!-- PROJECT REFERENCES -->
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>com.lyndir.masterpassword</groupId>
 | 
			
		||||
            <artifactId>masterpassword-algorithm</artifactId>
 | 
			
		||||
            <version>GIT-SNAPSHOT</version>
 | 
			
		||||
        </dependency>
 | 
			
		||||
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>ch.qos.logback</groupId>
 | 
			
		||||
            <artifactId>logback-classic</artifactId>
 | 
			
		||||
        </dependency>
 | 
			
		||||
 | 
			
		||||
    </dependencies>
 | 
			
		||||
 | 
			
		||||
</project>
 | 
			
		||||
@@ -1,189 +0,0 @@
 | 
			
		||||
//==============================================================================
 | 
			
		||||
// This file is part of Master Password.
 | 
			
		||||
// Copyright (c) 2011-2017, Maarten Billemont.
 | 
			
		||||
//
 | 
			
		||||
// Master Password is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// (at your option) any later version.
 | 
			
		||||
//
 | 
			
		||||
// Master Password is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You can find a copy of the GNU General Public License in the
 | 
			
		||||
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
package com.lyndir.masterpassword;
 | 
			
		||||
 | 
			
		||||
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.ifNotNullElse;
 | 
			
		||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
 | 
			
		||||
 | 
			
		||||
import com.google.common.base.Charsets;
 | 
			
		||||
import com.google.common.base.Joiner;
 | 
			
		||||
import com.google.common.collect.Maps;
 | 
			
		||||
import com.google.common.io.LineReader;
 | 
			
		||||
import com.google.common.primitives.UnsignedInteger;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.util.ConversionUtils;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.util.StringUtils;
 | 
			
		||||
import java.io.*;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p> <i>Jun 10, 2008</i> </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author mbillemo
 | 
			
		||||
 */
 | 
			
		||||
@SuppressWarnings({ "UseOfSystemOutOrSystemErr", "HardCodedStringLiteral" })
 | 
			
		||||
public final class CLI {
 | 
			
		||||
 | 
			
		||||
    public static void main(final String... args)
 | 
			
		||||
            throws IOException {
 | 
			
		||||
 | 
			
		||||
        // Read information from the environment.
 | 
			
		||||
        char[] masterPassword;
 | 
			
		||||
        String siteName = null, context = null;
 | 
			
		||||
        String userName = System.getenv( MPConstant.env_userName );
 | 
			
		||||
        String siteTypeName = ifNotNullElse( System.getenv( MPConstant.env_siteType ), "" );
 | 
			
		||||
        MPSiteType siteType = siteTypeName.isEmpty()? MPSiteType.GeneratedLong: MPSiteType.forOption( siteTypeName );
 | 
			
		||||
        MPSiteVariant variant = MPSiteVariant.Password;
 | 
			
		||||
        String siteCounterName = ifNotNullElse( System.getenv( MPConstant.env_siteCounter ), "" );
 | 
			
		||||
        UnsignedInteger siteCounter = siteCounterName.isEmpty()? UnsignedInteger.valueOf( 1 ): UnsignedInteger.valueOf( siteCounterName );
 | 
			
		||||
 | 
			
		||||
        // Parse information from option arguments.
 | 
			
		||||
        boolean userNameArg = false, typeArg = false, counterArg = false, variantArg = false, contextArg = false;
 | 
			
		||||
        for (final String arg : Arrays.asList( args ))
 | 
			
		||||
            // Full Name
 | 
			
		||||
            if ("-u".equals( arg ) || "--username".equals( arg ))
 | 
			
		||||
                userNameArg = true;
 | 
			
		||||
            else if (userNameArg) {
 | 
			
		||||
                userName = arg;
 | 
			
		||||
                userNameArg = false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Type
 | 
			
		||||
            else if ("-t".equals( arg ) || "--type".equals( arg ))
 | 
			
		||||
                typeArg = true;
 | 
			
		||||
            else if (typeArg) {
 | 
			
		||||
                siteType = MPSiteType.forOption( arg );
 | 
			
		||||
                typeArg = false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Counter
 | 
			
		||||
            else if ("-c".equals( arg ) || "--counter".equals( arg ))
 | 
			
		||||
                counterArg = true;
 | 
			
		||||
            else if (counterArg) {
 | 
			
		||||
                siteCounter = UnsignedInteger.valueOf( arg );
 | 
			
		||||
                counterArg = false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Variant
 | 
			
		||||
            else if ("-v".equals( arg ) || "--variant".equals( arg ))
 | 
			
		||||
                variantArg = true;
 | 
			
		||||
            else if (variantArg) {
 | 
			
		||||
                variant = MPSiteVariant.forOption( arg );
 | 
			
		||||
                variantArg = false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Context
 | 
			
		||||
            else if ("-C".equals( arg ) || "--context".equals( arg ))
 | 
			
		||||
                contextArg = true;
 | 
			
		||||
            else if (contextArg) {
 | 
			
		||||
                context = arg;
 | 
			
		||||
                contextArg = false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Help
 | 
			
		||||
            else if ("-h".equals( arg ) || "--help".equals( arg )) {
 | 
			
		||||
                System.out.println();
 | 
			
		||||
                System.out.format( "Usage: mpw [-u name] [-t type] [-c counter] site%n%n" );
 | 
			
		||||
                System.out.format( "    -u name      Specify the full name of the user.%n" );
 | 
			
		||||
                System.out.format( "                 Defaults to %s in env.%n%n", MPConstant.env_userName );
 | 
			
		||||
                System.out.format( "    -t type      Specify the password's template.%n" );
 | 
			
		||||
                System.out.format( "                 Defaults to %s in env or 'long' for password, 'name' for login.%n", MPConstant.env_siteType );
 | 
			
		||||
 | 
			
		||||
                int optionsLength = 0;
 | 
			
		||||
                Map<String, MPSiteType> typeMap = Maps.newLinkedHashMap();
 | 
			
		||||
                for (final MPSiteType elementType : MPSiteType.values()) {
 | 
			
		||||
                    String options = Joiner.on( ", " ).join( elementType.getOptions() );
 | 
			
		||||
                    typeMap.put( options, elementType );
 | 
			
		||||
                    optionsLength = Math.max( optionsLength, options.length() );
 | 
			
		||||
                }
 | 
			
		||||
                for (final Map.Entry<String, MPSiteType> entry : typeMap.entrySet()) {
 | 
			
		||||
                    String infoString = strf( "                  -v %" + optionsLength + "s | ", entry.getKey() );
 | 
			
		||||
                    String infoNewline = strf( "%n%s | ", StringUtils.repeat( " ", infoString.length() - 3 ) );
 | 
			
		||||
                    infoString += entry.getValue().getDescription().replaceAll( strf( "%n" ), infoNewline );
 | 
			
		||||
                    System.out.println( infoString );
 | 
			
		||||
                }
 | 
			
		||||
                System.out.println();
 | 
			
		||||
 | 
			
		||||
                System.out.format( "    -c counter   The value of the counter.%n" );
 | 
			
		||||
                System.out.format( "                 Defaults to %s in env or '1'.%n%n", MPConstant.env_siteCounter );
 | 
			
		||||
                System.out.format( "    -v variant   The kind of content to generate.%n" );
 | 
			
		||||
                System.out.format( "                 Defaults to 'password'.%n" );
 | 
			
		||||
 | 
			
		||||
                optionsLength = 0;
 | 
			
		||||
                Map<String, MPSiteVariant> variantMap = Maps.newLinkedHashMap();
 | 
			
		||||
                for (final MPSiteVariant elementVariant : MPSiteVariant.values()) {
 | 
			
		||||
                    String options = Joiner.on( ", " ).join( elementVariant.getOptions() );
 | 
			
		||||
                    variantMap.put( options, elementVariant );
 | 
			
		||||
                    optionsLength = Math.max( optionsLength, options.length() );
 | 
			
		||||
                }
 | 
			
		||||
                for (final Map.Entry<String, MPSiteVariant> entry : variantMap.entrySet()) {
 | 
			
		||||
                    String infoString = strf( "                  -v %" + optionsLength + "s | ", entry.getKey() );
 | 
			
		||||
                    String infoNewline = strf( "%n%s | ", StringUtils.repeat( " ", infoString.length() - 3 ) );
 | 
			
		||||
                    infoString += entry.getValue().getDescription().replaceAll( strf( "%n" ), infoNewline );
 | 
			
		||||
                    System.out.println( infoString );
 | 
			
		||||
                }
 | 
			
		||||
                System.out.println();
 | 
			
		||||
 | 
			
		||||
                System.out.format( "    -C context   A variant-specific context.%n" );
 | 
			
		||||
                System.out.format( "                 Defaults to empty.%n" );
 | 
			
		||||
                for (final Map.Entry<String, MPSiteVariant> entry : variantMap.entrySet()) {
 | 
			
		||||
                    String infoString = strf( "                  -v %" + optionsLength + "s | ", entry.getKey() );
 | 
			
		||||
                    String infoNewline = strf( "%n%s | ", StringUtils.repeat( " ", infoString.length() - 3 ) );
 | 
			
		||||
                    infoString += entry.getValue().getContextDescription().replaceAll( strf( "%n" ), infoNewline );
 | 
			
		||||
                    System.out.println( infoString );
 | 
			
		||||
                }
 | 
			
		||||
                System.out.println();
 | 
			
		||||
 | 
			
		||||
                System.out.format( "    ENVIRONMENT%n%n" );
 | 
			
		||||
                System.out.format( "        MP_USERNAME    | The full name of the user.%n" );
 | 
			
		||||
                System.out.format( "        MP_SITETYPE    | The default password template.%n" );
 | 
			
		||||
                System.out.format( "        MP_SITECOUNTER | The default counter value.%n%n" );
 | 
			
		||||
                return;
 | 
			
		||||
            } else
 | 
			
		||||
                siteName = arg;
 | 
			
		||||
 | 
			
		||||
        // Read missing information from the console.
 | 
			
		||||
        Console console = System.console();
 | 
			
		||||
        try (InputStreamReader inReader = new InputStreamReader( System.in, Charsets.UTF_8 )) {
 | 
			
		||||
            LineReader lineReader = new LineReader( inReader );
 | 
			
		||||
 | 
			
		||||
            if (siteName == null) {
 | 
			
		||||
                System.err.format( "Site name: " );
 | 
			
		||||
                siteName = lineReader.readLine();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (userName == null) {
 | 
			
		||||
                System.err.format( "User's name: " );
 | 
			
		||||
                userName = lineReader.readLine();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (console != null)
 | 
			
		||||
                masterPassword = console.readPassword( "%s's master password: ", userName );
 | 
			
		||||
 | 
			
		||||
            else {
 | 
			
		||||
                System.err.format( "%s's master password: ", userName );
 | 
			
		||||
                masterPassword = lineReader.readLine().toCharArray();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Encode and write out the site password.
 | 
			
		||||
        System.out.println( MasterKey.create( userName, masterPassword ).encode( siteName, siteType, siteCounter, variant, context ) );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,28 +0,0 @@
 | 
			
		||||
//==============================================================================
 | 
			
		||||
// This file is part of Master Password.
 | 
			
		||||
// Copyright (c) 2011-2017, Maarten Billemont.
 | 
			
		||||
//
 | 
			
		||||
// Master Password is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// (at your option) any later version.
 | 
			
		||||
//
 | 
			
		||||
// Master Password is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You can find a copy of the GNU General Public License in the
 | 
			
		||||
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @author lhunath, 15-02-04
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ParametersAreNonnullByDefault
 | 
			
		||||
package com.lyndir.masterpassword;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.ParametersAreNonnullByDefault;
 | 
			
		||||
@@ -1,15 +0,0 @@
 | 
			
		||||
<configuration scan="false">
 | 
			
		||||
 | 
			
		||||
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
 | 
			
		||||
        <encoder>
 | 
			
		||||
            <pattern>%-8relative %22c{0} [%-5level] %msg%n</pattern>
 | 
			
		||||
        </encoder>
 | 
			
		||||
    </appender>
 | 
			
		||||
 | 
			
		||||
    <logger name="com.lyndir" level="${mp.log.level:-INFO}" />
 | 
			
		||||
 | 
			
		||||
    <root level="INFO">
 | 
			
		||||
        <appender-ref ref="stdout" />
 | 
			
		||||
    </root>
 | 
			
		||||
 | 
			
		||||
</configuration>
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,56 +0,0 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
#
 | 
			
		||||
# Install the Master Password CLI tool.
 | 
			
		||||
set -e
 | 
			
		||||
cd "${BASH_SOURCE%/*}"
 | 
			
		||||
source bashlib
 | 
			
		||||
 | 
			
		||||
inf "This will install the mpw tool."
 | 
			
		||||
 | 
			
		||||
# Try to guess then ask for the bin dir to install to.
 | 
			
		||||
IFS=: read -a paths <<< "$PATH"
 | 
			
		||||
if inArray ~/bin "${paths[@]}"; then
 | 
			
		||||
    bindir=~/bin
 | 
			
		||||
elif inArray ~/.bin "${paths[@]}"; then
 | 
			
		||||
    bindir=~/.bin
 | 
			
		||||
elif inArray /usr/local/bin "${paths[@]}"; then
 | 
			
		||||
    bindir=/usr/local/bin
 | 
			
		||||
else
 | 
			
		||||
    bindir=~/bin
 | 
			
		||||
fi
 | 
			
		||||
bindir=$(ask -d "$bindir" "What bin directory should I install to?")
 | 
			
		||||
[[ -d "$bindir" ]] || mkdir "$bindir" || ftl 'Cannot create missing bin directory: %s' "$bindir" || exit
 | 
			
		||||
[[ -w "$bindir" ]] || ftl 'Cannot write to bin directory: %s' "$bindir" || exit
 | 
			
		||||
 | 
			
		||||
# Try to guess then ask for the share dir to install to.
 | 
			
		||||
sharedir=$(cd -P "$bindir/.."; [[ $bindir = */.bin ]] && printf '%s/.share' "$PWD" || printf '%s/share' "$PWD")
 | 
			
		||||
sharedir=$(ask -d "$sharedir" "What share directory should I install to?")
 | 
			
		||||
[[ -d "$sharedir" ]] || mkdir "$sharedir" || ftl 'Cannot create missing share directory: %s' "$sharedir" || exit
 | 
			
		||||
[[ -w "$sharedir" ]] || ftl 'Cannot write to share directory: %s' "$sharedir" || exit
 | 
			
		||||
 | 
			
		||||
# Install Master Password.
 | 
			
		||||
sharepath=$sharedir/masterpassword
 | 
			
		||||
mkdir -p "$sharepath"
 | 
			
		||||
cp -a "masterpassword-cli-"*".jar" bashlib mpw "$sharepath"
 | 
			
		||||
ex -c "%s~%SHAREPATH%~$sharepath~g|x" "$sharepath/mpw"
 | 
			
		||||
ln -sf "$sharepath/mpw" "$bindir/mpw"
 | 
			
		||||
chmod +x "$sharepath/mpw"
 | 
			
		||||
[[ ! -e "$bindir/bashlib" ]] && ln -s "$sharepath/bashlib" "$bindir/bashlib" ||:
 | 
			
		||||
 | 
			
		||||
# Convenience bash function.
 | 
			
		||||
inf "Installation successful!"
 | 
			
		||||
echo
 | 
			
		||||
inf "To improve usability, you can install an mpw function in your bash shell."
 | 
			
		||||
inf "This function adds the following features:"
 | 
			
		||||
inf "  - Ask the Master Password once, remember in the shell."
 | 
			
		||||
inf "  - Automatically put the password in the clipboard (some platforms)."
 | 
			
		||||
echo
 | 
			
		||||
inf "To do this you need the following function in ~/.bashrc:\n%s" "$(<mpw.bashrc)"
 | 
			
		||||
echo
 | 
			
		||||
inf "We can do this for you automatically now."
 | 
			
		||||
if ask -c Y!n "Append the mpw function to your .bashrc?"; then
 | 
			
		||||
    cat mpw.bashrc >> ~/.bashrc
 | 
			
		||||
    inf "Done!  Don't forget to run '%s' to apply the changes!" "source ~/.bashrc"
 | 
			
		||||
fi
 | 
			
		||||
echo
 | 
			
		||||
inf "To begin using Master Password, type: mpw [site name]"
 | 
			
		||||
@@ -1,9 +0,0 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
 | 
			
		||||
# Uncomment this to set your user name.  Master Password will no longer ask you for a user name.
 | 
			
		||||
# export MP_USERNAME="Robert Lee Mitchell"
 | 
			
		||||
# Uncomment this to hardcode your master password.  Make sure this file's permissions are tight.  Master Password will not ask you for your master password anymore.  This is probably not a good idea.
 | 
			
		||||
# export MP_PASSWORD="banana colored duckling"
 | 
			
		||||
 | 
			
		||||
cd "%SHAREPATH%"
 | 
			
		||||
exec java -jar masterpassword-cli-GIT-SNAPSHOT.jar "$@"
 | 
			
		||||
@@ -1,15 +0,0 @@
 | 
			
		||||
source bashlib
 | 
			
		||||
mpw() {
 | 
			
		||||
    _nocopy() { echo >&2 "$(cat)"; }
 | 
			
		||||
    _copy() { "$(type -P pbcopy || type -P xclip || echo _nocopy)"; }
 | 
			
		||||
 | 
			
		||||
    # Empty the clipboard
 | 
			
		||||
    :| _copy 2>/dev/null
 | 
			
		||||
 | 
			
		||||
    # Ask for the user's name and password if not yet known.
 | 
			
		||||
    MP_USERNAME=${MP_USERNAME:-$(ask -s 'Your Full Name:')}
 | 
			
		||||
    MP_PASSWORD=${MP_PASSWORD:-$(ask -s 'Master Password:')}
 | 
			
		||||
 | 
			
		||||
    # Start Master Password and copy the output.
 | 
			
		||||
    printf %s "$(MP_USERNAME=$MP_USERNAME MP_PASSWORD=$MP_PASSWORD command mpw "$@")" | _copy
 | 
			
		||||
}
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
package com.lyndir.masterpassword.gui.model;
 | 
			
		||||
 | 
			
		||||
import com.google.common.primitives.UnsignedInteger;
 | 
			
		||||
import com.lyndir.masterpassword.MPSiteType;
 | 
			
		||||
import com.lyndir.masterpassword.MPResultType;
 | 
			
		||||
import com.lyndir.masterpassword.MasterKey;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -11,15 +11,15 @@ import com.lyndir.masterpassword.MasterKey;
 | 
			
		||||
public class IncognitoSite extends Site {
 | 
			
		||||
 | 
			
		||||
    private String            siteName;
 | 
			
		||||
    private MPSiteType        siteType;
 | 
			
		||||
    private UnsignedInteger   siteCounter;
 | 
			
		||||
    private MPResultType      resultType;
 | 
			
		||||
    private MasterKey.Version algorithmVersion;
 | 
			
		||||
 | 
			
		||||
    public IncognitoSite(final String siteName, final MPSiteType siteType, final UnsignedInteger siteCounter,
 | 
			
		||||
    public IncognitoSite(final String siteName, final UnsignedInteger siteCounter, final MPResultType resultType,
 | 
			
		||||
                         final MasterKey.Version algorithmVersion) {
 | 
			
		||||
        this.siteName = siteName;
 | 
			
		||||
        this.siteType = siteType;
 | 
			
		||||
        this.siteCounter = siteCounter;
 | 
			
		||||
        this.resultType = resultType;
 | 
			
		||||
        this.algorithmVersion = algorithmVersion;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -34,13 +34,13 @@ public class IncognitoSite extends Site {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public MPSiteType getSiteType() {
 | 
			
		||||
        return siteType;
 | 
			
		||||
    public MPResultType getResultType() {
 | 
			
		||||
        return resultType;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setSiteType(final MPSiteType siteType) {
 | 
			
		||||
        this.siteType = siteType;
 | 
			
		||||
    public void setResultType(final MPResultType resultType) {
 | 
			
		||||
        this.resultType = resultType;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
package com.lyndir.masterpassword.gui.model;
 | 
			
		||||
 | 
			
		||||
import com.google.common.primitives.UnsignedInteger;
 | 
			
		||||
import com.lyndir.masterpassword.MPSiteType;
 | 
			
		||||
import com.lyndir.masterpassword.MPResultType;
 | 
			
		||||
import com.lyndir.masterpassword.MasterKey;
 | 
			
		||||
import com.lyndir.masterpassword.model.*;
 | 
			
		||||
 | 
			
		||||
@@ -33,14 +33,14 @@ public class ModelSite extends Site {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public MPSiteType getSiteType() {
 | 
			
		||||
        return model.getSiteType();
 | 
			
		||||
    public MPResultType getResultType() {
 | 
			
		||||
        return model.getResultType();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setSiteType(final MPSiteType siteType) {
 | 
			
		||||
        if (siteType != getSiteType()) {
 | 
			
		||||
            model.setSiteType( siteType );
 | 
			
		||||
    public void setResultType(final MPResultType resultType) {
 | 
			
		||||
        if (resultType != getResultType()) {
 | 
			
		||||
            model.setResultType( resultType );
 | 
			
		||||
            MPUserFileManager.get().save();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -79,7 +79,7 @@ public class ModelUser extends User {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void addSite(final Site site) {
 | 
			
		||||
        model.addSite( new MPSite( model, site.getSiteName(), site.getSiteType(), site.getSiteCounter() ) );
 | 
			
		||||
        model.addSite( new MPSite( model, site.getSiteName(), site.getSiteCounter(), site.getResultType() ) );
 | 
			
		||||
        model.updateLastUsed();
 | 
			
		||||
        MPUserFileManager.get().save();
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@ package com.lyndir.masterpassword.gui.model;
 | 
			
		||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
 | 
			
		||||
 | 
			
		||||
import com.google.common.primitives.UnsignedInteger;
 | 
			
		||||
import com.lyndir.masterpassword.MPSiteType;
 | 
			
		||||
import com.lyndir.masterpassword.MPResultType;
 | 
			
		||||
import com.lyndir.masterpassword.MasterKey;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -16,9 +16,9 @@ public abstract class Site {
 | 
			
		||||
 | 
			
		||||
    public abstract void setSiteName(String siteName);
 | 
			
		||||
 | 
			
		||||
    public abstract MPSiteType getSiteType();
 | 
			
		||||
    public abstract MPResultType getResultType();
 | 
			
		||||
 | 
			
		||||
    public abstract void setSiteType(MPSiteType siteType);
 | 
			
		||||
    public abstract void setResultType(MPResultType resultType);
 | 
			
		||||
 | 
			
		||||
    public abstract MasterKey.Version getAlgorithmVersion();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -32,10 +32,10 @@ public class PasswordFrame extends JFrame implements DocumentListener {
 | 
			
		||||
    private final Components.GradientPanel     root;
 | 
			
		||||
    private final JTextField                   siteNameField;
 | 
			
		||||
    private final JButton                      siteActionButton;
 | 
			
		||||
    private final JComboBox<MPSiteType>        siteTypeField;
 | 
			
		||||
    private final JComboBox<MasterKey.Version> siteVersionField;
 | 
			
		||||
    private final JSpinner                     siteCounterField;
 | 
			
		||||
    private final UnsignedIntegerModel         siteCounterModel;
 | 
			
		||||
    private final JComboBox<MPResultType>      resultTypeField;
 | 
			
		||||
    private final JPasswordField               passwordField;
 | 
			
		||||
    private final JLabel                       tipLabel;
 | 
			
		||||
    private final JCheckBox                    maskPasswordField;
 | 
			
		||||
@@ -118,17 +118,17 @@ public class PasswordFrame extends JFrame implements DocumentListener {
 | 
			
		||||
 | 
			
		||||
        // Site Type & Counter
 | 
			
		||||
        siteCounterModel = new UnsignedIntegerModel( UnsignedInteger.ONE, UnsignedInteger.ONE );
 | 
			
		||||
        MPSiteType[] types = Iterables.toArray( MPSiteType.forClass( MPSiteTypeClass.Generated ), MPSiteType.class );
 | 
			
		||||
        MPResultType[] types = Iterables.toArray( MPResultType.forClass( MPResultTypeClass.Generated ), MPResultType.class );
 | 
			
		||||
        JComponent siteSettings = Components.boxLayout( BoxLayout.LINE_AXIS,                                                  //
 | 
			
		||||
                                                        siteTypeField = Components.comboBox( types ),                         //
 | 
			
		||||
                                                        resultTypeField = Components.comboBox( types ),                         //
 | 
			
		||||
                                                        Components.stud(),                                                    //
 | 
			
		||||
                                                        siteVersionField = Components.comboBox( MasterKey.Version.values() ), //
 | 
			
		||||
                                                        Components.stud(),                                                    //
 | 
			
		||||
                                                        siteCounterField = Components.spinner( siteCounterModel ) );
 | 
			
		||||
        sitePanel.add( siteSettings );
 | 
			
		||||
        siteTypeField.setFont( Res.valueFont().deriveFont( 12f ) );
 | 
			
		||||
        siteTypeField.setSelectedItem( MPSiteType.GeneratedLong );
 | 
			
		||||
        siteTypeField.addItemListener( new ItemListener() {
 | 
			
		||||
        resultTypeField.setFont( Res.valueFont().deriveFont( 12f ) );
 | 
			
		||||
        resultTypeField.setSelectedItem( MPResultType.GeneratedLong );
 | 
			
		||||
        resultTypeField.addItemListener( new ItemListener() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void itemStateChanged(final ItemEvent e) {
 | 
			
		||||
                updatePassword( true );
 | 
			
		||||
@@ -215,7 +215,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
 | 
			
		||||
            return Futures.immediateCancelledFuture();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        MPSiteType        siteType    = siteTypeField.getModel().getElementAt( siteTypeField.getSelectedIndex() );
 | 
			
		||||
        MPResultType      resultType    = resultTypeField.getModel().getElementAt( resultTypeField.getSelectedIndex() );
 | 
			
		||||
        MasterKey.Version siteVersion = siteVersionField.getItemAt( siteVersionField.getSelectedIndex() );
 | 
			
		||||
        UnsignedInteger   siteCounter = siteCounterModel.getNumber();
 | 
			
		||||
 | 
			
		||||
@@ -228,9 +228,9 @@ public class PasswordFrame extends JFrame implements DocumentListener {
 | 
			
		||||
                }
 | 
			
		||||
            } );
 | 
			
		||||
        final Site site = ifNotNullElse( Iterables.getFirst( siteResults, null ),
 | 
			
		||||
                                         new IncognitoSite( siteNameQuery, siteType, siteCounter, siteVersion ) );
 | 
			
		||||
                                         new IncognitoSite( siteNameQuery, siteCounter, resultType, siteVersion ) );
 | 
			
		||||
        if ((currentSite != null) && currentSite.getSiteName().equals( site.getSiteName() )) {
 | 
			
		||||
            site.setSiteType( siteType );
 | 
			
		||||
            site.setResultType( resultType );
 | 
			
		||||
            site.setAlgorithmVersion( siteVersion );
 | 
			
		||||
            site.setSiteCounter( siteCounter );
 | 
			
		||||
        }
 | 
			
		||||
@@ -240,7 +240,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
 | 
			
		||||
            public String call()
 | 
			
		||||
                    throws Exception {
 | 
			
		||||
                return user.getKey( site.getAlgorithmVersion() )
 | 
			
		||||
                           .encode( site.getSiteName(), site.getSiteType(), site.getSiteCounter(), MPSiteVariant.Password, null );
 | 
			
		||||
                           .siteResult( site.getSiteName(), site.getSiteCounter(), MPKeyPurpose.Password, null, site.getResultType(), null );
 | 
			
		||||
            }
 | 
			
		||||
        } );
 | 
			
		||||
        Futures.addCallback( passwordFuture, new FutureCallback<String>() {
 | 
			
		||||
@@ -256,7 +256,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
 | 
			
		||||
                            siteActionButton.setText( "Delete Site" );
 | 
			
		||||
                        else
 | 
			
		||||
                            siteActionButton.setText( "Add Site" );
 | 
			
		||||
                        siteTypeField.setSelectedItem( currentSite.getSiteType() );
 | 
			
		||||
                        resultTypeField.setSelectedItem( currentSite.getResultType() );
 | 
			
		||||
                        siteVersionField.setSelectedItem( currentSite.getAlgorithmVersion() );
 | 
			
		||||
                        siteCounterField.setValue( currentSite.getSiteCounter() );
 | 
			
		||||
                        siteNameField.setText( currentSite.getSiteName() );
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user