Marshal refactoring to prepare for new format.
This commit is contained in:
		@@ -18,6 +18,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.lyndir.masterpassword;
 | 
					package com.lyndir.masterpassword;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.joda.time.format.DateTimeFormatter;
 | 
				
			||||||
 | 
					import org.joda.time.format.ISODateTimeFormat;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @author lhunath, 2016-10-29
 | 
					 * @author lhunath, 2016-10-29
 | 
				
			||||||
@@ -38,4 +41,6 @@ public final class MPConstant {
 | 
				
			|||||||
    /* Algorithm */
 | 
					    /* Algorithm */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static final int MS_PER_S = 1000;
 | 
					    public static final int MS_PER_S = 1000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final DateTimeFormatter dateTimeFormatter = ISODateTimeFormat.dateTimeNoMillis();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,41 @@
 | 
				
			|||||||
 | 
					//==============================================================================
 | 
				
			||||||
 | 
					// 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.primitives.UnsignedInteger;
 | 
				
			||||||
 | 
					import java.nio.ByteBuffer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @author lhunath, 2017-09-20
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class MPUtils {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static byte[] bytesForInt(final int number) {
 | 
				
			||||||
 | 
					        return ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( MasterKeyV0.mpw_byteOrder ).putInt( number ).array();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static byte[] bytesForInt(final UnsignedInteger number) {
 | 
				
			||||||
 | 
					        return ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( MasterKeyV0.mpw_byteOrder ).putInt( number.intValue() ).array();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static byte[] idForBytes(final byte[] bytes) {
 | 
				
			||||||
 | 
					        return MasterKeyV0.mpw_hash.of( bytes );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -18,13 +18,11 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.lyndir.masterpassword;
 | 
					package com.lyndir.masterpassword;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
 | 
					import static com.lyndir.masterpassword.MPUtils.idForBytes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.google.common.base.Preconditions;
 | 
					import com.google.common.base.Preconditions;
 | 
				
			||||||
import com.google.common.primitives.UnsignedInteger;
 | 
					import com.google.common.primitives.UnsignedInteger;
 | 
				
			||||||
import com.lyndir.lhunath.opal.system.*;
 | 
					 | 
				
			||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
					import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
				
			||||||
import java.util.Arrays;
 | 
					 | 
				
			||||||
import javax.annotation.Nonnull;
 | 
					import javax.annotation.Nonnull;
 | 
				
			||||||
import javax.annotation.Nullable;
 | 
					import javax.annotation.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -32,86 +30,25 @@ import javax.annotation.Nullable;
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * @author lhunath, 2014-08-30
 | 
					 * @author lhunath, 2014-08-30
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public abstract class MasterKey {
 | 
					public class MasterKey {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @SuppressWarnings("UnusedDeclaration")
 | 
					    @SuppressWarnings("UnusedDeclaration")
 | 
				
			||||||
    private static final Logger  logger               = Logger.get( MasterKey.class );
 | 
					    private static final Logger logger = Logger.get( MasterKey.class );
 | 
				
			||||||
    private static       boolean allowNativeByDefault = true;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Nonnull
 | 
					 | 
				
			||||||
    private final String fullName;
 | 
					    private final String fullName;
 | 
				
			||||||
    private boolean allowNative = allowNativeByDefault;
 | 
					    private final char[] masterPassword;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Nullable
 | 
					    @SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter")
 | 
				
			||||||
    private byte[] masterKey;
 | 
					    public MasterKey(final String fullName, final char[] masterPassword) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    @SuppressWarnings("MethodCanBeVariableArityMethod")
 | 
					 | 
				
			||||||
    public static MasterKey create(final String fullName, final char[] masterPassword) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return create( Version.CURRENT, fullName, masterPassword );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nonnull
 | 
					 | 
				
			||||||
    @SuppressWarnings("MethodCanBeVariableArityMethod")
 | 
					 | 
				
			||||||
    public static MasterKey create(final Version version, final String fullName, final char[] masterPassword) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        switch (version) {
 | 
					 | 
				
			||||||
            case V0:
 | 
					 | 
				
			||||||
                return new MasterKeyV0( fullName ).revalidate( masterPassword );
 | 
					 | 
				
			||||||
            case V1:
 | 
					 | 
				
			||||||
                return new MasterKeyV1( fullName ).revalidate( masterPassword );
 | 
					 | 
				
			||||||
            case V2:
 | 
					 | 
				
			||||||
                return new MasterKeyV2( fullName ).revalidate( masterPassword );
 | 
					 | 
				
			||||||
            case V3:
 | 
					 | 
				
			||||||
                return new MasterKeyV3( fullName ).revalidate( masterPassword );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        throw new UnsupportedOperationException( strf( "Unsupported version: %s", version ) );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public static boolean isAllowNativeByDefault() {
 | 
					 | 
				
			||||||
        return allowNativeByDefault;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Native libraries are useful for speeding up the performance of cryptographical functions.
 | 
					 | 
				
			||||||
     * Sometimes, however, we may prefer to use Java-only code.
 | 
					 | 
				
			||||||
     * For instance, for auditability / trust or because the native code doesn't work on our CPU/platform.
 | 
					 | 
				
			||||||
     * <p/>
 | 
					 | 
				
			||||||
     * This setter affects the default setting for any newly created {@link MasterKey}s.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param allowNative false to disallow the use of native libraries.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public static void setAllowNativeByDefault(final boolean allowNative) {
 | 
					 | 
				
			||||||
        allowNativeByDefault = allowNative;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected MasterKey(final String fullName) {
 | 
					 | 
				
			||||||
        Preconditions.checkArgument( !fullName.isEmpty() );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.fullName = fullName;
 | 
					        this.fullName = fullName;
 | 
				
			||||||
        logger.trc( "fullName: %s", fullName );
 | 
					        this.masterPassword = masterPassword;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    private byte[] getKey(final Version algorithmVersion) {
 | 
				
			||||||
     * Derive the master key for a user based on their name and master password.
 | 
					        // TODO: Cache keys.
 | 
				
			||||||
     *
 | 
					        return algorithmVersion.getAlgorithm().deriveKey( fullName, masterPassword );
 | 
				
			||||||
     * @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.
 | 
					     * Generate a site result token.
 | 
				
			||||||
@@ -122,16 +59,14 @@ public abstract class MasterKey {
 | 
				
			|||||||
     * @param keyContext  A site-scoped result modifier.
 | 
					     * @param keyContext  A site-scoped result modifier.
 | 
				
			||||||
     * @param resultType  The type of result to generate.
 | 
					     * @param resultType  The type of result to generate.
 | 
				
			||||||
     * @param resultParam A parameter for the resultType.  For stateful result types, the output of
 | 
					     * @param resultParam A parameter for the resultType.  For stateful result types, the output of
 | 
				
			||||||
     *                    {@link #siteState(String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String)}.
 | 
					     *                    {@link #siteState(String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String, Version)}.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public abstract String siteResult(String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose,
 | 
					    public String siteResult(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
 | 
				
			||||||
                                      @Nullable String keyContext, MPResultType resultType, @Nullable String resultParam);
 | 
					                             @Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam,
 | 
				
			||||||
 | 
					                             final Version algorithmVersion) {
 | 
				
			||||||
    protected abstract String sitePasswordFromTemplate(byte[] siteKey, MPResultType resultType, @Nullable String resultParam);
 | 
					        return algorithmVersion.getAlgorithm().siteResult(
 | 
				
			||||||
 | 
					                getKey( algorithmVersion ), siteName, siteCounter, keyPurpose, keyContext, resultType, resultParam );
 | 
				
			||||||
    protected abstract String sitePasswordFromCrypt(byte[] siteKey, MPResultType resultType, @Nullable String resultParam);
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected abstract String sitePasswordFromDerive(byte[] siteKey, MPResultType resultType, @Nullable String resultParam);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Encrypt a stateful site token for persistence.
 | 
					     * Encrypt a stateful site token for persistence.
 | 
				
			||||||
@@ -142,12 +77,14 @@ public abstract class MasterKey {
 | 
				
			|||||||
     * @param keyContext  A site-scoped key modifier.
 | 
					     * @param keyContext  A site-scoped key modifier.
 | 
				
			||||||
     * @param resultType  The type of result token to encrypt.
 | 
					     * @param resultType  The type of result token to encrypt.
 | 
				
			||||||
     * @param resultParam The result token desired from
 | 
					     * @param resultParam The result token desired from
 | 
				
			||||||
     *                    {@link #siteResult(String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String)}.
 | 
					     *                    {@link #siteResult(String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String, Version)}.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public abstract String siteState(String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose,
 | 
					    public String siteState(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
 | 
				
			||||||
                                     @Nullable String keyContext, MPResultType resultType, @Nullable String resultParam);
 | 
					                            @Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam,
 | 
				
			||||||
 | 
					                            final Version algorithmVersion) {
 | 
				
			||||||
    public abstract Version getAlgorithmVersion();
 | 
					        return algorithmVersion.getAlgorithm().siteState(
 | 
				
			||||||
 | 
					                getKey( algorithmVersion ), siteName, siteCounter, keyPurpose, keyContext, resultType, resultParam );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Nonnull
 | 
					    @Nonnull
 | 
				
			||||||
    public String getFullName() {
 | 
					    public String getFullName() {
 | 
				
			||||||
@@ -155,63 +92,11 @@ public abstract class MasterKey {
 | 
				
			|||||||
        return fullName;
 | 
					        return fullName;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public boolean isAllowNative() {
 | 
					    public byte[] getKeyID(final Version algorithmVersion) {
 | 
				
			||||||
        return allowNative;
 | 
					
 | 
				
			||||||
 | 
					        return idForBytes( getKey( algorithmVersion ) );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public MasterKey setAllowNative(final boolean allowNative) {
 | 
					 | 
				
			||||||
        this.allowNative = allowNative;
 | 
					 | 
				
			||||||
        return this;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nonnull
 | 
					 | 
				
			||||||
    protected byte[] getKey() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Preconditions.checkState( isValid() );
 | 
					 | 
				
			||||||
        return Preconditions.checkNotNull( masterKey );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public byte[] getKeyID() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return idForBytes( getKey() );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public boolean isValid() {
 | 
					 | 
				
			||||||
        return masterKey != null;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public void invalidate() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (masterKey != null) {
 | 
					 | 
				
			||||||
            Arrays.fill( masterKey, (byte) 0 );
 | 
					 | 
				
			||||||
            masterKey = null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @SuppressWarnings("MethodCanBeVariableArityMethod")
 | 
					 | 
				
			||||||
    public MasterKey revalidate(final char[] masterPassword) {
 | 
					 | 
				
			||||||
        invalidate();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        logger.trc( "masterPassword: %s", new String( masterPassword ) );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        long start = System.currentTimeMillis();
 | 
					 | 
				
			||||||
        masterKey = deriveKey( masterPassword );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (masterKey == null)
 | 
					 | 
				
			||||||
            logger.dbg( "masterKey calculation failed after %.2fs.", (double) (System.currentTimeMillis() - start) / MPConstant.MS_PER_S );
 | 
					 | 
				
			||||||
        else
 | 
					 | 
				
			||||||
            logger.trc( "masterKey ID: %s (derived in %.2fs)", CodeUtils.encodeHex( idForBytes( masterKey ) ),
 | 
					 | 
				
			||||||
                        (double) (System.currentTimeMillis() - start) / MPConstant.MS_PER_S );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return this;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected abstract byte[] bytesForInt(int number);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected abstract byte[] bytesForInt(UnsignedInteger number);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected abstract byte[] idForBytes(byte[] bytes);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public enum Version {
 | 
					    public enum Version {
 | 
				
			||||||
        /**
 | 
					        /**
 | 
				
			||||||
         * bugs:
 | 
					         * bugs:
 | 
				
			||||||
@@ -219,26 +104,36 @@ public abstract class MasterKey {
 | 
				
			|||||||
         * - miscounted the byte-length for multi-byte site names.
 | 
					         * - miscounted the byte-length for multi-byte site names.
 | 
				
			||||||
         * - miscounted the byte-length for multi-byte full names.
 | 
					         * - miscounted the byte-length for multi-byte full names.
 | 
				
			||||||
         */
 | 
					         */
 | 
				
			||||||
        V0,
 | 
					        V0( new MasterKeyV0() ),
 | 
				
			||||||
        /**
 | 
					        /**
 | 
				
			||||||
         * bugs:
 | 
					         * bugs:
 | 
				
			||||||
         * - miscounted the byte-length for multi-byte site names.
 | 
					         * - miscounted the byte-length for multi-byte site names.
 | 
				
			||||||
         * - miscounted the byte-length for multi-byte full names.
 | 
					         * - miscounted the byte-length for multi-byte full names.
 | 
				
			||||||
         */
 | 
					         */
 | 
				
			||||||
        V1,
 | 
					        V1( new MasterKeyV1() ),
 | 
				
			||||||
        /**
 | 
					        /**
 | 
				
			||||||
         * bugs:
 | 
					         * bugs:
 | 
				
			||||||
         * - miscounted the byte-length for multi-byte full names.
 | 
					         * - miscounted the byte-length for multi-byte full names.
 | 
				
			||||||
         */
 | 
					         */
 | 
				
			||||||
        V2,
 | 
					        V2( new MasterKeyV2() ),
 | 
				
			||||||
        /**
 | 
					        /**
 | 
				
			||||||
         * bugs:
 | 
					         * bugs:
 | 
				
			||||||
         * - no known issues.
 | 
					         * - no known issues.
 | 
				
			||||||
         */
 | 
					         */
 | 
				
			||||||
        V3;
 | 
					        V3( new MasterKeyV3() );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static final Version CURRENT = V3;
 | 
					        public static final Version CURRENT = V3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private final MasterKeyAlgorithm algorithm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Version(final MasterKeyAlgorithm algorithm) {
 | 
				
			||||||
 | 
					            this.algorithm = algorithm;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public MasterKeyAlgorithm getAlgorithm() {
 | 
				
			||||||
 | 
					            return algorithm;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static Version fromInt(final int algorithmVersion) {
 | 
					        public static Version fromInt(final int algorithmVersion) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return values()[algorithmVersion];
 | 
					            return values()[algorithmVersion];
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,49 @@
 | 
				
			|||||||
 | 
					//==============================================================================
 | 
				
			||||||
 | 
					// 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.primitives.UnsignedInteger;
 | 
				
			||||||
 | 
					import java.io.Serializable;
 | 
				
			||||||
 | 
					import javax.annotation.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @see MasterKey.Version
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public interface MasterKeyAlgorithm extends Serializable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    MasterKey.Version getAlgorithmVersion();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    byte[] deriveKey(String fullName, char[] masterPassword);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    byte[] siteKey(byte[] masterKey, String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose,
 | 
				
			||||||
 | 
					                   @Nullable String keyContext);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    String siteResult(byte[] masterKey, String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose,
 | 
				
			||||||
 | 
					                      @Nullable String keyContext, MPResultType resultType, @Nullable String resultParam);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    String sitePasswordFromTemplate(byte[] masterKey, byte[] siteKey, MPResultType resultType, @Nullable String resultParam);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    String sitePasswordFromCrypt(byte[] masterKey, byte[] siteKey, MPResultType resultType, @Nullable String resultParam);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    String sitePasswordFromDerive(byte[] masterKey, byte[] siteKey, MPResultType resultType, @Nullable String resultParam);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    String siteState(byte[] masterKey, String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose,
 | 
				
			||||||
 | 
					                     @Nullable String keyContext, MPResultType resultType, @Nullable String resultParam);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -18,6 +18,8 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.lyndir.masterpassword;
 | 
					package com.lyndir.masterpassword;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static com.lyndir.masterpassword.MPUtils.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.google.common.base.*;
 | 
					import com.google.common.base.*;
 | 
				
			||||||
import com.google.common.primitives.Bytes;
 | 
					import com.google.common.primitives.Bytes;
 | 
				
			||||||
import com.google.common.primitives.UnsignedInteger;
 | 
					import com.google.common.primitives.UnsignedInteger;
 | 
				
			||||||
@@ -36,14 +38,11 @@ import javax.crypto.IllegalBlockSizeException;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * bugs:
 | 
					 * @see MasterKey.Version#V0
 | 
				
			||||||
 * - 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
 | 
					 * @author lhunath, 2014-08-30
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class MasterKeyV0 extends MasterKey {
 | 
					public class MasterKeyV0 implements MasterKeyAlgorithm {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * mpw: validity for the time-based rolling counter.
 | 
					     * mpw: validity for the time-based rolling counter.
 | 
				
			||||||
@@ -82,25 +81,18 @@ public class MasterKeyV0 extends MasterKey {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    protected static final int                          scrypt_N       = 32768;
 | 
					    protected static final int                          scrypt_N       = 32768;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @SuppressWarnings("UnusedDeclaration")
 | 
					    protected final Logger logger = Logger.get( getClass() );
 | 
				
			||||||
    private static final Logger logger = Logger.get( MasterKeyV0.class );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public MasterKeyV0(final String fullName) {
 | 
					    @Override
 | 
				
			||||||
        super( fullName );
 | 
					    public MasterKey.Version getAlgorithmVersion() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return MasterKey.Version.V0;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public Version getAlgorithmVersion() {
 | 
					    public byte[] deriveKey(final String fullName, final char[] masterPassword) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
        return Version.V0;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    protected byte[] deriveKey(final char[] masterPassword) {
 | 
					 | 
				
			||||||
        Preconditions.checkArgument( masterPassword.length > 0 );
 | 
					        Preconditions.checkArgument( masterPassword.length > 0 );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        String fullName = getFullName();
 | 
					 | 
				
			||||||
        byte[] fullNameBytes = fullName.getBytes( mpw_charset );
 | 
					        byte[] fullNameBytes = fullName.getBytes( mpw_charset );
 | 
				
			||||||
        byte[] fullNameLengthBytes = bytesForInt( fullName.length() );
 | 
					        byte[] fullNameLengthBytes = bytesForInt( fullName.length() );
 | 
				
			||||||
        ByteBuffer mpBytesBuf = mpw_charset.encode( CharBuffer.wrap( masterPassword ) );
 | 
					        ByteBuffer mpBytesBuf = mpw_charset.encode( CharBuffer.wrap( masterPassword ) );
 | 
				
			||||||
@@ -127,28 +119,26 @@ public class MasterKeyV0 extends MasterKey {
 | 
				
			|||||||
        byte[] masterKey = scrypt( masterKeySalt, mpBytes ); // TODO: Why not mpBytesBuf.array()?
 | 
					        byte[] masterKey = scrypt( masterKeySalt, mpBytes ); // TODO: Why not mpBytesBuf.array()?
 | 
				
			||||||
        Arrays.fill( masterKeySalt, (byte) 0 );
 | 
					        Arrays.fill( masterKeySalt, (byte) 0 );
 | 
				
			||||||
        Arrays.fill( mpBytes, (byte) 0 );
 | 
					        Arrays.fill( mpBytes, (byte) 0 );
 | 
				
			||||||
        logger.trc( "  => masterKey.id: %s", (masterKey == null)? null: (Object) idForBytes( masterKey ) );
 | 
					        logger.trc( "  => masterKey.id: %s", (Object) idForBytes( masterKey ) );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return masterKey;
 | 
					        return masterKey;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    protected byte[] scrypt(final byte[] masterKeySalt, final byte[] mpBytes) {
 | 
					    protected byte[] scrypt(final byte[] masterKeySalt, final byte[] mpBytes) {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            if (isAllowNative())
 | 
					//            if (isAllowNative())
 | 
				
			||||||
                return SCrypt.scrypt( mpBytes, masterKeySalt, scrypt_N, scrypt_r, scrypt_p, mpw_dkLen );
 | 
					                return SCrypt.scrypt( mpBytes, masterKeySalt, scrypt_N, scrypt_r, scrypt_p, mpw_dkLen );
 | 
				
			||||||
            else
 | 
					//            else
 | 
				
			||||||
                return SCrypt.scryptJ( mpBytes, masterKeySalt, scrypt_N, scrypt_r, scrypt_p, mpw_dkLen );
 | 
					//                return SCrypt.scryptJ( mpBytes, masterKeySalt, scrypt_N, scrypt_r, scrypt_p, mpw_dkLen );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        catch (final GeneralSecurityException e) {
 | 
					        catch (final GeneralSecurityException e) {
 | 
				
			||||||
            logger.bug( e );
 | 
					            throw logger.bug( e );
 | 
				
			||||||
            return null;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    protected byte[] siteKey(final String siteName, UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
 | 
					    public byte[] siteKey(final byte[] masterKey, final String siteName, UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
 | 
				
			||||||
                             @Nullable final String keyContext) {
 | 
					                          @Nullable final String keyContext) {
 | 
				
			||||||
        Preconditions.checkArgument( !siteName.isEmpty() );
 | 
					        Preconditions.checkArgument( !siteName.isEmpty() );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        logger.trc( "-- mpw_siteKey (algorithm: %u)", getAlgorithmVersion().toInt() );
 | 
					        logger.trc( "-- mpw_siteKey (algorithm: %u)", getAlgorithmVersion().toInt() );
 | 
				
			||||||
@@ -179,7 +169,6 @@ public class MasterKeyV0 extends MasterKey {
 | 
				
			|||||||
            sitePasswordInfo = Bytes.concat( sitePasswordInfo, keyContextLengthBytes, keyContextBytes );
 | 
					            sitePasswordInfo = Bytes.concat( sitePasswordInfo, keyContextLengthBytes, keyContextBytes );
 | 
				
			||||||
        logger.trc( "  => siteSalt.id: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
 | 
					        logger.trc( "  => siteSalt.id: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        byte[] masterKey = getKey();
 | 
					 | 
				
			||||||
        logger.trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )", (Object) idForBytes( masterKey ) );
 | 
					        logger.trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )", (Object) idForBytes( masterKey ) );
 | 
				
			||||||
        byte[] sitePasswordSeedBytes = mpw_digest.of( masterKey, sitePasswordInfo );
 | 
					        byte[] sitePasswordSeedBytes = mpw_digest.of( masterKey, sitePasswordInfo );
 | 
				
			||||||
        logger.trc( "  => siteKey.id: %s", (Object) idForBytes( sitePasswordSeedBytes ) );
 | 
					        logger.trc( "  => siteKey.id: %s", (Object) idForBytes( sitePasswordSeedBytes ) );
 | 
				
			||||||
@@ -188,10 +177,10 @@ public class MasterKeyV0 extends MasterKey {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public String siteResult(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
 | 
					    public String siteResult(final byte[] masterKey, final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
 | 
				
			||||||
                             @Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam) {
 | 
					                             @Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        byte[] siteKey = siteKey( siteName, siteCounter, keyPurpose, keyContext );
 | 
					        byte[] siteKey = siteKey( masterKey, siteName, siteCounter, keyPurpose, keyContext );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        logger.trc( "-- mpw_siteResult (algorithm: %u)", getAlgorithmVersion().toInt() );
 | 
					        logger.trc( "-- mpw_siteResult (algorithm: %u)", getAlgorithmVersion().toInt() );
 | 
				
			||||||
        logger.trc( "resultType: %d (%s)", resultType.toInt(), resultType.getShortName() );
 | 
					        logger.trc( "resultType: %d (%s)", resultType.toInt(), resultType.getShortName() );
 | 
				
			||||||
@@ -199,18 +188,18 @@ public class MasterKeyV0 extends MasterKey {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        switch (resultType.getTypeClass()) {
 | 
					        switch (resultType.getTypeClass()) {
 | 
				
			||||||
            case Template:
 | 
					            case Template:
 | 
				
			||||||
                return sitePasswordFromTemplate( siteKey, resultType, resultParam );
 | 
					                return sitePasswordFromTemplate( masterKey, siteKey, resultType, resultParam );
 | 
				
			||||||
            case Stateful:
 | 
					            case Stateful:
 | 
				
			||||||
                return sitePasswordFromCrypt( siteKey, resultType, resultParam );
 | 
					                return sitePasswordFromCrypt( masterKey, siteKey, resultType, resultParam );
 | 
				
			||||||
            case Derive:
 | 
					            case Derive:
 | 
				
			||||||
                return sitePasswordFromDerive( siteKey, resultType, resultParam );
 | 
					                return sitePasswordFromDerive( masterKey, siteKey, resultType, resultParam );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        throw logger.bug( "Unsupported result type class: %s", resultType.getTypeClass() );
 | 
					        throw logger.bug( "Unsupported result type class: %s", resultType.getTypeClass() );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    protected String sitePasswordFromTemplate(final byte[] siteKey, final MPResultType resultType, @Nullable final String resultParam) {
 | 
					    public String sitePasswordFromTemplate(final byte[] masterKey, final byte[] siteKey, final MPResultType resultType, @Nullable final String resultParam) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        int[] _siteKey = new int[siteKey.length];
 | 
					        int[] _siteKey = new int[siteKey.length];
 | 
				
			||||||
        for (int i = 0; i < siteKey.length; ++i) {
 | 
					        for (int i = 0; i < siteKey.length; ++i) {
 | 
				
			||||||
@@ -244,7 +233,7 @@ public class MasterKeyV0 extends MasterKey {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    protected String sitePasswordFromCrypt(final byte[] siteKey, final MPResultType resultType, @Nullable final String resultParam) {
 | 
					    public String sitePasswordFromCrypt(final byte[] masterKey, final byte[] siteKey, final MPResultType resultType, @Nullable final String resultParam) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Preconditions.checkNotNull( resultParam );
 | 
					        Preconditions.checkNotNull( resultParam );
 | 
				
			||||||
        Preconditions.checkArgument( !resultParam.isEmpty() );
 | 
					        Preconditions.checkArgument( !resultParam.isEmpty() );
 | 
				
			||||||
@@ -255,7 +244,7 @@ public class MasterKeyV0 extends MasterKey {
 | 
				
			|||||||
            logger.trc( "b64 decoded: %zu bytes = %s", cipherBuf.length, CodeUtils.encodeHex( cipherBuf ) );
 | 
					            logger.trc( "b64 decoded: %zu bytes = %s", cipherBuf.length, CodeUtils.encodeHex( cipherBuf ) );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Decrypt
 | 
					            // Decrypt
 | 
				
			||||||
            byte[] plainBuf  = CryptUtils.decrypt( cipherBuf, getKey(), true );
 | 
					            byte[] plainBuf  = CryptUtils.decrypt( cipherBuf, masterKey, true );
 | 
				
			||||||
            String plainText = mpw_charset.decode( ByteBuffer.wrap( plainBuf ) ).toString();
 | 
					            String plainText = mpw_charset.decode( ByteBuffer.wrap( plainBuf ) ).toString();
 | 
				
			||||||
            logger.trc( "decrypted -> plainText: %s", plainText );
 | 
					            logger.trc( "decrypted -> plainText: %s", plainText );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -267,7 +256,7 @@ public class MasterKeyV0 extends MasterKey {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    protected String sitePasswordFromDerive(final byte[] siteKey, final MPResultType resultType, @Nullable final String resultParam) {
 | 
					    public String sitePasswordFromDerive(final byte[] masterKey, final byte[] siteKey, final MPResultType resultType, @Nullable final String resultParam) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (resultType == MPResultType.DeriveKey) {
 | 
					        if (resultType == MPResultType.DeriveKey) {
 | 
				
			||||||
            Preconditions.checkNotNull( resultParam );
 | 
					            Preconditions.checkNotNull( resultParam );
 | 
				
			||||||
@@ -296,7 +285,7 @@ public class MasterKeyV0 extends MasterKey {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public String siteState(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
 | 
					    public String siteState(final byte[] masterKey, final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
 | 
				
			||||||
                            @Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam) {
 | 
					                            @Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Preconditions.checkNotNull( resultParam );
 | 
					        Preconditions.checkNotNull( resultParam );
 | 
				
			||||||
@@ -305,7 +294,7 @@ public class MasterKeyV0 extends MasterKey {
 | 
				
			|||||||
        try {
 | 
					        try {
 | 
				
			||||||
            // Encrypt
 | 
					            // Encrypt
 | 
				
			||||||
            ByteBuffer plainText = mpw_charset.encode( CharBuffer.wrap( resultParam ) );
 | 
					            ByteBuffer plainText = mpw_charset.encode( CharBuffer.wrap( resultParam ) );
 | 
				
			||||||
            byte[] cipherBuf = CryptUtils.encrypt( plainText.array(), getKey(), true );
 | 
					            byte[] cipherBuf = CryptUtils.encrypt( plainText.array(), masterKey, true );
 | 
				
			||||||
            logger.trc( "cipherBuf: %zu bytes = %s", cipherBuf.length, CodeUtils.encodeHex( cipherBuf ) );
 | 
					            logger.trc( "cipherBuf: %zu bytes = %s", cipherBuf.length, CodeUtils.encodeHex( cipherBuf ) );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Base64-encode
 | 
					            // Base64-encode
 | 
				
			||||||
@@ -318,19 +307,4 @@ public class MasterKeyV0 extends MasterKey {
 | 
				
			|||||||
            throw logger.bug( e );
 | 
					            throw logger.bug( e );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    protected byte[] bytesForInt(final int number) {
 | 
					 | 
				
			||||||
        return ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( mpw_byteOrder ).putInt( number ).array();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    protected byte[] bytesForInt(final UnsignedInteger number) {
 | 
					 | 
				
			||||||
        return ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( mpw_byteOrder ).putInt( number.intValue() ).array();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    protected byte[] idForBytes(final byte[] bytes) {
 | 
					 | 
				
			||||||
        return mpw_hash.of( bytes );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,8 +19,6 @@
 | 
				
			|||||||
package com.lyndir.masterpassword;
 | 
					package com.lyndir.masterpassword;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.google.common.base.Preconditions;
 | 
					import com.google.common.base.Preconditions;
 | 
				
			||||||
import com.google.common.primitives.UnsignedInteger;
 | 
					 | 
				
			||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
					 | 
				
			||||||
import javax.annotation.Nullable;
 | 
					import javax.annotation.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -33,21 +31,14 @@ import javax.annotation.Nullable;
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
public class MasterKeyV1 extends MasterKeyV0 {
 | 
					public class MasterKeyV1 extends MasterKeyV0 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @SuppressWarnings("UnusedDeclaration")
 | 
					    @Override
 | 
				
			||||||
    private static final Logger logger = Logger.get( MasterKeyV1.class );
 | 
					    public MasterKey.Version getAlgorithmVersion() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public MasterKeyV1(final String fullName) {
 | 
					        return MasterKey.Version.V1;
 | 
				
			||||||
        super( fullName );
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public Version getAlgorithmVersion() {
 | 
					    public String sitePasswordFromTemplate(final byte[] masterKey, final byte[] siteKey, final MPResultType resultType, @Nullable final String resultParam) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
        return Version.V1;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    protected String sitePasswordFromTemplate(final byte[] siteKey, final MPResultType resultType, @Nullable final String resultParam) {
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        logger.trc( "-- mpw_siteResult (algorithm: %u)", getAlgorithmVersion().toInt() );
 | 
					        logger.trc( "-- mpw_siteResult (algorithm: %u)", getAlgorithmVersion().toInt() );
 | 
				
			||||||
        logger.trc( "resultType: %d (%s)", resultType.toInt(), resultType.getShortName() );
 | 
					        logger.trc( "resultType: %d (%s)", resultType.toInt(), resultType.getShortName() );
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,11 +18,12 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.lyndir.masterpassword;
 | 
					package com.lyndir.masterpassword;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static com.lyndir.masterpassword.MPUtils.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.google.common.base.Preconditions;
 | 
					import com.google.common.base.Preconditions;
 | 
				
			||||||
import com.google.common.primitives.Bytes;
 | 
					import com.google.common.primitives.Bytes;
 | 
				
			||||||
import com.google.common.primitives.UnsignedInteger;
 | 
					import com.google.common.primitives.UnsignedInteger;
 | 
				
			||||||
import com.lyndir.lhunath.opal.system.CodeUtils;
 | 
					import com.lyndir.lhunath.opal.system.CodeUtils;
 | 
				
			||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
					 | 
				
			||||||
import javax.annotation.Nullable;
 | 
					import javax.annotation.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -34,21 +35,14 @@ import javax.annotation.Nullable;
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
public class MasterKeyV2 extends MasterKeyV1 {
 | 
					public class MasterKeyV2 extends MasterKeyV1 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @SuppressWarnings("UnusedDeclaration")
 | 
					    @Override
 | 
				
			||||||
    private static final Logger logger = Logger.get( MasterKeyV2.class );
 | 
					    public MasterKey.Version getAlgorithmVersion() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public MasterKeyV2(final String fullName) {
 | 
					        return MasterKey.Version.V2;
 | 
				
			||||||
        super( fullName );
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public Version getAlgorithmVersion() {
 | 
					    public byte[] siteKey(final byte[] masterKey, final String siteName, UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
 | 
				
			||||||
 | 
					 | 
				
			||||||
        return Version.V2;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    protected byte[] siteKey(final String siteName, UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
 | 
					 | 
				
			||||||
                             @Nullable final String keyContext) {
 | 
					                             @Nullable final String keyContext) {
 | 
				
			||||||
        Preconditions.checkArgument( !siteName.isEmpty() );
 | 
					        Preconditions.checkArgument( !siteName.isEmpty() );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -80,7 +74,6 @@ public class MasterKeyV2 extends MasterKeyV1 {
 | 
				
			|||||||
            sitePasswordInfo = Bytes.concat( sitePasswordInfo, keyContextLengthBytes, keyContextBytes );
 | 
					            sitePasswordInfo = Bytes.concat( sitePasswordInfo, keyContextLengthBytes, keyContextBytes );
 | 
				
			||||||
        logger.trc( "  => siteSalt.id: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
 | 
					        logger.trc( "  => siteSalt.id: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        byte[] masterKey = getKey();
 | 
					 | 
				
			||||||
        logger.trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )", (Object) idForBytes( masterKey ) );
 | 
					        logger.trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )", (Object) idForBytes( masterKey ) );
 | 
				
			||||||
        byte[] sitePasswordSeedBytes = MasterKeyV0.mpw_digest.of( masterKey, sitePasswordInfo );
 | 
					        byte[] sitePasswordSeedBytes = MasterKeyV0.mpw_digest.of( masterKey, sitePasswordInfo );
 | 
				
			||||||
        logger.trc( "  => siteKey.id: %s", (Object) idForBytes( sitePasswordSeedBytes ) );
 | 
					        logger.trc( "  => siteKey.id: %s", (Object) idForBytes( sitePasswordSeedBytes ) );
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,14 +18,14 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.lyndir.masterpassword;
 | 
					package com.lyndir.masterpassword;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static com.lyndir.masterpassword.MPUtils.idForBytes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.google.common.base.Preconditions;
 | 
					import com.google.common.base.Preconditions;
 | 
				
			||||||
import com.google.common.primitives.Bytes;
 | 
					import com.google.common.primitives.Bytes;
 | 
				
			||||||
import com.lyndir.lhunath.opal.system.CodeUtils;
 | 
					import com.lyndir.lhunath.opal.system.CodeUtils;
 | 
				
			||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
					 | 
				
			||||||
import java.nio.ByteBuffer;
 | 
					import java.nio.ByteBuffer;
 | 
				
			||||||
import java.nio.CharBuffer;
 | 
					import java.nio.CharBuffer;
 | 
				
			||||||
import java.util.Arrays;
 | 
					import java.util.Arrays;
 | 
				
			||||||
import javax.annotation.Nullable;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -36,27 +36,18 @@ import javax.annotation.Nullable;
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
public class MasterKeyV3 extends MasterKeyV2 {
 | 
					public class MasterKeyV3 extends MasterKeyV2 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @SuppressWarnings("UnusedDeclaration")
 | 
					    @Override
 | 
				
			||||||
    private static final Logger logger = Logger.get( MasterKeyV3.class );
 | 
					    public MasterKey.Version getAlgorithmVersion() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public MasterKeyV3(final String fullName) {
 | 
					        return MasterKey.Version.V3;
 | 
				
			||||||
        super( fullName );
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public Version getAlgorithmVersion() {
 | 
					    public byte[] deriveKey(final String fullName, final char[] masterPassword) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
        return Version.V3;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    protected byte[] deriveKey(final char[] masterPassword) {
 | 
					 | 
				
			||||||
        Preconditions.checkArgument( masterPassword.length > 0 );
 | 
					        Preconditions.checkArgument( masterPassword.length > 0 );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        String fullName = getFullName();
 | 
					 | 
				
			||||||
        byte[] fullNameBytes = fullName.getBytes( MasterKeyV0.mpw_charset );
 | 
					        byte[] fullNameBytes = fullName.getBytes( MasterKeyV0.mpw_charset );
 | 
				
			||||||
        byte[] fullNameLengthBytes = bytesForInt( fullNameBytes.length );
 | 
					        byte[] fullNameLengthBytes = MPUtils.bytesForInt( fullNameBytes.length );
 | 
				
			||||||
        ByteBuffer mpBytesBuf = MasterKeyV0.mpw_charset.encode( CharBuffer.wrap( masterPassword ) );
 | 
					        ByteBuffer mpBytesBuf = MasterKeyV0.mpw_charset.encode( CharBuffer.wrap( masterPassword ) );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        logger.trc( "-- mpw_masterKey (algorithm: %u)", getAlgorithmVersion().toInt() );
 | 
					        logger.trc( "-- mpw_masterKey (algorithm: %u)", getAlgorithmVersion().toInt() );
 | 
				
			||||||
@@ -81,7 +72,7 @@ public class MasterKeyV3 extends MasterKeyV2 {
 | 
				
			|||||||
        byte[] masterKey = scrypt( masterKeySalt, mpBytes ); // TODO: Why not mpBytesBuf.array()?
 | 
					        byte[] masterKey = scrypt( masterKeySalt, mpBytes ); // TODO: Why not mpBytesBuf.array()?
 | 
				
			||||||
        Arrays.fill( masterKeySalt, (byte) 0 );
 | 
					        Arrays.fill( masterKeySalt, (byte) 0 );
 | 
				
			||||||
        Arrays.fill( mpBytes, (byte) 0 );
 | 
					        Arrays.fill( mpBytes, (byte) 0 );
 | 
				
			||||||
        logger.trc( "  => masterKey.id: %s", (masterKey == null)? null: (Object) idForBytes( masterKey ) );
 | 
					        logger.trc( "  => masterKey.id: %s", (Object) idForBytes( masterKey ) );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return masterKey;
 | 
					        return masterKey;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,80 @@
 | 
				
			|||||||
 | 
					//==============================================================================
 | 
				
			||||||
 | 
					// 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.model;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static com.lyndir.lhunath.opal.system.util.ObjectUtils.ifNotNullElse;
 | 
				
			||||||
 | 
					import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.lyndir.masterpassword.MPConstant;
 | 
				
			||||||
 | 
					import com.lyndir.masterpassword.MasterKey;
 | 
				
			||||||
 | 
					import org.joda.time.Instant;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @author lhunath, 2017-09-20
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class MPFlatMarshaller implements MPMarshaller {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final int FORMAT = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public String marshall(final MPUser user, final MasterKey masterKey, final ContentMode contentMode) {
 | 
				
			||||||
 | 
					        StringBuilder content = new StringBuilder();
 | 
				
			||||||
 | 
					        content.append( "# Master Password site export\n" );
 | 
				
			||||||
 | 
					        content.append( "#     " ).append( contentMode.description() ).append( '\n' );
 | 
				
			||||||
 | 
					        content.append( "# \n" );
 | 
				
			||||||
 | 
					        content.append( "##\n" );
 | 
				
			||||||
 | 
					        content.append( "# Format: " ).append( FORMAT ).append( '\n' );
 | 
				
			||||||
 | 
					        content.append( "# Date: " ).append( MPConstant.dateTimeFormatter.print( new Instant() ) ).append( '\n' );
 | 
				
			||||||
 | 
					        content.append( "# User Name: " ).append( user.getFullName() ).append( '\n' );
 | 
				
			||||||
 | 
					        content.append( "# Full Name: " ).append( user.getFullName() ).append( '\n' );
 | 
				
			||||||
 | 
					        content.append( "# Avatar: " ).append( user.getAvatar() ).append( '\n' );
 | 
				
			||||||
 | 
					        content.append( "# Key ID: " ).append( user.exportKeyID() ).append( '\n' );
 | 
				
			||||||
 | 
					        content.append( "# Algorithm: " ).append( MasterKey.Version.CURRENT.toInt() ).append( '\n' );
 | 
				
			||||||
 | 
					        content.append( "# Default Type: " ).append( user.getDefaultType().getType() ).append( '\n' );
 | 
				
			||||||
 | 
					        content.append( "# Passwords: " ).append( contentMode.name() ).append( '\n' );
 | 
				
			||||||
 | 
					        content.append( "##\n" );
 | 
				
			||||||
 | 
					        content.append( "#\n" );
 | 
				
			||||||
 | 
					        content.append( "#               Last     Times  Password                      Login\t                     Site\tSite\n" );
 | 
				
			||||||
 | 
					        content.append( "#               used      used      type                       name\t                     name\tpassword\n" );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (final MPSite site : user.getSites()) {
 | 
				
			||||||
 | 
					            String loginName = site.getLoginContent();
 | 
				
			||||||
 | 
					            String password = site.getSiteContent();
 | 
				
			||||||
 | 
					            if (!contentMode.isRedacted()) {
 | 
				
			||||||
 | 
					                loginName = site.loginFor( masterKey );
 | 
				
			||||||
 | 
					                password = site.resultFor( masterKey );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            content.append( strf( "%s  %8d  %8s  %25s\t%25s\t%s\n", //
 | 
				
			||||||
 | 
					                                  MPConstant.dateTimeFormatter.print( site.getLastUsed() ), // lastUsed
 | 
				
			||||||
 | 
					                                  site.getUses(), // uses
 | 
				
			||||||
 | 
					                                  strf( "%d:%d:%d", //
 | 
				
			||||||
 | 
					                                        site.getResultType().getType(), // type
 | 
				
			||||||
 | 
					                                        site.getAlgorithmVersion().toInt(), // algorithm
 | 
				
			||||||
 | 
					                                        site.getSiteCounter().intValue() ), // counter
 | 
				
			||||||
 | 
					                                  ifNotNullElse( loginName, "" ), // loginName
 | 
				
			||||||
 | 
					                                  site.getSiteName(), // siteName
 | 
				
			||||||
 | 
					                                  ifNotNullElse( password, "" ) // password
 | 
				
			||||||
 | 
					            ) );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return content.toString();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,139 @@
 | 
				
			|||||||
 | 
					//==============================================================================
 | 
				
			||||||
 | 
					// 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.model;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.common.base.*;
 | 
				
			||||||
 | 
					import com.google.common.io.CharStreams;
 | 
				
			||||||
 | 
					import com.google.common.primitives.UnsignedInteger;
 | 
				
			||||||
 | 
					import com.lyndir.lhunath.opal.system.CodeUtils;
 | 
				
			||||||
 | 
					import com.lyndir.lhunath.opal.system.util.ConversionUtils;
 | 
				
			||||||
 | 
					import com.lyndir.masterpassword.*;
 | 
				
			||||||
 | 
					import java.io.*;
 | 
				
			||||||
 | 
					import java.util.regex.Matcher;
 | 
				
			||||||
 | 
					import java.util.regex.Pattern;
 | 
				
			||||||
 | 
					import javax.annotation.Nonnull;
 | 
				
			||||||
 | 
					import org.joda.time.DateTime;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @author lhunath, 14-12-07
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class MPFlatUnmarshaller implements MPUnmarshaller {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final Pattern[] unmarshallFormats = {
 | 
				
			||||||
 | 
					            Pattern.compile( "^([^ ]+) +(\\d+) +(\\d+)(:\\d+)? +([^\t]+)\t(.*)" ),
 | 
				
			||||||
 | 
					            Pattern.compile( "^([^ ]+) +(\\d+) +(\\d+)(:\\d+)?(:\\d+)? +([^\t]*)\t *([^\t]+)\t(.*)" ) };
 | 
				
			||||||
 | 
					    private static final Pattern   headerFormat      = Pattern.compile( "^#\\s*([^:]+): (.*)" );
 | 
				
			||||||
 | 
					    private static final Pattern   colon             = Pattern.compile( ":" );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Nonnull
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public MPUser unmarshall(@Nonnull final File file)
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					        try (Reader reader = new InputStreamReader( new FileInputStream( file ), Charsets.UTF_8 )) {
 | 
				
			||||||
 | 
					            return unmarshall( CharStreams.toString( reader ) );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Nonnull
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public MPUser unmarshall(@Nonnull final String content) {
 | 
				
			||||||
 | 
					        MPUser       user         = null;
 | 
				
			||||||
 | 
					        byte[]       keyID        = null;
 | 
				
			||||||
 | 
					        String       fullName     = null;
 | 
				
			||||||
 | 
					        int          mpVersion    = 0, importFormat = 0, avatar = 0;
 | 
				
			||||||
 | 
					        boolean      clearContent = false, headerStarted = false;
 | 
				
			||||||
 | 
					        MPResultType defaultType  = MPResultType.DEFAULT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //noinspection HardcodedLineSeparator
 | 
				
			||||||
 | 
					        for (final String line : Splitter.on( CharMatcher.anyOf( "\r\n" ) ).omitEmptyStrings().split( content ))
 | 
				
			||||||
 | 
					            // Header delimitor.
 | 
				
			||||||
 | 
					            if (line.startsWith( "##" ))
 | 
				
			||||||
 | 
					                if (!headerStarted)
 | 
				
			||||||
 | 
					                    // Starts the header.
 | 
				
			||||||
 | 
					                    headerStarted = true;
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                    // Ends the header.
 | 
				
			||||||
 | 
					                    user = new MPUser( fullName, keyID, MasterKey.Version.fromInt( mpVersion ), avatar, defaultType, new DateTime( 0 ) );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Comment.
 | 
				
			||||||
 | 
					            else if (line.startsWith( "#" )) {
 | 
				
			||||||
 | 
					                if (headerStarted && (user == null)) {
 | 
				
			||||||
 | 
					                    // In header.
 | 
				
			||||||
 | 
					                    Matcher headerMatcher = headerFormat.matcher( line );
 | 
				
			||||||
 | 
					                    if (headerMatcher.matches()) {
 | 
				
			||||||
 | 
					                        String name = headerMatcher.group( 1 ), value = headerMatcher.group( 2 );
 | 
				
			||||||
 | 
					                        if ("Full Name".equalsIgnoreCase( name ) || "User Name".equalsIgnoreCase( name ))
 | 
				
			||||||
 | 
					                            fullName = value;
 | 
				
			||||||
 | 
					                        else if ("Key ID".equalsIgnoreCase( name ))
 | 
				
			||||||
 | 
					                            keyID = CodeUtils.decodeHex( value );
 | 
				
			||||||
 | 
					                        else if ("Algorithm".equalsIgnoreCase( name ))
 | 
				
			||||||
 | 
					                            mpVersion = ConversionUtils.toIntegerNN( value );
 | 
				
			||||||
 | 
					                        else if ("Format".equalsIgnoreCase( name ))
 | 
				
			||||||
 | 
					                            importFormat = ConversionUtils.toIntegerNN( value );
 | 
				
			||||||
 | 
					                        else if ("Avatar".equalsIgnoreCase( name ))
 | 
				
			||||||
 | 
					                            avatar = ConversionUtils.toIntegerNN( value );
 | 
				
			||||||
 | 
					                        else if ("Passwords".equalsIgnoreCase( name ))
 | 
				
			||||||
 | 
					                            clearContent = "visible".equalsIgnoreCase( value );
 | 
				
			||||||
 | 
					                        else if ("Default Type".equalsIgnoreCase( name ))
 | 
				
			||||||
 | 
					                            defaultType = MPResultType.forType( ConversionUtils.toIntegerNN( value ) );
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // No comment.
 | 
				
			||||||
 | 
					            else if (user != null) {
 | 
				
			||||||
 | 
					                Matcher siteMatcher = unmarshallFormats[importFormat].matcher( line );
 | 
				
			||||||
 | 
					                if (!siteMatcher.matches())
 | 
				
			||||||
 | 
					                    return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                MPSite site;
 | 
				
			||||||
 | 
					                switch (importFormat) {
 | 
				
			||||||
 | 
					                    case 0:
 | 
				
			||||||
 | 
					                        site = new MPSite( user, //
 | 
				
			||||||
 | 
					                                           siteMatcher.group( 5 ), siteMatcher.group( 6 ), MPSite.DEFAULT_COUNTER,
 | 
				
			||||||
 | 
					                                           MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
 | 
				
			||||||
 | 
					                                           MasterKey.Version.fromInt( ConversionUtils.toIntegerNN(
 | 
				
			||||||
 | 
					                                                   colon.matcher( siteMatcher.group( 4 ) ).replaceAll( "" ) ) ),
 | 
				
			||||||
 | 
					                                           null, null, null, ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ),
 | 
				
			||||||
 | 
					                                           MPConstant.dateTimeFormatter.parseDateTime( siteMatcher.group( 1 ) ).toInstant() );
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    case 1:
 | 
				
			||||||
 | 
					                        site = new MPSite( user, //
 | 
				
			||||||
 | 
					                                           siteMatcher.group( 7 ), siteMatcher.group( 8 ),
 | 
				
			||||||
 | 
					                                           UnsignedInteger.valueOf( colon.matcher( siteMatcher.group( 5 ) ).replaceAll( "" ) ),
 | 
				
			||||||
 | 
					                                           MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
 | 
				
			||||||
 | 
					                                           MasterKey.Version.fromInt( ConversionUtils.toIntegerNN(
 | 
				
			||||||
 | 
					                                                   colon.matcher( siteMatcher.group( 4 ) ).replaceAll( "" ) ) ),
 | 
				
			||||||
 | 
					                                           siteMatcher.group( 6 ), MPResultType.GeneratedName, null,
 | 
				
			||||||
 | 
					                                           ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ),
 | 
				
			||||||
 | 
					                                           MPConstant.dateTimeFormatter.parseDateTime( siteMatcher.group( 1 ) ).toInstant() );
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    default:
 | 
				
			||||||
 | 
					                        throw new UnsupportedOperationException( "Unexpected format: " + importFormat );
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                user.addSite( site );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return Preconditions.checkNotNull( user, "No full header found in import file." );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					//==============================================================================
 | 
				
			||||||
 | 
					// 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.model;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.lyndir.masterpassword.MasterKey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @author lhunath, 2017-09-20
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class MPJSONMarshaller implements MPMarshaller {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public String marshall(final MPUser user, final MasterKey masterKey, final ContentMode contentMode) {
 | 
				
			||||||
 | 
					        // TODO
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,45 @@
 | 
				
			|||||||
 | 
					//==============================================================================
 | 
				
			||||||
 | 
					// 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.model;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.File;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import javax.annotation.Nonnull;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @author lhunath, 2017-09-20
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class MPJSONUnmarshaller implements MPUnmarshaller {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Nonnull
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public MPUser unmarshall(@Nonnull final File file)
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					        // TODO
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Nonnull
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public MPUser unmarshall(@Nonnull final String content) {
 | 
				
			||||||
 | 
					        // TODO
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -25,11 +25,36 @@ public enum MPMarshalFormat {
 | 
				
			|||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Marshal using the line-based plain-text format.
 | 
					     * Marshal using the line-based plain-text format.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    Flat,
 | 
					    Flat {
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public MPMarshaller marshaller() {
 | 
				
			||||||
 | 
					            return new MPFlatMarshaller();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public MPUnmarshaller unmarshaller() {
 | 
				
			||||||
 | 
					            return new MPFlatUnmarshaller();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Marshal using the JSON structured format.
 | 
					     * Marshal using the JSON structured format.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    JSON;
 | 
					    JSON {
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public MPMarshaller marshaller() {
 | 
				
			||||||
 | 
					            return new MPJSONMarshaller();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static MPMarshalFormat DEFAULT = JSON;
 | 
					        @Override
 | 
				
			||||||
 | 
					        public MPUnmarshaller unmarshaller() {
 | 
				
			||||||
 | 
					            return new MPJSONUnmarshaller();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final MPMarshalFormat DEFAULT = JSON;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public abstract MPMarshaller marshaller();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public abstract MPUnmarshaller unmarshaller();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,50 @@
 | 
				
			|||||||
 | 
					//==============================================================================
 | 
				
			||||||
 | 
					// 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.model;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.lyndir.masterpassword.MasterKey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @author lhunath, 14-12-07
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public interface MPMarshaller {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    String marshall(MPUser user, MasterKey masterKey, ContentMode contentMode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    enum ContentMode {
 | 
				
			||||||
 | 
					        PROTECTED( "Export of site names and stored passwords (unless device-private) encrypted with the master key." ),
 | 
				
			||||||
 | 
					        VISIBLE( "Export of site names and passwords in clear-text." );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private final String description;
 | 
				
			||||||
 | 
					        private boolean redacted;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ContentMode(final String description) {
 | 
				
			||||||
 | 
					            this.description = description;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public String description() {
 | 
				
			||||||
 | 
					            return description;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public boolean isRedacted() {
 | 
				
			||||||
 | 
					            return redacted;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -32,16 +32,25 @@ import org.joda.time.Instant;
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
public class MPSite {
 | 
					public class MPSite {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static final UnsignedInteger DEFAULT_COUNTER = UnsignedInteger.valueOf( 1 );
 | 
					    public static final UnsignedInteger DEFAULT_COUNTER = UnsignedInteger.ONE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private final MPUser            user;
 | 
					    private final MPUser            user;
 | 
				
			||||||
    private       MasterKey.Version algorithmVersion;
 | 
					 | 
				
			||||||
    private       Instant           lastUsed;
 | 
					 | 
				
			||||||
    private       String            siteName;
 | 
					    private       String            siteName;
 | 
				
			||||||
    private       MPResultType      resultType;
 | 
					    @Nullable
 | 
				
			||||||
 | 
					    private       String            siteContent;
 | 
				
			||||||
    private       UnsignedInteger   siteCounter;
 | 
					    private       UnsignedInteger   siteCounter;
 | 
				
			||||||
    private       int               uses;
 | 
					    private       MPResultType      resultType;
 | 
				
			||||||
    private       String            loginName;
 | 
					    private       MasterKey.Version algorithmVersion;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Nullable
 | 
				
			||||||
 | 
					    private String       loginContent;
 | 
				
			||||||
 | 
					    @Nullable
 | 
				
			||||||
 | 
					    private MPResultType loginType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Nullable
 | 
				
			||||||
 | 
					    private String  url;
 | 
				
			||||||
 | 
					    private int     uses;
 | 
				
			||||||
 | 
					    private Instant lastUsed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public MPSite(final MPUser user, final String siteName) {
 | 
					    public MPSite(final MPUser user, final String siteName) {
 | 
				
			||||||
        this( user, siteName, DEFAULT_COUNTER, MPResultType.DEFAULT );
 | 
					        this( user, siteName, DEFAULT_COUNTER, MPResultType.DEFAULT );
 | 
				
			||||||
@@ -49,24 +58,28 @@ public class MPSite {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public MPSite(final MPUser user, final String siteName, final UnsignedInteger siteCounter, final MPResultType resultType) {
 | 
					    public MPSite(final MPUser user, final String siteName, final UnsignedInteger siteCounter, final MPResultType resultType) {
 | 
				
			||||||
        this.user = user;
 | 
					        this.user = user;
 | 
				
			||||||
 | 
					        this.siteName = siteName;
 | 
				
			||||||
 | 
					        this.siteCounter = siteCounter;
 | 
				
			||||||
 | 
					        this.resultType = resultType;
 | 
				
			||||||
        this.algorithmVersion = MasterKey.Version.CURRENT;
 | 
					        this.algorithmVersion = MasterKey.Version.CURRENT;
 | 
				
			||||||
        this.lastUsed = new Instant();
 | 
					        this.lastUsed = new Instant();
 | 
				
			||||||
        this.siteName = siteName;
 | 
					 | 
				
			||||||
        this.resultType = resultType;
 | 
					 | 
				
			||||||
        this.siteCounter = siteCounter;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected MPSite(final MPUser user, final MasterKey.Version algorithmVersion, final Instant lastUsed, final String siteName,
 | 
					    protected MPSite(final MPUser user, final String siteName, @Nullable final String siteContent, final UnsignedInteger siteCounter,
 | 
				
			||||||
                     final MPResultType resultType, final UnsignedInteger siteCounter, final int uses, @Nullable final String loginName,
 | 
					                     final MPResultType resultType, final MasterKey.Version algorithmVersion,
 | 
				
			||||||
                     @Nullable final String importContent) {
 | 
					                     @Nullable final String loginContent, @Nullable final MPResultType loginType,
 | 
				
			||||||
 | 
					                     @Nullable final String url, final int uses, final Instant lastUsed) {
 | 
				
			||||||
        this.user = user;
 | 
					        this.user = user;
 | 
				
			||||||
        this.algorithmVersion = algorithmVersion;
 | 
					 | 
				
			||||||
        this.lastUsed = lastUsed;
 | 
					 | 
				
			||||||
        this.siteName = siteName;
 | 
					        this.siteName = siteName;
 | 
				
			||||||
        this.resultType = resultType;
 | 
					        this.siteContent = siteContent;
 | 
				
			||||||
        this.siteCounter = siteCounter;
 | 
					        this.siteCounter = siteCounter;
 | 
				
			||||||
 | 
					        this.resultType = resultType;
 | 
				
			||||||
 | 
					        this.algorithmVersion = algorithmVersion;
 | 
				
			||||||
 | 
					        this.loginContent = loginContent;
 | 
				
			||||||
 | 
					        this.loginType = loginType;
 | 
				
			||||||
 | 
					        this.url = url;
 | 
				
			||||||
        this.uses = uses;
 | 
					        this.uses = uses;
 | 
				
			||||||
        this.loginName = loginName;
 | 
					        this.lastUsed = lastUsed;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public String resultFor(final MasterKey masterKey) {
 | 
					    public String resultFor(final MasterKey masterKey) {
 | 
				
			||||||
@@ -74,7 +87,15 @@ public class MPSite {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public String resultFor(final MasterKey masterKey, final MPKeyPurpose purpose, @Nullable final String context) {
 | 
					    public String resultFor(final MasterKey masterKey, final MPKeyPurpose purpose, @Nullable final String context) {
 | 
				
			||||||
        return masterKey.siteResult( siteName, siteCounter, purpose, context, resultType, null );
 | 
					        return masterKey.siteResult( siteName, siteCounter, purpose, context, resultType, siteContent, algorithmVersion );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String loginFor(final MasterKey masterKey) {
 | 
				
			||||||
 | 
					        if (loginType == null)
 | 
				
			||||||
 | 
					            loginType = MPResultType.GeneratedName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return masterKey.siteResult( siteName, DEFAULT_COUNTER, MPKeyPurpose.Identification, null, loginType, loginContent,
 | 
				
			||||||
 | 
					                                     algorithmVersion );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public MPUser getUser() {
 | 
					    public MPUser getUser() {
 | 
				
			||||||
@@ -111,6 +132,11 @@ public class MPSite {
 | 
				
			|||||||
        this.siteName = siteName;
 | 
					        this.siteName = siteName;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Nullable
 | 
				
			||||||
 | 
					    public String getSiteContent() {
 | 
				
			||||||
 | 
					        return siteContent;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public MPResultType getResultType() {
 | 
					    public MPResultType getResultType() {
 | 
				
			||||||
        return resultType;
 | 
					        return resultType;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -135,26 +161,47 @@ public class MPSite {
 | 
				
			|||||||
        this.uses = uses;
 | 
					        this.uses = uses;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public String getLoginName() {
 | 
					    @Nullable
 | 
				
			||||||
        return loginName;
 | 
					    public MPResultType getLoginType() {
 | 
				
			||||||
 | 
					        return loginType;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public void setLoginName(final String loginName) {
 | 
					    @Nullable
 | 
				
			||||||
        this.loginName = loginName;
 | 
					    public String getLoginContent() {
 | 
				
			||||||
 | 
					        return loginContent;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setLoginName(final MasterKey masterKey, @Nullable final MPResultType loginType, @Nullable final String result) {
 | 
				
			||||||
 | 
					        this.loginType = loginType;
 | 
				
			||||||
 | 
					        if (this.loginType != null)
 | 
				
			||||||
 | 
					            if (result == null)
 | 
				
			||||||
 | 
					                this.loginContent = null;
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                this.loginContent = masterKey.siteState(
 | 
				
			||||||
 | 
					                        siteName, DEFAULT_COUNTER, MPKeyPurpose.Identification, null, this.loginType, result, algorithmVersion );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Nullable
 | 
				
			||||||
 | 
					    public String getUrl() {
 | 
				
			||||||
 | 
					        return url;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setUrl(@Nullable final String url) {
 | 
				
			||||||
 | 
					        this.url = url;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public boolean equals(final Object obj) {
 | 
					    public boolean equals(final Object obj) {
 | 
				
			||||||
        return (this == obj) || ((obj instanceof MPSite) && Objects.equals( siteName, ((MPSite) obj).siteName ));
 | 
					        return (this == obj) || ((obj instanceof MPSite) && Objects.equals( getSiteName(), ((MPSite) obj).getSiteName() ));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public int hashCode() {
 | 
					    public int hashCode() {
 | 
				
			||||||
        return Objects.hashCode( siteName );
 | 
					        return Objects.hashCode( getSiteName() );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public String toString() {
 | 
					    public String toString() {
 | 
				
			||||||
        return strf( "{MPSite: %s}", siteName );
 | 
					        return strf( "{MPSite: %s}", getSiteName() );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,148 +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.model;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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.Preconditions;
 | 
					 | 
				
			||||||
import com.lyndir.masterpassword.MasterKey;
 | 
					 | 
				
			||||||
import javax.annotation.Nonnull;
 | 
					 | 
				
			||||||
import javax.annotation.Nullable;
 | 
					 | 
				
			||||||
import org.joda.time.Instant;
 | 
					 | 
				
			||||||
import org.joda.time.format.DateTimeFormatter;
 | 
					 | 
				
			||||||
import org.joda.time.format.ISODateTimeFormat;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * @author lhunath, 14-12-07
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public class MPSiteMarshaller {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected static final DateTimeFormatter rfc3339 = ISODateTimeFormat.dateTimeNoMillis();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private final StringBuilder export      = new StringBuilder();
 | 
					 | 
				
			||||||
    private   final    ContentMode   contentMode = ContentMode.PROTECTED;
 | 
					 | 
				
			||||||
    private final MasterKey masterKey;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public static MPSiteMarshaller marshallSafe(final MPUser user) {
 | 
					 | 
				
			||||||
        MPSiteMarshaller marshaller = new MPSiteMarshaller();
 | 
					 | 
				
			||||||
        marshaller.marshallHeaderForSafeContent( user );
 | 
					 | 
				
			||||||
        for (final MPSite site : user.getSites())
 | 
					 | 
				
			||||||
            marshaller.marshallSite( site );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return marshaller;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public static MPSiteMarshaller marshallVisible(final MPUser user, final MasterKey masterKey) {
 | 
					 | 
				
			||||||
        MPSiteMarshaller marshaller = new MPSiteMarshaller();
 | 
					 | 
				
			||||||
        marshaller.marshallHeaderForVisibleContentWithKey( user, masterKey );
 | 
					 | 
				
			||||||
        for (final MPSite site : user.getSites())
 | 
					 | 
				
			||||||
            marshaller.marshallSite( site );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return marshaller;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private String marshallHeaderForSafeContent(final MPUser user) {
 | 
					 | 
				
			||||||
        return marshallHeader( ContentMode.PROTECTED, user, null );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private String marshallHeaderForVisibleContentWithKey(final MPUser user, final MasterKey masterKey) {
 | 
					 | 
				
			||||||
        return marshallHeader( ContentMode.VISIBLE, user, masterKey );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private String marshallHeader(final ContentMode contentMode, final MPUser user, @Nullable final MasterKey masterKey) {
 | 
					 | 
				
			||||||
        this.contentMode = contentMode;
 | 
					 | 
				
			||||||
        this.masterKey = masterKey;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        StringBuilder header = new StringBuilder();
 | 
					 | 
				
			||||||
        header.append( "# Master Password site export\n" );
 | 
					 | 
				
			||||||
        header.append( "#     " ).append( this.contentMode.description() ).append( '\n' );
 | 
					 | 
				
			||||||
        header.append( "# \n" );
 | 
					 | 
				
			||||||
        header.append( "##\n" );
 | 
					 | 
				
			||||||
        header.append( "# Format: 1\n" );
 | 
					 | 
				
			||||||
        header.append( "# Date: " ).append( rfc3339.print( new Instant() ) ).append( '\n' );
 | 
					 | 
				
			||||||
        header.append( "# User Name: " ).append( user.getFullName() ).append( '\n' );
 | 
					 | 
				
			||||||
        header.append( "# Full Name: " ).append( user.getFullName() ).append( '\n' );
 | 
					 | 
				
			||||||
        header.append( "# Avatar: " ).append( user.getAvatar() ).append( '\n' );
 | 
					 | 
				
			||||||
        header.append( "# Key ID: " ).append( user.exportKeyID() ).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' );
 | 
					 | 
				
			||||||
        header.append( "##\n" );
 | 
					 | 
				
			||||||
        header.append( "#\n" );
 | 
					 | 
				
			||||||
        header.append( "#               Last     Times  Password                      Login\t                     Site\tSite\n" );
 | 
					 | 
				
			||||||
        header.append( "#               used      used      type                       name\t                     name\tpassword\n" );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        export.append( header );
 | 
					 | 
				
			||||||
        return header.toString();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public String marshallSite(final MPSite site) {
 | 
					 | 
				
			||||||
        String exportLine = strf( "%s  %8d  %8s  %25s\t%25s\t%s", //
 | 
					 | 
				
			||||||
                                  rfc3339.print( site.getLastUsed() ), // lastUsed
 | 
					 | 
				
			||||||
                                  site.getUses(), // uses
 | 
					 | 
				
			||||||
                                  strf( "%d:%d:%d", //
 | 
					 | 
				
			||||||
                                        site.getResultType().getType(), // type
 | 
					 | 
				
			||||||
                                        site.getAlgorithmVersion().toInt(), // algorithm
 | 
					 | 
				
			||||||
                                        site.getSiteCounter().intValue() ), // counter
 | 
					 | 
				
			||||||
                                  ifNotNullElse( site.getLoginName(), "" ), // loginName
 | 
					 | 
				
			||||||
                                  site.getSiteName(), // siteName
 | 
					 | 
				
			||||||
                                  ifNotNullElse( contentMode.contentForSite( site, masterKey ), "" ) // password
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        export.append( exportLine ).append( '\n' );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return exportLine;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public String getExport() {
 | 
					 | 
				
			||||||
        return export.toString();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public ContentMode getContentMode() {
 | 
					 | 
				
			||||||
        return contentMode;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public enum ContentMode {
 | 
					 | 
				
			||||||
        PROTECTED( "Export of site names and stored passwords (unless device-private) encrypted with the master key." ) {
 | 
					 | 
				
			||||||
            @Override
 | 
					 | 
				
			||||||
            public String contentForSite(final MPSite site, @Nullable final MasterKey masterKey) {
 | 
					 | 
				
			||||||
                return site.exportContent();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        VISIBLE( "Export of site names and passwords in clear-text." ) {
 | 
					 | 
				
			||||||
            @Override
 | 
					 | 
				
			||||||
            public String contentForSite(final MPSite site, @Nonnull final MasterKey masterKey) {
 | 
					 | 
				
			||||||
                return site.resultFor( Preconditions.checkNotNull( masterKey, "Master key is required when content mode is VISIBLE." ) );
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private final String description;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        ContentMode(final String description) {
 | 
					 | 
				
			||||||
            this.description = description;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public String description() {
 | 
					 | 
				
			||||||
            return description;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public abstract String contentForSite(MPSite site, MasterKey masterKey);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,181 +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.model;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.google.common.base.Charsets;
 | 
					 | 
				
			||||||
import com.google.common.base.Preconditions;
 | 
					 | 
				
			||||||
import com.google.common.collect.ImmutableList;
 | 
					 | 
				
			||||||
import com.google.common.io.CharStreams;
 | 
					 | 
				
			||||||
import com.google.common.primitives.UnsignedInteger;
 | 
					 | 
				
			||||||
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.MPResultType;
 | 
					 | 
				
			||||||
import com.lyndir.masterpassword.MasterKey;
 | 
					 | 
				
			||||||
import java.io.*;
 | 
					 | 
				
			||||||
import java.util.List;
 | 
					 | 
				
			||||||
import java.util.regex.Matcher;
 | 
					 | 
				
			||||||
import java.util.regex.Pattern;
 | 
					 | 
				
			||||||
import javax.annotation.Nonnull;
 | 
					 | 
				
			||||||
import javax.annotation.Nullable;
 | 
					 | 
				
			||||||
import org.joda.time.DateTime;
 | 
					 | 
				
			||||||
import org.joda.time.format.DateTimeFormatter;
 | 
					 | 
				
			||||||
import org.joda.time.format.ISODateTimeFormat;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * @author lhunath, 14-12-07
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public class MPSiteUnmarshaller {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @SuppressWarnings("UnusedDeclaration")
 | 
					 | 
				
			||||||
    private static final Logger            logger            = Logger.get( MPSite.class );
 | 
					 | 
				
			||||||
    private static final DateTimeFormatter rfc3339           = ISODateTimeFormat.dateTimeNoMillis();
 | 
					 | 
				
			||||||
    private static final Pattern[]         unmarshallFormats = {
 | 
					 | 
				
			||||||
            Pattern.compile( "^([^ ]+) +(\\d+) +(\\d+)(:\\d+)? +([^\t]+)\t(.*)" ),
 | 
					 | 
				
			||||||
            Pattern.compile( "^([^ ]+) +(\\d+) +(\\d+)(:\\d+)?(:\\d+)? +([^\t]*)\t *([^\t]+)\t(.*)" ) };
 | 
					 | 
				
			||||||
    private static final Pattern           headerFormat      = Pattern.compile( "^#\\s*([^:]+): (.*)" );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private final int     importFormat;
 | 
					 | 
				
			||||||
    @SuppressWarnings({ "FieldCanBeLocal", "unused" })
 | 
					 | 
				
			||||||
    private final int     mpVersion;
 | 
					 | 
				
			||||||
    @SuppressWarnings({ "FieldCanBeLocal", "unused" })
 | 
					 | 
				
			||||||
    private final boolean clearContent;
 | 
					 | 
				
			||||||
    private final MPUser  user;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nonnull
 | 
					 | 
				
			||||||
    public static MPSiteUnmarshaller unmarshall(@Nonnull final File file)
 | 
					 | 
				
			||||||
            throws IOException {
 | 
					 | 
				
			||||||
        try (Reader reader = new InputStreamReader( new FileInputStream( file ), Charsets.UTF_8 )) {
 | 
					 | 
				
			||||||
            return unmarshall( CharStreams.readLines( reader ) );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nonnull
 | 
					 | 
				
			||||||
    public static MPSiteUnmarshaller unmarshall(@Nonnull final List<String> lines) {
 | 
					 | 
				
			||||||
        byte[]                              keyID        = null;
 | 
					 | 
				
			||||||
        String                              fullName     = null;
 | 
					 | 
				
			||||||
        int                                 mpVersion    = 0, importFormat = 0, avatar = 0;
 | 
					 | 
				
			||||||
        boolean                             clearContent = false, headerStarted = false;
 | 
					 | 
				
			||||||
        MPResultType                        defaultType  = MPResultType.DEFAULT;
 | 
					 | 
				
			||||||
        MPSiteUnmarshaller                  marshaller   = null;
 | 
					 | 
				
			||||||
        final ImmutableList.Builder<MPSite> sites        = ImmutableList.builder();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for (final String line : lines)
 | 
					 | 
				
			||||||
            // Header delimitor.
 | 
					 | 
				
			||||||
            if (line.startsWith( "##" ))
 | 
					 | 
				
			||||||
                if (!headerStarted)
 | 
					 | 
				
			||||||
                    // Starts the header.
 | 
					 | 
				
			||||||
                    headerStarted = true;
 | 
					 | 
				
			||||||
                else
 | 
					 | 
				
			||||||
                    // Ends the header.
 | 
					 | 
				
			||||||
                    marshaller = new MPSiteUnmarshaller( importFormat, mpVersion, fullName, keyID, avatar, defaultType, clearContent );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // Comment.
 | 
					 | 
				
			||||||
            else if (line.startsWith( "#" )) {
 | 
					 | 
				
			||||||
                if (headerStarted && (marshaller == null)) {
 | 
					 | 
				
			||||||
                    // In header.
 | 
					 | 
				
			||||||
                    Matcher headerMatcher = headerFormat.matcher( line );
 | 
					 | 
				
			||||||
                    if (headerMatcher.matches()) {
 | 
					 | 
				
			||||||
                        String name = headerMatcher.group( 1 ), value = headerMatcher.group( 2 );
 | 
					 | 
				
			||||||
                        if ("Full Name".equalsIgnoreCase( name ) || "User Name".equalsIgnoreCase( name ))
 | 
					 | 
				
			||||||
                            fullName = value;
 | 
					 | 
				
			||||||
                        else if ("Key ID".equalsIgnoreCase( name ))
 | 
					 | 
				
			||||||
                            keyID = CodeUtils.decodeHex( value );
 | 
					 | 
				
			||||||
                        else if ("Algorithm".equalsIgnoreCase( name ))
 | 
					 | 
				
			||||||
                            mpVersion = ConversionUtils.toIntegerNN( value );
 | 
					 | 
				
			||||||
                        else if ("Format".equalsIgnoreCase( name ))
 | 
					 | 
				
			||||||
                            importFormat = ConversionUtils.toIntegerNN( value );
 | 
					 | 
				
			||||||
                        else if ("Avatar".equalsIgnoreCase( name ))
 | 
					 | 
				
			||||||
                            avatar = ConversionUtils.toIntegerNN( value );
 | 
					 | 
				
			||||||
                        else if ("Passwords".equalsIgnoreCase( name ))
 | 
					 | 
				
			||||||
                            clearContent = "visible".equalsIgnoreCase( value );
 | 
					 | 
				
			||||||
                        else if ("Default Type".equalsIgnoreCase( name ))
 | 
					 | 
				
			||||||
                            defaultType = MPResultType.forType( ConversionUtils.toIntegerNN( value ) );
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // No comment.
 | 
					 | 
				
			||||||
            else if (marshaller != null)
 | 
					 | 
				
			||||||
                ifNotNull( marshaller.unmarshallSite( line ), new NNOperation<MPSite>() {
 | 
					 | 
				
			||||||
                    @Override
 | 
					 | 
				
			||||||
                    public void apply(@Nonnull final MPSite site) {
 | 
					 | 
				
			||||||
                        sites.add( site );
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                } );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return Preconditions.checkNotNull( marshaller, "No full header found in import file." );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected MPSiteUnmarshaller(final int importFormat, final int mpVersion, final String fullName, final byte[] keyID, final int avatar,
 | 
					 | 
				
			||||||
                                 final MPResultType defaultType, final boolean clearContent) {
 | 
					 | 
				
			||||||
        this.importFormat = importFormat;
 | 
					 | 
				
			||||||
        this.mpVersion = mpVersion;
 | 
					 | 
				
			||||||
        this.clearContent = clearContent;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        user = new MPUser( fullName, keyID, MasterKey.Version.fromInt( mpVersion ), avatar, defaultType, new DateTime( 0 ) );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    public MPSite unmarshallSite(@Nonnull final String siteLine) {
 | 
					 | 
				
			||||||
        Matcher siteMatcher = unmarshallFormats[importFormat].matcher( siteLine );
 | 
					 | 
				
			||||||
        if (!siteMatcher.matches())
 | 
					 | 
				
			||||||
            return null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        MPSite site;
 | 
					 | 
				
			||||||
        switch (importFormat) {
 | 
					 | 
				
			||||||
            case 0:
 | 
					 | 
				
			||||||
                site = new MPSite( user, //
 | 
					 | 
				
			||||||
                                   MasterKey.Version.fromInt( ConversionUtils.toIntegerNN( siteMatcher.group( 4 ).replace( ":", "" ) ) ), //
 | 
					 | 
				
			||||||
                                   rfc3339.parseDateTime( siteMatcher.group( 1 ) ).toInstant(), //
 | 
					 | 
				
			||||||
                                   siteMatcher.group( 5 ), //
 | 
					 | 
				
			||||||
                                   MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ), MPSite.DEFAULT_COUNTER, //
 | 
					 | 
				
			||||||
                                   ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ), //
 | 
					 | 
				
			||||||
                                   null, //
 | 
					 | 
				
			||||||
                                   siteMatcher.group( 6 ) );
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            case 1:
 | 
					 | 
				
			||||||
                site = new MPSite( user, //
 | 
					 | 
				
			||||||
                                   MasterKey.Version.fromInt( ConversionUtils.toIntegerNN( siteMatcher.group( 4 ).replace( ":", "" ) ) ), //
 | 
					 | 
				
			||||||
                                   rfc3339.parseDateTime( siteMatcher.group( 1 ) ).toInstant(), //
 | 
					 | 
				
			||||||
                                   siteMatcher.group( 7 ), //
 | 
					 | 
				
			||||||
                                   MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
 | 
					 | 
				
			||||||
                                   UnsignedInteger.valueOf( siteMatcher.group( 5 ).replace( ":", "" ) ), //
 | 
					 | 
				
			||||||
                                   ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ), //
 | 
					 | 
				
			||||||
                                   siteMatcher.group( 6 ), //
 | 
					 | 
				
			||||||
                                   siteMatcher.group( 8 ) );
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            default:
 | 
					 | 
				
			||||||
                throw logger.bug( "Unexpected format: %d", importFormat );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        user.addSite( site );
 | 
					 | 
				
			||||||
        return site;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public MPUser getUser() {
 | 
					 | 
				
			||||||
        return user;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					//==============================================================================
 | 
				
			||||||
 | 
					// 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.model;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.*;
 | 
				
			||||||
 | 
					import javax.annotation.Nonnull;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @author lhunath, 14-12-07
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public interface MPUnmarshaller {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Nonnull
 | 
				
			||||||
 | 
					    MPUser unmarshall(@Nonnull File file)
 | 
				
			||||||
 | 
					            throws IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Nonnull
 | 
				
			||||||
 | 
					    MPUser unmarshall(@Nonnull String content);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -40,18 +40,19 @@ public class MPUser implements Comparable<MPUser> {
 | 
				
			|||||||
    private final Collection<MPSite> sites = Sets.newHashSet();
 | 
					    private final Collection<MPSite> sites = Sets.newHashSet();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Nullable
 | 
					    @Nullable
 | 
				
			||||||
    private       byte[]            keyID;
 | 
					    private byte[]            keyID;
 | 
				
			||||||
    private final MasterKey.Version algorithmVersion;
 | 
					    private MasterKey.Version algorithmVersion;
 | 
				
			||||||
    private       int               avatar;
 | 
					
 | 
				
			||||||
    private       MPResultType      defaultType;
 | 
					    private int             avatar;
 | 
				
			||||||
    private       ReadableInstant   lastUsed;
 | 
					    private MPResultType    defaultType;
 | 
				
			||||||
 | 
					    private ReadableInstant lastUsed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public MPUser(final String fullName) {
 | 
					    public MPUser(final String fullName) {
 | 
				
			||||||
        this( fullName, null );
 | 
					        this( fullName, null, MasterKey.Version.CURRENT );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public MPUser(final String fullName, @Nullable final byte[] keyID) {
 | 
					    public MPUser(final String fullName, @Nullable final byte[] keyID, final MasterKey.Version algorithmVersion) {
 | 
				
			||||||
        this( fullName, keyID, MasterKey.Version.CURRENT, 0, MPResultType.DEFAULT, new DateTime() );
 | 
					        this( fullName, keyID, algorithmVersion, 0, MPResultType.DEFAULT, new Instant() );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public MPUser(final String fullName, @Nullable final byte[] keyID, final MasterKey.Version algorithmVersion, final int avatar,
 | 
					    public MPUser(final String fullName, @Nullable final byte[] keyID, final MasterKey.Version algorithmVersion, final int avatar,
 | 
				
			||||||
@@ -108,10 +109,10 @@ public class MPUser implements Comparable<MPUser> {
 | 
				
			|||||||
    @SuppressWarnings("MethodCanBeVariableArityMethod")
 | 
					    @SuppressWarnings("MethodCanBeVariableArityMethod")
 | 
				
			||||||
    public MasterKey authenticate(final char[] masterPassword)
 | 
					    public MasterKey authenticate(final char[] masterPassword)
 | 
				
			||||||
            throws IncorrectMasterPasswordException {
 | 
					            throws IncorrectMasterPasswordException {
 | 
				
			||||||
        MasterKey masterKey = MasterKey.create( algorithmVersion, getFullName(), masterPassword );
 | 
					        MasterKey masterKey = new MasterKey( getFullName(), masterPassword );
 | 
				
			||||||
        if ((keyID == null) || (keyID.length == 0))
 | 
					        if ((keyID == null) || (keyID.length == 0))
 | 
				
			||||||
            keyID = masterKey.getKeyID();
 | 
					            keyID = masterKey.getKeyID( algorithmVersion );
 | 
				
			||||||
        else if (!Arrays.equals( masterKey.getKeyID(), keyID ))
 | 
					        else if (!Arrays.equals( masterKey.getKeyID( algorithmVersion ), keyID ))
 | 
				
			||||||
            throw new IncorrectMasterPasswordException( this );
 | 
					            throw new IncorrectMasterPasswordException( this );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return masterKey;
 | 
					        return masterKey;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -75,7 +75,7 @@ public class MPUserFileManager extends MPUserManager {
 | 
				
			|||||||
            @Override
 | 
					            @Override
 | 
				
			||||||
            public MPUser apply(@Nullable final File file) {
 | 
					            public MPUser apply(@Nullable final File file) {
 | 
				
			||||||
                try {
 | 
					                try {
 | 
				
			||||||
                    return MPSiteUnmarshaller.unmarshall( Preconditions.checkNotNull( file ) ).getUser();
 | 
					                    return new MPFlatUnmarshaller().unmarshall( Preconditions.checkNotNull( file ) );
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch (final IOException e) {
 | 
					                catch (final IOException e) {
 | 
				
			||||||
                    logger.err( e, "Couldn't read user from: %s", file );
 | 
					                    logger.err( e, "Couldn't read user from: %s", file );
 | 
				
			||||||
@@ -120,7 +120,7 @@ public class MPUserFileManager extends MPUserManager {
 | 
				
			|||||||
                        File mpsitesFile = new File( userFilesDirectory, user.getFullName() + ".mpsites" );
 | 
					                        File mpsitesFile = new File( userFilesDirectory, user.getFullName() + ".mpsites" );
 | 
				
			||||||
                        return new OutputStreamWriter( new FileOutputStream( mpsitesFile ), Charsets.UTF_8 );
 | 
					                        return new OutputStreamWriter( new FileOutputStream( mpsitesFile ), Charsets.UTF_8 );
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }.write( MPSiteMarshaller.marshallSafe( user ).getExport() );
 | 
					                }.write( new MPFlatMarshaller().marshall( user, null/*TODO: masterKey*/, MPMarshaller.ContentMode.PROTECTED ) );
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            catch (final IOException e) {
 | 
					            catch (final IOException e) {
 | 
				
			||||||
                logger.err( e, "Unable to save sites for user: %s", user );
 | 
					                logger.err( e, "Unable to save sites for user: %s", user );
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -172,10 +172,10 @@ public class MPTestSuite implements Callable<Boolean> {
 | 
				
			|||||||
            @Nonnull
 | 
					            @Nonnull
 | 
				
			||||||
            @Override
 | 
					            @Override
 | 
				
			||||||
            public Boolean apply(@Nonnull final MPTests.Case testCase) {
 | 
					            public Boolean apply(@Nonnull final MPTests.Case testCase) {
 | 
				
			||||||
                MasterKey masterKey = MasterKey.create( testCase.getAlgorithm(), testCase.getFullName(), testCase.getMasterPassword() );
 | 
					                MasterKey masterKey = new MasterKey( testCase.getFullName(), testCase.getMasterPassword() );
 | 
				
			||||||
                String sitePassword = masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
 | 
					                String sitePassword = masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
 | 
				
			||||||
                                                            testCase.getKeyContext(), testCase.getResultType(),
 | 
					                                                            testCase.getKeyContext(), testCase.getResultType(),
 | 
				
			||||||
                                                            null );
 | 
					                                                            null, testCase.getAlgorithm() );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                return testCase.getResult().equals( sitePassword );
 | 
					                return testCase.getResult().equals( sitePassword );
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -52,12 +52,12 @@ public class MasterKeyTest {
 | 
				
			|||||||
            @Nonnull
 | 
					            @Nonnull
 | 
				
			||||||
            @Override
 | 
					            @Override
 | 
				
			||||||
            public Boolean apply(@Nonnull final MPTests.Case testCase) {
 | 
					            public Boolean apply(@Nonnull final MPTests.Case testCase) {
 | 
				
			||||||
                MasterKey masterKey = MasterKey.create( testCase.getAlgorithm(), testCase.getFullName(), testCase.getMasterPassword() );
 | 
					                MasterKey masterKey = new MasterKey( testCase.getFullName(), testCase.getMasterPassword() );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                assertEquals(
 | 
					                assertEquals(
 | 
				
			||||||
                        masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
 | 
					                        masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
 | 
				
			||||||
                                              testCase.getKeyContext(), testCase.getResultType(),
 | 
					                                              testCase.getKeyContext(), testCase.getResultType(),
 | 
				
			||||||
                                              null ),
 | 
					                                              null, testCase.getAlgorithm() ),
 | 
				
			||||||
                        testCase.getResult(), "[testEncode] Failed test case: " + testCase );
 | 
					                        testCase.getResult(), "[testEncode] Failed test case: " + testCase );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                return true;
 | 
					                return true;
 | 
				
			||||||
@@ -71,7 +71,7 @@ public class MasterKeyTest {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        MPTests.Case defaultCase = testSuite.getTests().getDefaultCase();
 | 
					        MPTests.Case defaultCase = testSuite.getTests().getDefaultCase();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        assertEquals( MasterKey.create( defaultCase.getFullName(), defaultCase.getMasterPassword() ).getFullName(),
 | 
					        assertEquals( new MasterKey( defaultCase.getFullName(), defaultCase.getMasterPassword() ).getFullName(),
 | 
				
			||||||
                      defaultCase.getFullName(), "[testGetUserName] Failed test case: " + defaultCase );
 | 
					                      defaultCase.getFullName(), "[testGetUserName] Failed test case: " + defaultCase );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -83,32 +83,13 @@ public class MasterKeyTest {
 | 
				
			|||||||
            @Nonnull
 | 
					            @Nonnull
 | 
				
			||||||
            @Override
 | 
					            @Override
 | 
				
			||||||
            public Boolean apply(@Nonnull final MPTests.Case testCase) {
 | 
					            public Boolean apply(@Nonnull final MPTests.Case testCase) {
 | 
				
			||||||
                MasterKey masterKey = MasterKey.create( testCase.getFullName(), testCase.getMasterPassword() );
 | 
					                MasterKey masterKey = new MasterKey( testCase.getFullName(), testCase.getMasterPassword() );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                assertEquals( CodeUtils.encodeHex( masterKey.getKeyID() ),
 | 
					                assertEquals( CodeUtils.encodeHex( masterKey.getKeyID( testCase.getAlgorithm() ) ),
 | 
				
			||||||
                              testCase.getKeyID(), "[testGetKeyID] Failed test case: " + testCase );
 | 
					                              testCase.getKeyID(), "[testGetKeyID] Failed test case: " + testCase );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                return true;
 | 
					                return true;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } );
 | 
					        } );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Test
 | 
					 | 
				
			||||||
    public void testInvalidate()
 | 
					 | 
				
			||||||
            throws Exception {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            MPTests.Case defaultCase = testSuite.getTests().getDefaultCase();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            MasterKey masterKey = MasterKey.create( defaultCase.getFullName(), defaultCase.getMasterPassword() );
 | 
					 | 
				
			||||||
            masterKey.invalidate();
 | 
					 | 
				
			||||||
            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." );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        catch (final IllegalStateException ignored) {
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,7 +33,6 @@ import android.view.WindowManager;
 | 
				
			|||||||
import android.widget.*;
 | 
					import android.widget.*;
 | 
				
			||||||
import butterknife.BindView;
 | 
					import butterknife.BindView;
 | 
				
			||||||
import butterknife.ButterKnife;
 | 
					import butterknife.ButterKnife;
 | 
				
			||||||
import com.google.common.base.Throwables;
 | 
					 | 
				
			||||||
import com.google.common.collect.ImmutableList;
 | 
					import com.google.common.collect.ImmutableList;
 | 
				
			||||||
import com.google.common.primitives.UnsignedInteger;
 | 
					import com.google.common.primitives.UnsignedInteger;
 | 
				
			||||||
import com.google.common.util.concurrent.*;
 | 
					import com.google.common.util.concurrent.*;
 | 
				
			||||||
@@ -57,7 +56,7 @@ public class EmergencyActivity extends Activity {
 | 
				
			|||||||
    private final ImmutableList<MPResultType>      allResultTypes = ImmutableList.copyOf( MPResultType.forClass( MPResultTypeClass.Template ) );
 | 
					    private final ImmutableList<MPResultType>      allResultTypes = ImmutableList.copyOf( MPResultType.forClass( MPResultTypeClass.Template ) );
 | 
				
			||||||
    private final ImmutableList<MasterKey.Version> allVersions    = ImmutableList.copyOf( MasterKey.Version.values() );
 | 
					    private final ImmutableList<MasterKey.Version> allVersions    = ImmutableList.copyOf( MasterKey.Version.values() );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private ListenableFuture<MasterKey> masterKeyFuture;
 | 
					    private MasterKey masterKey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @BindView(R.id.progressView)
 | 
					    @BindView(R.id.progressView)
 | 
				
			||||||
    ProgressBar progressView;
 | 
					    ProgressBar progressView;
 | 
				
			||||||
@@ -97,7 +96,6 @@ public class EmergencyActivity extends Activity {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private int    id_userName;
 | 
					    private int    id_userName;
 | 
				
			||||||
    private int    id_masterPassword;
 | 
					    private int    id_masterPassword;
 | 
				
			||||||
    private int    id_version;
 | 
					 | 
				
			||||||
    private String sitePassword;
 | 
					    private String sitePassword;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static void start(final Context context) {
 | 
					    public static void start(final Context context) {
 | 
				
			||||||
@@ -213,7 +211,7 @@ public class EmergencyActivity extends Activity {
 | 
				
			|||||||
    protected void onResume() {
 | 
					    protected void onResume() {
 | 
				
			||||||
        super.onResume();
 | 
					        super.onResume();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        MasterKey.setAllowNativeByDefault( preferences.isAllowNativeKDF() );
 | 
					// FIXME:       MasterKey.setAllowNativeByDefault( preferences.isAllowNativeKDF() );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        fullNameField.setText( preferences.getFullName() );
 | 
					        fullNameField.setText( preferences.getFullName() );
 | 
				
			||||||
        rememberFullNameField.setChecked( preferences.isRememberFullName() );
 | 
					        rememberFullNameField.setChecked( preferences.isRememberFullName() );
 | 
				
			||||||
@@ -241,10 +239,8 @@ public class EmergencyActivity extends Activity {
 | 
				
			|||||||
        if (preferences.isForgetPassword()) {
 | 
					        if (preferences.isForgetPassword()) {
 | 
				
			||||||
            synchronized (this) {
 | 
					            synchronized (this) {
 | 
				
			||||||
                id_userName = id_masterPassword = 0;
 | 
					                id_userName = id_masterPassword = 0;
 | 
				
			||||||
                if (masterKeyFuture != null) {
 | 
					                if (masterKey != null)
 | 
				
			||||||
                    masterKeyFuture.cancel( true );
 | 
					                    masterKey = null;
 | 
				
			||||||
                    masterKeyFuture = null;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                masterPasswordField.setText( "" );
 | 
					                masterPasswordField.setText( "" );
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -260,23 +256,17 @@ public class EmergencyActivity extends Activity {
 | 
				
			|||||||
    private synchronized void updateMasterKey() {
 | 
					    private synchronized void updateMasterKey() {
 | 
				
			||||||
        final String fullName = fullNameField.getText().toString();
 | 
					        final String fullName = fullNameField.getText().toString();
 | 
				
			||||||
        final char[] masterPassword = masterPasswordField.getText().toString().toCharArray();
 | 
					        final char[] masterPassword = masterPasswordField.getText().toString().toCharArray();
 | 
				
			||||||
        final MasterKey.Version version = (MasterKey.Version) siteVersionButton.getTag();
 | 
					 | 
				
			||||||
        if ((id_userName == fullName.hashCode())
 | 
					        if ((id_userName == fullName.hashCode())
 | 
				
			||||||
            && (id_masterPassword == Arrays.hashCode( masterPassword ))
 | 
					            && (id_masterPassword == Arrays.hashCode( masterPassword )))
 | 
				
			||||||
            && (id_version == version.ordinal()))
 | 
					            if (masterKey != null)
 | 
				
			||||||
            if ((masterKeyFuture != null) && !masterKeyFuture.isCancelled())
 | 
					 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        id_userName = fullName.hashCode();
 | 
					        id_userName = fullName.hashCode();
 | 
				
			||||||
        id_masterPassword = Arrays.hashCode( masterPassword );
 | 
					        id_masterPassword = Arrays.hashCode( masterPassword );
 | 
				
			||||||
        id_version = version.ordinal();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (preferences.isRememberFullName())
 | 
					        if (preferences.isRememberFullName())
 | 
				
			||||||
            preferences.setFullName( fullName );
 | 
					            preferences.setFullName( fullName );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (masterKeyFuture != null)
 | 
					 | 
				
			||||||
            masterKeyFuture.cancel( true );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (fullName.isEmpty() || (masterPassword.length == 0)) {
 | 
					        if (fullName.isEmpty() || (masterPassword.length == 0)) {
 | 
				
			||||||
            sitePasswordField.setText( "" );
 | 
					            sitePasswordField.setText( "" );
 | 
				
			||||||
            progressView.setVisibility( View.INVISIBLE );
 | 
					            progressView.setVisibility( View.INVISIBLE );
 | 
				
			||||||
@@ -285,43 +275,21 @@ public class EmergencyActivity extends Activity {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        sitePasswordField.setText( "" );
 | 
					        sitePasswordField.setText( "" );
 | 
				
			||||||
        progressView.setVisibility( View.VISIBLE );
 | 
					        progressView.setVisibility( View.VISIBLE );
 | 
				
			||||||
        (masterKeyFuture = executor.submit( new Callable<MasterKey>() {
 | 
					        masterKey = new MasterKey( fullName, masterPassword );
 | 
				
			||||||
            @Override
 | 
					        updateSitePassword();
 | 
				
			||||||
            public MasterKey call()
 | 
					 | 
				
			||||||
                    throws Exception {
 | 
					 | 
				
			||||||
                try {
 | 
					 | 
				
			||||||
                    return MasterKey.create( version, fullName, masterPassword );
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                catch (final Exception e) {
 | 
					 | 
				
			||||||
                    sitePasswordField.setText( "" );
 | 
					 | 
				
			||||||
                    progressView.setVisibility( View.INVISIBLE );
 | 
					 | 
				
			||||||
                    logger.err( e, "While generating master key." );
 | 
					 | 
				
			||||||
                    throw e;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } )).addListener( new Runnable() {
 | 
					 | 
				
			||||||
            @Override
 | 
					 | 
				
			||||||
            public void run() {
 | 
					 | 
				
			||||||
                runOnUiThread( new Runnable() {
 | 
					 | 
				
			||||||
                    @Override
 | 
					 | 
				
			||||||
                    public void run() {
 | 
					 | 
				
			||||||
                        updateSitePassword();
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                } );
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }, executor );
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void updateSitePassword() {
 | 
					    private void updateSitePassword() {
 | 
				
			||||||
        final String          siteName = siteNameField.getText().toString();
 | 
					        final String          siteName = siteNameField.getText().toString();
 | 
				
			||||||
        final MPResultType    type     = (MPResultType) resultTypeButton.getTag();
 | 
					        final MPResultType    type     = (MPResultType) resultTypeButton.getTag();
 | 
				
			||||||
        final UnsignedInteger counter  = UnsignedInteger.valueOf( siteCounterButton.getText().toString() );
 | 
					        final UnsignedInteger counter  = UnsignedInteger.valueOf( siteCounterButton.getText().toString() );
 | 
				
			||||||
 | 
					        final MasterKey.Version version = (MasterKey.Version) siteVersionButton.getTag();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ((masterKeyFuture == null) || siteName.isEmpty() || (type == null)) {
 | 
					        if ((masterKey == null) || siteName.isEmpty() || (type == null)) {
 | 
				
			||||||
            sitePasswordField.setText( "" );
 | 
					            sitePasswordField.setText( "" );
 | 
				
			||||||
            progressView.setVisibility( View.INVISIBLE );
 | 
					            progressView.setVisibility( View.INVISIBLE );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (masterKeyFuture == null)
 | 
					            if (masterKey == null)
 | 
				
			||||||
                updateMasterKey();
 | 
					                updateMasterKey();
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -332,7 +300,7 @@ public class EmergencyActivity extends Activity {
 | 
				
			|||||||
            @Override
 | 
					            @Override
 | 
				
			||||||
            public void run() {
 | 
					            public void run() {
 | 
				
			||||||
                try {
 | 
					                try {
 | 
				
			||||||
                    sitePassword = masterKeyFuture.get().siteResult( siteName, counter, MPKeyPurpose.Authentication, null, type, null );
 | 
					                    sitePassword = masterKey.siteResult( siteName, counter, MPKeyPurpose.Authentication, null, type, null, version );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    runOnUiThread( new Runnable() {
 | 
					                    runOnUiThread( new Runnable() {
 | 
				
			||||||
                        @Override
 | 
					                        @Override
 | 
				
			||||||
@@ -342,16 +310,6 @@ public class EmergencyActivity extends Activity {
 | 
				
			|||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    } );
 | 
					                    } );
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch (final InterruptedException ignored) {
 | 
					 | 
				
			||||||
                    sitePasswordField.setText( "" );
 | 
					 | 
				
			||||||
                    progressView.setVisibility( View.INVISIBLE );
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                catch (final ExecutionException e) {
 | 
					 | 
				
			||||||
                    sitePasswordField.setText( "" );
 | 
					 | 
				
			||||||
                    progressView.setVisibility( View.INVISIBLE );
 | 
					 | 
				
			||||||
                    logger.err( e, "While generating site password." );
 | 
					 | 
				
			||||||
                    throw Throwables.propagate( e );
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                catch (final RuntimeException e) {
 | 
					                catch (final RuntimeException e) {
 | 
				
			||||||
                    sitePasswordField.setText( "" );
 | 
					                    sitePasswordField.setText( "" );
 | 
				
			||||||
                    progressView.setVisibility( View.INVISIBLE );
 | 
					                    progressView.setVisibility( View.INVISIBLE );
 | 
				
			||||||
@@ -363,10 +321,9 @@ public class EmergencyActivity extends Activity {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public void integrityTests(final View view) {
 | 
					    public void integrityTests(final View view) {
 | 
				
			||||||
        if (masterKeyFuture != null) {
 | 
					        if (masterKey != null)
 | 
				
			||||||
            masterKeyFuture.cancel( true );
 | 
					            masterKey = null;
 | 
				
			||||||
            masterKeyFuture = null;
 | 
					
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        TestActivity.startNoSkip( this );
 | 
					        TestActivity.startNoSkip( this );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -74,7 +74,7 @@ public final class Preferences {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public boolean isAllowNativeKDF() {
 | 
					    public boolean isAllowNativeKDF() {
 | 
				
			||||||
        return prefs().getBoolean( PREF_NATIVE_KDF, MasterKey.isAllowNativeByDefault() );
 | 
					        return prefs().getBoolean( PREF_NATIVE_KDF, true );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public boolean setTestsPassed(final Set<String> value) {
 | 
					    public boolean setTestsPassed(final Set<String> value) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -80,7 +80,7 @@ public class TestActivity extends Activity implements MPTestSuite.Listener {
 | 
				
			|||||||
            @Override
 | 
					            @Override
 | 
				
			||||||
            public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) {
 | 
					            public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) {
 | 
				
			||||||
                preferences.setNativeKDFEnabled( isChecked );
 | 
					                preferences.setNativeKDFEnabled( isChecked );
 | 
				
			||||||
                MasterKey.setAllowNativeByDefault( isChecked );
 | 
					                // TODO: MasterKey.setAllowNativeByDefault( isChecked );
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } );
 | 
					        } );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -122,7 +122,7 @@ public class TestActivity extends Activity implements MPTestSuite.Listener {
 | 
				
			|||||||
        if (testFuture != null)
 | 
					        if (testFuture != null)
 | 
				
			||||||
            testFuture.cancel( true );
 | 
					            testFuture.cancel( true );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        MasterKey.setAllowNativeByDefault( preferences.isAllowNativeKDF() );
 | 
					        // TODO: MasterKey.setAllowNativeByDefault( preferences.isAllowNativeKDF() );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        setStatus( R.string.tests_testing, R.string.tests_btn_testing, null );
 | 
					        setStatus( R.string.tests_testing, R.string.tests_btn_testing, null );
 | 
				
			||||||
        Futures.addCallback( testFuture = backgroundExecutor.submit( testSuite ), new FutureCallback<Boolean>() {
 | 
					        Futures.addCallback( testFuture = backgroundExecutor.submit( testSuite ), new FutureCallback<Boolean>() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,6 +19,7 @@
 | 
				
			|||||||
package com.lyndir.masterpassword.gui.model;
 | 
					package com.lyndir.masterpassword.gui.model;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.google.common.collect.ImmutableList;
 | 
					import com.google.common.collect.ImmutableList;
 | 
				
			||||||
 | 
					import com.lyndir.masterpassword.MasterKey;
 | 
				
			||||||
import com.lyndir.masterpassword.model.IncorrectMasterPasswordException;
 | 
					import com.lyndir.masterpassword.model.IncorrectMasterPasswordException;
 | 
				
			||||||
import javax.annotation.Nullable;
 | 
					import javax.annotation.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -29,8 +30,6 @@ import javax.annotation.Nullable;
 | 
				
			|||||||
public class IncognitoUser extends User {
 | 
					public class IncognitoUser extends User {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private final String fullName;
 | 
					    private final String fullName;
 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    private       char[] masterPassword;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public IncognitoUser(final String fullName) {
 | 
					    public IncognitoUser(final String fullName) {
 | 
				
			||||||
        this.fullName = fullName;
 | 
					        this.fullName = fullName;
 | 
				
			||||||
@@ -41,16 +40,10 @@ public class IncognitoUser extends User {
 | 
				
			|||||||
        return fullName;
 | 
					        return fullName;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    protected char[] getMasterPassword() {
 | 
					 | 
				
			||||||
        return masterPassword;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void authenticate(final char[] masterPassword)
 | 
					    public void authenticate(final char[] masterPassword)
 | 
				
			||||||
            throws IncorrectMasterPasswordException {
 | 
					            throws IncorrectMasterPasswordException {
 | 
				
			||||||
        this.masterPassword = masterPassword.clone();
 | 
					        this.key = new MasterKey( getFullName(), masterPassword );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,9 +34,6 @@ public class ModelUser extends User {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private final MPUser model;
 | 
					    private final MPUser model;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    private char[] masterPassword;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public ModelUser(final MPUser model) {
 | 
					    public ModelUser(final MPUser model) {
 | 
				
			||||||
        this.model = model;
 | 
					        this.model = model;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -50,12 +47,6 @@ public class ModelUser extends User {
 | 
				
			|||||||
        return model.getFullName();
 | 
					        return model.getFullName();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    protected char[] getMasterPassword() {
 | 
					 | 
				
			||||||
        return masterPassword;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public int getAvatar() {
 | 
					    public int getAvatar() {
 | 
				
			||||||
        return model.getAvatar();
 | 
					        return model.getAvatar();
 | 
				
			||||||
@@ -69,21 +60,10 @@ public class ModelUser extends User {
 | 
				
			|||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void authenticate(final char[] masterPassword)
 | 
					    public void authenticate(final char[] masterPassword)
 | 
				
			||||||
            throws IncorrectMasterPasswordException {
 | 
					            throws IncorrectMasterPasswordException {
 | 
				
			||||||
        putKey( model.authenticate( masterPassword ) );
 | 
					        key = model.authenticate( masterPassword );
 | 
				
			||||||
        this.masterPassword = masterPassword.clone();
 | 
					 | 
				
			||||||
        MPUserFileManager.get().save();
 | 
					        MPUserFileManager.get().save();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public void reset() {
 | 
					 | 
				
			||||||
        super.reset();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (masterPassword != null) {
 | 
					 | 
				
			||||||
            Arrays.fill( masterPassword, (char) 0 );
 | 
					 | 
				
			||||||
            masterPassword = null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public Iterable<Site> findSitesByName(final String siteName) {
 | 
					    public Iterable<Site> findSitesByName(final String siteName) {
 | 
				
			||||||
        return FluentIterable.from( model.findSitesByName( siteName ) ).transform( new Function<MPSiteResult, Site>() {
 | 
					        return FluentIterable.from( model.findSitesByName( siteName ) ).transform( new Function<MPSiteResult, Site>() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,6 @@
 | 
				
			|||||||
package com.lyndir.masterpassword.gui.model;
 | 
					package com.lyndir.masterpassword.gui.model;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.google.common.base.Preconditions;
 | 
					import com.google.common.base.Preconditions;
 | 
				
			||||||
import com.google.common.collect.Maps;
 | 
					 | 
				
			||||||
import com.lyndir.masterpassword.MasterKey;
 | 
					import com.lyndir.masterpassword.MasterKey;
 | 
				
			||||||
import com.lyndir.masterpassword.model.IncorrectMasterPasswordException;
 | 
					import com.lyndir.masterpassword.model.IncorrectMasterPasswordException;
 | 
				
			||||||
import java.util.*;
 | 
					import java.util.*;
 | 
				
			||||||
@@ -32,14 +31,11 @@ import javax.annotation.Nullable;
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
public abstract class User {
 | 
					public abstract class User {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Nonnull
 | 
					    @Nullable
 | 
				
			||||||
    private final Map<MasterKey.Version, MasterKey> keyByVersion = Maps.newEnumMap( MasterKey.Version.class  );
 | 
					    protected MasterKey key;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public abstract String getFullName();
 | 
					    public abstract String getFullName();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    protected abstract char[] getMasterPassword();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @SuppressWarnings("MethodCanBeVariableArityMethod")
 | 
					    @SuppressWarnings("MethodCanBeVariableArityMethod")
 | 
				
			||||||
    public abstract void authenticate(char[] masterPassword)
 | 
					    public abstract void authenticate(char[] masterPassword)
 | 
				
			||||||
            throws IncorrectMasterPasswordException;
 | 
					            throws IncorrectMasterPasswordException;
 | 
				
			||||||
@@ -49,31 +45,12 @@ public abstract class User {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public boolean isKeyAvailable() {
 | 
					    public boolean isKeyAvailable() {
 | 
				
			||||||
        return getMasterPassword() != null;
 | 
					        return key != null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Nonnull
 | 
					    @Nonnull
 | 
				
			||||||
    public MasterKey getKey(final MasterKey.Version algorithmVersion) {
 | 
					    public MasterKey getKey() {
 | 
				
			||||||
        char[] masterPassword = Preconditions.checkNotNull( getMasterPassword(), "User is not authenticated: " + getFullName() );
 | 
					        return Preconditions.checkNotNull( key, "User is not authenticated: " + getFullName() );
 | 
				
			||||||
 | 
					 | 
				
			||||||
        MasterKey key = keyByVersion.get( algorithmVersion );
 | 
					 | 
				
			||||||
        if (key == null)
 | 
					 | 
				
			||||||
            putKey( key = MasterKey.create( algorithmVersion, getFullName(), masterPassword ) );
 | 
					 | 
				
			||||||
        if (!key.isValid())
 | 
					 | 
				
			||||||
            key.revalidate( masterPassword );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return key;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected void putKey(final MasterKey masterKey) {
 | 
					 | 
				
			||||||
        MasterKey oldKey = keyByVersion.put( masterKey.getAlgorithmVersion(), masterKey );
 | 
					 | 
				
			||||||
        if (oldKey != null)
 | 
					 | 
				
			||||||
            oldKey.invalidate();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public void reset() {
 | 
					 | 
				
			||||||
        for (final MasterKey key : keyByVersion.values())
 | 
					 | 
				
			||||||
            key.invalidate();
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public abstract Iterable<Site> findSitesByName(String siteName);
 | 
					    public abstract Iterable<Site> findSitesByName(String siteName);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -257,8 +257,8 @@ public class PasswordFrame extends JFrame implements DocumentListener {
 | 
				
			|||||||
            @Override
 | 
					            @Override
 | 
				
			||||||
            public String call()
 | 
					            public String call()
 | 
				
			||||||
                    throws Exception {
 | 
					                    throws Exception {
 | 
				
			||||||
                return user.getKey( site.getAlgorithmVersion() )
 | 
					                return user.getKey()
 | 
				
			||||||
                           .siteResult( site.getSiteName(), site.getSiteCounter(), MPKeyPurpose.Authentication, null, site.getResultType(), null );
 | 
					                           .siteResult( site.getSiteName(), site.getSiteCounter(), MPKeyPurpose.Authentication, null, site.getResultType(), null, site.getAlgorithmVersion() );
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } );
 | 
					        } );
 | 
				
			||||||
        Futures.addCallback( passwordFuture, new FutureCallback<String>() {
 | 
					        Futures.addCallback( passwordFuture, new FutureCallback<String>() {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user